chore: remove the unused code

This commit is contained in:
Lucas.Xu 2023-08-13 17:14:58 +08:00
parent 7a6c829187
commit d0a343ee36
21 changed files with 50 additions and 1294 deletions

View File

@ -1,7 +1,7 @@
import 'dart:io';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/sidebar.dart';
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/services.dart';
@ -75,7 +75,7 @@ void main() {
await tester.pumpAndSettle();
expect(find.byType(HomeMenu), findsOneWidget);
expect(find.byType(HomeSideBar), findsOneWidget);
await FlowyTestKeyboard.simulateKeyDownEvent(
[
@ -89,7 +89,7 @@ void main() {
await tester.pumpAndSettle();
expect(find.byType(HomeMenu), findsNothing);
expect(find.byType(HomeSideBar), findsNothing);
});
});
}

View File

@ -17,6 +17,7 @@ import 'switch_folder_test.dart' as switch_folder_test;
import 'sidebar/sidebar_test_runner.dart' as sidebar_test_runner;
import 'board/board_test_runner.dart' as board_test_runner;
import 'tabs_test.dart' as tabs_test;
import 'hotkeys_test.dart' as hotkeys_test;
/// The main task runner for all integration tests in AppFlowy.
///
@ -55,6 +56,9 @@ void main() {
// Tabs
tabs_test.main();
// Others
hotkeys_test.main();
// board_test.main();
// empty_document_test.main();
// smart_menu_test.main();

View File

@ -1,7 +1,7 @@
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra/image.dart';

View File

@ -26,7 +26,7 @@ import 'package:appflowy/workspace/application/settings/prelude.dart';
import 'package:appflowy/user/application/prelude.dart';
import 'package:appflowy/user/presentation/router.dart';
import 'package:appflowy/plugins/trash/application/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
import 'package:fluttertoast/fluttertoast.dart';

View File

@ -2,7 +2,7 @@ import 'dart:collection';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/view/prelude.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:expandable/expandable.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';

View File

@ -3,7 +3,7 @@ import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart';

View File

@ -1,52 +0,0 @@
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flutter/material.dart';
import 'package:flowy_infra_ui/style_widget/extension.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
class NewAppButton extends StatelessWidget {
final Function(String)? press;
const NewAppButton({this.press, Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
final child = FlowyTextButton(
LocaleKeys.newPageText.tr(),
fillColor: Colors.transparent,
hoverColor: Colors.transparent,
fontColor: Theme.of(context).colorScheme.tertiary,
onPressed: () async => await _showCreateAppDialog(context),
heading: Container(
width: 16,
height: 16,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: Theme.of(context).colorScheme.surface,
),
child: svgWidget("home/new_app"),
),
padding: EdgeInsets.symmetric(horizontal: Insets.l, vertical: 20),
);
return SizedBox(
height: HomeSizes.menuAddButtonHeight,
child: child,
).topBorder(color: Theme.of(context).dividerColor);
}
Future<void> _showCreateAppDialog(BuildContext context) async {
return NavigatorTextFieldDialog(
title: LocaleKeys.newPageText.tr(),
value: "",
confirm: (newValue) {
if (newValue.isNotEmpty && press != null) {
press!(newValue);
}
},
).show(context);
}
}

View File

@ -1,146 +0,0 @@
import 'package:appflowy/plugins/document/document.dart';
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_panel.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_type.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
class AddButton extends StatelessWidget {
final String parentViewId;
final Function(
PluginBuilder,
String? name,
List<int>? initialDataBytes,
bool openAfterCreated,
) onSelected;
const AddButton({
required this.parentViewId,
Key? key,
required this.onSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final List<PopoverAction> actions = [];
// Plugins
actions.addAll(
pluginBuilders()
.map(
(pluginBuilder) =>
AddButtonActionWrapper(pluginBuilder: pluginBuilder),
)
.toList(),
);
// Import
actions.addAll(
getIt<PluginSandbox>()
.builders
.whereType<DocumentPluginBuilder>()
.map(
(pluginBuilder) =>
ImportActionWrapper(pluginBuilder: pluginBuilder),
)
.toList(),
);
return PopoverActionList<PopoverAction>(
direction: PopoverDirection.bottomWithLeftAligned,
actions: actions,
offset: const Offset(0, 8),
buildChild: (controller) {
return SizedBox(
width: 22,
child: InkWell(
onTap: () => controller.show(),
child: FlowyHover(
style: HoverStyle(
hoverColor: AFThemeExtension.of(context).greySelect,
),
builder: (context, onHover) => const FlowySvg(
name: 'home/add',
),
),
),
);
},
onSelected: (action, controller) {
if (action is AddButtonActionWrapper) {
onSelected(action.pluginBuilder, null, null, true);
}
if (action is ImportActionWrapper) {
showImportPanel(
parentViewId,
context,
(type, name, initialDataBytes) {
if (initialDataBytes == null) {
return;
}
switch (type) {
case ImportType.historyDocument:
case ImportType.historyDatabase:
case ImportType.databaseCSV:
case ImportType.databaseRawData:
onSelected(
action.pluginBuilder,
name,
initialDataBytes,
false,
);
break;
case ImportType.markdownOrText:
onSelected(
action.pluginBuilder,
name,
initialDataBytes,
true,
);
break;
}
},
);
}
controller.close();
},
);
}
}
class AddButtonActionWrapper extends ActionCell {
final PluginBuilder pluginBuilder;
AddButtonActionWrapper({required this.pluginBuilder});
@override
Widget? leftIcon(Color iconColor) => FlowySvg(name: pluginBuilder.menuIcon);
@override
String get name => pluginBuilder.menuName;
PluginType get pluginType => pluginBuilder.pluginType;
}
class ImportActionWrapper extends ActionCell {
final DocumentPluginBuilder pluginBuilder;
ImportActionWrapper({
required this.pluginBuilder,
});
@override
Widget? leftIcon(Color iconColor) => const FlowySvg(
name: 'editor/import',
);
@override
String get name => LocaleKeys.moreAction_import.tr();
}

View File

@ -1,205 +0,0 @@
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:expandable/expandable.dart';
import 'package:flowy_infra/icon_data.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:appflowy/workspace/application/app/app_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:flowy_infra/image.dart';
import '../menu_app.dart';
import 'add_button.dart';
class MenuAppHeader extends StatelessWidget {
final ViewPB parentView;
const MenuAppHeader(
this.parentView, {
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return SizedBox(
height: MenuAppSizes.headerHeight,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_renderExpandedIcon(context),
// HSpace(MenuAppSizes.iconPadding),
_renderTitle(context),
_renderCreateViewButton(context),
],
),
);
}
Widget _renderExpandedIcon(BuildContext context) {
return SizedBox(
width: MenuAppSizes.headerHeight,
height: MenuAppSizes.headerHeight,
child: InkWell(
onTap: () {
ExpandableController.of(
context,
rebuildOnChange: false,
required: true,
)?.toggle();
},
child: ExpandableIcon(
theme: ExpandableThemeData(
expandIcon: FlowyIconData.drop_down_show,
collapseIcon: FlowyIconData.drop_down_hide,
iconColor: Theme.of(context).colorScheme.tertiary,
iconSize: MenuAppSizes.iconSize,
iconPadding: const EdgeInsets.fromLTRB(0, 0, 10, 0),
hasIcon: false,
),
),
),
);
}
Widget _renderTitle(BuildContext context) {
return Expanded(
child: BlocListener<AppBloc, AppState>(
listenWhen: (p, c) =>
(p.latestCreatedView == null && c.latestCreatedView != null),
listener: (context, state) {
final expandableController = ExpandableController.of(
context,
rebuildOnChange: false,
required: true,
)!;
if (!expandableController.expanded) {
expandableController.toggle();
}
},
child: AppActionList(
onSelected: (action) {
switch (action) {
case AppDisclosureAction.rename:
NavigatorTextFieldDialog(
title: LocaleKeys.menuAppHeader_renameDialog.tr(),
autoSelectAllText: true,
value: context.read<AppBloc>().state.view.name,
confirm: (newValue) {
context.read<AppBloc>().add(AppEvent.rename(newValue));
},
).show(context);
break;
case AppDisclosureAction.delete:
context.read<AppBloc>().add(const AppEvent.delete());
break;
}
},
),
),
);
}
Widget _renderCreateViewButton(BuildContext context) {
return Tooltip(
message: LocaleKeys.menuAppHeader_addPageTooltip.tr(),
child: AddButton(
parentViewId: parentView.id,
onSelected: (pluginBuilder, name, initialDataBytes, openAfterCreated) {
context.read<AppBloc>().add(
AppEvent.createView(
name ?? LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
pluginBuilder.layoutType!,
initialDataBytes: initialDataBytes,
openAfterCreated: openAfterCreated,
),
);
},
).padding(right: MenuAppSizes.headerPadding),
);
}
}
enum AppDisclosureAction {
rename,
delete,
}
extension AppDisclosureExtension on AppDisclosureAction {
String get name {
switch (this) {
case AppDisclosureAction.rename:
return LocaleKeys.disclosureAction_rename.tr();
case AppDisclosureAction.delete:
return LocaleKeys.disclosureAction_delete.tr();
}
}
Widget icon(Color iconColor) {
switch (this) {
case AppDisclosureAction.rename:
return const FlowySvg(name: 'editor/edit');
case AppDisclosureAction.delete:
return const FlowySvg(name: 'editor/delete');
}
}
}
class AppActionList extends StatelessWidget {
final Function(AppDisclosureAction) onSelected;
const AppActionList({
required this.onSelected,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return PopoverActionList<DisclosureActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions: AppDisclosureAction.values
.map((action) => DisclosureActionWrapper(action))
.toList(),
buildChild: (controller) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => ExpandableController.of(
context,
rebuildOnChange: false,
required: true,
)?.toggle(),
onSecondaryTap: () {
controller.show();
},
child: BlocSelector<AppBloc, AppState, ViewPB>(
selector: (state) => state.view,
builder: (context, app) => FlowyText.medium(
app.name,
overflow: TextOverflow.ellipsis,
color: Theme.of(context).colorScheme.tertiary,
),
),
);
},
onSelected: (action, controller) {
onSelected(action.inner);
controller.close();
},
);
}
}
class DisclosureActionWrapper extends ActionCell {
final AppDisclosureAction inner;
DisclosureActionWrapper(this.inner);
@override
Widget? leftIcon(Color iconColor) => inner.icon(iconColor);
@override
String get name => inner.name;
}

