feat: customize self host url on launch page (#4465)

This commit is contained in:
Lucas.Xu 2024-01-23 16:38:15 +08:00 committed by GitHub
parent 2554ba81b5
commit e239ba46aa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 293 additions and 93 deletions

View File

@ -0,0 +1,37 @@
import 'package:appflowy/env/env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/mobile/presentation/setting/self_host_setting_group.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MobileLaunchSettingsPage extends StatelessWidget {
const MobileLaunchSettingsPage({
super.key,
});
static const routeName = '/launch_settings';
@override
Widget build(BuildContext context) {
context.watch<AppearanceSettingsCubit>();
return Scaffold(
appBar: AppBar(
title: Text(LocaleKeys.settings_title.tr()),
),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const LanguageSettingGroup(),
if (Env.enableCustomCloud) const SelfHostSettingGroup(),
],
),
),
),
);
}
}

View File

@ -1,38 +0,0 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
class LogoutSettingGroup extends StatelessWidget {
const LogoutSettingGroup({
super.key,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
const Divider(),
Padding(
padding: const EdgeInsets.only(bottom: 4),
child: FlowyButton(
margin: const EdgeInsets.symmetric(
vertical: 16.0,
),
text: FlowyText.medium(
LocaleKeys.settings_menu_logout.tr(),
textAlign: TextAlign.center,
fontSize: 14.0,
),
onTap: () async {
await getIt<AuthService>().signOut();
runAppFlowy();
},
),
),
],
);
}
}

View File

@ -0,0 +1,110 @@
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/settings/appflowy_cloud_urls_bloc.dart';
import 'package:appflowy_backend/log.dart';
import 'package:dartz/dartz.dart' show Some;
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class SelfHostUrlBottomSheet extends StatefulWidget {
const SelfHostUrlBottomSheet({
super.key,
required this.url,
});
final String url;
@override
State<SelfHostUrlBottomSheet> createState() => _SelfHostUrlBottomSheetState();
}
class _SelfHostUrlBottomSheetState extends State<SelfHostUrlBottomSheet> {
final TextEditingController _textFieldController = TextEditingController();
final _formKey = GlobalKey<FormState>();
@override
void initState() {
super.initState();
_textFieldController.text = widget.url;
}
@override
void dispose() {
_textFieldController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
LocaleKeys.editor_urlHint.tr(),
style: theme.textTheme.labelSmall,
),
IconButton(
icon: Icon(
Icons.close,
color: theme.hintColor,
),
onPressed: () {
context.pop();
},
),
],
),
const SizedBox(
height: 16,
),
Form(
key: _formKey,
child: TextFormField(
controller: _textFieldController,
keyboardType: TextInputType.text,
validator: (value) {
if (value == null || value.isEmpty) {
return LocaleKeys.settings_mobile_usernameEmptyError.tr();
}
return null;
},
onEditingComplete: _saveSelfHostUrl,
),
),
const SizedBox(
height: 16,
),
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _saveSelfHostUrl,
child: Text(LocaleKeys.settings_menu_restartApp.tr()),
),
),
],
);
}
void _saveSelfHostUrl() {
if (_formKey.currentState!.validate()) {
final value = _textFieldController.text;
if (value.isNotEmpty) {
validateUrl(value).fold(
(url) async {
await setAppFlowyCloudUrl(Some(url));
runAppFlowy();
},
(err) => Log.error(err),
);
}
}
}
}

View File

@ -0,0 +1,52 @@
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/mobile/presentation/setting/self_host/self_host_bottom_sheet.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'setting.dart';
class SelfHostSettingGroup extends StatefulWidget {
const SelfHostSettingGroup({
super.key,
});
@override
State<SelfHostSettingGroup> createState() => _SelfHostSettingGroupState();
}
class _SelfHostSettingGroupState extends State<SelfHostSettingGroup> {
final future = getAppFlowyCloudUrl();
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: future,
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const SizedBox.shrink();
}
final url = snapshot.data ?? '';
return MobileSettingGroup(
groupTitle: LocaleKeys.settings_menu_cloudAppFlowySelfHost.tr(),
settingItemList: [
MobileSettingItem(
name: url,
onTap: () {
showMobileBottomSheet(
context,
builder: (_) {
return SelfHostUrlBottomSheet(
url: url,
);
},
);
},
),
],
);
},
);
}
}

View File

@ -82,7 +82,6 @@ class InitAppWidgetTask extends LaunchTask {
path: 'assets/translations',
fallbackLocale: const Locale('en'),
useFallbackTranslations: true,
saveLocale: false,
child: app,
),
);

View File

