123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- export './app/header/header.dart';
- export './app/menu_app.dart';
- import 'dart:io' show Platform;
- import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
- import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
- import 'package:app_flowy/workspace/presentation/plugins/trash/menu.dart';
- import 'package:flowy_infra/notifier.dart';
- import 'package:flowy_infra/size.dart';
- import 'package:flowy_infra/theme.dart';
- import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
- import 'package:flowy_infra_ui/widget/spacing.dart';
- import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart' show UserProfilePB;
- import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
- import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
- import 'package:flutter/material.dart';
- import 'package:flutter_bloc/flutter_bloc.dart';
- import 'package:styled_widget/styled_widget.dart';
- import 'package:expandable/expandable.dart';
- import 'package:flowy_infra/time/duration.dart';
- import 'package:app_flowy/startup/startup.dart';
- import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
- import 'package:app_flowy/workspace/application/home/home_bloc.dart';
- import 'package:app_flowy/core/frameless_window.dart';
- // import 'package:app_flowy/workspace/presentation/home/home_sizes.dart';
- import 'package:flowy_infra/image.dart';
- import 'package:flowy_infra_ui/style_widget/icon_button.dart';
- import 'app/menu_app.dart';
- import 'app/create_button.dart';
- import 'menu_user.dart';
- class HomeMenu extends StatelessWidget {
- final PublishNotifier<bool> _collapsedNotifier;
- final UserProfilePB user;
- final CurrentWorkspaceSettingPB workspaceSetting;
- const HomeMenu({
- Key? key,
- required this.user,
- required this.workspaceSetting,
- required PublishNotifier<bool> collapsedNotifier,
- }) : _collapsedNotifier = collapsedNotifier,
- super(key: key);
- @override
- Widget build(BuildContext context) {
- return MultiBlocProvider(
- providers: [
- BlocProvider<MenuBloc>(
- create: (context) {
- final menuBloc = getIt<MenuBloc>(param1: user, param2: workspaceSetting.workspace.id);
- menuBloc.add(const MenuEvent.initial());
- return menuBloc;
- },
- ),
- ],
- child: MultiBlocListener(
- listeners: [
- BlocListener<MenuBloc, MenuState>(
- listenWhen: (p, c) => p.plugin.id != c.plugin.id,
- listener: (context, state) {
- getIt<HomeStackManager>().setPlugin(state.plugin);
- },
- ),
- BlocListener<HomeBloc, HomeState>(
- listenWhen: (p, c) => p.isMenuCollapsed != c.isMenuCollapsed,
- listener: (context, state) {
- _collapsedNotifier.value = state.isMenuCollapsed;
- },
- )
- ],
- 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
- final theme = context.watch<AppTheme>();
- return Container(
- color: theme.bg1,
- 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().padding(horizontal: Insets.l),
- const VSpace(20),
- _renderNewAppButton(context),
- ],
- ),
- );
- }
- 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.apps.map((app) => MenuApp(app, key: ValueKey(app.id))).toList(),
- builder: (context, menuItems) {
- return ReorderableListView.builder(
- itemCount: menuItems.length,
- buildDefaultDragHandles: false,
- header: Padding(
- padding: EdgeInsets.only(bottom: 20.0 - MenuAppSizes.appVPadding),
- child: MenuUser(user),
- ),
- 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
- 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],
- ),
- );
- },
- );
- },
- ),
- ),
- ),
- );
- }
- 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;
- set latestOpenView(ViewPB? view) {
- _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();
- }
- final theme = context.watch<AppTheme>();
- return (theme.isDark
- ? svgWithSize("flowy_logo_dark_mode", const Size(92, 17))
- : svgWithSize("flowy_logo_with_text", const Size(92, 17)));
- }
- @override
- Widget build(BuildContext context) {
- final theme = context.watch<AppTheme>();
- return BlocBuilder<MenuBloc, MenuState>(
- builder: (context, state) {
- return SizedBox(
- height: HomeSizes.topBarHeight,
- child: MoveWindowDetector(
- child: Row(
- children: [
- renderIcon(context),
- const Spacer(),
- FlowyIconButton(
- width: 28,
- onPressed: () => context.read<HomeBloc>().add(const HomeEvent.collapseMenu()),
- iconPadding: const EdgeInsets.fromLTRB(4, 4, 4, 4),
- icon: svgWidget("home/hide_menu", color: theme.iconColor),
- )
- ],
- )),
- );
- },
- );
- }
- }
|