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
This commit is contained in:
Lucas.Xu 2023-06-08 15:46:33 +08:00 committed by GitHub
parent 37547a6485
commit 02b7149514
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 149 additions and 108 deletions

View File

@ -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",

View File

@ -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<GridURLCell> createState() => _GridURLCellState();
@ -104,28 +105,35 @@ class GridURLCell extends GridCellWidget {
};
}
class _GridURLCellState extends GridCellState<GridURLCell> {
class _GridURLCellState extends GridFocusNodeCellState<GridURLCell> {
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<void> dispose() async {
_cellBloc.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return BlocProvider.value(
value: _cellBloc,
child: BlocBuilder<URLCellBloc, URLCellState>(
child: BlocConsumer<URLCellBloc, URLCellState>(
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<GridURLCell> {
),
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<GridURLCell> {
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<GridURLCell> {
);
}
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<void> dispose() async {
_cellBloc.close();
super.dispose();
Future<void> focusChanged() async {
_cellBloc.add(URLCellEvent.updateURL(_controller.text));
return super.focusChanged();
}
@override
@ -192,19 +184,17 @@ class _GridURLCellState extends GridCellState<GridURLCell> {
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<StatefulWidget> 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<StatefulWidget> 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<StatefulWidget> 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));
}
}

View File

@ -57,24 +57,24 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
late final Map<String, BlockComponentBuilder> blockComponentBuilders =
_customAppFlowyBlockComponentBuilders();
late final List<CharacterShortcutEvent> characterShortcutEvents = [
// code block
...codeBlockCharacterEvents,
List<CharacterShortcutEvent> 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<AppFlowyEditorPage> {
style: styleCustomizer.selectionMenuStyleBuilder(),
).handler;
late final styleCustomizer = EditorStyleCustomizer(context: context);
EditorStyleCustomizer get styleCustomizer => EditorStyleCustomizer(
context: context,
);
DocumentBloc get documentBloc => context.read<DocumentBloc>();
@override

View File

@ -140,7 +140,7 @@ class EditorStyleCustomizer {
FloatingToolbarStyle floatingToolbarStyleBuilder() {
final theme = Theme.of(context);
return FloatingToolbarStyle(
backgroundColor: theme.cardColor,
backgroundColor: theme.colorScheme.onTertiary,
);
}
}

View File

@ -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,
),
);
}

View File

@ -53,6 +53,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
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<SkipLogInScreen> {
},
),
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<SkipLogInScreen> {
},
),
),
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,

View File

@ -55,6 +55,18 @@ class SettingsFileExporterCubit extends Cubit<SettingsFileExportState> {
required List<ViewPB> views,
}) : super(SettingsFileExportState(views: views));
void selectOrDeselectAllItems() {
final List<List<bool>> 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<SettingsFileExportState> {
}
Map<String, List<String>> fetchSelectedPages() {
final apps = state.views;
final views = state.views;
final selectedItems = state.selectedItems;
final Map<String, List<String>> result = {};
for (var i = 0; i < selectedItems.length; i++) {
@ -77,11 +89,11 @@ class SettingsFileExporterCubit extends Cubit<SettingsFileExportState> {
final ids = <String>[];
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;

View File

@ -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<SettingsExportFileWidget> {
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,

View File

@ -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,
)
),
],
],
),
),

View File

@ -49,9 +49,29 @@ class _FileExporterWidgetState extends State<FileExporterWidget> {
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<SettingsFileExporterCubit,
SettingsFileExportState>(
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<SettingsFileExporterCubit>()
.selectOrDeselectAllItems();
},
),
)
],
),
const VSpace(8),
const Expanded(child: _ExpandedList()),

View File

@ -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(

View File

@ -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<KV> = RwLock::new(KV::new());
}