@ -12,6 +12,7 @@ import 'package:appflowy/mobile/presentation/presentation.dart';
import 'package:appflowy/mobile/presentation/setting/cloud/appflowy_cloud_page.dart';
import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart';
import 'package:appflowy/mobile/presentation/setting/language/language_picker_screen.dart';
import 'package:appflowy/mobile/presentation/setting/launch_settings_page.dart';
import 'package:appflowy/plugins/base/color/color_picker_screen.dart';
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/code_block/code_language_screen.dart';
@ -49,6 +50,7 @@ GoRouter generateRouter(Widget child) {
_mobileHomeSettingPageRoute(),
_mobileSettingUserAgreementPageRoute(),
_mobileCloudSettingAppFlowyCloudPageRoute(),
_mobileLaunchSettingsPageRoute(),
// view page
_mobileEditorScreenRoute(),
@ -215,6 +217,16 @@ GoRoute _mobileSettingUserAgreementPageRoute() {
);
}
GoRoute _mobileLaunchSettingsPageRoute() {
return GoRoute(
parentNavigatorKey: AppGlobals.rootNavKey,
path: MobileLaunchSettingsPage.routeName,
pageBuilder: (context, state) {
return const MaterialPage(child: MobileLaunchSettingsPage());
},
);
}
GoRoute _mobileHomeTrashPageRoute() {
return GoRoute(
parentNavigatorKey: AppGlobals.rootNavKey,

View File

@ -1,10 +1,12 @@
import 'package:appflowy/env/cloud_env.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/setting/launch_settings_page.dart';
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class MobileSignInScreen extends StatelessWidget {
const MobileSignInScreen({
@ -20,65 +22,89 @@ class MobileSignInScreen extends StatelessWidget {
padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 40),
child: Column(
children: [
const Spacer(
flex: 4,
),
const FlowySvg(
FlowySvgs.flowy_logo_xl,
size: Size.square(64),
blendMode: null,
),
const Spacer(flex: 4),
_buildLogo(),
const VSpace(spacing * 2),
// Welcome to
FlowyText(
LocaleKeys.welcomeTo.tr(),
textAlign: TextAlign.center,
fontSize: 32,
fontWeight: FontWeight.w700,
),
// AppFlowy
FlowyText(
LocaleKeys.appName.tr(),
textAlign: TextAlign.center,
fontSize: 32,
color: const Color(0xFF00BCF0),
fontWeight: FontWeight.w700,
),
_buildWelcomeText(),
_buildAppNameText(colorScheme),
const VSpace(spacing),
const Spacer(
flex: 2,
),
const Spacer(flex: 2),
const SignInAnonymousButton(),
const VSpace(spacing),
// if the cloud env is enabled, show the third-party sign in buttons.
if (isAuthEnabled) ...[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Expanded(child: Divider()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: FlowyText(
LocaleKeys.signIn_or.tr(),
color: colorScheme.onSecondary,
),
),
const Expanded(child: Divider()),
],
),
_buildThirdPartySignInButtons(colorScheme),
const VSpace(spacing),
const ThirdPartySignInButtons(),
_buildSettingsButton(context),
],
if (!isAuthEnabled)
const Spacer(
flex: 2,
),
const VSpace(spacing),
if (!isAuthEnabled) const Spacer(flex: 2),
],
),
),
);
}
Widget _buildWelcomeText() {
return FlowyText(
LocaleKeys.welcomeTo.tr(),
textAlign: TextAlign.center,
fontSize: 32,
fontWeight: FontWeight.w700,
);
}
Widget _buildLogo() {
return const FlowySvg(
FlowySvgs.flowy_logo_xl,
size: Size.square(64),
blendMode: null,
);
}
Widget _buildAppNameText(ColorScheme colorScheme) {
return FlowyText(
LocaleKeys.appName.tr(),
textAlign: TextAlign.center,
fontSize: 32,
color: const Color(0xFF00BCF0),
fontWeight: FontWeight.w700,
);
}
Widget _buildThirdPartySignInButtons(ColorScheme colorScheme) {
return Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Expanded(child: Divider()),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: FlowyText(
LocaleKeys.signIn_or.tr(),
color: colorScheme.onSecondary,
),
),
const Expanded(child: Divider()),
],
),
const VSpace(16),
const ThirdPartySignInButtons(),
],
);
}
Widget _buildSettingsButton(BuildContext context) {
return FlowyButton(
text: const FlowyText(
'settings',
textAlign: TextAlign.center,
fontSize: 12.0,
fontWeight: FontWeight.w500,
decoration: TextDecoration.underline,
),
onTap: () async {
context.push(MobileLaunchSettingsPage.routeName);
},
);
}
}

View File

@ -59,6 +59,7 @@ class SignInAnonymousButton extends StatelessWidget {
LocaleKeys.signIn_loginStartWithAnonymous.tr(),
fontSize: 14,
color: Theme.of(context).colorScheme.onPrimary,
fontWeight: FontWeight.w500,
),
);
}

View File

@ -51,9 +51,10 @@ class MobileSignInOrLogoutButton extends StatelessWidget {
),
const HSpace(8),
],
Text(
FlowyText(
labelText,
style: Theme.of(context).textTheme.titleSmall,
fontSize: 14.0,
fontWeight: FontWeight.w500,
),
],
),

View File

@ -23,7 +23,7 @@ class ThirdPartySignInButtons extends StatelessWidget {
// When user changes themeMode, it changes the state in AppearanceSettingsCubit, but the themeMode for the MaterialApp won't change, it only got updated(get value from AppearanceSettingsCubit) when user open the app again. Thus, we should get themeMode from AppearanceSettingsCubit rather than MediaQuery.
final themeModeFromCubit =
context.read<AppearanceSettingsCubit>().state.themeMode;
context.watch<AppearanceSettingsCubit>().state.themeMode;
final isDarkMode = themeModeFromCubit == ThemeMode.system
? MediaQuery.of(context).platformBrightness == Brightness.dark

View File

@ -440,8 +440,8 @@
},
"mobile": {
"personalInfo": "Personal Information",
"username": "Username",
"usernameEmptyError": "Username cannot be empty",
"username": "User Name",
"usernameEmptyError": "User name cannot be empty",
"about": "About",
"pushNotifications": "Push Notifications",
"support": "Support",