mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: remove single field listener (#5113)
This commit is contained in:
parent
3de2a20278
commit
6e3c731162
@ -322,6 +322,7 @@ class MobileRowDetailPageContentState
|
||||
BlocProvider<RowBannerBloc>(
|
||||
create: (context) => RowBannerBloc(
|
||||
viewId: viewId,
|
||||
fieldController: fieldController,
|
||||
rowMeta: rowController.rowMeta,
|
||||
)..add(const RowBannerEvent.initial()),
|
||||
child: BlocBuilder<RowBannerBloc, RowBannerState>(
|
||||
|
@ -62,7 +62,7 @@ class _QuickEditFieldState extends State<QuickEditField> {
|
||||
viewId: widget.viewId,
|
||||
fieldController: widget.fieldController,
|
||||
field: widget.fieldInfo.field,
|
||||
)..add(const FieldEditorEvent.initial()),
|
||||
),
|
||||
child: BlocConsumer<FieldEditorBloc, FieldEditorState>(
|
||||
listenWhen: (previous, current) =>
|
||||
previous.field.name != current.field.name,
|
||||
|
@ -87,15 +87,11 @@ class RelationCellBloc extends Bloc<RelationCellEvent, RelationCellState> {
|
||||
}
|
||||
},
|
||||
onCellFieldChanged: (field) {
|
||||
// hack: SingleFieldListener receives notification before
|
||||
// FieldController's copy is updated.
|
||||
Future.delayed(const Duration(milliseconds: 50), () {
|
||||
if (!isClosed) {
|
||||
final RelationTypeOptionPB typeOption =
|
||||
cellController.getTypeOption(RelationTypeOptionDataParser());
|
||||
add(RelationCellEvent.didUpdateRelationTypeOption(typeOption));
|
||||
}
|
||||
});
|
||||
if (!isClosed) {
|
||||
final RelationTypeOptionPB typeOption =
|
||||
cellController.getTypeOption(RelationTypeOptionDataParser());
|
||||
add(RelationCellEvent.didUpdateRelationTypeOption(typeOption));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ import 'dart:async';
|
||||
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/domain/cell_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/type_option/type_option_data_parser.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database/domain/row_meta_listener.dart';
|
||||
@ -49,7 +48,6 @@ class CellController<T, D> {
|
||||
_rowCache = rowCache,
|
||||
_cellDataLoader = cellDataLoader,
|
||||
_cellDataPersistence = cellDataPersistence,
|
||||
_fieldListener = SingleFieldListener(fieldId: cellContext.fieldId),
|
||||
_cellDataNotifier =
|
||||
CellDataNotifier(value: rowCache.cellCache.get(cellContext)) {
|
||||
_startListening();
|
||||
@ -64,10 +62,9 @@ class CellController<T, D> {
|
||||
|
||||
CellListener? _cellListener;
|
||||
RowMetaListener? _rowMetaListener;
|
||||
SingleFieldListener? _fieldListener;
|
||||
CellDataNotifier<T?>? _cellDataNotifier;
|
||||
|
||||
void Function(FieldPB field)? _onCellFieldChanged;
|
||||
void Function(FieldInfo field)? _onCellFieldChanged;
|
||||
VoidCallback? _onRowMetaChanged;
|
||||
Timer? _loadDataOperation;
|
||||
Timer? _saveDataOperation;
|
||||
@ -105,8 +102,9 @@ class CellController<T, D> {
|
||||
);
|
||||
|
||||
// 2. Listen on the field event and load the cell data if needed.
|
||||
_fieldListener?.start(
|
||||
onFieldChanged: (fieldPB) {
|
||||
_fieldController.addSingleFieldListener(
|
||||
fieldId,
|
||||
onFieldChanged: (fieldInfo) {
|
||||
// reloadOnFieldChanged should be true if you want to reload the cell
|
||||
// data when the corresponding field is changed.
|
||||
// For example:
|
||||
@ -114,7 +112,7 @@ class CellController<T, D> {
|
||||
if (_cellDataLoader.reloadOnFieldChange) {
|
||||
_loadData();
|
||||
}
|
||||
_onCellFieldChanged?.call(fieldPB);
|
||||
_onCellFieldChanged?.call(fieldInfo);
|
||||
},
|
||||
);
|
||||
|
||||
@ -132,7 +130,7 @@ class CellController<T, D> {
|
||||
/// Add a new listener
|
||||
VoidCallback? addListener({
|
||||
required void Function(T?) onCellChanged,
|
||||
void Function(FieldPB field)? onCellFieldChanged,
|
||||
void Function(FieldInfo field)? onCellFieldChanged,
|
||||
VoidCallback? onRowMetaChanged,
|
||||
}) {
|
||||
_onCellFieldChanged = onCellFieldChanged;
|
||||
@ -220,9 +218,6 @@ class CellController<T, D> {
|
||||
await _cellListener?.stop();
|
||||
_cellListener = null;
|
||||
|
||||
await _fieldListener?.stop();
|
||||
_fieldListener = null;
|
||||
|
||||
_loadDataOperation?.cancel();
|
||||
_saveDataOperation?.cancel();
|
||||
_cellDataNotifier?.dispose();
|
||||
|
@ -69,6 +69,7 @@ class _GridSortNotifier extends ChangeNotifier {
|
||||
}
|
||||
|
||||
typedef OnReceiveUpdateFields = void Function(List<FieldInfo>);
|
||||
typedef OnReceiveField = void Function(FieldInfo);
|
||||
typedef OnReceiveFields = void Function(List<FieldInfo>);
|
||||
typedef OnReceiveFilters = void Function(List<FilterInfo>);
|
||||
typedef OnReceiveSorts = void Function(List<SortInfo>);
|
||||
@ -686,6 +687,31 @@ class FieldController {
|
||||
}
|
||||
}
|
||||
|
||||
void addSingleFieldListener(
|
||||
String fieldId, {
|
||||
required OnReceiveField onFieldChanged,
|
||||
bool Function()? listenWhen,
|
||||
}) {
|
||||
void key(List<FieldInfo> fieldInfos) {
|
||||
final fieldInfo = fieldInfos.firstWhereOrNull(
|
||||
(fieldInfo) => fieldInfo.id == fieldId,
|
||||
);
|
||||
if (fieldInfo != null) {
|
||||
onFieldChanged(fieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void callback() {
|
||||
if (listenWhen != null && listenWhen() == false) {
|
||||
return;
|
||||
}
|
||||
key(fieldInfos);
|
||||
}
|
||||
|
||||
_fieldCallbacks[key] = callback;
|
||||
_fieldNotifier.addListener(callback);
|
||||
}
|
||||
|
||||
void removeListener({
|
||||
OnReceiveFields? onFieldsListener,
|
||||
OnReceiveSorts? onSortsListener,
|
||||
@ -713,6 +739,25 @@ class FieldController {
|
||||
}
|
||||
}
|
||||
|
||||
void removeSingleFieldListener({
|
||||
required String fieldId,
|
||||
required OnReceiveField onFieldChanged,
|
||||
}) {
|
||||
void key(List<FieldInfo> fieldInfos) {
|
||||
final fieldInfo = fieldInfos.firstWhereOrNull(
|
||||
(fieldInfo) => fieldInfo.id == fieldId,
|
||||
);
|
||||
if (fieldInfo != null) {
|
||||
onFieldChanged(fieldInfo);
|
||||
}
|
||||
}
|
||||
|
||||
final callback = _fieldCallbacks.remove(key);
|
||||
if (callback != null) {
|
||||
_fieldNotifier.removeListener(callback);
|
||||
}
|
||||
}
|
||||
|
||||
/// Stop listeners, dispose notifiers and clear listener callbacks
|
||||
Future<void> dispose() async {
|
||||
if (_isDisposed) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/plugins/database/domain/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_settings_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
@ -22,7 +21,6 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
this.onFieldInserted,
|
||||
required FieldPB field,
|
||||
}) : fieldId = field.id,
|
||||
_singleFieldListener = SingleFieldListener(fieldId: field.id),
|
||||
fieldService = FieldBackendService(
|
||||
viewId: viewId,
|
||||
fieldId: field.id,
|
||||
@ -30,19 +28,25 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
fieldSettingsService = FieldSettingsBackendService(viewId: viewId),
|
||||
super(FieldEditorState(field: FieldInfo.initial(field))) {
|
||||
_dispatch();
|
||||
_startListening();
|
||||
_init();
|
||||
}
|
||||
|
||||
final String viewId;
|
||||
final String fieldId;
|
||||
final FieldController fieldController;
|
||||
final SingleFieldListener _singleFieldListener;
|
||||
final FieldBackendService fieldService;
|
||||
final FieldSettingsBackendService fieldSettingsService;
|
||||
final void Function(String newFieldId)? onFieldInserted;
|
||||
|
||||
late final OnReceiveField _listener;
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_singleFieldListener.stop();
|
||||
fieldController.removeSingleFieldListener(
|
||||
fieldId: fieldId,
|
||||
onFieldChanged: _listener,
|
||||
);
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@ -50,19 +54,8 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
on<FieldEditorEvent>(
|
||||
(event, emit) async {
|
||||
await event.when(
|
||||
initial: () async {
|
||||
_singleFieldListener.start(
|
||||
onFieldChanged: (field) {
|
||||
if (!isClosed) {
|
||||
add(FieldEditorEvent.didReceiveFieldChanged(fieldId));
|
||||
}
|
||||
},
|
||||
);
|
||||
add(FieldEditorEvent.didReceiveFieldChanged(fieldId));
|
||||
},
|
||||
didReceiveFieldChanged: (fieldId) async {
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
emit(state.copyWith(field: fieldController.getField(fieldId)!));
|
||||
didUpdateField: (fieldInfo) {
|
||||
emit(state.copyWith(field: fieldInfo));
|
||||
},
|
||||
switchFieldType: (fieldType) async {
|
||||
await fieldService.updateType(fieldType: fieldType);
|
||||
@ -111,6 +104,28 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
);
|
||||
}
|
||||
|
||||
void _startListening() {
|
||||
_listener = (field) {
|
||||
if (!isClosed) {
|
||||
add(FieldEditorEvent.didUpdateField(field));
|
||||
}
|
||||
};
|
||||
fieldController.addSingleFieldListener(
|
||||
fieldId,
|
||||
onFieldChanged: _listener,
|
||||
);
|
||||
}
|
||||
|
||||
void _init() async {
|
||||
await Future.delayed(const Duration(milliseconds: 50));
|
||||
if (!isClosed) {
|
||||
final field = fieldController.getField(fieldId);
|
||||
if (field != null) {
|
||||
add(FieldEditorEvent.didUpdateField(field));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _logIfError(FlowyResult<void, FlowyError> result) {
|
||||
result.fold(
|
||||
(l) => null,
|
||||
@ -121,9 +136,8 @@ class FieldEditorBloc extends Bloc<FieldEditorEvent, FieldEditorState> {
|
||||
|
||||
@freezed
|
||||
class FieldEditorEvent with _$FieldEditorEvent {
|
||||
const factory FieldEditorEvent.initial() = _InitialField;
|
||||
const factory FieldEditorEvent.didReceiveFieldChanged(final String fieldId) =
|
||||
_DidReceiveFieldChanged;
|
||||
const factory FieldEditorEvent.didUpdateField(final FieldInfo fieldInfo) =
|
||||
_DidUpdateField;
|
||||
const factory FieldEditorEvent.switchFieldType(final FieldType fieldType) =
|
||||
_SwitchFieldType;
|
||||
const factory FieldEditorEvent.updateTypeOption(
|
||||
|
@ -1,4 +1,4 @@
|
||||
import 'package:appflowy/plugins/database/domain/field_listener.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/domain/field_service.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
@ -13,6 +13,7 @@ part 'row_banner_bloc.freezed.dart';
|
||||
class RowBannerBloc extends Bloc<RowBannerEvent, RowBannerState> {
|
||||
RowBannerBloc({
|
||||
required this.viewId,
|
||||
required this.fieldController,
|
||||
required RowMetaPB rowMeta,
|
||||
}) : _rowBackendSvc = RowBackendService(viewId: viewId),
|
||||
_metaListener = RowMetaListener(rowMeta.id),
|
||||
@ -21,16 +22,13 @@ class RowBannerBloc extends Bloc<RowBannerEvent, RowBannerState> {
|
||||
}
|
||||
|
||||
final String viewId;
|
||||
final FieldController fieldController;
|
||||
final RowBackendService _rowBackendSvc;
|
||||
final RowMetaListener _metaListener;
|
||||
SingleFieldListener? _fieldListener;
|
||||
|
||||
@override
|
||||
Future<void> close() async {
|
||||
await _metaListener.stop();
|
||||
await _fieldListener?.stop();
|
||||
_fieldListener = null;
|
||||
|
||||
return super.close();
|
||||
}
|
||||
|
||||
@ -66,11 +64,11 @@ class RowBannerBloc extends Bloc<RowBannerEvent, RowBannerState> {
|
||||
fieldOrError.fold(
|
||||
(primaryField) {
|
||||
if (!isClosed) {
|
||||
_fieldListener = SingleFieldListener(fieldId: primaryField.id);
|
||||
_fieldListener?.start(
|
||||
fieldController.addSingleFieldListener(
|
||||
primaryField.id,
|
||||
onFieldChanged: (updatedField) {
|
||||
if (!isClosed) {
|
||||
add(RowBannerEvent.didReceiveFieldUpdate(updatedField));
|
||||
add(RowBannerEvent.didReceiveFieldUpdate(updatedField.field));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -2,54 +2,11 @@ import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:appflowy/core/notification/grid_notification.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_result/appflowy_result.dart';
|
||||
import 'package:flowy_infra/notifier.dart';
|
||||
|
||||
typedef UpdateFieldNotifiedValue = FieldPB;
|
||||
|
||||
class SingleFieldListener {
|
||||
SingleFieldListener({required this.fieldId});
|
||||
|
||||
final String fieldId;
|
||||
|
||||
void Function(UpdateFieldNotifiedValue)? _updateFieldNotifier;
|
||||
DatabaseNotificationListener? _listener;
|
||||
|
||||
void start({
|
||||
required void Function(UpdateFieldNotifiedValue) onFieldChanged,
|
||||
}) {
|
||||
_updateFieldNotifier = onFieldChanged;
|
||||
_listener = DatabaseNotificationListener(
|
||||
objectId: fieldId,
|
||||
handler: _handler,
|
||||
);
|
||||
}
|
||||
|
||||
void _handler(
|
||||
DatabaseNotification ty,
|
||||
FlowyResult<Uint8List, FlowyError> result,
|
||||
) {
|
||||
switch (ty) {
|
||||
case DatabaseNotification.DidUpdateField:
|
||||
result.fold(
|
||||
(payload) => _updateFieldNotifier?.call(FieldPB.fromBuffer(payload)),
|
||||
(error) => Log.error(error),
|
||||
);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _listener?.stop();
|
||||
_updateFieldNotifier = null;
|
||||
}
|
||||
}
|
||||
|
||||
typedef UpdateFieldsNotifiedValue
|
||||
= FlowyResult<DatabaseFieldChangesetPB, FlowyError>;
|
||||
|
||||
|
@ -72,7 +72,7 @@ class _FieldEditorState extends State<FieldEditor> {
|
||||
field: widget.field,
|
||||
fieldController: widget.fieldController,
|
||||
onFieldInserted: widget.onFieldInserted,
|
||||
)..add(const FieldEditorEvent.initial()),
|
||||
),
|
||||
child: _currentPage == FieldEditorPage.details
|
||||
? _fieldDetails()
|
||||
: _fieldGeneral(),
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database/application/cell/cell_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_banner_bloc.dart';
|
||||
import 'package:appflowy/plugins/database/application/row/row_controller.dart';
|
||||
import 'package:appflowy/plugins/database/widgets/cell/editable_cell_builder.dart';
|
||||
@ -21,10 +22,12 @@ const _kBannerActionHeight = 40.0;
|
||||
class RowBanner extends StatefulWidget {
|
||||
const RowBanner({
|
||||
super.key,
|
||||
required this.fieldController,
|
||||
required this.rowController,
|
||||
required this.cellBuilder,
|
||||
});
|
||||
|
||||
final FieldController fieldController;
|
||||
final RowController rowController;
|
||||
final EditableCellBuilder cellBuilder;
|
||||
|
||||
@ -47,6 +50,7 @@ class _RowBannerState extends State<RowBanner> {
|
||||
return BlocProvider<RowBannerBloc>(
|
||||
create: (context) => RowBannerBloc(
|
||||
viewId: widget.rowController.viewId,
|
||||
fieldController: widget.fieldController,
|
||||
rowMeta: widget.rowController.rowMeta,
|
||||
)..add(const RowBannerEvent.initial()),
|
||||
child: MouseRegion(
|
||||
|
@ -57,6 +57,7 @@ class _RowDetailPageState extends State<RowDetailPage> {
|
||||
controller: scrollController,
|
||||
children: [
|
||||
RowBanner(
|
||||
fieldController: widget.databaseController.fieldController,
|
||||
rowController: widget.rowController,
|
||||
cellBuilder: cellBuilder,
|
||||
),
|
||||
|
@ -38,7 +38,7 @@ void main() {
|
||||
viewId: context.gridView.id,
|
||||
field: fieldInfo.field,
|
||||
fieldController: context.fieldController,
|
||||
)..add(const FieldEditorEvent.initial());
|
||||
);
|
||||
await boardResponseFuture();
|
||||
|
||||
editorBloc.add(const FieldEditorEvent.renameField('Hello world'));
|
||||
|
@ -18,7 +18,7 @@ void main() {
|
||||
final fieldInfo = context.singleSelectFieldContext();
|
||||
editorBloc = context.makeFieldEditor(
|
||||
fieldInfo: fieldInfo,
|
||||
)..add(const FieldEditorEvent.initial());
|
||||
);
|
||||
|
||||
await boardResponseFuture();
|
||||
});
|
||||
|
@ -96,8 +96,7 @@ class BoardTestContext {
|
||||
|
||||
Future<FieldEditorBloc> createField(FieldType fieldType) async {
|
||||
final editorBloc =
|
||||
await createFieldEditor(databaseController: _boardDataController)
|
||||
..add(const FieldEditorEvent.initial());
|
||||
await createFieldEditor(databaseController: _boardDataController);
|
||||
await gridResponseFuture();
|
||||
editorBloc.add(FieldEditorEvent.switchFieldType(fieldType));
|
||||
await gridResponseFuture();
|
||||
|
@ -11,7 +11,7 @@ Future<FieldEditorBloc> createEditorBloc(AppFlowyGridTest gridTest) async {
|
||||
viewId: context.gridView.id,
|
||||
fieldController: context.fieldController,
|
||||
field: fieldInfo.field,
|
||||
)..add(const FieldEditorEvent.initial());
|
||||
);
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
@ -31,8 +31,7 @@ class GridTestContext {
|
||||
|
||||
Future<FieldEditorBloc> createField(FieldType fieldType) async {
|
||||
final editorBloc =
|
||||
await createFieldEditor(databaseController: gridController)
|
||||
..add(const FieldEditorEvent.initial());
|
||||
await createFieldEditor(databaseController: gridController);
|
||||
await gridResponseFuture();
|
||||
editorBloc.add(FieldEditorEvent.switchFieldType(fieldType));
|
||||
await gridResponseFuture();
|
||||
|
Loading…
Reference in New Issue
Block a user