chore: clean up db sort blocs (#4725)

This commit is contained in:
Richard Shiue
2024-02-24 22:55:22 +08:00
committed by GitHub
parent cea1c17b76
commit f5cc6521fb
13 changed files with 284 additions and 623 deletions

View File

@ -1116,7 +1116,7 @@ extension AppFlowyDatabaseTest on WidgetTester {
/// Must call [tapSortMenuInSettingBar] first. /// Must call [tapSortMenuInSettingBar] first.
Future<void> tapAllSortButton() async { Future<void> tapAllSortButton() async {
await tapButton(find.byType(DatabaseDeleteSortButton)); await tapButton(find.byType(DeleteAllSortsButton));
} }
Future<void> scrollOptionFilterListByOffset(Offset offset) async { Future<void> scrollOptionFilterListByOffset(Offset offset) async {

View File

@ -121,8 +121,8 @@ class _Header extends StatelessWidget {
if (state.newSortFieldId != null && state.newSortCondition != null) { if (state.newSortFieldId != null && state.newSortCondition != null) {
context.read<SortEditorBloc>().add( context.read<SortEditorBloc>().add(
SortEditorEvent.createSort( SortEditorEvent.createSort(
state.newSortFieldId!, fieldId: state.newSortFieldId!,
state.newSortCondition!, condition: state.newSortCondition!,
), ),
); );
} }
@ -531,9 +531,8 @@ class _SortDetailContent extends StatelessWidget {
} else { } else {
context.read<SortEditorBloc>().add( context.read<SortEditorBloc>().add(
SortEditorEvent.editSort( SortEditorEvent.editSort(
sortInfo!.sortId, sortId: sortInfo!.sortId,
null, condition: newCondition,
newCondition,
), ),
); );
} }
@ -545,9 +544,8 @@ class _SortDetailContent extends StatelessWidget {
} else { } else {
context.read<SortEditorBloc>().add( context.read<SortEditorBloc>().add(
SortEditorEvent.editSort( SortEditorEvent.editSort(
sortInfo!.sortId, sortId: sortInfo!.sortId,
newFieldId, fieldId: newFieldId,
null,
), ),
); );
} }

View File

@ -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<CreateSortEvent, CreateSortState> {
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<FieldInfo>)? _onFieldFn;
void _dispatch() {
on<CreateSortEvent>(
(event, emit) async {
event.when(
initial: () {
_startListening();
},
didReceiveFields: (List<FieldInfo> 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<FieldInfo> _filterFields(
List<FieldInfo> fields,
String filterText,
) {
final List<FieldInfo> 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<FlowyResult<void, FlowyError>> _createDefaultSort(
FieldInfo field,
) async {
final result = await _sortBackendSvc.insertSort(
fieldId: field.id,
condition: SortConditionPB.Ascending,
);
return result;
}
@override
Future<void> 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<FieldInfo> 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<FieldInfo> creatableFields,
required List<FieldInfo> allFields,
required bool didCreateSort,
}) = _CreateSortState;
factory CreateSortState.initial(List<FieldInfo> fields) {
return CreateSortState(
filterText: "",
creatableFields: getCreatableSorts(fields),
allFields: fields,
didCreateSort: false,
);
}
}

View File

@ -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/application/sort/sort_service.dart';
import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_info.dart'; import 'package:appflowy/plugins/database/grid/presentation/widgets/sort/sort_info.dart';
import 'package:appflowy_backend/log.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/protobuf.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbserver.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'util.dart';
part 'sort_editor_bloc.freezed.dart'; part 'sort_editor_bloc.freezed.dart';
class SortEditorBloc extends Bloc<SortEditorEvent, SortEditorState> { class SortEditorBloc extends Bloc<SortEditorEvent, SortEditorState> {
SortEditorBloc({ SortEditorBloc({
required this.viewId, required this.viewId,
required this.fieldController, required this.fieldController,
required List<SortInfo> sortInfos,
}) : _sortBackendSvc = SortBackendService(viewId: viewId), }) : _sortBackendSvc = SortBackendService(viewId: viewId),
super( super(
SortEditorState.initial( SortEditorState.initial(
sortInfos, fieldController.sortInfos,
fieldController.fieldInfos, fieldController.fieldInfos,
), ),
) { ) {
_dispatch(); _dispatch();
_startListening();
} }
final String viewId; final String viewId;
@ -41,9 +38,6 @@ class SortEditorBloc extends Bloc<SortEditorEvent, SortEditorState> {
on<SortEditorEvent>( on<SortEditorEvent>(
(event, emit) async { (event, emit) async {
await event.when( await event.when(
initial: () {
_startListening();
},
didReceiveFields: (List<FieldInfo> fields) { didReceiveFields: (List<FieldInfo> fields) {
emit( emit(
state.copyWith( state.copyWith(
@ -52,10 +46,16 @@ class SortEditorBloc extends Bloc<SortEditorEvent, SortEditorState> {
), ),
); );
}, },
createSort: (String fieldId, SortConditionPB condition) async { updateCreateSortFilter: (text) {
emit(state.copyWith(filter: text));
},
createSort: (
String fieldId,
SortConditionPB? condition,
) async {
final result = await _sortBackendSvc.insertSort( final result = await _sortBackendSvc.insertSort(
fieldId: fieldId, fieldId: fieldId,
condition: condition, condition: condition ?? SortConditionPB.Ascending,
); );
result.fold((l) => {}, (err) => Log.error(err)); result.fold((l) => {}, (err) => Log.error(err));
}, },
@ -142,24 +142,25 @@ class SortEditorBloc extends Bloc<SortEditorEvent, SortEditorState> {
@freezed @freezed
class SortEditorEvent with _$SortEditorEvent { class SortEditorEvent with _$SortEditorEvent {
const factory SortEditorEvent.initial() = _Initial;
const factory SortEditorEvent.didReceiveFields(List<FieldInfo> fieldInfos) = const factory SortEditorEvent.didReceiveFields(List<FieldInfo> fieldInfos) =
_DidReceiveFields; _DidReceiveFields;
const factory SortEditorEvent.didReceiveSorts(List<SortInfo> sortInfos) = const factory SortEditorEvent.didReceiveSorts(List<SortInfo> sortInfos) =
_DidReceiveSorts; _DidReceiveSorts;
const factory SortEditorEvent.createSort( const factory SortEditorEvent.updateCreateSortFilter(String text) =
String fieldId, _UpdateCreateSortFilter;
SortConditionPB condition, const factory SortEditorEvent.createSort({
) = _CreateSort; required String fieldId,
const factory SortEditorEvent.editSort( SortConditionPB? condition,
String sortId, }) = _CreateSort;
const factory SortEditorEvent.editSort({
required String sortId,
String? fieldId, String? fieldId,
SortConditionPB? condition, SortConditionPB? condition,
) = _EditSort; }) = _EditSort;
const factory SortEditorEvent.deleteSort(SortInfo sortInfo) = _DeleteSort;
const factory SortEditorEvent.deleteAllSorts() = _DeleteAllSorts;
const factory SortEditorEvent.reorderSort(int oldIndex, int newIndex) = const factory SortEditorEvent.reorderSort(int oldIndex, int newIndex) =
_ReorderSort; _ReorderSort;
const factory SortEditorEvent.deleteSort(SortInfo sortInfo) = _DeleteSort;
const factory SortEditorEvent.deleteAllSorts() = _DeleteAllSorts;
} }
@freezed @freezed
@ -168,6 +169,7 @@ class SortEditorState with _$SortEditorState {
required List<SortInfo> sortInfos, required List<SortInfo> sortInfos,
required List<FieldInfo> creatableFields, required List<FieldInfo> creatableFields,
required List<FieldInfo> allFields, required List<FieldInfo> allFields,
required String filter,
}) = _SortEditorState; }) = _SortEditorState;
factory SortEditorState.initial( factory SortEditorState.initial(
@ -178,6 +180,13 @@ class SortEditorState with _$SortEditorState {
creatableFields: getCreatableSorts(fields), creatableFields: getCreatableSorts(fields),
allFields: fields, allFields: fields,
sortInfos: sortInfos, sortInfos: sortInfos,
filter: "",
); );
} }
} }
List<FieldInfo> getCreatableSorts(List<FieldInfo> fieldInfos) {
final List<FieldInfo> creatableFields = List.from(fieldInfos);
creatableFields.retainWhere((element) => element.canCreateSort);
return creatableFields;
}

View File

@ -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<SortMenuEvent, SortMenuState> {
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<SortInfo>)? _onSortChangeFn;
void Function(List<FieldInfo>)? _onFieldFn;
void _dispatch() {
on<SortMenuEvent>(
(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<FieldInfo> 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<void> 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<SortInfo> sortInfos) =
_DidReceiveSortInfos;
const factory SortMenuEvent.didReceiveFields(List<FieldInfo> fields) =
_DidReceiveFields;
const factory SortMenuEvent.toggleMenu() = _SetMenuVisibility;
}
@freezed
class SortMenuState with _$SortMenuState {
const factory SortMenuState({
required String viewId,
required List<SortInfo> sortInfos,
required List<FieldInfo> fields,
required List<FieldInfo> creatableFields,
required bool isVisible,
}) = _SortMenuState;
factory SortMenuState.initial(
String viewId,
List<SortInfo> sortInfos,
List<FieldInfo> fields,
) =>
SortMenuState(
viewId: viewId,
sortInfos: sortInfos,
fields: fields,
creatableFields: getCreatableSorts(fields),
isVisible: false,
);
}

View File

@ -1,7 +0,0 @@
import 'package:appflowy/plugins/database/application/field/field_info.dart';
List<FieldInfo> getCreatableSorts(List<FieldInfo> fieldInfos) {
final List<FieldInfo> creatableFields = List.from(fieldInfos);
creatableFields.retainWhere((element) => element.canCreateSort);
return creatableFields;
}

View File

@ -1,8 +1,7 @@
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.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/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/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy/util/field_type_extension.dart'; import 'package:appflowy/util/field_type_extension.dart';
import 'package:easy_localization/easy_localization.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/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class GridCreateSortList extends StatefulWidget { class CreateDatabaseViewSortList extends StatelessWidget {
const GridCreateSortList({ const CreateDatabaseViewSortList({
super.key, super.key,
required this.viewId, required this.onTap,
required this.fieldController,
required this.onClosed,
this.onCreateSort,
}); });
final String viewId; final VoidCallback onTap;
final FieldController fieldController;
final VoidCallback onClosed;
final VoidCallback? onCreateSort;
@override
State<StatefulWidget> createState() => _GridCreateSortListState();
}
class _GridCreateSortListState extends State<GridCreateSortList> {
late CreateSortBloc editBloc;
@override
void initState() {
editBloc = CreateSortBloc(
viewId: widget.viewId,
fieldController: widget.fieldController,
)..add(const CreateSortEvent.initial());
super.initState();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider.value( return BlocBuilder<SortEditorBloc, SortEditorState>(
value: editBloc, builder: (context, state) {
child: BlocListener<CreateSortBloc, CreateSortState>( final filter = state.filter.toLowerCase();
listener: (context, state) { final cells = state.creatableFields
if (state.didCreateSort) { .where((field) => field.field.name.toLowerCase().contains(filter))
widget.onClosed(); .map((fieldInfo) {
} return GridSortPropertyCell(
}, fieldInfo: fieldInfo,
child: BlocBuilder<CreateSortBloc, CreateSortState>( onTap: () {
builder: (context, state) { context
final cells = state.creatableFields.map((fieldInfo) { .read<SortEditorBloc>()
return SizedBox( .add(SortEditorEvent.createSort(fieldId: fieldInfo.id));
height: GridSize.popoverItemHeight, onTap.call();
child: GridSortPropertyCell( },
fieldInfo: fieldInfo, );
onTap: (fieldInfo) => createSort(fieldInfo), }).toList();
),
);
}).toList();
final List<Widget> slivers = [ final List<Widget> slivers = [
SliverPersistentHeader( SliverPersistentHeader(
pinned: true, pinned: true,
delegate: _SortTextFieldDelegate(), delegate: _SortTextFieldDelegate(),
), ),
SliverToBoxAdapter( SliverToBoxAdapter(
child: ListView.separated( 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(
shrinkWrap: true, shrinkWrap: true,
slivers: slivers, itemCount: cells.length,
physics: StyledScrollPhysics(), 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 { class _SortTextFieldDelegate extends SliverPersistentHeaderDelegate {
@ -127,8 +85,8 @@ class _SortTextFieldDelegate extends SliverPersistentHeaderDelegate {
hintText: LocaleKeys.grid_settings_sortBy.tr(), hintText: LocaleKeys.grid_settings_sortBy.tr(),
onChanged: (text) { onChanged: (text) {
context context
.read<CreateSortBloc>() .read<SortEditorBloc>()
.add(CreateSortEvent.didReceiveFilterText(text)); .add(SortEditorEvent.updateCreateSortFilter(text));
}, },
), ),
); );
@ -141,9 +99,7 @@ class _SortTextFieldDelegate extends SliverPersistentHeaderDelegate {
double get minExtent => fixHeight; double get minExtent => fixHeight;
@override @override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) { bool shouldRebuild(covariant oldDelegate) => false;
return false;
}
} }
class GridSortPropertyCell extends StatelessWidget { class GridSortPropertyCell extends StatelessWidget {
@ -154,20 +110,23 @@ class GridSortPropertyCell extends StatelessWidget {
}); });
final FieldInfo fieldInfo; final FieldInfo fieldInfo;
final Function(FieldInfo) onTap; final VoidCallback onTap;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FlowyButton( return SizedBox(
hoverColor: AFThemeExtension.of(context).lightGreyHover, height: GridSize.popoverItemHeight,
text: FlowyText.medium( child: FlowyButton(
fieldInfo.name, hoverColor: AFThemeExtension.of(context).lightGreyHover,
color: AFThemeExtension.of(context).textColor, text: FlowyText.medium(
), fieldInfo.name,
onTap: () => onTap(fieldInfo), color: AFThemeExtension.of(context).textColor,
leftIcon: FlowySvg( ),
fieldInfo.fieldType.svgData, onTap: onTap,
color: Theme.of(context).iconTheme.color, leftIcon: FlowySvg(
fieldInfo.fieldType.svgData,
color: Theme.of(context).iconTheme.color,
),
), ),
); );
} }

View File

@ -27,16 +27,15 @@ class SortChoiceButton extends StatelessWidget {
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.transparent, color: Colors.transparent,
border: Border.fromBorderSide( border: Border.fromBorderSide(
BorderSide( BorderSide(color: Theme.of(context).dividerColor),
color: AFThemeExtension.of(context).toggleOffFill,
),
), ),
borderRadius: const BorderRadius.all(Radius.circular(14)), borderRadius: BorderRadius.all(radius),
), ),
useIntrinsicWidth: true, useIntrinsicWidth: true,
text: FlowyText( text: FlowyText(
text, text,
color: AFThemeExtension.of(context).textColor, color: AFThemeExtension.of(context).textColor,
overflow: TextOverflow.ellipsis,
), ),
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
radius: BorderRadius.all(radius), radius: BorderRadius.all(radius),

View File

@ -2,9 +2,7 @@ import 'dart:io';
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/generated/locale_keys.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/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/plugins/database/grid/presentation/layout/sizes.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/sort_entities.pbenum.dart';
import 'package:appflowy_popover/appflowy_popover.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:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:math' as math;
import 'create_sort_list.dart'; import 'create_sort_list.dart';
import 'order_panel.dart'; import 'order_panel.dart';
@ -22,16 +19,7 @@ import 'sort_choice_button.dart';
import 'sort_info.dart'; import 'sort_info.dart';
class SortEditor extends StatefulWidget { class SortEditor extends StatefulWidget {
const SortEditor({ const SortEditor({super.key});
super.key,
required this.viewId,
required this.fieldController,
required this.sortInfos,
});
final String viewId;
final FieldController fieldController;
final List<SortInfo> sortInfos;
@override @override
State<SortEditor> createState() => _SortEditorState(); State<SortEditor> createState() => _SortEditorState();
@ -42,69 +30,57 @@ class _SortEditorState extends State<SortEditor> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocBuilder<SortEditorBloc, SortEditorState>(
create: (context) => SortEditorBloc( builder: (context, state) {
viewId: widget.viewId, final sortInfos = state.sortInfos;
fieldController: widget.fieldController, return ReorderableListView.builder(
sortInfos: widget.sortInfos, onReorder: (oldIndex, newIndex) => context
)..add(const SortEditorEvent.initial()), .read<SortEditorBloc>()
child: BlocBuilder<SortEditorBloc, SortEditorState>( .add(SortEditorEvent.reorderSort(oldIndex, newIndex)),
builder: (context, state) { itemCount: state.sortInfos.length,
final sortInfos = state.sortInfos; itemBuilder: (context, index) => DatabaseSortItem(
key: ValueKey(sortInfos[index].sortId),
return ReorderableListView.builder( index: index,
onReorder: (oldIndex, newIndex) => context sortInfo: sortInfos[index],
.read<SortEditorBloc>() popoverMutex: popoverMutex,
.add(SortEditorEvent.reorderSort(oldIndex, newIndex)), ),
itemCount: state.sortInfos.length, proxyDecorator: (child, index, animation) => Material(
itemBuilder: (context, index) => Padding( color: Colors.transparent,
key: ValueKey(sortInfos[index].sortId), child: Stack(
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<SortEditorBloc>(),
child: child,
),
MouseRegion(
cursor: Platform.isWindows
? SystemMouseCursors.click
: SystemMouseCursors.grabbing,
child: const SizedBox.expand(),
),
],
),
),
shrinkWrap: true,
buildDefaultDragHandles: false,
footer: Row(
children: [ children: [
Flexible( BlocProvider.value(
child: DatabaseAddSortButton( value: context.read<SortEditorBloc>(),
viewId: widget.viewId, child: child,
fieldController: widget.fieldController,
popoverMutex: popoverMutex,
),
), ),
const HSpace(6), MouseRegion(
Flexible( cursor: Platform.isWindows
child: DatabaseDeleteSortButton( ? SystemMouseCursors.click
popoverMutex: popoverMutex, : 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final deleteButton = FlowyIconButton( return Container(
width: 26, padding: const EdgeInsets.symmetric(vertical: 6),
onPressed: () => context color: Theme.of(context).cardColor,
.read<SortEditorBloc>() child: Row(
.add(SortEditorEvent.deleteSort(sortInfo)), children: [
iconPadding: const EdgeInsets.all(5), ReorderableDragStartListener(
hoverColor: AFThemeExtension.of(context).lightGreyHover, index: index,
icon: child: MouseRegion(
FlowySvg(FlowySvgs.close_s, color: Theme.of(context).iconTheme.color), cursor: Platform.isWindows
); ? SystemMouseCursors.click
: SystemMouseCursors.grab,
return Row( child: SizedBox(
children: [ width: 14 + 12,
ReorderableDragStartListener( height: 14,
index: index, child: FlowySvg(
child: MouseRegion( FlowySvgs.drag_element_s,
cursor: Platform.isWindows size: const Size.square(14),
? SystemMouseCursors.click color: Theme.of(context).iconTheme.color,
: SystemMouseCursors.grab, ),
child: SizedBox(
width: 14,
height: 14,
child: FlowySvg(
FlowySvgs.drag_element_s,
color: Theme.of(context).iconTheme.color,
), ),
), ),
), ),
), Flexible(
const HSpace(6), fit: FlexFit.tight,
SizedBox( child: SizedBox(
height: 26, height: 26,
child: SortChoiceButton( child: SortChoiceButton(
text: sortInfo.fieldInfo.name, text: sortInfo.fieldInfo.name,
editable: false, editable: false,
),
),
), ),
), const HSpace(6),
const HSpace(6), Flexible(
SizedBox( fit: FlexFit.tight,
height: 26, child: SizedBox(
child: DatabaseSortItemOrderButton( height: 26,
sortInfo: sortInfo, child: SortConditionButton(
popoverMutex: popoverMutex, sortInfo: sortInfo,
popoverMutex: popoverMutex,
),
),
), ),
), const HSpace(6),
const Spacer(), FlowyIconButton(
const HSpace(6), width: 26,
deleteButton, onPressed: () {
], context
.read<SortEditorBloc>()
.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 { extension SortConditionExtension on SortConditionPB {
String get title { String get title {
switch (this) { return switch (this) {
case SortConditionPB.Descending: SortConditionPB.Ascending => LocaleKeys.grid_sort_ascending.tr(),
return LocaleKeys.grid_sort_descending.tr(); SortConditionPB.Descending => LocaleKeys.grid_sort_descending.tr(),
default: _ => throw UnimplementedError(),
return LocaleKeys.grid_sort_ascending.tr(); };
}
} }
} }
class DatabaseAddSortButton extends StatefulWidget { class DatabaseAddSortButton extends StatefulWidget {
const DatabaseAddSortButton({ const DatabaseAddSortButton({
super.key, super.key,
required this.viewId, required this.disable,
required this.fieldController,
required this.popoverMutex, required this.popoverMutex,
}); });
final String viewId; final bool disable;
final FieldController fieldController;
final PopoverMutex popoverMutex; final PopoverMutex popoverMutex;
@override @override
@ -213,32 +198,36 @@ class _DatabaseAddSortButtonState extends State<DatabaseAddSortButton> {
mutex: widget.popoverMutex, mutex: widget.popoverMutex,
direction: PopoverDirection.bottomWithLeftAligned, direction: PopoverDirection.bottomWithLeftAligned,
constraints: BoxConstraints.loose(const Size(200, 300)), constraints: BoxConstraints.loose(const Size(200, 300)),
offset: const Offset(0, 8), offset: const Offset(-6, 8),
triggerActions: PopoverTriggerFlags.none, triggerActions: PopoverTriggerFlags.none,
asBarrier: true, asBarrier: true,
popupBuilder: (popoverContext) {
return BlocProvider.value(
value: context.read<SortEditorBloc>(),
child: CreateDatabaseViewSortList(
onTap: () => _popoverController.close(),
),
);
},
onClose: () => context
.read<SortEditorBloc>()
.add(const SortEditorEvent.updateCreateSortFilter("")),
child: SizedBox( child: SizedBox(
height: GridSize.popoverItemHeight, height: GridSize.popoverItemHeight,
child: FlowyButton( child: FlowyButton(
hoverColor: AFThemeExtension.of(context).greyHover, hoverColor: AFThemeExtension.of(context).greyHover,
disable: getCreatableSorts(widget.fieldController.fieldInfos).isEmpty, disable: widget.disable,
text: FlowyText.medium(LocaleKeys.grid_sort_addSort.tr()), text: FlowyText.medium(LocaleKeys.grid_sort_addSort.tr()),
onTap: () => _popoverController.show(), onTap: () => _popoverController.show(),
leftIcon: const FlowySvg(FlowySvgs.add_s), leftIcon: const FlowySvg(FlowySvgs.add_s),
), ),
), ),
popupBuilder: (BuildContext context) {
return GridCreateSortList(
viewId: widget.viewId,
fieldController: widget.fieldController,
onClosed: () => _popoverController.close(),
);
},
); );
} }
} }
class DatabaseDeleteSortButton extends StatelessWidget { class DeleteAllSortsButton extends StatelessWidget {
const DatabaseDeleteSortButton({super.key, required this.popoverMutex}); const DeleteAllSortsButton({super.key, required this.popoverMutex});
final PopoverMutex popoverMutex; final PopoverMutex popoverMutex;
@ -264,8 +253,8 @@ class DatabaseDeleteSortButton extends StatelessWidget {
} }
} }
class DatabaseSortItemOrderButton extends StatefulWidget { class SortConditionButton extends StatefulWidget {
const DatabaseSortItemOrderButton({ const SortConditionButton({
super.key, super.key,
required this.popoverMutex, required this.popoverMutex,
required this.sortInfo, required this.sortInfo,
@ -275,21 +264,14 @@ class DatabaseSortItemOrderButton extends StatefulWidget {
final SortInfo sortInfo; final SortInfo sortInfo;
@override @override
State<DatabaseSortItemOrderButton> createState() => State<SortConditionButton> createState() => _SortConditionButtonState();
_DatabaseSortItemOrderButtonState();
} }
class _DatabaseSortItemOrderButtonState class _SortConditionButtonState extends State<SortConditionButton> {
extends State<DatabaseSortItemOrderButton> {
final PopoverController popoverController = PopoverController(); final PopoverController popoverController = PopoverController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final arrow = Transform.rotate(
angle: -math.pi / 2,
child: const FlowySvg(FlowySvgs.arrow_left_s),
);
return AppFlowyPopover( return AppFlowyPopover(
controller: popoverController, controller: popoverController,
mutex: widget.popoverMutex, mutex: widget.popoverMutex,
@ -301,9 +283,8 @@ class _DatabaseSortItemOrderButtonState
onCondition: (condition) { onCondition: (condition) {
context.read<SortEditorBloc>().add( context.read<SortEditorBloc>().add(
SortEditorEvent.editSort( SortEditorEvent.editSort(
widget.sortInfo.sortId, sortId: widget.sortInfo.sortId,
null, condition: condition,
condition,
), ),
); );
popoverController.close(); popoverController.close();
@ -312,7 +293,10 @@ class _DatabaseSortItemOrderButtonState
}, },
child: SortChoiceButton( child: SortChoiceButton(
text: widget.sortInfo.sortPB.condition.title, text: widget.sortInfo.sortPB.condition.title,
rightIcon: arrow, rightIcon: FlowySvg(
FlowySvgs.arrow_down_s,
color: Theme.of(context).iconTheme.color,
),
onTap: () => popoverController.show(), onTap: () => popoverController.show(),
), ),
); );

View File

@ -1,6 +1,8 @@
import 'dart:math' as math;
import 'package:appflowy/generated/flowy_svgs.g.dart'; import 'package:appflowy/generated/flowy_svgs.g.dart';
import 'package:appflowy/plugins/database/application/field/field_controller.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:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.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:appflowy/generated/locale_keys.g.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'dart:math' as math;
import 'sort_choice_button.dart'; import 'sort_choice_button.dart';
import 'sort_editor.dart'; import 'sort_editor.dart';
import 'sort_info.dart'; import 'sort_info.dart';
@ -24,12 +24,12 @@ class SortMenu extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider<SortMenuBloc>( return BlocProvider(
create: (context) => SortMenuBloc( create: (context) => SortEditorBloc(
viewId: fieldController.viewId, viewId: fieldController.viewId,
fieldController: fieldController, fieldController: fieldController,
)..add(const SortMenuEvent.initial()), ),
child: BlocBuilder<SortMenuBloc, SortMenuState>( child: BlocBuilder<SortEditorBloc, SortEditorState>(
builder: (context, state) { builder: (context, state) {
if (state.sortInfos.isEmpty) { if (state.sortInfos.isEmpty) {
return const SizedBox.shrink(); return const SizedBox.shrink();
@ -42,10 +42,9 @@ class SortMenu extends StatelessWidget {
offset: const Offset(0, 5), offset: const Offset(0, 5),
margin: const EdgeInsets.fromLTRB(6.0, 0.0, 6.0, 6.0), margin: const EdgeInsets.fromLTRB(6.0, 0.0, 6.0, 6.0),
popupBuilder: (BuildContext popoverContext) { popupBuilder: (BuildContext popoverContext) {
return SortEditor( return BlocProvider.value(
viewId: state.viewId, value: context.read<SortEditorBloc>(),
fieldController: context.read<SortMenuBloc>().fieldController, child: const SortEditor(),
sortInfos: state.sortInfos,
); );
}, },
child: SortChoiceChip(sortInfos: state.sortInfos), child: SortChoiceChip(sortInfos: state.sortInfos),

View File

@ -1,6 +1,6 @@
import 'package:appflowy/plugins/database/application/database_controller.dart'; 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/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/grid/presentation/grid_page.dart';
import 'package:appflowy/plugins/database/widgets/setting/setting_button.dart'; import 'package:appflowy/plugins/database/widgets/setting/setting_button.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart'; import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@ -30,24 +30,16 @@ class GridSettingBar extends StatelessWidget {
fieldController: controller.fieldController, fieldController: controller.fieldController,
)..add(const GridFilterMenuEvent.initial()), )..add(const GridFilterMenuEvent.initial()),
), ),
BlocProvider<SortMenuBloc>( BlocProvider<SortEditorBloc>(
create: (context) => SortMenuBloc( create: (context) => SortEditorBloc(
viewId: controller.viewId, viewId: controller.viewId,
fieldController: controller.fieldController, fieldController: controller.fieldController,
)..add(const SortMenuEvent.initial()), ),
), ),
], ],
child: MultiBlocListener( child: BlocListener<GridFilterMenuBloc, GridFilterMenuState>(
listeners: [ listenWhen: (p, c) => p.isVisible != c.isVisible,
BlocListener<GridFilterMenuBloc, GridFilterMenuState>( listener: (context, state) => toggleExtension.toggle(),
listenWhen: (p, c) => p.isVisible != c.isVisible,
listener: (context, state) => toggleExtension.toggle(),
),
BlocListener<SortMenuBloc, SortMenuState>(
listenWhen: (p, c) => p.isVisible != c.isVisible,
listener: (context, state) => toggleExtension.toggle(),
),
],
child: ValueListenableBuilder<bool>( child: ValueListenableBuilder<bool>(
valueListenable: controller.isLoading, valueListenable: controller.isLoading,
builder: (context, value, child) { builder: (context, value, child) {
@ -61,7 +53,7 @@ class GridSettingBar extends StatelessWidget {
children: [ children: [
const FilterButton(), const FilterButton(),
const HSpace(2), const HSpace(2),
const SortButton(), SortButton(toggleExtension: toggleExtension),
const HSpace(2), const HSpace(2),
SettingButton( SettingButton(
databaseController: controller, databaseController: controller,

View File

@ -1,5 +1,6 @@
import 'package:appflowy/generated/locale_keys.g.dart'; 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:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/size.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'; import '../sort/create_sort_list.dart';
class SortButton extends StatefulWidget { class SortButton extends StatefulWidget {
const SortButton({super.key}); const SortButton({super.key, required this.toggleExtension});
final ToggleExtensionNotifier toggleExtension;
@override @override
State<SortButton> createState() => _SortButtonState(); State<SortButton> createState() => _SortButtonState();
@ -23,7 +26,7 @@ class _SortButtonState extends State<SortButton> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocBuilder<SortMenuBloc, SortMenuState>( return BlocBuilder<SortEditorBloc, SortEditorState>(
builder: (context, state) { builder: (context, state) {
final textColor = state.sortInfos.isEmpty final textColor = state.sortInfos.isEmpty
? AFThemeExtension.of(context).textColor ? AFThemeExtension.of(context).textColor
@ -41,11 +44,10 @@ class _SortButtonState extends State<SortButton> {
padding: GridSize.toolbarSettingButtonInsets, padding: GridSize.toolbarSettingButtonInsets,
radius: Corners.s4Border, radius: Corners.s4Border,
onPressed: () { onPressed: () {
final bloc = context.read<SortMenuBloc>(); if (state.sortInfos.isEmpty) {
if (bloc.state.sortInfos.isEmpty) {
_popoverController.show(); _popoverController.show();
} else { } else {
bloc.add(const SortMenuEvent.toggleMenu()); widget.toggleExtension.toggle();
} }
}, },
), ),
@ -54,27 +56,30 @@ class _SortButtonState extends State<SortButton> {
); );
} }
Widget wrapPopover(BuildContext buildContext, Widget child) { Widget wrapPopover(BuildContext context, Widget child) {
return AppFlowyPopover( return AppFlowyPopover(
controller: _popoverController, controller: _popoverController,
direction: PopoverDirection.bottomWithLeftAligned, direction: PopoverDirection.bottomWithLeftAligned,
constraints: BoxConstraints.loose(const Size(200, 300)), constraints: BoxConstraints.loose(const Size(200, 300)),
offset: const Offset(0, 8), offset: const Offset(0, 8),
triggerActions: PopoverTriggerFlags.none, triggerActions: PopoverTriggerFlags.none,
child: child, popupBuilder: (popoverContext) {
popupBuilder: (BuildContext context) { return BlocProvider.value(
final bloc = buildContext.read<SortMenuBloc>(); value: context.read<SortEditorBloc>(),
return GridCreateSortList( child: CreateDatabaseViewSortList(
viewId: bloc.viewId, onTap: () {
fieldController: bloc.fieldController, if (!widget.toggleExtension.isToggled) {
onClosed: () => _popoverController.close(), widget.toggleExtension.toggle();
onCreateSort: () { }
if (!bloc.state.isVisible) { _popoverController.close();
bloc.add(const SortMenuEvent.toggleMenu()); },
} ),
},
); );
}, },
onClose: () => context
.read<SortEditorBloc>()
.add(const SortEditorEvent.updateCreateSortFilter("")),
child: child,
); );
} }
} }

View File

@ -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/application/database_controller.dart';
import 'package:appflowy/plugins/database/grid/application/filter/filter_menu_bloc.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_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/plugins/database/grid/presentation/grid_page.dart';
import 'package:appflowy/workspace/application/view/view_bloc.dart'; import 'package:appflowy/workspace/application/view/view_bloc.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
@ -34,24 +33,16 @@ class MobileDatabaseControls extends StatelessWidget {
fieldController: controller.fieldController, fieldController: controller.fieldController,
)..add(const GridFilterMenuEvent.initial()), )..add(const GridFilterMenuEvent.initial()),
), ),
BlocProvider<SortMenuBloc>( BlocProvider<SortEditorBloc>(
create: (context) => SortMenuBloc( create: (context) => SortEditorBloc(
viewId: controller.viewId, viewId: controller.viewId,
fieldController: controller.fieldController, fieldController: controller.fieldController,
)..add(const SortMenuEvent.initial()), ),
), ),
], ],
child: MultiBlocListener( child: BlocListener<GridFilterMenuBloc, GridFilterMenuState>(
listeners: [ listenWhen: (p, c) => p.isVisible != c.isVisible,
BlocListener<GridFilterMenuBloc, GridFilterMenuState>( listener: (context, state) => toggleExtension.toggle(),
listenWhen: (p, c) => p.isVisible != c.isVisible,
listener: (context, state) => toggleExtension.toggle(),
),
BlocListener<SortMenuBloc, SortMenuState>(
listenWhen: (p, c) => p.isVisible != c.isVisible,
listener: (context, state) => toggleExtension.toggle(),
),
],
child: ValueListenableBuilder<bool>( child: ValueListenableBuilder<bool>(
valueListenable: controller.isLoading, valueListenable: controller.isLoading,
builder: (context, isLoading, child) { builder: (context, isLoading, child) {
@ -64,7 +55,7 @@ class MobileDatabaseControls extends StatelessWidget {
children: [ children: [
_DatabaseControlButton( _DatabaseControlButton(
icon: FlowySvgs.sort_ascending_s, icon: FlowySvgs.sort_ascending_s,
count: context.watch<SortMenuBloc>().state.sortInfos.length, count: context.watch<SortEditorBloc>().state.sortInfos.length,
onTap: () => _showEditSortPanelFromToolbar( onTap: () => _showEditSortPanelFromToolbar(
context, context,
controller, controller,
@ -161,12 +152,8 @@ void _showEditSortPanelFromToolbar(
showDivider: false, showDivider: false,
useSafeArea: false, useSafeArea: false,
builder: (_) { builder: (_) {
return BlocProvider( return BlocProvider.value(
create: (_) => SortEditorBloc( value: context.read<SortEditorBloc>(),
viewId: databaseController.viewId,
fieldController: databaseController.fieldController,
sortInfos: databaseController.fieldController.sortInfos,
)..add(const SortEditorEvent.initial()),
child: const MobileSortEditor(), child: const MobileSortEditor(),
); );
}, },