mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2025-09-20 14:07:58 +00:00
feat: support appflowy cloud in sign-in settings page (#6386)
* feat: support appflowy cloud in sign-in settings page * test: add cloud server test * test: wait until the tooltip disappear
This commit is contained in:
parent
0d2841227a
commit
c51b495544
@ -3,6 +3,7 @@ import 'package:integration_test/integration_test.dart';
|
|||||||
import 'notifications_settings_test.dart' as notifications_settings_test;
|
import 'notifications_settings_test.dart' as notifications_settings_test;
|
||||||
import 'settings_billing_test.dart' as settings_billing_test;
|
import 'settings_billing_test.dart' as settings_billing_test;
|
||||||
import 'shortcuts_settings_test.dart' as shortcuts_settings_test;
|
import 'shortcuts_settings_test.dart' as shortcuts_settings_test;
|
||||||
|
import 'sign_in_page_settings_test.dart' as sign_in_page_settings_test;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
@ -10,4 +11,5 @@ void main() {
|
|||||||
notifications_settings_test.main();
|
notifications_settings_test.main();
|
||||||
settings_billing_test.main();
|
settings_billing_test.main();
|
||||||
shortcuts_settings_test.main();
|
shortcuts_settings_test.main();
|
||||||
|
sign_in_page_settings_test.main();
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,103 @@
|
|||||||
|
import 'package:appflowy/env/cloud_env.dart';
|
||||||
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
|
import 'package:appflowy/user/presentation/screens/sign_in_screen/desktop_sign_in_screen.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
|
||||||
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_test.dart';
|
||||||
|
import 'package:toastification/toastification.dart';
|
||||||
|
|
||||||
|
import '../../shared/util.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
Finder findServerType(AuthenticatorType type) {
|
||||||
|
return find
|
||||||
|
.descendant(
|
||||||
|
of: find.byType(SettingsServerDropdownMenu),
|
||||||
|
matching: find.findTextInFlowyText(
|
||||||
|
type.label,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.last;
|
||||||
|
}
|
||||||
|
|
||||||
|
group('sign-in page settings: ', () {
|
||||||
|
testWidgets('change server type', (tester) async {
|
||||||
|
await tester.initializeAppFlowy();
|
||||||
|
|
||||||
|
// reset the app to the default state
|
||||||
|
await useAppFlowyBetaCloudWithURL(
|
||||||
|
kAppflowyCloudUrl,
|
||||||
|
AuthenticatorType.appflowyCloud,
|
||||||
|
);
|
||||||
|
|
||||||
|
// open the settings page
|
||||||
|
final settingsButton = find.byType(DesktopSignInSettingsButton);
|
||||||
|
await tester.tapButton(settingsButton);
|
||||||
|
|
||||||
|
expect(find.byType(SimpleSettingsDialog), findsOneWidget);
|
||||||
|
|
||||||
|
// the default type should be appflowy cloud
|
||||||
|
final appflowyCloudType = findServerType(AuthenticatorType.appflowyCloud);
|
||||||
|
expect(appflowyCloudType, findsOneWidget);
|
||||||
|
|
||||||
|
// change the server type to self-host
|
||||||
|
await tester.tapButton(appflowyCloudType);
|
||||||
|
final selfhostedButton = findServerType(
|
||||||
|
AuthenticatorType.appflowyCloudSelfHost,
|
||||||
|
);
|
||||||
|
await tester.tapButton(selfhostedButton);
|
||||||
|
|
||||||
|
// update server url
|
||||||
|
const serverUrl = 'https://test.appflowy.cloud';
|
||||||
|
await tester.enterText(
|
||||||
|
find.byKey(kSelfHostedTextInputFieldKey),
|
||||||
|
serverUrl,
|
||||||
|
);
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tapButton(
|
||||||
|
find.findTextInFlowyText(LocaleKeys.button_save.tr()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// wait the app to restart, and the tooltip to disappear
|
||||||
|
await tester.pumpUntilNotFound(find.byType(BuiltInToastBuilder));
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 250));
|
||||||
|
|
||||||
|
// open settings page to check the result
|
||||||
|
await tester.tapButton(settingsButton);
|
||||||
|
|
||||||
|
// check the server type
|
||||||
|
expect(
|
||||||
|
findServerType(AuthenticatorType.appflowyCloudSelfHost),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
// check the server url
|
||||||
|
expect(
|
||||||
|
find.text(serverUrl),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
|
||||||
|
// reset to appflowy cloud
|
||||||
|
await tester.tapButton(
|
||||||
|
findServerType(AuthenticatorType.appflowyCloudSelfHost),
|
||||||
|
);
|
||||||
|
// change the server type to appflowy cloud
|
||||||
|
await tester.tapButton(
|
||||||
|
findServerType(AuthenticatorType.appflowyCloud),
|
||||||
|
);
|
||||||
|
|
||||||
|
// wait the app to restart, and the tooltip to disappear
|
||||||
|
await tester.pumpUntilNotFound(find.byType(BuiltInToastBuilder));
|
||||||
|
await tester.pumpAndSettle(const Duration(milliseconds: 250));
|
||||||
|
|
||||||
|
// check the server type
|
||||||
|
await tester.tapButton(settingsButton);
|
||||||
|
expect(
|
||||||
|
findServerType(AuthenticatorType.appflowyCloud),
|
||||||
|
findsOneWidget,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -391,17 +391,23 @@ class _AFDropdownMenuState<T> extends State<AFDropdownMenu<T>> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove the code here, it will throw a FlutterError
|
||||||
|
// Unless we upgrade to Flutter 3.24 https://github.com/flutter/flutter/issues/146764
|
||||||
void scrollToHighlight() {
|
void scrollToHighlight() {
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
// WidgetsBinding.instance.addPostFrameCallback(
|
||||||
(_) {
|
// (_) {
|
||||||
final BuildContext? highlightContext =
|
// // try {
|
||||||
buttonItemKeys[currentHighlight!].currentContext;
|
// final BuildContext? highlightContext =
|
||||||
if (highlightContext != null) {
|
// buttonItemKeys[currentHighlight!].currentContext;
|
||||||
Scrollable.ensureVisible(highlightContext);
|
// if (highlightContext != null) {
|
||||||
}
|
// Scrollable.ensureVisible(highlightContext);
|
||||||
},
|
// }
|
||||||
debugLabel: 'DropdownMenu.scrollToHighlight',
|
// } catch (_) {
|
||||||
);
|
// return;
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
// debugLabel: 'DropdownMenu.scrollToHighlight',
|
||||||
|
// );
|
||||||
}
|
}
|
||||||
|
|
||||||
double? getWidth(GlobalKey key) {
|
double? getWidth(GlobalKey key) {
|
||||||
|
@ -68,7 +68,7 @@ class DesktopSignInScreen extends StatelessWidget {
|
|||||||
const Row(
|
const Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
_SettingsButton(),
|
DesktopSignInSettingsButton(),
|
||||||
HSpace(42),
|
HSpace(42),
|
||||||
SignInAnonymousButtonV2(),
|
SignInAnonymousButtonV2(),
|
||||||
],
|
],
|
||||||
@ -92,8 +92,10 @@ class DesktopSignInScreen extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _SettingsButton extends StatelessWidget {
|
class DesktopSignInSettingsButton extends StatelessWidget {
|
||||||
const _SettingsButton();
|
const DesktopSignInSettingsButton({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -14,7 +14,9 @@ import 'package:appflowy/workspace/presentation/settings/pages/settings_manage_d
|
|||||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_plan_view.dart';
|
import 'package:appflowy/workspace/presentation/settings/pages/settings_plan_view.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_shortcuts_view.dart';
|
import 'package:appflowy/workspace/presentation/settings/pages/settings_shortcuts_view.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/pages/settings_workspace_view.dart';
|
import 'package:appflowy/workspace/presentation/settings/pages/settings_workspace_view.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/settings/shared/af_dropdown_menu_entry.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_category.dart';
|
||||||
|
import 'package:appflowy/workspace/presentation/settings/shared/settings_dropdown.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/feature_flags/feature_flag_page.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_page.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_page.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu.dart';
|
||||||
@ -30,6 +32,10 @@ import 'package:toastification/toastification.dart';
|
|||||||
|
|
||||||
import 'widgets/setting_cloud.dart';
|
import 'widgets/setting_cloud.dart';
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
const kSelfHostedTextInputFieldKey =
|
||||||
|
ValueKey('self_hosted_url_input_text_field');
|
||||||
|
|
||||||
class SettingsDialog extends StatelessWidget {
|
class SettingsDialog extends StatelessWidget {
|
||||||
SettingsDialog(
|
SettingsDialog(
|
||||||
this.user, {
|
this.user, {
|
||||||
@ -173,6 +179,7 @@ class _SimpleSettingsDialogState extends State<SimpleSettingsDialog> {
|
|||||||
return FlowyDialog(
|
return FlowyDialog(
|
||||||
width: MediaQuery.of(context).size.width * 0.7,
|
width: MediaQuery.of(context).size.width * 0.7,
|
||||||
constraints: const BoxConstraints(maxWidth: 784, minWidth: 564),
|
constraints: const BoxConstraints(maxWidth: 784, minWidth: 564),
|
||||||
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(24.0),
|
padding: const EdgeInsets.all(24.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -200,6 +207,7 @@ class _SimpleSettingsDialogState extends State<SimpleSettingsDialog> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -229,6 +237,7 @@ class _SelfHostSettings extends StatefulWidget {
|
|||||||
|
|
||||||
class _SelfHostSettingsState extends State<_SelfHostSettings> {
|
class _SelfHostSettingsState extends State<_SelfHostSettings> {
|
||||||
final textController = TextEditingController();
|
final textController = TextEditingController();
|
||||||
|
AuthenticatorType type = AuthenticatorType.appflowyCloud;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -236,6 +245,11 @@ class _SelfHostSettingsState extends State<_SelfHostSettings> {
|
|||||||
|
|
||||||
getAppFlowyCloudUrl().then((url) {
|
getAppFlowyCloudUrl().then((url) {
|
||||||
textController.text = url;
|
textController.text = url;
|
||||||
|
if (kAppflowyCloudUrl != url) {
|
||||||
|
setState(() {
|
||||||
|
type = AuthenticatorType.appflowyCloudSelfHost;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,22 +262,38 @@ class _SelfHostSettingsState extends State<_SelfHostSettings> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SettingsCategory(
|
return SettingsCategory(
|
||||||
title: LocaleKeys.settings_menu_cloudAppFlowySelfHost.tr(),
|
title: LocaleKeys.settings_menu_cloudAppFlowy.tr(),
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Flexible(
|
||||||
|
child: SettingsServerDropdownMenu(
|
||||||
|
selectedServer: type,
|
||||||
|
onSelected: _onSelected,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (type == AuthenticatorType.appflowyCloudSelfHost) _buildInputField(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildInputField() {
|
||||||
|
return Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 36,
|
height: 36,
|
||||||
child: FlowyTextField(
|
child: FlowyTextField(
|
||||||
|
key: kSelfHostedTextInputFieldKey,
|
||||||
controller: textController,
|
controller: textController,
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
textStyle: const TextStyle(
|
textStyle: const TextStyle(
|
||||||
fontSize: 14,
|
fontSize: 14,
|
||||||
fontWeight: FontWeight.w400,
|
fontWeight: FontWeight.w400,
|
||||||
),
|
),
|
||||||
hintText: 'https://beta.appflowy.cloud',
|
hintText: kAppflowyCloudUrl,
|
||||||
onEditingComplete: _saveSelfHostUrl,
|
onEditingComplete: () => _saveUrl(
|
||||||
|
url: textController.text,
|
||||||
|
type: AuthenticatorType.appflowyCloudSelfHost,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -273,17 +303,40 @@ class _SelfHostSettingsState extends State<_SelfHostSettings> {
|
|||||||
constraints: const BoxConstraints(minWidth: 78),
|
constraints: const BoxConstraints(minWidth: 78),
|
||||||
child: OutlinedRoundedButton(
|
child: OutlinedRoundedButton(
|
||||||
text: LocaleKeys.button_save.tr(),
|
text: LocaleKeys.button_save.tr(),
|
||||||
onTap: _saveSelfHostUrl,
|
onTap: () => _saveUrl(
|
||||||
|
url: textController.text,
|
||||||
|
type: AuthenticatorType.appflowyCloudSelfHost,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _saveSelfHostUrl() {
|
void _onSelected(AuthenticatorType type) {
|
||||||
final url = textController.text;
|
if (type == this.type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.info('Switching server type to $type');
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
this.type = type;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (type == AuthenticatorType.appflowyCloud) {
|
||||||
|
textController.text = kAppflowyCloudUrl;
|
||||||
|
_saveUrl(
|
||||||
|
url: textController.text,
|
||||||
|
type: type,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveUrl({
|
||||||
|
required String url,
|
||||||
|
required AuthenticatorType type,
|
||||||
|
}) {
|
||||||
if (url.isEmpty) {
|
if (url.isEmpty) {
|
||||||
showToastNotification(
|
showToastNotification(
|
||||||
context,
|
context,
|
||||||
@ -301,7 +354,7 @@ class _SelfHostSettingsState extends State<_SelfHostSettings> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
await useSelfHostedAppFlowyCloudWithURL(url);
|
await useAppFlowyBetaCloudWithURL(url, type);
|
||||||
await runAppFlowy();
|
await runAppFlowy();
|
||||||
},
|
},
|
||||||
(err) {
|
(err) {
|
||||||
@ -316,6 +369,57 @@ class _SelfHostSettingsState extends State<_SelfHostSettings> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
extension SettingsServerDropdownMenuExtension on AuthenticatorType {
|
||||||
|
String get label {
|
||||||
|
switch (this) {
|
||||||
|
case AuthenticatorType.appflowyCloud:
|
||||||
|
return LocaleKeys.settings_menu_cloudAppFlowy.tr();
|
||||||
|
case AuthenticatorType.appflowyCloudSelfHost:
|
||||||
|
return LocaleKeys.settings_menu_cloudAppFlowySelfHost.tr();
|
||||||
|
default:
|
||||||
|
throw Exception('Unsupported server type: $this');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
class SettingsServerDropdownMenu extends StatelessWidget {
|
||||||
|
const SettingsServerDropdownMenu({
|
||||||
|
super.key,
|
||||||
|
required this.selectedServer,
|
||||||
|
required this.onSelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
final AuthenticatorType selectedServer;
|
||||||
|
final void Function(AuthenticatorType type) onSelected;
|
||||||
|
|
||||||
|
// in the settings page from sign in page, we only support appflowy cloud and self-hosted
|
||||||
|
static final supportedServers = [
|
||||||
|
AuthenticatorType.appflowyCloud,
|
||||||
|
AuthenticatorType.appflowyCloudSelfHost,
|
||||||
|
];
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SettingsDropdown<AuthenticatorType>(
|
||||||
|
expandWidth: false,
|
||||||
|
onChanged: onSelected,
|
||||||
|
selectedOption: selectedServer,
|
||||||
|
options: supportedServers
|
||||||
|
.map(
|
||||||
|
(serverType) => buildDropdownMenuEntry<AuthenticatorType>(
|
||||||
|
context,
|
||||||
|
selectedValue: selectedServer,
|
||||||
|
value: serverType,
|
||||||
|
label: serverType.label,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _SupportSettings extends StatelessWidget {
|
class _SupportSettings extends StatelessWidget {
|
||||||
const _SupportSettings({
|
const _SupportSettings({
|
||||||
super.key,
|
super.key,
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'package:appflowy/flutter/af_dropdown_menu.dart';
|
import 'package:appflowy/flutter/af_dropdown_menu.dart';
|
||||||
import 'package:appflowy/shared/google_fonts_extension.dart';
|
import 'package:appflowy/shared/google_fonts_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||||
@ -7,6 +5,7 @@ import 'package:appflowy/workspace/application/settings/appearance/base_appearan
|
|||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flowy_infra/size.dart';
|
import 'package:flowy_infra/size.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
class SettingsDropdown<T> extends StatefulWidget {
|
class SettingsDropdown<T> extends StatefulWidget {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user