diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart index 84bc5c4820..1583e9e2e0 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/bottom_sheet/default_mobile_action_pane.dart @@ -1,7 +1,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; -import 'package:appflowy/mobile/presentation/home/shared/mobile_view_card.dart'; +import 'package:appflowy/mobile/presentation/home/shared/mobile_page_card.dart'; import 'package:appflowy/mobile/presentation/page_item/mobile_slide_action_button.dart'; import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart'; import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart'; @@ -23,7 +23,7 @@ enum MobilePaneActionType { MobileSlideActionButton actionButton( BuildContext context, { - MobileViewCardType? cardType, + MobilePageCardType? cardType, FolderSpaceType? spaceType, }) { switch (this) { @@ -133,13 +133,13 @@ enum MobilePaneActionType { List _buildActions( ViewPB view, { - MobileViewCardType? cardType, + MobilePageCardType? cardType, }) { final isFavorite = view.isFavorite; if (cardType != null) { switch (cardType) { - case MobileViewCardType.recent: + case MobilePageCardType.recent: return [ isFavorite ? MobileViewItemBottomSheetBodyAction.removeFromFavorites @@ -150,7 +150,7 @@ enum MobilePaneActionType { MobileViewItemBottomSheetBodyAction.divider, MobileViewItemBottomSheetBodyAction.removeFromRecent, ]; - case MobileViewCardType.favorite: + case MobilePageCardType.favorite: return [ isFavorite ? MobileViewItemBottomSheetBodyAction.removeFromFavorites @@ -179,7 +179,7 @@ ActionPane buildEndActionPane( BuildContext context, List actions, { bool needSpace = true, - MobileViewCardType? cardType, + MobilePageCardType? cardType, FolderSpaceType? spaceType, }) { return ActionPane( diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/favorite_folder/favorite_space.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/favorite_folder/favorite_space.dart index 105ebdfc9c..ded486983e 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/favorite_folder/favorite_space.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/favorite_folder/favorite_space.dart @@ -1,6 +1,6 @@ import 'package:appflowy/mobile/application/mobile_router.dart'; import 'package:appflowy/mobile/presentation/home/shared/empty_placeholder.dart'; -import 'package:appflowy/mobile/presentation/home/shared/mobile_view_card.dart'; +import 'package:appflowy/mobile/presentation/home/shared/mobile_page_card.dart'; import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart'; import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart'; @@ -69,7 +69,7 @@ class _MobileFavoriteSpaceState extends State if (favoriteState.views.isEmpty) { return const EmptySpacePlaceholder( - type: MobileViewCardType.favorite, + type: MobilePageCardType.favorite, ); } @@ -117,11 +117,11 @@ class _FavoriteViews extends StatelessWidget { ), ), ), - child: MobileViewCard( + child: MobileViewPage( key: ValueKey(view.item.id), view: view.item, timestamp: view.timestamp, - type: MobileViewCardType.favorite, + type: MobilePageCardType.favorite, ), ); }, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_folders.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_folders.dart index b98999a35d..64ad7e4cd1 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_folders.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/mobile_folders.dart @@ -42,8 +42,8 @@ class MobileFolders extends StatelessWidget { create: (_) => FavoriteBloc()..add(const FavoriteEvent.initial()), ), BlocProvider( - create: (_) => - SpaceBloc()..add(SpaceEvent.initial(user, workspaceId)), + create: (_) => SpaceBloc() + ..add(SpaceEvent.initial(user, workspaceId, openFirstPage: false)), ), ], child: BlocListener( diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart index 5d313a3533..b2b2fbca85 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/recent_folder/recent_space.dart @@ -1,5 +1,5 @@ import 'package:appflowy/mobile/presentation/home/shared/empty_placeholder.dart'; -import 'package:appflowy/mobile/presentation/home/shared/mobile_view_card.dart'; +import 'package:appflowy/mobile/presentation/home/shared/mobile_page_card.dart'; import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy/workspace/application/recent/prelude.dart'; import 'package:appflowy/workspace/presentation/home/home_sizes.dart'; @@ -37,7 +37,7 @@ class _MobileRecentSpaceState extends State if (recentViews.isEmpty) { return const Center( - child: EmptySpacePlaceholder(type: MobileViewCardType.recent), + child: EmptySpacePlaceholder(type: MobilePageCardType.recent), ); } @@ -89,11 +89,11 @@ class _RecentViews extends StatelessWidget { ), ), ), - child: MobileViewCard( + child: MobileViewPage( key: ValueKey(sectionView.item.id), view: sectionView.item, timestamp: sectionView.timestamp, - type: MobileViewCardType.recent, + type: MobilePageCardType.recent, ), ); }, diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/empty_placeholder.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/empty_placeholder.dart index e59fa87538..341acb8099 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/empty_placeholder.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/empty_placeholder.dart @@ -1,6 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/mobile/presentation/home/shared/mobile_view_card.dart'; +import 'package:appflowy/mobile/presentation/home/shared/mobile_page_card.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; @@ -8,7 +8,7 @@ import 'package:flutter/material.dart'; class EmptySpacePlaceholder extends StatelessWidget { const EmptySpacePlaceholder({super.key, required this.type}); - final MobileViewCardType type; + final MobilePageCardType type; @override Widget build(BuildContext context) { @@ -42,14 +42,14 @@ class EmptySpacePlaceholder extends StatelessWidget { } String get _emptyPageText => switch (type) { - MobileViewCardType.recent => LocaleKeys.sideBar_emptyRecent.tr(), - MobileViewCardType.favorite => LocaleKeys.sideBar_emptyFavorite.tr(), + MobilePageCardType.recent => LocaleKeys.sideBar_emptyRecent.tr(), + MobilePageCardType.favorite => LocaleKeys.sideBar_emptyFavorite.tr(), }; String get _emptyPageSubText => switch (type) { - MobileViewCardType.recent => + MobilePageCardType.recent => LocaleKeys.sideBar_emptyRecentDescription.tr(), - MobileViewCardType.favorite => + MobilePageCardType.favorite => LocaleKeys.sideBar_emptyFavoriteDescription.tr(), }; } diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_view_card.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_page_card.dart similarity index 97% rename from frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_view_card.dart rename to frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_page_card.dart index 584bd381ed..71be20453d 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_view_card.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/shared/mobile_page_card.dart @@ -29,18 +29,18 @@ import 'package:provider/provider.dart'; import 'package:string_validator/string_validator.dart'; import 'package:time/time.dart'; -enum MobileViewCardType { +enum MobilePageCardType { recent, favorite; String get lastOperationHintText => switch (this) { - MobileViewCardType.recent => LocaleKeys.sideBar_lastViewed.tr(), - MobileViewCardType.favorite => LocaleKeys.sideBar_favoriteAt.tr(), + MobilePageCardType.recent => LocaleKeys.sideBar_lastViewed.tr(), + MobilePageCardType.favorite => LocaleKeys.sideBar_favoriteAt.tr(), }; } -class MobileViewCard extends StatelessWidget { - const MobileViewCard({ +class MobileViewPage extends StatelessWidget { + const MobileViewPage({ super.key, required this.view, this.timestamp, @@ -49,7 +49,7 @@ class MobileViewCard extends StatelessWidget { final ViewPB view; final Int64? timestamp; - final MobileViewCardType type; + final MobilePageCardType type; @override Widget build(BuildContext context) { diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/home/space/mobile_space.dart b/frontend/appflowy_flutter/lib/mobile/presentation/home/space/mobile_space.dart index fd3b45cbd4..9d288c3411 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/home/space/mobile_space.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/home/space/mobile_space.dart @@ -42,10 +42,6 @@ class _MobileSpaceState extends State { SpaceEvent.createPage( name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), index: 0, - viewSection: currentSpace.spacePermission == - SpacePermission.publicToAll - ? ViewSectionPB.Public - : ViewSectionPB.Private, ), ); context.read().add( diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart index 0015716d93..080809042f 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -58,8 +58,6 @@ class EditorStyleCustomizer { DefaultAppearanceSettings.getDefaultSelectionColor(context), defaultTextDirection: appearance.defaultTextDirection, textStyleConfiguration: TextStyleConfiguration( - // applyHeightToFirstAscent: true, - // applyHeightToLastDescent: true, text: baseTextStyle(fontFamily).copyWith( fontSize: fontSize, color: afThemeExtension.onBackground, diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart index 1727eedd55..d59b5b12c4 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/appearance/desktop_appearance.dart @@ -76,7 +76,7 @@ class DesktopAppearance extends BaseAppearance { scrollbarTheme: ScrollbarThemeData( thumbColor: WidgetStateProperty.resolveWith((states) { if (states.any(scrollbarInteractiveStates.contains)) { - return theme.shader7; + return theme.shader3; } return theme.shader5; }), @@ -102,6 +102,7 @@ class DesktopAppearance extends BaseAppearance { indicatorColor: theme.main1, cardColor: theme.input, colorScheme: colorScheme, + extensions: [ AFThemeExtension( warning: theme.yellow, diff --git a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart index eba03cd803..193c73f131 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/sidebar/space/space_bloc.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'package:appflowy/core/config/kv.dart'; import 'package:appflowy/core/config/kv_keys.dart'; +import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/user/application/user_service.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; @@ -16,6 +17,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'; import 'package:appflowy_result/appflowy_result.dart'; import 'package:collection/collection.dart'; +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:protobuf/protobuf.dart'; @@ -60,7 +62,7 @@ class SpaceBloc extends Bloc { on( (event, emit) async { await event.when( - initial: (userProfile, workspaceId) async { + initial: (userProfile, workspaceId, openFirstPage) async { _initial(userProfile, workspaceId); final (spaces, publicViews, privateViews) = await _getSpaces(); @@ -84,8 +86,20 @@ class SpaceBloc extends Bloc { if (shouldShowUpgradeDialog) { add(const SpaceEvent.migrate()); } + + if (openFirstPage) { + if (currentSpace != null) { + add(SpaceEvent.open(currentSpace)); + } + } }, - create: (name, icon, iconColor, permission) async { + create: ( + name, + icon, + iconColor, + permission, + createNewPageByDefault, + ) async { final space = await _createSpace( name: name, icon: icon, @@ -93,8 +107,22 @@ class SpaceBloc extends Bloc { permission: permission, ); if (space != null) { - emit(state.copyWith(spaces: [...state.spaces, space])); + emit( + state.copyWith( + spaces: [...state.spaces, space], + currentSpace: space, + ), + ); add(SpaceEvent.open(space)); + + if (createNewPageByDefault) { + add( + SpaceEvent.createPage( + name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(), + index: 0, + ), + ); + } } }, delete: (space) async { @@ -160,12 +188,28 @@ class SpaceBloc extends Bloc { await _openSpace(space); final isExpanded = await _getSpaceExpandStatus(space); emit(state.copyWith(currentSpace: space, isExpanded: isExpanded)); + + // open the first page by default + if (space.childViews.isNotEmpty) { + final firstPage = space.childViews.first; + emit( + state.copyWith( + lastCreatedPage: firstPage, + ), + ); + } else { + emit( + state.copyWith( + lastCreatedPage: ViewPB(), + ), + ); + } }, expand: (space, isExpanded) async { await _setSpaceExpandStatus(space, isExpanded); emit(state.copyWith(isExpanded: isExpanded)); }, - createPage: (name, section, index) async { + createPage: (name, index) async { final parentViewId = state.currentSpace?.id; if (parentViewId == null) { return; @@ -209,12 +253,33 @@ class SpaceBloc extends Bloc { reset: (userProfile, workspaceId) async { _reset(userProfile, workspaceId); - add(SpaceEvent.initial(userProfile, workspaceId)); + add( + SpaceEvent.initial( + userProfile, + workspaceId, + openFirstPage: true, + ), + ); }, migrate: () async { final result = await migrate(); emit(state.copyWith(shouldShowUpgradeDialog: !result)); }, + switchToNextSpace: () async { + final spaces = state.spaces; + if (spaces.isEmpty) { + return; + } + + final currentSpace = state.currentSpace; + if (currentSpace == null) { + return; + } + final currentIndex = spaces.indexOf(currentSpace); + final nextIndex = (currentIndex + 1) % spaces.length; + final nextSpace = spaces[nextIndex]; + add(SpaceEvent.open(nextSpace)); + }, ); }, ); @@ -410,7 +475,7 @@ class SpaceBloc extends Bloc { // only migrate the public space if there are any public views if (publicViews.isNotEmpty) { final publicSpace = await _createSpace( - name: 'General', + name: 'Shared', icon: builtInSpaceIcons.first, iconColor: builtInSpaceColors.first, permission: SpacePermission.publicToAll, @@ -487,13 +552,15 @@ class SpaceBloc extends Bloc { class SpaceEvent with _$SpaceEvent { const factory SpaceEvent.initial( UserProfilePB userProfile, - String workspaceId, - ) = _Initial; + String workspaceId, { + required bool openFirstPage, + }) = _Initial; const factory SpaceEvent.create({ required String name, required String icon, required String iconColor, required SpacePermission permission, + required bool createNewPageByDefault, }) = _Create; const factory SpaceEvent.rename(ViewPB space, String name) = _Rename; const factory SpaceEvent.changeIcon(String icon, String iconColor) = @@ -508,7 +575,6 @@ class SpaceEvent with _$SpaceEvent { const factory SpaceEvent.expand(ViewPB space, bool isExpanded) = _Expand; const factory SpaceEvent.createPage({ required String name, - required ViewSectionPB viewSection, int? index, }) = _CreatePage; const factory SpaceEvent.delete(ViewPB? space) = _Delete; @@ -518,6 +584,7 @@ class SpaceEvent with _$SpaceEvent { String workspaceId, ) = _Reset; const factory SpaceEvent.migrate() = _Migrate; + const factory SpaceEvent.switchToNextSpace() = _SwitchToNextSpace; } @freezed diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/hotkeys.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/hotkeys.dart index 3ac4274fb7..ac2a57ec80 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/hotkeys.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/hotkeys.dart @@ -1,7 +1,5 @@ import 'dart:io'; -import 'package:flutter/material.dart'; - import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/tasks/app_window_size_manager.dart'; import 'package:appflowy/workspace/application/home/home_setting_bloc.dart'; @@ -11,12 +9,15 @@ import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/sidebar_setting.dart'; import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart'; +import 'package:flutter/material.dart'; import 'package:hotkey_manager/hotkey_manager.dart'; import 'package:provider/provider.dart'; import 'package:scaled_app/scaled_app.dart'; typedef KeyDownHandler = void Function(HotKey hotKey); +ValueNotifier switchToTheNextSpace = ValueNotifier(0); + /// Helper class that utilizes the global [HotKeyManager] to easily /// add a [HotKey] with different handlers. /// @@ -163,6 +164,16 @@ class _HomeHotKeysState extends State { keyDownHandler: (_) => _scaleToSize(1), ), + // Switch to the next space + HotKeyItem( + hotKey: HotKey( + KeyCode.keyO, + modifiers: [Platform.isMacOS ? KeyModifier.meta : KeyModifier.control], + scope: HotKeyScope.inapp, + ), + keyDownHandler: (_) => switchToTheNextSpace.value++, + ), + // Open settings dialog openSettingsHotKey(context, widget.userProfile), ]; diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_menu.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_menu.dart index 91f178af60..e2fcc2021c 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_menu.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/favorites/favorite_menu.dart @@ -62,6 +62,21 @@ class FavoriteMenu extends StatelessWidget { } Widget _buildViews(BuildContext context, FavoriteMenuState state) { + final today = _buildGroups( + context, + state.todayViews, + LocaleKeys.sideBar_today.tr(), + ); + final thisWeek = _buildGroups( + context, + state.thisWeekViews, + LocaleKeys.sideBar_thisWeek.tr(), + ); + final others = _buildGroups( + context, + state.otherViews, + LocaleKeys.sideBar_others.tr(), + ); return Container( width: minWidth - 2 * _kHorizontalPadding, constraints: const BoxConstraints( @@ -72,21 +87,26 @@ class FavoriteMenu extends StatelessWidget { mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - ..._buildGroups( - context, - state.todayViews, - LocaleKeys.sideBar_today.tr(), - ), - ..._buildGroups( - context, - state.thisWeekViews, - LocaleKeys.sideBar_thisWeek.tr(), - ), - ..._buildGroups( - context, - state.otherViews, - LocaleKeys.sideBar_others.tr(), - ), + if (today.isNotEmpty) ...[ + ...today, + const VSpace(8), + const Divider(height: 1), + const VSpace(8), + ], + if (thisWeek.isNotEmpty) ...[ + ...thisWeek, + const VSpace(8), + const Divider(height: 1), + const VSpace(8), + ], + ...others.isNotEmpty && (today.isNotEmpty || thisWeek.isNotEmpty) + ? others + : _buildGroups( + context, + state.otherViews, + LocaleKeys.sideBar_others.tr(), + showHeader: false, + ), ], ), ), @@ -96,23 +116,23 @@ class FavoriteMenu extends StatelessWidget { List _buildGroups( BuildContext context, List views, - String title, - ) { + String title, { + bool showHeader = true, + }) { return [ if (views.isNotEmpty) ...[ - SizedBox( - height: 24, - child: FlowyText( - title, - fontSize: 12.0, - color: Theme.of(context).hintColor, + if (showHeader) + SizedBox( + height: 24, + child: FlowyText( + title, + fontSize: 12.0, + color: Theme.of(context).hintColor, + ), ), - ), const VSpace(2), _buildGroupedViews(context, views), const VSpace(8), - const Divider(height: 1), - const VSpace(8), ], ]; } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/shared/sidebar_new_page_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/shared/sidebar_new_page_button.dart index b0b7b17483..1417930b1e 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/shared/sidebar_new_page_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/shared/sidebar_new_page_button.dart @@ -1,6 +1,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart'; +import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart'; import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart'; import 'package:appflowy/workspace/presentation/home/home_sizes.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/rename_view_dialog.dart'; @@ -48,13 +49,23 @@ class SidebarNewPageButton extends StatelessWidget { context.read().state.isCollabWorkspaceOn ? ViewSectionPB.Private : ViewSectionPB.Public; - context.read().add( - SidebarSectionsEvent.createRootViewInSection( - name: viewName, - viewSection: section, - index: 0, - ), - ); + final spaceState = context.read().state; + if (spaceState.spaces.isNotEmpty) { + context.read().add( + SpaceEvent.createPage( + name: viewName, + index: 0, + ), + ); + } else { + context.read().add( + SidebarSectionsEvent.createRootViewInSection( + name: viewName, + viewSection: section, + index: 0, + ), + ); + } } }, ); diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart index 7fe35dd88c..3f5f0057a3 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/sidebar.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/plugins/blank/blank.dart'; import 'package:appflowy/shared/feature_flags.dart'; import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/workspace/application/action_navigation/action_navigation_bloc.dart'; @@ -116,6 +117,7 @@ class HomeSideBar extends StatelessWidget { userProfile, state.currentWorkspace?.workspaceId ?? workspaceSetting.workspaceId, + openFirstPage: false, ), ), ), @@ -134,11 +136,23 @@ class HomeSideBar extends StatelessWidget { BlocListener( listenWhen: (p, c) => p.lastCreatedPage?.id != c.lastCreatedPage?.id, - listener: (context, state) => context.read().add( - TabsEvent.openPlugin( - plugin: state.lastCreatedPage!.plugin(), - ), - ), + listener: (context, state) { + final page = state.lastCreatedPage; + if (page == null || page.id.isEmpty) { + // open the blank page + context.read().add( + TabsEvent.openPlugin( + plugin: BlankPagePlugin(), + ), + ); + } else { + context.read().add( + TabsEvent.openPlugin( + plugin: state.lastCreatedPage!.plugin(), + ), + ); + } + }, ), BlocListener( listenWhen: (_, curr) => curr.action != null, @@ -151,23 +165,27 @@ class HomeSideBar extends StatelessWidget { if (actionType == UserWorkspaceActionType.create || actionType == UserWorkspaceActionType.delete || actionType == UserWorkspaceActionType.open) { - context.read().add( - SidebarSectionsEvent.reload( - userProfile, - state.currentWorkspace?.workspaceId ?? - workspaceSetting.workspaceId, - ), - ); + if (context.read().state.spaces.isEmpty) { + context.read().add( + SidebarSectionsEvent.reload( + userProfile, + state.currentWorkspace?.workspaceId ?? + workspaceSetting.workspaceId, + ), + ); + } else { + context.read().add( + SpaceEvent.reset( + userProfile, + state.currentWorkspace?.workspaceId ?? + workspaceSetting.workspaceId, + ), + ); + } + context .read() .add(const FavoriteEvent.fetchFavorites()); - context.read().add( - SpaceEvent.reset( - userProfile, - state.currentWorkspace?.workspaceId ?? - workspaceSetting.workspaceId, - ), - ); } }, ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/create_space_popup.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/create_space_popup.dart index eba3b7843d..328c969729 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/create_space_popup.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/create_space_popup.dart @@ -50,7 +50,13 @@ class _CreateSpacePopupState extends State { ), ), const VSpace(8.0), - _SpaceNameTextField(onChanged: (value) => spaceName = value), + _SpaceNameTextField( + onChanged: (value) => spaceName = value, + onSubmitted: (value) { + spaceName = value; + _createSpace(); + }, + ), const VSpace(20.0), SpacePermissionSwitch( onPermissionChanged: (value) => spacePermission = value, @@ -59,29 +65,36 @@ class _CreateSpacePopupState extends State { SpaceCancelOrConfirmButton( confirmButtonName: LocaleKeys.button_create.tr(), onCancel: () => Navigator.of(context).pop(), - onConfirm: () { - context.read().add( - SpaceEvent.create( - name: spaceName, - icon: spaceIcon, - iconColor: spaceIconColor, - permission: spacePermission, - ), - ); - - Navigator.of(context).pop(); - }, + onConfirm: () => _createSpace(), ), ], ), ); } + + void _createSpace() { + context.read().add( + SpaceEvent.create( + name: spaceName, + icon: spaceIcon, + iconColor: spaceIconColor, + permission: spacePermission, + createNewPageByDefault: true, + ), + ); + + Navigator.of(context).pop(); + } } class _SpaceNameTextField extends StatelessWidget { - const _SpaceNameTextField({required this.onChanged}); + const _SpaceNameTextField({ + required this.onChanged, + required this.onSubmitted, + }); final void Function(String name) onChanged; + final void Function(String name) onSubmitted; @override Widget build(BuildContext context) { @@ -98,8 +111,9 @@ class _SpaceNameTextField extends StatelessWidget { SizedBox( height: 40, child: FlowyTextField( - text: LocaleKeys.space_defaultSpaceName.tr(), + hintText: LocaleKeys.space_spaceName.tr(), onChanged: onChanged, + onSubmitted: onSubmitted, ), ), ], diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/shared_widget.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/shared_widget.dart index 7b42c63b28..49dc1bc7e6 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/shared_widget.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/shared_widget.dart @@ -199,7 +199,7 @@ class SpaceCancelOrConfirmButton extends StatelessWidget { radius: BorderRadius.circular(8), text: FlowyText.regular( confirmButtonName, - color: Theme.of(context).colorScheme.onPrimary, + color: Colors.white, ), onTap: onConfirm, ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space.dart index 2571c1490b..890ae8e0de 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space.dart @@ -7,6 +7,7 @@ import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart'; import 'package:appflowy/workspace/application/view/view_bloc.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/presentation/home/home_sizes.dart'; +import 'package:appflowy/workspace/presentation/home/hotkeys.dart'; import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/favorites/favorite_folder.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/shared/rename_view_dialog.dart'; @@ -79,6 +80,18 @@ class _SpaceState extends State<_Space> { final PropertyValueNotifier isExpandedNotifier = PropertyValueNotifier(false); + @override + void initState() { + super.initState(); + switchToTheNextSpace.addListener(_switchToNextSpace); + } + + @override + void dispose() { + switchToTheNextSpace.removeListener(_switchToNextSpace); + super.dispose(); + } + @override Widget build(BuildContext context) { return BlocBuilder( @@ -142,10 +155,6 @@ class _SpaceState extends State<_Space> { SpaceEvent.createPage( name: viewName, index: 0, - viewSection: - space.spacePermission == SpacePermission.publicToAll - ? ViewSectionPB.Public - : ViewSectionPB.Private, ), ); @@ -154,6 +163,10 @@ class _SpaceState extends State<_Space> { }, ); } + + void _switchToNextSpace() { + context.read().add(const SpaceEvent.switchToNextSpace()); + } } class _Pages extends StatelessWidget { diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.dart index 5ccf8bb916..5a672c61c4 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/sidebar_space_header.dart @@ -1,5 +1,8 @@ +import 'dart:io'; + import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart'; import 'package:appflowy/workspace/presentation/home/home_sizes.dart'; import 'package:appflowy/workspace/presentation/home/menu/sidebar/space/manage_space_popup.dart'; @@ -13,6 +16,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:flowy_infra_ui/widget/flowy_tooltip.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; @@ -70,7 +74,6 @@ class _SidebarSpaceHeaderState extends State { height: HomeSizes.workspaceSectionHeight, child: FlowyButton( margin: const EdgeInsets.only(left: 6.0, right: 4.0), - // rightIcon: _buildRightIcon(), iconPadding: 10.0, text: _buildChild(), rightIcon: const HSpace(60.0), @@ -88,30 +91,50 @@ class _SidebarSpaceHeaderState extends State { } Widget _buildChild() { - return Row( + final color = Theme.of(context).isLightMode ? Colors.white : Colors.black; + final textSpan = TextSpan( children: [ - SpaceIcon( - dimension: 20, - space: widget.space, - cornerRadius: 6.0, + TextSpan( + text: '${LocaleKeys.space_quicklySwitch.tr()}\n', + style: + Theme.of(context).tooltipTheme.textStyle!.copyWith(color: color), ), - const HSpace(10), - Flexible( - child: FlowyText.medium( - widget.space.name, - lineHeight: 1.15, - fontSize: 14.0, - overflow: TextOverflow.ellipsis, - ), - ), - const HSpace(4.0), - FlowySvg( - widget.isExpanded - ? FlowySvgs.workspace_drop_down_menu_show_s - : FlowySvgs.workspace_drop_down_menu_hide_s, + TextSpan( + text: Platform.isMacOS ? '⌘+O' : 'Ctrl+O', + style: Theme.of(context) + .tooltipTheme + .textStyle! + .copyWith(color: Theme.of(context).hintColor), ), ], ); + return FlowyTooltip( + richMessage: textSpan, + child: Row( + children: [ + SpaceIcon( + dimension: 20, + space: widget.space, + cornerRadius: 6.0, + ), + const HSpace(10), + Flexible( + child: FlowyText.medium( + widget.space.name, + lineHeight: 1.15, + fontSize: 14.0, + overflow: TextOverflow.ellipsis, + ), + ), + const HSpace(4.0), + FlowySvg( + widget.isExpanded + ? FlowySvgs.workspace_drop_down_menu_show_s + : FlowySvgs.workspace_drop_down_menu_hide_s, + ), + ], + ), + ); } Widget _buildRightIcon() { diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_action_type.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_action_type.dart index d3eca1ed2e..83472b7a80 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_action_type.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_action_type.dart @@ -55,7 +55,6 @@ extension ViewMoreActionTypeExtension on SpaceMoreActionType { Widget get rightIcon { switch (this) { case SpaceMoreActionType.changeIcon: - return const FlowySvg(FlowySvgs.view_item_right_arrow_s); case SpaceMoreActionType.rename: case SpaceMoreActionType.collapseAllPages: case SpaceMoreActionType.divider: diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_migration.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_migration.dart index 603b695378..905082721d 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_migration.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/sidebar/space/space_migration.dart @@ -1,5 +1,6 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; +import 'package:appflowy/util/theme_extension.dart'; import 'package:appflowy/workspace/application/sidebar/space/space_bloc.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -22,7 +23,9 @@ class _SpaceMigrationState extends State { padding: const EdgeInsets.all(12), clipBehavior: Clip.antiAlias, decoration: ShapeDecoration( - color: const Color(0x66F5EAFF), + color: Theme.of(context).isLightMode + ? const Color(0x66F5EAFF) + : const Color(0x1AFFFFFF), shape: RoundedRectangleBorder( side: const BorderSide( strokeAlign: BorderSide.strokeAlignOutside, @@ -129,13 +132,6 @@ class _MigrationTitle extends StatelessWidget { lineHeight: 1.2, ), ), - GestureDetector( - onTap: onClose, - child: const Padding( - padding: EdgeInsets.only(top: 3.0), - child: FlowySvg(FlowySvgs.upgrade_close_s), - ), - ), ], ); } diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_action_type.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_action_type.dart index 5b04e50501..3065eb7495 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_action_type.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/view/view_action_type.dart @@ -82,7 +82,6 @@ extension ViewMoreActionTypeExtension on ViewMoreActionType { switch (this) { case ViewMoreActionType.changeIcon: case ViewMoreActionType.moveTo: - return const FlowySvg(FlowySvgs.view_item_right_arrow_s); case ViewMoreActionType.favorite: case ViewMoreActionType.unFavorite: case ViewMoreActionType.duplicate: diff --git a/frontend/appflowy_flutter/pubspec.lock b/frontend/appflowy_flutter/pubspec.lock index cf31629dec..9b42182809 100644 --- a/frontend/appflowy_flutter/pubspec.lock +++ b/frontend/appflowy_flutter/pubspec.lock @@ -53,8 +53,8 @@ packages: dependency: "direct main" description: path: "." - ref: d73e8893d1f1b06566ebed5868698be75c6b7f93 - resolved-ref: d73e8893d1f1b06566ebed5868698be75c6b7f93 + ref: b5da6e4 + resolved-ref: b5da6e4afcb832ca74f588d7007824b02b5d51de url: "https://github.com/AppFlowy-IO/appflowy-editor.git" source: git version: "2.5.1" diff --git a/frontend/appflowy_flutter/pubspec.yaml b/frontend/appflowy_flutter/pubspec.yaml index a332e75572..586be65d58 100644 --- a/frontend/appflowy_flutter/pubspec.yaml +++ b/frontend/appflowy_flutter/pubspec.yaml @@ -186,7 +186,7 @@ dependency_overrides: appflowy_editor: git: url: https://github.com/AppFlowy-IO/appflowy-editor.git - ref: "d73e8893d1f1b06566ebed5868698be75c6b7f93" + ref: "b5da6e4" sheet: git: diff --git a/frontend/resources/flowy_icons/16x/icon_board.svg b/frontend/resources/flowy_icons/16x/icon_board.svg index 9133d535b5..80ab6bb759 100644 --- a/frontend/resources/flowy_icons/16x/icon_board.svg +++ b/frontend/resources/flowy_icons/16x/icon_board.svg @@ -1,5 +1,4 @@ - - - + + diff --git a/frontend/resources/flowy_icons/16x/more.svg b/frontend/resources/flowy_icons/16x/more.svg index da54e4b6e6..c7f6a4bbed 100644 --- a/frontend/resources/flowy_icons/16x/more.svg +++ b/frontend/resources/flowy_icons/16x/more.svg @@ -1,7 +1,3 @@ - - - - - - + + \ No newline at end of file diff --git a/frontend/resources/flowy_icons/16x/space_lock.svg b/frontend/resources/flowy_icons/16x/space_lock.svg index ec650f4136..a048076b50 100644 --- a/frontend/resources/flowy_icons/16x/space_lock.svg +++ b/frontend/resources/flowy_icons/16x/space_lock.svg @@ -1,3 +1,3 @@ - - + + diff --git a/frontend/resources/flowy_icons/24x/m_board_thumbnail.svg b/frontend/resources/flowy_icons/24x/m_board_thumbnail.svg index ecfec44bbf..685745b0b1 100644 --- a/frontend/resources/flowy_icons/24x/m_board_thumbnail.svg +++ b/frontend/resources/flowy_icons/24x/m_board_thumbnail.svg @@ -1,5 +1,4 @@ - - - + + diff --git a/frontend/resources/translations/en.json b/frontend/resources/translations/en.json index 4e510c25aa..e1af37761e 100644 --- a/frontend/resources/translations/en.json +++ b/frontend/resources/translations/en.json @@ -260,7 +260,7 @@ "recent": "Recent", "today": "Today", "thisWeek": "This week", - "others": "Others", + "others": "Other favorites", "justNow": "just now", "minutesAgo": "{count} minutes ago", "lastViewed": "Last viewed", @@ -1891,16 +1891,16 @@ "clearSearchTooltip": "Clear search field" }, "space": { - "delete": "Delete space", - "deleteConfirmation": "Are you sure you want to delete this space?", - "deleteConfirmationDescription": "This action cannot be undone, and will remove the pages and data in this space.", - "rename": "Rename space", + "delete": "Delete", + "deleteConfirmation": "Delete: {Space Name}", + "deleteConfirmationDescription": "All pages within this Space will be deleted and moved to Trash.", + "rename": "Rename Space", "changeIcon": "Change icon", - "manage": "Manage space", - "addNewSpace": "Add new space", + "manage": "Manage Space", + "addNewSpace": "Create Space", "collapseAllSubPages": "Collapse all subpages", - "createNewSpace": "Create new space", - "createSpaceDescription": "Separate your tabs for life, work, project and more", + "createNewSpace": "Create a new space", + "createSpaceDescription": "Create multiple public and private spaces to better organize your work.", "spaceName": "Space name", "permission": "Permission", "publicPermission": "Public", @@ -1910,14 +1910,15 @@ "spaceIconBackground": "Background color", "spaceIcon": "Icon", "dangerZone": "Danger Zone", - "unableToDeleteLastSpace": "Cannot delete the last space", - "unableToDeleteSpaceNotCreatedByYou": "Cannot delete a space created by others", - "enableSpacesForYourWorkspace": "Enable spaces for your workspace", + "unableToDeleteLastSpace": "Unable to delete the last Space", + "unableToDeleteSpaceNotCreatedByYou": "Unable to delete Spaces created by others", + "enableSpacesForYourWorkspace": "Enable Spaces for your workspace", "title": "Spaces", "defaultSpaceName": "General", - "upgradeSpaceTitle": "Enable Spaces for your workspace", - "upgradeSpaceDescription": "Create multiple public and private spaces to better organize your work.", - "upgrade": "Upgrade", - "upgradeYourSpace": "Upgrade your space" + "upgradeSpaceTitle": "Enable Spaces", + "upgradeSpaceDescription": "Create multiple public and private Spaces to better organize your workspace.", + "upgrade": "Update", + "upgradeYourSpace": "Create multiple Spaces", + "quicklySwitch": "Quickly switch to the next space" } -} \ No newline at end of file +}