View File

@ -1,121 +0,0 @@
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:expandable/expandable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/app/app_bloc.dart';
import 'package:provider/provider.dart';
import 'section/section.dart';
class MenuApp extends StatefulWidget {
final ViewPB view;
const MenuApp(this.view, {Key? key}) : super(key: key);
@override
State<MenuApp> createState() => _MenuAppState();
}
class _MenuAppState extends State<MenuApp> {
late ViewDataContext viewDataContext;
@override
void initState() {
viewDataContext = ViewDataContext(viewId: widget.view.id);
super.initState();
}
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<AppBloc>(
create: (context) {
final appBloc = AppBloc(view: widget.view);
appBloc.add(const AppEvent.initial());
return appBloc;
},
),
],
child: MultiBlocListener(
listeners: [
BlocListener<AppBloc, AppState>(
listenWhen: (p, c) => p.latestCreatedView != c.latestCreatedView,
listener: (context, state) {
if (state.latestCreatedView != null) {
getIt<MenuSharedState>().latestOpenView =
state.latestCreatedView;
}
},
),
BlocListener<AppBloc, AppState>(
listener: (context, state) => viewDataContext.views = state.views,
),
],
child: BlocBuilder<AppBloc, AppState>(
builder: (context, state) {
return ChangeNotifierProvider.value(
value: viewDataContext,
child: Consumer<ViewDataContext>(
builder: (context, viewDataContext, _) {
return expandableWrapper(context, viewDataContext);
},
),
);
},
),
),
);
}
ExpandableNotifier expandableWrapper(
BuildContext context,
ViewDataContext viewDataContext,
) {
return ExpandableNotifier(
controller: viewDataContext.expandController,
child: ScrollOnExpand(
scrollOnExpand: false,
scrollOnCollapse: false,
child: Column(
children: <Widget>[
ExpandablePanel(
theme: const ExpandableThemeData(
headerAlignment: ExpandablePanelHeaderAlignment.center,
tapBodyToExpand: false,
tapBodyToCollapse: false,
tapHeaderToExpand: false,
iconPadding: EdgeInsets.zero,
hasIcon: false,
),
header: MenuAppHeader(widget.view),
expanded: ViewSection(appViewData: viewDataContext),
collapsed: const SizedBox(),
),
],
),
),
);
}
@override
void didUpdateWidget(covariant MenuApp oldWidget) {
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
viewDataContext.dispose();
super.dispose();
}
}
class MenuAppSizes {
static double iconSize = 16;
static double headerHeight = 26;
static double headerPadding = 6;
static double iconPadding = 6;
static double appVPadding = 14;
static double scale = 1;
static double get expandedPadding => iconSize * scale + headerPadding;
}

