diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json index 4e6c8f3420..e465e79607 100644 --- a/frontend/app_flowy/assets/translations/en.json +++ b/frontend/app_flowy/assets/translations/en.json @@ -180,7 +180,8 @@ "row": { "duplicate": "Duplicate", "delete": "Delete", - "textPlaceholder": "Empty" + "textPlaceholder": "Empty", + "copyProperty": "Copied property to clipboard" }, "selectOption": { "create": "Create", diff --git a/frontend/app_flowy/lib/startup/deps_resolver.dart b/frontend/app_flowy/lib/startup/deps_resolver.dart index 9be9420fb5..b378d6ee7b 100644 --- a/frontend/app_flowy/lib/startup/deps_resolver.dart +++ b/frontend/app_flowy/lib/startup/deps_resolver.dart @@ -16,6 +16,7 @@ import 'package:app_flowy/workspace/presentation/home/menu/menu.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder-data-model/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-user-data-model/user_profile.pb.dart'; +import 'package:fluttertoast/fluttertoast.dart'; import 'package:get_it/get_it.dart'; class DependencyResolver { @@ -46,6 +47,8 @@ void _resolveUserDeps(GetIt getIt) { } void _resolveHomeDeps(GetIt getIt) { + getIt.registerSingleton(FToast()); + getIt.registerSingleton(MenuSharedState()); getIt.registerFactoryParam( diff --git a/frontend/app_flowy/lib/startup/tasks/app_widget.dart b/frontend/app_flowy/lib/startup/tasks/app_widget.dart index 1747cfd8ec..9961142c6d 100644 --- a/frontend/app_flowy/lib/startup/tasks/app_widget.dart +++ b/frontend/app_flowy/lib/startup/tasks/app_widget.dart @@ -67,40 +67,42 @@ class ApplicationWidget extends StatelessWidget { }) : super(key: key); @override - Widget build(BuildContext context) => ChangeNotifierProvider.value( - value: settingModel, - builder: (context, _) { - const ratio = 1.73; - const minWidth = 600.0; - setWindowMinSize(const Size(minWidth, minWidth / ratio)); - settingModel.readLocaleWhenAppLaunch(context); - AppTheme theme = context.select( - (value) => value.theme, - ); - Locale locale = context.select( - (value) => value.locale, - ); + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: settingModel, + builder: (context, _) { + const ratio = 1.73; + const minWidth = 600.0; + setWindowMinSize(const Size(minWidth, minWidth / ratio)); + settingModel.readLocaleWhenAppLaunch(context); + AppTheme theme = context.select( + (value) => value.theme, + ); + Locale locale = context.select( + (value) => value.locale, + ); - return MultiProvider( - providers: [ - Provider.value(value: theme), - Provider.value(value: locale), - ], - builder: (context, _) { - return MaterialApp( - builder: overlayManagerBuilder(), - debugShowCheckedModeBanner: false, - theme: theme.themeData, - localizationsDelegates: context.localizationDelegates, - supportedLocales: context.supportedLocales, - locale: locale, - navigatorKey: AppGlobals.rootNavKey, - home: child, - ); - }, - ); - }, - ); + return MultiProvider( + providers: [ + Provider.value(value: theme), + Provider.value(value: locale), + ], + builder: (context, _) { + return MaterialApp( + builder: overlayManagerBuilder(), + debugShowCheckedModeBanner: false, + theme: theme.themeData, + localizationsDelegates: context.localizationDelegates, + supportedLocales: context.supportedLocales, + locale: locale, + navigatorKey: AppGlobals.rootNavKey, + home: child, + ); + }, + ); + }, + ); + } } class AppGlobals { diff --git a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart index e4141c3e16..6c0fb560e8 100644 --- a/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart +++ b/frontend/app_flowy/lib/workspace/application/grid/cell/cell_service/context_builder.dart @@ -105,7 +105,7 @@ class _GridCellContext extends Equatable { final FieldService _fieldService; late final CellListener _cellListener; - late final ValueNotifier _cellDataNotifier; + late final ValueNotifier? _cellDataNotifier; bool isListening = false; VoidCallback? _onFieldChangedFn; Timer? _loadDataOperation; @@ -163,19 +163,19 @@ class _GridCellContext extends Equatable { } onCellChangedFn() { - onCellChanged(_cellDataNotifier.value); + onCellChanged(_cellDataNotifier?.value); if (cellDataLoader.config.reloadOnCellChanged) { _loadData(); } } - _cellDataNotifier.addListener(onCellChangedFn); + _cellDataNotifier?.addListener(onCellChangedFn); return onCellChangedFn; } void removeListener(VoidCallback fn) { - _cellDataNotifier.removeListener(fn); + _cellDataNotifier?.removeListener(fn); } T? getCellData({bool loadIfNoCache = true}) { @@ -211,7 +211,7 @@ class _GridCellContext extends Equatable { _loadDataOperation?.cancel(); _loadDataOperation = Timer(const Duration(milliseconds: 10), () { cellDataLoader.loadData().then((data) { - _cellDataNotifier.value = data; + _cellDataNotifier?.value = data; cellCache.insert(GridCellCacheData(key: _cacheKey, object: data)); }); }); diff --git a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart index c16c965a82..c9c210ab35 100644 --- a/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart +++ b/frontend/app_flowy/lib/workspace/presentation/home/home_stack.dart @@ -2,15 +2,13 @@ import 'dart:io' show Platform; import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/workspace/application/home/home_bloc.dart'; -import 'package:app_flowy/workspace/presentation/home/home_screen.dart'; +import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_sdk/log.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:provider/provider.dart'; import 'package:time/time.dart'; -import 'package:fluttertoast/fluttertoast.dart'; - import 'package:app_flowy/plugin/plugin.dart'; import 'package:app_flowy/workspace/presentation/plugins/blank/blank.dart'; import 'package:app_flowy/workspace/presentation/home/home_sizes.dart'; @@ -22,8 +20,6 @@ import 'package:flowy_infra/notifier.dart'; typedef NavigationCallback = void Function(String id); -late FToast fToast; - class HomeStack extends StatelessWidget { static GlobalKey scaffoldKey = GlobalKey(); // final Size size; @@ -74,8 +70,7 @@ class _FadingIndexedStackState extends State { @override void initState() { super.initState(); - fToast = FToast(); - fToast.init(HomeScreen.scaffoldKey.currentState!.context); + initToastWithContext(context); } @override diff --git a/frontend/app_flowy/lib/workspace/presentation/home/toast.dart b/frontend/app_flowy/lib/workspace/presentation/home/toast.dart new file mode 100644 index 0000000000..28241c0ec4 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/home/toast.dart @@ -0,0 +1,37 @@ +import 'package:app_flowy/startup/startup.dart'; +import 'package:flowy_infra_ui/style_widget/text.dart'; +import 'package:flutter/material.dart'; +import 'package:fluttertoast/fluttertoast.dart'; + +class FlowyMessageToast extends StatelessWidget { + final String message; + const FlowyMessageToast({required this.message, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Container( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), + child: FlowyText.medium(message, color: Colors.white), + ), + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(4)), + color: Colors.black, + ), + ); + } +} + +void initToastWithContext(BuildContext context) { + getIt().init(context); +} + +void showMessageToast(String message) { + final child = FlowyMessageToast(message: message); + + getIt().showToast( + child: child, + gravity: ToastGravity.BOTTOM, + toastDuration: const Duration(seconds: 3), + ); +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart new file mode 100644 index 0000000000..41e22b8b98 --- /dev/null +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart @@ -0,0 +1,171 @@ +import 'package:flowy_infra_ui/style_widget/hover.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flowy_infra/theme.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:flowy_infra/size.dart'; +import 'package:styled_widget/styled_widget.dart'; + +class GridCellAccessoryBuildContext { + final BuildContext anchorContext; + + GridCellAccessoryBuildContext({required this.anchorContext}); +} + +abstract class GridCellAccessory implements Widget { + void onTap(); +} + +typedef AccessoryBuilder = List Function(GridCellAccessoryBuildContext buildContext); + +abstract class AccessoryWidget extends Widget { + const AccessoryWidget({Key? key}) : super(key: key); + + // The hover will show if the onFocus's value is true + ValueNotifier? get isFocus; + + AccessoryBuilder? get accessoryBuilder; +} + +class AccessoryHover extends StatefulWidget { + final AccessoryWidget child; + final EdgeInsets contentPadding; + const AccessoryHover({ + required this.child, + this.contentPadding = EdgeInsets.zero, + Key? key, + }) : super(key: key); + + @override + State createState() => _AccessoryHoverState(); +} + +class _AccessoryHoverState extends State { + late AccessoryHoverState _hoverState; + VoidCallback? _listenerFn; + + @override + void initState() { + _hoverState = AccessoryHoverState(); + _listenerFn = () => _hoverState.isFocus = widget.child.isFocus?.value ?? false; + widget.child.isFocus?.addListener(_listenerFn!); + + super.initState(); + } + + @override + void dispose() { + _hoverState.dispose(); + + if (_listenerFn != null) { + widget.child.isFocus?.removeListener(_listenerFn!); + _listenerFn = null; + } + super.dispose(); + } + + @override + Widget build(BuildContext context) { + List children = [ + const _Background(), + Padding(padding: widget.contentPadding, child: widget.child), + ]; + + final accessoryBuilder = widget.child.accessoryBuilder; + if (accessoryBuilder != null) { + final accessories = accessoryBuilder((GridCellAccessoryBuildContext(anchorContext: context))); + children.add( + Padding( + padding: const EdgeInsets.only(right: 6), + child: AccessoryContainer(accessories: accessories), + ).positioned(right: 0), + ); + } + + return ChangeNotifierProvider.value( + value: _hoverState, + child: MouseRegion( + cursor: SystemMouseCursors.click, + opaque: false, + onEnter: (p) => setState(() => _hoverState.onHover = true), + onExit: (p) => setState(() => _hoverState.onHover = false), + child: Stack( + fit: StackFit.loose, + alignment: AlignmentDirectional.center, + children: children, + ), + ), + ); + } +} + +class AccessoryHoverState extends ChangeNotifier { + bool _onHover = false; + bool _isFocus = false; + + set onHover(bool value) { + if (_onHover != value) { + _onHover = value; + notifyListeners(); + } + } + + bool get onHover => _onHover; + + set isFocus(bool value) { + if (_isFocus != value) { + _isFocus = value; + notifyListeners(); + } + } + + bool get isFocus => _isFocus; +} + +class _Background extends StatelessWidget { + const _Background({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + return Consumer( + builder: (context, state, child) { + if (state.onHover || state.isFocus) { + return FlowyHoverContainer( + style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6), + ); + } else { + return const SizedBox(); + } + }, + ); + } +} + +class AccessoryContainer extends StatelessWidget { + final List accessories; + const AccessoryContainer({required this.accessories, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final theme = context.watch(); + final children = accessories.map((accessory) { + final hover = FlowyHover( + style: HoverStyle(hoverColor: theme.bg3, backgroundColor: theme.surface), + builder: (_, onHover) => Container( + width: 26, + height: 26, + padding: const EdgeInsets.all(3), + child: accessory, + ), + ); + return GestureDetector( + child: hover, + behavior: HitTestBehavior.opaque, + onTap: () => accessory.onTap(), + ); + }).toList(); + + return Wrap(children: children, spacing: 6); + } +} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart index f8189e7f02..65bff03ecf 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/cell_builder.dart @@ -1,5 +1,4 @@ import 'package:app_flowy/workspace/application/grid/cell/cell_service/cell_service.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show FieldType; import 'package:flutter/widgets.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart'; @@ -8,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'cell_accessory.dart'; import 'checkbox_cell.dart'; import 'date_cell/date_cell.dart'; import 'number_cell.dart'; @@ -48,24 +48,27 @@ class BlankCell extends StatelessWidget { } } -abstract class GridCellWidget implements FlowyHoverWidget { +abstract class GridCellWidget implements AccessoryWidget, CellContainerFocustable { @override - final ValueNotifier onFocus = ValueNotifier(false); + final ValueNotifier isFocus = ValueNotifier(false); - final GridCellRequestFocusNotifier requestFocus = GridCellRequestFocusNotifier(); + @override + List Function(GridCellAccessoryBuildContext buildContext)? get accessoryBuilder => null; + + @override + final GridCellRequestBeginFocus requestBeginFocus = GridCellRequestBeginFocus(); } -class GridCellRequestFocusNotifier extends ChangeNotifier { +class GridCellRequestBeginFocus extends ChangeNotifier { VoidCallback? _listener; - @override - void addListener(VoidCallback listener) { + void setListener(VoidCallback listener) { if (_listener != null) { removeListener(_listener!); } _listener = listener; - super.addListener(listener); + addListener(listener); } void removeAllListener() { @@ -81,10 +84,10 @@ class GridCellRequestFocusNotifier extends ChangeNotifier { abstract class GridCellStyle {} -class CellSingleFocusNode extends FocusNode { +class SingleListenrFocusNode extends FocusNode { VoidCallback? _listener; - void setSingleListener(VoidCallback listener) { + void setListener(VoidCallback listener) { if (_listener != null) { removeListener(_listener!); } @@ -93,7 +96,7 @@ class CellSingleFocusNode extends FocusNode { super.addListener(listener); } - void removeSingleListener() { + void removeAllListener() { if (_listener != null) { removeListener(_listener!); } @@ -123,9 +126,14 @@ class CellStateNotifier extends ChangeNotifier { bool get onEnter => _onEnter; } +abstract class CellContainerFocustable { + // Listen on the requestBeginFocus if the + GridCellRequestBeginFocus get requestBeginFocus; +} + class CellContainer extends StatelessWidget { final GridCellWidget child; - final Widget? expander; + final AccessoryBuilder? accessoryBuilder; final double width; final RegionStateNotifier rowStateNotifier; const CellContainer({ @@ -133,7 +141,7 @@ class CellContainer extends StatelessWidget { required this.child, required this.width, required this.rowStateNotifier, - this.expander, + this.accessoryBuilder, }) : super(key: key); @override @@ -145,17 +153,21 @@ class CellContainer extends StatelessWidget { selector: (context, notifier) => notifier.isFocus, builder: (context, isFocus, _) { Widget container = Center(child: child); - child.onFocus.addListener(() { - Provider.of(context, listen: false).isFocus = child.onFocus.value; + child.isFocus.addListener(() { + Provider.of(context, listen: false).isFocus = child.isFocus.value; }); - if (expander != null) { - container = CellEnterRegion(child: container, expander: expander!); + if (accessoryBuilder != null) { + final buildContext = GridCellAccessoryBuildContext(anchorContext: context); + final accessories = accessoryBuilder!(buildContext); + if (accessories.isNotEmpty) { + container = CellEnterRegion(child: container, accessories: accessories); + } } return GestureDetector( behavior: HitTestBehavior.translucent, - onTap: () => child.requestFocus.notify(), + onTap: () => child.requestBeginFocus.notify(), child: Container( constraints: BoxConstraints(maxWidth: width, minHeight: 46), decoration: _makeBoxDecoration(context, isFocus), @@ -182,8 +194,8 @@ class CellContainer extends StatelessWidget { class CellEnterRegion extends StatelessWidget { final Widget child; - final Widget expander; - const CellEnterRegion({required this.child, required this.expander, Key? key}) : super(key: key); + final List accessories; + const CellEnterRegion({required this.child, required this.accessories, Key? key}) : super(key: key); @override Widget build(BuildContext context) { @@ -192,7 +204,7 @@ class CellEnterRegion extends StatelessWidget { builder: (context, onEnter, _) { List children = [child]; if (onEnter) { - children.add(expander.positioned(right: 0)); + children.add(AccessoryContainer(accessories: accessories).positioned(right: 0)); } return MouseRegion( @@ -202,7 +214,6 @@ class CellEnterRegion extends StatelessWidget { child: Stack( alignment: AlignmentDirectional.center, fit: StackFit.expand, - // alignment: AlignmentDirectional.centerEnd, children: children, ), ); diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart index b2493d55ed..78910348b3 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/checkbox_cell.dart @@ -24,7 +24,7 @@ class _CheckboxCellState extends State { void initState() { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const CheckboxCellEvent.initial()); - _listenCellRequestFocus(); + _handleRequestFocus(); super.initState(); } @@ -51,19 +51,19 @@ class _CheckboxCellState extends State { @override void didUpdateWidget(covariant CheckboxCell oldWidget) { - _listenCellRequestFocus(); + _handleRequestFocus(); super.didUpdateWidget(oldWidget); } @override Future dispose() async { - widget.requestFocus.removeAllListener(); + widget.requestBeginFocus.removeAllListener(); _cellBloc.close(); super.dispose(); } - void _listenCellRequestFocus() { - widget.requestFocus.addListener(() { + void _handleRequestFocus() { + widget.requestBeginFocus.setListener(() { _cellBloc.add(const CheckboxCellEvent.select()); }); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart index 417c9f270e..c644ac61a5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell/date_cell.dart @@ -76,8 +76,8 @@ class _DateCellState extends State { void _showCalendar(BuildContext context) { final bloc = context.read(); - widget.onFocus.value = true; - final calendar = DateCellEditor(onDismissed: () => widget.onFocus.value = false); + widget.isFocus.value = true; + final calendar = DateCellEditor(onDismissed: () => widget.isFocus.value = false); calendar.show( context, cellContext: bloc.cellContext.clone(), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart index d11229c4cb..64fff54c47 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/number_cell.dart @@ -22,7 +22,7 @@ class NumberCell extends StatefulWidget with GridCellWidget { class _NumberCellState extends State { late NumberCellBloc _cellBloc; late TextEditingController _controller; - late CellSingleFocusNode _focusNode; + late SingleListenrFocusNode _focusNode; Timer? _delayOperation; @override @@ -30,14 +30,14 @@ class _NumberCellState extends State { final cellContext = widget.cellContextBuilder.build(); _cellBloc = getIt(param1: cellContext)..add(const NumberCellEvent.initial()); _controller = TextEditingController(text: contentFromState(_cellBloc.state)); - _focusNode = CellSingleFocusNode(); - _listenFocusNode(); + _focusNode = SingleListenrFocusNode(); + _listenOnFocusNodeChanged(); super.initState(); } @override Widget build(BuildContext context) { - _listenCellRequestFocus(context); + _handleCellRequestFocus(context); return BlocProvider.value( value: _cellBloc, child: MultiBlocListener( @@ -65,19 +65,17 @@ class _NumberCellState extends State { @override Future dispose() async { - widget.requestFocus.removeAllListener(); + widget.requestBeginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); - _focusNode.removeSingleListener(); + _focusNode.removeAllListener(); _focusNode.dispose(); super.dispose(); } @override void didUpdateWidget(covariant NumberCell oldWidget) { - if (oldWidget != widget) { - _listenFocusNode(); - } + _listenOnFocusNodeChanged(); super.didUpdateWidget(oldWidget); } @@ -92,16 +90,16 @@ class _NumberCellState extends State { } } - void _listenFocusNode() { - widget.onFocus.value = _focusNode.hasFocus; - _focusNode.setSingleListener(() { - widget.onFocus.value = _focusNode.hasFocus; + void _listenOnFocusNodeChanged() { + widget.isFocus.value = _focusNode.hasFocus; + _focusNode.setListener(() { + widget.isFocus.value = _focusNode.hasFocus; focusChanged(); }); } - void _listenCellRequestFocus(BuildContext context) { - widget.requestFocus.addListener(() { + void _handleCellRequestFocus(BuildContext context) { + widget.requestBeginFocus.setListener(() { if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { FocusScope.of(context).requestFocus(_focusNode); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart index c57a1865be..aa780263b5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/select_option_cell/select_option_cell.dart @@ -59,7 +59,7 @@ class _SingleSelectCellState extends State { return _SelectOptionCell( selectOptions: state.selectedOptions, cellStyle: widget.cellStyle, - onFocus: (value) => widget.onFocus.value = value, + onFocus: (value) => widget.isFocus.value = value, cellContextBuilder: widget.cellContextBuilder); }, ), @@ -113,7 +113,7 @@ class _MultiSelectCellState extends State { return _SelectOptionCell( selectOptions: state.selectedOptions, cellStyle: widget.cellStyle, - onFocus: (value) => widget.onFocus.value = value, + onFocus: (value) => widget.isFocus.value = value, cellContextBuilder: widget.cellContextBuilder); }, ), diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart index 1563d41d86..b2eda288a1 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/text_cell.dart @@ -35,7 +35,7 @@ class GridTextCell extends StatefulWidget with GridCellWidget { class _GridTextCellState extends State { late TextCellBloc _cellBloc; late TextEditingController _controller; - late CellSingleFocusNode _focusNode; + late SingleListenrFocusNode _focusNode; Timer? _delayOperation; @override @@ -44,9 +44,9 @@ class _GridTextCellState extends State { _cellBloc = getIt(param1: cellContext); _cellBloc.add(const TextCellEvent.initial()); _controller = TextEditingController(text: _cellBloc.state.content); - _focusNode = CellSingleFocusNode(); + _focusNode = SingleListenrFocusNode(); - _listenFocusNode(); + _listenOnFocusNodeChanged(); _listenRequestFocus(context); super.initState(); } @@ -81,10 +81,10 @@ class _GridTextCellState extends State { @override Future dispose() async { - widget.requestFocus.removeAllListener(); + widget.requestBeginFocus.removeAllListener(); _delayOperation?.cancel(); _cellBloc.close(); - _focusNode.removeSingleListener(); + _focusNode.removeAllListener(); _focusNode.dispose(); super.dispose(); @@ -93,21 +93,21 @@ class _GridTextCellState extends State { @override void didUpdateWidget(covariant GridTextCell oldWidget) { if (oldWidget != widget) { - _listenFocusNode(); + _listenOnFocusNodeChanged(); } super.didUpdateWidget(oldWidget); } - void _listenFocusNode() { - widget.onFocus.value = _focusNode.hasFocus; - _focusNode.setSingleListener(() { - widget.onFocus.value = _focusNode.hasFocus; + void _listenOnFocusNodeChanged() { + widget.isFocus.value = _focusNode.hasFocus; + _focusNode.setListener(() { + widget.isFocus.value = _focusNode.hasFocus; focusChanged(); }); } void _listenRequestFocus(BuildContext context) { - widget.requestFocus.addListener(() { + widget.requestBeginFocus.setListener(() { if (_focusNode.hasFocus == false && _focusNode.canRequestFocus) { FocusScope.of(context).requestFocus(_focusNode); } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart index 055a4947c8..15af124749 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/cell_editor.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; -class URLCellEditor extends StatefulWidget { +class URLCellEditor extends StatefulWidget with FlowyOverlayDelegate { final GridURLCellContext cellContext; const URLCellEditor({required this.cellContext, Key? key}) : super(key: key); @@ -25,18 +25,27 @@ class URLCellEditor extends StatefulWidget { // FlowyOverlay.of(context).insertWithAnchor( widget: OverlayContainer( - child: SizedBox(width: 200, child: editor), + child: SizedBox( + width: 200, + child: Padding(padding: const EdgeInsets.all(6), child: editor), + ), constraints: BoxConstraints.loose(const Size(300, 160)), ), identifier: URLCellEditor.identifier(), anchorContext: context, anchorDirection: AnchorDirection.bottomWithCenterAligned, + delegate: editor, ); } static String identifier() { return (URLCellEditor).toString(); } + + @override + bool asBarrier() { + return true; + } } class _URLCellEditorState extends State { diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart index 917cee66d2..56f0b421a5 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart @@ -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 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 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 Function(GridCellAccessoryBuildContext buildContext) get accessoryBuilder => (buildContext) { + final List 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 { @@ -46,7 +86,7 @@ class _GridURLCellState extends State { 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 { 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().state.url; + await _openUrlOrEdit(url); + }, + )); }, ), ); @@ -84,18 +125,15 @@ class _GridURLCellState extends State { @override Future 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().state.url; - await _openUrlOrEdit(url); - }; - return gesture; + @override + void didUpdateWidget(covariant GridURLCell oldWidget) { + _handleRequestFocus(); + super.didUpdateWidget(oldWidget); } Future _openUrlOrEdit(String url) async { @@ -108,27 +146,48 @@ class _GridURLCellState extends State { } } - 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(); - 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/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(); + 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()); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart index 21172b38d9..5071161d5a 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart @@ -86,7 +86,6 @@ class _GridHeaderCellContainer extends StatelessWidget { Widget build(BuildContext context) { final theme = context.watch(); final borderSide = BorderSide(color: theme.shader5, width: 1.0); - final decoration = BoxDecoration( border: Border( top: borderSide, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart index a643b58928..b646af6c40 100755 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/grid_row.dart @@ -1,5 +1,6 @@ import 'package:app_flowy/workspace/application/grid/prelude.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart'; import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; @@ -170,16 +171,30 @@ class _RowCells extends StatelessWidget { List _makeCells(BuildContext context, GridCellMap gridCellMap) { return gridCellMap.values.map( (gridCell) { - Widget? expander; + final GridCellWidget child = buildGridCellWidget(gridCell, cellCache); + List accessories = []; if (gridCell.field.isPrimary) { - expander = _CellExpander(onExpand: onExpand); + accessories.add(_PrimaryCellAccessory(onTapCallback: onExpand)); + } + + accessoryBuilder(buildContext) { + final builder = child.accessoryBuilder; + List accessories = []; + if (gridCell.field.isPrimary) { + accessories.add(_PrimaryCellAccessory(onTapCallback: onExpand)); + } + + if (builder != null) { + accessories.addAll(builder(buildContext)); + } + return accessories; } return CellContainer( width: gridCell.field.width.toDouble(), - child: buildGridCellWidget(gridCell, cellCache), + child: child, rowStateNotifier: Provider.of(context, listen: false), - expander: expander, + accessoryBuilder: accessoryBuilder, ); }, ).toList(); @@ -199,23 +214,19 @@ class RegionStateNotifier extends ChangeNotifier { bool get onEnter => _onEnter; } -class _CellExpander extends StatelessWidget { - final VoidCallback onExpand; - const _CellExpander({required this.onExpand, Key? key}) : super(key: key); +class _PrimaryCellAccessory extends StatelessWidget with GridCellAccessory { + final VoidCallback onTapCallback; + const _PrimaryCellAccessory({required this.onTapCallback, Key? key}) : super(key: key); @override Widget build(BuildContext context) { final theme = context.watch(); - return FittedBox( - fit: BoxFit.contain, - child: FlowyIconButton( - width: 26, - onPressed: onExpand, - iconPadding: const EdgeInsets.all(5), - radius: BorderRadius.circular(4), - icon: svgWidget("grid/expander", color: theme.main1), - ), - ); + return svgWidget("grid/expander", color: theme.main1); + } + + @override + void onTap() { + onTapCallback(); } } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/number_cell.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/number_cell.dart deleted file mode 100644 index 0f3f7c5f32..0000000000 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/number_cell.dart +++ /dev/null @@ -1,44 +0,0 @@ -import 'package:app_flowy/startup/startup.dart'; -import 'package:app_flowy/workspace/application/grid/prelude.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; - -class NumberCell extends StatefulWidget { - final GridCell cellData; - - const NumberCell({ - required this.cellData, - Key? key, - }) : super(key: key); - - @override - State createState() => _NumberCellState(); -} - -class _NumberCellState extends State { - late NumberCellBloc _cellBloc; - - @override - void initState() { - _cellBloc = getIt(param1: widget.cellData); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return BlocProvider.value( - value: _cellBloc, - child: BlocBuilder( - builder: (context, state) { - return Container(); - }, - ), - ); - } - - @override - Future dispose() async { - _cellBloc.close(); - super.dispose(); - } -} diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart index 40e77d9c43..1d7886c86c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_action_sheet.dart @@ -24,6 +24,7 @@ class GridRowActionSheet extends StatelessWidget { child: BlocBuilder( builder: (context, state) { final cells = _RowAction.values + .where((value) => value.enable()) .map( (action) => _RowActionCell( action: action, diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart index 0900039b1f..fbff4665d9 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/row/row_detail.dart @@ -3,6 +3,7 @@ import 'package:app_flowy/workspace/application/grid/field/field_service.dart'; import 'package:app_flowy/workspace/application/grid/row/row_detail_bloc.dart'; import 'package:app_flowy/workspace/application/grid/row/row_service.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/layout/sizes.dart'; +import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/cell_accessory.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/prelude.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/cell/url_cell/url_cell.dart'; import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header/field_cell.dart'; @@ -10,7 +11,6 @@ import 'package:app_flowy/workspace/presentation/plugins/grid/src/widgets/header import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/theme.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; -import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_scroll_bar.dart'; import 'package:flowy_infra_ui/widget/spacing.dart'; @@ -149,12 +149,18 @@ class _RowDetailCell extends StatelessWidget { @override Widget build(BuildContext context) { final theme = context.watch(); + final style = _customCellStyle(theme, gridCell.field.fieldType); + final cell = buildGridCellWidget(gridCell, cellCache, style: style); - final cell = buildGridCellWidget( - gridCell, - cellCache, - style: _buildCellStyle(theme, gridCell.field.fieldType), + final gesture = GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => cell.requestBeginFocus.notify(), + child: AccessoryHover( + child: cell, + contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), + ), ); + return ConstrainedBox( constraints: const BoxConstraints(minHeight: 40), child: IntrinsicHeight( @@ -167,12 +173,7 @@ class _RowDetailCell extends StatelessWidget { child: FieldCellButton(field: gridCell.field, onTap: () => _showFieldEditor(context)), ), const HSpace(10), - Expanded( - child: FlowyHover2( - child: cell, - contentPadding: const EdgeInsets.symmetric(horizontal: 10, vertical: 12), - ), - ), + Expanded(child: gesture), ], ), ), @@ -191,7 +192,7 @@ class _RowDetailCell extends StatelessWidget { } } -GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) { +GridCellStyle? _customCellStyle(AppTheme theme, FieldType fieldType) { switch (fieldType) { case FieldType.Checkbox: return null; @@ -217,7 +218,11 @@ GridCellStyle? _buildCellStyle(AppTheme theme, FieldType fieldType) { case FieldType.URL: return GridURLCellStyle( placeholder: LocaleKeys.grid_row_textPlaceholder.tr(), + accessoryTypes: [ + GridURLCellAccessoryType.edit, + GridURLCellAccessoryType.copyURL, + ], ); } - return null; + throw UnimplementedError; } diff --git a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart index 05a72a5504..7e3c14e021 100644 --- a/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart +++ b/frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/toolbar/grid_setting.dart @@ -85,7 +85,7 @@ class GridSettingList extends StatelessWidget { } Widget _renderList() { - final cells = GridSettingAction.values.map((action) { + final cells = GridSettingAction.values.where((value) => value.enable()).map((action) { return _SettingItem(action: action); }).toList(); diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart index 762a978693..6cc150489c 100644 --- a/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart +++ b/frontend/app_flowy/lib/workspace/presentation/widgets/float_bubble/question_bubble.dart @@ -1,5 +1,5 @@ import 'package:app_flowy/startup/tasks/rust_sdk.dart'; -import 'package:app_flowy/workspace/presentation/home/home_stack.dart'; +import 'package:app_flowy/workspace/presentation/home/toast.dart'; import 'package:app_flowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/theme.dart'; @@ -16,7 +16,6 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:app_flowy/generated/locale_keys.g.dart'; import 'package:device_info_plus/device_info_plus.dart'; -import 'package:fluttertoast/fluttertoast.dart'; class QuestionBubble extends StatelessWidget { const QuestionBubble({Key? key}) : super(key: key); @@ -46,7 +45,7 @@ class QuestionBubble extends StatelessWidget { _launchURL("https://discord.gg/9Q2xaN37tV"); break; case BubbleAction.debug: - const _DebugToast().show(); + _DebugToast().show(); break; } }); @@ -71,55 +70,14 @@ class QuestionBubble extends StatelessWidget { } } -class _DebugToast extends StatelessWidget { - const _DebugToast({Key? key}) : super(key: key); +class _DebugToast { + void show() async { + var debugInfo = ""; + debugInfo += await _getDeviceInfo(); + debugInfo += await _getDocumentPath(); + Clipboard.setData(ClipboardData(text: debugInfo)); - @override - Widget build(BuildContext context) { - return FutureBuilder( - future: Future(() async { - var debugInfo = ""; - debugInfo += await _getDeviceInfo(); - debugInfo += await _getDocumentPath(); - - Clipboard.setData(ClipboardData(text: debugInfo)); - }), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.hasError) { - return _done(context, Text("Error: ${snapshot.error}")); - } else { - return _done(context, null); - } - } else { - return const CircularProgressIndicator(); - } - }, - ); - } - - Widget _done(BuildContext context, Widget? error) { - final theme = context.watch(); - return Container( - padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 12.0), - decoration: BoxDecoration(borderRadius: BorderRadius.circular(25.0), color: theme.main1), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.check), - const SizedBox(width: 12.0), - (error == null) ? Text(LocaleKeys.questionBubble_debug_success.tr()) : error - ], - ), - ); - } - - void show() { - fToast.showToast( - child: this, - gravity: ToastGravity.BOTTOM, - toastDuration: const Duration(seconds: 3), - ); + showMessageToast(LocaleKeys.questionBubble_debug_success.tr()); } Future _getDeviceInfo() async { diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart index bb7144974b..42ee43cabc 100644 --- a/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart +++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/style_widget/hover.dart @@ -1,9 +1,6 @@ import 'package:flutter/material.dart'; // ignore: unused_import import 'package:flowy_infra/time/duration.dart'; -import 'package:flowy_infra/size.dart'; -import 'package:flowy_infra/theme.dart'; -import 'package:provider/provider.dart'; typedef HoverBuilder = Widget Function(BuildContext context, bool onHover); @@ -52,7 +49,7 @@ class _FlowyHoverState extends State { child: child, ); } else { - return child; + return Container(child: child, color: widget.style.backgroundColor); } } } @@ -63,12 +60,14 @@ class HoverStyle { final Color hoverColor; final BorderRadius borderRadius; final EdgeInsets contentMargin; + final Color backgroundColor; const HoverStyle( {this.borderColor = Colors.transparent, this.borderWidth = 0, this.borderRadius = const BorderRadius.all(Radius.circular(6)), this.contentMargin = EdgeInsets.zero, + this.backgroundColor = Colors.transparent, required this.hoverColor}); } @@ -100,120 +99,3 @@ class FlowyHoverContainer extends StatelessWidget { ); } } - -// -abstract class FlowyHoverWidget extends Widget { - const FlowyHoverWidget({Key? key}) : super(key: key); - - ValueNotifier? get onFocus; -} - -class FlowyHover2 extends StatefulWidget { - final FlowyHoverWidget child; - final EdgeInsets contentPadding; - const FlowyHover2({ - required this.child, - this.contentPadding = EdgeInsets.zero, - Key? key, - }) : super(key: key); - - @override - State createState() => _FlowyHover2State(); -} - -class _FlowyHover2State extends State { - late FlowyHoverState _hoverState; - VoidCallback? _listenerFn; - - @override - void initState() { - _hoverState = FlowyHoverState(); - - listener() { - _hoverState.onFocus = widget.child.onFocus?.value ?? false; - } - - _listenerFn = listener; - widget.child.onFocus?.addListener(listener); - - super.initState(); - } - - @override - void dispose() { - _hoverState.dispose(); - - if (_listenerFn != null) { - widget.child.onFocus?.removeListener(_listenerFn!); - _listenerFn = null; - } - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ChangeNotifierProvider.value( - value: _hoverState, - child: MouseRegion( - cursor: SystemMouseCursors.click, - opaque: false, - onEnter: (p) => setState(() => _hoverState.onHover = true), - onExit: (p) => setState(() => _hoverState.onHover = false), - child: Stack( - fit: StackFit.loose, - alignment: AlignmentDirectional.center, - children: [ - const _HoverBackground(), - Padding( - padding: widget.contentPadding, - child: widget.child, - ), - ], - ), - ), - ); - } -} - -class _HoverBackground extends StatelessWidget { - const _HoverBackground({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context) { - final theme = context.watch(); - return Consumer( - builder: (context, state, child) { - if (state.onHover || state.onFocus) { - return FlowyHoverContainer( - style: HoverStyle(borderRadius: Corners.s6Border, hoverColor: theme.shader6), - ); - } else { - return const SizedBox(); - } - }, - ); - } -} - -class FlowyHoverState extends ChangeNotifier { - bool _onHover = false; - bool _onFocus = false; - - set onHover(bool value) { - if (_onHover != value) { - _onHover = value; - notifyListeners(); - } - } - - bool get onHover => _onHover; - - set onFocus(bool value) { - if (_onFocus != value) { - _onFocus = value; - notifyListeners(); - } - } - - bool get onFocus => _onFocus; -} diff --git a/frontend/app_flowy/pubspec.yaml b/frontend/app_flowy/pubspec.yaml index eb7b17dc21..8dbc3b4c70 100644 --- a/frontend/app_flowy/pubspec.yaml +++ b/frontend/app_flowy/pubspec.yaml @@ -72,7 +72,7 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 device_info_plus: ^3.2.1 - fluttertoast: ^8.0.8 + fluttertoast: ^8.0.9 table_calendar: ^3.0.5 reorderables: ^0.5.0 linked_scroll_controller: ^0.2.0