feat: word and char count (#4705)

* feat: word and char count

* chore: lints after merge

* feat: add create time of view in more action

* fix: missing comma lint

* feat: add duplicate/delete + database view more

* fix: dispose logic

* feat: code cleanup

* fix: add false for isDocument in databases

* feat: register view info bloc on plugin basis

* fix: accidental removal

* chore: clean up

* fix: add ViewInfoBloc above Row Document Editor

* chore: final clean up
This commit is contained in:
Mathias Mogensen 2024-02-25 16:46:13 +01:00 committed by GitHub
parent 93af6e69a1
commit 609557c357
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 566 additions and 173 deletions

View File

@ -1,10 +1,11 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/bottom_sheet.dart';
import 'package:appflowy/plugins/document/presentation/more/font_size_slider.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/font_size_stepper.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import '../setting.dart'; import '../setting.dart';

View File

@ -1,12 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart'; import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';

View File

@ -1,16 +1,20 @@
import 'package:flutter/material.dart';
import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/application/database_controller.dart';
import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart'; import 'package:appflowy/plugins/database/application/tab_bar_bloc.dart';
import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/plugins/database/widgets/share_button.dart'; import 'package:appflowy/plugins/database/widgets/share_button.dart';
import 'package:appflowy/plugins/util.dart'; import 'package:appflowy/plugins/util.dart';
import 'package:appflowy/startup/plugin/plugin.dart'; import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart'; import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/more_view_actions.dart';
import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart'; import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart';
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart'; import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'desktop/tab_bar_header.dart'; import 'desktop/tab_bar_header.dart';
@ -183,7 +187,9 @@ class DatabaseTabBarViewPlugin extends Plugin {
@override @override
final ViewPluginNotifier notifier; final ViewPluginNotifier notifier;
final PluginType _pluginType; final PluginType _pluginType;
late final ViewInfoBloc _viewInfoBloc;
/// Used to open a Row on plugin load /// Used to open a Row on plugin load
/// ///
@ -191,6 +197,7 @@ class DatabaseTabBarViewPlugin extends Plugin {
@override @override
PluginWidgetBuilder get widgetBuilder => DatabasePluginWidgetBuilder( PluginWidgetBuilder get widgetBuilder => DatabasePluginWidgetBuilder(
bloc: _viewInfoBloc,
notifier: notifier, notifier: notifier,
initialRowId: initialRowId, initialRowId: initialRowId,
); );
@ -200,11 +207,28 @@ class DatabaseTabBarViewPlugin extends Plugin {
@override @override
PluginType get pluginType => _pluginType; PluginType get pluginType => _pluginType;
@override
void init() {
_viewInfoBloc = ViewInfoBloc(view: notifier.view)
..add(const ViewInfoEvent.started());
}
@override
void dispose() {
_viewInfoBloc.close();
notifier.dispose();
}
} }
class DatabasePluginWidgetBuilder extends PluginWidgetBuilder { class DatabasePluginWidgetBuilder extends PluginWidgetBuilder {
DatabasePluginWidgetBuilder({required this.notifier, this.initialRowId}); DatabasePluginWidgetBuilder({
required this.bloc,
required this.notifier,
this.initialRowId,
});
final ViewInfoBloc bloc;
final ViewPluginNotifier notifier; final ViewPluginNotifier notifier;
/// Used to open a Row on plugin load /// Used to open a Row on plugin load
@ -225,6 +249,7 @@ class DatabasePluginWidgetBuilder extends PluginWidgetBuilder {
context?.onDeleted(notifier.view, deletedView.index); context?.onDeleted(notifier.view, deletedView.index);
} }
}); });
return DatabaseTabBarView( return DatabaseTabBarView(
key: ValueKey(notifier.view.id), key: ValueKey(notifier.view.id),
view: notifier.view, view: notifier.view,
@ -238,9 +263,16 @@ class DatabasePluginWidgetBuilder extends PluginWidgetBuilder {
@override @override
Widget? get rightBarItem { Widget? get rightBarItem {
return DatabaseShareButton( final view = notifier.view;
key: ValueKey(notifier.view.id), return BlocProvider<ViewInfoBloc>.value(
view: notifier.view, value: bloc,
child: Row(
children: [
DatabaseShareButton(key: ValueKey(view.id), view: view),
const HSpace(4),
MoreViewActions(view: view, isDocument: false),
],
),
); );
} }
} }

View File

@ -1,7 +1,9 @@
import 'package:flutter/material.dart';
import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/workspace/application/view/view_ext.dart'; import 'package:appflowy/workspace/application/view/view_ext.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart'; import 'package:appflowy/workspace/application/view/view_listener.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
class DatabaseViewWidget extends StatefulWidget { class DatabaseViewWidget extends StatefulWidget {
const DatabaseViewWidget({ const DatabaseViewWidget({
@ -28,11 +30,13 @@ class _DatabaseViewWidgetState extends State<DatabaseViewWidget> {
/// The view will be updated by the [ViewListener]. /// The view will be updated by the [ViewListener].
late ViewPB view; late ViewPB view;
late Plugin viewPlugin;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
view = widget.view; view = widget.view;
viewPlugin = view.plugin()..init();
_listenOnViewUpdated(); _listenOnViewUpdated();
} }
@ -40,6 +44,7 @@ class _DatabaseViewWidgetState extends State<DatabaseViewWidget> {
void dispose() { void dispose() {
_layoutTypeChangeNotifier.dispose(); _layoutTypeChangeNotifier.dispose();
_listener.stop(); _listener.stop();
viewPlugin.dispose();
super.dispose(); super.dispose();
} }
@ -47,12 +52,9 @@ class _DatabaseViewWidgetState extends State<DatabaseViewWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ValueListenableBuilder<ViewLayoutPB>( return ValueListenableBuilder<ViewLayoutPB>(
valueListenable: _layoutTypeChangeNotifier, valueListenable: _layoutTypeChangeNotifier,
builder: (_, __, ___) { builder: (_, __, ___) => viewPlugin.widgetBuilder.buildWidget(
return view shrinkWrap: widget.shrinkWrap,
.plugin() ),
.widgetBuilder
.buildWidget(shrinkWrap: widget.shrinkWrap);
},
); );
} }

View File

@ -1,13 +1,15 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database/grid/application/row/row_document_bloc.dart'; import 'package:appflowy/plugins/database/grid/application/row/row_document_bloc.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart'; import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_page.dart'; import 'package:appflowy/plugins/document/presentation/editor_page.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart'; import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class RowDocument extends StatelessWidget { class RowDocument extends StatelessWidget {
@ -107,22 +109,25 @@ class _RowEditorState extends State<RowEditor> {
howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(), howToFix: LocaleKeys.errorDialog_howToFixFallback.tr(),
); );
} }
return IntrinsicHeight( return IntrinsicHeight(
child: Container( child: Container(
constraints: const BoxConstraints(minHeight: 300), constraints: const BoxConstraints(minHeight: 300),
child: AppFlowyEditorPage( child: BlocProvider<ViewInfoBloc>(
shrinkWrap: true, create: (context) => ViewInfoBloc(view: widget.viewPB),
autoFocus: false, child: AppFlowyEditorPage(
editorState: editorState, shrinkWrap: true,
// scrollController: widget.scrollController, autoFocus: false,
styleCustomizer: EditorStyleCustomizer( editorState: editorState,
context: context, styleCustomizer: EditorStyleCustomizer(
padding: const EdgeInsets.only(left: 16, right: 54), context: context,
padding: const EdgeInsets.only(left: 16, right: 54),
),
showParagraphPlaceholder: (editorState, node) =>
editorState.document.isEmpty,
placeholderText: (node) =>
LocaleKeys.cardDetails_notesPlaceholder.tr(),
), ),
showParagraphPlaceholder: (editorState, node) =>
editorState.document.isEmpty,
placeholderText: (node) =>
LocaleKeys.cardDetails_notesPlaceholder.tr(),
), ),
), ),
); );

View File

@ -1,10 +1,11 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:appflowy/core/config/kv_keys.dart'; import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/util/color_to_hex_string.dart'; import 'package:appflowy/util/color_to_hex_string.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:bloc/bloc.dart'; import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class DocumentAppearance { class DocumentAppearance {

View File

@ -1,22 +1,24 @@
library document_plugin; library document_plugin;
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/document_page.dart'; import 'package:appflowy/plugins/document/document_page.dart';
import 'package:appflowy/plugins/document/presentation/favorite/favorite_button.dart'; import 'package:appflowy/plugins/document/presentation/favorite/favorite_button.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/more/more_button.dart';
import 'package:appflowy/plugins/document/presentation/share/share_button.dart'; import 'package:appflowy/plugins/document/presentation/share/share_button.dart';
import 'package:appflowy/plugins/util.dart'; import 'package:appflowy/plugins/util.dart';
import 'package:appflowy/startup/plugin/plugin.dart'; import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart'; import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/more_view_actions.dart';
import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart'; import 'package:appflowy/workspace/presentation/widgets/tab_bar_item.dart';
import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart'; import 'package:appflowy/workspace/presentation/widgets/view_title_bar.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class DocumentPluginBuilder extends PluginBuilder { class DocumentPluginBuilder extends PluginBuilder {
@ -52,6 +54,7 @@ class DocumentPlugin extends Plugin<int> {
} }
late PluginType _pluginType; late PluginType _pluginType;
late final ViewInfoBloc _viewInfoBloc;
@override @override
final ViewPluginNotifier notifier; final ViewPluginNotifier notifier;
@ -60,6 +63,7 @@ class DocumentPlugin extends Plugin<int> {
@override @override
PluginWidgetBuilder get widgetBuilder => DocumentPluginWidgetBuilder( PluginWidgetBuilder get widgetBuilder => DocumentPluginWidgetBuilder(
bloc: _viewInfoBloc,
notifier: notifier, notifier: notifier,
initialSelection: initialSelection, initialSelection: initialSelection,
); );
@ -69,15 +73,29 @@ class DocumentPlugin extends Plugin<int> {
@override @override
PluginId get id => notifier.view.id; PluginId get id => notifier.view.id;
@override
void init() {
_viewInfoBloc = ViewInfoBloc(view: notifier.view)
..add(const ViewInfoEvent.started());
}
@override
void dispose() {
_viewInfoBloc.close();
notifier.dispose();
}
} }
class DocumentPluginWidgetBuilder extends PluginWidgetBuilder class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
with NavigationItem { with NavigationItem {
DocumentPluginWidgetBuilder({ DocumentPluginWidgetBuilder({
required this.bloc,
required this.notifier, required this.notifier,
this.initialSelection, this.initialSelection,
}); });
final ViewInfoBloc bloc;
final ViewPluginNotifier notifier; final ViewPluginNotifier notifier;
ViewPB get view => notifier.view; ViewPB get view => notifier.view;
int? deletedViewIndex; int? deletedViewIndex;
@ -95,12 +113,15 @@ class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
} }
}); });
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>( return BlocProvider<ViewInfoBloc>.value(
builder: (_, state) => DocumentPage( value: bloc,
key: ValueKey(view.id), child: BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
view: view, builder: (_, state) => DocumentPage(
onDeleted: () => context?.onDeleted(view, deletedViewIndex), key: ValueKey(view.id),
initialSelection: initialSelection, view: view,
onDeleted: () => context?.onDeleted(view, deletedViewIndex),
initialSelection: initialSelection,
),
), ),
); );
} }
@ -113,17 +134,20 @@ class DocumentPluginWidgetBuilder extends PluginWidgetBuilder
@override @override
Widget? get rightBarItem { Widget? get rightBarItem {
return Row( return BlocProvider<ViewInfoBloc>.value(
children: [ value: bloc,
DocumentShareButton(key: ValueKey(view.id), view: view), child: Row(
const HSpace(4), children: [
DocumentFavoriteButton( DocumentShareButton(key: ValueKey(view.id), view: view),
key: ValueKey('favorite_button_${view.id}'), const HSpace(4),
view: view, DocumentFavoriteButton(
), key: ValueKey('favorite_button_${view.id}'),
const HSpace(4), view: view,
const DocumentMoreButton(), ),
], const HSpace(4),
MoreViewActions(view: view),
],
),
); );
} }