View File

@ -1,243 +0,0 @@
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
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/menu/menu.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:flowy_infra/image.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
class ViewSectionItem extends StatelessWidget {
final bool isSelected;
final ViewPB view;
final void Function(ViewPB) onSelected;
const ViewSectionItem({
Key? key,
required this.view,
required this.isSelected,
required this.onSelected,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(
create: (ctx) => getIt<ViewBloc>(param1: view)
..add(
const ViewEvent.initial(),
),
),
],
child: BlocBuilder<ViewBloc, ViewState>(
builder: (blocContext, state) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: InkWell(
onTap: () => onSelected(blocContext.read<ViewBloc>().state.view),
child: FlowyHover(
style: HoverStyle(
hoverColor: Theme.of(context).colorScheme.secondary,
),
// If current state.isEditing is true, the hover should not
// rebuild when onEnter/onExit events happened.
buildWhenOnHover: () => !state.isEditing,
builder: (_, onHover) => _render(
blocContext,
onHover,
state,
),
isSelected: () => state.isEditing || isSelected,
),
),
);
},
),
);
}
Widget _render(
BuildContext blocContext,
bool onHover,
ViewState state,
) {
final List<Widget> children = [
SizedBox(
width: 16,
height: 16,
child: state.view.renderThumbnail(),
),
const HSpace(2),
Expanded(
child: FlowyText.regular(
state.view.name,
overflow: TextOverflow.ellipsis,
),
),
];
if (onHover || state.isEditing) {
children.add(
ViewDisclosureButton(
state: state,
onEdit: (isEdit) =>
blocContext.read<ViewBloc>().add(ViewEvent.setIsEditing(isEdit)),
onAction: (action) {
switch (action) {
case ViewDisclosureAction.rename:
NavigatorTextFieldDialog(
title: LocaleKeys.disclosureAction_rename.tr(),
autoSelectAllText: true,
value: blocContext.read<ViewBloc>().state.view.name,
confirm: (newValue) {
blocContext
.read<ViewBloc>()
.add(ViewEvent.rename(newValue));
},
).show(blocContext);
break;
case ViewDisclosureAction.delete:
blocContext.read<ViewBloc>().add(const ViewEvent.delete());
break;
case ViewDisclosureAction.duplicate:
blocContext.read<ViewBloc>().add(const ViewEvent.duplicate());
break;
case ViewDisclosureAction.favorite:
blocContext
.read<FavoriteBloc>()
.add(FavoriteEvent.toggle(view));
break;
case ViewDisclosureAction.openInNewTab:
blocContext.read<TabsBloc>().openTab(state.view);
break;
}
},
),
);
}
return SizedBox(
height: 26,
child: Row(children: children).padding(
left: MenuAppSizes.expandedPadding,
right: MenuAppSizes.headerPadding,
),
);
}
}
enum ViewDisclosureAction {
rename,
delete,
duplicate,
favorite,
openInNewTab,
}
extension ViewDisclosureExtension on ViewDisclosureAction {
String name({ViewState? state}) {
switch (this) {
case ViewDisclosureAction.rename:
return LocaleKeys.disclosureAction_rename.tr();
case ViewDisclosureAction.delete:
return LocaleKeys.disclosureAction_delete.tr();
case ViewDisclosureAction.duplicate:
return LocaleKeys.disclosureAction_duplicate.tr();
case ViewDisclosureAction.favorite:
return state!.view.isFavorite
? LocaleKeys.disclosureAction_unfavorite.tr()
: LocaleKeys.disclosureAction_favorite.tr();
case ViewDisclosureAction.openInNewTab:
return LocaleKeys.disclosureAction_openNewTab.tr();
}
}
Widget icon(Color iconColor, {ViewState? state}) {
switch (this) {
case ViewDisclosureAction.rename:
return const FlowySvg(name: 'editor/edit');
case ViewDisclosureAction.delete:
return const FlowySvg(name: 'editor/delete');
case ViewDisclosureAction.duplicate:
return const FlowySvg(name: 'editor/copy');
case ViewDisclosureAction.favorite:
return state!.view.isFavorite
? const FlowySvg(name: 'home/favorite')
: const FlowySvg(name: 'home/unfavorite');
case ViewDisclosureAction.openInNewTab:
return const FlowySvg(name: 'grid/expander');
}
}
}
class ViewDisclosureButton extends StatelessWidget {
final Function(bool) onEdit;
final Function(ViewDisclosureAction) onAction;
final ViewState state;
const ViewDisclosureButton({
required this.onEdit,
required this.onAction,
required this.state,
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return PopoverActionList<ViewDisclosureActionWrapper>(
direction: PopoverDirection.bottomWithCenterAligned,
actions: ViewDisclosureAction.values
.map((action) => ViewDisclosureActionWrapper(action, state))
.toList(),
buildChild: (controller) {
return FlowyIconButton(
hoverColor: Colors.transparent,
iconPadding: const EdgeInsets.all(5),
width: 26,
icon: svgWidget(
"editor/details",
color: Theme.of(context).iconTheme.color,
),
onPressed: () {
onEdit(true);
controller.show();
},
);
},
onSelected: (action, controller) {
onEdit(false);
onAction(action.inner);
controller.close();
},
onClosed: () {
onEdit(false);
},
);
}
}
class ViewDisclosureActionWrapper extends ActionCell {
final ViewDisclosureAction inner;
final ViewState? state;
ViewDisclosureActionWrapper(this.inner, [this.state]);
@override
Widget? leftIcon(Color iconColor) => inner.icon(iconColor, state: state);
@override
String get name => inner.name(state: state);
}

View File

@ -1,79 +0,0 @@
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/app/app_bloc.dart';
import 'package:appflowy/workspace/application/menu/menu_view_section_bloc.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:reorderables/reorderables.dart';
class ViewSection extends StatelessWidget {
final ViewDataContext appViewData;
const ViewSection({Key? key, required this.appViewData}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) {
final bloc = ViewSectionBloc(appViewData: appViewData);
bloc.add(const ViewSectionEvent.initial());
return bloc;
},
child: BlocListener<ViewSectionBloc, ViewSectionState>(
listenWhen: (p, c) => p.selectedView != c.selectedView,
listener: (context, state) {
if (state.selectedView != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
getIt<TabsBloc>().add(
TabsEvent.openPlugin(
plugin: state.selectedView!.plugin(listenOnViewChanged: true),
),
);
});
}
},
child: BlocBuilder<ViewSectionBloc, ViewSectionState>(
builder: (context, state) {
return _reorderableColumn(context, state);
},
),
),
);
}
ReorderableColumn _reorderableColumn(
BuildContext context,
ViewSectionState state,
) {
final children = state.views.map((view) {
final isSelected = _isViewSelected(state, view.id);
return ViewSectionItem(
view: view,
key: ValueKey('$view.hashCode/$isSelected'),
isSelected: isSelected,
onSelected: (view) => getIt<MenuSharedState>().latestOpenView = view,
);
}).toList();
return ReorderableColumn(
needsLongPressDraggable: false,
onReorder: (oldIndex, index) {
context
.read<ViewSectionBloc>()
.add(ViewSectionEvent.moveView(oldIndex, index));
},
ignorePrimaryScrollController: true,
buildDraggableFeedback: (context, constraints, child) => ConstrainedBox(
constraints: constraints,
child: Material(color: Colors.transparent, child: child),
),
children: children,
);
}
bool _isViewSelected(ViewSectionState state, String viewId) {
return state.selectedView?.id == viewId;
}
}

