mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: support slide actions on mobile (#5463)
* feat: support slide actions on mobile * fix: some ui issues * fix: scale down emoji size on windows * fix: flutter analyze * fix: force text height on macos
This commit is contained in:
parent
9f66dcdc8f
commit
e4eff7e632
@ -200,7 +200,7 @@ SPEC CHECKSUMS:
|
|||||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||||
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
flowy_infra_ui: 0455e1fa8c51885aa1437848e361e99419f34ebc
|
||||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||||
fluttertoast: fafc4fa4d01a6a9e4f772ecd190ffa525e9e2d9c
|
fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265
|
||||||
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
image_gallery_saver: cb43cc43141711190510e92c460eb1655cd343cb
|
||||||
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
image_picker_ios: 99dfe1854b4fa34d0364e74a78448a0151025425
|
||||||
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4
|
||||||
@ -227,4 +227,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
PODFILE CHECKSUM: d0d9b4ff572d8695c38eb3f9b490f55cdfc57eca
|
||||||
|
|
||||||
COCOAPODS: 1.11.3
|
COCOAPODS: 1.15.2
|
||||||
|
@ -21,6 +21,7 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
FlowyOptionTile.text(
|
FlowyOptionTile.text(
|
||||||
text: LocaleKeys.document_menuName.tr(),
|
text: LocaleKeys.document_menuName.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.document_s,
|
FlowySvgs.document_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
@ -30,6 +31,7 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
FlowyOptionTile.text(
|
FlowyOptionTile.text(
|
||||||
text: LocaleKeys.grid_menuName.tr(),
|
text: LocaleKeys.grid_menuName.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.grid_s,
|
FlowySvgs.grid_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
@ -39,6 +41,7 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
FlowyOptionTile.text(
|
FlowyOptionTile.text(
|
||||||
text: LocaleKeys.board_menuName.tr(),
|
text: LocaleKeys.board_menuName.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.board_s,
|
FlowySvgs.board_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
@ -48,6 +51,7 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
FlowyOptionTile.text(
|
FlowyOptionTile.text(
|
||||||
text: LocaleKeys.calendar_menuName.tr(),
|
text: LocaleKeys.calendar_menuName.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.calendar_s,
|
FlowySvgs.calendar_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
@ -57,6 +61,7 @@ class AddNewPageWidgetBottomSheet extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
FlowyOptionTile.text(
|
FlowyOptionTile.text(
|
||||||
text: LocaleKeys.chat_newChat.tr(),
|
text: LocaleKeys.chat_newChat.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.chat_ai_page_s,
|
FlowySvgs.chat_ai_page_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
|
@ -44,6 +44,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
case MobileViewItemBottomSheetBodyAction.rename:
|
case MobileViewItemBottomSheetBodyAction.rename:
|
||||||
return FlowyOptionTile.text(
|
return FlowyOptionTile.text(
|
||||||
text: LocaleKeys.button_rename.tr(),
|
text: LocaleKeys.button_rename.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.view_item_rename_s,
|
FlowySvgs.view_item_rename_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
@ -57,6 +58,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
case MobileViewItemBottomSheetBodyAction.duplicate:
|
case MobileViewItemBottomSheetBodyAction.duplicate:
|
||||||
return FlowyOptionTile.text(
|
return FlowyOptionTile.text(
|
||||||
text: LocaleKeys.button_duplicate.tr(),
|
text: LocaleKeys.button_duplicate.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.duplicate_s,
|
FlowySvgs.duplicate_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
@ -71,6 +73,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
case MobileViewItemBottomSheetBodyAction.share:
|
case MobileViewItemBottomSheetBodyAction.share:
|
||||||
return FlowyOptionTile.text(
|
return FlowyOptionTile.text(
|
||||||
text: LocaleKeys.button_share.tr(),
|
text: LocaleKeys.button_share.tr(),
|
||||||
|
height: 52.0,
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.share_s,
|
FlowySvgs.share_s,
|
||||||
size: Size.square(18),
|
size: Size.square(18),
|
||||||
@ -84,9 +87,10 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
case MobileViewItemBottomSheetBodyAction.delete:
|
case MobileViewItemBottomSheetBodyAction.delete:
|
||||||
return FlowyOptionTile.text(
|
return FlowyOptionTile.text(
|
||||||
text: LocaleKeys.button_delete.tr(),
|
text: LocaleKeys.button_delete.tr(),
|
||||||
|
height: 52.0,
|
||||||
textColor: Theme.of(context).colorScheme.error,
|
textColor: Theme.of(context).colorScheme.error,
|
||||||
leftIcon: FlowySvg(
|
leftIcon: FlowySvg(
|
||||||
FlowySvgs.delete_s,
|
FlowySvgs.trash_s,
|
||||||
size: const Size.square(18),
|
size: const Size.square(18),
|
||||||
color: Theme.of(context).colorScheme.error,
|
color: Theme.of(context).colorScheme.error,
|
||||||
),
|
),
|
||||||
@ -98,6 +102,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
case MobileViewItemBottomSheetBodyAction.addToFavorites:
|
||||||
return FlowyOptionTile.text(
|
return FlowyOptionTile.text(
|
||||||
|
height: 52.0,
|
||||||
text: LocaleKeys.button_addToFavorites.tr(),
|
text: LocaleKeys.button_addToFavorites.tr(),
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.favorite_s,
|
FlowySvgs.favorite_s,
|
||||||
@ -111,6 +116,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
case MobileViewItemBottomSheetBodyAction.removeFromFavorites:
|
||||||
return FlowyOptionTile.text(
|
return FlowyOptionTile.text(
|
||||||
|
height: 52.0,
|
||||||
text: LocaleKeys.button_removeFromFavorites.tr(),
|
text: LocaleKeys.button_removeFromFavorites.tr(),
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.favorite_section_remove_from_favorite_s,
|
FlowySvgs.favorite_section_remove_from_favorite_s,
|
||||||
@ -124,6 +130,7 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
case MobileViewItemBottomSheetBodyAction.removeFromRecent:
|
case MobileViewItemBottomSheetBodyAction.removeFromRecent:
|
||||||
return FlowyOptionTile.text(
|
return FlowyOptionTile.text(
|
||||||
|
height: 52.0,
|
||||||
text: LocaleKeys.button_removeFromRecent.tr(),
|
text: LocaleKeys.button_removeFromRecent.tr(),
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.remove_from_recent_s,
|
FlowySvgs.remove_from_recent_s,
|
||||||
@ -137,7 +144,10 @@ class MobileViewItemBottomSheetBody extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
case MobileViewItemBottomSheetBodyAction.divider:
|
case MobileViewItemBottomSheetBodyAction.divider:
|
||||||
return const Divider(height: 0.5);
|
return const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
|
child: Divider(height: 0.5),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
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/bottom_sheet/bottom_sheet.dart';
|
||||||
|
import 'package:appflowy/mobile/presentation/home/shared/mobile_view_card.dart';
|
||||||
import 'package:appflowy/mobile/presentation/page_item/mobile_slide_action_button.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/favorite/favorite_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/recent/recent_views_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:flowy_infra/theme_extension.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/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:flutter_slidable/flutter_slidable.dart';
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
@ -13,11 +18,14 @@ enum MobilePaneActionType {
|
|||||||
delete,
|
delete,
|
||||||
addToFavorites,
|
addToFavorites,
|
||||||
removeFromFavorites,
|
removeFromFavorites,
|
||||||
more;
|
more,
|
||||||
|
add;
|
||||||
|
|
||||||
MobileSlideActionButton actionButton(
|
MobileSlideActionButton actionButton(
|
||||||
BuildContext context,
|
BuildContext context, {
|
||||||
) {
|
MobileViewCardType? cardType,
|
||||||
|
FolderSpaceType? spaceType,
|
||||||
|
}) {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case MobilePaneActionType.delete:
|
case MobilePaneActionType.delete:
|
||||||
return MobileSlideActionButton(
|
return MobileSlideActionButton(
|
||||||
@ -29,59 +37,88 @@ enum MobilePaneActionType {
|
|||||||
);
|
);
|
||||||
case MobilePaneActionType.removeFromFavorites:
|
case MobilePaneActionType.removeFromFavorites:
|
||||||
return MobileSlideActionButton(
|
return MobileSlideActionButton(
|
||||||
backgroundColor: Colors.orange,
|
backgroundColor: const Color(0xFFFA217F),
|
||||||
svg: FlowySvgs.favorite_s,
|
svg: FlowySvgs.favorite_section_remove_from_favorite_s,
|
||||||
|
size: 24.0,
|
||||||
onPressed: (context) => context
|
onPressed: (context) => context
|
||||||
.read<FavoriteBloc>()
|
.read<FavoriteBloc>()
|
||||||
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
|
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
|
||||||
);
|
);
|
||||||
case MobilePaneActionType.addToFavorites:
|
case MobilePaneActionType.addToFavorites:
|
||||||
return MobileSlideActionButton(
|
return MobileSlideActionButton(
|
||||||
backgroundColor: Colors.orange,
|
backgroundColor: const Color(0xFF00C8FF),
|
||||||
svg: FlowySvgs.m_favorite_unselected_lg,
|
svg: FlowySvgs.favorite_s,
|
||||||
size: 34.0,
|
size: 24.0,
|
||||||
onPressed: (context) => context
|
onPressed: (context) => context
|
||||||
.read<FavoriteBloc>()
|
.read<FavoriteBloc>()
|
||||||
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
|
.add(FavoriteEvent.toggle(context.read<ViewBloc>().view)),
|
||||||
);
|
);
|
||||||
case MobilePaneActionType.more:
|
case MobilePaneActionType.add:
|
||||||
return MobileSlideActionButton(
|
return MobileSlideActionButton(
|
||||||
backgroundColor: Colors.grey,
|
backgroundColor: const Color(0xFF00C8FF),
|
||||||
svg: FlowySvgs.three_dots_vertical_s,
|
svg: FlowySvgs.add_m,
|
||||||
size: 28.0,
|
size: 28.0,
|
||||||
|
onPressed: (context) {
|
||||||
|
final viewBloc = context.read<ViewBloc>();
|
||||||
|
final view = viewBloc.state.view;
|
||||||
|
final title = view.name;
|
||||||
|
showMobileBottomSheet(
|
||||||
|
context,
|
||||||
|
showHeader: true,
|
||||||
|
title: title,
|
||||||
|
showDragHandle: true,
|
||||||
|
showCloseButton: true,
|
||||||
|
useRootNavigator: true,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
|
builder: (sheetContext) {
|
||||||
|
return AddNewPageWidgetBottomSheet(
|
||||||
|
view: view,
|
||||||
|
onAction: (layout) {
|
||||||
|
context.read<ViewBloc>().add(
|
||||||
|
ViewEvent.createView(
|
||||||
|
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
||||||
|
layout,
|
||||||
|
section: spaceType!.toViewSectionPB,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case MobilePaneActionType.more:
|
||||||
|
return MobileSlideActionButton(
|
||||||
|
backgroundColor: const Color(0xE5515563),
|
||||||
|
svg: FlowySvgs.three_dots_s,
|
||||||
|
size: 24.0,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(10),
|
||||||
|
bottomLeft: Radius.circular(10),
|
||||||
|
),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
final viewBloc = context.read<ViewBloc>();
|
final viewBloc = context.read<ViewBloc>();
|
||||||
final favoriteBloc = context.read<FavoriteBloc>();
|
final favoriteBloc = context.read<FavoriteBloc>();
|
||||||
|
final recentViewsBloc = context.read<RecentViewsBloc?>();
|
||||||
showMobileBottomSheet(
|
showMobileBottomSheet(
|
||||||
context,
|
context,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
showDivider: false,
|
showDivider: false,
|
||||||
backgroundColor: AFThemeExtension.of(context).background,
|
|
||||||
useRootNavigator: true,
|
useRootNavigator: true,
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return MultiBlocProvider(
|
return MultiBlocProvider(
|
||||||
providers: [
|
providers: [
|
||||||
BlocProvider.value(value: viewBloc),
|
BlocProvider.value(value: viewBloc),
|
||||||
BlocProvider.value(value: favoriteBloc),
|
BlocProvider.value(value: favoriteBloc),
|
||||||
|
if (recentViewsBloc != null)
|
||||||
|
BlocProvider.value(value: recentViewsBloc),
|
||||||
],
|
],
|
||||||
child: BlocBuilder<ViewBloc, ViewState>(
|
child: BlocBuilder<ViewBloc, ViewState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
final isFavorite = state.view.isFavorite;
|
|
||||||
return MobileViewItemBottomSheet(
|
return MobileViewItemBottomSheet(
|
||||||
view: viewBloc.state.view,
|
view: viewBloc.state.view,
|
||||||
actions: [
|
actions: _buildActions(state.view, cardType: cardType),
|
||||||
isFavorite
|
|
||||||
? MobileViewItemBottomSheetBodyAction
|
|
||||||
.removeFromFavorites
|
|
||||||
: MobileViewItemBottomSheetBodyAction
|
|
||||||
.addToFavorites,
|
|
||||||
MobileViewItemBottomSheetBodyAction.divider,
|
|
||||||
MobileViewItemBottomSheetBodyAction.rename,
|
|
||||||
if (state.view.layout != ViewLayoutPB.Chat)
|
|
||||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
|
||||||
MobileViewItemBottomSheetBodyAction.divider,
|
|
||||||
MobileViewItemBottomSheetBodyAction.delete,
|
|
||||||
],
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -92,19 +129,71 @@ enum MobilePaneActionType {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<MobileViewItemBottomSheetBodyAction> _buildActions(
|
||||||
|
ViewPB view, {
|
||||||
|
MobileViewCardType? cardType,
|
||||||
|
}) {
|
||||||
|
final isFavorite = view.isFavorite;
|
||||||
|
|
||||||
|
if (cardType != null) {
|
||||||
|
switch (cardType) {
|
||||||
|
case MobileViewCardType.recent:
|
||||||
|
return [
|
||||||
|
isFavorite
|
||||||
|
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||||
|
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||||
|
MobileViewItemBottomSheetBodyAction.divider,
|
||||||
|
if (view.layout != ViewLayoutPB.Chat)
|
||||||
|
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||||
|
MobileViewItemBottomSheetBodyAction.divider,
|
||||||
|
MobileViewItemBottomSheetBodyAction.removeFromRecent,
|
||||||
|
];
|
||||||
|
case MobileViewCardType.favorite:
|
||||||
|
return [
|
||||||
|
isFavorite
|
||||||
|
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||||
|
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||||
|
MobileViewItemBottomSheetBodyAction.divider,
|
||||||
|
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
isFavorite
|
||||||
|
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
||||||
|
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
||||||
|
MobileViewItemBottomSheetBodyAction.divider,
|
||||||
|
MobileViewItemBottomSheetBodyAction.rename,
|
||||||
|
if (view.layout != ViewLayoutPB.Chat)
|
||||||
|
MobileViewItemBottomSheetBodyAction.duplicate,
|
||||||
|
MobileViewItemBottomSheetBodyAction.divider,
|
||||||
|
MobileViewItemBottomSheetBodyAction.delete,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionPane buildEndActionPane(
|
ActionPane buildEndActionPane(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
List<MobilePaneActionType> actions,
|
List<MobilePaneActionType> actions, {
|
||||||
) {
|
bool needSpace = true,
|
||||||
|
MobileViewCardType? cardType,
|
||||||
|
FolderSpaceType? spaceType,
|
||||||
|
}) {
|
||||||
|
debugPrint('actions: $actions');
|
||||||
return ActionPane(
|
return ActionPane(
|
||||||
motion: const ScrollMotion(),
|
motion: const ScrollMotion(),
|
||||||
extentRatio: actions.length / 5,
|
extentRatio: actions.length / 5,
|
||||||
children: actions
|
children: [
|
||||||
.map(
|
if (needSpace) const HSpace(20),
|
||||||
(action) => action.actionButton(context),
|
...actions.map(
|
||||||
)
|
(action) => action.actionButton(
|
||||||
.toList(),
|
context,
|
||||||
|
spaceType: spaceType,
|
||||||
|
cardType: cardType,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -63,12 +63,16 @@ class MobileFavoriteFolder extends StatelessWidget {
|
|||||||
view: view,
|
view: view,
|
||||||
level: 0,
|
level: 0,
|
||||||
onSelected: context.pushView,
|
onSelected: context.pushView,
|
||||||
endActionPane: (context) => buildEndActionPane(context, [
|
endActionPane: (context) => buildEndActionPane(
|
||||||
|
context,
|
||||||
|
[
|
||||||
view.isFavorite
|
view.isFavorite
|
||||||
? MobilePaneActionType.removeFromFavorites
|
? MobilePaneActionType.removeFromFavorites
|
||||||
: MobilePaneActionType.addToFavorites,
|
: MobilePaneActionType.addToFavorites,
|
||||||
MobilePaneActionType.more,
|
MobilePaneActionType.more,
|
||||||
]),
|
],
|
||||||
|
spaceType: FolderSpaceType.favorite,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -111,7 +111,7 @@ class _TrashButton extends StatelessWidget {
|
|||||||
height: 52,
|
height: 52,
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
expand: true,
|
expand: true,
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 2.0),
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.m_delete_s,
|
FlowySvgs.m_delete_s,
|
||||||
),
|
),
|
||||||
|
@ -123,12 +123,13 @@ class _MobileWorkspace extends StatelessWidget {
|
|||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
SizedBox.square(
|
SizedBox.square(
|
||||||
dimension: 34.0,
|
dimension: currentWorkspace.icon.isNotEmpty ? 34.0 : 26.0,
|
||||||
child: WorkspaceIcon(
|
child: WorkspaceIcon(
|
||||||
workspace: currentWorkspace,
|
workspace: currentWorkspace,
|
||||||
iconSize: 26,
|
iconSize: 26,
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
enableEdit: false,
|
enableEdit: false,
|
||||||
|
alignment: Alignment.centerLeft,
|
||||||
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
onSelected: (result) => context.read<UserWorkspaceBloc>().add(
|
||||||
UserWorkspaceEvent.updateWorkspaceIcon(
|
UserWorkspaceEvent.updateWorkspaceIcon(
|
||||||
currentWorkspace.workspaceId,
|
currentWorkspace.workspaceId,
|
||||||
@ -137,33 +138,14 @@ class _MobileWorkspace extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const HSpace(8),
|
currentWorkspace.icon.isNotEmpty
|
||||||
Expanded(
|
? const HSpace(2)
|
||||||
child: Column(
|
: const HSpace(8),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
FlowyText.semibold(
|
FlowyText.semibold(
|
||||||
currentWorkspace.name,
|
currentWorkspace.name,
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
const HSpace(4.0),
|
|
||||||
const FlowySvg(FlowySvgs.list_dropdown_s),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
FlowyText.regular(
|
|
||||||
userProfile.email.isNotEmpty
|
|
||||||
? userProfile.email
|
|
||||||
: userProfile.name,
|
|
||||||
overflow: TextOverflow.ellipsis,
|
|
||||||
fontSize: 12,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -179,7 +161,9 @@ class _MobileWorkspace extends StatelessWidget {
|
|||||||
showDivider: false,
|
showDivider: false,
|
||||||
showHeader: true,
|
showHeader: true,
|
||||||
showDragHandle: true,
|
showDragHandle: true,
|
||||||
|
showCloseButton: true,
|
||||||
title: LocaleKeys.workspace_menuTitle.tr(),
|
title: LocaleKeys.workspace_menuTitle.tr(),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return BlocProvider.value(
|
return BlocProvider.value(
|
||||||
value: context.read<UserWorkspaceBloc>(),
|
value: context.read<UserWorkspaceBloc>(),
|
||||||
|
@ -6,6 +6,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder/protobuf.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
|
|
||||||
class MobileRecentSpace extends StatefulWidget {
|
class MobileRecentSpace extends StatefulWidget {
|
||||||
const MobileRecentSpace({super.key});
|
const MobileRecentSpace({super.key});
|
||||||
@ -62,7 +63,8 @@ class _RecentViews extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scrollbar(
|
return SlidableAutoCloseBehavior(
|
||||||
|
child: Scrollbar(
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
key: const PageStorageKey('recent_views_page_storage_key'),
|
key: const PageStorageKey('recent_views_page_storage_key'),
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
@ -91,6 +93,7 @@ class _RecentViews extends StatelessWidget {
|
|||||||
separatorBuilder: (context, index) => const HSpace(8),
|
separatorBuilder: (context, index) => const HSpace(8),
|
||||||
itemCount: recentViews.length,
|
itemCount: recentViews.length,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/application/mobile_router.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/section_folder/mobile_home_section_folder_header.dart';
|
import 'package:appflowy/mobile/presentation/home/section_folder/mobile_home_section_folder_header.dart';
|
||||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item.dart';
|
||||||
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
import 'package:appflowy/workspace/application/menu/sidebar_sections_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
@ -69,6 +71,19 @@ class MobileSectionFolder extends StatelessWidget {
|
|||||||
leftPadding: HomeSpaceViewSizes.leftPadding,
|
leftPadding: HomeSpaceViewSizes.leftPadding,
|
||||||
isFeedback: false,
|
isFeedback: false,
|
||||||
onSelected: context.pushView,
|
onSelected: context.pushView,
|
||||||
|
endActionPane: (context) {
|
||||||
|
final view = context.read<ViewBloc>().state.view;
|
||||||
|
return buildEndActionPane(
|
||||||
|
context,
|
||||||
|
[
|
||||||
|
MobilePaneActionType.more,
|
||||||
|
if (view.layout == ViewLayoutPB.Document)
|
||||||
|
MobilePaneActionType.add,
|
||||||
|
],
|
||||||
|
spaceType: spaceType,
|
||||||
|
needSpace: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -38,7 +38,7 @@ class _MobileSectionFolderHeaderState extends State<MobileSectionFolderHeader> {
|
|||||||
widget.title,
|
widget.title,
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
),
|
),
|
||||||
margin: const EdgeInsets.symmetric(vertical: 8),
|
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 2.0),
|
||||||
expandText: false,
|
expandText: false,
|
||||||
iconPadding: 2,
|
iconPadding: 2,
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
@ -22,6 +22,7 @@ import 'package:flowy_infra/theme_extension.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
import 'package:flutter_slidable/flutter_slidable.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:string_validator/string_validator.dart';
|
import 'package:string_validator/string_validator.dart';
|
||||||
@ -63,7 +64,18 @@ class MobileViewCard extends StatelessWidget {
|
|||||||
],
|
],
|
||||||
child: BlocBuilder<RecentViewBloc, RecentViewState>(
|
child: BlocBuilder<RecentViewBloc, RecentViewState>(
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
return GestureDetector(
|
return Slidable(
|
||||||
|
endActionPane: buildEndActionPane(
|
||||||
|
context,
|
||||||
|
[
|
||||||
|
MobilePaneActionType.more,
|
||||||
|
context.watch<ViewBloc>().state.view.isFavorite
|
||||||
|
? MobilePaneActionType.removeFromFavorites
|
||||||
|
: MobilePaneActionType.addToFavorites,
|
||||||
|
],
|
||||||
|
cardType: type,
|
||||||
|
),
|
||||||
|
child: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTapUp: (_) => context.pushView(view),
|
onTapUp: (_) => context.pushView(view),
|
||||||
onLongPressUp: () => _showActionSheet(context),
|
onLongPressUp: () => _showActionSheet(context),
|
||||||
@ -79,6 +91,7 @@ class MobileViewCard extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||||
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
import 'package:appflowy/mobile/presentation/widgets/widgets.dart';
|
||||||
|
import 'package:appflowy/util/theme_extension.dart';
|
||||||
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
import 'package:appflowy/workspace/application/user/user_workspace_bloc.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/sidebar/workspace/_sidebar_workspace_icon.dart';
|
||||||
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart';
|
import 'package:appflowy/workspace/presentation/settings/widgets/members/workspace_member_bloc.dart';
|
||||||
@ -27,7 +28,13 @@ class MobileWorkspaceMenu extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final List<Widget> children = [];
|
final List<Widget> children = [
|
||||||
|
_WorkspaceUserItem(userProfile: userProfile),
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||||
|
child: Divider(height: 0.5),
|
||||||
|
),
|
||||||
|
];
|
||||||
for (var i = 0; i < workspaces.length; i++) {
|
for (var i = 0; i < workspaces.length; i++) {
|
||||||
final workspace = workspaces[i];
|
final workspace = workspaces[i];
|
||||||
children.add(
|
children.add(
|
||||||
@ -35,7 +42,7 @@ class MobileWorkspaceMenu extends StatelessWidget {
|
|||||||
key: ValueKey(workspace.workspaceId),
|
key: ValueKey(workspace.workspaceId),
|
||||||
userProfile: userProfile,
|
userProfile: userProfile,
|
||||||
workspace: workspace,
|
workspace: workspace,
|
||||||
showTopBorder: i == 0,
|
showTopBorder: false,
|
||||||
currentWorkspace: currentWorkspace,
|
currentWorkspace: currentWorkspace,
|
||||||
onWorkspaceSelected: onWorkspaceSelected,
|
onWorkspaceSelected: onWorkspaceSelected,
|
||||||
),
|
),
|
||||||
@ -47,6 +54,34 @@ class MobileWorkspaceMenu extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _WorkspaceUserItem extends StatelessWidget {
|
||||||
|
const _WorkspaceUserItem({required this.userProfile});
|
||||||
|
|
||||||
|
final UserProfilePB userProfile;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final color = Theme.of(context).isLightMode
|
||||||
|
? const Color(0x99333333)
|
||||||
|
: const Color(0x99CCCCCC);
|
||||||
|
return FlowyOptionTile.text(
|
||||||
|
height: 32,
|
||||||
|
showTopBorder: false,
|
||||||
|
showBottomBorder: false,
|
||||||
|
content: Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(),
|
||||||
|
child: FlowyText(
|
||||||
|
userProfile.email,
|
||||||
|
fontSize: 14,
|
||||||
|
color: color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _WorkspaceMenuItem extends StatelessWidget {
|
class _WorkspaceMenuItem extends StatelessWidget {
|
||||||
const _WorkspaceMenuItem({
|
const _WorkspaceMenuItem({
|
||||||
super.key,
|
super.key,
|
||||||
@ -102,6 +137,7 @@ class _WorkspaceMenuItem extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
height: 60,
|
height: 60,
|
||||||
showTopBorder: showTopBorder,
|
showTopBorder: showTopBorder,
|
||||||
|
showBottomBorder: false,
|
||||||
leftIcon: WorkspaceIcon(
|
leftIcon: WorkspaceIcon(
|
||||||
enableEdit: false,
|
enableEdit: false,
|
||||||
iconSize: 26,
|
iconSize: 26,
|
||||||
|
@ -9,6 +9,7 @@ class MobileSlideActionButton extends StatelessWidget {
|
|||||||
required this.svg,
|
required this.svg,
|
||||||
this.size = 32.0,
|
this.size = 32.0,
|
||||||
this.backgroundColor = Colors.transparent,
|
this.backgroundColor = Colors.transparent,
|
||||||
|
this.borderRadius = BorderRadius.zero,
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -16,15 +17,18 @@ class MobileSlideActionButton extends StatelessWidget {
|
|||||||
final double size;
|
final double size;
|
||||||
final Color backgroundColor;
|
final Color backgroundColor;
|
||||||
final SlidableActionCallback onPressed;
|
final SlidableActionCallback onPressed;
|
||||||
|
final BorderRadius borderRadius;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return CustomSlidableAction(
|
return CustomSlidableAction(
|
||||||
|
borderRadius: borderRadius,
|
||||||
backgroundColor: backgroundColor,
|
backgroundColor: backgroundColor,
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
HapticFeedback.mediumImpact();
|
HapticFeedback.mediumImpact();
|
||||||
onPressed(context);
|
onPressed(context);
|
||||||
},
|
},
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
child: FlowySvg(
|
child: FlowySvg(
|
||||||
svg,
|
svg,
|
||||||
size: Size.square(size),
|
size: Size.square(size),
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
|
||||||
import 'package:appflowy/mobile/application/mobile_router.dart';
|
import 'package:appflowy/mobile/application/mobile_router.dart';
|
||||||
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
|
|
||||||
import 'package:appflowy/mobile/presentation/page_item/mobile_view_item_add_button.dart';
|
|
||||||
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
|
|
||||||
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
import 'package:appflowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:appflowy/workspace/application/view/view_ext.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/home_sizes.dart';
|
||||||
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
|
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.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:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
@ -270,17 +265,6 @@ class _SingleMobileInnerViewItemState extends State<SingleMobileInnerViewItem> {
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
// hover action
|
|
||||||
|
|
||||||
// ··· more action button
|
|
||||||
children.add(_buildViewMoreButton(context));
|
|
||||||
// only support add button for document layout
|
|
||||||
if (!widget.isFeedback && widget.view.layout == ViewLayoutPB.Document) {
|
|
||||||
// + button
|
|
||||||
|
|
||||||
children.add(_buildViewAddButton(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget child = InkWell(
|
Widget child = InkWell(
|
||||||
borderRadius: BorderRadius.circular(4.0),
|
borderRadius: BorderRadius.circular(4.0),
|
||||||
onTap: () => widget.onSelected(widget.view),
|
onTap: () => widget.onSelected(widget.view),
|
||||||
@ -345,86 +329,6 @@ class _SingleMobileInnerViewItemState extends State<SingleMobileInnerViewItem> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// + button
|
|
||||||
Widget _buildViewAddButton(BuildContext context) {
|
|
||||||
return MobileViewAddButton(
|
|
||||||
onPressed: () {
|
|
||||||
final title = widget.view.name;
|
|
||||||
showMobileBottomSheet(
|
|
||||||
context,
|
|
||||||
showHeader: true,
|
|
||||||
title: title,
|
|
||||||
showDragHandle: true,
|
|
||||||
showCloseButton: true,
|
|
||||||
useRootNavigator: true,
|
|
||||||
builder: (sheetContext) {
|
|
||||||
return AddNewPageWidgetBottomSheet(
|
|
||||||
view: widget.view,
|
|
||||||
onAction: (layout) {
|
|
||||||
Navigator.of(sheetContext).pop();
|
|
||||||
context.read<ViewBloc>().add(
|
|
||||||
ViewEvent.createView(
|
|
||||||
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
|
|
||||||
layout,
|
|
||||||
section: widget.spaceType != FolderSpaceType.favorite
|
|
||||||
? widget.spaceType.toViewSectionPB
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// + button
|
|
||||||
Widget _buildViewMoreButton(BuildContext context) {
|
|
||||||
return MobileViewMoreButton(onPressed: () => _showMoreActions(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _showMoreActions(BuildContext context) async {
|
|
||||||
final viewBloc = context.read<ViewBloc>();
|
|
||||||
final favoriteBloc = context.read<FavoriteBloc>();
|
|
||||||
await showMobileBottomSheet(
|
|
||||||
context,
|
|
||||||
showHeader: true,
|
|
||||||
title: widget.view.name,
|
|
||||||
showDragHandle: true,
|
|
||||||
showCloseButton: true,
|
|
||||||
useRootNavigator: true,
|
|
||||||
builder: (context) {
|
|
||||||
return MultiBlocProvider(
|
|
||||||
providers: [
|
|
||||||
BlocProvider.value(value: viewBloc),
|
|
||||||
BlocProvider.value(value: favoriteBloc),
|
|
||||||
],
|
|
||||||
child: BlocBuilder<ViewBloc, ViewState>(
|
|
||||||
builder: (context, state) {
|
|
||||||
final isFavorite = state.view.isFavorite;
|
|
||||||
return MobileViewItemBottomSheet(
|
|
||||||
view: viewBloc.state.view,
|
|
||||||
actions: [
|
|
||||||
isFavorite
|
|
||||||
? MobileViewItemBottomSheetBodyAction.removeFromFavorites
|
|
||||||
: MobileViewItemBottomSheetBodyAction.addToFavorites,
|
|
||||||
MobileViewItemBottomSheetBodyAction.divider,
|
|
||||||
MobileViewItemBottomSheetBodyAction.rename,
|
|
||||||
MobileViewItemBottomSheetBodyAction.divider,
|
|
||||||
if (state.view.layout != ViewLayoutPB.Chat)
|
|
||||||
MobileViewItemBottomSheetBodyAction.duplicate,
|
|
||||||
MobileViewItemBottomSheetBodyAction.divider,
|
|
||||||
MobileViewItemBottomSheetBodyAction.delete,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround: we should use view.isEndPoint or something to check if the view can contain child views. But currently, we don't have that field.
|
// workaround: we should use view.isEndPoint or something to check if the view can contain child views. But currently, we don't have that field.
|
||||||
|
@ -189,11 +189,13 @@ class FavoriteMoreButton extends StatelessWidget {
|
|||||||
margin: EdgeInsets.zero,
|
margin: EdgeInsets.zero,
|
||||||
child: FlowyButton(
|
child: FlowyButton(
|
||||||
onTap: () {},
|
onTap: () {},
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 7.0),
|
margin: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 7.0),
|
||||||
leftIcon: const FlowySvg(
|
leftIcon: const FlowySvg(
|
||||||
FlowySvgs.workspace_three_dots_s,
|
FlowySvgs.workspace_three_dots_s,
|
||||||
),
|
),
|
||||||
text: FlowyText.regular(LocaleKeys.button_more.tr()),
|
text: FlowyText.regular(
|
||||||
|
LocaleKeys.button_more.tr(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ class SidebarTopMenu extends StatelessWidget {
|
|||||||
builder: (_, value, ___) => Opacity(
|
builder: (_, value, ___) => Opacity(
|
||||||
opacity: value ? 1 : 0,
|
opacity: value ? 1 : 0,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(top: 12.0, right: 4.0),
|
padding: const EdgeInsets.only(top: 12.0, right: 6.0),
|
||||||
child: FlowyTooltip(
|
child: FlowyTooltip(
|
||||||
richMessage: textSpan,
|
richMessage: textSpan,
|
||||||
child: Listener(
|
child: Listener(
|
||||||
|
@ -191,6 +191,7 @@ class _SidebarState extends State<_Sidebar> {
|
|||||||
Timer? _scrollDebounce;
|
Timer? _scrollDebounce;
|
||||||
bool _isScrolling = false;
|
bool _isScrolling = false;
|
||||||
final _isHovered = ValueNotifier(false);
|
final _isHovered = ValueNotifier(false);
|
||||||
|
final _scrollOffset = ValueNotifier<double>(0);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -203,6 +204,7 @@ class _SidebarState extends State<_Sidebar> {
|
|||||||
_scrollDebounce?.cancel();
|
_scrollDebounce?.cancel();
|
||||||
_scrollController.removeListener(_onScrollChanged);
|
_scrollController.removeListener(_onScrollChanged);
|
||||||
_scrollController.dispose();
|
_scrollController.dispose();
|
||||||
|
_scrollOffset.dispose();
|
||||||
_isHovered.dispose();
|
_isHovered.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
@ -255,13 +257,22 @@ class _SidebarState extends State<_Sidebar> {
|
|||||||
const SidebarNewPageButton(),
|
const SidebarNewPageButton(),
|
||||||
// scrollable document list
|
// scrollable document list
|
||||||
const VSpace(12.0),
|
const VSpace(12.0),
|
||||||
const Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 12.0),
|
padding: const EdgeInsets.symmetric(horizontal: 12.0),
|
||||||
child: Divider(
|
child: ValueListenableBuilder(
|
||||||
color: Color(0x1E1F2329),
|
valueListenable: _scrollOffset,
|
||||||
|
builder: (_, offset, child) {
|
||||||
|
return Opacity(
|
||||||
|
opacity: offset > 0 ? 1 : 0,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const Divider(
|
||||||
|
color: Color(0x141F2329),
|
||||||
height: 0.5,
|
height: 0.5,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: menuHorizontalInset - const EdgeInsets.only(right: 6),
|
padding: menuHorizontalInset - const EdgeInsets.only(right: 6),
|
||||||
@ -281,7 +292,7 @@ class _SidebarState extends State<_Sidebar> {
|
|||||||
Padding(
|
Padding(
|
||||||
padding: menuHorizontalInset +
|
padding: menuHorizontalInset +
|
||||||
const EdgeInsets.symmetric(horizontal: 4.0),
|
const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
child: const Divider(height: 1.0, color: Color(0x141F2329)),
|
child: const Divider(height: 0.5, color: Color(0x141F2329)),
|
||||||
),
|
),
|
||||||
const VSpace(8),
|
const VSpace(8),
|
||||||
Padding(
|
Padding(
|
||||||
@ -302,6 +313,8 @@ class _SidebarState extends State<_Sidebar> {
|
|||||||
_scrollDebounce?.cancel();
|
_scrollDebounce?.cancel();
|
||||||
_scrollDebounce =
|
_scrollDebounce =
|
||||||
Timer(const Duration(milliseconds: 300), _setScrollStopped);
|
Timer(const Duration(milliseconds: 300), _setScrollStopped);
|
||||||
|
|
||||||
|
_scrollOffset.value = _scrollController.offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setScrollStopped() {
|
void _setScrollStopped() {
|
||||||
|
@ -17,6 +17,7 @@ class WorkspaceIcon extends StatefulWidget {
|
|||||||
required this.onSelected,
|
required this.onSelected,
|
||||||
this.borderRadius = 4,
|
this.borderRadius = 4,
|
||||||
this.emojiSize,
|
this.emojiSize,
|
||||||
|
this.alignment,
|
||||||
});
|
});
|
||||||
|
|
||||||
final UserWorkspacePB workspace;
|
final UserWorkspacePB workspace;
|
||||||
@ -26,6 +27,7 @@ class WorkspaceIcon extends StatefulWidget {
|
|||||||
final double? emojiSize;
|
final double? emojiSize;
|
||||||
final void Function(EmojiPickerResult) onSelected;
|
final void Function(EmojiPickerResult) onSelected;
|
||||||
final double borderRadius;
|
final double borderRadius;
|
||||||
|
final Alignment? alignment;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<WorkspaceIcon> createState() => _WorkspaceIconState();
|
State<WorkspaceIcon> createState() => _WorkspaceIconState();
|
||||||
@ -38,8 +40,8 @@ class _WorkspaceIconState extends State<WorkspaceIcon> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget child = widget.workspace.icon.isNotEmpty
|
Widget child = widget.workspace.icon.isNotEmpty
|
||||||
? Container(
|
? Container(
|
||||||
width: widget.iconSize,
|
width: widget.emojiSize ?? widget.iconSize,
|
||||||
alignment: Alignment.center,
|
alignment: widget.alignment ?? Alignment.center,
|
||||||
child: FlowyText.emoji(
|
child: FlowyText.emoji(
|
||||||
widget.workspace.icon,
|
widget.workspace.icon,
|
||||||
fontSize: widget.emojiSize ?? widget.iconSize,
|
fontSize: widget.emojiSize ?? widget.iconSize,
|
||||||
|
@ -50,7 +50,7 @@ class _SidebarWorkspaceState extends State<SidebarWorkspace> {
|
|||||||
UserSettingButton(userProfile: widget.userProfile),
|
UserSettingButton(userProfile: widget.userProfile),
|
||||||
const HSpace(8.0),
|
const HSpace(8.0),
|
||||||
const NotificationButton(),
|
const NotificationButton(),
|
||||||
const HSpace(10.0),
|
const HSpace(12.0),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -434,7 +434,6 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
child: FlowyText.regular(
|
child: FlowyText.regular(
|
||||||
widget.view.name,
|
widget.view.name,
|
||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
lineHeight: 1.1,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@ -466,7 +465,6 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(left: widget.level * widget.leftPadding),
|
padding: EdgeInsets.only(left: widget.level * widget.leftPadding),
|
||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: children,
|
children: children,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -499,7 +497,6 @@ class _SingleInnerViewItemState extends State<SingleInnerViewItem> {
|
|||||||
? FlowyText.emoji(
|
? FlowyText.emoji(
|
||||||
widget.view.icon.value,
|
widget.view.icon.value,
|
||||||
fontSize: 16.0,
|
fontSize: 16.0,
|
||||||
lineHeight: 1.4,
|
|
||||||
)
|
)
|
||||||
: Opacity(opacity: 0.6, child: widget.view.defaultIcon());
|
: Opacity(opacity: 0.6, child: widget.view.defaultIcon());
|
||||||
|
|
||||||
|
@ -3,8 +3,6 @@ import 'dart:io';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
|
|
||||||
const String _emojiFontFamily = 'noto color emoji';
|
|
||||||
|
|
||||||
class FlowyText extends StatelessWidget {
|
class FlowyText extends StatelessWidget {
|
||||||
final String text;
|
final String text;
|
||||||
final TextOverflow? overflow;
|
final TextOverflow? overflow;
|
||||||
@ -138,16 +136,13 @@ class FlowyText extends StatelessWidget {
|
|||||||
|
|
||||||
var fontFamily = this.fontFamily;
|
var fontFamily = this.fontFamily;
|
||||||
var fallbackFontFamily = this.fallbackFontFamily;
|
var fallbackFontFamily = this.fallbackFontFamily;
|
||||||
if (isEmoji && (Platform.isLinux || Platform.isAndroid)) {
|
var fontSize =
|
||||||
|
this.fontSize ?? Theme.of(context).textTheme.bodyMedium!.fontSize!;
|
||||||
|
if (isEmoji && _useNotoColorEmoji) {
|
||||||
fontFamily = _loadEmojiFontFamilyIfNeeded();
|
fontFamily = _loadEmojiFontFamilyIfNeeded();
|
||||||
if (fontFamily != null && fallbackFontFamily == null) {
|
if (fontFamily != null && fallbackFontFamily == null) {
|
||||||
fallbackFontFamily = [fontFamily];
|
fallbackFontFamily = [fontFamily];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var fontSize =
|
|
||||||
this.fontSize ?? Theme.of(context).textTheme.bodyMedium!.fontSize!;
|
|
||||||
if (Platform.isLinux && fontFamily == _emojiFontFamily) {
|
|
||||||
fontSize = fontSize * 0.8;
|
fontSize = fontSize * 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,6 +170,14 @@ class FlowyText extends StatelessWidget {
|
|||||||
textAlign: textAlign,
|
textAlign: textAlign,
|
||||||
overflow: overflow ?? TextOverflow.clip,
|
overflow: overflow ?? TextOverflow.clip,
|
||||||
style: textStyle,
|
style: textStyle,
|
||||||
|
strutStyle: Platform.isMacOS
|
||||||
|
? StrutStyle.fromTextStyle(
|
||||||
|
textStyle,
|
||||||
|
forceStrutHeight: true,
|
||||||
|
leadingDistribution: TextLeadingDistribution.even,
|
||||||
|
height: 1.1,
|
||||||
|
)
|
||||||
|
: null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,10 +192,13 @@ class FlowyText extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String? _loadEmojiFontFamilyIfNeeded() {
|
String? _loadEmojiFontFamilyIfNeeded() {
|
||||||
if (Platform.isLinux || Platform.isAndroid) {
|
if (_useNotoColorEmoji) {
|
||||||
return GoogleFonts.notoColorEmoji().fontFamily;
|
return GoogleFonts.notoColorEmoji().fontFamily;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get _useNotoColorEmoji =>
|
||||||
|
Platform.isLinux || Platform.isAndroid || Platform.isWindows;
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ class FlowyTooltip extends StatelessWidget {
|
|||||||
final isLightMode = Theme.of(context).brightness == Brightness.light;
|
final isLightMode = Theme.of(context).brightness == Brightness.light;
|
||||||
return Tooltip(
|
return Tooltip(
|
||||||
margin: margin,
|
margin: margin,
|
||||||
|
verticalOffset: 16.0,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: isLightMode ? const Color(0xE5171717) : const Color(0xE5E5E5E5),
|
color: isLightMode ? const Color(0xE5171717) : const Color(0xE5E5E5E5),
|
||||||
|
Loading…
Reference in New Issue
Block a user