fix: launch review improvements (#5263)

* fix: recent views fix

* fix: text button font color

* fix: sidebar scrollbar inset

* fix: account settings launch review

* fix: open + menu on mobile provider issue

* fix: code review

* fix: push view improvement
This commit is contained in:
Mathias Mogensen 2024-05-06 12:13:17 +02:00 committed by GitHub
parent 3bdc8c222c
commit c4f6703b76
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 271 additions and 365 deletions

View File

@ -1,5 +1,6 @@
import 'dart:convert';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/mobile/presentation/database/board/mobile_board_screen.dart';
@ -19,6 +20,7 @@ extension MobileRouter on BuildContext {
queryParameters: view.queryParameters(arguments),
).toString(),
).then((value) {
getIt<MenuSharedState>().latestOpenView = view;
getIt<CachedRecentService>().updateRecentViews([view.id], true);
});
}

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/mobile/presentation/home/favorite_folder/mobile_home_favorite_folder.dart';
@ -8,7 +10,6 @@ import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
@ -28,23 +29,15 @@ class MobileFavoritePageFolder extends StatelessWidget {
providers: [
BlocProvider(
create: (_) => SidebarSectionsBloc()
..add(
SidebarSectionsEvent.initial(
userProfile,
workspaceId,
),
),
..add(SidebarSectionsEvent.initial(userProfile, workspaceId)),
),
BlocProvider(
create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()),
),
],
child: BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
listener: (context, state) {
context.read<FavoriteBloc>().add(
const FavoriteEvent.initial(),
);
},
listener: (context, state) =>
context.read<FavoriteBloc>().add(const FavoriteEvent.initial()),
child: MultiBlocListener(
listeners: [
BlocListener<SidebarSectionsBloc, SidebarSectionsState>(

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart';
import 'package:appflowy/mobile/presentation/home/favorite_folder/mobile_home_favorite_folder_header.dart';
@ -5,7 +7,6 @@ import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MobileFavoriteFolder extends StatelessWidget {
@ -62,9 +63,7 @@ class MobileFavoriteFolder extends StatelessWidget {
isFeedback: false,
view: view,
level: 0,
onSelected: (view) async {
await context.pushView(view);
},
onSelected: context.pushView,
endActionPane: (context) => buildEndActionPane(context, [
view.isFavorite
? MobilePaneActionType.removeFromFavorites

View File

@ -24,16 +24,16 @@ class _MobileRecentFolderState extends State<MobileRecentFolder> {
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => RecentViewsBloc()
..add(
const RecentViewsEvent.initial(),
),
create: (context) =>
RecentViewsBloc()..add(const RecentViewsEvent.initial()),
child: BlocListener<UserWorkspaceBloc, UserWorkspaceState>(
listener: (context, state) {
context.read<RecentViewsBloc>().add(
const RecentViewsEvent.fetchRecentViews(),
);
},
listenWhen: (previous, current) =>
current.currentWorkspace != null &&
previous.currentWorkspace?.workspaceId !=
current.currentWorkspace!.workspaceId,
listener: (context, state) => context
.read<RecentViewsBloc>()
.add(const RecentViewsEvent.resetRecentViews()),
child: BlocBuilder<RecentViewsBloc, RecentViewsState>(
builder: (context, state) {
final ids = <String>{};

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart';
@ -9,7 +11,6 @@ import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MobileSectionFolder extends StatelessWidget {
@ -71,9 +72,7 @@ class MobileSectionFolder extends StatelessWidget {
level: 0,
leftPadding: 16,
isFeedback: false,
onSelected: (view) async {
await context.pushView(view);
},
onSelected: context.pushView,
endActionPane: (context) {
final view = context.read<ViewBloc>().state.view;
return buildEndActionPane(context, [

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
@ -10,7 +12,6 @@ import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_it
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_slidable/flutter_slidable.dart';

View File

@ -11,10 +11,7 @@ import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
class SettingButton extends StatefulWidget {
const SettingButton({
super.key,
required this.databaseController,
});
const SettingButton({super.key, required this.databaseController});
final DatabaseController databaseController;
@ -44,9 +41,8 @@ class _SettingButtonState extends State<SettingButton> {
radius: Corners.s4Border,
onPressed: _popoverController.show,
),
popupBuilder: (BuildContext context) => DatabaseSettingsList(
databaseController: widget.databaseController,
),
popupBuilder: (_) =>
DatabaseSettingsList(databaseController: widget.databaseController),
);
}
}

View File

@ -325,24 +325,20 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
final editorState = widget.editorState;
if (PlatformExtension.isMobile) {
return BlocProvider.value(
value: documentBloc,
child: AppFlowyMobileToolbar(
toolbarHeight: 42.0,
return AppFlowyMobileToolbar(
toolbarHeight: 42.0,
editorState: editorState,
toolbarItemsBuilder: (sel) => buildMobileToolbarItems(editorState, sel),
child: MobileFloatingToolbar(
editorState: editorState,
toolbarItemsBuilder: (selection) =>
buildMobileToolbarItems(editorState, selection),
child: MobileFloatingToolbar(
editorScrollController: editorScrollController,
toolbarBuilder: (_, anchor, closeToolbar) =>
CustomMobileFloatingToolbar(
editorState: editorState,
editorScrollController: editorScrollController,
toolbarBuilder: (_, anchor, closeToolbar) =>
CustomMobileFloatingToolbar(
editorState: editorState,
anchor: anchor,
closeToolbar: closeToolbar,
),
child: editor,
anchor: anchor,
closeToolbar: closeToolbar,
),
child: editor,
),
);
}

View File

@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:appflowy/plugins/base/emoji/emoji_picker_screen.dart';
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
class EmojiPickerButton extends StatelessWidget {
@ -41,7 +42,7 @@ class EmojiPickerButton extends StatelessWidget {
),
offset: offset,
direction: direction ?? PopoverDirection.rightWithTopAligned,
popupBuilder: (context) => Container(
popupBuilder: (_) => Container(
width: emojiPickerSize.width,
height: emojiPickerSize.height,
padding: const EdgeInsets.all(4.0),
@ -54,7 +55,7 @@ class EmojiPickerButton extends StatelessWidget {
? FlowyButton(
useIntrinsicWidth: true,
text: defaultIcon!,
onTap: () => popoverController.show(),
onTap: popoverController.show,
)
: FlowyTextButton(
emoji,
@ -64,37 +65,29 @@ class EmojiPickerButton extends StatelessWidget {
constraints: const BoxConstraints(minWidth: 35.0),
fillColor: Colors.transparent,
mainAxisAlignment: MainAxisAlignment.center,
onPressed: () {
popoverController.show();
},
onPressed: popoverController.show,
),
);
} else {
return FlowyTextButton(
emoji,
overflow: TextOverflow.visible,
fontSize: emojiSize,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(minWidth: 35.0),
fillColor: Colors.transparent,
mainAxisAlignment: MainAxisAlignment.center,
onPressed: () async {
final result = await context.push<EmojiPickerResult>(
Uri(
path: MobileEmojiPickerScreen.routeName,
queryParameters: {
MobileEmojiPickerScreen.pageTitle: title,
},
).toString(),
);
if (result != null) {
onSubmitted(
result.emoji,
null,
);
}
},
);
}
return FlowyTextButton(
emoji,
overflow: TextOverflow.visible,
fontSize: emojiSize,
padding: EdgeInsets.zero,
constraints: const BoxConstraints(minWidth: 35.0),
fillColor: Colors.transparent,
mainAxisAlignment: MainAxisAlignment.center,
onPressed: () async {
final result = await context.push<EmojiPickerResult>(
Uri(
path: MobileEmojiPickerScreen.routeName,
queryParameters: {MobileEmojiPickerScreen.pageTitle: title},
).toString(),
);
if (result != null) {
onSubmitted(result.emoji, null);
}
},
);
}
}

View File

@ -16,11 +16,11 @@ class MobileCodeLanguagePickerScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: FlowyAppBar(
titleText: LocaleKeys.titleBar_language.tr(),
),
appBar: FlowyAppBar(titleText: LocaleKeys.titleBar_language.tr()),
body: SafeArea(
child: ListView.separated(
separatorBuilder: (_, __) => const Divider(),
itemCount: defaultCodeBlockSupportedLanguages.length,
itemBuilder: (context, index) {
final language = defaultCodeBlockSupportedLanguages[index];
return SizedBox(
@ -35,8 +35,6 @@ class MobileCodeLanguagePickerScreen extends StatelessWidget {
),
);
},
separatorBuilder: (_, __) => const Divider(),
itemCount: defaultCodeBlockSupportedLanguages.length,
),
),
);

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart';
@ -6,7 +8,6 @@ import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
final customizeFontToolbarItem = ToolbarItem(
id: 'editor.font',
@ -34,9 +35,8 @@ final customizeFontToolbarItem = ToolbarItem(
Log.error('Failed to set font family: $e');
}
},
onResetFont: () async => editorState.formatDelta(selection, {
AppFlowyRichTextKeys.fontFamily: null,
}),
onResetFont: () async => editorState
.formatDelta(selection, {AppFlowyRichTextKeys.fontFamily: null}),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: FlowyTooltip(

View File

@ -6,7 +6,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/type_option_menu_item.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/plugins/document/application/document_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/image/image_placeholder.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
@ -14,11 +13,12 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mo
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_item/mobile_add_block_toolbar_item.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/aa_menu/_toolbar_theme.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/startup/tasks/app_widget.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor_plugins/appflowy_editor_plugins.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
final addBlockToolbarItem = AppFlowyMobileToolbarItem(
@ -45,7 +45,6 @@ final addBlockToolbarItem = AppFlowyMobileToolbarItem(
keepEditorFocusNotifier.increase();
final didAddBlock = await showAddBlockMenu(
AppGlobals.rootNavKey.currentContext!,
documentBloc: context.read<DocumentBloc>(),
editorState: editorState,
selection: selection!,
);
@ -60,33 +59,25 @@ final addBlockToolbarItem = AppFlowyMobileToolbarItem(
Future<bool?> showAddBlockMenu(
BuildContext context, {
required DocumentBloc documentBloc,
required EditorState editorState,
required Selection selection,
}) async {
final theme = ToolbarColorExtension.of(context);
return showMobileBottomSheet<bool>(
context,
showHeader: true,
showDragHandle: true,
showCloseButton: true,
title: LocaleKeys.button_add.tr(),
barrierColor: Colors.transparent,
backgroundColor: theme.toolbarMenuBackgroundColor,
elevation: 20,
enableDraggableScrollable: true,
builder: (_) => Padding(
padding: EdgeInsets.all(16 * context.scale),
child: BlocProvider.value(
value: documentBloc,
child: _AddBlockMenu(
selection: selection,
editorState: editorState,
),
}) async =>
showMobileBottomSheet<bool>(
context,
showHeader: true,
showDragHandle: true,
showCloseButton: true,
title: LocaleKeys.button_add.tr(),
barrierColor: Colors.transparent,
backgroundColor:
ToolbarColorExtension.of(context).toolbarMenuBackgroundColor,
elevation: 20,
enableDraggableScrollable: true,
builder: (_) => Padding(
padding: EdgeInsets.all(16 * context.scale),
child: _AddBlockMenu(selection: selection, editorState: editorState),
),
),
);
}
);
class _AddBlockMenu extends StatelessWidget {
const _AddBlockMenu({
@ -99,12 +90,9 @@ class _AddBlockMenu extends StatelessWidget {
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: context.read<DocumentBloc>(),
child: TypeOptionMenu<String>(
values: buildTypeOptionMenuItemValues(context),
scaleFactor: context.scale,
),
return TypeOptionMenu<String>(
values: buildTypeOptionMenuItemValues(context),
scaleFactor: context.scale,
);
}
@ -226,7 +214,7 @@ class _AddBlockMenu extends StatelessWidget {
onTap: (_, __) async {
AppGlobals.rootNavKey.currentContext?.pop(true);
final currentViewId = context.read<DocumentBloc>().documentId;
final currentViewId = getIt<MenuSharedState>().latestOpenView?.id;
final viewId = await showPageSelectorSheet(
context,
currentViewId: currentViewId,

View File

@ -24,7 +24,10 @@ class CachedRecentService {
Completer<void> _completer = Completer();
ValueNotifier<List<ViewPB>> notifier = ValueNotifier(const []);
List<ViewPB> _recentViews = const [];
List<ViewPB> get _recentViews => notifier.value;
set _recentViews(List<ViewPB> value) => notifier.value = value;
final _listener = RecentViewsListener();
Future<List<ViewPB>> recentViews() async {
@ -35,7 +38,6 @@ class CachedRecentService {
_listener.start(recentViewsUpdated: _recentViewsUpdated);
final result = await _readRecentViews();
_recentViews = result.toNullable()?.items ?? const [];
notifier.value = _recentViews;
_completer.complete();
return _recentViews;
@ -64,6 +66,7 @@ class CachedRecentService {
}
Future<void> dispose() async {
notifier.dispose();
await _listener.stop();
}

View File

@ -37,6 +37,10 @@ class RecentViewsBloc extends Bloc<RecentViewsEvent, RecentViewsState> {
fetchRecentViews: (e) async {
emit(state.copyWith(views: await _service.recentViews()));
},
resetRecentViews: (e) async {
await _service.reset();
add(const RecentViewsEvent.fetchRecentViews());
},
);
},
);
@ -54,13 +58,13 @@ class RecentViewsEvent with _$RecentViewsEvent {
const factory RecentViewsEvent.removeRecentViews(List<String> viewIds) =
RemoveRecentViews;
const factory RecentViewsEvent.fetchRecentViews() = FetchRecentViews;
const factory RecentViewsEvent.resetRecentViews() = ResetRecentViews;
}
@freezed
class RecentViewsState with _$RecentViewsState {
const factory RecentViewsState({
required List<ViewPB> views,
}) = _RecentViewsState;
const factory RecentViewsState({required List<ViewPB> views}) =
_RecentViewsState;
factory RecentViewsState.initial() => const RecentViewsState(views: []);
}

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:appflowy/core/frameless_window.dart';
import 'package:appflowy/plugins/blank/blank.dart';
@ -110,9 +111,7 @@ class FadingIndexedStack extends StatefulWidget {
super.key,
required this.index,
required this.children,
this.duration = const Duration(
milliseconds: 250,
),
this.duration = const Duration(milliseconds: 250),
});
final int index;
@ -135,8 +134,10 @@ class FadingIndexedStackState extends State<FadingIndexedStack> {
@override
void didUpdateWidget(FadingIndexedStack oldWidget) {
if (oldWidget.index == widget.index) return;
setState(() => _targetOpacity = 0);
Future.delayed(1.milliseconds, () => setState(() => _targetOpacity = 1));
_targetOpacity = 0;
SchedulerBinding.instance.addPostFrameCallback(
(_) => setState(() => _targetOpacity = 1),
);
super.didUpdateWidget(oldWidget);
}

View File

@ -1,12 +1,14 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_item.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class FavoriteFolder extends StatelessWidget {
@ -98,6 +100,8 @@ class _FavoriteHeaderState extends State<FavoriteHeader> {
children: [
FlowyTextButton(
LocaleKeys.sideBar_favorites.tr(),
fontColor: AFThemeExtension.of(context).textColor,
fontHoverColor: Theme.of(context).colorScheme.onSurface,
tooltip: LocaleKeys.sideBar_clickToHideFavorites.tr(),
constraints: const BoxConstraints(maxHeight: iconSize),
padding: const EdgeInsets.all(4),

View File

@ -43,6 +43,7 @@ class _FolderHeaderState extends State<FolderHeader> {
minHeight: iconSize + textPadding * 2,
),
fontColor: AFThemeExtension.of(context).textColor,
fontHoverColor: Theme.of(context).colorScheme.onSurface,
padding: const EdgeInsets.all(textPadding),
fillColor: Colors.transparent,
onPressed: widget.onPressed,

View File

@ -178,9 +178,7 @@ class HomeSideBar extends StatelessWidget {
}
class _Sidebar extends StatefulWidget {
const _Sidebar({
required this.userProfile,
});
const _Sidebar({required this.userProfile});
final UserProfilePB userProfile;
@ -222,10 +220,7 @@ class _SidebarState extends State<_Sidebar> {
mainAxisSize: MainAxisSize.min,
children: [
// top menu
const Padding(
padding: menuHorizontalInset,
child: SidebarTopMenu(),
),
const Padding(padding: menuHorizontalInset, child: SidebarTopMenu()),
// user or workspace, setting
Padding(
padding: menuHorizontalInset,
@ -245,8 +240,9 @@ class _SidebarState extends State<_Sidebar> {
// scrollable document list
Expanded(
child: Padding(
padding: menuHorizontalInset,
padding: menuHorizontalInset - const EdgeInsets.only(right: 6),
child: SingleChildScrollView(
padding: const EdgeInsets.only(right: 6),
controller: _scrollController,
physics: const ClampingScrollPhysics(),
child: SidebarFolder(

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
@ -10,7 +12,6 @@ import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/_sectio
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SidebarFolder extends StatelessWidget {
@ -38,10 +39,7 @@ class SidebarFolder extends StatelessWidget {
}
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: FavoriteFolder(
// remove the duplicate views
views: state.views,
),
child: FavoriteFolder(views: state.views),
);
},
),
@ -52,30 +50,27 @@ class SidebarFolder extends StatelessWidget {
final isCollaborativeWorkspace =
context.read<UserWorkspaceBloc>().state.isCollabWorkspaceOn;
// only show public and private section if the workspace is collaborative
return Column(
children:
// only show public and private section if the workspace is collaborative
isCollaborativeWorkspace
? [
// public
const VSpace(10),
PublicSectionFolder(
views: state.section.publicViews,
),
children: isCollaborativeWorkspace
? [
// public
const VSpace(10),
PublicSectionFolder(views: state.section.publicViews),
// private
const VSpace(10),
PrivateSectionFolder(
views: state.section.privateViews,
),
]
: [
// personal
const VSpace(10),
PersonalSectionFolder(
views: state.section.publicViews,
),
],
// private
const VSpace(10),
PrivateSectionFolder(
views: state.section.privateViews,
),
]
: [
// personal
const VSpace(10),
PersonalSectionFolder(
views: state.section.publicViews,
),
],
);
},
),
@ -87,10 +82,8 @@ class SidebarFolder extends StatelessWidget {
}
class PrivateSectionFolder extends SectionFolder {
PrivateSectionFolder({
super.key,
required super.views,
}) : super(
PrivateSectionFolder({super.key, required super.views})
: super(
title: LocaleKeys.sideBar_private.tr(),
categoryType: FolderCategoryType.private,
expandButtonTooltip: LocaleKeys.sideBar_clickToHidePrivate.tr(),
@ -99,10 +92,8 @@ class PrivateSectionFolder extends SectionFolder {
}
class PublicSectionFolder extends SectionFolder {
PublicSectionFolder({
super.key,
required super.views,
}) : super(
PublicSectionFolder({super.key, required super.views})
: super(
title: LocaleKeys.sideBar_workspace.tr(),
categoryType: FolderCategoryType.public,
expandButtonTooltip: LocaleKeys.sideBar_clickToHideWorkspace.tr(),
@ -111,10 +102,8 @@ class PublicSectionFolder extends SectionFolder {
}
class PersonalSectionFolder extends SectionFolder {
PersonalSectionFolder({
super.key,
required super.views,
}) : super(
PersonalSectionFolder({super.key, required super.views})
: super(
title: LocaleKeys.sideBar_personal.tr(),
categoryType: FolderCategoryType.public,
expandButtonTooltip: LocaleKeys.sideBar_clickToHidePersonal.tr(),

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/base/emoji/emoji_text.dart';
@ -24,7 +26,6 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
typedef ViewItemOnSelected = void Function(ViewPB, BuildContext);

View File

@ -1,10 +1,9 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
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/plugins/base/emoji/emoji_picker.dart';
import 'package:appflowy/plugins/base/icon/icon_picker.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/user/settings_user_bloc.dart';
@ -14,18 +13,16 @@ import 'package:appflowy/workspace/presentation/settings/shared/settings_categor
import 'package:appflowy/workspace/presentation/settings/shared/settings_category_spacer.dart';
import 'package:appflowy/workspace/presentation/settings/shared/settings_header.dart';
import 'package:appflowy/workspace/presentation/settings/shared/settings_input_field.dart';
import 'package:appflowy/workspace/presentation/settings/shared/single_setting_action.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/setting_third_party_login.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy/workspace/presentation/widgets/user_avatar.dart';
import 'package:appflowy_backend/protobuf/flowy-user/auth.pbenum.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -51,15 +48,6 @@ class SettingsAccountView extends StatefulWidget {
class _SettingsAccountViewState extends State<SettingsAccountView> {
late String userName = widget.userProfile.name;
late final TextEditingController _emailController = TextEditingController(
text: widget.userProfile.email,
);
@override
void dispose() {
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
@ -67,11 +55,7 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
create: (context) =>
getIt<SettingsUserViewBloc>(param1: widget.userProfile)
..add(const SettingsUserEvent.initial()),
child: BlocConsumer<SettingsUserViewBloc, SettingsUserState>(
listenWhen: (previous, current) =>
previous.userProfile.email != current.userProfile.email,
listener: (context, state) =>
_emailController.text = state.userProfile.email,
child: BlocBuilder<SettingsUserViewBloc, SettingsUserState>(
builder: (context, state) {
return SettingsBody(
children: [
@ -97,45 +81,47 @@ class _SettingsAccountViewState extends State<SettingsAccountView> {
),
],
),
// Only show change email if the user is authenticated and not using local auth
if (isAuthEnabled &&
state.userProfile.authenticator != AuthenticatorPB.Local) ...[
const SettingsCategorySpacer(),
SettingsCategory(
title: LocaleKeys.settings_accountPage_email_title.tr(),
children: [
SingleSettingAction(
label: state.userProfile.email,
buttonLabel: LocaleKeys
.settings_accountPage_email_actions_change
.tr(),
onPressed: () => SettingsAlertDialog(
title: LocaleKeys
.settings_accountPage_email_actions_change
.tr(),
confirmLabel: LocaleKeys.button_save.tr(),
confirm: () {
context.read<SettingsUserViewBloc>().add(
SettingsUserEvent.updateUserEmail(
_emailController.text,
),
);
Navigator.of(context).pop();
},
children: [
SettingsInputField(
label: LocaleKeys.settings_accountPage_email_title
.tr(),
value: state.userProfile.email,
hideActions: true,
textController: _emailController,
),
],
).show(context),
),
],
),
],
// Enable when/if we need change email feature
// // Only show change email if the user is authenticated and not using local auth
// if (isAuthEnabled &&
// state.userProfile.authenticator != AuthenticatorPB.Local) ...[
// const SettingsCategorySpacer(),
// SettingsCategory(
// title: LocaleKeys.settings_accountPage_email_title.tr(),
// children: [
// SingleSettingAction(
// label: state.userProfile.email,
// buttonLabel: LocaleKeys
// .settings_accountPage_email_actions_change
// .tr(),
// onPressed: () => SettingsAlertDialog(
// title: LocaleKeys
// .settings_accountPage_email_actions_change
// .tr(),
// confirmLabel: LocaleKeys.button_save.tr(),
// confirm: () {
// context.read<SettingsUserViewBloc>().add(
// SettingsUserEvent.updateUserEmail(
// _emailController.text,
// ),
// );
// Navigator.of(context).pop();
// },
// children: [
// SettingsInputField(
// label: LocaleKeys.settings_accountPage_email_title
// .tr(),
// value: state.userProfile.email,
// hideActions: true,
// textController: _emailController,
// ),
// ],
// ).show(context),
// ),
// ],
// ),
// ],
/// Enable when we have change password feature and 2FA
// const SettingsCategorySpacer(),
@ -259,7 +245,9 @@ class SignInOutButton extends StatelessWidget {
hoverColor: const Color(0xFF005483),
fontHoverColor: Colors.white,
onPressed: () => SettingsAlertDialog(
title: LocaleKeys.settings_accountPage_login_loginLabel.tr(),
title: signIn
? LocaleKeys.settings_accountPage_login_loginLabel.tr()
: LocaleKeys.settings_accountPage_login_logoutLabel.tr(),
subtitle: signIn
? null
: switch (userProfile.encryptionType) {
@ -303,6 +291,7 @@ class UserProfileSetting extends StatefulWidget {
}
class _UserProfileSettingState extends State<UserProfileSetting> {
late final _nameController = TextEditingController(text: widget.name);
late final FocusNode focusNode;
bool isEditing = false;
bool isHovering = false;
@ -314,14 +303,20 @@ class _UserProfileSettingState extends State<UserProfileSetting> {
onKeyEvent: (_, event) {
if (event is KeyDownEvent &&
event.logicalKey == LogicalKeyboardKey.escape &&
isEditing) {
isEditing &&
mounted) {
setState(() => isEditing = false);
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
);
)..addListener(() {
if (!focusNode.hasFocus && isEditing && mounted) {
widget.onSave?.call(_nameController.text);
setState(() => isEditing = false);
}
});
}
@override
@ -335,69 +330,28 @@ class _UserProfileSettingState extends State<UserProfileSetting> {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Stack(
children: [
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _showIconPickerDialog(context),
child: FlowyHover(
resetHoverOnRebuild: false,
onHover: (state) => setState(() => isHovering = state),
style: HoverStyle(
hoverColor: Colors.transparent,
borderRadius: BorderRadius.circular(100),
),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
UserAvatar(
iconUrl: widget.iconUrl,
name: widget.name,
isLarge: true,
isHovering: isHovering,
),
const VSpace(4),
FlowyText.regular(
LocaleKeys
.settings_accountPage_general_changeProfilePicture
.tr(),
color: AFThemeExtension.of(context).textColor,
),
],
),
GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => _showIconPickerDialog(context),
child: FlowyHover(
resetHoverOnRebuild: false,
onHover: (state) => setState(() => isHovering = state),
style: HoverStyle(
hoverColor: Colors.transparent,
borderRadius: BorderRadius.circular(100),
),
child: FlowyTooltip(
message: LocaleKeys
.settings_accountPage_general_changeProfilePicture
.tr(),
child: UserAvatar(
iconUrl: widget.iconUrl,
name: widget.name,
isLarge: true,
isHovering: isHovering,
),
),
if (widget.iconUrl.isNotEmpty)
Positioned(
right: 0,
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => context
.read<SettingsUserViewBloc>()
.add(const SettingsUserEvent.removeUserIcon()),
child: DecoratedBox(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.primary,
shape: BoxShape.circle,
),
child: FlowyHover(
resetHoverOnRebuild: false,
style: const HoverStyle(
borderRadius: BorderRadius.all(Radius.circular(24)),
hoverColor: Color(0xFF005483),
),
builder: (_, __) => Padding(
padding: const EdgeInsets.all(4),
child: FlowySvg(
FlowySvgs.close_s,
color: Theme.of(context).colorScheme.onPrimary,
),
),
),
),
),
),
],
),
),
const HSpace(16),
if (!isEditing) ...[
@ -430,6 +384,7 @@ class _UserProfileSettingState extends State<UserProfileSetting> {
] else ...[
Flexible(
child: SettingsInputField(
textController: _nameController,
value: widget.name,
focusNode: focusNode..requestFocus(),
onCancel: () => setState(() => isEditing = false),
@ -448,20 +403,16 @@ class _UserProfileSettingState extends State<UserProfileSetting> {
return showDialog(
context: context,
builder: (dialogContext) => SimpleDialog(
title: FlowyText.medium(
LocaleKeys.settings_user_selectAnIcon.tr(),
fontSize: FontSizes.s16,
),
children: [
Container(
height: 380,
width: 360,
margin: const EdgeInsets.symmetric(horizontal: 12),
child: FlowyEmojiPicker(
onEmojiSelected: (_, emoji) {
context
.read<SettingsUserViewBloc>()
.add(SettingsUserEvent.updateUserIcon(iconUrl: emoji));
child: FlowyIconPicker(
onSelected: (result) {
context.read<SettingsUserViewBloc>().add(
SettingsUserEvent.updateUserIcon(iconUrl: result.emoji),
);
Navigator.of(dialogContext).pop();
},
),

View File

@ -1,5 +1,7 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/export/document_exporter.dart';
import 'package:appflowy/workspace/application/settings/settings_file_exporter_cubit.dart';
@ -13,8 +15,8 @@ import 'package:appflowy_backend/protobuf/flowy-folder/workspace.pb.dart';
import 'package:appflowy_result/appflowy_result.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/file_picker/file_picker_service.dart';
import 'package:flowy_infra/theme_extension.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:path/path.dart' as p;
@ -64,6 +66,7 @@ class _FileExporterWidgetState extends State<FileExporterWidget> {
.every((element) => element)
? LocaleKeys.settings_files_deselectAll.tr()
: LocaleKeys.settings_files_selectAll.tr(),
fontColor: AFThemeExtension.of(context).textColor,
onPressed: () {
context
.read<SettingsFileExporterCubit>()
@ -93,13 +96,13 @@ class _FileExporterWidgetState extends State<FileExporterWidget> {
const Spacer(),
FlowyTextButton(
LocaleKeys.button_cancel.tr(),
onPressed: () {
Navigator.of(context).pop();
},
fontColor: AFThemeExtension.of(context).textColor,
onPressed: () => Navigator.of(context).pop(),
),
const HSpace(8),
FlowyTextButton(
LocaleKeys.button_ok.tr(),
fontColor: AFThemeExtension.of(context).textColor,
onPressed: () async {
await getIt<FilePickerService>()
.getDirectoryPath()

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
@ -8,8 +10,8 @@ import 'package:appflowy/workspace/application/settings/appearance/appearance_cu
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart';
@ -208,9 +210,7 @@ class _FontFamilyDropDownState extends State<FontFamilyDropDown> {
}
class _ResetFontButton extends SliverPersistentHeaderDelegate {
_ResetFontButton({
this.onPressed,
});
_ResetFontButton({this.onPressed});
final VoidCallback? onPressed;
@ -224,6 +224,9 @@ class _ResetFontButton extends SliverPersistentHeaderDelegate {
padding: const EdgeInsets.only(right: 8, bottom: 8.0),
child: FlowyTextButton(
LocaleKeys.document_toolbar_resetToDefaultFont.tr(),
fontColor: AFThemeExtension.of(context).textColor,
fontHoverColor: Theme.of(context).colorScheme.onSurface,
fontSize: 12,
onPressed: onPressed,
),
);

View File

@ -9,6 +9,7 @@ import 'package:appflowy/workspace/presentation/settings/shared/settings_body.da
import 'package:appflowy/workspace/presentation/settings/shared/settings_header.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@ -75,6 +76,7 @@ class ShortcutsListView extends StatelessWidget {
const Spacer(),
FlowyTextButton(
LocaleKeys.settings_shortcuts_resetToDefault.tr(),
fontColor: AFThemeExtension.of(context).textColor,
onPressed: () => context.read<ShortcutsCubit>().resetToDefault(),
),
],
@ -109,9 +111,8 @@ class ShortcutsListTile extends StatelessWidget {
FlowyTextButton(
shortcutEvent.command,
fillColor: Colors.transparent,
onPressed: () {
showKeyListenerDialog(context);
},
fontColor: AFThemeExtension.of(context).textColor,
onPressed: () => showKeyListenerDialog(context),
),
],
),

View File

@ -8,7 +8,6 @@ import 'package:appflowy/workspace/application/settings/settings_dialog_bloc.dar
import 'package:appflowy/workspace/presentation/settings/widgets/settings_menu_element.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
class SettingsMenu extends StatelessWidget {
@ -59,60 +58,42 @@ class SettingsMenu extends StatelessWidget {
page: SettingsPage.appearance,
selectedPage: currentPage,
label: LocaleKeys.settings_menu_appearance.tr(),
icon: Icon(
Icons.brightness_4,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.brightness_4),
changeSelectedPage: changeSelectedPage,
),
SettingsMenuElement(
page: SettingsPage.language,
selectedPage: currentPage,
label: LocaleKeys.settings_menu_language.tr(),
icon: Icon(
Icons.translate,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.translate),
changeSelectedPage: changeSelectedPage,
),
SettingsMenuElement(
page: SettingsPage.files,
selectedPage: currentPage,
label: LocaleKeys.settings_menu_files.tr(),
icon: Icon(
Icons.file_present_outlined,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.file_present_outlined),
changeSelectedPage: changeSelectedPage,
),
SettingsMenuElement(
page: SettingsPage.notifications,
selectedPage: currentPage,
label: LocaleKeys.settings_menu_notifications.tr(),
icon: Icon(
Icons.notifications_outlined,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.notifications_outlined),
changeSelectedPage: changeSelectedPage,
),
SettingsMenuElement(
page: SettingsPage.cloud,
selectedPage: currentPage,
label: LocaleKeys.settings_menu_cloudSettings.tr(),
icon: Icon(
Icons.sync,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.sync),
changeSelectedPage: changeSelectedPage,
),
SettingsMenuElement(
page: SettingsPage.shortcuts,
selectedPage: currentPage,
label: LocaleKeys.settings_shortcuts_shortcutsLabel.tr(),
icon: Icon(
Icons.cut,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.cut),
changeSelectedPage: changeSelectedPage,
),
if (FeatureFlag.membersSettings.isOn &&
@ -122,10 +103,7 @@ class SettingsMenu extends StatelessWidget {
page: SettingsPage.member,
selectedPage: currentPage,
label: LocaleKeys.settings_appearance_members_label.tr(),
icon: Icon(
Icons.people,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.people),
changeSelectedPage: changeSelectedPage,
),
if (kDebugMode)
@ -134,10 +112,7 @@ class SettingsMenu extends StatelessWidget {
page: SettingsPage.featureFlags,
selectedPage: currentPage,
label: 'Feature Flags',
icon: Icon(
Icons.flag,
color: AFThemeExtension.of(context).textColor,
),
icon: const Icon(Icons.flag),
changeSelectedPage: changeSelectedPage,
),
],

View File

@ -31,9 +31,13 @@ class SettingsMenuElement extends StatelessWidget {
hoverColor: AFThemeExtension.of(context).greySelect,
borderRadius: BorderRadius.circular(4),
),
child: ListTile(
builder: (_, isHovering) => ListTile(
dense: true,
leading: icon,
leading: iconWidget(
isHovering || page == selectedPage
? Theme.of(context).colorScheme.onSurface
: AFThemeExtension.of(context).textColor,
),
onTap: () => changeSelectedPage(page),
selected: page == selectedPage,
selectedColor: Theme.of(context).colorScheme.onSurface,
@ -53,4 +57,9 @@ class SettingsMenuElement extends StatelessWidget {
),
);
}
Widget iconWidget(Color color) => IconTheme(
data: IconThemeData(color: color),
child: icon,
);
}

View File

@ -324,7 +324,7 @@
"description": "Customize your profile, manage account security and AI API keys, or login into your account.",
"general": {
"title": "Account name & profile image",
"changeProfilePicture": "Change"
"changeProfilePicture": "Change profile picture"
},
"email": {
"title": "Email",