View File

@ -1,289 +0,0 @@
import 'dart:io' show Platform;
import 'package:appflowy/core/frameless_window.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/trash/menu.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/favorite/favorite_bloc.dart';
import 'package:appflowy/workspace/application/home/home_setting_bloc.dart';
import 'package:appflowy/workspace/application/menu/menu_bloc.dart';
import 'package:appflowy/workspace/presentation/home/home_sizes.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/workspace.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show UserProfilePB;
import 'package:easy_localization/easy_localization.dart';
import 'package:expandable/expandable.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/time/duration.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
import '../navigation.dart';
import 'app/create_button.dart';
import 'app/menu_app.dart';
import 'app/section/item.dart';
import 'menu_user.dart';
export './app/header/header.dart';
export './app/menu_app.dart';
class HomeMenu extends StatelessWidget {
final UserProfilePB user;
final WorkspaceSettingPB workspaceSetting;
const HomeMenu({
Key? key,
required this.user,
required this.workspaceSetting,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider<MenuBloc>(
create: (context) => MenuBloc(
user: user,
workspace: workspaceSetting.workspace,
)..add(const MenuEvent.initial()),
),
BlocProvider(
create: (context) =>
getIt<FavoriteBloc>()..add(const FavoriteEvent.initial()),
)
],
child: BlocBuilder<MenuBloc, MenuState>(
builder: (context, state) => _renderBody(context),
),
);
}
Widget _renderBody(BuildContext context) {
// nested column: https://siddharthmolleti.com/flutter-box-constraints-nested-column-s-row-s-3dfacada7361
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceVariant,
border:
Border(right: BorderSide(color: Theme.of(context).dividerColor)),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const MenuTopBar(),
const VSpace(10),
_renderApps(context),
],
).padding(horizontal: Insets.l),
),
const VSpace(20),
const MenuTrash(),
const VSpace(20),
_renderNewAppButton(context),
],
),
);
}
Widget _renderFavorites(BuildContext context) {
return BlocBuilder<FavoriteBloc, FavoriteState>(
builder: (context, state) {
return state.views.isNotEmpty
? ExpandableTheme(
data: ExpandableThemeData(
useInkWell: true,
animationDuration: Durations.medium,
),
child: ExpandablePanel(
theme: const ExpandableThemeData(
headerAlignment: ExpandablePanelHeaderAlignment.center,
tapBodyToExpand: false,
tapBodyToCollapse: false,
tapHeaderToExpand: false,
iconPadding: EdgeInsets.zero,
hasIcon: false,
),
// header: const FavoriteHeader(),
expanded: ScrollConfiguration(
behavior:
const ScrollBehavior().copyWith(scrollbars: false),
child: Column(
children: state.views
.map(
(e) => ViewSectionItem(
key: ValueKey(e.id),
isSelected: false,
onSelected: (view) => getIt<MenuSharedState>()
.latestOpenView = view,
view: e,
),
)
.toList(),
),
),
collapsed: const SizedBox.shrink(),
),
)
: const SizedBox.shrink();
},
);
}
Widget _renderApps(BuildContext context) {
return ExpandableTheme(
data: ExpandableThemeData(
useInkWell: true,
animationDuration: Durations.medium,
),
child: Expanded(
child: ScrollConfiguration(
behavior: const ScrollBehavior().copyWith(scrollbars: false),
child: BlocSelector<MenuBloc, MenuState, List<Widget>>(
selector: (state) => state.views
.map((app) => MenuApp(app, key: ValueKey(app.id)))
.toList(),
builder: (context, menuItems) {
return ReorderableListView.builder(
itemCount: menuItems.length,
buildDefaultDragHandles: false,
header: Column(
children: [
Padding(
padding: EdgeInsets.only(
bottom: MenuAppSizes.appVPadding,
),
child: MenuUser(user),
),
_renderFavorites(context),
],
),
onReorder: (oldIndex, newIndex) {
// Moving item1 from index 0 to index 1
// expect: oldIndex: 0, newIndex: 1
// receive: oldIndex: 0, newIndex: 2
// Workaround: if newIndex > oldIndex, we just minus one
final int index =
newIndex > oldIndex ? newIndex - 1 : newIndex;
context
.read<MenuBloc>()
.add(MenuEvent.moveApp(oldIndex, index));
},
physics: StyledScrollPhysics(),
itemBuilder: (BuildContext context, int index) {
return ReorderableDragStartListener(
key: ValueKey(menuItems[index].key),
index: index,
child: Padding(
padding: EdgeInsets.symmetric(
vertical: MenuAppSizes.appVPadding / 2,
),
child: menuItems[index],
),
);
},
proxyDecorator: (child, index, animation) =>
Material(color: Colors.transparent, child: child),
);
},
),
),
),
);
}
Widget _renderNewAppButton(BuildContext context) {
return NewAppButton(
press: (appName) =>
context.read<MenuBloc>().add(MenuEvent.createApp(appName, desc: "")),
);
}
}
class MenuSharedState {
final ValueNotifier<ViewPB?> _latestOpenView = ValueNotifier<ViewPB?>(null);
MenuSharedState({ViewPB? view}) {
_latestOpenView.value = view;
}
ViewPB? get latestOpenView => _latestOpenView.value;
ValueNotifier<ViewPB?> get notifier => _latestOpenView;
set latestOpenView(ViewPB? view) {
if (_latestOpenView.value?.id != view?.id) {
_latestOpenView.value = view;
}
}
VoidCallback addLatestViewListener(void Function(ViewPB?) callback) {
listener() {
callback(_latestOpenView.value);
}
_latestOpenView.addListener(listener);
return listener;
}
void removeLatestViewListener(VoidCallback listener) {
_latestOpenView.removeListener(listener);
}
}
class MenuTopBar extends StatelessWidget {
const MenuTopBar({Key? key}) : super(key: key);
Widget renderIcon(BuildContext context) {
if (Platform.isMacOS) {
return Container();
}
return (Theme.of(context).brightness == Brightness.dark
? svgWidget("flowy_logo_dark_mode", size: const Size(92, 17))
: svgWidget("flowy_logo_with_text", size: const Size(92, 17)));
}
@override
Widget build(BuildContext context) {
return BlocBuilder<MenuBloc, MenuState>(
builder: (context, state) {
return SizedBox(
height: HomeSizes.topBarHeight,
child: MoveWindowDetector(
child: Row(
children: [
renderIcon(context),
const Spacer(),
Tooltip(
richMessage: sidebarTooltipTextSpan(
context,
LocaleKeys.sideBar_closeSidebar.tr(),
),
child: FlowyIconButton(
width: 28,
hoverColor: Colors.transparent,
onPressed: () => context
.read<HomeSettingBloc>()
.add(const HomeSettingEvent.collapseMenu()),
iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
icon: svgWidget(
"home/hide_menu",
color: Theme.of(context).iconTheme.color,
),
),
)
],
),
),
);
},
);
}
}

