mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: improve space UX (#5549)
* fix: scroll bar hover color * chore: replace board icon * chore: remove close button in upgrade menu * chore: add translation * feat: switch to the next space by shortcut * chore: replace space lock icon * chore: remove arrow icon in change icon button * feat: only show other favorites header if the other two is empty * fix: create new page in current space * feat: create a new page in a newly created space by default * chore: update translations * feat: open the first page after switching space by default * chore: replace board icon * fix: open the first page(non-space) after switching workspace * chore: revert more icon * chore: revert editor version
This commit is contained in:
@ -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<MobileViewItemBottomSheetBodyAction> _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<MobilePaneActionType> actions, {
|
||||
bool needSpace = true,
|
||||
MobileViewCardType? cardType,
|
||||
MobilePageCardType? cardType,
|
||||
FolderSpaceType? spaceType,
|
||||
}) {
|
||||
return ActionPane(
|
||||
|
@ -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<MobileFavoriteSpace>
|
||||
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -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<UserWorkspaceBloc, UserWorkspaceState>(
|
||||
|
@ -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<MobileRecentSpace>
|
||||
|
||||
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,
|
||||
),
|
||||
);
|
||||
},
|
||||
|
@ -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(),
|
||||
};
|
||||
}
|
||||
|
@ -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) {
|
@ -42,10 +42,6 @@ class _MobileSpaceState extends State<MobileSpace> {
|
||||
SpaceEvent.createPage(
|
||||
name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||
index: 0,
|
||||
viewSection: currentSpace.spacePermission ==
|
||||
SpacePermission.publicToAll
|
||||
? ViewSectionPB.Public
|
||||
: ViewSectionPB.Private,
|
||||
),
|
||||
);
|
||||
context.read<SpaceBloc>().add(
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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<SpaceEvent, SpaceState> {
|
||||
on<SpaceEvent>(
|
||||
(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<SpaceEvent, SpaceState> {
|
||||
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<SpaceEvent, SpaceState> {
|
||||
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<SpaceEvent, SpaceState> {
|
||||
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<SpaceEvent, SpaceState> {
|
||||
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<SpaceEvent, SpaceState> {
|
||||
// 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<SpaceEvent, SpaceState> {
|
||||
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
|
||||
|
@ -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<int> 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<HomeHotKeys> {
|
||||
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),
|
||||
];
|
||||
|
@ -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<Widget> _buildGroups(
|
||||
BuildContext context,
|
||||
List<ViewPB> 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),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
@ -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<UserWorkspaceBloc>().state.isCollabWorkspaceOn
|
||||
? ViewSectionPB.Private
|
||||
: ViewSectionPB.Public;
|
||||
context.read<SidebarSectionsBloc>().add(
|
||||
SidebarSectionsEvent.createRootViewInSection(
|
||||
name: viewName,
|
||||
viewSection: section,
|
||||
index: 0,
|
||||
),
|
||||
);
|
||||
final spaceState = context.read<SpaceBloc>().state;
|
||||
if (spaceState.spaces.isNotEmpty) {
|
||||
context.read<SpaceBloc>().add(
|
||||
SpaceEvent.createPage(
|
||||
name: viewName,
|
||||
index: 0,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
context.read<SidebarSectionsBloc>().add(
|
||||
SidebarSectionsEvent.createRootViewInSection(
|
||||
name: viewName,
|
||||
viewSection: section,
|
||||
index: 0,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -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<SpaceBloc, SpaceState>(
|
||||
listenWhen: (p, c) =>
|
||||
p.lastCreatedPage?.id != c.lastCreatedPage?.id,
|
||||
listener: (context, state) => context.read<TabsBloc>().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<TabsBloc>().add(
|
||||
TabsEvent.openPlugin(
|
||||
plugin: BlankPagePlugin(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
context.read<TabsBloc>().add(
|
||||
TabsEvent.openPlugin(
|
||||
plugin: state.lastCreatedPage!.plugin(),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
BlocListener<ActionNavigationBloc, ActionNavigationState>(
|
||||
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<SidebarSectionsBloc>().add(
|
||||
SidebarSectionsEvent.reload(
|
||||
userProfile,
|
||||
state.currentWorkspace?.workspaceId ??
|
||||
workspaceSetting.workspaceId,
|
||||
),
|
||||
);
|
||||
if (context.read<SpaceBloc>().state.spaces.isEmpty) {
|
||||
context.read<SidebarSectionsBloc>().add(
|
||||
SidebarSectionsEvent.reload(
|
||||
userProfile,
|
||||
state.currentWorkspace?.workspaceId ??
|
||||
workspaceSetting.workspaceId,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
context.read<SpaceBloc>().add(
|
||||
SpaceEvent.reset(
|
||||
userProfile,
|
||||
state.currentWorkspace?.workspaceId ??
|
||||
workspaceSetting.workspaceId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
context
|
||||
.read<FavoriteBloc>()
|
||||
.add(const FavoriteEvent.fetchFavorites());
|
||||
context.read<SpaceBloc>().add(
|
||||
SpaceEvent.reset(
|
||||
userProfile,
|
||||
state.currentWorkspace?.workspaceId ??
|
||||
workspaceSetting.workspaceId,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
|
@ -50,7 +50,13 @@ class _CreateSpacePopupState extends State<CreateSpacePopup> {
|
||||
),
|
||||
),
|
||||
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<CreateSpacePopup> {
|
||||
SpaceCancelOrConfirmButton(
|
||||
confirmButtonName: LocaleKeys.button_create.tr(),
|
||||
onCancel: () => Navigator.of(context).pop(),
|
||||
onConfirm: () {
|
||||
context.read<SpaceBloc>().add(
|
||||
SpaceEvent.create(
|
||||
name: spaceName,
|
||||
icon: spaceIcon,
|
||||
iconColor: spaceIconColor,
|
||||
permission: spacePermission,
|
||||
),
|
||||
);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
onConfirm: () => _createSpace(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _createSpace() {
|
||||
context.read<SpaceBloc>().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,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
@ -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,
|
||||
),
|
||||
|
@ -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<bool> 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<SpaceBloc, SpaceState>(
|
||||
@ -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<SpaceBloc>().add(const SpaceEvent.switchToNextSpace());
|
||||
}
|
||||
}
|
||||
|
||||
class _Pages extends StatelessWidget {
|
||||
|
@ -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<SidebarSpaceHeader> {
|
||||
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<SidebarSpaceHeader> {
|
||||
}
|
||||
|
||||
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() {
|
||||
|
@ -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:
|
||||
|
@ -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<SpaceMigration> {
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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"
|
||||
|
@ -186,7 +186,7 @@ dependency_overrides:
|
||||
appflowy_editor:
|
||||
git:
|
||||
url: https://github.com/AppFlowy-IO/appflowy-editor.git
|
||||
ref: "d73e8893d1f1b06566ebed5868698be75c6b7f93"
|
||||
ref: "b5da6e4"
|
||||
|
||||
sheet:
|
||||
git:
|
||||
|
Reference in New Issue
Block a user