mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: mobile setting page, trash page and recent file UI with mock data (#3734)
This commit is contained in:
parent
d34eec1d99
commit
ad21a61ffb
@ -25,8 +25,8 @@ void main() {
|
||||
await tester.openSettingsPage(SettingsPage.user);
|
||||
await tester.tapButton(find.byType(SettingLogoutButton));
|
||||
|
||||
tester.expectToSeeText(LocaleKeys.button_OK.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_OK.tr());
|
||||
tester.expectToSeeText(LocaleKeys.button_ok.tr());
|
||||
await tester.tapButtonWithName(LocaleKeys.button_ok.tr());
|
||||
|
||||
// Go to the sign in page again
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
@ -236,7 +236,7 @@ extension CommonOperations on WidgetTester {
|
||||
final okButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(okButton);
|
||||
}
|
||||
|
@ -747,7 +747,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
@ -1432,7 +1432,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final field = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(field);
|
||||
}
|
||||
@ -1452,7 +1452,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
|
||||
final okButton = find.byWidgetPredicate(
|
||||
(widget) =>
|
||||
widget is PrimaryTextButton &&
|
||||
widget.label == LocaleKeys.button_OK.tr(),
|
||||
widget.label == LocaleKeys.button_ok.tr(),
|
||||
);
|
||||
await tapButton(okButton);
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ SPEC CHECKSUMS:
|
||||
SDWebImage: b9a731e1d6307f44ca703b3976d18c24ca561e84
|
||||
shared_preferences_foundation: e2dae3258e06f44cc55f49d42024fd8dd03c590c
|
||||
sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440
|
||||
super_native_extensions: 49e897b6039bb784226e7354e502a3c68e1659b5
|
||||
super_native_extensions: 4916b3c627a9c7fffdc48a23a9eca0b1ac228fa7
|
||||
SwiftyGif: 6c3eafd0ce693cad58bb63d2b2fb9bacb8552780
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
|
@ -1,48 +1,98 @@
|
||||
// ThemeData in mobile
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:flowy_infra/colorscheme/colorscheme.dart';
|
||||
import 'package:flowy_infra/size.dart';
|
||||
import 'package:flowy_infra/theme_extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const _primaryColor = Color(0xFF2DA2F6); //primary 100
|
||||
const _onBackgroundColor = Color(0xff2F3030); // text/title color
|
||||
const _onSurfaceColor = Color(0xff676666); // text/body color
|
||||
const _onSecondaryColor = Color(0xFFC5C7CB); // text/body2 color
|
||||
|
||||
// TODO(yijing): improve theme color before release
|
||||
ThemeData getMobileThemeData(
|
||||
Theme theme,
|
||||
Brightness brightness,
|
||||
FlowyColorScheme theme,
|
||||
String fontFamily,
|
||||
String monospaceFontFamily,
|
||||
) {
|
||||
const mobileColorTheme = ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xFF2DA2F6), //primary 100
|
||||
onPrimary: Colors.white,
|
||||
// TODO(yijing): add color later
|
||||
secondary: Colors.white,
|
||||
onSecondary: Colors.white,
|
||||
error: Color(0xffFB006D),
|
||||
onError: Color(0xffFB006D),
|
||||
background: Colors.white,
|
||||
onBackground: Color(0xff2F3030), // title text
|
||||
outline: Color(0xffBDC0C5), //caption
|
||||
//Snack bar
|
||||
surface: Colors.white,
|
||||
onSurface: Color(0xff2F3030), // title text
|
||||
);
|
||||
final mobileColorTheme = (brightness == Brightness.light)
|
||||
? ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: _primaryColor,
|
||||
onPrimary: Colors.white,
|
||||
// TODO(yijing): add color later
|
||||
secondary: Colors.white,
|
||||
onSecondary: _onSecondaryColor,
|
||||
error: const Color(0xffFB006D),
|
||||
onError: const Color(0xffFB006D),
|
||||
background: Colors.white,
|
||||
onBackground: _onBackgroundColor,
|
||||
outline: const Color(0xffBDC0C5), //caption
|
||||
outlineVariant: const Color(0xffCBD5E0).withOpacity(0.24),
|
||||
//Snack bar
|
||||
surface: Colors.white,
|
||||
onSurface: _onSurfaceColor, // text/body color
|
||||
)
|
||||
: ColorScheme(
|
||||
brightness: brightness,
|
||||
primary: _primaryColor,
|
||||
onPrimary: Colors.white,
|
||||
// TODO(yijing): add color later
|
||||
secondary: Colors.black,
|
||||
onSecondary: Colors.white,
|
||||
error: const Color(0xffFB006D),
|
||||
onError: const Color(0xffFB006D),
|
||||
background: const Color(0xff1C1C1E), // BG/Secondary color
|
||||
onBackground: Colors.white,
|
||||
outline: const Color(0xff96989C), //caption
|
||||
outlineVariant: Colors.black,
|
||||
//Snack bar
|
||||
surface: const Color(0xff2F3030),
|
||||
onSurface: const Color(0xffC5C6C7), // text/body color
|
||||
);
|
||||
|
||||
return ThemeData(
|
||||
// color
|
||||
primaryColor: mobileColorTheme.primary, //primary 100
|
||||
primaryColorLight: const Color(0xFF57B5F8), //primary 80
|
||||
dividerColor: mobileColorTheme.outline, //caption
|
||||
hintColor: mobileColorTheme.outline,
|
||||
disabledColor: mobileColorTheme.outline,
|
||||
scaffoldBackgroundColor: mobileColorTheme.background,
|
||||
appBarTheme: AppBarTheme(
|
||||
foregroundColor: mobileColorTheme.onBackground,
|
||||
backgroundColor: mobileColorTheme.background,
|
||||
elevation: 0,
|
||||
elevation: 80,
|
||||
centerTitle: false,
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'Poppins',
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.05,
|
||||
),
|
||||
shadowColor: mobileColorTheme.outlineVariant,
|
||||
),
|
||||
radioTheme: RadioThemeData(
|
||||
fillColor: MaterialStateProperty.resolveWith((states) {
|
||||
if (states.contains(MaterialState.selected)) {
|
||||
return mobileColorTheme.primary;
|
||||
}
|
||||
return mobileColorTheme.outline;
|
||||
}),
|
||||
),
|
||||
// button
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
fixedSize: MaterialStateProperty.all(const Size.fromHeight(48)),
|
||||
elevation: MaterialStateProperty.all(0),
|
||||
textStyle: MaterialStateProperty.all(
|
||||
const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
shadowColor: MaterialStateProperty.all(null),
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
@ -57,6 +107,12 @@ ThemeData getMobileThemeData(
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
textStyle: MaterialStateProperty.all(
|
||||
const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
mobileColorTheme.onBackground,
|
||||
),
|
||||
@ -81,10 +137,19 @@ ThemeData getMobileThemeData(
|
||||
),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
textStyle: MaterialStateProperty.all(
|
||||
const TextStyle(
|
||||
fontFamily: 'Poppins',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// text
|
||||
fontFamily: 'Poppins',
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
textTheme: TextTheme(
|
||||
displayLarge: const TextStyle(
|
||||
color: Color(0xFF57B5F8),
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
@ -92,7 +157,7 @@ ThemeData getMobileThemeData(
|
||||
letterSpacing: 0.16,
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
color: Color(0xff2F3030),
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.20,
|
||||
@ -100,33 +165,46 @@ ThemeData getMobileThemeData(
|
||||
),
|
||||
// H1 Semi 26
|
||||
displaySmall: TextStyle(
|
||||
color: Color(0xFF2F3030),
|
||||
fontSize: 26,
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.10,
|
||||
letterSpacing: 0.13,
|
||||
),
|
||||
// body2 14 Regular
|
||||
bodyMedium: TextStyle(
|
||||
color: Colors.black,
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
height: 1.20,
|
||||
letterSpacing: 0.07,
|
||||
),
|
||||
// blue text button
|
||||
// Trash empty title
|
||||
labelLarge: TextStyle(
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: -0.3,
|
||||
),
|
||||
// setting item title
|
||||
labelMedium: TextStyle(
|
||||
color: Color(0xFF2DA2F6),
|
||||
fontSize: 14,
|
||||
color: mobileColorTheme.onSurface,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.20,
|
||||
),
|
||||
// setting group title
|
||||
labelSmall: TextStyle(
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.05,
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
contentPadding: const EdgeInsets.all(8),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
width: 2,
|
||||
color: Color(0xFF2DA2F6), //primary 100
|
||||
color: _primaryColor,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
@ -138,13 +216,54 @@ ThemeData getMobileThemeData(
|
||||
borderSide: BorderSide(color: mobileColorTheme.error),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xffBDC0C5), //caption
|
||||
color: mobileColorTheme.outline,
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
),
|
||||
colorScheme: mobileColorTheme,
|
||||
extensions: [
|
||||
AFThemeExtension(
|
||||
warning: theme.yellow,
|
||||
success: theme.green,
|
||||
tint1: theme.tint1,
|
||||
tint2: theme.tint2,
|
||||
tint3: theme.tint3,
|
||||
tint4: theme.tint4,
|
||||
tint5: theme.tint5,
|
||||
tint6: theme.tint6,
|
||||
tint7: theme.tint7,
|
||||
tint8: theme.tint8,
|
||||
tint9: theme.tint9,
|
||||
textColor: theme.text,
|
||||
greyHover: theme.hoverBG1,
|
||||
greySelect: theme.bg3,
|
||||
lightGreyHover: theme.hoverBG3,
|
||||
toggleOffFill: theme.shader5,
|
||||
progressBarBGColor: theme.progressBarBGColor,
|
||||
toggleButtonBGColor: theme.toggleButtonBGColor,
|
||||
calendarWeekendBGColor: theme.calendarWeekendBGColor,
|
||||
gridRowCountColor: theme.gridRowCountColor,
|
||||
code: getFontStyle(
|
||||
fontFamily: monospaceFontFamily,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
callout: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
calloutBGColor: theme.hoverBG3,
|
||||
tableCellBGColor: theme.surface,
|
||||
caption: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontColor: theme.hint,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -50,16 +50,8 @@ enum MobilePaneActionType {
|
||||
onPressed: (context) {
|
||||
final viewBloc = context.read<ViewBloc>();
|
||||
final favoriteBloc = context.read<FavoriteBloc>();
|
||||
showModalBottomSheet(
|
||||
showMobileBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8.0),
|
||||
topRight: Radius.circular(8.0),
|
||||
),
|
||||
),
|
||||
builder: (context) {
|
||||
return MultiBlocProvider(
|
||||
providers: [
|
||||
|
@ -1,12 +1,32 @@
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/mobile_bottom_sheet_body.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/mobile_bottom_sheet_header.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/mobile_bottom_sheet_rename_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/mobile_bottom_sheet_view_item_header.dart';
|
||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart' hide WidgetBuilder;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
Future<void> showMobileBottomSheet({
|
||||
required BuildContext context,
|
||||
required WidgetBuilder builder,
|
||||
}) async {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
enableDrag: true,
|
||||
useSafeArea: true,
|
||||
shape: const RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: Radius.circular(8.0),
|
||||
topRight: Radius.circular(8.0),
|
||||
),
|
||||
),
|
||||
builder: builder,
|
||||
);
|
||||
}
|
||||
|
||||
enum MobileBottomSheetType {
|
||||
view,
|
||||
@ -16,10 +36,12 @@ enum MobileBottomSheetType {
|
||||
class MobileViewItemBottomSheet extends StatefulWidget {
|
||||
const MobileViewItemBottomSheet({
|
||||
super.key,
|
||||
required this.view,
|
||||
this.view,
|
||||
this.defaultType = MobileBottomSheetType.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
final ViewPB? view;
|
||||
final MobileBottomSheetType defaultType;
|
||||
|
||||
@override
|
||||
State<MobileViewItemBottomSheet> createState() =>
|
||||
@ -29,6 +51,18 @@ class MobileViewItemBottomSheet extends StatefulWidget {
|
||||
class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
MobileBottomSheetType type = MobileBottomSheetType.view;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
|
||||
type = widget.defaultType;
|
||||
|
||||
if ([MobileBottomSheetType.view, MobileBottomSheetType.rename]
|
||||
.contains(type)) {
|
||||
assert(widget.view != null);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
@ -48,30 +82,39 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
),
|
||||
|
||||
// header
|
||||
MobileBottomSheetHeader(
|
||||
showBackButton: type != MobileBottomSheetType.view,
|
||||
view: widget.view,
|
||||
onBack: () {
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.view;
|
||||
});
|
||||
},
|
||||
),
|
||||
_buildHeader(),
|
||||
const VSpace(8.0),
|
||||
const Divider(),
|
||||
|
||||
// body
|
||||
_buildBody(),
|
||||
const VSpace(12.0),
|
||||
const VSpace(24.0),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
case MobileBottomSheetType.rename:
|
||||
// header
|
||||
return MobileViewItemBottomSheetHeader(
|
||||
showBackButton: type != MobileBottomSheetType.view,
|
||||
view: widget.view!,
|
||||
onBack: () {
|
||||
setState(() {
|
||||
type = MobileBottomSheetType.view;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
switch (type) {
|
||||
case MobileBottomSheetType.view:
|
||||
return MobileViewItemBottomSheetBody(
|
||||
isFavorite: widget.view.isFavorite,
|
||||
isFavorite: widget.view!.isFavorite,
|
||||
onAction: (action) {
|
||||
switch (action) {
|
||||
case MobileViewItemBottomSheetBodyAction.rename:
|
||||
@ -81,34 +124,34 @@ class _MobileViewItemBottomSheetState extends State<MobileViewItemBottomSheet> {
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.share:
|
||||
// unimplemented
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.delete:
|
||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
break;
|
||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||
context
|
||||
.read<FavoriteBloc>()
|
||||
.add(FavoriteEvent.toggle(widget.view));
|
||||
Navigator.pop(context);
|
||||
.add(FavoriteEvent.toggle(widget.view!));
|
||||
context.pop();
|
||||
break;
|
||||
}
|
||||
},
|
||||
);
|
||||
case MobileBottomSheetType.rename:
|
||||
return MobileBottomSheetRenameWidget(
|
||||
name: widget.view.name,
|
||||
name: widget.view!.name,
|
||||
onRename: (name) {
|
||||
if (name != widget.view.name) {
|
||||
if (name != widget.view!.name) {
|
||||
context.read<ViewBloc>().add(ViewEvent.rename(name));
|
||||
}
|
||||
Navigator.pop(context);
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileBottomSheetHeader extends StatelessWidget {
|
||||
const MobileBottomSheetHeader({
|
||||
class MobileViewItemBottomSheetHeader extends StatelessWidget {
|
||||
const MobileViewItemBottomSheetHeader({
|
||||
super.key,
|
||||
required this.view,
|
||||
required this.showBackButton,
|
@ -1 +1,3 @@
|
||||
export 'mobile_home_page.dart';
|
||||
export 'mobile_home_setting_page.dart';
|
||||
export 'mobile_home_trash_page.dart';
|
||||
|
@ -1,3 +1,5 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/mobile_folders.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/mobile_home_page_header.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/mobile_home_page_recent_files.dart';
|
||||
@ -7,7 +9,11 @@ import 'package:appflowy/workspace/presentation/home/errors/workspace_failed_scr
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'home.dart';
|
||||
|
||||
class MobileHomeScreen extends StatelessWidget {
|
||||
const MobileHomeScreen({super.key});
|
||||
@ -87,6 +93,8 @@ class MobileHomePage extends StatelessWidget {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// Recent files
|
||||
const MobileHomePageRecentFilesWidget(),
|
||||
@ -101,15 +109,41 @@ class MobileHomePage extends StatelessWidget {
|
||||
workspaceSetting: workspaceSetting,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
const _TrashButton(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// TODO: Trash
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TrashButton extends StatelessWidget {
|
||||
const _TrashButton();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO(yijing): improve style UI later
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton.icon(
|
||||
onPressed: () {
|
||||
context.push(MobileHomeTrashPage.routeName);
|
||||
},
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_delete_m,
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
),
|
||||
label: Text(
|
||||
LocaleKeys.trash_text.tr(),
|
||||
style: Theme.of(context).textTheme.labelMedium,
|
||||
),
|
||||
style: const ButtonStyle(alignment: Alignment.centerLeft),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/home/mobile_home_setting_page.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class MobileHomePageHeader extends StatelessWidget {
|
||||
const MobileHomePageHeader({
|
||||
@ -12,6 +17,7 @@ class MobileHomePageHeader extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
// TODO: implement the details later.
|
||||
return SizedBox(
|
||||
height: 80,
|
||||
@ -22,27 +28,64 @@ class MobileHomePageHeader extends StatelessWidget {
|
||||
fontSize: 26,
|
||||
),
|
||||
const HSpace(14),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const FlowyText.medium(
|
||||
'AppFlowy',
|
||||
fontSize: 18,
|
||||
),
|
||||
const VSpace(4.0),
|
||||
FlowyText.regular(
|
||||
userProfile.email,
|
||||
fontSize: 12,
|
||||
color: Colors.grey,
|
||||
)
|
||||
],
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// TODO: replace with the real data
|
||||
Row(
|
||||
children: [
|
||||
const FlowyText.medium(
|
||||
'AppFlowy',
|
||||
fontSize: 18,
|
||||
),
|
||||
// temporary placeholder for log out icon button
|
||||
// needs to be replaced with workspace switcher and log out
|
||||
IconButton(
|
||||
onPressed: () => showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
title: const Text('Log out'),
|
||||
content:
|
||||
const Text('Are you sure you want to log out?'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context, 'Cancel'),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
await getIt<AuthService>().signOut();
|
||||
runAppFlowy();
|
||||
},
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.arrow_drop_down,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
FlowyText.regular(
|
||||
userProfile.email,
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.onSurface,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.more_horiz_outlined),
|
||||
onPressed: () {},
|
||||
),
|
||||
onPressed: () {
|
||||
context.push(MobileHomeSettingPage.routeName);
|
||||
},
|
||||
icon: const FlowySvg(
|
||||
FlowySvgs.m_setting_m,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -1,16 +1,26 @@
|
||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const rainbowColors = <Color>[
|
||||
Color(0xFFFF0064),
|
||||
Color(0xFFFF7600),
|
||||
Color(0xFFFFD500),
|
||||
Color(0xFF8CFE00),
|
||||
Color(0xFF00E86C),
|
||||
Color(0xFF00F4F2),
|
||||
Color(0xFF00CCFF),
|
||||
Color(0xFF70A2FF),
|
||||
Color(0xFFA96CFF),
|
||||
// TODO(yijing): replace by real data later
|
||||
class MockRecentFile {
|
||||
MockRecentFile({
|
||||
required this.title,
|
||||
});
|
||||
final String title;
|
||||
final String icon = '🐼';
|
||||
|
||||
final image = Image.asset(
|
||||
'assets/images/app_flowy_abstract_cover_1.jpg',
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
}
|
||||
|
||||
final recentFilesList = <MockRecentFile>[
|
||||
MockRecentFile(title: 'Work out plan'),
|
||||
MockRecentFile(title: 'Travel plan'),
|
||||
MockRecentFile(title: 'Meeting notes'),
|
||||
MockRecentFile(title: 'Recipes'),
|
||||
MockRecentFile(title: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'),
|
||||
];
|
||||
|
||||
class MobileHomePageRecentFilesWidget extends StatelessWidget {
|
||||
@ -20,7 +30,7 @@ class MobileHomePageRecentFilesWidget extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
// TODO: implement the details later.
|
||||
return SizedBox(
|
||||
height: 160,
|
||||
height: 168,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -33,20 +43,80 @@ class MobileHomePageRecentFilesWidget extends StatelessWidget {
|
||||
),
|
||||
Expanded(
|
||||
child: ListView.separated(
|
||||
separatorBuilder: (context, index) => const HSpace(20),
|
||||
separatorBuilder: (context, index) => const HSpace(8),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 10,
|
||||
vertical: 8,
|
||||
),
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: rainbowColors.length,
|
||||
itemCount: recentFilesList.length,
|
||||
itemBuilder: (context, index) {
|
||||
return Container(
|
||||
alignment: Alignment.center,
|
||||
width: 144,
|
||||
width: 120,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
color: rainbowColors[index],
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.outline
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
Align(
|
||||
alignment: Alignment.topCenter,
|
||||
child: SizedBox(
|
||||
height: 60,
|
||||
width: double.infinity,
|
||||
child: ClipRRect(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(8),
|
||||
topRight: Radius.circular(8),
|
||||
),
|
||||
child: recentFilesList[index].image,
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Container(
|
||||
height: 32,
|
||||
width: 32,
|
||||
margin: const EdgeInsets.only(left: 8),
|
||||
child: Text(
|
||||
recentFilesList[index].icon,
|
||||
style: const TextStyle(fontSize: 32),
|
||||
),
|
||||
),
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
height: 32,
|
||||
width: double.infinity,
|
||||
margin: const EdgeInsets.only(
|
||||
left: 8,
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
),
|
||||
child: Text(
|
||||
recentFilesList[index].title,
|
||||
softWrap: true,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onBackground,
|
||||
),
|
||||
maxLines: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -0,0 +1,57 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/presentation.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:flutter/material.dart';
|
||||
|
||||
class MobileHomeSettingPage extends StatefulWidget {
|
||||
const MobileHomeSettingPage({
|
||||
super.key,
|
||||
});
|
||||
|
||||
static const routeName = '/MobileHomeSettingPage';
|
||||
|
||||
@override
|
||||
State<MobileHomeSettingPage> createState() => _MobileHomeSettingPageState();
|
||||
}
|
||||
|
||||
class _MobileHomeSettingPageState extends State<MobileHomeSettingPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder(
|
||||
future: getIt<AuthService>().getUser(),
|
||||
builder: ((context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator.adaptive());
|
||||
}
|
||||
final userProfile = snapshot.data?.fold((error) => null, (userProfile) {
|
||||
return userProfile;
|
||||
});
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(LocaleKeys.settings_title.tr()),
|
||||
),
|
||||
body: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
children: [
|
||||
PersonalInfoSettingGroup(
|
||||
userProfile: userProfile,
|
||||
),
|
||||
// TODO(yijing): implement this along with Notification Page
|
||||
const NotificationsSettingGroup(),
|
||||
const AppearanceSettingGroup(),
|
||||
const SupportSettingGroup(),
|
||||
const AboutSettingGroup(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,185 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/mobile_bottom_sheet_action_widget.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
|
||||
|
||||
import 'package:appflowy/plugins/trash/application/prelude.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class MobileHomeTrashPage extends StatelessWidget {
|
||||
const MobileHomeTrashPage({super.key});
|
||||
|
||||
static const routeName = "/MobileHomeTrashPage";
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => getIt<TrashBloc>()..add(const TrashEvent.initial()),
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(LocaleKeys.trash_text.tr()),
|
||||
elevation: 0,
|
||||
actions: [
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
onPressed: () {
|
||||
showFlowyMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.trash_mobile_actions.tr(),
|
||||
builder: (_) => Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_restore_m,
|
||||
text: LocaleKeys.trash_restoreAll.tr(),
|
||||
onTap: () {
|
||||
context
|
||||
..read<TrashBloc>()
|
||||
.add(const TrashEvent.restoreAll())
|
||||
..pop();
|
||||
},
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: BottomSheetActionWidget(
|
||||
svg: FlowySvgs.m_delete_m,
|
||||
text: LocaleKeys.trash_deleteAll.tr(),
|
||||
onTap: () {
|
||||
context
|
||||
..read<TrashBloc>()
|
||||
.add(const TrashEvent.deleteAll())
|
||||
..pop();
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: BlocBuilder<TrashBloc, TrashState>(
|
||||
builder: (_, state) {
|
||||
if (state.objects.isEmpty) {
|
||||
return const _TrashEmptyPage();
|
||||
}
|
||||
return _DeletedFilesListView(state);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _DeletedFilesListView extends StatelessWidget {
|
||||
const _DeletedFilesListView(
|
||||
this.state,
|
||||
);
|
||||
|
||||
final TrashState state;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return ListView.builder(
|
||||
itemBuilder: (context, index) {
|
||||
final object = state.objects[index];
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
child: ListTile(
|
||||
// TODO(Yijing): implement file type after TrashPB has file type
|
||||
leading: FlowySvg(
|
||||
FlowySvgs.documents_s,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
title: Text(
|
||||
object.name,
|
||||
style: theme.textTheme.labelMedium
|
||||
?.copyWith(color: theme.colorScheme.onBackground),
|
||||
),
|
||||
horizontalTitleGap: 0,
|
||||
// TODO(yiing): needs improve by container/surface theme color
|
||||
tileColor: theme.colorScheme.onSurface.withOpacity(0.1),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// TODO(yijing): extract icon button
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_restore_m,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
onPressed: () {
|
||||
context
|
||||
.read<TrashBloc>()
|
||||
.add(TrashEvent.putback(object.id));
|
||||
},
|
||||
),
|
||||
IconButton(
|
||||
splashRadius: 20,
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_delete_m,
|
||||
size: const Size.square(24),
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
onPressed: () {
|
||||
context.read<TrashBloc>().add(TrashEvent.delete(object));
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
itemCount: state.objects.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _TrashEmptyPage extends StatelessWidget {
|
||||
const _TrashEmptyPage();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Text(
|
||||
'🗑️',
|
||||
style: TextStyle(fontSize: 40),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
LocaleKeys.trash_mobile_empty.tr(),
|
||||
style: theme.textTheme.labelLarge,
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
LocaleKeys.trash_mobile_emptyDescription.tr(),
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.hintColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -23,10 +23,6 @@ class MobileBottomNavigationBar extends StatelessWidget {
|
||||
bottomNavigationBar: BottomNavigationBar(
|
||||
showSelectedLabels: false,
|
||||
showUnselectedLabels: false,
|
||||
// Here, the items of BottomNavigationBar are hard coded. In a real
|
||||
// world scenario, the items would most likely be generated from the
|
||||
// branches of the shell route, which can be fetched using
|
||||
// `navigationShell.route.branches`.
|
||||
type: BottomNavigationBarType.fixed,
|
||||
items: <BottomNavigationBarItem>[
|
||||
BottomNavigationBarItem(
|
||||
@ -46,13 +42,6 @@ class MobileBottomNavigationBar extends StatelessWidget {
|
||||
blendMode: null,
|
||||
),
|
||||
),
|
||||
const BottomNavigationBarItem(
|
||||
label: 'add',
|
||||
icon: FlowySvg(
|
||||
FlowySvgs.m_add_circle_xl,
|
||||
blendMode: null,
|
||||
),
|
||||
),
|
||||
BottomNavigationBarItem(
|
||||
label: 'search',
|
||||
icon: const FlowySvg(FlowySvgs.m_search_lg),
|
||||
|
@ -3,3 +3,4 @@ export 'editor/mobile_editor_screen.dart';
|
||||
export 'home/home.dart';
|
||||
export 'mobile_bottom_navigation_bar.dart';
|
||||
export 'root_placeholder_page.dart';
|
||||
export 'setting/setting.dart';
|
||||
|
@ -0,0 +1,3 @@
|
||||
export 'about_setting_group.dart';
|
||||
export 'privacy_policy_page.dart';
|
||||
export 'user_agreement_page.dart';
|
@ -0,0 +1,41 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../widgets/widgets.dart';
|
||||
import 'about.dart';
|
||||
|
||||
class AboutSettingGroup extends StatelessWidget {
|
||||
const AboutSettingGroup({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileSettingGroup(
|
||||
groupTitle: LocaleKeys.settings_mobile_about.tr(),
|
||||
settingItemList: [
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_privacyPolicy.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () {
|
||||
context.push(PrivacyPolicyPage.routeName);
|
||||
},
|
||||
),
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_userAgreement.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () {
|
||||
context.push(UserAgreementPage.routeName);
|
||||
},
|
||||
),
|
||||
],
|
||||
showDivider: false,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PrivacyPolicyPage extends StatelessWidget {
|
||||
const PrivacyPolicyPage({super.key});
|
||||
|
||||
static const routeName = '/PrivacyPolicyPage';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO(yijing): implement page
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(LocaleKeys.settings_mobile_privacyPolicy.tr()),
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('🪜 Under construction'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class UserAgreementPage extends StatelessWidget {
|
||||
const UserAgreementPage({super.key});
|
||||
|
||||
static const routeName = '/UserAgreementPage';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO(yijing): implement page
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(LocaleKeys.settings_mobile_userAgreement.tr()),
|
||||
),
|
||||
body: const Center(
|
||||
child: Text('🪜 Under construction'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,110 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/show_flowy_mobile_bottom_sheet.dart';
|
||||
import 'package:appflowy/util/theme_mode_extension.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';
|
||||
import 'setting.dart';
|
||||
|
||||
class AppearanceSettingGroup extends StatefulWidget {
|
||||
const AppearanceSettingGroup({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<AppearanceSettingGroup> createState() => _AppearanceSettingGroupState();
|
||||
}
|
||||
|
||||
class _AppearanceSettingGroupState extends State<AppearanceSettingGroup> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocSelector<AppearanceSettingsCubit, AppearanceSettingsState,
|
||||
ThemeMode>(
|
||||
selector: (state) {
|
||||
return state.themeMode;
|
||||
},
|
||||
builder: (context, themeMode) {
|
||||
final theme = Theme.of(context);
|
||||
return MobileSettingGroup(
|
||||
groupTitle: LocaleKeys.settings_menu_appearance.tr(),
|
||||
settingItemList: [
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_appearance_themeMode_label.tr(),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
themeMode.labelText,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
const Icon(Icons.chevron_right)
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
showFlowyMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.settings_appearance_themeMode_label.tr(),
|
||||
builder: (_) {
|
||||
return Column(
|
||||
children: [
|
||||
_ThemeModeRadioListTile(
|
||||
title: LocaleKeys.settings_appearance_themeMode_system
|
||||
.tr(),
|
||||
value: ThemeMode.system,
|
||||
),
|
||||
_ThemeModeRadioListTile(
|
||||
title: LocaleKeys.settings_appearance_themeMode_light
|
||||
.tr(),
|
||||
value: ThemeMode.light,
|
||||
),
|
||||
_ThemeModeRadioListTile(
|
||||
title: LocaleKeys.settings_appearance_themeMode_dark
|
||||
.tr(),
|
||||
value: ThemeMode.dark,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ThemeModeRadioListTile extends StatelessWidget {
|
||||
const _ThemeModeRadioListTile({
|
||||
required this.title,
|
||||
required this.value,
|
||||
});
|
||||
final String title;
|
||||
final ThemeMode value;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return RadioListTile<ThemeMode>(
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.fromLTRB(0, 0, 4, 0),
|
||||
controlAffinity: ListTileControlAffinity.trailing,
|
||||
title: Text(
|
||||
title,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
),
|
||||
groupValue: context.read<AppearanceSettingsCubit>().state.themeMode,
|
||||
value: value,
|
||||
onChanged: (selectedThemeMode) {
|
||||
if (selectedThemeMode == null) return;
|
||||
context.read<AppearanceSettingsCubit>().setThemeMode(selectedThemeMode);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'widgets/widgets.dart';
|
||||
|
||||
class NotificationsSettingGroup extends StatefulWidget {
|
||||
const NotificationsSettingGroup({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<NotificationsSettingGroup> createState() =>
|
||||
_NotificationsSettingGroupState();
|
||||
}
|
||||
|
||||
class _NotificationsSettingGroupState extends State<NotificationsSettingGroup> {
|
||||
// TODO(yijing):remove this after notification page is implemented
|
||||
bool isPushNotificationOn = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return MobileSettingGroup(
|
||||
groupTitle: LocaleKeys.notificationHub_title.tr(),
|
||||
settingItemList: [
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_pushNotifications.tr(),
|
||||
trailing: Switch.adaptive(
|
||||
activeColor: theme.colorScheme.primary,
|
||||
value: isPushNotificationOn,
|
||||
onChanged: (bool value) {
|
||||
setState(() {
|
||||
isPushNotificationOn = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
class EditUsernameBottomSheet extends StatefulWidget {
|
||||
const EditUsernameBottomSheet(
|
||||
this.context, {
|
||||
this.userName,
|
||||
required this.onSubmitted,
|
||||
super.key,
|
||||
});
|
||||
final BuildContext context;
|
||||
final String? userName;
|
||||
final void Function(String) onSubmitted;
|
||||
@override
|
||||
State<EditUsernameBottomSheet> createState() =>
|
||||
_EditUsernameBottomSheetState();
|
||||
}
|
||||
|
||||
class _EditUsernameBottomSheetState extends State<EditUsernameBottomSheet> {
|
||||
late TextEditingController _textFieldController;
|
||||
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_textFieldController = TextEditingController(text: widget.userName);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_textFieldController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
void submitUserName() {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final value = _textFieldController.text;
|
||||
widget.onSubmitted.call(value);
|
||||
widget.context.pop();
|
||||
}
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 16,
|
||||
right: 16,
|
||||
left: 16,
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom + 32,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
LocaleKeys.settings_mobile_username.tr(),
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: theme.hintColor,
|
||||
),
|
||||
onPressed: () {
|
||||
widget.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: submitUserName,
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
onPressed: submitUserName,
|
||||
child: Text(LocaleKeys.button_update.tr()),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export 'edit_username_bottom_sheet.dart';
|
||||
export 'personal_info_setting_group.dart';
|
@ -0,0 +1,72 @@
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/user/prelude.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../widgets/widgets.dart';
|
||||
import 'personal_info.dart';
|
||||
|
||||
class PersonalInfoSettingGroup extends StatelessWidget {
|
||||
const PersonalInfoSettingGroup({
|
||||
super.key,
|
||||
required this.userProfile,
|
||||
});
|
||||
|
||||
final UserProfilePB? userProfile;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return BlocProvider<SettingsUserViewBloc>(
|
||||
create: (context) => getIt<SettingsUserViewBloc>(
|
||||
param1: userProfile,
|
||||
)..add(const SettingsUserEvent.initial()),
|
||||
child: BlocSelector<SettingsUserViewBloc, SettingsUserState, String>(
|
||||
selector: (state) => state.userProfile.name,
|
||||
builder: (context, userName) {
|
||||
return MobileSettingGroup(
|
||||
groupTitle: LocaleKeys.settings_mobile_personalInfo.tr(),
|
||||
settingItemList: [
|
||||
MobileSettingItem(
|
||||
name: userName,
|
||||
subtitle: isCloudEnabled && userProfile != null
|
||||
? Text(
|
||||
userProfile!.email,
|
||||
style: theme.textTheme.bodyMedium?.copyWith(
|
||||
color: theme.colorScheme.onSurface,
|
||||
),
|
||||
)
|
||||
: null,
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
showModalBottomSheet<void>(
|
||||
context: context,
|
||||
// avoid bottom sheet overflow from resizing when keyboard appears
|
||||
isScrollControlled: true,
|
||||
builder: (_) {
|
||||
return EditUsernameBottomSheet(
|
||||
context,
|
||||
userName: userName,
|
||||
onSubmitted: (value) {
|
||||
context.read<SettingsUserViewBloc>().add(
|
||||
SettingsUserEvent.updateUserName(
|
||||
value,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export 'personal_info/personal_info.dart';
|
||||
export 'notifications_setting_group.dart';
|
||||
export 'appearance_setting_group.dart';
|
||||
export 'support_setting_group.dart';
|
||||
export 'about/about.dart';
|
||||
export 'widgets/widgets.dart';
|
@ -0,0 +1,45 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'widgets/widgets.dart';
|
||||
|
||||
class SupportSettingGroup extends StatelessWidget {
|
||||
const SupportSettingGroup({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MobileSettingGroup(
|
||||
groupTitle: LocaleKeys.settings_mobile_support.tr(),
|
||||
settingItemList: [
|
||||
// 'Help Center'
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.settings_mobile_joinDiscord.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () => safeLaunchUrl('https://discord.gg/JucBXeU2FE'),
|
||||
),
|
||||
MobileSettingItem(
|
||||
name: LocaleKeys.workspace_errorActions_reportIssue.tr(),
|
||||
trailing: const Icon(
|
||||
Icons.chevron_right,
|
||||
),
|
||||
onTap: () {
|
||||
// TODO(yijing): get app version before release
|
||||
const String version = 'Beta';
|
||||
final String os = Platform.operatingSystem;
|
||||
safeLaunchUrl(
|
||||
'https://github.com/AppFlowy-IO/AppFlowy/issues/new?assignees=&labels=&projects=&template=bug_report.yaml&title=[Bug]%20Mobile:%20&version=$version&os=$os',
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'mobile_setting_item_widget.dart';
|
||||
|
||||
class MobileSettingGroup extends StatelessWidget {
|
||||
const MobileSettingGroup({
|
||||
required this.groupTitle,
|
||||
required this.settingItemList,
|
||||
this.showDivider = true,
|
||||
super.key,
|
||||
});
|
||||
final String groupTitle;
|
||||
final List<MobileSettingItem> settingItemList;
|
||||
final bool showDivider;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(
|
||||
height: 8,
|
||||
),
|
||||
Text(
|
||||
groupTitle,
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 12,
|
||||
),
|
||||
...settingItemList,
|
||||
showDivider ? const Divider() : const SizedBox.shrink(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MobileSettingItem extends StatelessWidget {
|
||||
const MobileSettingItem({
|
||||
super.key,
|
||||
required this.name,
|
||||
this.subtitle,
|
||||
required this.trailing,
|
||||
this.onTap,
|
||||
});
|
||||
final String name;
|
||||
final Widget? subtitle;
|
||||
final Widget trailing;
|
||||
final VoidCallback? onTap;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
name,
|
||||
style: theme.textTheme.labelMedium,
|
||||
),
|
||||
subtitle: subtitle,
|
||||
trailing: trailing,
|
||||
onTap: onTap,
|
||||
visualDensity: VisualDensity.compact,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
export 'mobile_setting_group_widget.dart';
|
||||
export 'mobile_setting_item_widget.dart';
|
@ -0,0 +1,54 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
Future<T?> showFlowyMobileBottomSheet<T>(
|
||||
BuildContext context, {
|
||||
required String title,
|
||||
required Widget Function(BuildContext) builder,
|
||||
}) async {
|
||||
return showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) => Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 16, 16, 32),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_BottomSheetTitle(title),
|
||||
const SizedBox(
|
||||
height: 16,
|
||||
),
|
||||
builder(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _BottomSheetTitle extends StatelessWidget {
|
||||
const _BottomSheetTitle(this.title);
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
title,
|
||||
style: theme.textTheme.labelSmall,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: theme.hintColor,
|
||||
),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -438,11 +438,11 @@ class DeleteImageAlertDialog extends StatelessWidget {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: const Text(LocaleKeys.button_Cancel).tr(),
|
||||
child: const Text(LocaleKeys.button_cancel).tr(),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: onSubmit,
|
||||
child: const Text(LocaleKeys.button_OK).tr(),
|
||||
child: const Text(LocaleKeys.button_ok).tr(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
@ -142,7 +142,7 @@ class _MathInputTextFieldState extends State<MathInputTextField> {
|
||||
),
|
||||
const HSpace(4.0),
|
||||
FlowyButton(
|
||||
text: FlowyText(LocaleKeys.button_Done.tr()),
|
||||
text: FlowyText(LocaleKeys.button_done.tr()),
|
||||
useIntrinsicWidth: true,
|
||||
onTap: () => widget.onSubmit(textEditingController.text),
|
||||
),
|
||||
|
@ -188,11 +188,11 @@ class _MathEquationBlockComponentWidgetState
|
||||
),
|
||||
actions: [
|
||||
SecondaryTextButton(
|
||||
LocaleKeys.button_Cancel.tr(),
|
||||
LocaleKeys.button_cancel.tr(),
|
||||
onPressed: () => dismiss(context),
|
||||
),
|
||||
PrimaryTextButton(
|
||||
LocaleKeys.button_Done.tr(),
|
||||
LocaleKeys.button_done.tr(),
|
||||
onPressed: () => updateMathEquation(controller.text, context),
|
||||
),
|
||||
],
|
||||
|
@ -500,7 +500,7 @@ class AutoCompletionInputFooter extends StatelessWidget {
|
||||
),
|
||||
const Space(10, 0),
|
||||
SecondaryTextButton(
|
||||
LocaleKeys.button_Cancel.tr(),
|
||||
LocaleKeys.button_cancel.tr(),
|
||||
onPressed: onExit,
|
||||
),
|
||||
Expanded(
|
||||
|
@ -20,7 +20,7 @@ class DiscardDialog extends StatelessWidget {
|
||||
return NavigatorOkCancelDialog(
|
||||
message: LocaleKeys.document_plugins_discardResponse.tr(),
|
||||
okTitle: LocaleKeys.button_discard.tr(),
|
||||
cancelTitle: LocaleKeys.button_Cancel.tr(),
|
||||
cancelTitle: LocaleKeys.button_cancel.tr(),
|
||||
onOkPressed: onConfirm,
|
||||
onCancelPressed: onCancel,
|
||||
);
|
||||
|
@ -326,7 +326,7 @@ class _SmartEditInputWidgetState extends State<SmartEditInputWidget> {
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: LocaleKeys.button_Cancel.tr(),
|
||||
text: LocaleKeys.button_cancel.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
],
|
||||
|
@ -30,11 +30,23 @@ GoRouter generateRouter(Widget child) {
|
||||
if (!PlatformExtension.isMobile) _desktopHomeScreenRoute(),
|
||||
// Mobile only
|
||||
if (PlatformExtension.isMobile) ...[
|
||||
// settings
|
||||
_mobileHomeSettingPageRoute(),
|
||||
_mobileSettingPrivacyPolicyPageRoute(),
|
||||
_mobileSettingUserAgreementPageRoute(),
|
||||
|
||||
// view page
|
||||
_mobileEditorScreenRoute(),
|
||||
_mobileGridScreenRoute(),
|
||||
_mobileBoardScreenRoute(),
|
||||
_mobileCalendarScreenRoute(),
|
||||
|
||||
// home
|
||||
// MobileHomeSettingPage is outside the bottom navigation bar, thus it is not in the StatefulShellRoute.
|
||||
_mobileHomeScreenWithNavigationBarRoute(),
|
||||
|
||||
// trash
|
||||
_mobileHomeTrashPageRoute(),
|
||||
],
|
||||
|
||||
// Desktop and Mobile
|
||||
@ -92,7 +104,6 @@ StatefulShellRoute _mobileHomeScreenWithNavigationBarRoute() {
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
StatefulShellBranch(
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
@ -103,32 +114,6 @@ StatefulShellRoute _mobileHomeScreenWithNavigationBarRoute() {
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
// The route branch for the third tab of the bottom navigation bar.
|
||||
StatefulShellBranch(
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
// The screen to display as the root in the third tab of the
|
||||
// bottom navigation bar.
|
||||
path: '/c',
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
const RootPlaceholderScreen(
|
||||
label: 'Add Document',
|
||||
detailsPath: '/c/details',
|
||||
),
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: 'details',
|
||||
builder: (BuildContext context, GoRouterState state) =>
|
||||
DetailsPlaceholderScreen(
|
||||
label: 'Add Document details',
|
||||
extra: state.extra,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
StatefulShellBranch(
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
@ -175,6 +160,46 @@ StatefulShellRoute _mobileHomeScreenWithNavigationBarRoute() {
|
||||
);
|
||||
}
|
||||
|
||||
GoRoute _mobileHomeSettingPageRoute() {
|
||||
return GoRoute(
|
||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||
path: MobileHomeSettingPage.routeName,
|
||||
pageBuilder: (context, state) {
|
||||
return const MaterialPage(child: MobileHomeSettingPage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
GoRoute _mobileSettingPrivacyPolicyPageRoute() {
|
||||
return GoRoute(
|
||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||
path: PrivacyPolicyPage.routeName,
|
||||
pageBuilder: (context, state) {
|
||||
return const MaterialPage(child: PrivacyPolicyPage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
GoRoute _mobileSettingUserAgreementPageRoute() {
|
||||
return GoRoute(
|
||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||
path: UserAgreementPage.routeName,
|
||||
pageBuilder: (context, state) {
|
||||
return const MaterialPage(child: UserAgreementPage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
GoRoute _mobileHomeTrashPageRoute() {
|
||||
return GoRoute(
|
||||
parentNavigatorKey: AppGlobals.rootNavKey,
|
||||
path: MobileHomeTrashPage.routeName,
|
||||
pageBuilder: (context, state) {
|
||||
return const MaterialPage(child: MobileHomeTrashPage());
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
GoRoute _desktopHomeScreenRoute() {
|
||||
return GoRoute(
|
||||
path: DesktopHomeScreen.routeName,
|
||||
|
@ -58,7 +58,9 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
// TODO(yijing): confirm the subtitle before release app
|
||||
Text(
|
||||
'You are in charge of your data and customizations.',
|
||||
style: style.textTheme.bodyMedium,
|
||||
style: style.textTheme.bodyMedium?.copyWith(
|
||||
color: style.colorScheme.onSecondary,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Spacer(
|
||||
@ -74,7 +76,9 @@ class MobileSignInScreen extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
child: Text(
|
||||
LocaleKeys.signIn_or.tr(),
|
||||
style: style.textTheme.bodyMedium,
|
||||
style: style.textTheme.bodyMedium?.copyWith(
|
||||
color: style.colorScheme.onSecondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
const Expanded(child: Divider()),
|
||||
|
@ -101,7 +101,10 @@ class _ThirdPartySignInButton extends StatelessWidget {
|
||||
label: Container(
|
||||
padding: const EdgeInsets.only(left: 4),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(labelText),
|
||||
child: Text(
|
||||
labelText,
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
onPressed: onPressed,
|
||||
),
|
||||
|
18
frontend/appflowy_flutter/lib/util/theme_mode_extension.dart
Normal file
18
frontend/appflowy_flutter/lib/util/theme_mode_extension.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
extension LabelTextPhrasing on ThemeMode {
|
||||
String get labelText {
|
||||
switch (this) {
|
||||
case (ThemeMode.light):
|
||||
return LocaleKeys.settings_appearance_themeMode_light.tr();
|
||||
case (ThemeMode.dark):
|
||||
return LocaleKeys.settings_appearance_themeMode_dark.tr();
|
||||
case (ThemeMode.system):
|
||||
return LocaleKeys.settings_appearance_themeMode_system.tr();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/mobile/application/mobile_theme_data.dart';
|
||||
import 'package:appflowy/user/application/user_settings_service.dart';
|
||||
import 'package:appflowy/util/platform_extension.dart';
|
||||
import 'package:appflowy/workspace/application/appearance_defaults.dart';
|
||||
@ -404,204 +405,27 @@ class AppearanceSettingsState with _$AppearanceSettingsState {
|
||||
if (PlatformExtension.isMobile) {
|
||||
// Mobile version has only one theme(light mode) for now.
|
||||
// The desktop theme and the mobile theme are independent.
|
||||
const mobileColorTheme = ColorScheme(
|
||||
brightness: Brightness.light,
|
||||
primary: Color(0xFF2DA2F6), //primary 100
|
||||
onPrimary: Colors.white,
|
||||
// TODO(yijing): add color later
|
||||
secondary: Colors.white,
|
||||
onSecondary: Colors.white,
|
||||
error: Color(0xffFB006D),
|
||||
onError: Color(0xffFB006D),
|
||||
background: Colors.white,
|
||||
onBackground: Color(0xff2F3030), // title text
|
||||
outline: Color(0xffBDC0C5), //caption
|
||||
//Snack bar
|
||||
surface: Colors.white,
|
||||
onSurface: Color(0xff2F3030), // title text
|
||||
);
|
||||
return ThemeData(
|
||||
// color
|
||||
primaryColor: mobileColorTheme.primary, //primary 100
|
||||
primaryColorLight: const Color(0xFF57B5F8), //primary 80
|
||||
dividerColor: mobileColorTheme.outline, //caption
|
||||
scaffoldBackgroundColor: mobileColorTheme.background,
|
||||
appBarTheme: AppBarTheme(
|
||||
foregroundColor: mobileColorTheme.onBackground,
|
||||
backgroundColor: mobileColorTheme.background,
|
||||
elevation: 0,
|
||||
centerTitle: false,
|
||||
titleTextStyle: TextStyle(
|
||||
fontFamily: 'Poppins',
|
||||
color: mobileColorTheme.onBackground,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.05,
|
||||
),
|
||||
),
|
||||
// button
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
elevation: MaterialStateProperty.all(0),
|
||||
shadowColor: MaterialStateProperty.all(null),
|
||||
backgroundColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(Set<MaterialState> states) {
|
||||
if (states.contains(MaterialState.disabled)) {
|
||||
return const Color(0xFF57B5F8);
|
||||
}
|
||||
return mobileColorTheme.primary;
|
||||
},
|
||||
),
|
||||
foregroundColor: MaterialStateProperty.all(Colors.white),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.all(
|
||||
mobileColorTheme.onBackground,
|
||||
),
|
||||
backgroundColor: MaterialStateProperty.all(Colors.white),
|
||||
shape: MaterialStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
),
|
||||
side: MaterialStateProperty.all(
|
||||
BorderSide(
|
||||
color: mobileColorTheme.outline,
|
||||
width: 0.5,
|
||||
),
|
||||
),
|
||||
padding: MaterialStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 16),
|
||||
),
|
||||
// splash color
|
||||
overlayColor: MaterialStateProperty.all(
|
||||
Colors.grey[100],
|
||||
),
|
||||
),
|
||||
),
|
||||
// text
|
||||
fontFamily: 'Poppins',
|
||||
textTheme: const TextTheme(
|
||||
displayLarge: TextStyle(
|
||||
color: Color(0xFF57B5F8),
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w700,
|
||||
height: 1.20,
|
||||
letterSpacing: 0.16,
|
||||
),
|
||||
displayMedium: TextStyle(
|
||||
color: Color(0xff2F3030),
|
||||
fontSize: 32,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.20,
|
||||
letterSpacing: 0.16,
|
||||
),
|
||||
// H1 Semi 26
|
||||
displaySmall: TextStyle(
|
||||
color: Color(0xFF2F3030),
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.10,
|
||||
letterSpacing: 0.13,
|
||||
),
|
||||
// body2 14 Regular
|
||||
bodyMedium: TextStyle(
|
||||
color: Colors.black,
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w400,
|
||||
height: 1.20,
|
||||
letterSpacing: 0.07,
|
||||
),
|
||||
// blue text button
|
||||
labelMedium: TextStyle(
|
||||
color: Color(0xFF2DA2F6),
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
height: 1.20,
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
width: 2,
|
||||
color: Color(0xFF2DA2F6), //primary 100
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: mobileColorTheme.error),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: mobileColorTheme.error),
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(
|
||||
color: Color(0xffBDC0C5), //caption
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
),
|
||||
colorScheme: mobileColorTheme,
|
||||
extensions: [
|
||||
AFThemeExtension(
|
||||
warning: theme.yellow,
|
||||
success: theme.green,
|
||||
tint1: theme.tint1,
|
||||
tint2: theme.tint2,
|
||||
tint3: theme.tint3,
|
||||
tint4: theme.tint4,
|
||||
tint5: theme.tint5,
|
||||
tint6: theme.tint6,
|
||||
tint7: theme.tint7,
|
||||
tint8: theme.tint8,
|
||||
tint9: theme.tint9,
|
||||
textColor: theme.text,
|
||||
greyHover: theme.hoverBG1,
|
||||
greySelect: theme.bg3,
|
||||
lightGreyHover: theme.hoverBG3,
|
||||
toggleOffFill: theme.shader5,
|
||||
progressBarBGColor: theme.progressBarBGColor,
|
||||
toggleButtonBGColor: theme.toggleButtonBGColor,
|
||||
calendarWeekendBGColor: theme.calendarWeekendBGColor,
|
||||
gridRowCountColor: theme.gridRowCountColor,
|
||||
code: _getFontStyle(
|
||||
fontFamily: monospaceFontFamily,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
callout: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
calloutBGColor: theme.hoverBG3,
|
||||
tableCellBGColor: theme.surface,
|
||||
caption: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontWeight: FontWeight.w400,
|
||||
fontColor: theme.hint,
|
||||
),
|
||||
),
|
||||
],
|
||||
final mobileThemeData = getMobileThemeData(
|
||||
brightness,
|
||||
theme,
|
||||
fontFamily,
|
||||
monospaceFontFamily,
|
||||
);
|
||||
return mobileThemeData;
|
||||
}
|
||||
|
||||
// Due to Desktop version has multiple themes, it relies on the current theme to build the ThemeData
|
||||
final desktopThemeData = ThemeData(
|
||||
brightness: brightness,
|
||||
dialogBackgroundColor: theme.surface,
|
||||
textTheme: _getTextTheme(fontFamily: fontFamily, fontColor: theme.text),
|
||||
textTheme: getTextTheme(fontFamily: fontFamily, fontColor: theme.text),
|
||||
textSelectionTheme: TextSelectionThemeData(
|
||||
cursorColor: theme.main2,
|
||||
selectionHandleColor: theme.main2,
|
||||
),
|
||||
iconTheme: IconThemeData(color: theme.icon),
|
||||
tooltipTheme: TooltipThemeData(
|
||||
textStyle: _getFontStyle(
|
||||
textStyle: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontWeight: FontWeight.w400,
|
||||
@ -664,18 +488,18 @@ class AppearanceSettingsState with _$AppearanceSettingsState {
|
||||
toggleButtonBGColor: theme.toggleButtonBGColor,
|
||||
calendarWeekendBGColor: theme.calendarWeekendBGColor,
|
||||
gridRowCountColor: theme.gridRowCountColor,
|
||||
code: _getFontStyle(
|
||||
code: getFontStyle(
|
||||
fontFamily: monospaceFontFamily,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
callout: _getFontStyle(
|
||||
callout: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontColor: theme.shader3,
|
||||
),
|
||||
calloutBGColor: theme.hoverBG3,
|
||||
tableCellBGColor: theme.surface,
|
||||
caption: _getFontStyle(
|
||||
caption: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s11,
|
||||
fontWeight: FontWeight.w400,
|
||||
@ -686,90 +510,90 @@ class AppearanceSettingsState with _$AppearanceSettingsState {
|
||||
);
|
||||
return desktopThemeData;
|
||||
}
|
||||
}
|
||||
|
||||
TextStyle _getFontStyle({
|
||||
required String fontFamily,
|
||||
double? fontSize,
|
||||
FontWeight? fontWeight,
|
||||
Color? fontColor,
|
||||
double? letterSpacing,
|
||||
double? lineHeight,
|
||||
}) {
|
||||
try {
|
||||
return GoogleFonts.getFont(
|
||||
fontFamily,
|
||||
fontSize: fontSize ?? FontSizes.s12,
|
||||
color: fontColor,
|
||||
fontWeight: fontWeight ?? FontWeight.w500,
|
||||
letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005),
|
||||
height: lineHeight,
|
||||
);
|
||||
} catch (e) {
|
||||
return TextStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: fontSize ?? FontSizes.s12,
|
||||
color: fontColor,
|
||||
fontWeight: fontWeight ?? FontWeight.w500,
|
||||
fontFamilyFallback: const ["Noto Color Emoji"],
|
||||
letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005),
|
||||
height: lineHeight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TextTheme _getTextTheme({
|
||||
required String fontFamily,
|
||||
required Color fontColor,
|
||||
}) {
|
||||
return TextTheme(
|
||||
displayLarge: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s32,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
lineHeight: 42.0,
|
||||
), // h2
|
||||
displayMedium: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s24,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
lineHeight: 34.0,
|
||||
), // h3
|
||||
displaySmall: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s20,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
lineHeight: 28.0,
|
||||
), // h4
|
||||
titleLarge: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s18,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
), // title
|
||||
titleMedium: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s16,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
), // heading
|
||||
titleSmall: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s14,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
), // subheading
|
||||
bodyMedium: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontColor: fontColor,
|
||||
), // body-regular
|
||||
bodySmall: _getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
), // body-thin
|
||||
TextStyle getFontStyle({
|
||||
required String fontFamily,
|
||||
double? fontSize,
|
||||
FontWeight? fontWeight,
|
||||
Color? fontColor,
|
||||
double? letterSpacing,
|
||||
double? lineHeight,
|
||||
}) {
|
||||
try {
|
||||
return GoogleFonts.getFont(
|
||||
fontFamily,
|
||||
fontSize: fontSize ?? FontSizes.s12,
|
||||
color: fontColor,
|
||||
fontWeight: fontWeight ?? FontWeight.w500,
|
||||
letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005),
|
||||
height: lineHeight,
|
||||
);
|
||||
} catch (e) {
|
||||
return TextStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: fontSize ?? FontSizes.s12,
|
||||
color: fontColor,
|
||||
fontWeight: fontWeight ?? FontWeight.w500,
|
||||
fontFamilyFallback: const ["Noto Color Emoji"],
|
||||
letterSpacing: (fontSize ?? FontSizes.s12) * (letterSpacing ?? 0.005),
|
||||
height: lineHeight,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TextTheme getTextTheme({
|
||||
required String fontFamily,
|
||||
required Color fontColor,
|
||||
}) {
|
||||
return TextTheme(
|
||||
displayLarge: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s32,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
lineHeight: 42.0,
|
||||
), // h2
|
||||
displayMedium: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s24,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
lineHeight: 34.0,
|
||||
), // h3
|
||||
displaySmall: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s20,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
lineHeight: 28.0,
|
||||
), // h4
|
||||
titleLarge: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s18,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
), // title
|
||||
titleMedium: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s16,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
), // heading
|
||||
titleSmall: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontSize: FontSizes.s14,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w600,
|
||||
), // subheading
|
||||
bodyMedium: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontColor: fontColor,
|
||||
), // body-regular
|
||||
bodySmall: getFontStyle(
|
||||
fontFamily: fontFamily,
|
||||
fontColor: fontColor,
|
||||
fontWeight: FontWeight.w400,
|
||||
), // body-thin
|
||||
);
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ void showSnackBarMessage(
|
||||
action: !showCancel
|
||||
? null
|
||||
: SnackBarAction(
|
||||
label: LocaleKeys.button_Cancel.tr(),
|
||||
label: LocaleKeys.button_cancel.tr(),
|
||||
textColor: Theme.of(context).colorScheme.onSurface,
|
||||
onPressed: () {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
|
@ -2,6 +2,7 @@ import 'dart:io';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/util/theme_mode_extension.dart';
|
||||
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
@ -23,7 +24,7 @@ class BrightnessSetting extends StatelessWidget {
|
||||
onResetRequested: context.read<AppearanceSettingsCubit>().resetThemeMode,
|
||||
trailing: [
|
||||
ThemeValueDropDown(
|
||||
currentValue: _themeModeLabelText(currentThemeMode),
|
||||
currentValue: currentThemeMode.labelText,
|
||||
popupBuilder: (context) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
@ -47,7 +48,7 @@ class BrightnessSetting extends StatelessWidget {
|
||||
return SizedBox(
|
||||
height: 32,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.medium(_themeModeLabelText(themeMode)),
|
||||
text: FlowyText.medium(themeMode.labelText),
|
||||
rightIcon: currentThemeMode == themeMode
|
||||
? const FlowySvg(
|
||||
FlowySvgs.check_s,
|
||||
@ -62,17 +63,4 @@ class BrightnessSetting extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _themeModeLabelText(ThemeMode themeMode) {
|
||||
switch (themeMode) {
|
||||
case (ThemeMode.light):
|
||||
return LocaleKeys.settings_appearance_themeMode_light.tr();
|
||||
case (ThemeMode.dark):
|
||||
return LocaleKeys.settings_appearance_themeMode_dark.tr();
|
||||
case (ThemeMode.system):
|
||||
return LocaleKeys.settings_appearance_themeMode_system.tr();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,14 +91,14 @@ class _FileExporterWidgetState extends State<FileExporterWidget> {
|
||||
children: [
|
||||
const Spacer(),
|
||||
FlowyTextButton(
|
||||
LocaleKeys.button_Cancel.tr(),
|
||||
LocaleKeys.button_cancel.tr(),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
const HSpace(8),
|
||||
FlowyTextButton(
|
||||
LocaleKeys.button_OK.tr(),
|
||||
LocaleKeys.button_ok.tr(),
|
||||
onPressed: () async {
|
||||
await getIt<FilePickerService>()
|
||||
.getDirectoryPath()
|
||||
|
@ -36,7 +36,7 @@ class ThemeConfirmDeleteDialog extends StatelessWidget {
|
||||
width: ThemeUploadWidget.buttonSize.width,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.semibold(
|
||||
LocaleKeys.button_OK.tr(),
|
||||
LocaleKeys.button_ok.tr(),
|
||||
fontSize: ThemeUploadWidget.buttonFontSize,
|
||||
),
|
||||
onTap: () => onConfirm(context),
|
||||
@ -46,7 +46,7 @@ class ThemeConfirmDeleteDialog extends StatelessWidget {
|
||||
width: ThemeUploadWidget.buttonSize.width,
|
||||
child: FlowyButton(
|
||||
text: FlowyText.semibold(
|
||||
LocaleKeys.button_Cancel.tr(),
|
||||
LocaleKeys.button_cancel.tr(),
|
||||
fontSize: ThemeUploadWidget.buttonFontSize,
|
||||
),
|
||||
onTap: () => onCancel(context),
|
||||
|
@ -245,14 +245,14 @@ class OkCancelButton extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
if (onCancelPressed != null)
|
||||
SecondaryTextButton(
|
||||
cancelTitle ?? LocaleKeys.button_Cancel.tr(),
|
||||
cancelTitle ?? LocaleKeys.button_cancel.tr(),
|
||||
onPressed: onCancelPressed,
|
||||
mode: mode,
|
||||
),
|
||||
HSpace(Insets.m),
|
||||
if (onOkPressed != null)
|
||||
PrimaryTextButton(
|
||||
okTitle ?? LocaleKeys.button_OK.tr(),
|
||||
okTitle ?? LocaleKeys.button_ok.tr(),
|
||||
onPressed: onOkPressed,
|
||||
mode: mode,
|
||||
),
|
||||
|
3
frontend/resources/flowy_icons/24x/m_restore.svg
Normal file
3
frontend/resources/flowy_icons/24x/m_restore.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.9949 18.3967L2.2759 13.7097C1.8859 13.3187 1.8859 12.6627 2.2759 12.2717L6.9949 7.58471L8.4009 8.99072L5.43189 11.9907L16.9639 11.9597C18.0879 11.9597 18.9949 11.0777 18.9949 9.99072V4.99072C18.9949 4.43872 19.4429 3.99072 19.9949 3.99072C20.5469 3.99072 20.9949 4.43872 20.9949 4.99072V9.99072C20.9949 12.1947 19.1779 13.9597 16.9639 13.9597L5.43189 13.9907L8.4009 16.9907L6.9949 18.3967Z" fill="#2F3030"/>
|
||||
</svg>
|
After Width: | Height: | Size: 523 B |
@ -57,7 +57,7 @@
|
||||
"notFoundError": "Workspace not found",
|
||||
"failedToLoad": "Something went wrong! Failed to load the workspace. Try to close any open instance of AppFlowy and try again.",
|
||||
"errorActions": {
|
||||
"reportIssue": "Report issue",
|
||||
"reportIssue": "Report an issue",
|
||||
"reachOut": "Reach out on Discord"
|
||||
}
|
||||
},
|
||||
@ -116,6 +116,11 @@
|
||||
"confirmRestoreAll": {
|
||||
"title": "Are you sure to restore all pages in Trash?",
|
||||
"caption": "This action cannot be undone."
|
||||
},
|
||||
"mobile": {
|
||||
"actions": "Trash Actions",
|
||||
"empty": "Trash Bin is Empty",
|
||||
"emptyDescription": "You don't have any deleted file"
|
||||
}
|
||||
},
|
||||
"deletePagePrompt": {
|
||||
@ -194,9 +199,9 @@
|
||||
"editContact": "Edit Contact"
|
||||
},
|
||||
"button": {
|
||||
"OK": "OK",
|
||||
"Done": "Done",
|
||||
"Cancel": "Cancel",
|
||||
"ok": "OK",
|
||||
"done": "Done",
|
||||
"cancel": "Cancel",
|
||||
"signIn": "Sign In",
|
||||
"signOut": "Sign Out",
|
||||
"complete": "Complete",
|
||||
@ -212,8 +217,8 @@
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"duplicate": "Duplicate",
|
||||
"done": "Done",
|
||||
"putback": "Put Back",
|
||||
"update": "Update",
|
||||
"share": "Share",
|
||||
"removeFromFavorites": "Remove from favorites",
|
||||
"addToFavorites": "Add to favorites",
|
||||
@ -378,6 +383,17 @@
|
||||
"resetToDefault": "Reset to default keybindings",
|
||||
"couldNotLoadErrorMsg": "Could not load shortcuts, Try again",
|
||||
"couldNotSaveErrorMsg": "Could not save shortcuts, Try again"
|
||||
},
|
||||
"mobile": {
|
||||
"personalInfo": "Personal Information",
|
||||
"username": "Username",
|
||||
"usernameEmptyError": "Username cannot be empty",
|
||||
"about": "About",
|
||||
"pushNotifications": "Push Notifications",
|
||||
"support": "Support",
|
||||
"joinDiscord": "Join us in Discord",
|
||||
"privacyPolicy": "Privacy Policy",
|
||||
"userAgreement": "User Agreement"
|
||||
}
|
||||
},
|
||||
"grid": {
|
||||
|
Loading…
Reference in New Issue
Block a user