View File

@ -0,0 +1,32 @@
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
import 'package:flutter/material.dart';
class MenuSharedState {
final ValueNotifier<ViewPB?> _latestOpenView = ValueNotifier<ViewPB?>(null);
MenuSharedState({ViewPB? view}) {
_latestOpenView.value = view;
}
ViewPB? get latestOpenView => _latestOpenView.value;
ValueNotifier<ViewPB?> get notifier => _latestOpenView;
set latestOpenView(ViewPB? view) {
if (_latestOpenView.value?.id != view?.id) {
_latestOpenView.value = view;
}
}
VoidCallback addLatestViewListener(void Function(ViewPB?) callback) {
listener() {
callback(_latestOpenView.value);
}
_latestOpenView.addListener(listener);
return listener;
}
void removeLatestViewListener(VoidCallback listener) {
_latestOpenView.removeListener(listener);
}
}

View File

@ -1,147 +0,0 @@
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/startup/entry_point.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/color_generator/color_generator.dart';
import 'package:appflowy/workspace/application/menu/menu_user_bloc.dart';
import 'package:appflowy/workspace/presentation/settings/settings_dialog.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_user_view.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart'
show UserProfilePB;
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart';
class MenuUser extends StatelessWidget {
final UserProfilePB user;
MenuUser(this.user, {Key? key}) : super(key: ValueKey(user.id));
@override
Widget build(BuildContext context) {
return BlocProvider<MenuUserBloc>(
create: (context) =>
getIt<MenuUserBloc>(param1: user)..add(const MenuUserEvent.initial()),
child: BlocBuilder<MenuUserBloc, MenuUserState>(
builder: (context, state) => Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_renderAvatar(context),
const HSpace(10),
Expanded(
child: _renderUserName(context),
),
_renderSettingsButton(context),
//ToDo: when the user is allowed to create another workspace,
//we get the below block back
//_renderDropButton(context),
],
),
),
);
}
Widget _renderAvatar(BuildContext context) {
String iconUrl = context.read<MenuUserBloc>().state.userProfile.iconUrl;
if (iconUrl.isEmpty) {
iconUrl = defaultUserAvatar;
final String name =
userName(context.read<MenuUserBloc>().state.userProfile);
final Color color = ColorGenerator().generateColorFromString(name);
const initialsCount = 2;
// Taking the first letters of the name components and limiting to 2 elements
final nameInitials = name
.split(' ')
.where((element) => element.isNotEmpty)
.take(initialsCount)
.map((element) => element[0].toUpperCase())
.join('');
return Container(
width: 28,
height: 28,
alignment: Alignment.center,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
child: FlowyText.semibold(
nameInitials,
color: Colors.white,
fontSize: nameInitials.length == initialsCount ? 12 : 14,
),
);
}
return SizedBox(
width: 25,
height: 25,
child: ClipRRect(
borderRadius: Corners.s5Border,
child: CircleAvatar(
backgroundColor: Colors.transparent,
child: svgWidget('emoji/$iconUrl'),
),
),
);
}
Widget _renderUserName(BuildContext context) {
final String name =
userName(context.read<MenuUserBloc>().state.userProfile);
return FlowyText.medium(
name,
overflow: TextOverflow.ellipsis,
color: Theme.of(context).colorScheme.tertiary,
);
}
Widget _renderSettingsButton(BuildContext context) {
final userProfile = context.read<MenuUserBloc>().state.userProfile;
return Tooltip(
message: LocaleKeys.settings_menu_open.tr(),
child: IconButton(
onPressed: () {
showDialog(
context: context,
builder: (dialogContext) {
return BlocProvider<DocumentAppearanceCubit>.value(
value: BlocProvider.of<DocumentAppearanceCubit>(context),
child: SettingsDialog(
userProfile,
didLogout: () async {
Navigator.of(dialogContext).pop();
Navigator.of(context).pop();
await FlowyRunner.run(
FlowyApp(),
integrationEnv(),
);
},
dismissDialog: () => Navigator.of(context).pop(),
didOpenUser: () {},
),
);
},
);
},
icon: SizedBox.square(
dimension: 20,
child: svgWidget(
"home/settings",
color: Theme.of(context).colorScheme.tertiary,
),
),
),
);
}
/// Return the user name, if the user name is empty, return the default user name.
String userName(UserProfilePB userProfile) {
String name = userProfile.name;
if (name.isEmpty) {
name = LocaleKeys.defaultUsername.tr();
}
return name;
}
}

View File

@ -6,7 +6,8 @@ import 'package:appflowy/plugins/document/application/document_data_pb_extension
import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/settings/share/import_service.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_type.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/import/import_type.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/protobuf.dart';
import 'package:flowy_infra/file_picker/file_picker_service.dart';
import 'package:appflowy_editor/appflowy_editor.dart';

View File

@ -1,5 +1,5 @@
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/favorite_folder.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/folder/personal_folder.dart';
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';

View File

@ -1,7 +1,7 @@
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/tabs/tabs_bloc.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra/image.dart';

View File

@ -1,7 +1,8 @@
import 'package:appflowy/plugins/document/document.dart';
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_panel.dart';
import 'package:appflowy/workspace/presentation/home/menu/sidebar/import/import_panel.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra/image.dart';

View File

@ -5,7 +5,7 @@ import 'package:appflowy/workspace/application/sidebar/folder/folder_bloc.dart';
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/menu/menu.dart';
import 'package:appflowy/workspace/presentation/home/menu/menu_shared_state.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/draggable_view_item.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_action_type.dart';
import 'package:appflowy/workspace/presentation/home/menu/view/view_add_button.dart';