mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
[flutter]: right click action for menu's app
This commit is contained in:
parent
4798823df3
commit
dd9456e7ff
@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:window_size/window_size.dart';
|
import 'package:window_size/window_size.dart';
|
||||||
import 'package:app_flowy/startup/launcher.dart';
|
import 'package:app_flowy/startup/launcher.dart';
|
||||||
|
import 'package:bloc/bloc.dart';
|
||||||
|
import 'package:flowy_log/flowy_log.dart';
|
||||||
|
|
||||||
class AppWidgetTask extends LaunchTask {
|
class AppWidgetTask extends LaunchTask {
|
||||||
@override
|
@override
|
||||||
@ -14,6 +16,7 @@ class AppWidgetTask extends LaunchTask {
|
|||||||
Future<void> initialize(LaunchContext context) {
|
Future<void> initialize(LaunchContext context) {
|
||||||
final widget = context.getIt<EntryPoint>().create();
|
final widget = context.getIt<EntryPoint>().create();
|
||||||
final app = ApplicationWidget(child: widget);
|
final app = ApplicationWidget(child: widget);
|
||||||
|
Bloc.observer = ApplicationBlocObserver();
|
||||||
runApp(app);
|
runApp(app);
|
||||||
|
|
||||||
return Future(() => {});
|
return Future(() => {});
|
||||||
@ -53,3 +56,19 @@ class AppGlobals {
|
|||||||
static GlobalKey<NavigatorState> rootNavKey = GlobalKey();
|
static GlobalKey<NavigatorState> rootNavKey = GlobalKey();
|
||||||
static NavigatorState get nav => rootNavKey.currentState!;
|
static NavigatorState get nav => rootNavKey.currentState!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ApplicationBlocObserver extends BlocObserver {
|
||||||
|
@override
|
||||||
|
// ignore: unnecessary_overrides
|
||||||
|
void onTransition(Bloc bloc, Transition transition) {
|
||||||
|
// Log.debug("[current]: ${transition.currentState} \n\n[next]: ${transition.nextState}");
|
||||||
|
Log.debug("${transition.nextState}");
|
||||||
|
super.onTransition(bloc, transition);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
|
||||||
|
Log.debug(error);
|
||||||
|
super.onError(bloc, error, stackTrace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:app_flowy/startup/launcher.dart';
|
import 'package:app_flowy/startup/launcher.dart';
|
||||||
import 'package:app_flowy/startup/startup.dart';
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
import 'package:bloc/bloc.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:flowy_sdk/flowy_sdk.dart';
|
import 'package:flowy_sdk/flowy_sdk.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flowy_log/flowy_log.dart';
|
|
||||||
|
|
||||||
class RustSDKInitTask extends LaunchTask {
|
class RustSDKInitTask extends LaunchTask {
|
||||||
@override
|
@override
|
||||||
@ -15,8 +13,6 @@ class RustSDKInitTask extends LaunchTask {
|
|||||||
Future<void> initialize(LaunchContext context) async {
|
Future<void> initialize(LaunchContext context) async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
Bloc.observer = ApplicationBlocObserver();
|
|
||||||
|
|
||||||
Directory directory = await getApplicationDocumentsDirectory();
|
Directory directory = await getApplicationDocumentsDirectory();
|
||||||
final documentPath = directory.path;
|
final documentPath = directory.path;
|
||||||
|
|
||||||
@ -35,18 +31,3 @@ class RustSDKInitTask extends LaunchTask {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ApplicationBlocObserver extends BlocObserver {
|
|
||||||
@override
|
|
||||||
// ignore: unnecessary_overrides
|
|
||||||
void onTransition(Bloc bloc, Transition transition) {
|
|
||||||
// Log.debug("[current]: ${transition.currentState} \n[next]: ${transition.nextState}");
|
|
||||||
super.onTransition(bloc, transition);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
|
|
||||||
Log.debug(error);
|
|
||||||
super.onError(bloc, error, stackTrace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
27
app_flowy/lib/workspace/domain/edit_action/app_edit.dart
Normal file
27
app_flowy/lib/workspace/domain/edit_action/app_edit.dart
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import 'package:flowy_infra/image.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
enum AppDisclosureAction {
|
||||||
|
rename,
|
||||||
|
delete,
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AppDisclosureExtension on AppDisclosureAction {
|
||||||
|
String get name {
|
||||||
|
switch (this) {
|
||||||
|
case AppDisclosureAction.rename:
|
||||||
|
return 'rename';
|
||||||
|
case AppDisclosureAction.delete:
|
||||||
|
return 'delete';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget get icon {
|
||||||
|
switch (this) {
|
||||||
|
case AppDisclosureAction.rename:
|
||||||
|
return svg('editor/edit');
|
||||||
|
case AppDisclosureAction.delete:
|
||||||
|
return svg('editor/delete');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,31 +1,31 @@
|
|||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
enum ViewAction {
|
enum ViewDisclosureAction {
|
||||||
rename,
|
rename,
|
||||||
delete,
|
delete,
|
||||||
duplicate,
|
duplicate,
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ViewActionExtension on ViewAction {
|
extension ViewDisclosureExtension on ViewDisclosureAction {
|
||||||
String get name {
|
String get name {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ViewAction.rename:
|
case ViewDisclosureAction.rename:
|
||||||
return 'rename';
|
return 'rename';
|
||||||
case ViewAction.delete:
|
case ViewDisclosureAction.delete:
|
||||||
return 'delete';
|
return 'delete';
|
||||||
case ViewAction.duplicate:
|
case ViewDisclosureAction.duplicate:
|
||||||
return 'duplicate';
|
return 'duplicate';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget get icon {
|
Widget get icon {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case ViewAction.rename:
|
case ViewDisclosureAction.rename:
|
||||||
return svg('editor/edit');
|
return svg('editor/edit');
|
||||||
case ViewAction.delete:
|
case ViewDisclosureAction.delete:
|
||||||
return svg('editor/delete');
|
return svg('editor/delete');
|
||||||
case ViewAction.duplicate:
|
case ViewDisclosureAction.duplicate:
|
||||||
return svg('editor/copy');
|
return svg('editor/copy');
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -128,8 +128,7 @@ class HomeMenu extends StatelessWidget {
|
|||||||
List<MenuItem> items = [];
|
List<MenuItem> items = [];
|
||||||
items.add(MenuUser(user));
|
items.add(MenuUser(user));
|
||||||
|
|
||||||
List<MenuItem> appWidgets =
|
List<MenuItem> appWidgets = apps.foldRight([], (apps, _) => apps.map((app) => MenuApp(app)).toList());
|
||||||
apps.foldRight([], (apps, _) => apps.map((app) => MenuApp(MenuAppContext(app))).toList());
|
|
||||||
|
|
||||||
items.addAll(appWidgets);
|
items.addAll(appWidgets);
|
||||||
return items;
|
return items;
|
||||||
@ -166,7 +165,7 @@ class MenuList extends StatelessWidget {
|
|||||||
if (index == 0) {
|
if (index == 0) {
|
||||||
return const VSpace(29);
|
return const VSpace(29);
|
||||||
} else {
|
} else {
|
||||||
return const VSpace(24);
|
return VSpace(MenuAppSizes.appVPadding);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
physics: StyledScrollPhysics(),
|
physics: StyledScrollPhysics(),
|
||||||
|
@ -1,76 +1,14 @@
|
|||||||
import 'package:expandable/expandable.dart';
|
|
||||||
import 'package:flowy_infra/flowy_icon_data_icons.dart';
|
|
||||||
import 'package:flowy_infra/image.dart';
|
import 'package:flowy_infra/image.dart';
|
||||||
import 'package:flowy_infra/theme.dart';
|
import 'package:flowy_infra/theme.dart';
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
|
||||||
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
|
|
||||||
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.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:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
|
|
||||||
|
|
||||||
import 'menu_app.dart';
|
|
||||||
|
|
||||||
class MenuAppHeader extends StatelessWidget {
|
|
||||||
final App app;
|
|
||||||
const MenuAppHeader(
|
|
||||||
this.app, {
|
|
||||||
Key? key,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = context.watch<AppTheme>();
|
|
||||||
return SizedBox(
|
|
||||||
height: 20,
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
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.shader1,
|
|
||||||
iconSize: MenuAppSizes.expandedIconSize,
|
|
||||||
iconPadding: EdgeInsets.zero,
|
|
||||||
hasIcon: false,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
HSpace(MenuAppSizes.expandedIconPadding),
|
|
||||||
Expanded(
|
|
||||||
child: GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTapDown: (_) {
|
|
||||||
ExpandableController.of(context, rebuildOnChange: false, required: true)?.toggle();
|
|
||||||
},
|
|
||||||
child: FlowyText.medium(
|
|
||||||
app.name,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
AddButton(
|
|
||||||
onSelected: (viewType) {
|
|
||||||
context.read<AppBloc>().add(AppEvent.createView("New view", "", viewType));
|
|
||||||
},
|
|
||||||
).padding(right: MenuAppSizes.expandedIconPadding),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddButton extends StatelessWidget {
|
class AddButton extends StatelessWidget {
|
||||||
final Function(ViewType) onSelected;
|
final Function(ViewType) onSelected;
|
||||||
const AddButton({
|
const AddButton({
|
@ -0,0 +1,93 @@
|
|||||||
|
import 'package:expandable/expandable.dart';
|
||||||
|
import 'package:flowy_infra/flowy_icon_data_icons.dart';
|
||||||
|
import 'package:flowy_infra/theme.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
|
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
|
|
||||||
|
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
|
import '../menu_app.dart';
|
||||||
|
import 'add_button.dart';
|
||||||
|
import 'right_click_action.dart';
|
||||||
|
|
||||||
|
class MenuAppHeader extends StatelessWidget {
|
||||||
|
final App app;
|
||||||
|
const MenuAppHeader(
|
||||||
|
this.app, {
|
||||||
|
Key? key,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = context.watch<AppTheme>();
|
||||||
|
return SizedBox(
|
||||||
|
height: MenuAppSizes.headerHeight,
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
_renderExpandedIcon(context, theme),
|
||||||
|
HSpace(MenuAppSizes.iconPadding),
|
||||||
|
_renderTitle(context),
|
||||||
|
_renderAddButton(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _renderExpandedIcon(BuildContext context, AppTheme theme) {
|
||||||
|
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.shader1,
|
||||||
|
iconSize: MenuAppSizes.iconSize,
|
||||||
|
iconPadding: const EdgeInsets.fromLTRB(10, 0, 0, 0),
|
||||||
|
hasIcon: false,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _renderTitle(BuildContext context) {
|
||||||
|
return Expanded(
|
||||||
|
child: GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () {
|
||||||
|
// Open the document
|
||||||
|
ExpandableController.of(context, rebuildOnChange: false, required: true)?.toggle();
|
||||||
|
},
|
||||||
|
onSecondaryTap: () {
|
||||||
|
AppDisclosureActions(onSelected: (action) {
|
||||||
|
print(action);
|
||||||
|
}).show(context, context, anchorDirection: AnchorDirection.bottomWithCenterAligned);
|
||||||
|
},
|
||||||
|
child: FlowyText.medium(
|
||||||
|
app.name,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _renderAddButton(BuildContext context) {
|
||||||
|
return AddButton(
|
||||||
|
onSelected: (viewType) {
|
||||||
|
context.read<AppBloc>().add(AppEvent.createView("New view", "", viewType));
|
||||||
|
},
|
||||||
|
).padding(right: MenuAppSizes.headerPadding);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import 'package:app_flowy/workspace/domain/edit_action/app_edit.dart';
|
||||||
|
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||||
|
import 'package:dartz/dartz.dart' as dartz;
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AppDisclosureActions with ActionList<AppDisclosureActionWrapper> implements FlowyOverlayDelegate {
|
||||||
|
final Function(dartz.Option<AppDisclosureAction>) onSelected;
|
||||||
|
final _items = AppDisclosureAction.values.map((action) => AppDisclosureActionWrapper(action)).toList();
|
||||||
|
|
||||||
|
AppDisclosureActions({
|
||||||
|
required this.onSelected,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get identifier => "ViewDisclosureActions";
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<AppDisclosureActionWrapper> get items => _items;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get maxWidth => 162;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void Function(dartz.Option<AppDisclosureActionWrapper> p1) get selectCallback => (result) {
|
||||||
|
result.fold(
|
||||||
|
() => onSelected(dartz.none()),
|
||||||
|
(wrapper) => onSelected(dartz.some(wrapper.inner)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
FlowyOverlayDelegate? get delegate => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didRemove() {
|
||||||
|
onSelected(dartz.none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppDisclosureActionWrapper extends ActionItemData {
|
||||||
|
final AppDisclosureAction inner;
|
||||||
|
|
||||||
|
AppDisclosureActionWrapper(this.inner);
|
||||||
|
@override
|
||||||
|
Widget get icon => inner.icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => inner.name;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/header.dart';
|
import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/header/header.dart';
|
||||||
import 'package:expandable/expandable.dart';
|
import 'package:expandable/expandable.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-workspace/app_create.pb.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
|
||||||
@ -10,36 +10,12 @@ import 'package:app_flowy/workspace/presentation/widgets/menu/menu.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
import 'section/section.dart';
|
import 'section/section.dart';
|
||||||
// [[diagram: MenuApp]]
|
|
||||||
// ┌────────┐
|
|
||||||
// ┌────▶│AppBloc │────────────────┐
|
|
||||||
// │ └────────┘ │
|
|
||||||
// │ │
|
|
||||||
// │ 1.1 fetch views │
|
|
||||||
// │ 1.2 update the MenuAppContext │
|
|
||||||
// │ with the views │
|
|
||||||
// │ ▼ 3.render sections
|
|
||||||
// ┌────────┐ │ ┌──────────────┐ ┌──────────────┐
|
|
||||||
// │MenuApp │────┤ │MenuAppContext│─┬──▶│ ViewSection │────────────────┐
|
|
||||||
// └────────┘ │ └──────────────┘ │ └──────────────┘ │
|
|
||||||
// │ ▲ │ │
|
|
||||||
// │ │ │ │
|
|
||||||
// │ │ hold │ │
|
|
||||||
// │ │ │ bind ▼
|
|
||||||
// │ │ │ ┌─────────────────┐ ┌────────────────────┐
|
|
||||||
// │ ┌──────────────┐ │ └─▶│ViewListNotifier │──▶│ViewSectionNotifier │
|
|
||||||
// └───▶│AppListenBloc │───────────┘ └─────────────────┘ └────────────────────┘
|
|
||||||
// └──────────────┘
|
|
||||||
// 4.notifier binding. So The ViewSection
|
|
||||||
// 2.1 listen on the app will be re rebuild if the the number of
|
|
||||||
// 2.2 notify if the number of the app's view the views in MenuAppContext was changed.
|
|
||||||
// was changed
|
|
||||||
// 2.3 update MenuAppContext with the new
|
|
||||||
// views
|
|
||||||
|
|
||||||
class MenuApp extends MenuItem {
|
class MenuApp extends MenuItem {
|
||||||
final MenuAppContext appCtx;
|
final App app;
|
||||||
MenuApp(this.appCtx, {Key? key}) : super(key: appCtx.valueKey());
|
final notifier = AppDataNotifier();
|
||||||
|
|
||||||
|
MenuApp(this.app, {Key? key}) : super(key: ValueKey("${app.id}${app.version}"));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -47,7 +23,7 @@ class MenuApp extends MenuItem {
|
|||||||
providers: [
|
providers: [
|
||||||
BlocProvider<AppBloc>(
|
BlocProvider<AppBloc>(
|
||||||
create: (context) {
|
create: (context) {
|
||||||
final appBloc = getIt<AppBloc>(param1: appCtx.app.id);
|
final appBloc = getIt<AppBloc>(param1: app.id);
|
||||||
appBloc.add(const AppEvent.initial());
|
appBloc.add(const AppEvent.initial());
|
||||||
return appBloc;
|
return appBloc;
|
||||||
},
|
},
|
||||||
@ -55,13 +31,12 @@ class MenuApp extends MenuItem {
|
|||||||
],
|
],
|
||||||
child: BlocListener<AppBloc, AppState>(
|
child: BlocListener<AppBloc, AppState>(
|
||||||
listenWhen: (p, c) => p.selectedView != c.selectedView,
|
listenWhen: (p, c) => p.selectedView != c.selectedView,
|
||||||
listener: (context, state) => appCtx.viewList.selectView = state.selectedView,
|
listener: (context, state) => notifier.selectView = state.selectedView,
|
||||||
child: BlocBuilder<AppBloc, AppState>(
|
child: BlocBuilder<AppBloc, AppState>(
|
||||||
buildWhen: (p, c) => p.views != c.views,
|
buildWhen: (p, c) => p.views != c.views,
|
||||||
builder: (context, state) {
|
builder: (context, state) {
|
||||||
appCtx.viewList.views = state.views;
|
notifier.views = state.views;
|
||||||
final child = _renderViewSection(appCtx.viewList);
|
return expandableWrapper(context, _renderViewSection(notifier));
|
||||||
return expandableWrapper(context, child);
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -71,7 +46,7 @@ class MenuApp extends MenuItem {
|
|||||||
ExpandableNotifier expandableWrapper(BuildContext context, Widget child) {
|
ExpandableNotifier expandableWrapper(BuildContext context, Widget child) {
|
||||||
return ExpandableNotifier(
|
return ExpandableNotifier(
|
||||||
child: ScrollOnExpand(
|
child: ScrollOnExpand(
|
||||||
scrollOnExpand: true,
|
scrollOnExpand: false,
|
||||||
scrollOnCollapse: false,
|
scrollOnCollapse: false,
|
||||||
child: Column(
|
child: Column(
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -84,7 +59,7 @@ class MenuApp extends MenuItem {
|
|||||||
iconPadding: EdgeInsets.zero,
|
iconPadding: EdgeInsets.zero,
|
||||||
hasIcon: false,
|
hasIcon: false,
|
||||||
),
|
),
|
||||||
header: MenuAppHeader(appCtx.app),
|
header: MenuAppHeader(app),
|
||||||
expanded: child,
|
expanded: child,
|
||||||
collapsed: const SizedBox(),
|
collapsed: const SizedBox(),
|
||||||
),
|
),
|
||||||
@ -94,12 +69,10 @@ class MenuApp extends MenuItem {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _renderViewSection(ViewListNotifier viewListNotifier) {
|
Widget _renderViewSection(AppDataNotifier viewListNotifier) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [ChangeNotifierProvider.value(value: viewListNotifier)],
|
||||||
ChangeNotifierProvider.value(value: viewListNotifier),
|
child: Consumer(builder: (context, AppDataNotifier notifier, child) {
|
||||||
],
|
|
||||||
child: Consumer(builder: (context, ViewListNotifier notifier, child) {
|
|
||||||
return const ViewSection().padding(vertical: 8);
|
return const ViewSection().padding(vertical: 8);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -110,16 +83,19 @@ class MenuApp extends MenuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MenuAppSizes {
|
class MenuAppSizes {
|
||||||
static double expandedIconSize = 16;
|
static double iconSize = 16;
|
||||||
static double expandedIconPadding = 6;
|
static double headerHeight = 26;
|
||||||
|
static double headerPadding = 6;
|
||||||
|
static double iconPadding = 6;
|
||||||
|
static double appVPadding = 14;
|
||||||
static double scale = 1;
|
static double scale = 1;
|
||||||
static double get expandedPadding => expandedIconSize * scale + expandedIconPadding;
|
static double get expandedPadding => iconSize * scale + headerPadding;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ViewListNotifier extends ChangeNotifier {
|
class AppDataNotifier extends ChangeNotifier {
|
||||||
List<View> _views = [];
|
List<View> _views = [];
|
||||||
View? _selectedView;
|
View? _selectedView;
|
||||||
ViewListNotifier();
|
AppDataNotifier();
|
||||||
|
|
||||||
set views(List<View>? items) {
|
set views(List<View>? items) {
|
||||||
_views = items ?? List.empty(growable: false);
|
_views = items ?? List.empty(growable: false);
|
||||||
@ -135,12 +111,3 @@ class ViewListNotifier extends ChangeNotifier {
|
|||||||
|
|
||||||
List<View> get views => _views;
|
List<View> get views => _views;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MenuAppContext {
|
|
||||||
final App app;
|
|
||||||
final viewList = ViewListNotifier();
|
|
||||||
|
|
||||||
MenuAppContext(this.app);
|
|
||||||
|
|
||||||
Key valueKey() => ValueKey("${app.id}${app.version}");
|
|
||||||
}
|
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
import 'package:dartz/dartz.dart' as dartz;
|
|
||||||
import 'package:flowy_infra/theme.dart';
|
|
||||||
import 'package:flowy_infra_ui/flowy_infra_ui.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:flutter/material.dart';
|
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:provider/provider.dart';
|
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
|
||||||
import 'package:app_flowy/workspace/domain/view_edit.dart';
|
|
||||||
|
|
||||||
class ViewActionList implements FlowyOverlayDelegate {
|
|
||||||
final Function(dartz.Option<ViewAction>) onSelected;
|
|
||||||
final BuildContext anchorContext;
|
|
||||||
final String _identifier = 'ViewActionList';
|
|
||||||
|
|
||||||
const ViewActionList({required this.anchorContext, required this.onSelected});
|
|
||||||
|
|
||||||
void show(BuildContext buildContext) {
|
|
||||||
final items = ViewAction.values
|
|
||||||
.map((action) => ActionItem(
|
|
||||||
action: action,
|
|
||||||
onSelected: (action) {
|
|
||||||
FlowyOverlay.of(buildContext).remove(_identifier);
|
|
||||||
onSelected(dartz.some(action));
|
|
||||||
}))
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
ListOverlay.showWithAnchor(
|
|
||||||
buildContext,
|
|
||||||
identifier: _identifier,
|
|
||||||
itemCount: items.length,
|
|
||||||
itemBuilder: (context, index) => items[index],
|
|
||||||
anchorContext: anchorContext,
|
|
||||||
anchorDirection: AnchorDirection.bottomRight,
|
|
||||||
maxWidth: 162,
|
|
||||||
maxHeight: ViewAction.values.length * 32,
|
|
||||||
delegate: this,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didRemove() {
|
|
||||||
onSelected(dartz.none());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ActionItem extends StatelessWidget {
|
|
||||||
final ViewAction action;
|
|
||||||
final Function(ViewAction) onSelected;
|
|
||||||
const ActionItem({
|
|
||||||
Key? key,
|
|
||||||
required this.action,
|
|
||||||
required this.onSelected,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final theme = context.watch<AppTheme>();
|
|
||||||
|
|
||||||
return FlowyHover(
|
|
||||||
config: HoverDisplayConfig(hoverColor: theme.hover),
|
|
||||||
builder: (context, onHover) {
|
|
||||||
return GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () => onSelected(action),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
action.icon,
|
|
||||||
const HSpace(10),
|
|
||||||
FlowyText.medium(
|
|
||||||
action.name,
|
|
||||||
fontSize: 12,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
).padding(
|
|
||||||
horizontal: 6,
|
|
||||||
vertical: 6,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,72 @@
|
|||||||
|
import 'package:app_flowy/workspace/domain/edit_action/view_edit.dart';
|
||||||
|
import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||||
|
import 'package:dartz/dartz.dart' as dartz;
|
||||||
|
import 'package:flowy_infra/image.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
|
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
// [[Widget: LifeCycle]]
|
||||||
|
// https://flutterbyexample.com/lesson/stateful-widget-lifecycle
|
||||||
|
class ViewDisclosureButton extends StatelessWidget
|
||||||
|
with ActionList<ViewDisclosureActionWrapper>
|
||||||
|
implements FlowyOverlayDelegate {
|
||||||
|
final Function() onTap;
|
||||||
|
final Function(dartz.Option<ViewDisclosureAction>) onSelected;
|
||||||
|
final _items = ViewDisclosureAction.values.map((action) => ViewDisclosureActionWrapper(action)).toList();
|
||||||
|
|
||||||
|
ViewDisclosureButton({
|
||||||
|
Key? key,
|
||||||
|
required this.onTap,
|
||||||
|
required this.onSelected,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FlowyIconButton(
|
||||||
|
iconPadding: const EdgeInsets.all(5),
|
||||||
|
width: 26,
|
||||||
|
onPressed: () {
|
||||||
|
onTap();
|
||||||
|
show(context, context);
|
||||||
|
},
|
||||||
|
icon: svg("editor/details"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get identifier => "ViewDisclosureActions";
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<ViewDisclosureActionWrapper> get items => _items;
|
||||||
|
|
||||||
|
@override
|
||||||
|
double get maxWidth => 162;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void Function(dartz.Option<ViewDisclosureActionWrapper> p1) get selectCallback => (result) {
|
||||||
|
result.fold(
|
||||||
|
() => onSelected(dartz.none()),
|
||||||
|
(wrapper) => onSelected(dartz.some(wrapper.inner)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
FlowyOverlayDelegate? get delegate => this;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didRemove() {
|
||||||
|
onSelected(dartz.none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ViewDisclosureActionWrapper extends ActionItemData {
|
||||||
|
final ViewDisclosureAction inner;
|
||||||
|
|
||||||
|
ViewDisclosureActionWrapper(this.inner);
|
||||||
|
@override
|
||||||
|
Widget get icon => inner.icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get name => inner.name;
|
||||||
|
}
|
@ -1,13 +1,10 @@
|
|||||||
import 'package:app_flowy/startup/startup.dart';
|
import 'package:app_flowy/startup/startup.dart';
|
||||||
import 'package:app_flowy/workspace/application/view/view_bloc.dart';
|
import 'package:app_flowy/workspace/application/view/view_bloc.dart';
|
||||||
import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart';
|
import 'package:app_flowy/workspace/domain/edit_action/view_edit.dart';
|
||||||
import 'package:app_flowy/workspace/domain/view_ext.dart';
|
|
||||||
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
|
||||||
import 'package:dartz/dartz.dart' as dartz;
|
import 'package:dartz/dartz.dart' as dartz;
|
||||||
import 'package:flowy_infra/image.dart';
|
|
||||||
import 'package:flowy_infra/theme.dart';
|
import 'package:flowy_infra/theme.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
import 'package:flowy_infra_ui/style_widget/hover.dart';
|
||||||
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
|
||||||
import 'package:flowy_infra_ui/style_widget/text.dart';
|
import 'package:flowy_infra_ui/style_widget/text.dart';
|
||||||
import 'package:flowy_infra_ui/widget/spacing.dart';
|
import 'package:flowy_infra_ui/widget/spacing.dart';
|
||||||
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
|
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
|
||||||
@ -15,12 +12,10 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:styled_widget/styled_widget.dart';
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
|
||||||
import 'package:app_flowy/workspace/domain/image.dart';
|
import 'package:app_flowy/workspace/domain/image.dart';
|
||||||
import 'package:app_flowy/workspace/domain/view_edit.dart';
|
|
||||||
import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
|
import 'package:app_flowy/workspace/presentation/widgets/menu/widget/app/menu_app.dart';
|
||||||
|
|
||||||
import 'action.dart';
|
import 'disclosure_action.dart';
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class ViewSectionItem extends StatelessWidget {
|
class ViewSectionItem extends StatelessWidget {
|
||||||
@ -81,15 +76,15 @@ class ViewSectionItem extends StatelessWidget {
|
|||||||
height: 26,
|
height: 26,
|
||||||
child: Row(children: children).padding(
|
child: Row(children: children).padding(
|
||||||
left: MenuAppSizes.expandedPadding,
|
left: MenuAppSizes.expandedPadding,
|
||||||
right: MenuAppSizes.expandedIconPadding,
|
right: MenuAppSizes.headerPadding,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleAction(BuildContext context, dartz.Option<ViewAction> action) {
|
void _handleAction(BuildContext context, dartz.Option<ViewDisclosureAction> action) {
|
||||||
action.foldRight({}, (action, previous) {
|
action.foldRight({}, (action, previous) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ViewAction.rename:
|
case ViewDisclosureAction.rename:
|
||||||
TextFieldDialog(
|
TextFieldDialog(
|
||||||
title: 'Rename',
|
title: 'Rename',
|
||||||
value: context.read<ViewBloc>().state.view.name,
|
value: context.read<ViewBloc>().state.view.name,
|
||||||
@ -99,42 +94,13 @@ class ViewSectionItem extends StatelessWidget {
|
|||||||
).show(context);
|
).show(context);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case ViewAction.delete:
|
case ViewDisclosureAction.delete:
|
||||||
context.read<ViewBloc>().add(const ViewEvent.delete());
|
context.read<ViewBloc>().add(const ViewEvent.delete());
|
||||||
break;
|
break;
|
||||||
case ViewAction.duplicate:
|
case ViewDisclosureAction.duplicate:
|
||||||
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
context.read<ViewBloc>().add(const ViewEvent.duplicate());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [[Widget: LifeCycle]]
|
|
||||||
// https://flutterbyexample.com/lesson/stateful-widget-lifecycle
|
|
||||||
|
|
||||||
class ViewDisclosureButton extends StatelessWidget {
|
|
||||||
final Function() onTap;
|
|
||||||
final Function(dartz.Option<ViewAction>) onSelected;
|
|
||||||
const ViewDisclosureButton({
|
|
||||||
Key? key,
|
|
||||||
required this.onTap,
|
|
||||||
required this.onSelected,
|
|
||||||
}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FlowyIconButton(
|
|
||||||
iconPadding: const EdgeInsets.all(5),
|
|
||||||
width: 26,
|
|
||||||
onPressed: () {
|
|
||||||
onTap();
|
|
||||||
ViewActionList(
|
|
||||||
anchorContext: context,
|
|
||||||
onSelected: onSelected,
|
|
||||||
).show(context);
|
|
||||||
},
|
|
||||||
icon: svg("editor/details"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -12,20 +12,20 @@ import 'item.dart';
|
|||||||
import 'package:async/async.dart';
|
import 'package:async/async.dart';
|
||||||
|
|
||||||
class ViewSectionNotifier with ChangeNotifier {
|
class ViewSectionNotifier with ChangeNotifier {
|
||||||
List<View> innerViews;
|
List<View> _views;
|
||||||
View? _selectedView;
|
View? _selectedView;
|
||||||
CancelableOperation? _notifyListenerOperation;
|
CancelableOperation? _notifyListenerOperation;
|
||||||
ViewSectionNotifier(this.innerViews);
|
ViewSectionNotifier(List<View> views) : _views = views;
|
||||||
|
|
||||||
set views(List<View> views) => innerViews = views;
|
set views(List<View> views) {
|
||||||
List<View> get views => innerViews;
|
if (_views != views) {
|
||||||
set setViews(List<View> views) {
|
_views = views;
|
||||||
if (innerViews != views) {
|
|
||||||
innerViews = views;
|
|
||||||
_notifyListeners();
|
_notifyListeners();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<View> get views => _views;
|
||||||
|
|
||||||
set selectView(View? view) {
|
set selectView(View? view) {
|
||||||
if (_selectedView == view) {
|
if (_selectedView == view) {
|
||||||
return;
|
return;
|
||||||
@ -48,8 +48,8 @@ class ViewSectionNotifier with ChangeNotifier {
|
|||||||
|
|
||||||
View? get selectedView => _selectedView;
|
View? get selectedView => _selectedView;
|
||||||
|
|
||||||
void update(ViewListNotifier notifier) {
|
void update(AppDataNotifier notifier) {
|
||||||
setViews = notifier.views;
|
views = notifier.views;
|
||||||
selectView = notifier.selectedView;
|
selectView = notifier.selectedView;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,9 +69,9 @@ class ViewSection extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// The ViewListNotifier will be updated after ViewListData changed passed by parent widget
|
// The ViewListNotifier will be updated after ViewListData changed passed by parent widget
|
||||||
return ChangeNotifierProxyProvider<ViewListNotifier, ViewSectionNotifier>(
|
return ChangeNotifierProxyProvider<AppDataNotifier, ViewSectionNotifier>(
|
||||||
create: (_) {
|
create: (_) {
|
||||||
final views = Provider.of<ViewListNotifier>(context, listen: false).views;
|
final views = Provider.of<AppDataNotifier>(context, listen: false).views;
|
||||||
return ViewSectionNotifier(views);
|
return ViewSectionNotifier(views);
|
||||||
},
|
},
|
||||||
update: (_, notifier, controller) => controller!..update(notifier),
|
update: (_, notifier, controller) => controller!..update(notifier),
|
||||||
|
@ -0,0 +1,99 @@
|
|||||||
|
import 'package:flowy_infra/theme.dart';
|
||||||
|
import 'package:flowy_infra_ui/flowy_infra_ui.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:flutter/material.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:styled_widget/styled_widget.dart';
|
||||||
|
import 'package:dartz/dartz.dart' as dartz;
|
||||||
|
|
||||||
|
abstract class ActionList<T extends ActionItemData> {
|
||||||
|
List<T> get items;
|
||||||
|
|
||||||
|
String get identifier;
|
||||||
|
|
||||||
|
double get maxWidth;
|
||||||
|
|
||||||
|
void Function(dartz.Option<T>) get selectCallback;
|
||||||
|
|
||||||
|
FlowyOverlayDelegate? get delegate;
|
||||||
|
|
||||||
|
void show(BuildContext buildContext, BuildContext anchorContext,
|
||||||
|
{AnchorDirection anchorDirection = AnchorDirection.bottomRight}) {
|
||||||
|
final widgets = items
|
||||||
|
.map((action) => ActionItem<T>(
|
||||||
|
action: action,
|
||||||
|
onSelected: (action) {
|
||||||
|
FlowyOverlay.of(buildContext).remove(identifier);
|
||||||
|
selectCallback(dartz.some(action));
|
||||||
|
}))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
double totalHeight = widgets.length * (ActionListSizes.itemHeight + ActionListSizes.padding * 2);
|
||||||
|
|
||||||
|
ListOverlay.showWithAnchor(
|
||||||
|
buildContext,
|
||||||
|
identifier: identifier,
|
||||||
|
itemCount: widgets.length,
|
||||||
|
itemBuilder: (context, index) => widgets[index],
|
||||||
|
anchorContext: anchorContext,
|
||||||
|
anchorDirection: anchorDirection,
|
||||||
|
maxWidth: maxWidth,
|
||||||
|
maxHeight: totalHeight,
|
||||||
|
delegate: delegate,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class ActionItemData {
|
||||||
|
Widget get icon;
|
||||||
|
String get name;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActionListSizes {
|
||||||
|
static double itemHPadding = 10;
|
||||||
|
static double itemHeight = 16;
|
||||||
|
static double padding = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ActionItem<T extends ActionItemData> extends StatelessWidget {
|
||||||
|
final T action;
|
||||||
|
final Function(T) onSelected;
|
||||||
|
const ActionItem({
|
||||||
|
Key? key,
|
||||||
|
required this.action,
|
||||||
|
required this.onSelected,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = context.watch<AppTheme>();
|
||||||
|
|
||||||
|
return FlowyHover(
|
||||||
|
config: HoverDisplayConfig(hoverColor: theme.hover),
|
||||||
|
builder: (context, onHover) {
|
||||||
|
return GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () => onSelected(action),
|
||||||
|
child: SizedBox(
|
||||||
|
height: ActionListSizes.itemHeight,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
action.icon,
|
||||||
|
HSpace(ActionListSizes.itemHPadding),
|
||||||
|
FlowyText.medium(
|
||||||
|
action.name,
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).padding(
|
||||||
|
horizontal: ActionListSizes.padding,
|
||||||
|
vertical: ActionListSizes.padding,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -20,13 +20,14 @@ class ListOverlay extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
const padding = EdgeInsets.symmetric(horizontal: 6, vertical: 6);
|
||||||
return Material(
|
return Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
child: Container(
|
child: Container(
|
||||||
constraints: BoxConstraints.tight(Size(maxWidth, maxHeight)),
|
constraints: BoxConstraints.tight(Size(maxWidth, maxHeight + padding.vertical)),
|
||||||
decoration: FlowyDecoration.decoration(),
|
decoration: FlowyDecoration.decoration(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
|
padding: padding,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemBuilder: itemBuilder,
|
itemBuilder: itemBuilder,
|
||||||
|
@ -9,7 +9,7 @@ use crate::{
|
|||||||
use async_stream::stream;
|
use async_stream::stream;
|
||||||
use flowy_ot::core::{Delta, OperationTransformable};
|
use flowy_ot::core::{Delta, OperationTransformable};
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
use std::{convert::TryFrom, sync::Arc, thread};
|
use std::{convert::TryFrom, sync::Arc};
|
||||||
use tokio::sync::{mpsc, RwLock};
|
use tokio::sync::{mpsc, RwLock};
|
||||||
|
|
||||||
pub struct DocumentActor {
|
pub struct DocumentActor {
|
||||||
|
@ -164,6 +164,9 @@ impl ViewController {
|
|||||||
.payload(updated_view.clone())
|
.payload(updated_view.clone())
|
||||||
.send();
|
.send();
|
||||||
|
|
||||||
|
//
|
||||||
|
let _ = notify_view_num_changed(&updated_view.belong_to_id, self.trash_can.clone(), conn)?;
|
||||||
|
|
||||||
let _ = self.update_view_on_server(params);
|
let _ = self.update_view_on_server(params);
|
||||||
Ok(updated_view)
|
Ok(updated_view)
|
||||||
}
|
}
|
||||||
@ -267,8 +270,7 @@ async fn handle_trash_event(
|
|||||||
let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
|
let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
|
||||||
for identifier in identifiers.items {
|
for identifier in identifiers.items {
|
||||||
let view_table = ViewTableSql::read_view(&identifier.id, conn)?;
|
let view_table = ViewTableSql::read_view(&identifier.id, conn)?;
|
||||||
let repeated_view = read_belonging_view(&view_table.belong_to_id, trash_can.clone(), conn)?;
|
let _ = notify_view_num_changed(&view_table.belong_to_id, trash_can.clone(), conn)?;
|
||||||
let _ = notify_view_num_changed(&view_table.belong_to_id, repeated_view)?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
@ -289,8 +291,7 @@ async fn handle_trash_event(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for notify_id in notify_ids {
|
for notify_id in notify_ids {
|
||||||
let repeated_view = read_belonging_view(¬ify_id, trash_can.clone(), conn)?;
|
let _ = notify_view_num_changed(¬ify_id, trash_can.clone(), conn)?;
|
||||||
let _ = notify_view_num_changed(¬ify_id, repeated_view)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -302,8 +303,13 @@ async fn handle_trash_event(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(repeated_view), fields(view_count), err)]
|
#[tracing::instrument(skip(belong_to_id, trash_can, conn), fields(view_count), err)]
|
||||||
fn notify_view_num_changed(belong_to_id: &str, repeated_view: RepeatedView) -> WorkspaceResult<()> {
|
fn notify_view_num_changed(
|
||||||
|
belong_to_id: &str,
|
||||||
|
trash_can: Arc<TrashCan>,
|
||||||
|
conn: &SqliteConnection,
|
||||||
|
) -> WorkspaceResult<()> {
|
||||||
|
let repeated_view = read_belonging_view(belong_to_id, trash_can.clone(), conn)?;
|
||||||
tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
|
tracing::Span::current().record("view_count", &format!("{}", repeated_view.len()).as_str());
|
||||||
send_dart_notification(&belong_to_id, WorkspaceNotification::AppViewsChanged)
|
send_dart_notification(&belong_to_id, WorkspaceNotification::AppViewsChanged)
|
||||||
.payload(repeated_view)
|
.payload(repeated_view)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user