201 lines
7.5 KiB
Dart
Raw Normal View History

import 'dart:io';
2022-08-22 11:03:36 +08:00
import 'package:app_flowy/generated/locale_keys.g.dart';
2022-05-27 10:34:12 +08:00
import 'package:app_flowy/workspace/application/home/home_bloc.dart';
2022-03-01 16:05:45 +08:00
import 'package:app_flowy/workspace/presentation/home/home_stack.dart';
2021-11-05 14:23:30 +08:00
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/notifier.dart';
2022-02-02 22:21:31 +08:00
import 'package:flowy_infra/theme.dart';
2021-11-05 14:23:30 +08:00
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
2021-10-12 16:58:05 +08:00
import 'package:flowy_infra_ui/style_widget/text.dart';
2021-07-28 18:19:16 +08:00
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:easy_localization/easy_localization.dart';
2021-07-28 18:19:16 +08:00
typedef NaviAction = void Function();
class NavigationNotifier with ChangeNotifier {
List<NavigationItem> navigationItems;
2021-11-05 14:23:30 +08:00
PublishNotifier<bool> collapasedNotifier;
NavigationNotifier(
{required this.navigationItems, required this.collapasedNotifier});
2021-07-28 18:19:16 +08:00
2021-10-10 15:58:57 +08:00
void update(HomeStackNotifier notifier) {
2021-11-05 14:23:30 +08:00
bool shouldNotify = false;
2022-03-02 11:38:22 +08:00
if (navigationItems != notifier.plugin.display.navigationItems) {
navigationItems = notifier.plugin.display.navigationItems;
2021-11-05 14:23:30 +08:00
shouldNotify = true;
}
if (shouldNotify) {
notifyListeners();
}
2021-07-28 18:19:16 +08:00
}
}
2021-10-11 13:15:41 +08:00
// [[diagram: HomeStack navigation flow]]
// ┌───────────────────────┐
// 2.notify listeners ┌──────│DefaultHomeStackContext│
// ┌────────────────┐ ┌───────────┐ ┌────────────────┐ │ └───────────────────────┘
// │HomeStackNotifie│◀──────────│ HomeStack │◀──│HomeStackContext│◀─ impl
// └────────────────┘ └───────────┘ └────────────────┘ │ ┌───────────────────┐
// │ ▲ └───────│ DocStackContext │
// │ │ └───────────────────┘
// 3.notify change 1.set context
// │ │
// ▼ │
// ┌───────────────────┐ ┌──────────────────┐
// │NavigationNotifier │ │ ViewSectionItem │
// └───────────────────┘ └──────────────────┘
// │
// │
// ▼
// ┌─────────────────┐
// │ FlowyNavigation │ 4.render navigation items
// └─────────────────┘
2021-10-10 15:58:57 +08:00
class FlowyNavigation extends StatelessWidget {
const FlowyNavigation({Key? key}) : super(key: key);
2021-07-28 18:19:16 +08:00
@override
Widget build(BuildContext context) {
final theme = context.watch<AppTheme>();
2021-10-10 15:58:57 +08:00
return ChangeNotifierProxyProvider<HomeStackNotifier, NavigationNotifier>(
create: (_) {
final notifier = Provider.of<HomeStackNotifier>(context, listen: false);
return NavigationNotifier(
2022-03-02 11:38:22 +08:00
navigationItems: notifier.plugin.display.navigationItems,
2021-11-05 14:23:30 +08:00
collapasedNotifier: notifier.collapsedNotifier,
);
},
2021-07-28 18:19:16 +08:00
update: (_, notifier, controller) => controller!..update(notifier),
child: Expanded(
child: Row(children: [
Selector<NavigationNotifier, PublishNotifier<bool>>(
selector: (context, notifier) => notifier.collapasedNotifier,
builder: (ctx, collapsedNotifier, child) =>
_renderCollapse(ctx, collapsedNotifier, theme)),
Selector<NavigationNotifier, List<NavigationItem>>(
2021-11-05 14:23:30 +08:00
selector: (context, notifier) => notifier.navigationItems,
builder: (ctx, items, child) => Expanded(
child: Row(
children: _renderNavigationItems(items),
// crossAxisAlignment: WrapCrossAlignment.start,
),
),
),
]),
),
2021-11-05 14:23:30 +08:00
);
}
Widget _renderCollapse(BuildContext context,
PublishNotifier<bool> collapsedNotifier, AppTheme theme) {
2021-11-05 14:23:30 +08:00
return ChangeNotifierProvider.value(
value: collapsedNotifier,
child: Consumer(
builder: (ctx, PublishNotifier<bool> notifier, child) {
if (notifier.currentValue ?? false) {
return RotationTransition(
turns: const AlwaysStoppedAnimation(180 / 360),
child: Tooltip(
richMessage: TextSpan(children: [
TextSpan(text: LocaleKeys.sideBar_openSidebar.tr() + "\n"),
TextSpan(
text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\",
style: const TextStyle(color: Colors.white60),
),
]),
child: FlowyIconButton(
width: 24,
onPressed: () {
notifier.value = false;
ctx.read<HomeBloc>().add(const HomeEvent.collapseMenu());
},
iconPadding: const EdgeInsets.fromLTRB(2, 2, 2, 2),
icon: svgWidget("home/hide_menu", color: theme.iconColor),
)),
2021-11-05 14:23:30 +08:00
);
} else {
return Container();
}
},
),
2021-07-28 18:19:16 +08:00
);
}
2021-11-05 14:23:30 +08:00
List<Widget> _renderNavigationItems(List<NavigationItem> items) {
2021-07-28 18:19:16 +08:00
if (items.isEmpty) {
return [];
}
List<NavigationItem> newItems = _filter(items);
2021-07-28 18:19:16 +08:00
Widget last = NaviItemWidget(newItems.removeLast());
List<Widget> widgets = List.empty(growable: true);
// widgets.addAll(newItems.map((item) => NaviItemDivider(child: NaviItemWidget(item))).toList());
for (final item in newItems) {
widgets.add(NaviItemWidget(item));
widgets.add(const Text('/'));
}
2021-07-28 18:19:16 +08:00
widgets.add(last);
return widgets;
}
List<NavigationItem> _filter(List<NavigationItem> items) {
2021-07-28 18:19:16 +08:00
final length = items.length;
if (length > 4) {
final first = items[0];
final ellipsisItems = items.getRange(1, length - 2).toList();
final last = items.getRange(length - 2, length).toList();
return [
first,
EllipsisNaviItem(items: ellipsisItems),
...last,
];
} else {
return items;
}
}
}
class NaviItemWidget extends StatelessWidget {
final NavigationItem item;
2021-07-28 18:19:16 +08:00
const NaviItemWidget(this.item, {Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Expanded(
child: item.leftBarItem.padding(horizontal: 2, vertical: 2));
2021-07-28 18:19:16 +08:00
}
}
class NaviItemDivider extends StatelessWidget {
final Widget child;
const NaviItemDivider({Key? key, required this.child}) : super(key: key);
@override
Widget build(BuildContext context) {
return Row(
children: [child, const Text('/')],
2021-07-28 18:19:16 +08:00
);
}
}
class EllipsisNaviItem extends NavigationItem {
final List<NavigationItem> items;
2021-07-28 18:19:16 +08:00
EllipsisNaviItem({
required this.items,
});
@override
Widget get leftBarItem => const FlowyText.medium('...');
2021-07-28 18:19:16 +08:00
@override
NavigationCallback get action => (id) {};
2021-07-28 18:19:16 +08:00
}