|
@@ -1,10 +1,13 @@
|
|
|
import 'dart:async';
|
|
|
+import 'package:app_flowy/generated/locale_keys.g.dart';
|
|
|
import 'package:app_flowy/workspace/application/grid/cell/url_cell_bloc.dart';
|
|
|
+import 'package:app_flowy/workspace/presentation/home/toast.dart';
|
|
|
+import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart';
|
|
|
+import 'package:easy_localization/easy_localization.dart';
|
|
|
import 'package:flowy_infra/image.dart';
|
|
|
import 'package:flowy_infra/theme.dart';
|
|
|
-import 'package:flowy_infra_ui/style_widget/icon_button.dart';
|
|
|
-import 'package:flutter/gestures.dart';
|
|
|
import 'package:flutter/material.dart';
|
|
|
+import 'package:flutter/services.dart';
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
import 'package:app_flowy/workspace/application/grid/prelude.dart';
|
|
|
import 'package:url_launcher/url_launcher.dart';
|
|
@@ -14,11 +17,19 @@ import 'cell_editor.dart';
|
|
|
class GridURLCellStyle extends GridCellStyle {
|
|
|
String? placeholder;
|
|
|
|
|
|
+ List<GridURLCellAccessoryType> accessoryTypes;
|
|
|
+
|
|
|
GridURLCellStyle({
|
|
|
this.placeholder,
|
|
|
+ this.accessoryTypes = const [],
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+enum GridURLCellAccessoryType {
|
|
|
+ edit,
|
|
|
+ copyURL,
|
|
|
+}
|
|
|
+
|
|
|
class GridURLCell extends StatefulWidget with GridCellWidget {
|
|
|
final GridCellContextBuilder cellContextBuilder;
|
|
|
late final GridURLCellStyle? cellStyle;
|
|
@@ -36,6 +47,35 @@ class GridURLCell extends StatefulWidget with GridCellWidget {
|
|
|
|
|
|
@override
|
|
|
State<GridURLCell> createState() => _GridURLCellState();
|
|
|
+
|
|
|
+ GridCellAccessory accessoryFromType(GridURLCellAccessoryType ty, GridCellAccessoryBuildContext buildContext) {
|
|
|
+ switch (ty) {
|
|
|
+ case GridURLCellAccessoryType.edit:
|
|
|
+ final cellContext = cellContextBuilder.build() as GridURLCellContext;
|
|
|
+ return _EditURLAccessory(cellContext: cellContext, anchorContext: buildContext.anchorContext);
|
|
|
+
|
|
|
+ case GridURLCellAccessoryType.copyURL:
|
|
|
+ final cellContext = cellContextBuilder.build() as GridURLCellContext;
|
|
|
+ return _CopyURLAccessory(cellContext: cellContext);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ List<GridCellAccessory> Function(GridCellAccessoryBuildContext buildContext) get accessoryBuilder => (buildContext) {
|
|
|
+ final List<GridCellAccessory> accessories = [];
|
|
|
+ if (cellStyle != null) {
|
|
|
+ accessories.addAll(cellStyle!.accessoryTypes.map((ty) {
|
|
|
+ return accessoryFromType(ty, buildContext);
|
|
|
+ }));
|
|
|
+ }
|
|
|
+
|
|
|
+ // If the accessories is empty then the default accessory will be GridURLCellAccessoryType.edit
|
|
|
+ if (accessories.isEmpty) {
|
|
|
+ accessories.add(accessoryFromType(GridURLCellAccessoryType.edit, buildContext));
|
|
|
+ }
|
|
|
+
|
|
|
+ return accessories;
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
class _GridURLCellState extends State<GridURLCell> {
|
|
@@ -46,7 +86,7 @@ class _GridURLCellState extends State<GridURLCell> {
|
|
|
final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
|
|
_cellBloc = URLCellBloc(cellContext: cellContext);
|
|
|
_cellBloc.add(const URLCellEvent.initial());
|
|
|
- _listenRequestFocus(context);
|
|
|
+ _handleRequestFocus();
|
|
|
super.initState();
|
|
|
}
|
|
|
|
|
@@ -66,17 +106,18 @@ class _GridURLCellState extends State<GridURLCell> {
|
|
|
fontSize: 14,
|
|
|
decoration: TextDecoration.underline,
|
|
|
),
|
|
|
- recognizer: _tapGesture(context),
|
|
|
),
|
|
|
);
|
|
|
|
|
|
- return CellEnterRegion(
|
|
|
+ return SizedBox.expand(
|
|
|
+ child: GestureDetector(
|
|
|
child: Align(alignment: Alignment.centerLeft, child: richText),
|
|
|
- expander: _EditCellIndicator(onTap: () {
|
|
|
- final cellContext = widget.cellContextBuilder.build() as GridURLCellContext;
|
|
|
- URLCellEditor.show(context, cellContext);
|
|
|
- }),
|
|
|
- );
|
|
|
+ onTap: () async {
|
|
|
+ widget.isFocus.value = true;
|
|
|
+ final url = context.read<URLCellBloc>().state.url;
|
|
|
+ await _openUrlOrEdit(url);
|
|
|
+ },
|
|
|
+ ));
|
|
|
},
|
|
|
),
|
|
|
);
|
|
@@ -84,18 +125,15 @@ class _GridURLCellState extends State<GridURLCell> {
|
|
|
|
|
|
@override
|
|
|
Future<void> dispose() async {
|
|
|
- widget.requestFocus.removeAllListener();
|
|
|
+ widget.requestBeginFocus.removeAllListener();
|
|
|
_cellBloc.close();
|
|
|
super.dispose();
|
|
|
}
|
|
|
|
|
|
- TapGestureRecognizer _tapGesture(BuildContext context) {
|
|
|
- final gesture = TapGestureRecognizer();
|
|
|
- gesture.onTap = () async {
|
|
|
- final url = context.read<URLCellBloc>().state.url;
|
|
|
- await _openUrlOrEdit(url);
|
|
|
- };
|
|
|
- return gesture;
|
|
|
+ @override
|
|
|
+ void didUpdateWidget(covariant GridURLCell oldWidget) {
|
|
|
+ _handleRequestFocus();
|
|
|
+ super.didUpdateWidget(oldWidget);
|
|
|
}
|
|
|
|
|
|
Future<void> _openUrlOrEdit(String url) async {
|
|
@@ -108,27 +146,48 @@ class _GridURLCellState extends State<GridURLCell> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- void _listenRequestFocus(BuildContext context) {
|
|
|
- widget.requestFocus.addListener(() {
|
|
|
+ void _handleRequestFocus() {
|
|
|
+ widget.requestBeginFocus.setListener(() {
|
|
|
_openUrlOrEdit(_cellBloc.state.url);
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-class _EditCellIndicator extends StatelessWidget {
|
|
|
- final VoidCallback onTap;
|
|
|
- const _EditCellIndicator({required this.onTap, Key? key}) : super(key: key);
|
|
|
+class _EditURLAccessory extends StatelessWidget with GridCellAccessory {
|
|
|
+ final GridURLCellContext cellContext;
|
|
|
+ final BuildContext anchorContext;
|
|
|
+ const _EditURLAccessory({
|
|
|
+ required this.cellContext,
|
|
|
+ required this.anchorContext,
|
|
|
+ Key? key,
|
|
|
+ }) : super(key: key);
|
|
|
+
|
|
|
+ @override
|
|
|
+ Widget build(BuildContext context) {
|
|
|
+ final theme = context.watch<AppTheme>();
|
|
|
+ return svgWidget("editor/edit", color: theme.iconColor);
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onTap() {
|
|
|
+ URLCellEditor.show(anchorContext, cellContext);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+class _CopyURLAccessory extends StatelessWidget with GridCellAccessory {
|
|
|
+ final GridURLCellContext cellContext;
|
|
|
+ const _CopyURLAccessory({required this.cellContext, Key? key}) : super(key: key);
|
|
|
|
|
|
@override
|
|
|
Widget build(BuildContext context) {
|
|
|
final theme = context.watch<AppTheme>();
|
|
|
- return FlowyIconButton(
|
|
|
- width: 26,
|
|
|
- onPressed: onTap,
|
|
|
- hoverColor: theme.hover,
|
|
|
- radius: BorderRadius.circular(4),
|
|
|
- iconPadding: const EdgeInsets.all(5),
|
|
|
- icon: svgWidget("editor/edit", color: theme.iconColor),
|
|
|
- );
|
|
|
+ return svgWidget("editor/copy", color: theme.iconColor);
|
|
|
+ }
|
|
|
+
|
|
|
+ @override
|
|
|
+ void onTap() {
|
|
|
+ final content = cellContext.getCellData(loadIfNoCache: false)?.content ?? "";
|
|
|
+ Clipboard.setData(ClipboardData(text: content));
|
|
|
+ showMessageToast(LocaleKeys.grid_row_copyProperty.tr());
|
|
|
}
|
|
|
}
|