chore: add reorder bloc test (#1354)

* chore: add reorder bloc test

* chore: add trash test

Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
Nathan.fooo 2022-10-25 16:51:51 +08:00 committed by GitHub
parent 6fb677d346
commit 67e4a759c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 315 additions and 108 deletions

View File

@ -10,14 +10,16 @@ import 'package:app_flowy/plugins/trash/application/trash_listener.dart';
part 'trash_bloc.freezed.dart'; part 'trash_bloc.freezed.dart';
class TrashBloc extends Bloc<TrashEvent, TrashState> { class TrashBloc extends Bloc<TrashEvent, TrashState> {
final TrashService service; final TrashService _service;
final TrashListener listener; final TrashListener _listener;
TrashBloc({required this.service, required this.listener}) TrashBloc()
: super(TrashState.init()) { : _service = TrashService(),
_listener = TrashListener(),
super(TrashState.init()) {
on<TrashEvent>((event, emit) async { on<TrashEvent>((event, emit) async {
await event.map(initial: (e) async { await event.map(initial: (e) async {
listener.start(trashUpdated: _listenTrashUpdated); _listener.start(trashUpdated: _listenTrashUpdated);
final result = await service.readTrash(); final result = await _service.readTrash();
emit(result.fold( emit(result.fold(
(object) => state.copyWith( (object) => state.copyWith(
objects: object.items, successOrFailure: left(unit)), objects: object.items, successOrFailure: left(unit)),
@ -26,17 +28,17 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
}, didReceiveTrash: (e) async { }, didReceiveTrash: (e) async {
emit(state.copyWith(objects: e.trash)); emit(state.copyWith(objects: e.trash));
}, putback: (e) async { }, putback: (e) async {
final result = await service.putback(e.trashId); final result = await _service.putback(e.trashId);
await _handleResult(result, emit); await _handleResult(result, emit);
}, delete: (e) async { }, delete: (e) async {
final result = final result =
await service.deleteViews([Tuple2(e.trash.id, e.trash.ty)]); await _service.deleteViews([Tuple2(e.trash.id, e.trash.ty)]);
await _handleResult(result, emit); await _handleResult(result, emit);
}, deleteAll: (e) async { }, deleteAll: (e) async {
final result = await service.deleteAll(); final result = await _service.deleteAll();
await _handleResult(result, emit); await _handleResult(result, emit);
}, restoreAll: (e) async { }, restoreAll: (e) async {
final result = await service.restoreAll(); final result = await _service.restoreAll();
await _handleResult(result, emit); await _handleResult(result, emit);
}); });
}); });
@ -63,7 +65,7 @@ class TrashBloc extends Bloc<TrashEvent, TrashState> {
@override @override
Future<void> close() async { Future<void> close() async {
await listener.close(); await _listener.close();
return super.close(); return super.close();
} }
} }

View File

@ -92,14 +92,6 @@ void _resolveFolderDeps(GetIt getIt) {
), ),
); );
//Menu
getIt.registerFactoryParam<MenuBloc, UserProfilePB, String>(
(user, workspaceId) => MenuBloc(
workspaceId: workspaceId,
listener: getIt<WorkspaceListener>(param1: user, param2: workspaceId),
),
);
getIt.registerFactoryParam<MenuUserBloc, UserProfilePB, void>( getIt.registerFactoryParam<MenuUserBloc, UserProfilePB, void>(
(user, _) => MenuUserBloc(user), (user, _) => MenuUserBloc(user),
); );
@ -123,10 +115,7 @@ void _resolveFolderDeps(GetIt getIt) {
getIt.registerLazySingleton<TrashService>(() => TrashService()); getIt.registerLazySingleton<TrashService>(() => TrashService());
getIt.registerLazySingleton<TrashListener>(() => TrashListener()); getIt.registerLazySingleton<TrashListener>(() => TrashListener());
getIt.registerFactory<TrashBloc>( getIt.registerFactory<TrashBloc>(
() => TrashBloc( () => TrashBloc(),
service: getIt<TrashService>(),
listener: getIt<TrashListener>(),
),
); );
} }

View File

@ -39,9 +39,9 @@ class FlowyRunner {
// add task // add task
getIt<AppLauncher>().addTask(InitRustSDKTask()); getIt<AppLauncher>().addTask(InitRustSDKTask());
getIt<AppLauncher>().addTask(PluginLoadTask());
if (!env.isTest()) { if (!env.isTest()) {
getIt<AppLauncher>().addTask(PluginLoadTask());
getIt<AppLauncher>().addTask(InitAppWidgetTask()); getIt<AppLauncher>().addTask(InitAppWidgetTask());
getIt<AppLauncher>().addTask(InitPlatformServiceTask()); getIt<AppLauncher>().addTask(InitPlatformServiceTask());
} }

View File

@ -18,11 +18,10 @@ import 'package:dartz/dartz.dart';
part 'app_bloc.freezed.dart'; part 'app_bloc.freezed.dart';
class AppBloc extends Bloc<AppEvent, AppState> { class AppBloc extends Bloc<AppEvent, AppState> {
final AppPB app;
final AppService appService; final AppService appService;
final AppListener appListener; final AppListener appListener;
AppBloc({required this.app}) AppBloc({required AppPB app})
: appService = AppService(), : appService = AppService(),
appListener = AppListener(appId: app.id), appListener = AppListener(appId: app.id),
super(AppState.initial(app)) { super(AppState.initial(app)) {
@ -34,8 +33,6 @@ class AppBloc extends Bloc<AppEvent, AppState> {
await _createView(value, emit); await _createView(value, emit);
}, loadViews: (_) async { }, loadViews: (_) async {
await _loadViews(emit); await _loadViews(emit);
}, didReceiveViewUpdated: (e) async {
await _didReceiveViewUpdated(e.views, emit);
}, delete: (e) async { }, delete: (e) async {
await _deleteApp(emit); await _deleteApp(emit);
}, deleteView: (deletedView) async { }, deleteView: (deletedView) async {
@ -43,23 +40,26 @@ class AppBloc extends Bloc<AppEvent, AppState> {
}, rename: (e) async { }, rename: (e) async {
await _renameView(e, emit); await _renameView(e, emit);
}, appDidUpdate: (e) async { }, appDidUpdate: (e) async {
emit(state.copyWith(app: e.app)); final latestCreatedView = state.latestCreatedView;
final views = e.app.belongings.items;
AppState newState = state.copyWith(
views: views,
app: e.app,
);
if (latestCreatedView != null) {
final index =
views.indexWhere((element) => element.id == latestCreatedView.id);
if (index == -1) {
newState = newState.copyWith(latestCreatedView: null);
}
}
emit(newState);
}); });
}); });
} }
void _startListening() { void _startListening() {
appListener.start( appListener.start(
onViewsChanged: (result) {
result.fold(
(views) {
if (!isClosed) {
add(AppEvent.didReceiveViewUpdated(views));
}
},
(error) => Log.error(error),
);
},
onAppUpdated: (app) { onAppUpdated: (app) {
if (!isClosed) { if (!isClosed) {
add(AppEvent.appDidUpdate(app)); add(AppEvent.appDidUpdate(app));
@ -69,7 +69,8 @@ class AppBloc extends Bloc<AppEvent, AppState> {
} }
Future<void> _renameView(Rename e, Emitter<AppState> emit) async { Future<void> _renameView(Rename e, Emitter<AppState> emit) async {
final result = await appService.updateApp(appId: app.id, name: e.newName); final result =
await appService.updateApp(appId: state.app.id, name: e.newName);
result.fold( result.fold(
(l) => emit(state.copyWith(successOrFailure: left(unit))), (l) => emit(state.copyWith(successOrFailure: left(unit))),
(error) => emit(state.copyWith(successOrFailure: right(error))), (error) => emit(state.copyWith(successOrFailure: right(error))),
@ -78,7 +79,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
// Delete the current app // Delete the current app
Future<void> _deleteApp(Emitter<AppState> emit) async { Future<void> _deleteApp(Emitter<AppState> emit) async {
final result = await appService.delete(appId: app.id); final result = await appService.delete(appId: state.app.id);
result.fold( result.fold(
(unit) => emit(state.copyWith(successOrFailure: left(unit))), (unit) => emit(state.copyWith(successOrFailure: left(unit))),
(error) => emit(state.copyWith(successOrFailure: right(error))), (error) => emit(state.copyWith(successOrFailure: right(error))),
@ -95,7 +96,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
Future<void> _createView(CreateView value, Emitter<AppState> emit) async { Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
final result = await appService.createView( final result = await appService.createView(
appId: app.id, appId: state.app.id,
name: value.name, name: value.name,
desc: value.desc ?? "", desc: value.desc ?? "",
dataFormatType: value.pluginBuilder.dataFormatType, dataFormatType: value.pluginBuilder.dataFormatType,
@ -120,25 +121,8 @@ class AppBloc extends Bloc<AppEvent, AppState> {
return super.close(); return super.close();
} }
Future<void> _didReceiveViewUpdated(
List<ViewPB> views,
Emitter<AppState> emit,
) async {
final latestCreatedView = state.latestCreatedView;
AppState newState = state.copyWith(views: views);
if (latestCreatedView != null) {
final index =
views.indexWhere((element) => element.id == latestCreatedView.id);
if (index == -1) {
newState = newState.copyWith(latestCreatedView: null);
}
}
emit(newState);
}
Future<void> _loadViews(Emitter<AppState> emit) async { Future<void> _loadViews(Emitter<AppState> emit) async {
final viewsOrFailed = await appService.getViews(appId: app.id); final viewsOrFailed = await appService.getViews(appId: state.app.id);
viewsOrFailed.fold( viewsOrFailed.fold(
(views) => emit(state.copyWith(views: views)), (views) => emit(state.copyWith(views: views)),
(error) { (error) {
@ -161,8 +145,6 @@ class AppEvent with _$AppEvent {
const factory AppEvent.delete() = DeleteApp; const factory AppEvent.delete() = DeleteApp;
const factory AppEvent.deleteView(String viewId) = DeleteView; const factory AppEvent.deleteView(String viewId) = DeleteView;
const factory AppEvent.rename(String newName) = Rename; const factory AppEvent.rename(String newName) = Rename;
const factory AppEvent.didReceiveViewUpdated(List<ViewPB> views) =
ReceiveViews;
const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate; const factory AppEvent.appDidUpdate(AppPB app) = AppDidUpdate;
} }

View File

@ -11,11 +11,11 @@ import 'package:flowy_sdk/protobuf/flowy-folder/dart_notification.pb.dart';
import 'package:flowy_sdk/rust_stream.dart'; import 'package:flowy_sdk/rust_stream.dart';
typedef AppDidUpdateCallback = void Function(AppPB app); typedef AppDidUpdateCallback = void Function(AppPB app);
typedef ViewsDidChangeCallback = void Function(Either<List<ViewPB>, FlowyError> viewsOrFailed); typedef ViewsDidChangeCallback = void Function(
Either<List<ViewPB>, FlowyError> viewsOrFailed);
class AppListener { class AppListener {
StreamSubscription<SubscribeObject>? _subscription; StreamSubscription<SubscribeObject>? _subscription;
ViewsDidChangeCallback? _viewsChanged;
AppDidUpdateCallback? _updated; AppDidUpdateCallback? _updated;
FolderNotificationParser? _parser; FolderNotificationParser? _parser;
String appId; String appId;
@ -24,26 +24,16 @@ class AppListener {
required this.appId, required this.appId,
}); });
void start({ViewsDidChangeCallback? onViewsChanged, AppDidUpdateCallback? onAppUpdated}) { void start({AppDidUpdateCallback? onAppUpdated}) {
_viewsChanged = onViewsChanged;
_updated = onAppUpdated; _updated = onAppUpdated;
_parser = FolderNotificationParser(id: appId, callback: _bservableCallback); _parser = FolderNotificationParser(id: appId, callback: _handleCallback);
_subscription = RustStreamReceiver.listen((observable) => _parser?.parse(observable)); _subscription =
RustStreamReceiver.listen((observable) => _parser?.parse(observable));
} }
void _bservableCallback(FolderNotification ty, Either<Uint8List, FlowyError> result) { void _handleCallback(
FolderNotification ty, Either<Uint8List, FlowyError> result) {
switch (ty) { switch (ty) {
case FolderNotification.AppViewsChanged:
if (_viewsChanged != null) {
result.fold(
(payload) {
final repeatedView = RepeatedViewPB.fromBuffer(payload);
_viewsChanged!(left(repeatedView.items));
},
(error) => _viewsChanged!(right(error)),
);
}
break;
case FolderNotification.AppUpdated: case FolderNotification.AppUpdated:
if (_updated != null) { if (_updated != null) {
result.fold( result.fold(
@ -63,7 +53,6 @@ class AppListener {
Future<void> stop() async { Future<void> stop() async {
_parser = null; _parser = null;
await _subscription?.cancel(); await _subscription?.cancel();
_viewsChanged = null;
_updated = null; _updated = null;
} }
} }

View File

@ -6,6 +6,8 @@ import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/log.dart'; import 'package:flowy_sdk/log.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/workspace.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
@ -13,16 +15,23 @@ part 'menu_bloc.freezed.dart';
class MenuBloc extends Bloc<MenuEvent, MenuState> { class MenuBloc extends Bloc<MenuEvent, MenuState> {
final WorkspaceService _workspaceService; final WorkspaceService _workspaceService;
final WorkspaceListener listener; final WorkspaceListener _listener;
final String workspaceId; final UserProfilePB user;
final WorkspacePB workspace;
MenuBloc({required this.workspaceId, required this.listener}) MenuBloc({
: _workspaceService = WorkspaceService(workspaceId: workspaceId), required this.user,
super(MenuState.initial()) { required this.workspace,
}) : _workspaceService = WorkspaceService(workspaceId: workspace.id),
_listener = WorkspaceListener(
user: user,
workspaceId: workspace.id,
),
super(MenuState.initial(workspace)) {
on<MenuEvent>((event, emit) async { on<MenuEvent>((event, emit) async {
await event.map( await event.map(
initial: (e) async { initial: (e) async {
listener.start(appsChanged: _handleAppsOrFail); _listener.start(appsChanged: _handleAppsOrFail);
await _fetchApps(emit); await _fetchApps(emit);
}, },
openPage: (e) async { openPage: (e) async {
@ -55,7 +64,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
@override @override
Future<void> close() async { Future<void> close() async {
await listener.stop(); await _listener.stop();
return super.close(); return super.close();
} }
@ -110,8 +119,8 @@ class MenuState with _$MenuState {
required Plugin plugin, required Plugin plugin,
}) = _MenuState; }) = _MenuState;
factory MenuState.initial() => MenuState( factory MenuState.initial(WorkspacePB workspace) => MenuState(
apps: [], apps: workspace.apps.items,
successOrFailure: left(unit), successOrFailure: left(unit),
plugin: makePlugin(pluginType: PluginType.blank), plugin: makePlugin(pluginType: PluginType.blank),
); );

View File

@ -51,8 +51,10 @@ class HomeMenu extends StatelessWidget {
providers: [ providers: [
BlocProvider<MenuBloc>( BlocProvider<MenuBloc>(
create: (context) { create: (context) {
final menuBloc = getIt<MenuBloc>( final menuBloc = MenuBloc(
param1: user, param2: workspaceSetting.workspace.id); user: user,
workspace: workspaceSetting.workspace,
);
menuBloc.add(const MenuEvent.initial()); menuBloc.add(const MenuEvent.initial());
return menuBloc; return menuBloc;
}, },
@ -221,8 +223,7 @@ class MenuTopBar extends StatelessWidget {
const Spacer(), const Spacer(),
Tooltip( Tooltip(
richMessage: TextSpan(children: [ richMessage: TextSpan(children: [
TextSpan( TextSpan(text: "${LocaleKeys.sideBar_closeSidebar.tr()}\n"),
text: "${LocaleKeys.sideBar_closeSidebar.tr()}\n"),
TextSpan( TextSpan(
text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\", text: Platform.isMacOS ? "⌘+\\" : "Ctrl+\\",
style: const TextStyle(color: Colors.white60), style: const TextStyle(color: Colors.white60),

View File

@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/board/board.dart';
import 'package:app_flowy/plugins/doc/document.dart'; import 'package:app_flowy/plugins/doc/document.dart';
import 'package:app_flowy/plugins/grid/grid.dart'; import 'package:app_flowy/plugins/grid/grid.dart';
import 'package:app_flowy/workspace/application/app/app_bloc.dart'; import 'package:app_flowy/workspace/application/app/app_bloc.dart';
import 'package:app_flowy/workspace/application/menu/menu_view_section_bloc.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
@ -125,4 +126,64 @@ void main() {
}, },
); );
}); });
group('$AppBloc', () {
late AppPB app;
setUpAll(() async {
app = await test.createTestApp();
});
blocTest<AppBloc, AppState>(
"create documents' order test",
build: () => AppBloc(app: app)..add(const AppEvent.initial()),
act: (bloc) async {
bloc.add(AppEvent.createView("1", DocumentPluginBuilder()));
await blocResponseFuture();
bloc.add(AppEvent.createView("2", DocumentPluginBuilder()));
await blocResponseFuture();
bloc.add(AppEvent.createView("3", DocumentPluginBuilder()));
await blocResponseFuture();
},
wait: blocResponseDuration(),
verify: (bloc) {
assert(bloc.state.views[0].name == '1');
assert(bloc.state.views[1].name == '2');
assert(bloc.state.views[2].name == '3');
},
);
});
group('$AppBloc', () {
late AppPB app;
setUpAll(() async {
app = await test.createTestApp();
});
blocTest<AppBloc, AppState>(
"reorder documents",
build: () => AppBloc(app: app)..add(const AppEvent.initial()),
act: (bloc) async {
bloc.add(AppEvent.createView("1", DocumentPluginBuilder()));
await blocResponseFuture();
bloc.add(AppEvent.createView("2", DocumentPluginBuilder()));
await blocResponseFuture();
bloc.add(AppEvent.createView("3", DocumentPluginBuilder()));
await blocResponseFuture();
final appViewData = AppViewDataContext(appId: app.id);
appViewData.views = bloc.state.views;
final viewSectionBloc = ViewSectionBloc(
appViewData: appViewData,
)..add(const ViewSectionEvent.initial());
await blocResponseFuture();
viewSectionBloc.add(const ViewSectionEvent.moveView(0, 2));
await blocResponseFuture();
},
wait: blocResponseDuration(),
verify: (bloc) {
assert(bloc.state.views[0].name == '2');
assert(bloc.state.views[1].name == '3');
assert(bloc.state.views[2].name == '1');
},
);
});
} }

View File

@ -0,0 +1,66 @@
import 'package:app_flowy/workspace/application/menu/menu_bloc.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../util.dart';
void main() {
late AppFlowyUnitTest test;
setUpAll(() async {
test = await AppFlowyUnitTest.ensureInitialized();
});
group('$MenuBloc', () {
late MenuBloc menuBloc;
setUp(() async {
menuBloc = MenuBloc(
user: test.userProfile,
workspace: test.currentWorkspace,
)..add(const MenuEvent.initial());
await blocResponseFuture();
});
blocTest<MenuBloc, MenuState>(
"assert initial apps is the build-in app",
build: () => menuBloc,
wait: blocResponseDuration(),
verify: (bloc) {
assert(bloc.state.apps.length == 1);
},
);
//
blocTest<MenuBloc, MenuState>(
"create apps",
build: () => menuBloc,
act: (bloc) async {
bloc.add(const MenuEvent.createApp("App 1"));
await blocResponseFuture();
bloc.add(const MenuEvent.createApp("App 2"));
await blocResponseFuture();
bloc.add(const MenuEvent.createApp("App 3"));
},
wait: blocResponseDuration(),
verify: (bloc) {
// apps[0] is the build-in app
assert(bloc.state.apps[1].name == 'App 1');
assert(bloc.state.apps[2].name == 'App 2');
assert(bloc.state.apps[3].name == 'App 3');
},
);
blocTest<MenuBloc, MenuState>(
"reorder apps",
build: () => menuBloc,
act: (bloc) async {
bloc.add(const MenuEvent.moveApp(1, 3));
},
wait: blocResponseDuration(),
verify: (bloc) {
assert(bloc.state.apps[1].name == 'App 2');
assert(bloc.state.apps[2].name == 'App 3');
assert(bloc.state.apps[3].name == 'App 1');
},
);
});
//
}

View File

@ -0,0 +1,110 @@
import 'package:app_flowy/plugins/doc/document.dart';
import 'package:app_flowy/plugins/trash/application/trash_bloc.dart';
import 'package:app_flowy/workspace/application/app/app_bloc.dart';
import 'package:bloc_test/bloc_test.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/app.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../util.dart';
void main() {
late AppFlowyUnitTest test;
late AppPB app;
late AppBloc appBloc;
late List<ViewPB> allViews;
setUpAll(() async {
test = await AppFlowyUnitTest.ensureInitialized();
/// Create a new app with three documents
app = await test.createTestApp();
appBloc = AppBloc(app: app)
..add(const AppEvent.initial())
..add(AppEvent.createView(
"Document 1",
DocumentPluginBuilder(),
))
..add(AppEvent.createView(
"Document 2",
DocumentPluginBuilder(),
))
..add(
AppEvent.createView(
"Document 3",
DocumentPluginBuilder(),
),
);
await blocResponseFuture(millisecond: 200);
allViews = [...appBloc.state.app.belongings.items];
assert(allViews.length == 3);
});
group('$TrashBloc', () {
late TrashBloc trashBloc;
late ViewPB deletedView;
setUpAll(() {});
setUp(() async {
trashBloc = TrashBloc()..add(const TrashEvent.initial());
await blocResponseFuture();
});
blocTest<TrashBloc, TrashState>(
"delete view",
build: () => trashBloc,
act: (bloc) async {
deletedView = appBloc.state.app.belongings.items[0];
appBloc.add(AppEvent.deleteView(deletedView.id));
},
wait: blocResponseDuration(),
verify: (bloc) {
assert(appBloc.state.app.belongings.items.length == 2);
assert(bloc.state.objects.length == 1);
assert(bloc.state.objects.first.id == deletedView.id);
},
);
blocTest<TrashBloc, TrashState>(
"delete all views",
build: () => trashBloc,
act: (bloc) async {
for (final view in appBloc.state.app.belongings.items) {
appBloc.add(AppEvent.deleteView(view.id));
await blocResponseFuture();
}
},
wait: blocResponseDuration(),
verify: (bloc) {
assert(bloc.state.objects[0].id == allViews[0].id);
assert(bloc.state.objects[1].id == allViews[1].id);
assert(bloc.state.objects[2].id == allViews[2].id);
},
);
blocTest<TrashBloc, TrashState>(
"put back",
build: () => trashBloc,
act: (bloc) async {
bloc.add(TrashEvent.putback(allViews[0].id));
},
wait: blocResponseDuration(),
verify: (bloc) {
assert(appBloc.state.app.belongings.items.length == 1);
assert(bloc.state.objects.length == 2);
},
);
blocTest<TrashBloc, TrashState>(
"put back all",
build: () => trashBloc,
act: (bloc) async {
bloc.add(const TrashEvent.restoreAll());
},
wait: blocResponseDuration(),
verify: (bloc) {
assert(appBloc.state.app.belongings.items.length == 3);
assert(bloc.state.objects.isEmpty);
},
);
//
});
}

View File

@ -113,10 +113,10 @@ class FlowyTestApp implements EntryPoint {
} }
} }
Future<void> blocResponseFuture() { Future<void> blocResponseFuture({int millisecond = 100}) {
return Future.delayed(const Duration(milliseconds: 100)); return Future.delayed(Duration(milliseconds: millisecond));
} }
Duration blocResponseDuration({int millseconds = 100}) { Duration blocResponseDuration({int milliseconds = 100}) {
return Duration(milliseconds: millseconds); return Duration(milliseconds: milliseconds);
} }

View File

@ -12,7 +12,6 @@ pub(crate) enum FolderNotification {
WorkspaceAppsChanged = 14, WorkspaceAppsChanged = 14,
WorkspaceSetting = 15, WorkspaceSetting = 15,
AppUpdated = 21, AppUpdated = 21,
AppViewsChanged = 24,
ViewUpdated = 31, ViewUpdated = 31,
ViewDeleted = 32, ViewDeleted = 32,
ViewRestored = 33, ViewRestored = 33,

View File

@ -1,5 +1,5 @@
pub use crate::entities::view::ViewDataFormatPB; pub use crate::entities::view::ViewDataFormatPB;
use crate::entities::{DeletedViewPB, ViewInfoPB, ViewLayoutTypePB}; use crate::entities::{AppPB, DeletedViewPB, ViewInfoPB, ViewLayoutTypePB};
use crate::manager::{ViewDataProcessor, ViewDataProcessorMap}; use crate::manager::{ViewDataProcessor, ViewDataProcessorMap};
use crate::{ use crate::{
dart_notification::{send_dart_notification, FolderNotification}, dart_notification::{send_dart_notification, FolderNotification},
@ -531,16 +531,15 @@ fn notify_views_changed<'a>(
trash_controller: Arc<TrashController>, trash_controller: Arc<TrashController>,
transaction: &'a (dyn FolderPersistenceTransaction + 'a), transaction: &'a (dyn FolderPersistenceTransaction + 'a),
) -> FlowyResult<()> { ) -> FlowyResult<()> {
let items: Vec<ViewPB> = read_belonging_views_on_local(belong_to_id, trash_controller.clone(), transaction)? let mut app_rev = transaction.read_app(belong_to_id)?;
.into_iter() let trash_ids = trash_controller.read_trash_ids(transaction)?;
.map(|view_rev| view_rev.into()) app_rev.belongings.retain(|view| !trash_ids.contains(&view.id));
.collect(); let app: AppPB = app_rev.into();
tracing::Span::current().record("view_count", &format!("{}", items.len()).as_str());
let repeated_view = RepeatedViewPB { items }; send_dart_notification(belong_to_id, FolderNotification::AppUpdated)
send_dart_notification(belong_to_id, FolderNotification::AppViewsChanged) .payload(app)
.payload(repeated_view)
.send(); .send();
Ok(()) Ok(())
} }