From 02b7149514326502b385dc82b16fb79b9df5aa6b Mon Sep 17 00:00:00 2001 From: "Lucas.Xu" Date: Thu, 8 Jun 2023 15:46:33 +0800 Subject: [PATCH] fix: can't input url in Grid (#2737) * fix: launch url in url cell * fix: can't input url in Grid * feat: support selecting or deselecting all items in export page * fix: remove the circle shape * fix: light mode toolbar color * chore: update language and adjust the launch page --- .../assets/translations/en.json | 11 +- .../widgets/row/cells/url_cell/url_cell.dart | 121 +++++++++--------- .../document/presentation/editor_page.dart | 34 ++--- .../document/presentation/editor_style.dart | 2 +- .../presentation/folder/folder_widget.dart | 11 +- .../user/presentation/skip_log_in_screen.dart | 9 +- .../settings_file_exporter_cubit.dart | 18 ++- .../widgets/settings_export_file_widget.dart | 6 +- ...settings_file_customize_location_view.dart | 6 +- .../settings_file_exporter_widget.dart | 26 +++- .../lib/style_widget/color_picker.dart | 11 +- frontend/rust-lib/flowy-sqlite/src/kv/kv.rs | 2 +- 12 files changed, 149 insertions(+), 108 deletions(-) diff --git a/frontend/appflowy_flutter/assets/translations/en.json b/frontend/appflowy_flutter/assets/translations/en.json index 1ce3a2c762..f19b688d2e 100644 --- a/frontend/appflowy_flutter/assets/translations/en.json +++ b/frontend/appflowy_flutter/assets/translations/en.json @@ -196,6 +196,8 @@ "restartApp": "Please restart app for the changes to take effect.", "exportDatabase": "Export database", "selectFiles": "Select the files that need to be export", + "selectAll": "Select all", + "deselectAll": "Deselect all", "createNewFolder": "Create a new folder", "createNewFolderDesc": "Tell us where you want to store your data", "defineWhereYourDataIsStored": "Define where your data is stored", @@ -211,12 +213,13 @@ "folderPath": "Path to store your folder", "locationCannotBeEmpty": "Path cannot be empty", "pathCopiedSnackbar": "File storage path copied to clipboard!", - "changeLocationTooltips": "Change the files read the data directory", + "changeLocationTooltips": "Change the data directory", "change": "Change", - "openLocationTooltips": "Open the files read the data directory", - "recoverLocationTooltips": "Recover the files read the data directory", + "openLocationTooltips": "Open another data directory", + "recoverLocationTooltips": "Reset to AppFlowy's default data directory", "exportFileSuccess": "Export file successfully!", - "exportFileFail": "Export file failed!" + "exportFileFail": "Export file failed!", + "export": "Export" }, "user": { "name": "Name", diff --git a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart index 9222aae8f7..cd9e035e56 100644 --- a/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart +++ b/frontend/appflowy_flutter/lib/plugins/database_view/widgets/row/cells/url_cell/url_cell.dart @@ -10,7 +10,7 @@ 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:url_launcher/url_launcher.dart'; +import 'package:url_launcher/url_launcher_string.dart'; import '../../../../grid/presentation/layout/sizes.dart'; import '../../accessory/cell_accessory.dart'; import '../../cell_builder.dart'; @@ -36,13 +36,11 @@ enum GridURLCellAccessoryType { } class GridURLCell extends GridCellWidget { - final CellControllerBuilder cellControllerBuilder; - late final GridURLCellStyle? cellStyle; GridURLCell({ + super.key, required this.cellControllerBuilder, GridCellStyle? style, - Key? key, - }) : super(key: key) { + }) { if (style != null) { cellStyle = (style as GridURLCellStyle); } else { @@ -50,6 +48,9 @@ class GridURLCell extends GridCellWidget { } } + final CellControllerBuilder cellControllerBuilder; + late final GridURLCellStyle? cellStyle; + @override GridCellState createState() => _GridURLCellState(); @@ -104,28 +105,35 @@ class GridURLCell extends GridCellWidget { }; } -class _GridURLCellState extends GridCellState { +class _GridURLCellState extends GridFocusNodeCellState { final _popoverController = PopoverController(); - late URLCellBloc _cellBloc; - late TextEditingController _controller; - late FocusNode _focusNode; + late final URLCellBloc _cellBloc; + late final TextEditingController _controller; @override void initState() { + super.initState(); + final cellController = widget.cellControllerBuilder.build() as URLCellController; - _cellBloc = URLCellBloc(cellController: cellController); - _cellBloc.add(const URLCellEvent.initial()); + _cellBloc = URLCellBloc(cellController: cellController) + ..add(const URLCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); - _focusNode = FocusNode(); - super.initState(); + } + + @override + Future dispose() async { + _cellBloc.close(); + super.dispose(); } @override Widget build(BuildContext context) { return BlocProvider.value( value: _cellBloc, - child: BlocBuilder( + child: BlocConsumer( + listenWhen: (previous, current) => previous.content != current.content, + listener: (context, state) => _controller.text = state.content, builder: (context, state) { final urlEditor = Padding( padding: EdgeInsets.only( @@ -134,7 +142,7 @@ class _GridURLCellState extends GridCellState { ), child: TextField( controller: _controller, - focusNode: _focusNode, + focusNode: focusNode, maxLines: 1, style: (widget.cellStyle?.textStyle ?? Theme.of(context).textTheme.bodyMedium) @@ -143,8 +151,6 @@ class _GridURLCellState extends GridCellState { decoration: TextDecoration.underline, ), autofocus: false, - onEditingComplete: focusChanged, - onSubmitted: (value) => focusChanged(isUrlSubmitted: true), decoration: InputDecoration( contentPadding: EdgeInsets.only( top: GridSize.cellContentInsets.top, @@ -162,24 +168,10 @@ class _GridURLCellState extends GridCellState { ); } - void focusChanged({ - bool isUrlSubmitted = false, - }) { - if (mounted) { - if (_cellBloc.isClosed == false && - _controller.text != _cellBloc.state.content) { - _cellBloc.add(URLCellEvent.updateURL(_controller.text)); - } - if (isUrlSubmitted) { - _focusNode.unfocus(); - } - } - } - @override - Future dispose() async { - _cellBloc.close(); - super.dispose(); + Future focusChanged() async { + _cellBloc.add(URLCellEvent.updateURL(_controller.text)); + return super.focusChanged(); } @override @@ -192,19 +184,17 @@ class _GridURLCellState extends GridCellState { String? onCopy() => _cellBloc.state.content; @override - void onInsert(String value) { - _cellBloc.add(URLCellEvent.updateURL(value)); - } + void onInsert(String value) => _cellBloc.add(URLCellEvent.updateURL(value)); } class _EditURLAccessory extends StatefulWidget { - final CellControllerBuilder cellControllerBuilder; - final BuildContext anchorContext; const _EditURLAccessory({ required this.cellControllerBuilder, required this.anchorContext, - Key? key, - }) : super(key: key); + }); + + final CellControllerBuilder cellControllerBuilder; + final BuildContext anchorContext; @override State createState() => _EditURLAccessoryState(); @@ -212,20 +202,14 @@ class _EditURLAccessory extends StatefulWidget { class _EditURLAccessoryState extends State<_EditURLAccessory> with GridCellAccessoryState { - late PopoverController _popoverController; - - @override - void initState() { - _popoverController = PopoverController(); - super.initState(); - } + final popoverController = PopoverController(); @override Widget build(BuildContext context) { return AppFlowyPopover( margin: EdgeInsets.zero, constraints: BoxConstraints.loose(const Size(300, 160)), - controller: _popoverController, + controller: popoverController, direction: PopoverDirection.bottomWithLeftAligned, offset: const Offset(0, 8), child: svgWidget( @@ -236,7 +220,7 @@ class _EditURLAccessoryState extends State<_EditURLAccessory> return URLEditorPopover( cellController: widget.cellControllerBuilder.build() as URLCellController, - onExit: () => _popoverController.close(), + onExit: () => popoverController.close(), ); }, ); @@ -244,14 +228,17 @@ class _EditURLAccessoryState extends State<_EditURLAccessory> @override void onTap() { - _popoverController.show(); + popoverController.show(); } } class _CopyURLAccessory extends StatefulWidget { + const _CopyURLAccessory({ + super.key, + required this.cellContext, + }); + final URLCellController cellContext; - const _CopyURLAccessory({required this.cellContext, Key? key}) - : super(key: key); @override State createState() => _CopyURLAccessoryState(); @@ -270,16 +257,22 @@ class _CopyURLAccessoryState extends State<_CopyURLAccessory> @override void onTap() { final content = - widget.cellContext.getCellData(loadIfNotExist: false)?.content ?? ""; + widget.cellContext.getCellData(loadIfNotExist: false)?.content; + if (content == null) { + return; + } Clipboard.setData(ClipboardData(text: content)); showMessageToast(LocaleKeys.grid_row_copyProperty.tr()); } } class _VisitURLAccessory extends StatefulWidget { + const _VisitURLAccessory({ + super.key, + required this.cellContext, + }); + final URLCellController cellContext; - const _VisitURLAccessory({required this.cellContext, Key? key}) - : super(key: key); @override State createState() => _VisitURLAccessoryState(); @@ -297,14 +290,14 @@ class _VisitURLAccessoryState extends State<_VisitURLAccessory> @override void onTap() { - var content = - widget.cellContext.getCellData(loadIfNotExist: false)?.content ?? ""; - if (!content.startsWith('http://') && !content.startsWith('https://')) { - content = 'http://$content'; - } - final uri = Uri.parse(content); - if (content.isNotEmpty) { - canLaunchUrl(uri).then((value) => launchUrl(uri)); + final content = + widget.cellContext.getCellData(loadIfNotExist: false)?.content; + if (content == null) { + return; } + final shouldAddScheme = + !['http', 'https'].any((pattern) => content.startsWith(pattern)); + final url = shouldAddScheme ? 'http://$content' : content; + canLaunchUrlString(url).then((value) => launchUrlString(url)); } } diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart index 1b955f051e..c98683be31 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_page.dart @@ -57,24 +57,24 @@ class _AppFlowyEditorPageState extends State { late final Map blockComponentBuilders = _customAppFlowyBlockComponentBuilders(); - late final List characterShortcutEvents = [ - // code block - ...codeBlockCharacterEvents, + List get characterShortcutEvents => [ + // code block + ...codeBlockCharacterEvents, - // toggle list - // formatGreaterToToggleList, + // toggle list + // formatGreaterToToggleList, - // customize the slash menu command - customSlashCommand( - slashMenuItems, - style: styleCustomizer.selectionMenuStyleBuilder(), - ), + // customize the slash menu command + customSlashCommand( + slashMenuItems, + style: styleCustomizer.selectionMenuStyleBuilder(), + ), - ...standardCharacterShortcutEvents - ..removeWhere( - (element) => element == slashCommand, - ), // remove the default slash command. - ]; + ...standardCharacterShortcutEvents + ..removeWhere( + (element) => element == slashCommand, + ), // remove the default slash command. + ]; late final showSlashMenu = customSlashCommand( slashMenuItems, @@ -82,7 +82,9 @@ class _AppFlowyEditorPageState extends State { style: styleCustomizer.selectionMenuStyleBuilder(), ).handler; - late final styleCustomizer = EditorStyleCustomizer(context: context); + EditorStyleCustomizer get styleCustomizer => EditorStyleCustomizer( + context: context, + ); DocumentBloc get documentBloc => context.read(); @override diff --git a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart index bd653bbe13..32344dde28 100644 --- a/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart +++ b/frontend/appflowy_flutter/lib/plugins/document/presentation/editor_style.dart @@ -140,7 +140,7 @@ class EditorStyleCustomizer { FloatingToolbarStyle floatingToolbarStyleBuilder() { final theme = Theme.of(context); return FloatingToolbarStyle( - backgroundColor: theme.cardColor, + backgroundColor: theme.colorScheme.onTertiary, ); } } diff --git a/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart b/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart index 12b3e58193..097c0cf55b 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/folder/folder_widget.dart @@ -218,10 +218,13 @@ Widget _buildTextButton( String title, VoidCallback onPressed, ) { - return SecondaryTextButton( - title, - mode: SecondaryTextButtonMode.small, - onPressed: onPressed, + return SizedBox( + width: 60, + child: SecondaryTextButton( + title, + mode: SecondaryTextButtonMode.small, + onPressed: onPressed, + ), ); } diff --git a/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart b/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart index 50a1fd3441..9bf1892762 100644 --- a/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart +++ b/frontend/appflowy_flutter/lib/user/presentation/skip_log_in_screen.dart @@ -53,6 +53,7 @@ class _SkipLogInScreenState extends State { mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ + const Spacer(), FlowyLogoTitle( title: LocaleKeys.welcomeText.tr(), logoSize: const Size.square(40), @@ -68,8 +69,6 @@ class _SkipLogInScreenState extends State { }, ), const VSpace(32), - _buildSubscribeButtons(context), - const VSpace(32), SizedBox( width: MediaQuery.of(context).size.width * 0.5, child: FolderWidget( @@ -78,7 +77,10 @@ class _SkipLogInScreenState extends State { }, ), ), - const VSpace(64), + const Spacer(), + const VSpace(48), + _buildSubscribeButtons(context), + const VSpace(24), ], ); } @@ -182,6 +184,7 @@ class GoButton extends StatelessWidget { maxWidth: 340, maxHeight: 48, ), + radius: BorderRadius.circular(12), mainAxisAlignment: MainAxisAlignment.center, fontSize: FontSizes.s14, fontFamily: GoogleFonts.poppins(fontWeight: FontWeight.w500).fontFamily, diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/settings_file_exporter_cubit.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/settings_file_exporter_cubit.dart index a7937762dc..430a95dd77 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/settings_file_exporter_cubit.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/settings_file_exporter_cubit.dart @@ -55,6 +55,18 @@ class SettingsFileExporterCubit extends Cubit { required List views, }) : super(SettingsFileExportState(views: views)); + void selectOrDeselectAllItems() { + final List> selectedItems = state.selectedItems; + final isSelectAll = + selectedItems.expand((element) => element).every((element) => element); + for (var i = 0; i < selectedItems.length; i++) { + for (var j = 0; j < selectedItems[i].length; j++) { + selectedItems[i][j] = !isSelectAll; + } + } + emit(state.copyWith(selectedItems: selectedItems)); + } + void selectOrDeselectItem(int outerIndex, int innerIndex) { final selectedItems = state.selectedItems; selectedItems[outerIndex][innerIndex] = @@ -69,7 +81,7 @@ class SettingsFileExporterCubit extends Cubit { } Map> fetchSelectedPages() { - final apps = state.views; + final views = state.views; final selectedItems = state.selectedItems; final Map> result = {}; for (var i = 0; i < selectedItems.length; i++) { @@ -77,11 +89,11 @@ class SettingsFileExporterCubit extends Cubit { final ids = []; for (var j = 0; j < selectedItem.length; j++) { if (selectedItem[j]) { - ids.add(apps[i].childViews[j].id); + ids.add(views[i].childViews[j].id); } } if (ids.isNotEmpty) { - result[apps[i].id] = ids; + result[views[i].id] = ids; } } return result; diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_export_file_widget.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_export_file_widget.dart index 9421292b98..b381305607 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_export_file_widget.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_export_file_widget.dart @@ -3,6 +3,7 @@ import 'package:flowy_infra/image.dart'; import 'package:flutter/material.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; +import 'package:styled_widget/styled_widget.dart'; import '../../../../generated/locale_keys.g.dart'; @@ -25,8 +26,9 @@ class SettingsExportFileWidgetState extends State { children: [ FlowyText.medium( LocaleKeys.settings_files_exportData.tr(), + fontSize: 13, overflow: TextOverflow.ellipsis, - ), + ).padding(horizontal: 5.0), const Spacer(), _OpenExportedDirectoryButton( onTap: () async { @@ -62,7 +64,7 @@ class _OpenExportedDirectoryButton extends StatelessWidget { Widget build(BuildContext context) { return FlowyIconButton( hoverColor: Theme.of(context).colorScheme.secondaryContainer, - tooltipText: LocaleKeys.settings_files_open.tr(), + tooltipText: LocaleKeys.settings_files_export.tr(), icon: svgWidget( 'common/open_folder', color: Theme.of(context).iconTheme.color, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart index 8a7e9af1f7..f0ba9122f4 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_customize_location_view.dart @@ -129,12 +129,14 @@ class _CopyableText extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - if (onHover) + if (onHover) ...[ + const HSpace(5), FlowyText.regular( LocaleKeys.settings_files_copy.tr(), fontSize: 12, color: Theme.of(context).colorScheme.primary, - ) + ), + ], ], ), ), diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart index 4266ffe037..6c2ac4fcf5 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/settings_file_exporter_widget.dart @@ -49,9 +49,29 @@ class _FileExporterWidgetState extends State { crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ - FlowyText.medium( - LocaleKeys.settings_files_selectFiles.tr(), - fontSize: 16.0, + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + FlowyText.medium( + LocaleKeys.settings_files_selectFiles.tr(), + fontSize: 16.0, + ), + BlocBuilder( + builder: (context, state) => FlowyTextButton( + state.selectedItems + .expand((element) => element) + .every((element) => element) + ? LocaleKeys.settings_files_deselectAll.tr() + : LocaleKeys.settings_files_selectAll.tr(), + onPressed: () { + context + .read() + .selectOrDeselectAllItems(); + }, + ), + ) + ], ), const VSpace(8), const Expanded(child: _ExpandedList()), diff --git a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart index fb5bfe1610..bc372e24d8 100644 --- a/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart +++ b/frontend/appflowy_flutter/packages/flowy_infra_ui/lib/style_widget/color_picker.dart @@ -60,11 +60,12 @@ class FlowyColorPicker extends StatelessWidget { final colorIcon = SizedBox.square( dimension: iconSize, child: Container( - decoration: BoxDecoration( - color: option.color, - shape: BoxShape.circle, - border: border, - )), + decoration: BoxDecoration( + color: option.color, + shape: BoxShape.circle, + // border: border, + ), + ), ); return SizedBox( diff --git a/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs b/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs index 3209a64e37..1884c3a68b 100644 --- a/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs +++ b/frontend/rust-lib/flowy-sqlite/src/kv/kv.rs @@ -11,7 +11,7 @@ use serde::Serialize; use crate::kv::schema::{kv_table, kv_table::dsl, KV_SQL}; use crate::sqlite::{DBConnection, Database, PoolConfig}; -const DB_NAME: &str = "kv.db"; +const DB_NAME: &str = "cache.db"; lazy_static! { static ref KV_HOLDER: RwLock = RwLock::new(KV::new()); }