View File

@ -1,3 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart'; import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_configuration.dart'; import 'package:appflowy/plugins/document/presentation/editor_configuration.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/align_toolbar_item/custom_text_align_command.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/align_toolbar_item/custom_text_align_command.dart';
@ -14,13 +17,12 @@ import 'package:appflowy/plugins/inline_actions/inline_actions_command.dart';
import 'package:appflowy/plugins/inline_actions/inline_actions_service.dart'; import 'package:appflowy/plugins/inline_actions/inline_actions_service.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart'; import 'package:appflowy/workspace/application/settings/shortcuts/settings_shortcuts_service.dart';
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/emoji_picker/emoji_picker.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
final List<CommandShortcutEvent> commandShortcutEvents = [ final List<CommandShortcutEvent> commandShortcutEvents = [
@ -176,6 +178,8 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
late final EditorScrollController editorScrollController; late final EditorScrollController editorScrollController;
late final ViewInfoBloc viewInfoBloc = context.read<ViewInfoBloc>();
Future<bool> showSlashMenu(editorState) async => customSlashCommand( Future<bool> showSlashMenu(editorState) async => customSlashCommand(
slashMenuItems, slashMenuItems,
shouldInsertSlash: false, shouldInsertSlash: false,
@ -186,6 +190,12 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
void initState() { void initState() {
super.initState(); super.initState();
viewInfoBloc.add(
ViewInfoEvent.registerEditorState(
editorState: widget.editorState,
),
);
_initEditorL10n(); _initEditorL10n();
_initializeShortcuts(); _initializeShortcuts();
indentableBlockTypes.add(ToggleListBlockKeys.type); indentableBlockTypes.add(ToggleListBlockKeys.type);
@ -224,6 +234,10 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
@override @override
void dispose() { void dispose() {
if (!viewInfoBloc.isClosed) {
viewInfoBloc.add(const ViewInfoEvent.unregisterEditorState());
}
SystemChannels.textInput.invokeMethod('TextInput.hide'); SystemChannels.textInput.invokeMethod('TextInput.hide');
if (widget.scrollController == null) { if (widget.scrollController == null) {

View File

@ -5,8 +5,8 @@ import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart'; import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
import 'package:appflowy/plugins/base/drag_handler.dart'; import 'package:appflowy/plugins/base/drag_handler.dart';
import 'package:appflowy/plugins/document/application/doc_bloc.dart'; import 'package:appflowy/plugins/document/application/doc_bloc.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/user/application/reminder/reminder_extension.dart'; import 'package:appflowy/user/application/reminder/reminder_extension.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';

View File

@ -1,12 +1,13 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart'; import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/util/google_font_family_extension.dart'; import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';

View File

@ -1,9 +1,12 @@
import 'dart:math'; import 'dart:math';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_block.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_item/utils.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_item/utils.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart'; import 'package:appflowy/plugins/inline_actions/inline_actions_menu.dart';
import 'package:appflowy/util/google_font_family_extension.dart'; import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy/workspace/application/appearance_defaults.dart'; import 'package:appflowy/workspace/application/appearance_defaults.dart';
@ -11,8 +14,6 @@ import 'package:appflowy/workspace/application/settings/appearance/appearance_cu
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log; import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';

View File

@ -1,85 +0,0 @@
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/more/font_size_slider.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class DocumentMoreButton extends StatelessWidget {
const DocumentMoreButton({super.key});
@override
Widget build(BuildContext context) {
return AppFlowyPopover(
constraints: BoxConstraints.loose(const Size(200, 400)),
offset: const Offset(0, 30),
popupBuilder: (_) {
final actions = [
AppFlowyPopover(
direction: PopoverDirection.leftWithCenterAligned,
constraints: const BoxConstraints(maxHeight: 40, maxWidth: 240),
offset: const Offset(-10, 0),
popupBuilder: (context) {
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
builder: (context, state) {
return FontSizeStepper(
minimumValue: 10,
maximumValue: 24,
value: state.fontSize,
divisions: 8,
onChanged: (newFontSize) {
context
.read<DocumentAppearanceCubit>()
.syncFontSize(newFontSize);
},
);
},
);
},
child: FlowyButton(
text: FlowyText.regular(
LocaleKeys.moreAction_fontSize.tr(),
color: AFThemeExtension.of(context).textColor,
),
leftIcon: Icon(
Icons.format_size_sharp,
color: Theme.of(context).iconTheme.color,
size: 18,
),
leftIconSize: const Size(18, 18),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
),
),
];
return ListView.separated(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: actions.length,
separatorBuilder: (_, __) => const VSpace(4),
physics: StyledScrollPhysics(),
itemBuilder: (_, index) => actions[index],
);
},
child: FlowyTooltip(
message: LocaleKeys.moreAction_moreOptions.tr(),
child: FlowyHover(
child: Padding(
padding: const EdgeInsets.all(6),
child: FlowySvg(
FlowySvgs.details_s,
size: const Size(18, 18),
color: Theme.of(context).iconTheme.color,
),
),
),
),
);
}
}

View File

@ -1,11 +1,12 @@
library flowy_plugin; library flowy_plugin;
import 'package:flutter/widgets.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/startup/plugin/plugin.dart'; import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart'; import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/widgets.dart';
export "./src/sandbox.dart"; export "./src/sandbox.dart";
@ -29,6 +30,8 @@ abstract class Plugin<T> {
PluginType get pluginType; PluginType get pluginType;
void init() {}
void dispose() { void dispose() {
notifier?.dispose(); notifier?.dispose();
} }

View File

@ -1,7 +1,10 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:appflowy/mobile/application/mobile_router.dart'; import 'package:appflowy/mobile/application/mobile_router.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/reminder/reminder_bloc.dart'; import 'package:appflowy/user/application/reminder/reminder_bloc.dart';
import 'package:appflowy/user/application/user_settings_service.dart'; import 'package:appflowy/user/application/user_settings_service.dart';
@ -18,8 +21,6 @@ import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.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:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';

View File

@ -0,0 +1,87 @@
import 'package:appflowy/util/int64_extension.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:bloc/bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'view_info_bloc.freezed.dart';
class ViewInfoBloc extends Bloc<ViewInfoEvent, ViewInfoState> {
ViewInfoBloc({required this.view}) : super(ViewInfoState.initial()) {
on<ViewInfoEvent>((event, emit) {
event.when(
started: () {
emit(state.copyWith(createdAt: view.createTime.toDateTime()));
},
unregisterEditorState: () {
_clearWordCountService();
emit(state.copyWith(documentCounters: null));
},
registerEditorState: (editorState) {
_clearWordCountService();
_wordCountService = WordCountService(editorState: editorState)
..addListener(_onWordCountChanged)
..register();
emit(
state.copyWith(
documentCounters: _wordCountService!.documentCounters,
),
);
},
wordCountChanged: () {
emit(
state.copyWith(
documentCounters: _wordCountService?.documentCounters,
),
);
},
);
});
}
final ViewPB view;
WordCountService? _wordCountService;
@override
Future<void> close() async {
_clearWordCountService();
await super.close();
}
void _onWordCountChanged() => add(const ViewInfoEvent.wordCountChanged());
void _clearWordCountService() {
_wordCountService
?..removeListener(_onWordCountChanged)
..dispose();
_wordCountService = null;
}
}
@freezed
class ViewInfoEvent with _$ViewInfoEvent {
const factory ViewInfoEvent.started() = _Started;
const factory ViewInfoEvent.unregisterEditorState() = _UnregisterEditorState;
const factory ViewInfoEvent.registerEditorState({
required EditorState editorState,
}) = _RegisterEditorState;
const factory ViewInfoEvent.wordCountChanged() = _WordCountChanged;
}
@freezed
class ViewInfoState with _$ViewInfoState {
const factory ViewInfoState({
required Counters? documentCounters,
required DateTime? createdAt,
}) = _ViewInfoState;
factory ViewInfoState.initial() => const ViewInfoState(
documentCounters: null,
createdAt: null,
);
}

View File

@ -1,3 +1,5 @@
import 'package:flutter/material.dart';
import 'package:appflowy/core/frameless_window.dart'; import 'package:appflowy/core/frameless_window.dart';
import 'package:appflowy/plugins/blank/blank.dart'; import 'package:appflowy/plugins/blank/blank.dart';
import 'package:appflowy/startup/plugin/plugin.dart'; import 'package:appflowy/startup/plugin/plugin.dart';
@ -10,7 +12,6 @@ import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy_backend/dispatch/dispatch.dart'; import 'package:appflowy_backend/dispatch/dispatch.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
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:time/time.dart'; import 'package:time/time.dart';
@ -175,6 +176,7 @@ class PageNotifier extends ChangeNotifier {
/// No need compare the old plugin with the new plugin. Just set it. /// No need compare the old plugin with the new plugin. Just set it.
set plugin(Plugin newPlugin) { set plugin(Plugin newPlugin) {
_plugin.dispose(); _plugin.dispose();
newPlugin.init();
/// Set the plugin view as the latest view. /// Set the plugin view as the latest view.
FolderEventSetLatestView(ViewIdPB(value: newPlugin.id)).send(); FolderEventSetLatestView(ViewIdPB(value: newPlugin.id)).send();

View File

@ -1,6 +1,6 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/user/application/auth/auth_service.dart'; import 'package:appflowy/user/application/auth/auth_service.dart';
import 'package:appflowy/workspace/application/menu/menu_user_bloc.dart'; import 'package:appflowy/workspace/application/menu/menu_user_bloc.dart';

View File

@ -1,11 +1,12 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'theme_setting_entry_template.dart'; import 'theme_setting_entry_template.dart';

View File

@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/appearance_defaults.dart'; import 'package:appflowy/workspace/application/appearance_defaults.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/document_color_setting_button.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/document_color_setting_button.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class DocumentCursorColorSetting extends StatelessWidget { class DocumentCursorColorSetting extends StatelessWidget {

View File

@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/appearance_defaults.dart'; import 'package:appflowy/workspace/application/appearance_defaults.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/document_color_setting_button.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/document_color_setting_button.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/theme_setting_entry_template.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class DocumentSelectionColorSetting extends StatelessWidget { class DocumentSelectionColorSetting extends StatelessWidget {

View File

@ -1,6 +1,8 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/util/google_font_family_extension.dart'; import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy/workspace/application/appearance_defaults.dart'; import 'package:appflowy/workspace/application/appearance_defaults.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
@ -8,7 +10,6 @@ import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';

View File

@ -0,0 +1,119 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/view_info/view_info_bloc.dart';
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/common_view_action.dart';
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/font_size_action.dart';
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/view_meta_info.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/date_time.pbenum.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class MoreViewActions extends StatefulWidget {
const MoreViewActions({
super.key,
required this.view,
this.isDocument = true,
});
/// The view to show the actions for.
final ViewPB view;
/// If false the view is a Database, otherwise it is a Document.
final bool isDocument;
@override
State<MoreViewActions> createState() => _MoreViewActionsState();
}
class _MoreViewActionsState extends State<MoreViewActions> {
late final List<Widget> viewActions;
late final UserDateFormatPB dateFormat;
final popoverMutex = PopoverMutex();
@override
void initState() {
super.initState();
dateFormat = context.read<AppearanceSettingsCubit>().state.dateFormat;
viewActions = ViewActionType.values
.map(
(type) => ViewAction(
type: type,
view: widget.view,
mutex: popoverMutex,
),
)
.toList();
}
@override
void dispose() {
popoverMutex.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocBuilder<ViewInfoBloc, ViewInfoState>(
builder: (context, state) {
return AppFlowyPopover(
mutex: popoverMutex,
constraints: BoxConstraints.loose(const Size(200, 400)),
offset: const Offset(0, 30),
popupBuilder: (_) {
final actions = [
if (widget.isDocument) ...[
const FontSizeAction(),
const Divider(height: 4),
],
...viewActions,
if (state.documentCounters != null ||
state.createdAt != null) ...[
const Divider(height: 4),
ViewMetaInfo(
dateFormat: dateFormat,
documentCounters: state.documentCounters,
createdAt: state.createdAt,
),
],
];
return ListView.separated(
shrinkWrap: true,
padding: EdgeInsets.zero,
itemCount: actions.length,
separatorBuilder: (_, __) => const VSpace(4),
physics: StyledScrollPhysics(),
itemBuilder: (_, index) => actions[index],
);
},
child: FlowyTooltip(
message: LocaleKeys.moreAction_moreOptions.tr(),
child: FlowyHover(
style: HoverStyle(
foregroundColorOnHover: Theme.of(context).colorScheme.onPrimary,
),
builder: (context, isHovering) => Padding(
padding: const EdgeInsets.all(6),
child: FlowySvg(
FlowySvgs.details_s,
size: const Size(18, 18),
color: isHovering
? Theme.of(context).colorScheme.onPrimary
: Theme.of(context).iconTheme.color,
),
),
),
),
);
},
);
}
}

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
enum ViewActionType {
delete,
duplicate;
String get label => switch (this) {
ViewActionType.delete => LocaleKeys.moreAction_deleteView.tr(),
ViewActionType.duplicate => LocaleKeys.moreAction_duplicateView.tr(),
};
FlowySvgData get icon => switch (this) {
ViewActionType.delete => FlowySvgs.delete_s,
ViewActionType.duplicate => FlowySvgs.m_duplicate_s,
};
ViewEvent get actionEvent => switch (this) {
ViewActionType.delete => const ViewEvent.delete(),
ViewActionType.duplicate => const ViewEvent.duplicate(),
};
}
class ViewAction extends StatelessWidget {
const ViewAction({
super.key,
required this.type,
required this.view,
this.mutex,
});
final ViewActionType type;
final ViewPB view;
final PopoverMutex? mutex;
@override
Widget build(BuildContext context) {
return FlowyButton(
onTap: () {
getIt<ViewBloc>(param1: view).add(type.actionEvent);
mutex?.close();
},
text: FlowyText.regular(
type.label,
color: AFThemeExtension.of(context).textColor,
),
leftIcon: FlowySvg(
type.icon,
color: Theme.of(context).iconTheme.color,
size: const Size.square(18),
),
leftIconSize: const Size(18, 18),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
);
}
}

View File

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/widgets/more_view_actions/widgets/font_size_stepper.dart';
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class FontSizeAction extends StatelessWidget {
const FontSizeAction({super.key});
@override
Widget build(BuildContext context) {
return AppFlowyPopover(
direction: PopoverDirection.leftWithCenterAligned,
constraints: const BoxConstraints(maxHeight: 40, maxWidth: 240),
offset: const Offset(-10, 0),
popupBuilder: (context) {
return BlocBuilder<DocumentAppearanceCubit, DocumentAppearance>(
builder: (_, state) => FontSizeStepper(
minimumValue: 10,
maximumValue: 24,
value: state.fontSize,
divisions: 8,
onChanged: (newFontSize) => context
.read<DocumentAppearanceCubit>()
.syncFontSize(newFontSize),
),
);
},
child: FlowyButton(
text: FlowyText.regular(
LocaleKeys.moreAction_fontSize.tr(),
color: AFThemeExtension.of(context).textColor,
),
leftIcon: Icon(
Icons.format_size_sharp,
color: Theme.of(context).iconTheme.color,
size: 18,
),
leftIconSize: const Size(18, 18),
hoverColor: AFThemeExtension.of(context).lightGreyHover,
),
);
}
}

View File

@ -1,6 +1,7 @@
import 'package:flutter/material.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:flutter/material.dart';
class FontSizeStepper extends StatefulWidget { class FontSizeStepper extends StatefulWidget {
const FontSizeStepper({ const FontSizeStepper({
@ -53,10 +54,7 @@ class _FontSizeStepperState extends State<FontSizeStepper> {
max: widget.maximumValue, max: widget.maximumValue,
divisions: widget.divisions, divisions: widget.divisions,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() => _value = value);
_value = value;
});
widget.onChanged(value); widget.onChanged(value);
}, },
), ),

View File

@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/workspace/application/settings/date_time/date_format_ext.dart';
import 'package:appflowy_backend/protobuf/flowy-user/date_time.pbenum.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
class ViewMetaInfo extends StatelessWidget {
const ViewMetaInfo({
super.key,
required this.dateFormat,
this.documentCounters,
this.createdAt,
});
final UserDateFormatPB dateFormat;
final Counters? documentCounters;
final DateTime? createdAt;
@override
Widget build(BuildContext context) {
// If more info is added to this Widget, use a separated ListView
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 6),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (documentCounters != null) ...[
FlowyText.regular(
LocaleKeys.moreAction_wordCount.tr(
args: [documentCounters!.wordCount.toString()],
),
color: Theme.of(context).hintColor,
),
const VSpace(2),
FlowyText.regular(
LocaleKeys.moreAction_charCount.tr(
args: [documentCounters!.charCount.toString()],
),
color: Theme.of(context).hintColor,
),
],
if (createdAt != null) ...[
if (documentCounters != null) const VSpace(2),
FlowyText.regular(
LocaleKeys.moreAction_createdAt.tr(
args: [dateFormat.formatDate(createdAt!, false)],
),
color: Theme.of(context).hintColor,
),
],
],
),
);
}
}

View File

@ -1,7 +1,8 @@
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
import 'package:appflowy/core/config/kv_keys.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';

View File

@ -1,7 +1,8 @@
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/editor_style.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';

View File

@ -1,9 +1,10 @@
import 'package:appflowy/plugins/document/presentation/more/cubit/document_appearance_cubit.dart'; import 'package:flutter/material.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart'; import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart'; import 'package:appflowy/workspace/application/settings/appearance/base_appearance.dart';
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart'; import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance/font_family_setting.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';

View File

@ -78,7 +78,12 @@
"large": "large", "large": "large",
"fontSize": "Font size", "fontSize": "Font size",
"import": "Import", "import": "Import",
"moreOptions": "More options" "moreOptions": "More options",
"wordCount": "Word count: {}",
"charCount": "Character count: {}",
"createdAt": "Created at: {}",
"deleteView": "Delete",
"duplicateView": "Duplicate"
}, },
"importPanel": { "importPanel": {
"textAndMarkdown": "Text & Markdown", "textAndMarkdown": "Text & Markdown",
@ -1285,4 +1290,4 @@
"userIcon": "User icon" "userIcon": "User icon"
}, },
"noLogFiles": "There're no log files" "noLogFiles": "There're no log files"
} }