diff --git a/frontend/appflowy_flutter/integration_test/util/database_test_op.dart b/frontend/appflowy_flutter/integration_test/util/database_test_op.dart index 12722ebe1b..4d6e6ecd27 100644 --- a/frontend/appflowy_flutter/integration_test/util/database_test_op.dart +++ b/frontend/appflowy_flutter/integration_test/util/database_test_op.dart @@ -1116,7 +1116,7 @@ extension AppFlowyDatabaseTest on WidgetTester { /// Must call [tapSortMenuInSettingBar] first. Future tapAllSortButton() async { - await tapButton(find.byType(DatabaseDeleteSortButton)); + await tapButton(find.byType(DeleteAllSortsButton)); } Future scrollOptionFilterListByOffset(Offset offset) async { diff --git a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_sort_bottom_sheet.dart b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_sort_bottom_sheet.dart index d1e58838c4..f840db7667 100644 --- a/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_sort_bottom_sheet.dart +++ b/frontend/appflowy_flutter/lib/mobile/presentation/database/view/database_sort_bottom_sheet.dart @@ -121,8 +121,8 @@ class _Header extends StatelessWidget { if (state.newSortFieldId != null && state.newSortCondition != null) { context.read().add( SortEditorEvent.createSort( - state.newSortFieldId!, - state.newSortCondition!, + fieldId: state.newSortFieldId!, + condition: state.newSortCondition!, ), ); } @@ -531,9 +531,8 @@ class _SortDetailContent extends StatelessWidget { } else { context.read().add( SortEditorEvent.editSort( - sortInfo!.sortId, - null, - newCondition, + sortId: sortInfo!.sortId, + condition: newCondition, ), ); } @@ -545,9 +544,8 @@ class _SortDetailContent extends StatelessWidget { } else { context.read().add( SortEditorEvent.editSort( - sortInfo!.sortId, - newFieldId, - null, + sortId: sortInfo!.sortId, + fieldId: newFieldId, ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_create_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_create_bloc.dart deleted file mode 100644 index 22dabf671a..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_create_bloc.dart +++ /dev/null @@ -1,141 +0,0 @@ -import 'dart:async'; - -import 'package:appflowy/plugins/database/application/field/field_info.dart'; -import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart'; -import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbserver.dart'; -import 'package:appflowy_backend/protobuf/flowy-error/errors.pbserver.dart'; -import 'package:appflowy_result/appflowy_result.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../../../application/field/field_controller.dart'; -import '../../../application/sort/sort_service.dart'; -import 'util.dart'; - -part 'sort_create_bloc.freezed.dart'; - -class CreateSortBloc extends Bloc { - CreateSortBloc({required this.viewId, required this.fieldController}) - : _sortBackendSvc = SortBackendService(viewId: viewId), - super(CreateSortState.initial(fieldController.fieldInfos)) { - _dispatch(); - } - - final String viewId; - final SortBackendService _sortBackendSvc; - final FieldController fieldController; - void Function(List)? _onFieldFn; - - void _dispatch() { - on( - (event, emit) async { - event.when( - initial: () { - _startListening(); - }, - didReceiveFields: (List fields) { - emit( - state.copyWith( - allFields: fields, - creatableFields: _filterFields(fields, state.filterText), - ), - ); - }, - didReceiveFilterText: (String text) { - emit( - state.copyWith( - filterText: text, - creatableFields: _filterFields(state.allFields, text), - ), - ); - }, - createDefaultSort: (FieldInfo field) { - emit(state.copyWith(didCreateSort: true)); - _createDefaultSort(field); - }, - ); - }, - ); - } - - List _filterFields( - List fields, - String filterText, - ) { - final List allFields = List.from(fields); - final keyword = filterText.toLowerCase(); - allFields.retainWhere((field) { - if (!field.canCreateSort) { - return false; - } - - if (filterText.isNotEmpty) { - return field.name.toLowerCase().contains(keyword); - } - - return true; - }); - - return allFields; - } - - void _startListening() { - _onFieldFn = (fields) { - fields.retainWhere((field) => field.canCreateSort); - add(CreateSortEvent.didReceiveFields(fields)); - }; - fieldController.addListener(onReceiveFields: _onFieldFn); - } - - Future> _createDefaultSort( - FieldInfo field, - ) async { - final result = await _sortBackendSvc.insertSort( - fieldId: field.id, - condition: SortConditionPB.Ascending, - ); - - return result; - } - - @override - Future close() async { - if (_onFieldFn != null) { - fieldController.removeListener(onFieldsListener: _onFieldFn); - _onFieldFn = null; - } - return super.close(); - } -} - -@freezed -class CreateSortEvent with _$CreateSortEvent { - const factory CreateSortEvent.initial() = _Initial; - const factory CreateSortEvent.didReceiveFields(List fields) = - _DidReceiveFields; - - const factory CreateSortEvent.createDefaultSort(FieldInfo field) = - _CreateDefaultSort; - - const factory CreateSortEvent.didReceiveFilterText(String text) = - _DidReceiveFilterText; -} - -@freezed -class CreateSortState with _$CreateSortState { - const factory CreateSortState({ - required String filterText, - required List creatableFields, - required List allFields, - required bool didCreateSort, - }) = _CreateSortState; - - factory CreateSortState.initial(List fields) { - return CreateSortState( - filterText: "", - creatableFields: getCreatableSorts(fields), - allFields: fields, - didCreateSort: false, - ); - } -} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_editor_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_editor_bloc.dart index 4cb258f7b5..713bfc442d 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_editor_bloc.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_editor_bloc.dart @@ -5,29 +5,26 @@ import 'package:appflowy/plugins/database/application/field/field_info.dart'; import 'package:appflowy/plugins/database/application/sort/sort_service.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_info.dart'; import 'package:appflowy_backend/log.dart'; -import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart'; -import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbserver.dart'; +import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:collection/collection.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'util.dart'; - part 'sort_editor_bloc.freezed.dart'; class SortEditorBloc extends Bloc { SortEditorBloc({ required this.viewId, required this.fieldController, - required List sortInfos, }) : _sortBackendSvc = SortBackendService(viewId: viewId), super( SortEditorState.initial( - sortInfos, + fieldController.sortInfos, fieldController.fieldInfos, ), ) { _dispatch(); + _startListening(); } final String viewId; @@ -41,9 +38,6 @@ class SortEditorBloc extends Bloc { on( (event, emit) async { await event.when( - initial: () { - _startListening(); - }, didReceiveFields: (List fields) { emit( state.copyWith( @@ -52,10 +46,16 @@ class SortEditorBloc extends Bloc { ), ); }, - createSort: (String fieldId, SortConditionPB condition) async { + updateCreateSortFilter: (text) { + emit(state.copyWith(filter: text)); + }, + createSort: ( + String fieldId, + SortConditionPB? condition, + ) async { final result = await _sortBackendSvc.insertSort( fieldId: fieldId, - condition: condition, + condition: condition ?? SortConditionPB.Ascending, ); result.fold((l) => {}, (err) => Log.error(err)); }, @@ -142,24 +142,25 @@ class SortEditorBloc extends Bloc { @freezed class SortEditorEvent with _$SortEditorEvent { - const factory SortEditorEvent.initial() = _Initial; const factory SortEditorEvent.didReceiveFields(List fieldInfos) = _DidReceiveFields; const factory SortEditorEvent.didReceiveSorts(List sortInfos) = _DidReceiveSorts; - const factory SortEditorEvent.createSort( - String fieldId, - SortConditionPB condition, - ) = _CreateSort; - const factory SortEditorEvent.editSort( - String sortId, + const factory SortEditorEvent.updateCreateSortFilter(String text) = + _UpdateCreateSortFilter; + const factory SortEditorEvent.createSort({ + required String fieldId, + SortConditionPB? condition, + }) = _CreateSort; + const factory SortEditorEvent.editSort({ + required String sortId, String? fieldId, SortConditionPB? condition, - ) = _EditSort; - const factory SortEditorEvent.deleteSort(SortInfo sortInfo) = _DeleteSort; - const factory SortEditorEvent.deleteAllSorts() = _DeleteAllSorts; + }) = _EditSort; const factory SortEditorEvent.reorderSort(int oldIndex, int newIndex) = _ReorderSort; + const factory SortEditorEvent.deleteSort(SortInfo sortInfo) = _DeleteSort; + const factory SortEditorEvent.deleteAllSorts() = _DeleteAllSorts; } @freezed @@ -168,6 +169,7 @@ class SortEditorState with _$SortEditorState { required List sortInfos, required List creatableFields, required List allFields, + required String filter, }) = _SortEditorState; factory SortEditorState.initial( @@ -178,6 +180,13 @@ class SortEditorState with _$SortEditorState { creatableFields: getCreatableSorts(fields), allFields: fields, sortInfos: sortInfos, + filter: "", ); } } + +List getCreatableSorts(List fieldInfos) { + final List creatableFields = List.from(fieldInfos); + creatableFields.retainWhere((element) => element.canCreateSort); + return creatableFields; +} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_menu_bloc.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_menu_bloc.dart deleted file mode 100644 index b65f71ed1e..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/sort_menu_bloc.dart +++ /dev/null @@ -1,123 +0,0 @@ -import 'dart:async'; - -import 'package:appflowy/plugins/database/application/field/field_info.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../../../application/field/field_controller.dart'; -import '../../presentation/widgets/sort/sort_info.dart'; - -import 'util.dart'; - -part 'sort_menu_bloc.freezed.dart'; - -class SortMenuBloc extends Bloc { - SortMenuBloc({required this.viewId, required this.fieldController}) - : super( - SortMenuState.initial( - viewId, - fieldController.sortInfos, - fieldController.fieldInfos, - ), - ) { - _dispatch(); - } - - final String viewId; - final FieldController fieldController; - void Function(List)? _onSortChangeFn; - void Function(List)? _onFieldFn; - - void _dispatch() { - on( - (event, emit) async { - event.when( - initial: () { - _startListening(); - }, - didReceiveSortInfos: (sortInfos) { - emit(state.copyWith(sortInfos: sortInfos)); - }, - toggleMenu: () { - final isVisible = !state.isVisible; - emit(state.copyWith(isVisible: isVisible)); - }, - didReceiveFields: (List fields) { - emit( - state.copyWith( - fields: fields, - creatableFields: getCreatableSorts(fields), - ), - ); - }, - ); - }, - ); - } - - void _startListening() { - _onSortChangeFn = (sortInfos) { - add(SortMenuEvent.didReceiveSortInfos(sortInfos)); - }; - - _onFieldFn = (fields) { - add(SortMenuEvent.didReceiveFields(fields)); - }; - - fieldController.addListener( - onSorts: (sortInfos) { - _onSortChangeFn?.call(sortInfos); - }, - onReceiveFields: (fields) { - _onFieldFn?.call(fields); - }, - ); - } - - @override - Future close() { - if (_onSortChangeFn != null) { - fieldController.removeListener(onSortsListener: _onSortChangeFn!); - _onSortChangeFn = null; - } - if (_onFieldFn != null) { - fieldController.removeListener(onFieldsListener: _onFieldFn!); - _onFieldFn = null; - } - return super.close(); - } -} - -@freezed -class SortMenuEvent with _$SortMenuEvent { - const factory SortMenuEvent.initial() = _Initial; - const factory SortMenuEvent.didReceiveSortInfos(List sortInfos) = - _DidReceiveSortInfos; - const factory SortMenuEvent.didReceiveFields(List fields) = - _DidReceiveFields; - const factory SortMenuEvent.toggleMenu() = _SetMenuVisibility; -} - -@freezed -class SortMenuState with _$SortMenuState { - const factory SortMenuState({ - required String viewId, - required List sortInfos, - required List fields, - required List creatableFields, - required bool isVisible, - }) = _SortMenuState; - - factory SortMenuState.initial( - String viewId, - List sortInfos, - List fields, - ) => - SortMenuState( - viewId: viewId, - sortInfos: sortInfos, - fields: fields, - creatableFields: getCreatableSorts(fields), - isVisible: false, - ); -} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/util.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/util.dart deleted file mode 100644 index 5a147b8c67..0000000000 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/application/sort/util.dart +++ /dev/null @@ -1,7 +0,0 @@ -import 'package:appflowy/plugins/database/application/field/field_info.dart'; - -List getCreatableSorts(List fieldInfos) { - final List creatableFields = List.from(fieldInfos); - creatableFields.retainWhere((element) => element.canCreateSort); - return creatableFields; -} diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/create_sort_list.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/create_sort_list.dart index 7891247224..69e46a04ff 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/create_sort_list.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/create_sort_list.dart @@ -1,8 +1,7 @@ import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/database/application/field/field_controller.dart'; import 'package:appflowy/plugins/database/application/field/field_info.dart'; -import 'package:appflowy/plugins/database/grid/application/sort/sort_create_bloc.dart'; +import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy/util/field_type_extension.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -15,97 +14,56 @@ import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -class GridCreateSortList extends StatefulWidget { - const GridCreateSortList({ +class CreateDatabaseViewSortList extends StatelessWidget { + const CreateDatabaseViewSortList({ super.key, - required this.viewId, - required this.fieldController, - required this.onClosed, - this.onCreateSort, + required this.onTap, }); - final String viewId; - final FieldController fieldController; - final VoidCallback onClosed; - final VoidCallback? onCreateSort; - - @override - State createState() => _GridCreateSortListState(); -} - -class _GridCreateSortListState extends State { - late CreateSortBloc editBloc; - - @override - void initState() { - editBloc = CreateSortBloc( - viewId: widget.viewId, - fieldController: widget.fieldController, - )..add(const CreateSortEvent.initial()); - super.initState(); - } + final VoidCallback onTap; @override Widget build(BuildContext context) { - return BlocProvider.value( - value: editBloc, - child: BlocListener( - listener: (context, state) { - if (state.didCreateSort) { - widget.onClosed(); - } - }, - child: BlocBuilder( - builder: (context, state) { - final cells = state.creatableFields.map((fieldInfo) { - return SizedBox( - height: GridSize.popoverItemHeight, - child: GridSortPropertyCell( - fieldInfo: fieldInfo, - onTap: (fieldInfo) => createSort(fieldInfo), - ), - ); - }).toList(); + return BlocBuilder( + builder: (context, state) { + final filter = state.filter.toLowerCase(); + final cells = state.creatableFields + .where((field) => field.field.name.toLowerCase().contains(filter)) + .map((fieldInfo) { + return GridSortPropertyCell( + fieldInfo: fieldInfo, + onTap: () { + context + .read() + .add(SortEditorEvent.createSort(fieldId: fieldInfo.id)); + onTap.call(); + }, + ); + }).toList(); - final List slivers = [ - SliverPersistentHeader( - pinned: true, - delegate: _SortTextFieldDelegate(), - ), - SliverToBoxAdapter( - child: ListView.separated( - shrinkWrap: true, - itemCount: cells.length, - itemBuilder: (BuildContext context, int index) { - return cells[index]; - }, - separatorBuilder: (BuildContext context, int index) { - return VSpace(GridSize.typeOptionSeparatorHeight); - }, - ), - ), - ]; - return CustomScrollView( + final List slivers = [ + SliverPersistentHeader( + pinned: true, + delegate: _SortTextFieldDelegate(), + ), + SliverToBoxAdapter( + child: ListView.separated( shrinkWrap: true, - slivers: slivers, - physics: StyledScrollPhysics(), - ); - }, - ), - ), + itemCount: cells.length, + itemBuilder: (_, index) => cells[index], + separatorBuilder: (_, __) => + VSpace(GridSize.typeOptionSeparatorHeight), + ), + ), + ]; + return CustomScrollView( + shrinkWrap: true, + slivers: slivers, + physics: StyledScrollPhysics(), + ); + }, ); } - - @override - void dispose() { - editBloc.close(); - super.dispose(); - } - - void createSort(FieldInfo field) { - editBloc.add(CreateSortEvent.createDefaultSort(field)); - widget.onCreateSort?.call(); - } } class _SortTextFieldDelegate extends SliverPersistentHeaderDelegate { @@ -127,8 +85,8 @@ class _SortTextFieldDelegate extends SliverPersistentHeaderDelegate { hintText: LocaleKeys.grid_settings_sortBy.tr(), onChanged: (text) { context - .read() - .add(CreateSortEvent.didReceiveFilterText(text)); + .read() + .add(SortEditorEvent.updateCreateSortFilter(text)); }, ), ); @@ -141,9 +99,7 @@ class _SortTextFieldDelegate extends SliverPersistentHeaderDelegate { double get minExtent => fixHeight; @override - bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { - return false; - } + bool shouldRebuild(covariant oldDelegate) => false; } class GridSortPropertyCell extends StatelessWidget { @@ -154,20 +110,23 @@ class GridSortPropertyCell extends StatelessWidget { }); final FieldInfo fieldInfo; - final Function(FieldInfo) onTap; + final VoidCallback onTap; @override Widget build(BuildContext context) { - return FlowyButton( - hoverColor: AFThemeExtension.of(context).lightGreyHover, - text: FlowyText.medium( - fieldInfo.name, - color: AFThemeExtension.of(context).textColor, - ), - onTap: () => onTap(fieldInfo), - leftIcon: FlowySvg( - fieldInfo.fieldType.svgData, - color: Theme.of(context).iconTheme.color, + return SizedBox( + height: GridSize.popoverItemHeight, + child: FlowyButton( + hoverColor: AFThemeExtension.of(context).lightGreyHover, + text: FlowyText.medium( + fieldInfo.name, + color: AFThemeExtension.of(context).textColor, + ), + onTap: onTap, + leftIcon: FlowySvg( + fieldInfo.fieldType.svgData, + color: Theme.of(context).iconTheme.color, + ), ), ); } diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_choice_button.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_choice_button.dart index 847c6daca7..a00bc1002f 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_choice_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_choice_button.dart @@ -27,16 +27,15 @@ class SortChoiceButton extends StatelessWidget { decoration: BoxDecoration( color: Colors.transparent, border: Border.fromBorderSide( - BorderSide( - color: AFThemeExtension.of(context).toggleOffFill, - ), + BorderSide(color: Theme.of(context).dividerColor), ), - borderRadius: const BorderRadius.all(Radius.circular(14)), + borderRadius: BorderRadius.all(radius), ), useIntrinsicWidth: true, text: FlowyText( text, color: AFThemeExtension.of(context).textColor, + overflow: TextOverflow.ellipsis, ), margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), radius: BorderRadius.all(radius), diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_editor.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_editor.dart index 2bd844a958..671a5c2084 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_editor.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_editor.dart @@ -2,9 +2,7 @@ import 'dart:io'; import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/database/application/field/field_controller.dart'; import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart'; -import 'package:appflowy/plugins/database/grid/application/sort/util.dart'; import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; @@ -14,7 +12,6 @@ import 'package:flowy_infra/theme_extension.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; -import 'dart:math' as math; import 'create_sort_list.dart'; import 'order_panel.dart'; @@ -22,16 +19,7 @@ import 'sort_choice_button.dart'; import 'sort_info.dart'; class SortEditor extends StatefulWidget { - const SortEditor({ - super.key, - required this.viewId, - required this.fieldController, - required this.sortInfos, - }); - - final String viewId; - final FieldController fieldController; - final List sortInfos; + const SortEditor({super.key}); @override State createState() => _SortEditorState(); @@ -42,69 +30,57 @@ class _SortEditorState extends State { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => SortEditorBloc( - viewId: widget.viewId, - fieldController: widget.fieldController, - sortInfos: widget.sortInfos, - )..add(const SortEditorEvent.initial()), - child: BlocBuilder( - builder: (context, state) { - final sortInfos = state.sortInfos; - - return ReorderableListView.builder( - onReorder: (oldIndex, newIndex) => context - .read() - .add(SortEditorEvent.reorderSort(oldIndex, newIndex)), - itemCount: state.sortInfos.length, - itemBuilder: (context, index) => Padding( - key: ValueKey(sortInfos[index].sortId), - padding: const EdgeInsets.symmetric(vertical: 6), - child: DatabaseSortItem( - index: index, - sortInfo: sortInfos[index], - popoverMutex: popoverMutex, - ), - ), - proxyDecorator: (child, index, animation) => Material( - color: Colors.transparent, - child: Stack( - children: [ - BlocProvider.value( - value: context.read(), - child: child, - ), - MouseRegion( - cursor: Platform.isWindows - ? SystemMouseCursors.click - : SystemMouseCursors.grabbing, - child: const SizedBox.expand(), - ), - ], - ), - ), - shrinkWrap: true, - buildDefaultDragHandles: false, - footer: Row( + return BlocBuilder( + builder: (context, state) { + final sortInfos = state.sortInfos; + return ReorderableListView.builder( + onReorder: (oldIndex, newIndex) => context + .read() + .add(SortEditorEvent.reorderSort(oldIndex, newIndex)), + itemCount: state.sortInfos.length, + itemBuilder: (context, index) => DatabaseSortItem( + key: ValueKey(sortInfos[index].sortId), + index: index, + sortInfo: sortInfos[index], + popoverMutex: popoverMutex, + ), + proxyDecorator: (child, index, animation) => Material( + color: Colors.transparent, + child: Stack( children: [ - Flexible( - child: DatabaseAddSortButton( - viewId: widget.viewId, - fieldController: widget.fieldController, - popoverMutex: popoverMutex, - ), + BlocProvider.value( + value: context.read(), + child: child, ), - const HSpace(6), - Flexible( - child: DatabaseDeleteSortButton( - popoverMutex: popoverMutex, - ), + MouseRegion( + cursor: Platform.isWindows + ? SystemMouseCursors.click + : SystemMouseCursors.grabbing, + child: const SizedBox.expand(), ), ], ), - ); - }, - ), + ), + shrinkWrap: true, + buildDefaultDragHandles: false, + footer: Row( + children: [ + Flexible( + child: DatabaseAddSortButton( + disable: state.creatableFields.isEmpty, + popoverMutex: popoverMutex, + ), + ), + const HSpace(6), + Flexible( + child: DeleteAllSortsButton( + popoverMutex: popoverMutex, + ), + ), + ], + ), + ); + }, ); } } @@ -123,80 +99,89 @@ class DatabaseSortItem extends StatelessWidget { @override Widget build(BuildContext context) { - final deleteButton = FlowyIconButton( - width: 26, - onPressed: () => context - .read() - .add(SortEditorEvent.deleteSort(sortInfo)), - iconPadding: const EdgeInsets.all(5), - hoverColor: AFThemeExtension.of(context).lightGreyHover, - icon: - FlowySvg(FlowySvgs.close_s, color: Theme.of(context).iconTheme.color), - ); - - return Row( - children: [ - ReorderableDragStartListener( - index: index, - child: MouseRegion( - cursor: Platform.isWindows - ? SystemMouseCursors.click - : SystemMouseCursors.grab, - child: SizedBox( - width: 14, - height: 14, - child: FlowySvg( - FlowySvgs.drag_element_s, - color: Theme.of(context).iconTheme.color, + return Container( + padding: const EdgeInsets.symmetric(vertical: 6), + color: Theme.of(context).cardColor, + child: Row( + children: [ + ReorderableDragStartListener( + index: index, + child: MouseRegion( + cursor: Platform.isWindows + ? SystemMouseCursors.click + : SystemMouseCursors.grab, + child: SizedBox( + width: 14 + 12, + height: 14, + child: FlowySvg( + FlowySvgs.drag_element_s, + size: const Size.square(14), + color: Theme.of(context).iconTheme.color, + ), ), ), ), - ), - const HSpace(6), - SizedBox( - height: 26, - child: SortChoiceButton( - text: sortInfo.fieldInfo.name, - editable: false, + Flexible( + fit: FlexFit.tight, + child: SizedBox( + height: 26, + child: SortChoiceButton( + text: sortInfo.fieldInfo.name, + editable: false, + ), + ), ), - ), - const HSpace(6), - SizedBox( - height: 26, - child: DatabaseSortItemOrderButton( - sortInfo: sortInfo, - popoverMutex: popoverMutex, + const HSpace(6), + Flexible( + fit: FlexFit.tight, + child: SizedBox( + height: 26, + child: SortConditionButton( + sortInfo: sortInfo, + popoverMutex: popoverMutex, + ), + ), ), - ), - const Spacer(), - const HSpace(6), - deleteButton, - ], + const HSpace(6), + FlowyIconButton( + width: 26, + onPressed: () { + context + .read() + .add(SortEditorEvent.deleteSort(sortInfo)); + PopoverContainer.of(context).close(); + }, + hoverColor: AFThemeExtension.of(context).lightGreyHover, + icon: FlowySvg( + FlowySvgs.trash_m, + color: Theme.of(context).iconTheme.color, + size: const Size.square(16), + ), + ), + ], + ), ); } } extension SortConditionExtension on SortConditionPB { String get title { - switch (this) { - case SortConditionPB.Descending: - return LocaleKeys.grid_sort_descending.tr(); - default: - return LocaleKeys.grid_sort_ascending.tr(); - } + return switch (this) { + SortConditionPB.Ascending => LocaleKeys.grid_sort_ascending.tr(), + SortConditionPB.Descending => LocaleKeys.grid_sort_descending.tr(), + _ => throw UnimplementedError(), + }; } } class DatabaseAddSortButton extends StatefulWidget { const DatabaseAddSortButton({ super.key, - required this.viewId, - required this.fieldController, + required this.disable, required this.popoverMutex, }); - final String viewId; - final FieldController fieldController; + final bool disable; final PopoverMutex popoverMutex; @override @@ -213,32 +198,36 @@ class _DatabaseAddSortButtonState extends State { mutex: widget.popoverMutex, direction: PopoverDirection.bottomWithLeftAligned, constraints: BoxConstraints.loose(const Size(200, 300)), - offset: const Offset(0, 8), + offset: const Offset(-6, 8), triggerActions: PopoverTriggerFlags.none, asBarrier: true, + popupBuilder: (popoverContext) { + return BlocProvider.value( + value: context.read(), + child: CreateDatabaseViewSortList( + onTap: () => _popoverController.close(), + ), + ); + }, + onClose: () => context + .read() + .add(const SortEditorEvent.updateCreateSortFilter("")), child: SizedBox( height: GridSize.popoverItemHeight, child: FlowyButton( hoverColor: AFThemeExtension.of(context).greyHover, - disable: getCreatableSorts(widget.fieldController.fieldInfos).isEmpty, + disable: widget.disable, text: FlowyText.medium(LocaleKeys.grid_sort_addSort.tr()), onTap: () => _popoverController.show(), leftIcon: const FlowySvg(FlowySvgs.add_s), ), ), - popupBuilder: (BuildContext context) { - return GridCreateSortList( - viewId: widget.viewId, - fieldController: widget.fieldController, - onClosed: () => _popoverController.close(), - ); - }, ); } } -class DatabaseDeleteSortButton extends StatelessWidget { - const DatabaseDeleteSortButton({super.key, required this.popoverMutex}); +class DeleteAllSortsButton extends StatelessWidget { + const DeleteAllSortsButton({super.key, required this.popoverMutex}); final PopoverMutex popoverMutex; @@ -264,8 +253,8 @@ class DatabaseDeleteSortButton extends StatelessWidget { } } -class DatabaseSortItemOrderButton extends StatefulWidget { - const DatabaseSortItemOrderButton({ +class SortConditionButton extends StatefulWidget { + const SortConditionButton({ super.key, required this.popoverMutex, required this.sortInfo, @@ -275,21 +264,14 @@ class DatabaseSortItemOrderButton extends StatefulWidget { final SortInfo sortInfo; @override - State createState() => - _DatabaseSortItemOrderButtonState(); + State createState() => _SortConditionButtonState(); } -class _DatabaseSortItemOrderButtonState - extends State { +class _SortConditionButtonState extends State { final PopoverController popoverController = PopoverController(); @override Widget build(BuildContext context) { - final arrow = Transform.rotate( - angle: -math.pi / 2, - child: const FlowySvg(FlowySvgs.arrow_left_s), - ); - return AppFlowyPopover( controller: popoverController, mutex: widget.popoverMutex, @@ -301,9 +283,8 @@ class _DatabaseSortItemOrderButtonState onCondition: (condition) { context.read().add( SortEditorEvent.editSort( - widget.sortInfo.sortId, - null, - condition, + sortId: widget.sortInfo.sortId, + condition: condition, ), ); popoverController.close(); @@ -312,7 +293,10 @@ class _DatabaseSortItemOrderButtonState }, child: SortChoiceButton( text: widget.sortInfo.sortPB.condition.title, - rightIcon: arrow, + rightIcon: FlowySvg( + FlowySvgs.arrow_down_s, + color: Theme.of(context).iconTheme.color, + ), onTap: () => popoverController.show(), ), ); diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_menu.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_menu.dart index 30fe44f101..43f583bd07 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_menu.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/sort/sort_menu.dart @@ -1,6 +1,8 @@ +import 'dart:math' as math; + import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/plugins/database/application/field/field_controller.dart'; -import 'package:appflowy/plugins/database/grid/application/sort/sort_menu_bloc.dart'; +import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flutter/material.dart'; @@ -8,8 +10,6 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:easy_localization/easy_localization.dart'; -import 'dart:math' as math; - import 'sort_choice_button.dart'; import 'sort_editor.dart'; import 'sort_info.dart'; @@ -24,12 +24,12 @@ class SortMenu extends StatelessWidget { @override Widget build(BuildContext context) { - return BlocProvider( - create: (context) => SortMenuBloc( + return BlocProvider( + create: (context) => SortEditorBloc( viewId: fieldController.viewId, fieldController: fieldController, - )..add(const SortMenuEvent.initial()), - child: BlocBuilder( + ), + child: BlocBuilder( builder: (context, state) { if (state.sortInfos.isEmpty) { return const SizedBox.shrink(); @@ -42,10 +42,9 @@ class SortMenu extends StatelessWidget { offset: const Offset(0, 5), margin: const EdgeInsets.fromLTRB(6.0, 0.0, 6.0, 6.0), popupBuilder: (BuildContext popoverContext) { - return SortEditor( - viewId: state.viewId, - fieldController: context.read().fieldController, - sortInfos: state.sortInfos, + return BlocProvider.value( + value: context.read(), + child: const SortEditor(), ); }, child: SortChoiceChip(sortInfos: state.sortInfos), diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/grid_setting_bar.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/grid_setting_bar.dart index 347ada8883..cd861642bb 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/grid_setting_bar.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/grid_setting_bar.dart @@ -1,6 +1,6 @@ import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/grid/application/filter/filter_menu_bloc.dart'; -import 'package:appflowy/plugins/database/grid/application/sort/sort_menu_bloc.dart'; +import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart'; import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart'; import 'package:appflowy/plugins/database/widgets/setting/setting_button.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart'; @@ -30,24 +30,16 @@ class GridSettingBar extends StatelessWidget { fieldController: controller.fieldController, )..add(const GridFilterMenuEvent.initial()), ), - BlocProvider( - create: (context) => SortMenuBloc( + BlocProvider( + create: (context) => SortEditorBloc( viewId: controller.viewId, fieldController: controller.fieldController, - )..add(const SortMenuEvent.initial()), + ), ), ], - child: MultiBlocListener( - listeners: [ - BlocListener( - listenWhen: (p, c) => p.isVisible != c.isVisible, - listener: (context, state) => toggleExtension.toggle(), - ), - BlocListener( - listenWhen: (p, c) => p.isVisible != c.isVisible, - listener: (context, state) => toggleExtension.toggle(), - ), - ], + child: BlocListener( + listenWhen: (p, c) => p.isVisible != c.isVisible, + listener: (context, state) => toggleExtension.toggle(), child: ValueListenableBuilder( valueListenable: controller.isLoading, builder: (context, value, child) { @@ -61,7 +53,7 @@ class GridSettingBar extends StatelessWidget { children: [ const FilterButton(), const HSpace(2), - const SortButton(), + SortButton(toggleExtension: toggleExtension), const HSpace(2), SettingButton( databaseController: controller, diff --git a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/sort_button.dart b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/sort_button.dart index 585be21787..be4740cea0 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/sort_button.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/grid/presentation/widgets/toolbar/sort_button.dart @@ -1,5 +1,6 @@ import 'package:appflowy/generated/locale_keys.g.dart'; -import 'package:appflowy/plugins/database/grid/application/sort/sort_menu_bloc.dart'; +import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart'; +import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart'; import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flowy_infra/size.dart'; @@ -12,7 +13,9 @@ import 'package:appflowy/plugins/database/grid/presentation/layout/sizes.dart'; import '../sort/create_sort_list.dart'; class SortButton extends StatefulWidget { - const SortButton({super.key}); + const SortButton({super.key, required this.toggleExtension}); + + final ToggleExtensionNotifier toggleExtension; @override State createState() => _SortButtonState(); @@ -23,7 +26,7 @@ class _SortButtonState extends State { @override Widget build(BuildContext context) { - return BlocBuilder( + return BlocBuilder( builder: (context, state) { final textColor = state.sortInfos.isEmpty ? AFThemeExtension.of(context).textColor @@ -41,11 +44,10 @@ class _SortButtonState extends State { padding: GridSize.toolbarSettingButtonInsets, radius: Corners.s4Border, onPressed: () { - final bloc = context.read(); - if (bloc.state.sortInfos.isEmpty) { + if (state.sortInfos.isEmpty) { _popoverController.show(); } else { - bloc.add(const SortMenuEvent.toggleMenu()); + widget.toggleExtension.toggle(); } }, ), @@ -54,27 +56,30 @@ class _SortButtonState extends State { ); } - Widget wrapPopover(BuildContext buildContext, Widget child) { + Widget wrapPopover(BuildContext context, Widget child) { return AppFlowyPopover( controller: _popoverController, direction: PopoverDirection.bottomWithLeftAligned, constraints: BoxConstraints.loose(const Size(200, 300)), offset: const Offset(0, 8), triggerActions: PopoverTriggerFlags.none, - child: child, - popupBuilder: (BuildContext context) { - final bloc = buildContext.read(); - return GridCreateSortList( - viewId: bloc.viewId, - fieldController: bloc.fieldController, - onClosed: () => _popoverController.close(), - onCreateSort: () { - if (!bloc.state.isVisible) { - bloc.add(const SortMenuEvent.toggleMenu()); - } - }, + popupBuilder: (popoverContext) { + return BlocProvider.value( + value: context.read(), + child: CreateDatabaseViewSortList( + onTap: () { + if (!widget.toggleExtension.isToggled) { + widget.toggleExtension.toggle(); + } + _popoverController.close(); + }, + ), ); }, + onClose: () => context + .read() + .add(const SortEditorEvent.updateCreateSortFilter("")), + child: child, ); } } diff --git a/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart b/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart index ee3aea478e..fc1c1140ef 100644 --- a/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart +++ b/frontend/appflowy_flutter/lib/plugins/database/widgets/setting/mobile_database_controls.dart @@ -6,7 +6,6 @@ import 'package:appflowy/mobile/presentation/database/view/database_sort_bottom_ import 'package:appflowy/plugins/database/application/database_controller.dart'; import 'package:appflowy/plugins/database/grid/application/filter/filter_menu_bloc.dart'; import 'package:appflowy/plugins/database/grid/application/sort/sort_editor_bloc.dart'; -import 'package:appflowy/plugins/database/grid/application/sort/sort_menu_bloc.dart'; import 'package:appflowy/plugins/database/grid/presentation/grid_page.dart'; import 'package:appflowy/workspace/application/view/view_bloc.dart'; import 'package:easy_localization/easy_localization.dart'; @@ -34,24 +33,16 @@ class MobileDatabaseControls extends StatelessWidget { fieldController: controller.fieldController, )..add(const GridFilterMenuEvent.initial()), ), - BlocProvider( - create: (context) => SortMenuBloc( + BlocProvider( + create: (context) => SortEditorBloc( viewId: controller.viewId, fieldController: controller.fieldController, - )..add(const SortMenuEvent.initial()), + ), ), ], - child: MultiBlocListener( - listeners: [ - BlocListener( - listenWhen: (p, c) => p.isVisible != c.isVisible, - listener: (context, state) => toggleExtension.toggle(), - ), - BlocListener( - listenWhen: (p, c) => p.isVisible != c.isVisible, - listener: (context, state) => toggleExtension.toggle(), - ), - ], + child: BlocListener( + listenWhen: (p, c) => p.isVisible != c.isVisible, + listener: (context, state) => toggleExtension.toggle(), child: ValueListenableBuilder( valueListenable: controller.isLoading, builder: (context, isLoading, child) { @@ -64,7 +55,7 @@ class MobileDatabaseControls extends StatelessWidget { children: [ _DatabaseControlButton( icon: FlowySvgs.sort_ascending_s, - count: context.watch().state.sortInfos.length, + count: context.watch().state.sortInfos.length, onTap: () => _showEditSortPanelFromToolbar( context, controller, @@ -161,12 +152,8 @@ void _showEditSortPanelFromToolbar( showDivider: false, useSafeArea: false, builder: (_) { - return BlocProvider( - create: (_) => SortEditorBloc( - viewId: databaseController.viewId, - fieldController: databaseController.fieldController, - sortInfos: databaseController.fieldController.sortInfos, - )..add(const SortEditorEvent.initial()), + return BlocProvider.value( + value: context.read(), child: const MobileSortEditor(), ); },