diff --git a/app_flowy/lib/workspace/application/app/app_bloc.dart b/app_flowy/lib/workspace/application/app/app_bloc.dart index 0bf12b0065..0675e562cc 100644 --- a/app_flowy/lib/workspace/application/app/app_bloc.dart +++ b/app_flowy/lib/workspace/application/app/app_bloc.dart @@ -20,7 +20,7 @@ class AppBloc extends Bloc { ) async* { yield* event.map(initial: (e) async* { listener.start( - viewsChangeCallback: _handleViewsOrFail, + viewsChangeCallback: _handleViewsChanged, updatedCallback: (app) => add(AppEvent.appDidUpdate(app)), ); yield* _fetchViews(); @@ -37,11 +37,11 @@ class AppBloc extends Bloc { }, ); }, didReceiveViews: (e) async* { - yield state.copyWith(views: e.views); + yield* handleDidReceiveViews(e.views); }, delete: (e) async* { final result = await appManager.delete(); yield result.fold( - (l) => state.copyWith(successOrFailure: left(unit)), + (unit) => state.copyWith(successOrFailure: left(unit)), (error) => state.copyWith(successOrFailure: right(error)), ); }, rename: (e) async* { @@ -61,8 +61,8 @@ class AppBloc extends Bloc { return super.close(); } - void _handleViewsOrFail(Either, WorkspaceError> viewsOrFail) { - viewsOrFail.fold( + void _handleViewsChanged(Either, WorkspaceError> result) { + result.fold( (views) => add(AppEvent.didReceiveViews(views)), (error) { Log.error(error); @@ -70,6 +70,19 @@ class AppBloc extends Bloc { ); } + Stream handleDidReceiveViews(List views) async* { + final selectedView = state.selectedView; + AppState newState = state.copyWith(views: views); + if (selectedView != null) { + final index = views.indexWhere((element) => element.id == selectedView.id); + if (index != -1) { + newState = newState.copyWith(selectedView: null); + } + } + + yield newState; + } + Stream _fetchViews() async* { final viewsOrFailed = await appManager.getViews(); yield viewsOrFailed.fold( diff --git a/app_flowy/lib/workspace/application/doc/doc_bloc.dart b/app_flowy/lib/workspace/application/doc/doc_bloc.dart index 693ac99011..4ec072a938 100644 --- a/app_flowy/lib/workspace/application/doc/doc_bloc.dart +++ b/app_flowy/lib/workspace/application/doc/doc_bloc.dart @@ -1,5 +1,6 @@ import 'dart:convert'; +import 'package:app_flowy/workspace/domain/i_view.dart'; import 'package:flutter_quill/flutter_quill.dart'; import 'package:flowy_log/flowy_log.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; @@ -12,24 +13,50 @@ part 'doc_bloc.freezed.dart'; class DocBloc extends Bloc { final IDoc docManager; + final IViewListener listener; late Document document; late StreamSubscription _subscription; - DocBloc({required this.docManager}) : super(DocState.initial()); + DocBloc({required this.docManager, required this.listener}) : super(DocState.initial()); @override Stream mapEventToState(DocEvent event) async* { - yield* event.map(initial: _initial); + yield* event.map( + initial: _initial, + deleted: (Deleted value) async* { + yield state.copyWith(isDeleted: true); + }, + restore: (Restore value) async* { + yield state.copyWith(isDeleted: false); + }, + ); } @override Future close() async { + await listener.stop(); await _subscription.cancel(); docManager.closeDoc(); return super.close(); } Stream _initial(Initial value) async* { + listener.deletedNotifier.addPublishListener((result) { + result.fold( + (view) => add(const DocEvent.deleted()), + (error) {}, + ); + }); + + listener.restoredNotifier.addPublishListener((result) { + result.fold( + (view) => add(const DocEvent.restore()), + (error) {}, + ); + }); + + listener.start(); + final result = await docManager.readDoc(); yield result.fold( (doc) { @@ -78,15 +105,21 @@ class DocBloc extends Bloc { @freezed class DocEvent with _$DocEvent { const factory DocEvent.initial() = Initial; + const factory DocEvent.deleted() = Deleted; + const factory DocEvent.restore() = Restore; } @freezed class DocState with _$DocState { const factory DocState({ required DocLoadState loadState, + required bool isDeleted, }) = _DocState; - factory DocState.initial() => const DocState(loadState: _Loading()); + factory DocState.initial() => const DocState( + loadState: _Loading(), + isDeleted: false, + ); } @freezed diff --git a/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart b/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart index 4c20ec5ec6..754ff95530 100644 --- a/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart +++ b/app_flowy/lib/workspace/application/doc/doc_bloc.freezed.dart @@ -19,6 +19,14 @@ class _$DocEventTearOff { Initial initial() { return const Initial(); } + + Deleted deleted() { + return const Deleted(); + } + + Restore restore() { + return const Restore(); + } } /// @nodoc @@ -29,22 +37,30 @@ mixin _$DocEvent { @optionalTypeArgs TResult when({ required TResult Function() initial, + required TResult Function() deleted, + required TResult Function() restore, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, + TResult Function()? deleted, + TResult Function()? restore, required TResult orElse(), }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult map({ required TResult Function(Initial value) initial, + required TResult Function(Deleted value) deleted, + required TResult Function(Restore value) restore, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeMap({ TResult Function(Initial value)? initial, + TResult Function(Deleted value)? deleted, + TResult Function(Restore value)? restore, required TResult orElse(), }) => throw _privateConstructorUsedError; @@ -103,6 +119,8 @@ class _$Initial implements Initial { @optionalTypeArgs TResult when({ required TResult Function() initial, + required TResult Function() deleted, + required TResult Function() restore, }) { return initial(); } @@ -111,6 +129,8 @@ class _$Initial implements Initial { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, + TResult Function()? deleted, + TResult Function()? restore, required TResult orElse(), }) { if (initial != null) { @@ -123,6 +143,8 @@ class _$Initial implements Initial { @optionalTypeArgs TResult map({ required TResult Function(Initial value) initial, + required TResult Function(Deleted value) deleted, + required TResult Function(Restore value) restore, }) { return initial(this); } @@ -131,6 +153,8 @@ class _$Initial implements Initial { @optionalTypeArgs TResult maybeMap({ TResult Function(Initial value)? initial, + TResult Function(Deleted value)? deleted, + TResult Function(Restore value)? restore, required TResult orElse(), }) { if (initial != null) { @@ -144,13 +168,188 @@ abstract class Initial implements DocEvent { const factory Initial() = _$Initial; } +/// @nodoc +abstract class $DeletedCopyWith<$Res> { + factory $DeletedCopyWith(Deleted value, $Res Function(Deleted) then) = + _$DeletedCopyWithImpl<$Res>; +} + +/// @nodoc +class _$DeletedCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res> + implements $DeletedCopyWith<$Res> { + _$DeletedCopyWithImpl(Deleted _value, $Res Function(Deleted) _then) + : super(_value, (v) => _then(v as Deleted)); + + @override + Deleted get _value => super._value as Deleted; +} + +/// @nodoc + +class _$Deleted implements Deleted { + const _$Deleted(); + + @override + String toString() { + return 'DocEvent.deleted()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || (other is Deleted); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() deleted, + required TResult Function() restore, + }) { + return deleted(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? deleted, + TResult Function()? restore, + required TResult orElse(), + }) { + if (deleted != null) { + return deleted(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(Initial value) initial, + required TResult Function(Deleted value) deleted, + required TResult Function(Restore value) restore, + }) { + return deleted(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(Initial value)? initial, + TResult Function(Deleted value)? deleted, + TResult Function(Restore value)? restore, + required TResult orElse(), + }) { + if (deleted != null) { + return deleted(this); + } + return orElse(); + } +} + +abstract class Deleted implements DocEvent { + const factory Deleted() = _$Deleted; +} + +/// @nodoc +abstract class $RestoreCopyWith<$Res> { + factory $RestoreCopyWith(Restore value, $Res Function(Restore) then) = + _$RestoreCopyWithImpl<$Res>; +} + +/// @nodoc +class _$RestoreCopyWithImpl<$Res> extends _$DocEventCopyWithImpl<$Res> + implements $RestoreCopyWith<$Res> { + _$RestoreCopyWithImpl(Restore _value, $Res Function(Restore) _then) + : super(_value, (v) => _then(v as Restore)); + + @override + Restore get _value => super._value as Restore; +} + +/// @nodoc + +class _$Restore implements Restore { + const _$Restore(); + + @override + String toString() { + return 'DocEvent.restore()'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || (other is Restore); + } + + @override + int get hashCode => runtimeType.hashCode; + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function() deleted, + required TResult Function() restore, + }) { + return restore(); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function()? deleted, + TResult Function()? restore, + required TResult orElse(), + }) { + if (restore != null) { + return restore(); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(Initial value) initial, + required TResult Function(Deleted value) deleted, + required TResult Function(Restore value) restore, + }) { + return restore(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(Initial value)? initial, + TResult Function(Deleted value)? deleted, + TResult Function(Restore value)? restore, + required TResult orElse(), + }) { + if (restore != null) { + return restore(this); + } + return orElse(); + } +} + +abstract class Restore implements DocEvent { + const factory Restore() = _$Restore; +} + /// @nodoc class _$DocStateTearOff { const _$DocStateTearOff(); - _DocState call({required DocLoadState loadState}) { + _DocState call({required DocLoadState loadState, required bool isDeleted}) { return _DocState( loadState: loadState, + isDeleted: isDeleted, ); } } @@ -161,6 +360,7 @@ const $DocState = _$DocStateTearOff(); /// @nodoc mixin _$DocState { DocLoadState get loadState => throw _privateConstructorUsedError; + bool get isDeleted => throw _privateConstructorUsedError; @JsonKey(ignore: true) $DocStateCopyWith get copyWith => @@ -171,7 +371,7 @@ mixin _$DocState { abstract class $DocStateCopyWith<$Res> { factory $DocStateCopyWith(DocState value, $Res Function(DocState) then) = _$DocStateCopyWithImpl<$Res>; - $Res call({DocLoadState loadState}); + $Res call({DocLoadState loadState, bool isDeleted}); $DocLoadStateCopyWith<$Res> get loadState; } @@ -187,12 +387,17 @@ class _$DocStateCopyWithImpl<$Res> implements $DocStateCopyWith<$Res> { @override $Res call({ Object? loadState = freezed, + Object? isDeleted = freezed, }) { return _then(_value.copyWith( loadState: loadState == freezed ? _value.loadState : loadState // ignore: cast_nullable_to_non_nullable as DocLoadState, + isDeleted: isDeleted == freezed + ? _value.isDeleted + : isDeleted // ignore: cast_nullable_to_non_nullable + as bool, )); } @@ -209,7 +414,7 @@ abstract class _$DocStateCopyWith<$Res> implements $DocStateCopyWith<$Res> { factory _$DocStateCopyWith(_DocState value, $Res Function(_DocState) then) = __$DocStateCopyWithImpl<$Res>; @override - $Res call({DocLoadState loadState}); + $Res call({DocLoadState loadState, bool isDeleted}); @override $DocLoadStateCopyWith<$Res> get loadState; @@ -227,12 +432,17 @@ class __$DocStateCopyWithImpl<$Res> extends _$DocStateCopyWithImpl<$Res> @override $Res call({ Object? loadState = freezed, + Object? isDeleted = freezed, }) { return _then(_DocState( loadState: loadState == freezed ? _value.loadState : loadState // ignore: cast_nullable_to_non_nullable as DocLoadState, + isDeleted: isDeleted == freezed + ? _value.isDeleted + : isDeleted // ignore: cast_nullable_to_non_nullable + as bool, )); } } @@ -240,14 +450,16 @@ class __$DocStateCopyWithImpl<$Res> extends _$DocStateCopyWithImpl<$Res> /// @nodoc class _$_DocState implements _DocState { - const _$_DocState({required this.loadState}); + const _$_DocState({required this.loadState, required this.isDeleted}); @override final DocLoadState loadState; + @override + final bool isDeleted; @override String toString() { - return 'DocState(loadState: $loadState)'; + return 'DocState(loadState: $loadState, isDeleted: $isDeleted)'; } @override @@ -256,12 +468,17 @@ class _$_DocState implements _DocState { (other is _DocState && (identical(other.loadState, loadState) || const DeepCollectionEquality() - .equals(other.loadState, loadState))); + .equals(other.loadState, loadState)) && + (identical(other.isDeleted, isDeleted) || + const DeepCollectionEquality() + .equals(other.isDeleted, isDeleted))); } @override int get hashCode => - runtimeType.hashCode ^ const DeepCollectionEquality().hash(loadState); + runtimeType.hashCode ^ + const DeepCollectionEquality().hash(loadState) ^ + const DeepCollectionEquality().hash(isDeleted); @JsonKey(ignore: true) @override @@ -270,11 +487,14 @@ class _$_DocState implements _DocState { } abstract class _DocState implements DocState { - const factory _DocState({required DocLoadState loadState}) = _$_DocState; + const factory _DocState( + {required DocLoadState loadState, required bool isDeleted}) = _$_DocState; @override DocLoadState get loadState => throw _privateConstructorUsedError; @override + bool get isDeleted => throw _privateConstructorUsedError; + @override @JsonKey(ignore: true) _$DocStateCopyWith<_DocState> get copyWith => throw _privateConstructorUsedError; diff --git a/app_flowy/lib/workspace/application/view/view_bloc.dart b/app_flowy/lib/workspace/application/view/view_bloc.dart index b755ca2788..8c53280f1d 100644 --- a/app_flowy/lib/workspace/application/view/view_bloc.dart +++ b/app_flowy/lib/workspace/application/view/view_bloc.dart @@ -20,7 +20,10 @@ class ViewBloc extends Bloc { Stream mapEventToState(ViewEvent event) async* { yield* event.map( initial: (e) async* { - listener.start(updatedCallback: (result) => add(ViewEvent.viewDidUpdate(result))); + listener.updatedNotifier.addPublishListener((result) { + add(ViewEvent.viewDidUpdate(result)); + }); + listener.start(); yield state; }, setIsEditing: (e) async* { diff --git a/app_flowy/lib/workspace/domain/i_view.dart b/app_flowy/lib/workspace/domain/i_view.dart index 4ecd8b6842..d9639b0bf8 100644 --- a/app_flowy/lib/workspace/domain/i_view.dart +++ b/app_flowy/lib/workspace/domain/i_view.dart @@ -1,9 +1,14 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; import 'package:dartz/dartz.dart'; +import 'package:flowy_infra/notifier.dart'; typedef ViewUpdatedCallback = void Function(Either); +typedef DeleteNotifierValue = Either; +typedef UpdateNotifierValue = Either; +typedef RestoreNotifierValue = Either; + abstract class IView { View get view; @@ -15,7 +20,13 @@ abstract class IView { } abstract class IViewListener { - void start({ViewUpdatedCallback? updatedCallback}); + void start(); + + PublishNotifier get updatedNotifier; + + PublishNotifier get deletedNotifier; + + PublishNotifier get restoredNotifier; Future stop(); } diff --git a/app_flowy/lib/workspace/infrastructure/deps_resolver.dart b/app_flowy/lib/workspace/infrastructure/deps_resolver.dart index 5cb7acd8c9..ebc04ca788 100644 --- a/app_flowy/lib/workspace/infrastructure/deps_resolver.dart +++ b/app_flowy/lib/workspace/infrastructure/deps_resolver.dart @@ -87,7 +87,12 @@ class HomeDepsResolver { ); // Doc - getIt.registerFactoryParam((docId, _) => DocBloc(docManager: getIt(param1: docId))); + getIt.registerFactoryParam( + (view, _) => DocBloc( + docManager: getIt(param1: view.id), + listener: getIt(param1: view), + ), + ); // trash getIt.registerLazySingleton(() => TrashRepo()); diff --git a/app_flowy/lib/workspace/infrastructure/i_view_impl.dart b/app_flowy/lib/workspace/infrastructure/i_view_impl.dart index 207b352e12..b9838c563c 100644 --- a/app_flowy/lib/workspace/infrastructure/i_view_impl.dart +++ b/app_flowy/lib/workspace/infrastructure/i_view_impl.dart @@ -1,5 +1,6 @@ import 'package:app_flowy/workspace/domain/i_view.dart'; import 'package:app_flowy/workspace/infrastructure/repos/view_repo.dart'; +import 'package:flowy_infra/notifier.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; @@ -40,12 +41,21 @@ class IViewListenerImpl extends IViewListener { }); @override - void start({ViewUpdatedCallback? updatedCallback}) { - repo.startWatching(update: updatedCallback); + void start() { + repo.start(); } @override Future stop() async { await repo.close(); } + + @override + PublishNotifier get deletedNotifier => repo.deletedNotifier; + + @override + PublishNotifier get updatedNotifier => repo.updatedNotifier; + + @override + PublishNotifier get restoredNotifier => repo.updatedNotifier; } diff --git a/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart b/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart index 6a9d109d65..371c63333a 100644 --- a/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart +++ b/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart @@ -11,6 +11,7 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/view_update.pb.dart'; import 'package:flowy_sdk/rust_stream.dart'; import 'package:app_flowy/workspace/domain/i_view.dart'; +import 'package:flowy_infra/notifier.dart'; import 'helper.dart'; @@ -52,7 +53,9 @@ class ViewRepository { class ViewListenerRepository { StreamSubscription? _subscription; - ViewUpdatedCallback? _update; + PublishNotifier updatedNotifier = PublishNotifier(); + PublishNotifier deletedNotifier = PublishNotifier(); + PublishNotifier restoredNotifier = PublishNotifier(); late WorkspaceNotificationParser _parser; View view; @@ -60,10 +63,7 @@ class ViewListenerRepository { required this.view, }); - void startWatching({ - ViewUpdatedCallback? update, - }) { - _update = update; + void start() { _parser = WorkspaceNotificationParser( id: view.id, callback: (ty, result) { @@ -77,15 +77,22 @@ class ViewListenerRepository { void _handleObservableType(WorkspaceNotification ty, Either result) { switch (ty) { case WorkspaceNotification.ViewUpdated: - if (_update != null) { - result.fold( - (payload) { - final view = View.fromBuffer(payload); - _update!(left(view)); - }, - (error) => _update!(right(error)), - ); - } + result.fold( + (payload) => updatedNotifier.value = left(View.fromBuffer(payload)), + (error) => updatedNotifier.value = right(error), + ); + break; + case WorkspaceNotification.ViewDeleted: + result.fold( + (payload) => deletedNotifier.value = left(View.fromBuffer(payload)), + (error) => deletedNotifier.value = right(error), + ); + break; + case WorkspaceNotification.ViewRestored: + result.fold( + (payload) => restoredNotifier.value = left(View.fromBuffer(payload)), + (error) => restoredNotifier.value = right(error), + ); break; default: break; diff --git a/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart b/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart index b01371a8d5..f381d9bda6 100644 --- a/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart +++ b/app_flowy/lib/workspace/presentation/stack_page/doc/doc_page.dart @@ -28,7 +28,7 @@ class _DocPageState extends State { @override void initState() { - docBloc = getIt(param1: super.widget.view.id)..add(const DocEvent.initial()); + docBloc = getIt(param1: super.widget.view)..add(const DocEvent.initial()); super.initState(); } diff --git a/app_flowy/lib/workspace/presentation/stack_page/doc/doc_stack_page.dart b/app_flowy/lib/workspace/presentation/stack_page/doc/doc_stack_page.dart index e332060d48..40927f95b1 100644 --- a/app_flowy/lib/workspace/presentation/stack_page/doc/doc_stack_page.dart +++ b/app_flowy/lib/workspace/presentation/stack_page/doc/doc_stack_page.dart @@ -15,7 +15,7 @@ class DocStackContext extends HomeStackContext { DocStackContext({required View view, Key? key}) : _view = view { _listener = getIt(param1: view); - _listener.start(updatedCallback: (result) { + _listener.updatedNotifier.addPublishListener((result) { result.fold( (newView) { _view = newView; @@ -24,6 +24,7 @@ class DocStackContext extends HomeStackContext { (error) {}, ); }); + _listener.start(); } @override diff --git a/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart b/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart index 8aaff4f0ce..c3c9b1d630 100644 --- a/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart +++ b/app_flowy/lib/workspace/presentation/widgets/menu/widget/app/section/section.dart @@ -68,7 +68,7 @@ class ViewSection extends StatelessWidget { @override Widget build(BuildContext context) { - // The ViewListNotifier will be updated after ViewListData changed passed by parent widget + // The ViewSectionNotifier will be updated after AppDataNotifier changed passed by parent widget return ChangeNotifierProxyProvider( create: (_) { final views = Provider.of(context, listen: false).views; diff --git a/app_flowy/packages/flowy_infra/lib/notifier.dart b/app_flowy/packages/flowy_infra/lib/notifier.dart new file mode 100644 index 0000000000..fdd16d0233 --- /dev/null +++ b/app_flowy/packages/flowy_infra/lib/notifier.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; + +class PublishNotifier extends ChangeNotifier { + T? _value; + + set value(T newValue) { + _value = newValue; + notifyListeners(); + } + + T? get currentValue => _value; + + void addPublishListener(void Function(T) callback) { + super.addListener(() { + if (_value != null) { + callback(_value!); + } + }); + } +} diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart index 326ca0300f..78a5cfb8b9 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbenum.dart @@ -19,6 +19,8 @@ class WorkspaceNotification extends $pb.ProtobufEnum { static const WorkspaceNotification AppUpdated = WorkspaceNotification._(21, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppUpdated'); static const WorkspaceNotification AppViewsChanged = WorkspaceNotification._(24, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'AppViewsChanged'); static const WorkspaceNotification ViewUpdated = WorkspaceNotification._(31, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewUpdated'); + static const WorkspaceNotification ViewDeleted = WorkspaceNotification._(32, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewDeleted'); + static const WorkspaceNotification ViewRestored = WorkspaceNotification._(33, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ViewRestored'); static const WorkspaceNotification UserUnauthorized = WorkspaceNotification._(100, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UserUnauthorized'); static const WorkspaceNotification TrashUpdated = WorkspaceNotification._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'TrashUpdated'); @@ -32,6 +34,8 @@ class WorkspaceNotification extends $pb.ProtobufEnum { AppUpdated, AppViewsChanged, ViewUpdated, + ViewDeleted, + ViewRestored, UserUnauthorized, TrashUpdated, ]; diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbjson.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbjson.dart index 99ff02e2ff..335bb4a4a8 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbjson.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/observable.pbjson.dart @@ -21,10 +21,12 @@ const WorkspaceNotification$json = const { const {'1': 'AppUpdated', '2': 21}, const {'1': 'AppViewsChanged', '2': 24}, const {'1': 'ViewUpdated', '2': 31}, + const {'1': 'ViewDeleted', '2': 32}, + const {'1': 'ViewRestored', '2': 33}, const {'1': 'UserUnauthorized', '2': 100}, const {'1': 'TrashUpdated', '2': 1000}, ], }; /// Descriptor for `WorkspaceNotification`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List workspaceNotificationDescriptor = $convert.base64Decode('ChVXb3Jrc3BhY2VOb3RpZmljYXRpb24SCwoHVW5rbm93bhAAEhcKE1VzZXJDcmVhdGVXb3Jrc3BhY2UQChIXChNVc2VyRGVsZXRlV29ya3NwYWNlEAsSFAoQV29ya3NwYWNlVXBkYXRlZBAMEhgKFFdvcmtzcGFjZUxpc3RVcGRhdGVkEA0SGAoUV29ya3NwYWNlQXBwc0NoYW5nZWQQDhIOCgpBcHBVcGRhdGVkEBUSEwoPQXBwVmlld3NDaGFuZ2VkEBgSDwoLVmlld1VwZGF0ZWQQHxIUChBVc2VyVW5hdXRob3JpemVkEGQSEQoMVHJhc2hVcGRhdGVkEOgH'); +final $typed_data.Uint8List workspaceNotificationDescriptor = $convert.base64Decode('ChVXb3Jrc3BhY2VOb3RpZmljYXRpb24SCwoHVW5rbm93bhAAEhcKE1VzZXJDcmVhdGVXb3Jrc3BhY2UQChIXChNVc2VyRGVsZXRlV29ya3NwYWNlEAsSFAoQV29ya3NwYWNlVXBkYXRlZBAMEhgKFFdvcmtzcGFjZUxpc3RVcGRhdGVkEA0SGAoUV29ya3NwYWNlQXBwc0NoYW5nZWQQDhIOCgpBcHBVcGRhdGVkEBUSEwoPQXBwVmlld3NDaGFuZ2VkEBgSDwoLVmlld1VwZGF0ZWQQHxIPCgtWaWV3RGVsZXRlZBAgEhAKDFZpZXdSZXN0b3JlZBAhEhQKEFVzZXJVbmF1dGhvcml6ZWQQZBIRCgxUcmFzaFVwZGF0ZWQQ6Ac='); diff --git a/app_flowy/pubspec.lock b/app_flowy/pubspec.lock index 390f0bdc28..043f67c969 100644 --- a/app_flowy/pubspec.lock +++ b/app_flowy/pubspec.lock @@ -1025,5 +1025,5 @@ packages: source: hosted version: "8.0.0" sdks: - dart: ">=2.14.0 <3.0.0" + dart: ">=2.15.0-116.0.dev <3.0.0" flutter: ">=2.5.0" diff --git a/app_flowy/pubspec.yaml b/app_flowy/pubspec.yaml index 876f7ad3d3..4579b3bd5d 100644 --- a/app_flowy/pubspec.yaml +++ b/app_flowy/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.15.0-116.0.dev <3.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/rust-lib/flowy-workspace/src/notify/observable.rs b/rust-lib/flowy-workspace/src/notify/observable.rs index 43b2e4fe2f..340fc2156a 100644 --- a/rust-lib/flowy-workspace/src/notify/observable.rs +++ b/rust-lib/flowy-workspace/src/notify/observable.rs @@ -2,6 +2,8 @@ use flowy_dart_notify::DartNotifyBuilder; use flowy_derive::ProtoBuf_Enum; const OBSERVABLE_CATEGORY: &'static str = "Workspace"; +// Opti: Using the Rust macro to generate the serde code automatically that can +// be use directly in flutter #[derive(ProtoBuf_Enum, Debug)] pub(crate) enum WorkspaceNotification { Unknown = 0, @@ -13,6 +15,8 @@ pub(crate) enum WorkspaceNotification { AppUpdated = 21, AppViewsChanged = 24, ViewUpdated = 31, + ViewDeleted = 32, + ViewRestored = 33, UserUnauthorized = 100, TrashUpdated = 1000, } diff --git a/rust-lib/flowy-workspace/src/protobuf/model/observable.rs b/rust-lib/flowy-workspace/src/protobuf/model/observable.rs index 5c477c0eff..cf527bb5c5 100644 --- a/rust-lib/flowy-workspace/src/protobuf/model/observable.rs +++ b/rust-lib/flowy-workspace/src/protobuf/model/observable.rs @@ -34,6 +34,8 @@ pub enum WorkspaceNotification { AppUpdated = 21, AppViewsChanged = 24, ViewUpdated = 31, + ViewDeleted = 32, + ViewRestored = 33, UserUnauthorized = 100, TrashUpdated = 1000, } @@ -54,6 +56,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceNotification { 21 => ::std::option::Option::Some(WorkspaceNotification::AppUpdated), 24 => ::std::option::Option::Some(WorkspaceNotification::AppViewsChanged), 31 => ::std::option::Option::Some(WorkspaceNotification::ViewUpdated), + 32 => ::std::option::Option::Some(WorkspaceNotification::ViewDeleted), + 33 => ::std::option::Option::Some(WorkspaceNotification::ViewRestored), 100 => ::std::option::Option::Some(WorkspaceNotification::UserUnauthorized), 1000 => ::std::option::Option::Some(WorkspaceNotification::TrashUpdated), _ => ::std::option::Option::None @@ -71,6 +75,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceNotification { WorkspaceNotification::AppUpdated, WorkspaceNotification::AppViewsChanged, WorkspaceNotification::ViewUpdated, + WorkspaceNotification::ViewDeleted, + WorkspaceNotification::ViewRestored, WorkspaceNotification::UserUnauthorized, WorkspaceNotification::TrashUpdated, ]; @@ -101,37 +107,42 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceNotification { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x10observable.proto*\xff\x01\n\x15WorkspaceNotification\x12\x0b\n\x07\ + \n\x10observable.proto*\xa2\x02\n\x15WorkspaceNotification\x12\x0b\n\x07\ Unknown\x10\0\x12\x17\n\x13UserCreateWorkspace\x10\n\x12\x17\n\x13UserDe\ leteWorkspace\x10\x0b\x12\x14\n\x10WorkspaceUpdated\x10\x0c\x12\x18\n\ \x14WorkspaceListUpdated\x10\r\x12\x18\n\x14WorkspaceAppsChanged\x10\x0e\ \x12\x0e\n\nAppUpdated\x10\x15\x12\x13\n\x0fAppViewsChanged\x10\x18\x12\ - \x0f\n\x0bViewUpdated\x10\x1f\x12\x14\n\x10UserUnauthorized\x10d\x12\x11\ - \n\x0cTrashUpdated\x10\xe8\x07J\xed\x03\n\x06\x12\x04\0\0\x0e\x01\n\x08\ - \n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x0e\x01\n\n\n\ - \x03\x05\0\x01\x12\x03\x02\x05\x1a\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\ - \x04\x10\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x0b\n\x0c\n\x05\x05\ - \0\x02\0\x02\x12\x03\x03\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\ - \x04\x1d\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x17\n\x0c\n\x05\ - \x05\0\x02\x01\x02\x12\x03\x04\x1a\x1c\n\x0b\n\x04\x05\0\x02\x02\x12\x03\ - \x05\x04\x1d\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x17\n\x0c\n\ - \x05\x05\0\x02\x02\x02\x12\x03\x05\x1a\x1c\n\x0b\n\x04\x05\0\x02\x03\x12\ - \x03\x06\x04\x1a\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x14\n\x0c\ - \n\x05\x05\0\x02\x03\x02\x12\x03\x06\x17\x19\n\x0b\n\x04\x05\0\x02\x04\ - \x12\x03\x07\x04\x1e\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x18\n\ - \x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x1b\x1d\n\x0b\n\x04\x05\0\x02\ - \x05\x12\x03\x08\x04\x1e\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\ - \x18\n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x1b\x1d\n\x0b\n\x04\x05\0\ - \x02\x06\x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\ - \x0e\n\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\t\x11\x13\n\x0b\n\x04\x05\0\ - \x02\x07\x12\x03\n\x04\x19\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\ - \x13\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\n\x16\x18\n\x0b\n\x04\x05\0\ - \x02\x08\x12\x03\x0b\x04\x15\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\ - \x04\x0f\n\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x12\x14\n\x0b\n\x04\ - \x05\0\x02\t\x12\x03\x0c\x04\x1b\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x0c\ - \x04\x14\n\x0c\n\x05\x05\0\x02\t\x02\x12\x03\x0c\x17\x1a\n\x0b\n\x04\x05\ - \0\x02\n\x12\x03\r\x04\x18\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x10\ - \n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\x13\x17b\x06proto3\ + \x0f\n\x0bViewUpdated\x10\x1f\x12\x0f\n\x0bViewDeleted\x10\x20\x12\x10\n\ + \x0cViewRestored\x10!\x12\x14\n\x10UserUnauthorized\x10d\x12\x11\n\x0cTr\ + ashUpdated\x10\xe8\x07J\xbf\x04\n\x06\x12\x04\0\0\x10\x01\n\x08\n\x01\ + \x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x10\x01\n\n\n\x03\x05\ + \0\x01\x12\x03\x02\x05\x1a\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x10\n\ + \x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x0b\n\x0c\n\x05\x05\0\x02\0\ + \x02\x12\x03\x03\x0e\x0f\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x1d\n\ + \x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x17\n\x0c\n\x05\x05\0\x02\ + \x01\x02\x12\x03\x04\x1a\x1c\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\ + \x1d\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x17\n\x0c\n\x05\x05\0\ + \x02\x02\x02\x12\x03\x05\x1a\x1c\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\ + \x04\x1a\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x14\n\x0c\n\x05\ + \x05\0\x02\x03\x02\x12\x03\x06\x17\x19\n\x0b\n\x04\x05\0\x02\x04\x12\x03\ + \x07\x04\x1e\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x18\n\x0c\n\ + \x05\x05\0\x02\x04\x02\x12\x03\x07\x1b\x1d\n\x0b\n\x04\x05\0\x02\x05\x12\ + \x03\x08\x04\x1e\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x18\n\x0c\ + \n\x05\x05\0\x02\x05\x02\x12\x03\x08\x1b\x1d\n\x0b\n\x04\x05\0\x02\x06\ + \x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\x0e\n\x0c\ + \n\x05\x05\0\x02\x06\x02\x12\x03\t\x11\x13\n\x0b\n\x04\x05\0\x02\x07\x12\ + \x03\n\x04\x19\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\x13\n\x0c\n\ + \x05\x05\0\x02\x07\x02\x12\x03\n\x16\x18\n\x0b\n\x04\x05\0\x02\x08\x12\ + \x03\x0b\x04\x15\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x0f\n\x0c\ + \n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x12\x14\n\x0b\n\x04\x05\0\x02\t\x12\ + \x03\x0c\x04\x15\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x0c\x04\x0f\n\x0c\n\ + \x05\x05\0\x02\t\x02\x12\x03\x0c\x12\x14\n\x0b\n\x04\x05\0\x02\n\x12\x03\ + \r\x04\x16\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x10\n\x0c\n\x05\x05\ + \0\x02\n\x02\x12\x03\r\x13\x15\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\ + \x1b\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x0e\x04\x14\n\x0c\n\x05\x05\0\ + \x02\x0b\x02\x12\x03\x0e\x17\x1a\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x0f\ + \x04\x18\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x0f\x04\x10\n\x0c\n\x05\ + \x05\0\x02\x0c\x02\x12\x03\x0f\x13\x17b\x06proto3\ "; static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT; diff --git a/rust-lib/flowy-workspace/src/protobuf/proto/observable.proto b/rust-lib/flowy-workspace/src/protobuf/proto/observable.proto index 3663543a75..349a4b8db2 100644 --- a/rust-lib/flowy-workspace/src/protobuf/proto/observable.proto +++ b/rust-lib/flowy-workspace/src/protobuf/proto/observable.proto @@ -10,6 +10,8 @@ enum WorkspaceNotification { AppUpdated = 21; AppViewsChanged = 24; ViewUpdated = 31; + ViewDeleted = 32; + ViewRestored = 33; UserUnauthorized = 100; TrashUpdated = 1000; } diff --git a/rust-lib/flowy-workspace/src/services/view_controller.rs b/rust-lib/flowy-workspace/src/services/view_controller.rs index c0ecc5d69a..aeca182f27 100644 --- a/rust-lib/flowy-workspace/src/services/view_controller.rs +++ b/rust-lib/flowy-workspace/src/services/view_controller.rs @@ -22,6 +22,7 @@ use flowy_document::{ use crate::{entities::trash::TrashType, errors::WorkspaceResult}; +use crate::entities::trash::TrashIdentifiers; use futures::{FutureExt, StreamExt}; use std::{collections::HashSet, sync::Arc}; @@ -263,16 +264,26 @@ async fn handle_trash_event( let db_result = database.db_connection(); match event { - TrashEvent::NewTrash(identifiers, ret) | TrashEvent::Putback(identifiers, ret) => { + TrashEvent::NewTrash(identifiers, ret) => { let result = || { let conn = &*db_result?; - let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| { - for identifier in identifiers.items { - let view_table = ViewTableSql::read_view(&identifier.id, conn)?; - let _ = notify_views_changed(&view_table.belong_to_id, trash_can.clone(), conn)?; - } - Ok(()) - })?; + let view_tables = get_view_table_from(identifiers, conn)?; + for view_table in view_tables { + let _ = notify_views_changed(&view_table.belong_to_id, trash_can.clone(), conn)?; + notify_dart(view_table, WorkspaceNotification::ViewDeleted); + } + Ok::<(), WorkspaceError>(()) + }; + let _ = ret.send(result()).await; + }, + TrashEvent::Putback(identifiers, ret) => { + let result = || { + let conn = &*db_result?; + let view_tables = get_view_table_from(identifiers, conn)?; + for view_table in view_tables { + let _ = notify_views_changed(&view_table.belong_to_id, trash_can.clone(), conn)?; + notify_dart(view_table, WorkspaceNotification::ViewRestored); + } Ok::<(), WorkspaceError>(()) }; let _ = ret.send(result()).await; @@ -302,6 +313,26 @@ async fn handle_trash_event( } } +fn get_view_table_from( + identifiers: TrashIdentifiers, + conn: &SqliteConnection, +) -> Result, WorkspaceError> { + let mut view_tables = vec![]; + let _ = conn.immediate_transaction::<_, WorkspaceError, _>(|| { + for identifier in identifiers.items { + let view_table = ViewTableSql::read_view(&identifier.id, conn)?; + view_tables.push(view_table); + } + Ok(()) + })?; + Ok(view_tables) +} + +fn notify_dart(view_table: ViewTable, notification: WorkspaceNotification) { + let view: View = view_table.into(); + send_dart_notification(&view.id, notification).payload(view).send(); +} + #[tracing::instrument(skip(belong_to_id, trash_can, conn), fields(view_count), err)] fn notify_views_changed(belong_to_id: &str, trash_can: Arc, conn: &SqliteConnection) -> WorkspaceResult<()> { let repeated_view = read_local_belonging_view(belong_to_id, trash_can.clone(), conn)?;