mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: revamp mobile url editor (#4602)
* chore: revamp mobile url editor * chore: add i18n * chore: use shared url launcher * chore: translation bump * chore: add a toast notification after URL is copied to clipboard * chore: listen to onchanged * chore: improve text field editing experience * chore: disable quick action buttons if url cell data is empty * chore: apply suggestions from code review Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> * chore: provide the bloc and watch changes --------- Co-authored-by: Mathias Mogensen <42929161+Xazin@users.noreply.github.com>
This commit is contained in:
@ -115,7 +115,7 @@ class _CopyURLAccessoryState extends State<_CopyURLAccessory>
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.cellDataNotifier.value.isNotEmpty) {
|
||||
return FlowyTooltip(
|
||||
message: LocaleKeys.tooltip_urlCopyAccessory.tr(),
|
||||
message: LocaleKeys.grid_url_copy.tr(),
|
||||
preferBelow: false,
|
||||
child: _URLAccessoryIconContainer(
|
||||
child: FlowySvg(
|
||||
@ -161,7 +161,7 @@ class _VisitURLAccessoryState extends State<_VisitURLAccessory>
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.cellDataNotifier.value.isNotEmpty) {
|
||||
return FlowyTooltip(
|
||||
message: LocaleKeys.tooltip_urlLaunchAccessory.tr(),
|
||||
message: LocaleKeys.grid_url_launch.tr(),
|
||||
preferBelow: false,
|
||||
child: _URLAccessoryIconContainer(
|
||||
child: FlowySvg(
|
||||
|
@ -1,5 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/widgets/flowy_mobile_quick_action_button.dart';
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller_builder.dart';
|
||||
@ -8,8 +11,13 @@ import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.d
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/url_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||
import 'package:easy_localization/easy_localization.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:fluttertoast/fluttertoast.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../desktop_grid/desktop_grid_url_cell.dart';
|
||||
import '../desktop_row_detail/desktop_row_detail_url_cell.dart';
|
||||
@ -106,7 +114,8 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
|
||||
child: BlocListener<URLCellBloc, URLCellState>(
|
||||
listenWhen: (previous, current) => previous.content != current.content,
|
||||
listener: (context, state) {
|
||||
_textEditingController.text = state.content;
|
||||
_textEditingController.value =
|
||||
_textEditingController.value.copyWith(text: state.content);
|
||||
widget._cellDataNotifier.value = state.content;
|
||||
},
|
||||
child: widget.skin.build(
|
||||
@ -135,6 +144,74 @@ class _GridURLCellState extends GridEditableTextCell<EditableURLCell> {
|
||||
String? onCopy() => cellBloc.state.content;
|
||||
}
|
||||
|
||||
class MobileURLEditor extends StatelessWidget {
|
||||
const MobileURLEditor({
|
||||
super.key,
|
||||
required this.textEditingController,
|
||||
});
|
||||
|
||||
final TextEditingController textEditingController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
const VSpace(4.0),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
child: FlowyTextField(
|
||||
controller: textEditingController,
|
||||
hintStyle: Theme.of(context)
|
||||
.textTheme
|
||||
.bodyMedium
|
||||
?.copyWith(color: Theme.of(context).hintColor),
|
||||
hintText: LocaleKeys.grid_url_textFieldHint.tr(),
|
||||
textStyle: Theme.of(context).textTheme.bodyMedium,
|
||||
keyboardType: TextInputType.url,
|
||||
hintTextConstraints: const BoxConstraints(maxHeight: 52),
|
||||
onChanged: (_) {
|
||||
if (textEditingController.value.composing.isCollapsed) {
|
||||
context
|
||||
.read<URLCellBloc>()
|
||||
.add(URLCellEvent.updateURL(textEditingController.text));
|
||||
}
|
||||
},
|
||||
onSubmitted: (text) =>
|
||||
context.read<URLCellBloc>().add(URLCellEvent.updateURL(text)),
|
||||
),
|
||||
),
|
||||
const VSpace(8.0),
|
||||
MobileQuickActionButton(
|
||||
enable: context.watch<URLCellBloc>().state.content.isNotEmpty,
|
||||
onTap: () {
|
||||
openUrlCellLink(textEditingController.text);
|
||||
context.pop();
|
||||
},
|
||||
icon: FlowySvgs.url_s,
|
||||
text: LocaleKeys.grid_url_launch.tr(),
|
||||
),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
MobileQuickActionButton(
|
||||
enable: context.watch<URLCellBloc>().state.content.isNotEmpty,
|
||||
onTap: () {
|
||||
Clipboard.setData(
|
||||
ClipboardData(text: textEditingController.text),
|
||||
);
|
||||
Fluttertoast.showToast(
|
||||
msg: LocaleKeys.grid_url_copiedNotification.tr(),
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
context.pop();
|
||||
},
|
||||
icon: FlowySvgs.copy_s,
|
||||
text: LocaleKeys.grid_url_copy.tr(),
|
||||
),
|
||||
const Divider(height: 8.5, thickness: 0.5),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void openUrlCellLink(String content) {
|
||||
if (RegExp(regexUrl).hasMatch(content)) {
|
||||
const linkPrefix = [
|
||||
|
@ -1,14 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/url_cell_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../editable_cell_skeleton/url.dart';
|
||||
|
||||
@ -25,53 +20,9 @@ class MobileGridURLCellSkin extends IEditableURLCellSkin {
|
||||
return BlocSelector<URLCellBloc, URLCellState, String>(
|
||||
selector: (state) => state.content,
|
||||
builder: (context, content) {
|
||||
if (content.isEmpty) {
|
||||
return TextField(
|
||||
focusNode: focusNode,
|
||||
keyboardType: TextInputType.url,
|
||||
decoration: const InputDecoration(
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 14,
|
||||
vertical: 12,
|
||||
),
|
||||
isCollapsed: true,
|
||||
),
|
||||
onTapOutside: (event) =>
|
||||
FocusManager.instance.primaryFocus?.unfocus(),
|
||||
onSubmitted: (value) => bloc.add(URLCellEvent.updateURL(value)),
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (content.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final shouldAddScheme = !['http', 'https']
|
||||
.any((pattern) => content.startsWith(pattern));
|
||||
final url = shouldAddScheme ? 'http://$content' : content;
|
||||
afLaunchUrlString(url);
|
||||
},
|
||||
onLongPress: () => showMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.board_mobile_editURL.tr(),
|
||||
showHeader: true,
|
||||
showCloseButton: true,
|
||||
builder: (_) {
|
||||
final controller = TextEditingController(text: content);
|
||||
return TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
keyboardType: TextInputType.url,
|
||||
onEditingComplete: () {
|
||||
bloc.add(URLCellEvent.updateURL(controller.text));
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
onTap: () => _showURLEditor(context, bloc, textEditingController),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: Container(
|
||||
alignment: AlignmentDirectional.centerStart,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
@ -92,6 +43,23 @@ class MobileGridURLCellSkin extends IEditableURLCellSkin {
|
||||
);
|
||||
}
|
||||
|
||||
void _showURLEditor(
|
||||
BuildContext context,
|
||||
URLCellBloc bloc,
|
||||
TextEditingController textEditingController,
|
||||
) {
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
showDragHandle: true,
|
||||
builder: (context) => BlocProvider.value(
|
||||
value: bloc,
|
||||
child: MobileURLEditor(
|
||||
textEditingController: textEditingController,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<GridCellAccessoryBuilder<State<StatefulWidget>>> accessoryBuilder(
|
||||
GridCellAccessoryBuildContext context,
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:appflowy/core/helpers/url_launcher.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/mobile/presentation/bottom_sheet/show_mobile_bottom_sheet.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/bloc/url_cell_bloc.dart';
|
||||
@ -8,7 +7,6 @@ import 'package:appflowy/plugins/database/widgets/row/accessory/cell_accessory.d
|
||||
import 'package:appflowy/plugins/database/widgets/row/cells/cell_container.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import '../editable_cell_skeleton/url.dart';
|
||||
|
||||
@ -27,17 +25,18 @@ class MobileRowDetailURLCellSkin extends IEditableURLCellSkin {
|
||||
builder: (context, content) {
|
||||
return InkWell(
|
||||
borderRadius: const BorderRadius.all(Radius.circular(14)),
|
||||
onTap: () {
|
||||
if (content.isEmpty) {
|
||||
_showURLEditor(context, bloc, content);
|
||||
return;
|
||||
}
|
||||
final shouldAddScheme = !['http', 'https']
|
||||
.any((pattern) => content.startsWith(pattern));
|
||||
final url = shouldAddScheme ? 'http://$content' : content;
|
||||
afLaunchUrlString(url);
|
||||
},
|
||||
onLongPress: () => _showURLEditor(context, bloc, content),
|
||||
onTap: () => showMobileBottomSheet(
|
||||
context,
|
||||
showDragHandle: true,
|
||||
builder: (_) {
|
||||
return BlocProvider.value(
|
||||
value: bloc,
|
||||
child: MobileURLEditor(
|
||||
textEditingController: textEditingController,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(
|
||||
minHeight: 48,
|
||||
@ -77,25 +76,4 @@ class MobileRowDetailURLCellSkin extends IEditableURLCellSkin {
|
||||
URLCellDataNotifier cellDataNotifier,
|
||||
) =>
|
||||
const [];
|
||||
|
||||
void _showURLEditor(BuildContext context, URLCellBloc bloc, String content) {
|
||||
final controller = TextEditingController(text: content);
|
||||
showMobileBottomSheet(
|
||||
context,
|
||||
title: LocaleKeys.board_mobile_editURL.tr(),
|
||||
showHeader: true,
|
||||
showCloseButton: true,
|
||||
builder: (_) {
|
||||
return TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
keyboardType: TextInputType.url,
|
||||
onEditingComplete: () {
|
||||
bloc.add(URLCellEvent.updateURL(controller.text));
|
||||
context.pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
).then((_) => controller.dispose());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user