From f18cc717eafe17c86f7503c6279501107323c21c Mon Sep 17 00:00:00 2001 From: appflowy Date: Tue, 7 Sep 2021 17:12:03 +0800 Subject: [PATCH] refactor extract observable payload --- .../lib/user/infrastructure/i_auth_impl.dart | 2 - .../welcome/presentation/welcome_screen.dart | 20 +- .../workspace/application/menu/menu_bloc.dart | 1 + .../application/menu/menu_user_bloc.dart | 22 +- .../application/workspace/welcome_bloc.dart | 110 +++++ ...freezed.dart => welcome_bloc.freezed.dart} | 403 ++++++++++-------- .../workspace/workspace_list_bloc.dart | 91 ---- app_flowy/lib/workspace/domain/i_user.dart | 9 +- app_flowy/lib/workspace/domain/i_view.dart | 2 +- .../infrastructure/deps_resolver.dart | 13 +- .../workspace/infrastructure/i_user_impl.dart | 16 +- .../infrastructure/i_workspace_impl.dart | 4 +- .../infrastructure/repos/app_repo.dart | 63 ++- .../infrastructure/repos/helper.dart | 35 ++ .../infrastructure/repos/user_repo.dart | 47 +- .../infrastructure/repos/view_repo.dart | 44 +- .../infrastructure/repos/workspace_repo.dart | 95 ++--- .../flowy_sdk/lib/dispatch/code_gen.dart | 17 + .../protobuf/flowy-observable/subject.pb.dart | 73 +++- .../flowy-observable/subject.pbjson.dart | 10 +- .../flowy-workspace/event.pbenum.dart | 2 + .../flowy-workspace/event.pbjson.dart | 3 +- rust-lib/flowy-database/src/macros.rs | 71 ++- .../src/sql_tables/doc/doc_sql.rs | 6 +- .../flowy-observable/src/entities/subject.rs | 12 +- .../src/protobuf/model/subject.rs | 227 +++++++--- .../src/protobuf/proto/subject.proto | 5 +- rust-lib/flowy-sdk/src/lib.rs | 14 +- .../src/services/user/user_session.rs | 4 +- rust-lib/flowy-workspace/Cargo.toml | 3 + rust-lib/flowy-workspace/src/event.rs | 29 +- .../src/handlers/app_handler.rs | 3 +- .../src/handlers/view_handler.rs | 3 +- .../src/handlers/workspace_handler.rs | 12 +- rust-lib/flowy-workspace/src/module.rs | 3 +- .../src/observable/observable.rs | 54 ++- .../src/protobuf/model/event.rs | 72 ++-- .../src/protobuf/proto/event.proto | 1 + .../src/services/app_controller.rs | 40 +- .../flowy-workspace/src/services/helper.rs | 6 +- .../src/services/view_controller.rs | 42 +- .../src/services/workspace_controller.rs | 125 +++--- .../src/sql_tables/app/app_sql.rs | 39 +- .../src/sql_tables/app/app_table.rs | 13 +- .../src/sql_tables/view/view_sql.rs | 17 +- .../src/sql_tables/workspace/workspace_sql.rs | 80 +++- .../sql_tables/workspace/workspace_table.rs | 8 + 47 files changed, 1220 insertions(+), 751 deletions(-) create mode 100644 app_flowy/lib/workspace/application/workspace/welcome_bloc.dart rename app_flowy/lib/workspace/application/workspace/{workspace_list_bloc.freezed.dart => welcome_bloc.freezed.dart} (69%) delete mode 100644 app_flowy/lib/workspace/application/workspace/workspace_list_bloc.dart create mode 100644 app_flowy/lib/workspace/infrastructure/repos/helper.dart diff --git a/app_flowy/lib/user/infrastructure/i_auth_impl.dart b/app_flowy/lib/user/infrastructure/i_auth_impl.dart index 977a38ed17..a4403d9510 100644 --- a/app_flowy/lib/user/infrastructure/i_auth_impl.dart +++ b/app_flowy/lib/user/infrastructure/i_auth_impl.dart @@ -1,8 +1,6 @@ import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/user/presentation/sign_up_screen.dart'; import 'package:app_flowy/welcome/domain/i_welcome.dart'; -import 'package:app_flowy/workspace/infrastructure/repos/user_repo.dart'; -import 'package:app_flowy/workspace/presentation/workspace/workspace_select_screen.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_infra_ui/widget/route/animation.dart'; import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart'; diff --git a/app_flowy/lib/welcome/presentation/welcome_screen.dart b/app_flowy/lib/welcome/presentation/welcome_screen.dart index b16fd2892c..2d26b9a516 100644 --- a/app_flowy/lib/welcome/presentation/welcome_screen.dart +++ b/app_flowy/lib/welcome/presentation/welcome_screen.dart @@ -1,4 +1,6 @@ -import 'package:app_flowy/workspace/application/workspace/workspace_list_bloc.dart'; +import 'package:app_flowy/startup/startup.dart'; +import 'package:app_flowy/workspace/application/workspace/welcome_bloc.dart'; +import 'package:app_flowy/workspace/domain/i_user.dart'; import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart'; import 'package:flowy_infra_ui/style_widget/text_button.dart'; import 'package:flowy_infra_ui/widget/error_page.dart'; @@ -17,9 +19,9 @@ class WelcomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( - create: (_) => - WorkspaceListBloc(repo)..add(const WorkspaceListEvent.initial()), - child: BlocBuilder( + create: (_) => getIt(param1: repo.user) + ..add(const WelcomeEvent.initial()), + child: BlocBuilder( builder: (context, state) { return Scaffold( body: Padding( @@ -37,7 +39,7 @@ class WelcomeScreen extends StatelessWidget { ); } - Widget _renderBody(WorkspaceListState state) { + Widget _renderBody(WelcomeState state) { final body = state.successOrFailure.fold( (_) => _renderList(state.workspaces), (error) => FlowyErrorPage(error.toString()), @@ -54,8 +56,8 @@ class WelcomeScreen extends StatelessWidget { fontSize: 14, onPressed: () { context - .read() - .add(const WorkspaceListEvent.createWorkspace("workspace", "")); + .read() + .add(const WelcomeEvent.createWorkspace("workspace", "")); }, ), ); @@ -77,9 +79,7 @@ class WelcomeScreen extends StatelessWidget { } void _handleOnPress(BuildContext context, Workspace workspace) { - context - .read() - .add(WorkspaceListEvent.openWorkspace(workspace)); + context.read().add(WelcomeEvent.openWorkspace(workspace)); Navigator.of(context).pop(workspace.id); } diff --git a/app_flowy/lib/workspace/application/menu/menu_bloc.dart b/app_flowy/lib/workspace/application/menu/menu_bloc.dart index 351d1eddaa..7d83e636e0 100644 --- a/app_flowy/lib/workspace/application/menu/menu_bloc.dart +++ b/app_flowy/lib/workspace/application/menu/menu_bloc.dart @@ -52,6 +52,7 @@ class MenuBloc extends Bloc { ); } + // ignore: unused_element Stream _fetchApps() async* { final appsOrFail = await workspace.getApps(); yield appsOrFail.fold( diff --git a/app_flowy/lib/workspace/application/menu/menu_user_bloc.dart b/app_flowy/lib/workspace/application/menu/menu_user_bloc.dart index 56682277f7..8b081c1d95 100644 --- a/app_flowy/lib/workspace/application/menu/menu_user_bloc.dart +++ b/app_flowy/lib/workspace/application/menu/menu_user_bloc.dart @@ -16,17 +16,17 @@ class MenuUserBloc extends Bloc { Stream mapEventToState(MenuUserEvent event) async* { yield* event.map( initial: (_) async* { - // fetch workspaces - iUserImpl.fetchWorkspaces().then((result) { - result.fold( - (workspaces) async* { - yield state.copyWith(workspaces: some(workspaces)); - }, - (error) async* { - yield state.copyWith(successOrFailure: right(error.msg)); - }, - ); - }); + // // fetch workspaces + // iUserImpl.fetchWorkspaces().then((result) { + // result.fold( + // (workspaces) async* { + // yield state.copyWith(workspaces: some(workspaces)); + // }, + // (error) async* { + // yield state.copyWith(successOrFailure: right(error.msg)); + // }, + // ); + // }); }, fetchWorkspaces: (_FetchWorkspaces value) async* {}, ); diff --git a/app_flowy/lib/workspace/application/workspace/welcome_bloc.dart b/app_flowy/lib/workspace/application/workspace/welcome_bloc.dart new file mode 100644 index 0000000000..8c05a1fe57 --- /dev/null +++ b/app_flowy/lib/workspace/application/workspace/welcome_bloc.dart @@ -0,0 +1,110 @@ +import 'package:app_flowy/workspace/domain/i_user.dart'; +import 'package:app_flowy/workspace/infrastructure/repos/user_repo.dart'; +import 'package:flowy_infra/flowy_logger.dart'; +import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:dartz/dartz.dart'; + +part 'welcome_bloc.freezed.dart'; + +class WelcomeBloc extends Bloc { + UserRepo repo; + IUserWorkspaceListWatch watcher; + WelcomeBloc({required this.repo, required this.watcher}) + : super(WelcomeState.initial()); + + @override + Stream mapEventToState( + WelcomeEvent event, + ) async* { + yield* event.map(initial: (e) async* { + watcher.startWatching( + workspaceListUpdatedCallback: (workspacesOrFail) => + _handleWorkspaceListUpdated(workspacesOrFail), + ); + yield* _fetchWorkspaces(); + }, openWorkspace: (e) async* { + yield* _openWorkspace(e.workspace); + }, createWorkspace: (e) async* { + yield* _createWorkspace(e.name, e.desc); + }, workspacesReveived: (e) async* { + yield e.workspacesOrFail.fold( + (workspaces) => state.copyWith( + workspaces: workspaces, successOrFailure: left(unit)), + (error) => state.copyWith(successOrFailure: right(error)), + ); + }); + } + + Stream _fetchWorkspaces() async* { + final workspacesOrFailed = await repo.getWorkspaces(); + yield workspacesOrFailed.fold( + (workspaces) => + state.copyWith(workspaces: workspaces, successOrFailure: left(unit)), + (error) { + Log.error(error); + return state.copyWith(successOrFailure: right(error)); + }, + ); + } + + Stream _openWorkspace(Workspace workspace) async* { + final result = await repo.openWorkspace(workspace.id); + yield result.fold( + (workspaces) => state.copyWith(successOrFailure: left(unit)), + (error) { + Log.error(error); + return state.copyWith(successOrFailure: right(error)); + }, + ); + } + + Stream _createWorkspace(String name, String desc) async* { + final result = await repo.createWorkspace(name, desc); + yield result.fold( + (workspace) { + // add(const WelcomeEvent.fetchWorkspaces()); + return state.copyWith(successOrFailure: left(unit)); + }, + (error) { + Log.error(error); + return state.copyWith(successOrFailure: right(error)); + }, + ); + } + + void _handleWorkspaceListUpdated( + Either, WorkspaceError> workspacesOrFail) { + add(WelcomeEvent.workspacesReveived(workspacesOrFail)); + } +} + +@freezed +abstract class WelcomeEvent with _$WelcomeEvent { + const factory WelcomeEvent.initial() = Initial; + // const factory WelcomeEvent.fetchWorkspaces() = FetchWorkspace; + const factory WelcomeEvent.createWorkspace(String name, String desc) = + CreateWorkspace; + const factory WelcomeEvent.openWorkspace(Workspace workspace) = OpenWorkspace; + + const factory WelcomeEvent.workspacesReveived( + Either, WorkspaceError> workspacesOrFail) = + WorkspacesReceived; +} + +@freezed +abstract class WelcomeState implements _$WelcomeState { + const factory WelcomeState({ + required bool isLoading, + required List workspaces, + required Either successOrFailure, + }) = _WelcomeState; + + factory WelcomeState.initial() => WelcomeState( + isLoading: false, + workspaces: List.empty(), + successOrFailure: left(unit), + ); +} diff --git a/app_flowy/lib/workspace/application/workspace/workspace_list_bloc.freezed.dart b/app_flowy/lib/workspace/application/workspace/welcome_bloc.freezed.dart similarity index 69% rename from app_flowy/lib/workspace/application/workspace/workspace_list_bloc.freezed.dart rename to app_flowy/lib/workspace/application/workspace/welcome_bloc.freezed.dart index c9ba029082..4a22133d0f 100644 --- a/app_flowy/lib/workspace/application/workspace/workspace_list_bloc.freezed.dart +++ b/app_flowy/lib/workspace/application/workspace/welcome_bloc.freezed.dart @@ -1,7 +1,7 @@ // GENERATED CODE - DO NOT MODIFY BY HAND // ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target -part of 'workspace_list_bloc.dart'; +part of 'welcome_bloc.dart'; // ************************************************************************** // FreezedGenerator @@ -13,17 +13,13 @@ final _privateConstructorUsedError = UnsupportedError( 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); /// @nodoc -class _$WorkspaceListEventTearOff { - const _$WorkspaceListEventTearOff(); +class _$WelcomeEventTearOff { + const _$WelcomeEventTearOff(); Initial initial() { return const Initial(); } - FetchWorkspace fetchWorkspaces() { - return const FetchWorkspace(); - } - CreateWorkspace createWorkspace(String name, String desc) { return CreateWorkspace( name, @@ -36,64 +32,73 @@ class _$WorkspaceListEventTearOff { workspace, ); } + + WorkspacesReceived workspacesReveived( + Either, WorkspaceError> workspacesOrFail) { + return WorkspacesReceived( + workspacesOrFail, + ); + } } /// @nodoc -const $WorkspaceListEvent = _$WorkspaceListEventTearOff(); +const $WelcomeEvent = _$WelcomeEventTearOff(); /// @nodoc -mixin _$WorkspaceListEvent { +mixin _$WelcomeEvent { @optionalTypeArgs TResult when({ required TResult Function() initial, - required TResult Function() fetchWorkspaces, required TResult Function(String name, String desc) createWorkspace, required TResult Function(Workspace workspace) openWorkspace, + required TResult Function( + Either, WorkspaceError> workspacesOrFail) + workspacesReveived, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, - TResult Function()? fetchWorkspaces, TResult Function(String name, String desc)? createWorkspace, TResult Function(Workspace workspace)? openWorkspace, + TResult Function(Either, WorkspaceError> workspacesOrFail)? + workspacesReveived, required TResult orElse(), }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult map({ required TResult Function(Initial value) initial, - required TResult Function(FetchWorkspace value) fetchWorkspaces, required TResult Function(CreateWorkspace value) createWorkspace, required TResult Function(OpenWorkspace value) openWorkspace, + required TResult Function(WorkspacesReceived value) workspacesReveived, }) => throw _privateConstructorUsedError; @optionalTypeArgs TResult maybeMap({ TResult Function(Initial value)? initial, - TResult Function(FetchWorkspace value)? fetchWorkspaces, TResult Function(CreateWorkspace value)? createWorkspace, TResult Function(OpenWorkspace value)? openWorkspace, + TResult Function(WorkspacesReceived value)? workspacesReveived, required TResult orElse(), }) => throw _privateConstructorUsedError; } /// @nodoc -abstract class $WorkspaceListEventCopyWith<$Res> { - factory $WorkspaceListEventCopyWith( - WorkspaceListEvent value, $Res Function(WorkspaceListEvent) then) = - _$WorkspaceListEventCopyWithImpl<$Res>; +abstract class $WelcomeEventCopyWith<$Res> { + factory $WelcomeEventCopyWith( + WelcomeEvent value, $Res Function(WelcomeEvent) then) = + _$WelcomeEventCopyWithImpl<$Res>; } /// @nodoc -class _$WorkspaceListEventCopyWithImpl<$Res> - implements $WorkspaceListEventCopyWith<$Res> { - _$WorkspaceListEventCopyWithImpl(this._value, this._then); +class _$WelcomeEventCopyWithImpl<$Res> implements $WelcomeEventCopyWith<$Res> { + _$WelcomeEventCopyWithImpl(this._value, this._then); - final WorkspaceListEvent _value; + final WelcomeEvent _value; // ignore: unused_field - final $Res Function(WorkspaceListEvent) _then; + final $Res Function(WelcomeEvent) _then; } /// @nodoc @@ -103,7 +108,7 @@ abstract class $InitialCopyWith<$Res> { } /// @nodoc -class _$InitialCopyWithImpl<$Res> extends _$WorkspaceListEventCopyWithImpl<$Res> +class _$InitialCopyWithImpl<$Res> extends _$WelcomeEventCopyWithImpl<$Res> implements $InitialCopyWith<$Res> { _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then) : super(_value, (v) => _then(v as Initial)); @@ -119,7 +124,7 @@ class _$Initial implements Initial { @override String toString() { - return 'WorkspaceListEvent.initial()'; + return 'WelcomeEvent.initial()'; } @override @@ -134,9 +139,11 @@ class _$Initial implements Initial { @optionalTypeArgs TResult when({ required TResult Function() initial, - required TResult Function() fetchWorkspaces, required TResult Function(String name, String desc) createWorkspace, required TResult Function(Workspace workspace) openWorkspace, + required TResult Function( + Either, WorkspaceError> workspacesOrFail) + workspacesReveived, }) { return initial(); } @@ -145,9 +152,10 @@ class _$Initial implements Initial { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, - TResult Function()? fetchWorkspaces, TResult Function(String name, String desc)? createWorkspace, TResult Function(Workspace workspace)? openWorkspace, + TResult Function(Either, WorkspaceError> workspacesOrFail)? + workspacesReveived, required TResult orElse(), }) { if (initial != null) { @@ -160,9 +168,9 @@ class _$Initial implements Initial { @optionalTypeArgs TResult map({ required TResult Function(Initial value) initial, - required TResult Function(FetchWorkspace value) fetchWorkspaces, required TResult Function(CreateWorkspace value) createWorkspace, required TResult Function(OpenWorkspace value) openWorkspace, + required TResult Function(WorkspacesReceived value) workspacesReveived, }) { return initial(this); } @@ -171,9 +179,9 @@ class _$Initial implements Initial { @optionalTypeArgs TResult maybeMap({ TResult Function(Initial value)? initial, - TResult Function(FetchWorkspace value)? fetchWorkspaces, TResult Function(CreateWorkspace value)? createWorkspace, TResult Function(OpenWorkspace value)? openWorkspace, + TResult Function(WorkspacesReceived value)? workspacesReveived, required TResult orElse(), }) { if (initial != null) { @@ -183,104 +191,10 @@ class _$Initial implements Initial { } } -abstract class Initial implements WorkspaceListEvent { +abstract class Initial implements WelcomeEvent { const factory Initial() = _$Initial; } -/// @nodoc -abstract class $FetchWorkspaceCopyWith<$Res> { - factory $FetchWorkspaceCopyWith( - FetchWorkspace value, $Res Function(FetchWorkspace) then) = - _$FetchWorkspaceCopyWithImpl<$Res>; -} - -/// @nodoc -class _$FetchWorkspaceCopyWithImpl<$Res> - extends _$WorkspaceListEventCopyWithImpl<$Res> - implements $FetchWorkspaceCopyWith<$Res> { - _$FetchWorkspaceCopyWithImpl( - FetchWorkspace _value, $Res Function(FetchWorkspace) _then) - : super(_value, (v) => _then(v as FetchWorkspace)); - - @override - FetchWorkspace get _value => super._value as FetchWorkspace; -} - -/// @nodoc - -class _$FetchWorkspace implements FetchWorkspace { - const _$FetchWorkspace(); - - @override - String toString() { - return 'WorkspaceListEvent.fetchWorkspaces()'; - } - - @override - bool operator ==(dynamic other) { - return identical(this, other) || (other is FetchWorkspace); - } - - @override - int get hashCode => runtimeType.hashCode; - - @override - @optionalTypeArgs - TResult when({ - required TResult Function() initial, - required TResult Function() fetchWorkspaces, - required TResult Function(String name, String desc) createWorkspace, - required TResult Function(Workspace workspace) openWorkspace, - }) { - return fetchWorkspaces(); - } - - @override - @optionalTypeArgs - TResult maybeWhen({ - TResult Function()? initial, - TResult Function()? fetchWorkspaces, - TResult Function(String name, String desc)? createWorkspace, - TResult Function(Workspace workspace)? openWorkspace, - required TResult orElse(), - }) { - if (fetchWorkspaces != null) { - return fetchWorkspaces(); - } - return orElse(); - } - - @override - @optionalTypeArgs - TResult map({ - required TResult Function(Initial value) initial, - required TResult Function(FetchWorkspace value) fetchWorkspaces, - required TResult Function(CreateWorkspace value) createWorkspace, - required TResult Function(OpenWorkspace value) openWorkspace, - }) { - return fetchWorkspaces(this); - } - - @override - @optionalTypeArgs - TResult maybeMap({ - TResult Function(Initial value)? initial, - TResult Function(FetchWorkspace value)? fetchWorkspaces, - TResult Function(CreateWorkspace value)? createWorkspace, - TResult Function(OpenWorkspace value)? openWorkspace, - required TResult orElse(), - }) { - if (fetchWorkspaces != null) { - return fetchWorkspaces(this); - } - return orElse(); - } -} - -abstract class FetchWorkspace implements WorkspaceListEvent { - const factory FetchWorkspace() = _$FetchWorkspace; -} - /// @nodoc abstract class $CreateWorkspaceCopyWith<$Res> { factory $CreateWorkspaceCopyWith( @@ -291,7 +205,7 @@ abstract class $CreateWorkspaceCopyWith<$Res> { /// @nodoc class _$CreateWorkspaceCopyWithImpl<$Res> - extends _$WorkspaceListEventCopyWithImpl<$Res> + extends _$WelcomeEventCopyWithImpl<$Res> implements $CreateWorkspaceCopyWith<$Res> { _$CreateWorkspaceCopyWithImpl( CreateWorkspace _value, $Res Function(CreateWorkspace) _then) @@ -330,7 +244,7 @@ class _$CreateWorkspace implements CreateWorkspace { @override String toString() { - return 'WorkspaceListEvent.createWorkspace(name: $name, desc: $desc)'; + return 'WelcomeEvent.createWorkspace(name: $name, desc: $desc)'; } @override @@ -358,9 +272,11 @@ class _$CreateWorkspace implements CreateWorkspace { @optionalTypeArgs TResult when({ required TResult Function() initial, - required TResult Function() fetchWorkspaces, required TResult Function(String name, String desc) createWorkspace, required TResult Function(Workspace workspace) openWorkspace, + required TResult Function( + Either, WorkspaceError> workspacesOrFail) + workspacesReveived, }) { return createWorkspace(name, desc); } @@ -369,9 +285,10 @@ class _$CreateWorkspace implements CreateWorkspace { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, - TResult Function()? fetchWorkspaces, TResult Function(String name, String desc)? createWorkspace, TResult Function(Workspace workspace)? openWorkspace, + TResult Function(Either, WorkspaceError> workspacesOrFail)? + workspacesReveived, required TResult orElse(), }) { if (createWorkspace != null) { @@ -384,9 +301,9 @@ class _$CreateWorkspace implements CreateWorkspace { @optionalTypeArgs TResult map({ required TResult Function(Initial value) initial, - required TResult Function(FetchWorkspace value) fetchWorkspaces, required TResult Function(CreateWorkspace value) createWorkspace, required TResult Function(OpenWorkspace value) openWorkspace, + required TResult Function(WorkspacesReceived value) workspacesReveived, }) { return createWorkspace(this); } @@ -395,9 +312,9 @@ class _$CreateWorkspace implements CreateWorkspace { @optionalTypeArgs TResult maybeMap({ TResult Function(Initial value)? initial, - TResult Function(FetchWorkspace value)? fetchWorkspaces, TResult Function(CreateWorkspace value)? createWorkspace, TResult Function(OpenWorkspace value)? openWorkspace, + TResult Function(WorkspacesReceived value)? workspacesReveived, required TResult orElse(), }) { if (createWorkspace != null) { @@ -407,7 +324,7 @@ class _$CreateWorkspace implements CreateWorkspace { } } -abstract class CreateWorkspace implements WorkspaceListEvent { +abstract class CreateWorkspace implements WelcomeEvent { const factory CreateWorkspace(String name, String desc) = _$CreateWorkspace; String get name => throw _privateConstructorUsedError; @@ -426,8 +343,7 @@ abstract class $OpenWorkspaceCopyWith<$Res> { } /// @nodoc -class _$OpenWorkspaceCopyWithImpl<$Res> - extends _$WorkspaceListEventCopyWithImpl<$Res> +class _$OpenWorkspaceCopyWithImpl<$Res> extends _$WelcomeEventCopyWithImpl<$Res> implements $OpenWorkspaceCopyWith<$Res> { _$OpenWorkspaceCopyWithImpl( OpenWorkspace _value, $Res Function(OpenWorkspace) _then) @@ -459,7 +375,7 @@ class _$OpenWorkspace implements OpenWorkspace { @override String toString() { - return 'WorkspaceListEvent.openWorkspace(workspace: $workspace)'; + return 'WelcomeEvent.openWorkspace(workspace: $workspace)'; } @override @@ -484,9 +400,11 @@ class _$OpenWorkspace implements OpenWorkspace { @optionalTypeArgs TResult when({ required TResult Function() initial, - required TResult Function() fetchWorkspaces, required TResult Function(String name, String desc) createWorkspace, required TResult Function(Workspace workspace) openWorkspace, + required TResult Function( + Either, WorkspaceError> workspacesOrFail) + workspacesReveived, }) { return openWorkspace(workspace); } @@ -495,9 +413,10 @@ class _$OpenWorkspace implements OpenWorkspace { @optionalTypeArgs TResult maybeWhen({ TResult Function()? initial, - TResult Function()? fetchWorkspaces, TResult Function(String name, String desc)? createWorkspace, TResult Function(Workspace workspace)? openWorkspace, + TResult Function(Either, WorkspaceError> workspacesOrFail)? + workspacesReveived, required TResult orElse(), }) { if (openWorkspace != null) { @@ -510,9 +429,9 @@ class _$OpenWorkspace implements OpenWorkspace { @optionalTypeArgs TResult map({ required TResult Function(Initial value) initial, - required TResult Function(FetchWorkspace value) fetchWorkspaces, required TResult Function(CreateWorkspace value) createWorkspace, required TResult Function(OpenWorkspace value) openWorkspace, + required TResult Function(WorkspacesReceived value) workspacesReveived, }) { return openWorkspace(this); } @@ -521,9 +440,9 @@ class _$OpenWorkspace implements OpenWorkspace { @optionalTypeArgs TResult maybeMap({ TResult Function(Initial value)? initial, - TResult Function(FetchWorkspace value)? fetchWorkspaces, TResult Function(CreateWorkspace value)? createWorkspace, TResult Function(OpenWorkspace value)? openWorkspace, + TResult Function(WorkspacesReceived value)? workspacesReveived, required TResult orElse(), }) { if (openWorkspace != null) { @@ -533,7 +452,7 @@ class _$OpenWorkspace implements OpenWorkspace { } } -abstract class OpenWorkspace implements WorkspaceListEvent { +abstract class OpenWorkspace implements WelcomeEvent { const factory OpenWorkspace(Workspace workspace) = _$OpenWorkspace; Workspace get workspace => throw _privateConstructorUsedError; @@ -543,14 +462,146 @@ abstract class OpenWorkspace implements WorkspaceListEvent { } /// @nodoc -class _$WorkspaceListStateTearOff { - const _$WorkspaceListStateTearOff(); +abstract class $WorkspacesReceivedCopyWith<$Res> { + factory $WorkspacesReceivedCopyWith( + WorkspacesReceived value, $Res Function(WorkspacesReceived) then) = + _$WorkspacesReceivedCopyWithImpl<$Res>; + $Res call({Either, WorkspaceError> workspacesOrFail}); +} - _WorkspaceListState call( +/// @nodoc +class _$WorkspacesReceivedCopyWithImpl<$Res> + extends _$WelcomeEventCopyWithImpl<$Res> + implements $WorkspacesReceivedCopyWith<$Res> { + _$WorkspacesReceivedCopyWithImpl( + WorkspacesReceived _value, $Res Function(WorkspacesReceived) _then) + : super(_value, (v) => _then(v as WorkspacesReceived)); + + @override + WorkspacesReceived get _value => super._value as WorkspacesReceived; + + @override + $Res call({ + Object? workspacesOrFail = freezed, + }) { + return _then(WorkspacesReceived( + workspacesOrFail == freezed + ? _value.workspacesOrFail + : workspacesOrFail // ignore: cast_nullable_to_non_nullable + as Either, WorkspaceError>, + )); + } +} + +/// @nodoc + +class _$WorkspacesReceived implements WorkspacesReceived { + const _$WorkspacesReceived(this.workspacesOrFail); + + @override + final Either, WorkspaceError> workspacesOrFail; + + @override + String toString() { + return 'WelcomeEvent.workspacesReveived(workspacesOrFail: $workspacesOrFail)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other is WorkspacesReceived && + (identical(other.workspacesOrFail, workspacesOrFail) || + const DeepCollectionEquality() + .equals(other.workspacesOrFail, workspacesOrFail))); + } + + @override + int get hashCode => + runtimeType.hashCode ^ + const DeepCollectionEquality().hash(workspacesOrFail); + + @JsonKey(ignore: true) + @override + $WorkspacesReceivedCopyWith get copyWith => + _$WorkspacesReceivedCopyWithImpl(this, _$identity); + + @override + @optionalTypeArgs + TResult when({ + required TResult Function() initial, + required TResult Function(String name, String desc) createWorkspace, + required TResult Function(Workspace workspace) openWorkspace, + required TResult Function( + Either, WorkspaceError> workspacesOrFail) + workspacesReveived, + }) { + return workspacesReveived(workspacesOrFail); + } + + @override + @optionalTypeArgs + TResult maybeWhen({ + TResult Function()? initial, + TResult Function(String name, String desc)? createWorkspace, + TResult Function(Workspace workspace)? openWorkspace, + TResult Function(Either, WorkspaceError> workspacesOrFail)? + workspacesReveived, + required TResult orElse(), + }) { + if (workspacesReveived != null) { + return workspacesReveived(workspacesOrFail); + } + return orElse(); + } + + @override + @optionalTypeArgs + TResult map({ + required TResult Function(Initial value) initial, + required TResult Function(CreateWorkspace value) createWorkspace, + required TResult Function(OpenWorkspace value) openWorkspace, + required TResult Function(WorkspacesReceived value) workspacesReveived, + }) { + return workspacesReveived(this); + } + + @override + @optionalTypeArgs + TResult maybeMap({ + TResult Function(Initial value)? initial, + TResult Function(CreateWorkspace value)? createWorkspace, + TResult Function(OpenWorkspace value)? openWorkspace, + TResult Function(WorkspacesReceived value)? workspacesReveived, + required TResult orElse(), + }) { + if (workspacesReveived != null) { + return workspacesReveived(this); + } + return orElse(); + } +} + +abstract class WorkspacesReceived implements WelcomeEvent { + const factory WorkspacesReceived( + Either, WorkspaceError> workspacesOrFail) = + _$WorkspacesReceived; + + Either, WorkspaceError> get workspacesOrFail => + throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $WorkspacesReceivedCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +class _$WelcomeStateTearOff { + const _$WelcomeStateTearOff(); + + _WelcomeState call( {required bool isLoading, required List workspaces, required Either successOrFailure}) { - return _WorkspaceListState( + return _WelcomeState( isLoading: isLoading, workspaces: workspaces, successOrFailure: successOrFailure, @@ -559,25 +610,25 @@ class _$WorkspaceListStateTearOff { } /// @nodoc -const $WorkspaceListState = _$WorkspaceListStateTearOff(); +const $WelcomeState = _$WelcomeStateTearOff(); /// @nodoc -mixin _$WorkspaceListState { +mixin _$WelcomeState { bool get isLoading => throw _privateConstructorUsedError; List get workspaces => throw _privateConstructorUsedError; Either get successOrFailure => throw _privateConstructorUsedError; @JsonKey(ignore: true) - $WorkspaceListStateCopyWith get copyWith => + $WelcomeStateCopyWith get copyWith => throw _privateConstructorUsedError; } /// @nodoc -abstract class $WorkspaceListStateCopyWith<$Res> { - factory $WorkspaceListStateCopyWith( - WorkspaceListState value, $Res Function(WorkspaceListState) then) = - _$WorkspaceListStateCopyWithImpl<$Res>; +abstract class $WelcomeStateCopyWith<$Res> { + factory $WelcomeStateCopyWith( + WelcomeState value, $Res Function(WelcomeState) then) = + _$WelcomeStateCopyWithImpl<$Res>; $Res call( {bool isLoading, List workspaces, @@ -585,13 +636,12 @@ abstract class $WorkspaceListStateCopyWith<$Res> { } /// @nodoc -class _$WorkspaceListStateCopyWithImpl<$Res> - implements $WorkspaceListStateCopyWith<$Res> { - _$WorkspaceListStateCopyWithImpl(this._value, this._then); +class _$WelcomeStateCopyWithImpl<$Res> implements $WelcomeStateCopyWith<$Res> { + _$WelcomeStateCopyWithImpl(this._value, this._then); - final WorkspaceListState _value; + final WelcomeState _value; // ignore: unused_field - final $Res Function(WorkspaceListState) _then; + final $Res Function(WelcomeState) _then; @override $Res call({ @@ -617,11 +667,11 @@ class _$WorkspaceListStateCopyWithImpl<$Res> } /// @nodoc -abstract class _$WorkspaceListStateCopyWith<$Res> - implements $WorkspaceListStateCopyWith<$Res> { - factory _$WorkspaceListStateCopyWith( - _WorkspaceListState value, $Res Function(_WorkspaceListState) then) = - __$WorkspaceListStateCopyWithImpl<$Res>; +abstract class _$WelcomeStateCopyWith<$Res> + implements $WelcomeStateCopyWith<$Res> { + factory _$WelcomeStateCopyWith( + _WelcomeState value, $Res Function(_WelcomeState) then) = + __$WelcomeStateCopyWithImpl<$Res>; @override $Res call( {bool isLoading, @@ -630,15 +680,14 @@ abstract class _$WorkspaceListStateCopyWith<$Res> } /// @nodoc -class __$WorkspaceListStateCopyWithImpl<$Res> - extends _$WorkspaceListStateCopyWithImpl<$Res> - implements _$WorkspaceListStateCopyWith<$Res> { - __$WorkspaceListStateCopyWithImpl( - _WorkspaceListState _value, $Res Function(_WorkspaceListState) _then) - : super(_value, (v) => _then(v as _WorkspaceListState)); +class __$WelcomeStateCopyWithImpl<$Res> extends _$WelcomeStateCopyWithImpl<$Res> + implements _$WelcomeStateCopyWith<$Res> { + __$WelcomeStateCopyWithImpl( + _WelcomeState _value, $Res Function(_WelcomeState) _then) + : super(_value, (v) => _then(v as _WelcomeState)); @override - _WorkspaceListState get _value => super._value as _WorkspaceListState; + _WelcomeState get _value => super._value as _WelcomeState; @override $Res call({ @@ -646,7 +695,7 @@ class __$WorkspaceListStateCopyWithImpl<$Res> Object? workspaces = freezed, Object? successOrFailure = freezed, }) { - return _then(_WorkspaceListState( + return _then(_WelcomeState( isLoading: isLoading == freezed ? _value.isLoading : isLoading // ignore: cast_nullable_to_non_nullable @@ -665,8 +714,8 @@ class __$WorkspaceListStateCopyWithImpl<$Res> /// @nodoc -class _$_WorkspaceListState implements _WorkspaceListState { - const _$_WorkspaceListState( +class _$_WelcomeState implements _WelcomeState { + const _$_WelcomeState( {required this.isLoading, required this.workspaces, required this.successOrFailure}); @@ -680,13 +729,13 @@ class _$_WorkspaceListState implements _WorkspaceListState { @override String toString() { - return 'WorkspaceListState(isLoading: $isLoading, workspaces: $workspaces, successOrFailure: $successOrFailure)'; + return 'WelcomeState(isLoading: $isLoading, workspaces: $workspaces, successOrFailure: $successOrFailure)'; } @override bool operator ==(dynamic other) { return identical(this, other) || - (other is _WorkspaceListState && + (other is _WelcomeState && (identical(other.isLoading, isLoading) || const DeepCollectionEquality() .equals(other.isLoading, isLoading)) && @@ -707,16 +756,16 @@ class _$_WorkspaceListState implements _WorkspaceListState { @JsonKey(ignore: true) @override - _$WorkspaceListStateCopyWith<_WorkspaceListState> get copyWith => - __$WorkspaceListStateCopyWithImpl<_WorkspaceListState>(this, _$identity); + _$WelcomeStateCopyWith<_WelcomeState> get copyWith => + __$WelcomeStateCopyWithImpl<_WelcomeState>(this, _$identity); } -abstract class _WorkspaceListState implements WorkspaceListState { - const factory _WorkspaceListState( +abstract class _WelcomeState implements WelcomeState { + const factory _WelcomeState( {required bool isLoading, required List workspaces, required Either successOrFailure}) = - _$_WorkspaceListState; + _$_WelcomeState; @override bool get isLoading => throw _privateConstructorUsedError; @@ -727,6 +776,6 @@ abstract class _WorkspaceListState implements WorkspaceListState { throw _privateConstructorUsedError; @override @JsonKey(ignore: true) - _$WorkspaceListStateCopyWith<_WorkspaceListState> get copyWith => + _$WelcomeStateCopyWith<_WelcomeState> get copyWith => throw _privateConstructorUsedError; } diff --git a/app_flowy/lib/workspace/application/workspace/workspace_list_bloc.dart b/app_flowy/lib/workspace/application/workspace/workspace_list_bloc.dart deleted file mode 100644 index da23a0ca71..0000000000 --- a/app_flowy/lib/workspace/application/workspace/workspace_list_bloc.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:app_flowy/workspace/infrastructure/repos/user_repo.dart'; -import 'package:flowy_infra/flowy_logger.dart'; -import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; -import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:dartz/dartz.dart'; - -part 'workspace_list_bloc.freezed.dart'; - -class WorkspaceListBloc extends Bloc { - UserRepo repo; - WorkspaceListBloc(this.repo) : super(WorkspaceListState.initial()); - - @override - Stream mapEventToState( - WorkspaceListEvent event, - ) async* { - yield* event.map(initial: (e) async* { - yield* _fetchWorkspaces(); - }, openWorkspace: (e) async* { - yield* _openWorkspace(e.workspace); - }, createWorkspace: (e) async* { - yield* _createWorkspace(e.name, e.desc); - }, fetchWorkspaces: (e) async* { - yield* _fetchWorkspaces(); - }); - } - - Stream _fetchWorkspaces() async* { - final workspacesOrFailed = await repo.fetchWorkspaces(); - yield workspacesOrFailed.fold( - (workspaces) => - state.copyWith(workspaces: workspaces, successOrFailure: left(unit)), - (error) { - Log.error(error); - return state.copyWith(successOrFailure: right(error)); - }, - ); - } - - Stream _openWorkspace(Workspace workspace) async* { - final result = await repo.openWorkspace(workspace.id); - yield result.fold( - (workspaces) => state.copyWith(successOrFailure: left(unit)), - (error) { - Log.error(error); - return state.copyWith(successOrFailure: right(error)); - }, - ); - } - - Stream _createWorkspace(String name, String desc) async* { - final result = await repo.createWorkspace(name, desc); - yield result.fold( - (workspace) { - add(const WorkspaceListEvent.fetchWorkspaces()); - return state.copyWith(successOrFailure: left(unit)); - }, - (error) { - Log.error(error); - return state.copyWith(successOrFailure: right(error)); - }, - ); - } -} - -@freezed -abstract class WorkspaceListEvent with _$WorkspaceListEvent { - const factory WorkspaceListEvent.initial() = Initial; - const factory WorkspaceListEvent.fetchWorkspaces() = FetchWorkspace; - const factory WorkspaceListEvent.createWorkspace(String name, String desc) = - CreateWorkspace; - const factory WorkspaceListEvent.openWorkspace(Workspace workspace) = - OpenWorkspace; -} - -@freezed -abstract class WorkspaceListState implements _$WorkspaceListState { - const factory WorkspaceListState({ - required bool isLoading, - required List workspaces, - required Either successOrFailure, - }) = _WorkspaceListState; - - factory WorkspaceListState.initial() => WorkspaceListState( - isLoading: false, - workspaces: List.empty(), - successOrFailure: left(unit), - ); -} diff --git a/app_flowy/lib/workspace/domain/i_user.dart b/app_flowy/lib/workspace/domain/i_user.dart index 0bf887604d..335dd2ec23 100644 --- a/app_flowy/lib/workspace/domain/i_user.dart +++ b/app_flowy/lib/workspace/domain/i_user.dart @@ -8,9 +8,7 @@ export 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart'; export 'package:flowy_sdk/protobuf/flowy-user/errors.pb.dart'; export 'package:flowy_sdk/protobuf/flowy-user/user_profile.pb.dart'; -typedef UserCreateWorkspaceCallback = void Function( - Either, WorkspaceError> workspacesOrFailed); -typedef UserDeleteWorkspaceCallback = void Function( +typedef WorkspaceListUpdatedCallback = void Function( Either, WorkspaceError> workspacesOrFailed); abstract class IUser { @@ -21,10 +19,9 @@ abstract class IUser { Future> signOut(); } -abstract class IUserWatch { +abstract class IUserWorkspaceListWatch { void startWatching( - {UserCreateWorkspaceCallback? createWorkspaceCallback, - UserDeleteWorkspaceCallback? deleteWorkspaceCallback}); + {WorkspaceListUpdatedCallback? workspaceListUpdatedCallback}); Future stopWatching(); } diff --git a/app_flowy/lib/workspace/domain/i_view.dart b/app_flowy/lib/workspace/domain/i_view.dart index 2334b65e57..21e191af32 100644 --- a/app_flowy/lib/workspace/domain/i_view.dart +++ b/app_flowy/lib/workspace/domain/i_view.dart @@ -2,7 +2,7 @@ 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'; -typedef ViewUpdatedCallback = void Function(View view); +typedef ViewUpdatedCallback = void Function(Either); abstract class IView { Future> readView(); diff --git a/app_flowy/lib/workspace/infrastructure/deps_resolver.dart b/app_flowy/lib/workspace/infrastructure/deps_resolver.dart index 94185f4552..74e435180a 100644 --- a/app_flowy/lib/workspace/infrastructure/deps_resolver.dart +++ b/app_flowy/lib/workspace/infrastructure/deps_resolver.dart @@ -7,6 +7,7 @@ import 'package:app_flowy/workspace/application/menu/menu_watch.dart'; import 'package:app_flowy/workspace/application/view/doc_watch_bloc.dart'; import 'package:app_flowy/workspace/application/view/view_bloc.dart'; import 'package:app_flowy/workspace/application/view/view_list_bloc.dart'; +import 'package:app_flowy/workspace/application/workspace/welcome_bloc.dart'; import 'package:app_flowy/workspace/domain/i_doc.dart'; import 'package:app_flowy/workspace/domain/i_view.dart'; import 'package:app_flowy/workspace/domain/page_stack/page_stack.dart'; @@ -57,8 +58,9 @@ class HomeDepsResolver { // User getIt.registerFactoryParam( (user, _) => IUserImpl(repo: UserRepo(user: user))); - getIt.registerFactoryParam( - (user, _) => IUserWatchImpl(repo: UserWatchRepo(user: user))); + getIt.registerFactoryParam( + (user, _) => + IUserWorkspaceListWatchImpl(repo: UserWatchRepo(user: user))); //Menu Bloc getIt.registerFactoryParam( @@ -93,6 +95,13 @@ class HomeDepsResolver { getIt.registerFactoryParam, void>( (views, _) => ViewListBloc(views: views)); + getIt.registerFactoryParam( + (user, _) => WelcomeBloc( + repo: UserRepo(user: user), + watcher: getIt(param1: user), + ), + ); + // getIt.registerFactoryParam( // (viewId, _) => ViewBloc(iViewImpl: getIt(param1: viewId))); } diff --git a/app_flowy/lib/workspace/infrastructure/i_user_impl.dart b/app_flowy/lib/workspace/infrastructure/i_user_impl.dart index 22d4d40508..6e3236585c 100644 --- a/app_flowy/lib/workspace/infrastructure/i_user_impl.dart +++ b/app_flowy/lib/workspace/infrastructure/i_user_impl.dart @@ -31,22 +31,20 @@ class IUserImpl extends IUser { @override Future, WorkspaceError>> fetchWorkspaces() { - return repo.fetchWorkspaces(); + return repo.getWorkspaces(); } } -class IUserWatchImpl extends IUserWatch { +class IUserWorkspaceListWatchImpl extends IUserWorkspaceListWatch { UserWatchRepo repo; - IUserWatchImpl({ + IUserWorkspaceListWatchImpl({ required this.repo, }); @override - void startWatching( - {UserCreateWorkspaceCallback? createWorkspaceCallback, - UserDeleteWorkspaceCallback? deleteWorkspaceCallback}) { - repo.startWatching( - createWorkspace: createWorkspaceCallback, - deleteWorkspace: deleteWorkspaceCallback); + void startWatching({ + WorkspaceListUpdatedCallback? workspaceListUpdatedCallback, + }) { + repo.startWatching(workspaceListUpdated: workspaceListUpdatedCallback); } @override diff --git a/app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart b/app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart index 42a0971697..44707dd48e 100644 --- a/app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart +++ b/app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart @@ -20,9 +20,9 @@ class IWorkspaceImpl extends IWorkspace { @override Future, WorkspaceError>> getApps() { - return repo.getWorkspace().then((result) { + return repo.getApps().then((result) { return result.fold( - (workspace) => left(workspace.apps.items), + (apps) => left(apps), (error) => right(error), ); }); diff --git a/app_flowy/lib/workspace/infrastructure/repos/app_repo.dart b/app_flowy/lib/workspace/infrastructure/repos/app_repo.dart index b0c4dbbbaf..da56945424 100644 --- a/app_flowy/lib/workspace/infrastructure/repos/app_repo.dart +++ b/app_flowy/lib/workspace/infrastructure/repos/app_repo.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:app_flowy/workspace/domain/i_app.dart'; import 'package:dartz/dartz.dart'; import 'package:flowy_infra/flowy_logger.dart'; @@ -11,6 +12,7 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pbenum.dart'; import 'package:flowy_sdk/rust_stream.dart'; +import 'helper.dart'; class AppRepository { String appId; @@ -56,13 +58,12 @@ class AppWatchRepository { AppCreateViewCallback? _createView; AppDeleteViewCallback? _deleteView; AppUpdatedCallback? _update; + late ObservableExtractor _extractor; String appId; - late AppRepository _repo; + AppWatchRepository({ required this.appId, - }) { - _repo = AppRepository(appId: appId); - } + }); void startWatching( {AppCreateViewCallback? createView, @@ -71,52 +72,48 @@ class AppWatchRepository { _createView = createView; _deleteView = deleteView; _update = update; - _subscription = RustStreamReceiver.listen((observable) { - if (observable.subjectId != appId) { - return; - } - - final ty = WorkspaceObservable.valueOf(observable.ty); - if (ty != null) { - _handleObservableType(ty); - } - }); + _extractor = ObservableExtractor( + id: appId, + callback: (ty, result) => _handleObservableType(ty, result), + ); + _subscription = + RustStreamReceiver.listen((observable) => _extractor.parse(observable)); } - void _handleObservableType(WorkspaceObservable ty) { + void _handleObservableType( + WorkspaceObservable ty, Either result) { switch (ty) { case WorkspaceObservable.AppCreateView: - if (_createView == null) { - return; - } - _repo.getViews().then((result) { + if (_createView != null) { result.fold( - (views) => _createView!(left(views)), + (payload) { + final repeatedView = RepeatedView.fromBuffer(payload); + _createView!(left(repeatedView.items)); + }, (error) => _createView!(right(error)), ); - }); + } break; case WorkspaceObservable.AppDeleteView: - if (_deleteView == null) { - return; - } - _repo.getViews().then((result) { + if (_deleteView != null) { result.fold( - (views) => _deleteView!(left(views)), + (payload) => _deleteView!( + left(RepeatedView.fromBuffer(payload).items), + ), (error) => _deleteView!(right(error)), ); - }); + } break; case WorkspaceObservable.AppUpdated: - if (_update == null) { - return; - } - _repo.getAppDesc().then((result) { + if (_update != null) { result.fold( - (app) => _update!(app.name, app.desc), + (payload) { + final app = App.fromBuffer(payload); + _update!(app.name, app.desc); + }, (error) => Log.error(error), ); - }); + } break; default: break; diff --git a/app_flowy/lib/workspace/infrastructure/repos/helper.dart b/app_flowy/lib/workspace/infrastructure/repos/helper.dart new file mode 100644 index 0000000000..adfbbae9d5 --- /dev/null +++ b/app_flowy/lib/workspace/infrastructure/repos/helper.dart @@ -0,0 +1,35 @@ +import 'dart:typed_data'; +import 'package:flowy_sdk/protobuf/flowy-observable/protobuf.dart'; +import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart'; +import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart'; +import 'package:dartz/dartz.dart'; + +class ObservableExtractor { + String id; + void Function(WorkspaceObservable, Either) + callback; + + ObservableExtractor({required this.id, required this.callback}); + + void parse(ObservableSubject subject) { + if (subject.id != id) { + return; + } + + final ty = WorkspaceObservable.valueOf(subject.ty); + if (ty == null) { + return; + } + + if (subject.hasPayload()) { + final bytes = Uint8List.fromList(subject.error); + callback(ty, left(bytes)); + } else if (subject.hasError()) { + final bytes = Uint8List.fromList(subject.error); + final error = WorkspaceError.fromBuffer(bytes); + callback(ty, right(error)); + } else { + // do nothing + } + } +} diff --git a/app_flowy/lib/workspace/infrastructure/repos/user_repo.dart b/app_flowy/lib/workspace/infrastructure/repos/user_repo.dart index 301b645334..f5b8d3015d 100644 --- a/app_flowy/lib/workspace/infrastructure/repos/user_repo.dart +++ b/app_flowy/lib/workspace/infrastructure/repos/user_repo.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:dartz/dartz.dart'; import 'package:flowy_sdk/dispatch/dispatch.dart'; @@ -32,7 +33,7 @@ class UserRepo { return UserEventSignOut().send(); } - Future, WorkspaceError>> fetchWorkspaces() { + Future, WorkspaceError>> getWorkspaces() { final request = QueryWorkspaceRequest.create(); return WorkspaceEventReadWorkspaces(request).send().then((result) { @@ -69,8 +70,7 @@ class UserRepo { class UserWatchRepo { StreamSubscription? _subscription; - UserCreateWorkspaceCallback? _createWorkspace; - UserDeleteWorkspaceCallback? _deleteWorkspace; + WorkspaceListUpdatedCallback? _workspaceListUpdated; late UserRepo _repo; UserWatchRepo({ required UserProfile user, @@ -78,19 +78,16 @@ class UserWatchRepo { _repo = UserRepo(user: user); } - void startWatching( - {UserCreateWorkspaceCallback? createWorkspace, - UserDeleteWorkspaceCallback? deleteWorkspace}) { - _createWorkspace = createWorkspace; - _deleteWorkspace = deleteWorkspace; + void startWatching({WorkspaceListUpdatedCallback? workspaceListUpdated}) { + _workspaceListUpdated = workspaceListUpdated; _subscription = RustStreamReceiver.listen((observable) { - if (observable.subjectId != _repo.user.id) { + if (observable.id != _repo.user.id) { return; } final ty = WorkspaceObservable.valueOf(observable.ty); if (ty != null) { - _handleObservableType(ty); + _handleObservableType(ty, Uint8List.fromList(observable.payload)); } }); } @@ -99,31 +96,23 @@ class UserWatchRepo { await _subscription?.cancel(); } - void _handleObservableType(WorkspaceObservable ty) { + void _handleObservableType(WorkspaceObservable ty, Uint8List payload) { + if (_workspaceListUpdated == null) { + return; + } + switch (ty) { case WorkspaceObservable.UserCreateWorkspace: - if (_createWorkspace == null) { - return; - } - _repo.fetchWorkspaces().then((result) { - result.fold( - (workspaces) => _createWorkspace!(left(workspaces)), - (error) => _createWorkspace!(right(error)), - ); - }); - break; case WorkspaceObservable.UserDeleteWorkspace: - if (_deleteWorkspace == null) { + case WorkspaceObservable.WorkspaceListUpdated: + if (_workspaceListUpdated == null) { return; } - _repo.fetchWorkspaces().then((result) { - result.fold( - (workspaces) => _deleteWorkspace!(left(workspaces)), - (error) => _deleteWorkspace!(right(error)), - ); - }); - break; + final workspaces = RepeatedWorkspace.fromBuffer(payload); + _workspaceListUpdated!(left(workspaces.items)); + + break; default: break; } diff --git a/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart b/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart index 0b5b846757..b314f02d4b 100644 --- a/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart +++ b/app_flowy/lib/workspace/infrastructure/repos/view_repo.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:dartz/dartz.dart'; import 'package:flowy_infra/flowy_logger.dart'; @@ -12,6 +13,8 @@ import 'package:flowy_sdk/rust_stream.dart'; import 'package:app_flowy/workspace/domain/i_view.dart'; +import 'helper.dart'; + class ViewRepository { View view; ViewRepository({ @@ -27,42 +30,41 @@ class ViewRepository { class ViewWatchRepository { StreamSubscription? _subscription; ViewUpdatedCallback? _update; + late ObservableExtractor _extractor; View view; - late ViewRepository _repo; + ViewWatchRepository({ required this.view, - }) { - _repo = ViewRepository(view: view); - } + }); void startWatching({ ViewUpdatedCallback? update, }) { _update = update; - _subscription = RustStreamReceiver.listen((observable) { - if (observable.subjectId != view.id) { - return; - } + _extractor = ObservableExtractor( + id: view.id, + callback: (ty, result) { + _handleObservableType(ty, result); + }, + ); - final ty = WorkspaceObservable.valueOf(observable.ty); - if (ty != null) { - _handleObservableType(ty); - } - }); + _subscription = + RustStreamReceiver.listen((observable) => _extractor.parse(observable)); } - void _handleObservableType(WorkspaceObservable ty) { + void _handleObservableType( + WorkspaceObservable ty, Either result) { switch (ty) { case WorkspaceObservable.ViewUpdated: - if (_update == null) { - return; - } - _repo.readView().then((result) { + if (_update != null) { result.fold( - (view) => _update!(view), - (error) => Log.error(error), + (payload) { + final view = View.fromBuffer(payload); + _update!(left(view)); + }, + (error) => _update!(right(error)), ); - }); + } break; default: break; diff --git a/app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart b/app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart index b50934fbf7..1ab9043937 100644 --- a/app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart +++ b/app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:dartz/dartz.dart'; import 'package:flowy_infra/flowy_logger.dart'; @@ -14,6 +15,8 @@ import 'package:flowy_sdk/rust_stream.dart'; import 'package:app_flowy/workspace/domain/i_workspace.dart'; +import 'helper.dart'; + class WorkspaceRepo { UserProfile user; String workspaceId; @@ -23,25 +26,15 @@ class WorkspaceRepo { }); Future> createApp(String appName, String desc) { - return WorkspaceEventReadCurWorkspace().send().then((result) { - return result.fold( - (workspace) { - final request = CreateAppRequest.create() - ..name = appName - ..workspaceId = workspace.id - ..desc = desc; - return WorkspaceEventCreateApp(request).send(); - }, - (error) { - return right(error); - }, - ); - }); + final request = CreateAppRequest.create() + ..name = appName + ..workspaceId = workspaceId + ..desc = desc; + return WorkspaceEventCreateApp(request).send(); } Future> getWorkspace() { final request = QueryWorkspaceRequest.create()..workspaceId = workspaceId; - return WorkspaceEventReadWorkspaces(request).send().then((result) { return result.fold( (workspaces) { @@ -57,6 +50,16 @@ class WorkspaceRepo { ); }); } + + Future, WorkspaceError>> getApps() { + final request = QueryWorkspaceRequest.create()..workspaceId = workspaceId; + return WorkspaceEventReadWorkspaceApps(request).send().then((result) { + return result.fold( + (apps) => left(apps.items), + (error) => right(error), + ); + }); + } } class WorkspaceWatchRepo { @@ -64,16 +67,14 @@ class WorkspaceWatchRepo { WorkspaceCreateAppCallback? _createApp; WorkspaceDeleteAppCallback? _deleteApp; WorkspaceUpdatedCallback? _update; + late ObservableExtractor _extractor; final UserProfile user; final String workspaceId; - late WorkspaceRepo _repo; WorkspaceWatchRepo({ required this.user, required this.workspaceId, - }) { - _repo = WorkspaceRepo(user: user, workspaceId: workspaceId); - } + }); void startWatching({ WorkspaceCreateAppCallback? createApp, @@ -84,54 +85,50 @@ class WorkspaceWatchRepo { _deleteApp = deleteApp; _update = update; - _subscription = RustStreamReceiver.listen((observable) { - if (observable.subjectId != workspaceId) { - return; - } + _extractor = ObservableExtractor( + id: workspaceId, + callback: (ty, result) { + _handleObservableType(ty, result); + }, + ); - final ty = WorkspaceObservable.valueOf(observable.ty); - if (ty != null) { - _handleObservableType(ty); - } - }); + _subscription = + RustStreamReceiver.listen((observable) => _extractor.parse(observable)); } - void _handleObservableType(WorkspaceObservable ty) { + void _handleObservableType( + WorkspaceObservable ty, Either result) { switch (ty) { case WorkspaceObservable.WorkspaceUpdated: - if (_update == null) { - return; - } - _repo.getWorkspace().then((result) { + if (_update != null) { result.fold( - (workspace) => _update!(workspace.name, workspace.desc), + (payload) { + final workspace = Workspace.fromBuffer(payload); + _update!(workspace.name, workspace.desc); + }, (error) => Log.error(error), ); - }); + } break; case WorkspaceObservable.WorkspaceCreateApp: - if (_createApp == null) { - return; - } - - _repo.getWorkspace().then((result) { + if (_createApp != null) { result.fold( - (workspace) => _createApp!(left(workspace.apps.items)), + (payload) => _createApp!( + left(RepeatedApp.fromBuffer(payload).items), + ), (error) => _createApp!(right(error)), ); - }); - + } break; case WorkspaceObservable.WorkspaceDeleteApp: - if (_deleteApp == null) { - return; - } - _repo.getWorkspace().then((result) { + if (_deleteApp != null) { result.fold( - (workspace) => _deleteApp!(left(workspace.apps.items)), + (payload) => _deleteApp!( + left(RepeatedApp.fromBuffer(payload).items), + ), (error) => _deleteApp!(right(error)), ); - }); + } break; default: break; diff --git a/app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart b/app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart index 6f5e6ac10b..9b7a490c04 100644 --- a/app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart +++ b/app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart @@ -84,6 +84,23 @@ class WorkspaceEventOpenWorkspace { } } +class WorkspaceEventReadWorkspaceApps { + QueryWorkspaceRequest request; + WorkspaceEventReadWorkspaceApps(this.request); + + Future> send() { + final request = FFIRequest.create() + ..event = WorkspaceEvent.ReadWorkspaceApps.toString() + ..payload = requestToBytes(this.request); + + return Dispatch.asyncRequest(request) + .then((bytesResult) => bytesResult.fold( + (okBytes) => left(RepeatedApp.fromBuffer(okBytes)), + (errBytes) => right(WorkspaceError.fromBuffer(errBytes)), + )); + } +} + class WorkspaceEventCreateApp { CreateAppRequest request; WorkspaceEventCreateApp(this.request); diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pb.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pb.dart index fee3f5e319..06e6096588 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pb.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pb.dart @@ -9,22 +9,33 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; -enum ObservableSubject_OneOfSubjectPayload { - subjectPayload, +enum ObservableSubject_OneOfPayload { + payload, + notSet +} + +enum ObservableSubject_OneOfError { + error, notSet } class ObservableSubject extends $pb.GeneratedMessage { - static const $core.Map<$core.int, ObservableSubject_OneOfSubjectPayload> _ObservableSubject_OneOfSubjectPayloadByTag = { - 4 : ObservableSubject_OneOfSubjectPayload.subjectPayload, - 0 : ObservableSubject_OneOfSubjectPayload.notSet + static const $core.Map<$core.int, ObservableSubject_OneOfPayload> _ObservableSubject_OneOfPayloadByTag = { + 4 : ObservableSubject_OneOfPayload.payload, + 0 : ObservableSubject_OneOfPayload.notSet + }; + static const $core.Map<$core.int, ObservableSubject_OneOfError> _ObservableSubject_OneOfErrorByTag = { + 5 : ObservableSubject_OneOfError.error, + 0 : ObservableSubject_OneOfError.notSet }; static final $pb.BuilderInfo _i = $pb.BuilderInfo(const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'ObservableSubject', createEmptyInstance: create) ..oo(0, [4]) + ..oo(1, [5]) ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'category') ..a<$core.int>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'ty', $pb.PbFieldType.O3) - ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'subjectId') - ..a<$core.List<$core.int>>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'subjectPayload', $pb.PbFieldType.OY) + ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'id') + ..a<$core.List<$core.int>>(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'payload', $pb.PbFieldType.OY) + ..a<$core.List<$core.int>>(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'error', $pb.PbFieldType.OY) ..hasRequiredFields = false ; @@ -32,8 +43,9 @@ class ObservableSubject extends $pb.GeneratedMessage { factory ObservableSubject({ $core.String? category, $core.int? ty, - $core.String? subjectId, - $core.List<$core.int>? subjectPayload, + $core.String? id, + $core.List<$core.int>? payload, + $core.List<$core.int>? error, }) { final _result = create(); if (category != null) { @@ -42,11 +54,14 @@ class ObservableSubject extends $pb.GeneratedMessage { if (ty != null) { _result.ty = ty; } - if (subjectId != null) { - _result.subjectId = subjectId; + if (id != null) { + _result.id = id; } - if (subjectPayload != null) { - _result.subjectPayload = subjectPayload; + if (payload != null) { + _result.payload = payload; + } + if (error != null) { + _result.error = error; } return _result; } @@ -71,8 +86,11 @@ class ObservableSubject extends $pb.GeneratedMessage { static ObservableSubject getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); static ObservableSubject? _defaultInstance; - ObservableSubject_OneOfSubjectPayload whichOneOfSubjectPayload() => _ObservableSubject_OneOfSubjectPayloadByTag[$_whichOneof(0)]!; - void clearOneOfSubjectPayload() => clearField($_whichOneof(0)); + ObservableSubject_OneOfPayload whichOneOfPayload() => _ObservableSubject_OneOfPayloadByTag[$_whichOneof(0)]!; + void clearOneOfPayload() => clearField($_whichOneof(0)); + + ObservableSubject_OneOfError whichOneOfError() => _ObservableSubject_OneOfErrorByTag[$_whichOneof(1)]!; + void clearOneOfError() => clearField($_whichOneof(1)); @$pb.TagNumber(1) $core.String get category => $_getSZ(0); @@ -93,21 +111,30 @@ class ObservableSubject extends $pb.GeneratedMessage { void clearTy() => clearField(2); @$pb.TagNumber(3) - $core.String get subjectId => $_getSZ(2); + $core.String get id => $_getSZ(2); @$pb.TagNumber(3) - set subjectId($core.String v) { $_setString(2, v); } + set id($core.String v) { $_setString(2, v); } @$pb.TagNumber(3) - $core.bool hasSubjectId() => $_has(2); + $core.bool hasId() => $_has(2); @$pb.TagNumber(3) - void clearSubjectId() => clearField(3); + void clearId() => clearField(3); @$pb.TagNumber(4) - $core.List<$core.int> get subjectPayload => $_getN(3); + $core.List<$core.int> get payload => $_getN(3); @$pb.TagNumber(4) - set subjectPayload($core.List<$core.int> v) { $_setBytes(3, v); } + set payload($core.List<$core.int> v) { $_setBytes(3, v); } @$pb.TagNumber(4) - $core.bool hasSubjectPayload() => $_has(3); + $core.bool hasPayload() => $_has(3); @$pb.TagNumber(4) - void clearSubjectPayload() => clearField(4); + void clearPayload() => clearField(4); + + @$pb.TagNumber(5) + $core.List<$core.int> get error => $_getN(4); + @$pb.TagNumber(5) + set error($core.List<$core.int> v) { $_setBytes(4, v); } + @$pb.TagNumber(5) + $core.bool hasError() => $_has(4); + @$pb.TagNumber(5) + void clearError() => clearField(5); } diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pbjson.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pbjson.dart index d81e160f35..44b12092c8 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pbjson.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-observable/subject.pbjson.dart @@ -14,13 +14,15 @@ const ObservableSubject$json = const { '2': const [ const {'1': 'category', '3': 1, '4': 1, '5': 9, '10': 'category'}, const {'1': 'ty', '3': 2, '4': 1, '5': 5, '10': 'ty'}, - const {'1': 'subject_id', '3': 3, '4': 1, '5': 9, '10': 'subjectId'}, - const {'1': 'subject_payload', '3': 4, '4': 1, '5': 12, '9': 0, '10': 'subjectPayload'}, + const {'1': 'id', '3': 3, '4': 1, '5': 9, '10': 'id'}, + const {'1': 'payload', '3': 4, '4': 1, '5': 12, '9': 0, '10': 'payload'}, + const {'1': 'error', '3': 5, '4': 1, '5': 12, '9': 1, '10': 'error'}, ], '8': const [ - const {'1': 'one_of_subject_payload'}, + const {'1': 'one_of_payload'}, + const {'1': 'one_of_error'}, ], }; /// Descriptor for `ObservableSubject`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List observableSubjectDescriptor = $convert.base64Decode('ChFPYnNlcnZhYmxlU3ViamVjdBIaCghjYXRlZ29yeRgBIAEoCVIIY2F0ZWdvcnkSDgoCdHkYAiABKAVSAnR5Eh0KCnN1YmplY3RfaWQYAyABKAlSCXN1YmplY3RJZBIpCg9zdWJqZWN0X3BheWxvYWQYBCABKAxIAFIOc3ViamVjdFBheWxvYWRCGAoWb25lX29mX3N1YmplY3RfcGF5bG9hZA=='); +final $typed_data.Uint8List observableSubjectDescriptor = $convert.base64Decode('ChFPYnNlcnZhYmxlU3ViamVjdBIaCghjYXRlZ29yeRgBIAEoCVIIY2F0ZWdvcnkSDgoCdHkYAiABKAVSAnR5Eg4KAmlkGAMgASgJUgJpZBIaCgdwYXlsb2FkGAQgASgMSABSB3BheWxvYWQSFgoFZXJyb3IYBSABKAxIAVIFZXJyb3JCEAoOb25lX29mX3BheWxvYWRCDgoMb25lX29mX2Vycm9y'); diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart index 29ec6ef75a..80362c97b0 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart @@ -15,6 +15,7 @@ class WorkspaceEvent extends $pb.ProtobufEnum { static const WorkspaceEvent ReadWorkspaces = WorkspaceEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadWorkspaces'); static const WorkspaceEvent DeleteWorkspace = WorkspaceEvent._(3, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteWorkspace'); static const WorkspaceEvent OpenWorkspace = WorkspaceEvent._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenWorkspace'); + static const WorkspaceEvent ReadWorkspaceApps = WorkspaceEvent._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadWorkspaceApps'); static const WorkspaceEvent CreateApp = WorkspaceEvent._(101, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateApp'); static const WorkspaceEvent DeleteApp = WorkspaceEvent._(102, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteApp'); static const WorkspaceEvent ReadApp = WorkspaceEvent._(103, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadApp'); @@ -30,6 +31,7 @@ class WorkspaceEvent extends $pb.ProtobufEnum { ReadWorkspaces, DeleteWorkspace, OpenWorkspace, + ReadWorkspaceApps, CreateApp, DeleteApp, ReadApp, diff --git a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart index 943731cba2..acab31ab83 100644 --- a/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart +++ b/app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart @@ -17,6 +17,7 @@ const WorkspaceEvent$json = const { const {'1': 'ReadWorkspaces', '2': 2}, const {'1': 'DeleteWorkspace', '2': 3}, const {'1': 'OpenWorkspace', '2': 4}, + const {'1': 'ReadWorkspaceApps', '2': 5}, const {'1': 'CreateApp', '2': 101}, const {'1': 'DeleteApp', '2': 102}, const {'1': 'ReadApp', '2': 103}, @@ -29,4 +30,4 @@ const WorkspaceEvent$json = const { }; /// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSDQoJQ3JlYXRlQXBwEGUSDQoJRGVsZXRlQXBwEGYSCwoHUmVhZEFwcBBnEg0KCVVwZGF0ZUFwcBBoEg8KCkNyZWF0ZVZpZXcQyQESDQoIUmVhZFZpZXcQygESDwoKVXBkYXRlVmlldxDLARIPCgpEZWxldGVWaWV3EMwB'); +final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAE='); diff --git a/rust-lib/flowy-database/src/macros.rs b/rust-lib/flowy-database/src/macros.rs index 764af40956..babd102a75 100644 --- a/rust-lib/flowy-database/src/macros.rs +++ b/rust-lib/flowy-database/src/macros.rs @@ -1,14 +1,77 @@ +#[rustfmt::skip] +/* +diesel master support on_conflict on sqlite but not 1.4.7 version. Workaround for this + +match dsl::workspace_table + .filter(workspace_table::id.eq(table.id.clone())) + .count() + .get_result(conn) + .unwrap_or(0) +{ + 0 => diesel::insert_into(workspace_table::table).values(table) + .on_conflict(workspace_table::id) + .do_update() + .set(WorkspaceTableChangeset::from_table(workspace_table)) + .execute(conn)?, + _ => { + let changeset = WorkspaceTableChangeset::from_table(table); + let filter = dsl::workspace_table.filter(workspace_table::id.eq(changeset.id.clone())); + diesel::update(filter).set(changeset).execute(conn)?; + }, +} + +is equivalent to: + +match diesel_record_count!(workspace_table, &table.id, conn) { + 0 => diesel_insert_table!(workspace_table, table, conn), + _ => diesel_update_table!(workspace_table, WorkspaceTableChangeset::from_table(table), &*conn), +} +*/ + +#[macro_export] +macro_rules! diesel_insert_table { + ( + $table_name:ident, + $table:expr, + $connection:expr + ) => { + { + let _ = diesel::insert_into($table_name::table) + .values($table.clone()) + // .on_conflict($table_name::dsl::id) + // .do_update() + // .set(WorkspaceTableChangeset::from_table(workspace_table)) + .execute($connection)?; + } + }; +} + +#[macro_export] +macro_rules! diesel_record_count { + ( + $table_name:ident, + $id:expr, + $connection:expr + ) => { + $table_name::dsl::$table_name + .filter($table_name::dsl::id.eq($id.clone())) + .count() + .get_result($connection) + .unwrap_or(0); + }; +} + #[macro_export] macro_rules! diesel_update_table { ( $table_name:ident, - $changeset:ident, + $changeset:expr, $connection:expr - ) => { + ) => {{ let filter = $table_name::dsl::$table_name.filter($table_name::dsl::id.eq($changeset.id.clone())); - let affected_row = diesel::update(filter).set($changeset).execute(&*$connection)?; + let affected_row = diesel::update(filter).set($changeset).execute($connection)?; debug_assert_eq!(affected_row, 1); - }; + }}; } #[macro_export] diff --git a/rust-lib/flowy-document/src/sql_tables/doc/doc_sql.rs b/rust-lib/flowy-document/src/sql_tables/doc/doc_sql.rs index d93b5dfccb..6a41b3140f 100644 --- a/rust-lib/flowy-document/src/sql_tables/doc/doc_sql.rs +++ b/rust-lib/flowy-document/src/sql_tables/doc/doc_sql.rs @@ -16,15 +16,13 @@ pub struct DocTableSql { impl DocTableSql { pub(crate) fn create_doc_table(&self, doc_table: DocTable) -> Result<(), DocError> { let conn = self.database.db_connection()?; - let _ = diesel::insert_into(doc_table::table) - .values(doc_table) - .execute(&*conn)?; + let _ = diesel::insert_into(doc_table::table).values(doc_table).execute(&*conn)?; Ok(()) } pub(crate) fn update_doc_table(&self, changeset: DocTableChangeset) -> Result<(), DocError> { let conn = self.database.db_connection()?; - diesel_update_table!(doc_table, changeset, conn); + diesel_update_table!(doc_table, changeset, &*conn); Ok(()) } diff --git a/rust-lib/flowy-observable/src/entities/subject.rs b/rust-lib/flowy-observable/src/entities/subject.rs index 89ed245db0..6ab3fe0c99 100644 --- a/rust-lib/flowy-observable/src/entities/subject.rs +++ b/rust-lib/flowy-observable/src/entities/subject.rs @@ -9,10 +9,13 @@ pub struct ObservableSubject { pub ty: i32, #[pb(index = 3)] - pub subject_id: String, + pub id: String, #[pb(index = 4, one_of)] - pub subject_payload: Option>, + pub payload: Option>, + + #[pb(index = 5, one_of)] + pub error: Option>, } impl std::default::Default for ObservableSubject { @@ -20,8 +23,9 @@ impl std::default::Default for ObservableSubject { Self { category: "".to_string(), ty: 0, - subject_id: "".to_string(), - subject_payload: None, + id: "".to_string(), + payload: None, + error: None, } } } diff --git a/rust-lib/flowy-observable/src/protobuf/model/subject.rs b/rust-lib/flowy-observable/src/protobuf/model/subject.rs index 7497b4001b..d7ead2e78d 100644 --- a/rust-lib/flowy-observable/src/protobuf/model/subject.rs +++ b/rust-lib/flowy-observable/src/protobuf/model/subject.rs @@ -28,9 +28,10 @@ pub struct ObservableSubject { // message fields pub category: ::std::string::String, pub ty: i32, - pub subject_id: ::std::string::String, + pub id: ::std::string::String, // message oneof groups - pub one_of_subject_payload: ::std::option::Option, + pub one_of_payload: ::std::option::Option, + pub one_of_error: ::std::option::Option, // special fields pub unknown_fields: ::protobuf::UnknownFields, pub cached_size: ::protobuf::CachedSize, @@ -43,8 +44,13 @@ impl<'a> ::std::default::Default for &'a ObservableSubject { } #[derive(Clone,PartialEq,Debug)] -pub enum ObservableSubject_oneof_one_of_subject_payload { - subject_payload(::std::vec::Vec), +pub enum ObservableSubject_oneof_one_of_payload { + payload(::std::vec::Vec), +} + +#[derive(Clone,PartialEq,Debug)] +pub enum ObservableSubject_oneof_one_of_error { + error(::std::vec::Vec), } impl ObservableSubject { @@ -93,74 +99,123 @@ impl ObservableSubject { self.ty = v; } - // string subject_id = 3; + // string id = 3; - pub fn get_subject_id(&self) -> &str { - &self.subject_id + pub fn get_id(&self) -> &str { + &self.id } - pub fn clear_subject_id(&mut self) { - self.subject_id.clear(); + pub fn clear_id(&mut self) { + self.id.clear(); } // Param is passed by value, moved - pub fn set_subject_id(&mut self, v: ::std::string::String) { - self.subject_id = v; + pub fn set_id(&mut self, v: ::std::string::String) { + self.id = v; } // Mutable pointer to the field. // If field is not initialized, it is initialized with default value first. - pub fn mut_subject_id(&mut self) -> &mut ::std::string::String { - &mut self.subject_id + pub fn mut_id(&mut self) -> &mut ::std::string::String { + &mut self.id } // Take field - pub fn take_subject_id(&mut self) -> ::std::string::String { - ::std::mem::replace(&mut self.subject_id, ::std::string::String::new()) + pub fn take_id(&mut self) -> ::std::string::String { + ::std::mem::replace(&mut self.id, ::std::string::String::new()) } - // bytes subject_payload = 4; + // bytes payload = 4; - pub fn get_subject_payload(&self) -> &[u8] { - match self.one_of_subject_payload { - ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(ref v)) => v, + pub fn get_payload(&self) -> &[u8] { + match self.one_of_payload { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(ref v)) => v, _ => &[], } } - pub fn clear_subject_payload(&mut self) { - self.one_of_subject_payload = ::std::option::Option::None; + pub fn clear_payload(&mut self) { + self.one_of_payload = ::std::option::Option::None; } - pub fn has_subject_payload(&self) -> bool { - match self.one_of_subject_payload { - ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(..)) => true, + pub fn has_payload(&self) -> bool { + match self.one_of_payload { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(..)) => true, _ => false, } } // Param is passed by value, moved - pub fn set_subject_payload(&mut self, v: ::std::vec::Vec) { - self.one_of_subject_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(v)) + pub fn set_payload(&mut self, v: ::std::vec::Vec) { + self.one_of_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(v)) } // Mutable pointer to the field. - pub fn mut_subject_payload(&mut self) -> &mut ::std::vec::Vec { - if let ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(_)) = self.one_of_subject_payload { + pub fn mut_payload(&mut self) -> &mut ::std::vec::Vec { + if let ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(_)) = self.one_of_payload { } else { - self.one_of_subject_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(::std::vec::Vec::new())); + self.one_of_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(::std::vec::Vec::new())); } - match self.one_of_subject_payload { - ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(ref mut v)) => v, + match self.one_of_payload { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(ref mut v)) => v, _ => panic!(), } } // Take field - pub fn take_subject_payload(&mut self) -> ::std::vec::Vec { - if self.has_subject_payload() { - match self.one_of_subject_payload.take() { - ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(v)) => v, + pub fn take_payload(&mut self) -> ::std::vec::Vec { + if self.has_payload() { + match self.one_of_payload.take() { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(v)) => v, + _ => panic!(), + } + } else { + ::std::vec::Vec::new() + } + } + + // bytes error = 5; + + + pub fn get_error(&self) -> &[u8] { + match self.one_of_error { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(ref v)) => v, + _ => &[], + } + } + pub fn clear_error(&mut self) { + self.one_of_error = ::std::option::Option::None; + } + + pub fn has_error(&self) -> bool { + match self.one_of_error { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(..)) => true, + _ => false, + } + } + + // Param is passed by value, moved + pub fn set_error(&mut self, v: ::std::vec::Vec) { + self.one_of_error = ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(v)) + } + + // Mutable pointer to the field. + pub fn mut_error(&mut self) -> &mut ::std::vec::Vec { + if let ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(_)) = self.one_of_error { + } else { + self.one_of_error = ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(::std::vec::Vec::new())); + } + match self.one_of_error { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(ref mut v)) => v, + _ => panic!(), + } + } + + // Take field + pub fn take_error(&mut self) -> ::std::vec::Vec { + if self.has_error() { + match self.one_of_error.take() { + ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(v)) => v, _ => panic!(), } } else { @@ -189,13 +244,19 @@ impl ::protobuf::Message for ObservableSubject { self.ty = tmp; }, 3 => { - ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.subject_id)?; + ::protobuf::rt::read_singular_proto3_string_into(wire_type, is, &mut self.id)?; }, 4 => { if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited { return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); } - self.one_of_subject_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_subject_payload::subject_payload(is.read_bytes()?)); + self.one_of_payload = ::std::option::Option::Some(ObservableSubject_oneof_one_of_payload::payload(is.read_bytes()?)); + }, + 5 => { + if wire_type != ::protobuf::wire_format::WireTypeLengthDelimited { + return ::std::result::Result::Err(::protobuf::rt::unexpected_wire_type(wire_type)); + } + self.one_of_error = ::std::option::Option::Some(ObservableSubject_oneof_one_of_error::error(is.read_bytes()?)); }, _ => { ::protobuf::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?; @@ -215,16 +276,23 @@ impl ::protobuf::Message for ObservableSubject { if self.ty != 0 { my_size += ::protobuf::rt::value_size(2, self.ty, ::protobuf::wire_format::WireTypeVarint); } - if !self.subject_id.is_empty() { - my_size += ::protobuf::rt::string_size(3, &self.subject_id); + if !self.id.is_empty() { + my_size += ::protobuf::rt::string_size(3, &self.id); } - if let ::std::option::Option::Some(ref v) = self.one_of_subject_payload { + if let ::std::option::Option::Some(ref v) = self.one_of_payload { match v { - &ObservableSubject_oneof_one_of_subject_payload::subject_payload(ref v) => { + &ObservableSubject_oneof_one_of_payload::payload(ref v) => { my_size += ::protobuf::rt::bytes_size(4, &v); }, }; } + if let ::std::option::Option::Some(ref v) = self.one_of_error { + match v { + &ObservableSubject_oneof_one_of_error::error(ref v) => { + my_size += ::protobuf::rt::bytes_size(5, &v); + }, + }; + } my_size += ::protobuf::rt::unknown_fields_size(self.get_unknown_fields()); self.cached_size.set(my_size); my_size @@ -237,16 +305,23 @@ impl ::protobuf::Message for ObservableSubject { if self.ty != 0 { os.write_int32(2, self.ty)?; } - if !self.subject_id.is_empty() { - os.write_string(3, &self.subject_id)?; + if !self.id.is_empty() { + os.write_string(3, &self.id)?; } - if let ::std::option::Option::Some(ref v) = self.one_of_subject_payload { + if let ::std::option::Option::Some(ref v) = self.one_of_payload { match v { - &ObservableSubject_oneof_one_of_subject_payload::subject_payload(ref v) => { + &ObservableSubject_oneof_one_of_payload::payload(ref v) => { os.write_bytes(4, v)?; }, }; } + if let ::std::option::Option::Some(ref v) = self.one_of_error { + match v { + &ObservableSubject_oneof_one_of_error::error(ref v) => { + os.write_bytes(5, v)?; + }, + }; + } os.write_unknown_fields(self.get_unknown_fields())?; ::std::result::Result::Ok(()) } @@ -296,14 +371,19 @@ impl ::protobuf::Message for ObservableSubject { |m: &mut ObservableSubject| { &mut m.ty }, )); fields.push(::protobuf::reflect::accessor::make_simple_field_accessor::<_, ::protobuf::types::ProtobufTypeString>( - "subject_id", - |m: &ObservableSubject| { &m.subject_id }, - |m: &mut ObservableSubject| { &mut m.subject_id }, + "id", + |m: &ObservableSubject| { &m.id }, + |m: &mut ObservableSubject| { &mut m.id }, )); fields.push(::protobuf::reflect::accessor::make_singular_bytes_accessor::<_>( - "subject_payload", - ObservableSubject::has_subject_payload, - ObservableSubject::get_subject_payload, + "payload", + ObservableSubject::has_payload, + ObservableSubject::get_payload, + )); + fields.push(::protobuf::reflect::accessor::make_singular_bytes_accessor::<_>( + "error", + ObservableSubject::has_error, + ObservableSubject::get_error, )); ::protobuf::reflect::MessageDescriptor::new_pb_name::( "ObservableSubject", @@ -323,8 +403,9 @@ impl ::protobuf::Clear for ObservableSubject { fn clear(&mut self) { self.category.clear(); self.ty = 0; - self.subject_id.clear(); - self.one_of_subject_payload = ::std::option::Option::None; + self.id.clear(); + self.one_of_payload = ::std::option::Option::None; + self.one_of_error = ::std::option::Option::None; self.unknown_fields.clear(); } } @@ -342,25 +423,29 @@ impl ::protobuf::reflect::ProtobufValue for ObservableSubject { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\rsubject.proto\"\xa3\x01\n\x11ObservableSubject\x12\x1a\n\x08category\ + \n\rsubject.proto\"\xa5\x01\n\x11ObservableSubject\x12\x1a\n\x08category\ \x18\x01\x20\x01(\tR\x08category\x12\x0e\n\x02ty\x18\x02\x20\x01(\x05R\ - \x02ty\x12\x1d\n\nsubject_id\x18\x03\x20\x01(\tR\tsubjectId\x12)\n\x0fsu\ - bject_payload\x18\x04\x20\x01(\x0cH\0R\x0esubjectPayloadB\x18\n\x16one_o\ - f_subject_payloadJ\xa1\x02\n\x06\x12\x04\0\0\x07\x01\n\x08\n\x01\x0c\x12\ - \x03\0\0\x12\n\n\n\x02\x04\0\x12\x04\x02\0\x07\x01\n\n\n\x03\x04\0\x01\ - \x12\x03\x02\x08\x19\n\x0b\n\x04\x04\0\x02\0\x12\x03\x03\x04\x18\n\x0c\n\ - \x05\x04\0\x02\0\x05\x12\x03\x03\x04\n\n\x0c\n\x05\x04\0\x02\0\x01\x12\ - \x03\x03\x0b\x13\n\x0c\n\x05\x04\0\x02\0\x03\x12\x03\x03\x16\x17\n\x0b\n\ - \x04\x04\0\x02\x01\x12\x03\x04\x04\x11\n\x0c\n\x05\x04\0\x02\x01\x05\x12\ - \x03\x04\x04\t\n\x0c\n\x05\x04\0\x02\x01\x01\x12\x03\x04\n\x0c\n\x0c\n\ - \x05\x04\0\x02\x01\x03\x12\x03\x04\x0f\x10\n\x0b\n\x04\x04\0\x02\x02\x12\ - \x03\x05\x04\x1a\n\x0c\n\x05\x04\0\x02\x02\x05\x12\x03\x05\x04\n\n\x0c\n\ - \x05\x04\0\x02\x02\x01\x12\x03\x05\x0b\x15\n\x0c\n\x05\x04\0\x02\x02\x03\ - \x12\x03\x05\x18\x19\n\x0b\n\x04\x04\0\x08\0\x12\x03\x06\x04?\n\x0c\n\ - \x05\x04\0\x08\0\x01\x12\x03\x06\n\x20\n\x0b\n\x04\x04\0\x02\x03\x12\x03\ - \x06#=\n\x0c\n\x05\x04\0\x02\x03\x05\x12\x03\x06#(\n\x0c\n\x05\x04\0\x02\ - \x03\x01\x12\x03\x06)8\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06; = ::protobuf::rt::LazyV2::INIT; diff --git a/rust-lib/flowy-observable/src/protobuf/proto/subject.proto b/rust-lib/flowy-observable/src/protobuf/proto/subject.proto index 97efe0d9c2..00609cf63c 100644 --- a/rust-lib/flowy-observable/src/protobuf/proto/subject.proto +++ b/rust-lib/flowy-observable/src/protobuf/proto/subject.proto @@ -3,6 +3,7 @@ syntax = "proto3"; message ObservableSubject { string category = 1; int32 ty = 2; - string subject_id = 3; - oneof one_of_subject_payload { bytes subject_payload = 4; }; + string id = 3; + oneof one_of_payload { bytes payload = 4; }; + oneof one_of_error { bytes error = 5; }; } diff --git a/rust-lib/flowy-sdk/src/lib.rs b/rust-lib/flowy-sdk/src/lib.rs index 38cfec9332..1115d982d5 100644 --- a/rust-lib/flowy-sdk/src/lib.rs +++ b/rust-lib/flowy-sdk/src/lib.rs @@ -22,16 +22,26 @@ impl FlowySDKConfig { pub fn new(root: &str) -> Self { FlowySDKConfig { root: root.to_owned(), - log_filter: std::env::var("RUST_LOG").unwrap_or("info".to_owned()), + log_filter: crate_log_filter(None), } } pub fn log_filter(mut self, filter: &str) -> Self { - self.log_filter = filter.to_owned(); + self.log_filter = crate_log_filter(Some(filter.to_owned())); self } } +fn crate_log_filter(level: Option) -> String { + let level = level.unwrap_or(std::env::var("RUST_LOG").unwrap_or("info".to_owned())); + let mut filters = vec![]; + filters.push(format!("flowy_sdk={}", level)); + filters.push(format!("flowy_workspace={}", level)); + filters.push(format!("flowy_user={}", level)); + filters.push(format!("info")); + filters.join(",") +} + #[derive(Clone)] pub struct FlowySDK { config: FlowySDKConfig, diff --git a/rust-lib/flowy-user/src/services/user/user_session.rs b/rust-lib/flowy-user/src/services/user/user_session.rs index 4a176523c6..850125852f 100644 --- a/rust-lib/flowy-user/src/services/user/user_session.rs +++ b/rust-lib/flowy-user/src/services/user/user_session.rs @@ -111,7 +111,7 @@ impl UserSession { pub async fn update_user(&self, params: UpdateUserParams) -> Result<(), UserError> { let session = self.get_session()?; let changeset = UserTableChangeset::new(params.clone()); - diesel_update_table!(user_table, changeset, self.db_conn()?); + diesel_update_table!(user_table, changeset, &*self.db_conn()?); let _ = self.update_user_on_server(&session.token, params).await?; Ok(()) @@ -231,7 +231,7 @@ impl UserSession { pub async fn update_user(_server: Server, pool: Arc, params: UpdateUserParams) -> Result<(), UserError> { let changeset = UserTableChangeset::new(params); let conn = pool.get()?; - diesel_update_table!(user_table, changeset, conn); + diesel_update_table!(user_table, changeset, &*conn); Ok(()) } diff --git a/rust-lib/flowy-workspace/Cargo.toml b/rust-lib/flowy-workspace/Cargo.toml index 3f89cf7186..febcaea55b 100644 --- a/rust-lib/flowy-workspace/Cargo.toml +++ b/rust-lib/flowy-workspace/Cargo.toml @@ -19,6 +19,9 @@ protobuf = {version = "2.18.0"} log = "0.4.14" diesel = {version = "1.4.7", features = ["sqlite"]} diesel_derives = {version = "1.4.1", features = ["sqlite"]} +#diesel = { git = "https://github.com/diesel-rs/diesel.git", branch = "master", features = ["sqlite"] } +#diesel_derives = { git = "https://github.com/diesel-rs/diesel.git", branch = "master",features = ["sqlite"] } + futures-core = { version = "0.3", default-features = false } pin-project = "1.0.0" strum = "0.21" diff --git a/rust-lib/flowy-workspace/src/event.rs b/rust-lib/flowy-workspace/src/event.rs index ee1a239989..7a1391662a 100644 --- a/rust-lib/flowy-workspace/src/event.rs +++ b/rust-lib/flowy-workspace/src/event.rs @@ -5,41 +5,44 @@ use strum_macros::Display; #[event_err = "WorkspaceError"] pub enum WorkspaceEvent { #[event(input = "CreateWorkspaceRequest", output = "Workspace")] - CreateWorkspace = 0, + CreateWorkspace = 0, #[event(output = "Workspace")] - ReadCurWorkspace = 1, + ReadCurWorkspace = 1, #[event(input = "QueryWorkspaceRequest", output = "RepeatedWorkspace")] - ReadWorkspaces = 2, + ReadWorkspaces = 2, #[event(input = "DeleteWorkspaceRequest")] - DeleteWorkspace = 3, + DeleteWorkspace = 3, #[event(input = "QueryWorkspaceRequest", output = "Workspace")] - OpenWorkspace = 4, + OpenWorkspace = 4, + + #[event(input = "QueryWorkspaceRequest", output = "RepeatedApp")] + ReadWorkspaceApps = 5, #[event(input = "CreateAppRequest", output = "App")] - CreateApp = 101, + CreateApp = 101, #[event(input = "DeleteAppRequest")] - DeleteApp = 102, + DeleteApp = 102, #[event(input = "QueryAppRequest", output = "App")] - ReadApp = 103, + ReadApp = 103, #[event(input = "UpdateAppRequest")] - UpdateApp = 104, + UpdateApp = 104, #[event(input = "CreateViewRequest", output = "View")] - CreateView = 201, + CreateView = 201, #[event(input = "QueryViewRequest", output = "View")] - ReadView = 202, + ReadView = 202, #[event(input = "UpdateViewRequest")] - UpdateView = 203, + UpdateView = 203, #[event(input = "DeleteViewRequest")] - DeleteView = 204, + DeleteView = 204, } diff --git a/rust-lib/flowy-workspace/src/handlers/app_handler.rs b/rust-lib/flowy-workspace/src/handlers/app_handler.rs index 8d6928cc80..c6bfa601fb 100644 --- a/rust-lib/flowy-workspace/src/handlers/app_handler.rs +++ b/rust-lib/flowy-workspace/src/handlers/app_handler.rs @@ -55,8 +55,7 @@ pub(crate) async fn read_app_handler( // The View's belonging is the view indexed by the belong_to_id for now if params.read_belongings { - let views = view_controller.read_views_belong_to(¶ms.app_id).await?; - app.belongings = RepeatedView { items: views }; + app.belongings = view_controller.read_views_belong_to(¶ms.app_id).await?; } data_result(app) diff --git a/rust-lib/flowy-workspace/src/handlers/view_handler.rs b/rust-lib/flowy-workspace/src/handlers/view_handler.rs index f54bd25416..20547c91d5 100644 --- a/rust-lib/flowy-workspace/src/handlers/view_handler.rs +++ b/rust-lib/flowy-workspace/src/handlers/view_handler.rs @@ -36,8 +36,7 @@ pub(crate) async fn read_view_handler( let mut view = controller.read_view(params.clone()).await?; if params.read_belongings { - let views = controller.read_views_belong_to(¶ms.view_id).await?; - view.belongings = RepeatedView { items: views } + view.belongings = controller.read_views_belong_to(¶ms.view_id).await?; } data_result(view) diff --git a/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs b/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs index 7a7105a5ed..cdd879ce2f 100644 --- a/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs +++ b/rust-lib/flowy-workspace/src/handlers/workspace_handler.rs @@ -1,4 +1,8 @@ -use crate::{entities::workspace::*, errors::WorkspaceError, services::WorkspaceController}; +use crate::{ + entities::{app::RepeatedApp, workspace::*}, + errors::WorkspaceError, + services::WorkspaceController, +}; use flowy_dispatch::prelude::{data_result, Data, DataResult, Unit}; use std::{convert::TryInto, sync::Arc}; @@ -19,6 +23,12 @@ pub(crate) async fn read_cur_workspace_handler(controller: Unit>) -> DataResult { + let repeated_app = controller.read_workspace_apps().await?; + data_result(repeated_app) +} + #[tracing::instrument(skip(data, controller), err)] pub(crate) async fn read_workspaces_handler( data: Data, diff --git a/rust-lib/flowy-workspace/src/module.rs b/rust-lib/flowy-workspace/src/module.rs index 61f0be34f5..f731581697 100644 --- a/rust-lib/flowy-workspace/src/module.rs +++ b/rust-lib/flowy-workspace/src/module.rs @@ -52,7 +52,8 @@ pub fn create(user: Arc, database: Arc .event(WorkspaceEvent::CreateWorkspace, create_workspace_handler) .event(WorkspaceEvent::ReadCurWorkspace, read_cur_workspace_handler) .event(WorkspaceEvent::ReadWorkspaces, read_workspaces_handler) - .event(WorkspaceEvent::OpenWorkspace, open_workspace_handler); + .event(WorkspaceEvent::OpenWorkspace, open_workspace_handler) + .event(WorkspaceEvent::ReadWorkspaceApps, read_workspace_apps_handler); module = module .event(WorkspaceEvent::CreateApp, create_app_handler) diff --git a/rust-lib/flowy-workspace/src/observable/observable.rs b/rust-lib/flowy-workspace/src/observable/observable.rs index f19bdafdeb..b23f74aff3 100644 --- a/rust-lib/flowy-workspace/src/observable/observable.rs +++ b/rust-lib/flowy-workspace/src/observable/observable.rs @@ -28,22 +28,23 @@ impl std::default::Default for WorkspaceObservable { fn default() -> Self { WorkspaceObservable::Unknown } } -pub(crate) struct ObservableSender { - ty: WorkspaceObservable, - subject_id: String, +pub(crate) struct ObservableBuilder { + id: String, payload: Option, + error: Option, + ty: WorkspaceObservable, } -impl ObservableSender { - pub(crate) fn new(subject_id: &str, ty: WorkspaceObservable) -> Self { +impl ObservableBuilder { + pub(crate) fn new(id: &str, ty: WorkspaceObservable) -> Self { Self { - subject_id: subject_id.to_owned(), + id: id.to_owned(), ty, payload: None, + error: None, } } - #[allow(dead_code)] pub(crate) fn payload(mut self, payload: T) -> Self where T: ToBytes, @@ -58,10 +59,28 @@ impl ObservableSender { self } - pub(crate) fn send(self) { - log::trace!("Workspace observable id: {}, ty: {:?}", self.subject_id, self.ty); + pub(crate) fn error(mut self, error: T) -> Self + where + T: ToBytes, + { + match error.into_bytes() { + Ok(bytes) => self.error = Some(bytes), + Err(e) => { + log::error!("Set observable error failed: {:?}", e); + }, + } + self + } - let subject_payload = match self.payload { + pub(crate) fn build(self) { + log::trace!("Workspace observable id: {}, ty: {:?}", self.id, self.ty); + + let payload = match self.payload { + None => None, + Some(bytes) => Some(bytes.to_vec()), + }; + + let error = match self.error { None => None, Some(bytes) => Some(bytes.to_vec()), }; @@ -69,8 +88,9 @@ impl ObservableSender { let subject = ObservableSubject { category: OBSERVABLE_CATEGORY.to_string(), ty: self.ty as i32, - subject_id: self.subject_id, - subject_payload, + id: self.id, + payload, + error, }; match RustStreamSender::post(subject) { Ok(_) => {}, @@ -78,13 +98,3 @@ impl ObservableSender { } } } - -pub(crate) fn send_observable(id: &str, ty: WorkspaceObservable) { ObservableSender::new(id, ty).send(); } - -#[allow(dead_code)] -pub(crate) fn send_observable_with_payload(id: &str, ty: WorkspaceObservable, payload: T) -where - T: ToBytes, -{ - ObservableSender::new(id, ty).payload(payload).send(); -} diff --git a/rust-lib/flowy-workspace/src/protobuf/model/event.rs b/rust-lib/flowy-workspace/src/protobuf/model/event.rs index c9e2dd29ca..28243edd5c 100644 --- a/rust-lib/flowy-workspace/src/protobuf/model/event.rs +++ b/rust-lib/flowy-workspace/src/protobuf/model/event.rs @@ -30,6 +30,7 @@ pub enum WorkspaceEvent { ReadWorkspaces = 2, DeleteWorkspace = 3, OpenWorkspace = 4, + ReadWorkspaceApps = 5, CreateApp = 101, DeleteApp = 102, ReadApp = 103, @@ -52,6 +53,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent { 2 => ::std::option::Option::Some(WorkspaceEvent::ReadWorkspaces), 3 => ::std::option::Option::Some(WorkspaceEvent::DeleteWorkspace), 4 => ::std::option::Option::Some(WorkspaceEvent::OpenWorkspace), + 5 => ::std::option::Option::Some(WorkspaceEvent::ReadWorkspaceApps), 101 => ::std::option::Option::Some(WorkspaceEvent::CreateApp), 102 => ::std::option::Option::Some(WorkspaceEvent::DeleteApp), 103 => ::std::option::Option::Some(WorkspaceEvent::ReadApp), @@ -71,6 +73,7 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent { WorkspaceEvent::ReadWorkspaces, WorkspaceEvent::DeleteWorkspace, WorkspaceEvent::OpenWorkspace, + WorkspaceEvent::ReadWorkspaceApps, WorkspaceEvent::CreateApp, WorkspaceEvent::DeleteApp, WorkspaceEvent::ReadApp, @@ -107,41 +110,44 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent { } static file_descriptor_proto_data: &'static [u8] = b"\ - \n\x0bevent.proto*\xf3\x01\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\ + \n\x0bevent.proto*\x8a\x02\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\ ace\x10\0\x12\x14\n\x10ReadCurWorkspace\x10\x01\x12\x12\n\x0eReadWorkspa\ ces\x10\x02\x12\x13\n\x0fDeleteWorkspace\x10\x03\x12\x11\n\rOpenWorkspac\ - e\x10\x04\x12\r\n\tCreateApp\x10e\x12\r\n\tDeleteApp\x10f\x12\x0b\n\x07R\ - eadApp\x10g\x12\r\n\tUpdateApp\x10h\x12\x0f\n\nCreateView\x10\xc9\x01\ - \x12\r\n\x08ReadView\x10\xca\x01\x12\x0f\n\nUpdateView\x10\xcb\x01\x12\ - \x0f\n\nDeleteView\x10\xcc\x01J\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\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\ - \x04\x18\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\ - \0\x02\0\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\ - \x04\x19\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\ - \x05\0\x02\x01\x02\x12\x03\x04\x17\x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\ - \x05\x04\x17\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x12\n\x0c\n\ - \x05\x05\0\x02\x02\x02\x12\x03\x05\x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\ - \x03\x06\x04\x18\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\ - \n\x05\x05\0\x02\x03\x02\x12\x03\x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\ - \x12\x03\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\ - \x0c\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\ - \x05\x12\x03\x08\x04\x14\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\r\ - \n\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x10\x13\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\r\n\ - \x0c\n\x05\x05\0\x02\x06\x02\x12\x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\ - \x12\x03\n\x04\x12\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\x0b\n\x0c\ - \n\x05\x05\0\x02\x07\x02\x12\x03\n\x0e\x11\n\x0b\n\x04\x05\0\x02\x08\x12\ - \x03\x0b\x04\x14\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\r\n\x0c\n\ - \x05\x05\0\x02\x08\x02\x12\x03\x0b\x10\x13\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\x0e\n\x0c\n\ - \x05\x05\0\x02\t\x02\x12\x03\x0c\x11\x14\n\x0b\n\x04\x05\0\x02\n\x12\x03\ - \r\x04\x13\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x0c\n\x0c\n\x05\x05\ - \0\x02\n\x02\x12\x03\r\x0f\x12\n\x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\ - \x15\n\x0c\n\x05\x05\0\x02\x0b\x01\x12\x03\x0e\x04\x0e\n\x0c\n\x05\x05\0\ - \x02\x0b\x02\x12\x03\x0e\x11\x14\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x0f\ - \x04\x15\n\x0c\n\x05\x05\0\x02\x0c\x01\x12\x03\x0f\x04\x0e\n\x0c\n\x05\ - \x05\0\x02\x0c\x02\x12\x03\x0f\x11\x14b\x06proto3\ + e\x10\x04\x12\x15\n\x11ReadWorkspaceApps\x10\x05\x12\r\n\tCreateApp\x10e\ + \x12\r\n\tDeleteApp\x10f\x12\x0b\n\x07ReadApp\x10g\x12\r\n\tUpdateApp\ + \x10h\x12\x0f\n\nCreateView\x10\xc9\x01\x12\r\n\x08ReadView\x10\xca\x01\ + \x12\x0f\n\nUpdateView\x10\xcb\x01\x12\x0f\n\nDeleteView\x10\xcc\x01J\ + \xe8\x04\n\x06\x12\x04\0\0\x11\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\ + \x02\x05\0\x12\x04\x02\0\x11\x01\n\n\n\x03\x05\0\x01\x12\x03\x02\x05\x13\ + \n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x18\n\x0c\n\x05\x05\0\x02\0\x01\ + \x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\x02\0\x02\x12\x03\x03\x16\x17\n\ + \x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x19\n\x0c\n\x05\x05\0\x02\x01\ + \x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\x02\x01\x02\x12\x03\x04\x17\ + \x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\x17\n\x0c\n\x05\x05\0\x02\ + \x02\x01\x12\x03\x05\x04\x12\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\ + \x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\x04\x18\n\x0c\n\x05\x05\0\ + \x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\x05\x05\0\x02\x03\x02\x12\x03\ + \x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\x12\x03\x07\x04\x16\n\x0c\n\x05\ + \x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\n\x05\x05\0\x02\x04\x02\x12\ + \x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x1a\n\x0c\n\ + \x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\x0c\n\x05\x05\0\x02\x05\x02\ + \x12\x03\x08\x18\x19\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\r\n\x0c\n\x05\x05\0\x02\x06\x02\x12\ + \x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\x12\x03\n\x04\x14\n\x0c\n\x05\ + \x05\0\x02\x07\x01\x12\x03\n\x04\r\n\x0c\n\x05\x05\0\x02\x07\x02\x12\x03\ + \n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\x03\x0b\x04\x12\n\x0c\n\x05\x05\ + \0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\x0c\n\x05\x05\0\x02\x08\x02\x12\x03\ + \x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\x12\x03\x0c\x04\x14\n\x0c\n\x05\x05\ + \0\x02\t\x01\x12\x03\x0c\x04\r\n\x0c\n\x05\x05\0\x02\t\x02\x12\x03\x0c\ + \x10\x13\n\x0b\n\x04\x05\0\x02\n\x12\x03\r\x04\x15\n\x0c\n\x05\x05\0\x02\ + \n\x01\x12\x03\r\x04\x0e\n\x0c\n\x05\x05\0\x02\n\x02\x12\x03\r\x11\x14\n\ + \x0b\n\x04\x05\0\x02\x0b\x12\x03\x0e\x04\x13\n\x0c\n\x05\x05\0\x02\x0b\ + \x01\x12\x03\x0e\x04\x0c\n\x0c\n\x05\x05\0\x02\x0b\x02\x12\x03\x0e\x0f\ + \x12\n\x0b\n\x04\x05\0\x02\x0c\x12\x03\x0f\x04\x15\n\x0c\n\x05\x05\0\x02\ + \x0c\x01\x12\x03\x0f\x04\x0e\n\x0c\n\x05\x05\0\x02\x0c\x02\x12\x03\x0f\ + \x11\x14\n\x0b\n\x04\x05\0\x02\r\x12\x03\x10\x04\x15\n\x0c\n\x05\x05\0\ + \x02\r\x01\x12\x03\x10\x04\x0e\n\x0c\n\x05\x05\0\x02\r\x02\x12\x03\x10\ + \x11\x14b\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/event.proto b/rust-lib/flowy-workspace/src/protobuf/proto/event.proto index 4a8e8baf8d..acad4f8100 100644 --- a/rust-lib/flowy-workspace/src/protobuf/proto/event.proto +++ b/rust-lib/flowy-workspace/src/protobuf/proto/event.proto @@ -6,6 +6,7 @@ enum WorkspaceEvent { ReadWorkspaces = 2; DeleteWorkspace = 3; OpenWorkspace = 4; + ReadWorkspaceApps = 5; CreateApp = 101; DeleteApp = 102; ReadApp = 103; diff --git a/rust-lib/flowy-workspace/src/services/app_controller.rs b/rust-lib/flowy-workspace/src/services/app_controller.rs index 24411ba99f..e057992a36 100644 --- a/rust-lib/flowy-workspace/src/services/app_controller.rs +++ b/rust-lib/flowy-workspace/src/services/app_controller.rs @@ -7,6 +7,7 @@ use crate::{ sql_tables::app::{AppTable, AppTableChangeset, AppTableSql}, }; +use crate::entities::view::RepeatedView; use std::sync::Arc; pub(crate) struct AppController { @@ -24,7 +25,7 @@ impl AppController { view_controller: Arc, server: Server, ) -> Self { - let sql = Arc::new(AppTableSql { database }); + let sql = Arc::new(AppTableSql::new(database)); Self { user, sql, @@ -38,7 +39,12 @@ impl AppController { let app = self.create_app_on_server(params).await?; let app_table = AppTable::new(app.clone()); let _ = self.sql.create_app(app_table)?; - send_observable(&app.workspace_id, WorkspaceObservable::WorkspaceCreateApp); + + // Opti: transaction + let apps = self.read_local_apps(&app.workspace_id)?; + ObservableBuilder::new(&app.workspace_id, WorkspaceObservable::WorkspaceCreateApp) + .payload(apps) + .build(); Ok(app) } @@ -51,16 +57,31 @@ impl AppController { pub(crate) async fn delete_app(&self, app_id: &str) -> Result<(), WorkspaceError> { let app = self.sql.delete_app(app_id)?; let _ = self.delete_app_on_server(app_id).await?; - send_observable(&app.workspace_id, WorkspaceObservable::WorkspaceDeleteApp); + + // Opti: transaction + let apps = self.read_local_apps(&app.workspace_id)?; + ObservableBuilder::new(&app.workspace_id, WorkspaceObservable::WorkspaceDeleteApp) + .payload(apps) + .build(); Ok(()) } + fn read_local_apps(&self, workspace_id: &str) -> Result { + let app_tables = self.sql.read_apps(workspace_id, false)?; + let apps = app_tables.into_iter().map(|table| table.into()).collect::>(); + Ok(RepeatedApp { items: apps }) + } + pub(crate) async fn update_app(&self, params: UpdateAppParams) -> Result<(), WorkspaceError> { let changeset = AppTableChangeset::new(params.clone()); let app_id = changeset.id.clone(); let _ = self.sql.update_app(changeset)?; let _ = self.update_app_on_server(params).await?; - send_observable(&app_id, WorkspaceObservable::AppUpdated); + + let app: App = self.sql.read_app(&app_id, false)?.into(); + ObservableBuilder::new(&app_id, WorkspaceObservable::AppUpdated) + .payload(app) + .build(); Ok(()) } } @@ -114,12 +135,11 @@ impl AppController { let token = self.user.token()?; let server = self.server.clone(); spawn(async move { - match server.read_app(&token, params).await { - Ok(_) => {}, - Err(e) => { - // TODO: retry? - log::error!("Read app failed: {:?}", e); - }, + // Opti: retry? + let app = server.read_app(&token, params).await.unwrap(); + match app { + None => {}, + Some(_) => {}, } }); Ok(()) diff --git a/rust-lib/flowy-workspace/src/services/helper.rs b/rust-lib/flowy-workspace/src/services/helper.rs index 3819eb1271..73ae8328fa 100644 --- a/rust-lib/flowy-workspace/src/services/helper.rs +++ b/rust-lib/flowy-workspace/src/services/helper.rs @@ -1,7 +1,9 @@ -pub fn spawn(f: F) +use tokio::task::JoinHandle; + +pub fn spawn(f: F) -> JoinHandle where F: std::future::Future + Send + 'static, F::Output: Send + 'static, { - let _ = tokio::spawn(f); + tokio::spawn(f) } diff --git a/rust-lib/flowy-workspace/src/services/view_controller.rs b/rust-lib/flowy-workspace/src/services/view_controller.rs index c58b6ee3ee..a7d1243477 100644 --- a/rust-lib/flowy-workspace/src/services/view_controller.rs +++ b/rust-lib/flowy-workspace/src/services/view_controller.rs @@ -2,14 +2,15 @@ use crate::{ entities::view::{CreateViewParams, UpdateViewParams, View}, errors::WorkspaceError, module::WorkspaceDatabase, - observable::{send_observable, WorkspaceObservable}, + observable::WorkspaceObservable, services::{helper::spawn, server::Server}, sql_tables::view::{ViewTable, ViewTableChangeset, ViewTableSql}, }; use crate::{ - entities::view::{DeleteViewParams, QueryViewParams}, + entities::view::{DeleteViewParams, QueryViewParams, RepeatedView}, module::WorkspaceUser, + observable::ObservableBuilder, }; use std::sync::Arc; @@ -30,7 +31,10 @@ impl ViewController { let view_table = ViewTable::new(view.clone()); let _ = self.sql.create_view(view_table)?; - send_observable(&view.belong_to_id, WorkspaceObservable::AppCreateView); + let repeated_view = self.read_local_views_belong_to(&view.belong_to_id)?; + ObservableBuilder::new(&view.belong_to_id, WorkspaceObservable::AppCreateView) + .payload(repeated_view) + .build(); Ok(view) } @@ -42,14 +46,19 @@ impl ViewController { } pub(crate) async fn delete_view(&self, view_id: &str) -> Result<(), WorkspaceError> { - let view = self.sql.delete_view(view_id)?; + let view_table = self.sql.delete_view(view_id)?; let _ = self.delete_view_on_server(view_id).await?; - send_observable(&view.belong_to_id, WorkspaceObservable::AppDeleteView); + + let repeated_view = self.read_local_views_belong_to(&view_table.belong_to_id)?; + ObservableBuilder::new(&view_table.belong_to_id, WorkspaceObservable::AppDeleteView) + .payload(repeated_view) + .build(); Ok(()) } // belong_to_id will be the app_id or view_id. - pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result, WorkspaceError> { + pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result { + // TODO: read from server let views = self .sql .read_views_belong_to(belong_to_id)? @@ -57,16 +66,19 @@ impl ViewController { .map(|view_table| view_table.into()) .collect::>(); - Ok(views) + Ok(RepeatedView { items: views }) } pub(crate) async fn update_view(&self, params: UpdateViewParams) -> Result<(), WorkspaceError> { let changeset = ViewTableChangeset::new(params.clone()); let view_id = changeset.id.clone(); let _ = self.sql.update_view(changeset)?; - let _ = self.update_view_on_server(params).await?; - send_observable(&view_id, WorkspaceObservable::ViewUpdated); + + let view: View = self.sql.read_view(&view_id, false)?.into(); + ObservableBuilder::new(&view_id, WorkspaceObservable::ViewUpdated) + .payload(view) + .build(); Ok(()) } } @@ -129,4 +141,16 @@ impl ViewController { }); Ok(()) } + + // belong_to_id will be the app_id or view_id. + fn read_local_views_belong_to(&self, belong_to_id: &str) -> Result { + let views = self + .sql + .read_views_belong_to(belong_to_id)? + .into_iter() + .map(|view_table| view_table.into()) + .collect::>(); + + Ok(RepeatedView { items: views }) + } } diff --git a/rust-lib/flowy-workspace/src/services/workspace_controller.rs b/rust-lib/flowy-workspace/src/services/workspace_controller.rs index 9e91f9653e..74373e73a4 100644 --- a/rust-lib/flowy-workspace/src/services/workspace_controller.rs +++ b/rust-lib/flowy-workspace/src/services/workspace_controller.rs @@ -2,13 +2,14 @@ use crate::{ entities::{app::App, workspace::*}, errors::*, module::{WorkspaceDatabase, WorkspaceUser}, - observable::{send_observable, WorkspaceObservable}, + observable::WorkspaceObservable, services::{helper::spawn, server::Server, AppController}, sql_tables::workspace::{WorkspaceSql, WorkspaceTable, WorkspaceTableChangeset}, }; use flowy_infra::kv::KV; +use crate::{entities::app::RepeatedApp, observable::ObservableBuilder}; use std::sync::Arc; pub(crate) struct WorkspaceController { @@ -25,7 +26,7 @@ impl WorkspaceController { app_controller: Arc, server: Server, ) -> Self { - let sql = Arc::new(WorkspaceSql { database }); + let sql = Arc::new(WorkspaceSql::new(database)); Self { user, sql, @@ -39,7 +40,12 @@ impl WorkspaceController { let user_id = self.user.user_id()?; let workspace_table = WorkspaceTable::new(workspace.clone(), &user_id); let _ = self.sql.create_workspace(workspace_table)?; - send_observable(&user_id, WorkspaceObservable::UserCreateWorkspace); + + // Opti: read all local workspaces may cause performance issues + let repeated_workspace = self.read_local_workspaces(None, &user_id)?; + ObservableBuilder::new(&user_id, WorkspaceObservable::UserCreateWorkspace) + .payload(repeated_workspace) + .build(); Ok(workspace) } @@ -48,7 +54,13 @@ impl WorkspaceController { let workspace_id = changeset.id.clone(); let _ = self.sql.update_workspace(changeset)?; let _ = self.update_workspace_on_server(params).await?; - send_observable(&workspace_id, WorkspaceObservable::WorkspaceUpdated); + + // Opti: transaction + let user_id = self.user.user_id()?; + let workspace = self.read_local_workspace(workspace_id.clone(), &user_id)?; + ObservableBuilder::new(&workspace_id, WorkspaceObservable::WorkspaceUpdated) + .payload(workspace) + .build(); Ok(()) } @@ -56,23 +68,21 @@ impl WorkspaceController { let user_id = self.user.user_id()?; let _ = self.sql.delete_workspace(workspace_id)?; let _ = self.delete_workspace_on_server(workspace_id).await?; - send_observable(&user_id, WorkspaceObservable::UserDeleteWorkspace); + + // Opti: read all local workspaces may cause performance issues + let repeated_workspace = self.read_local_workspaces(None, &user_id)?; + ObservableBuilder::new(&user_id, WorkspaceObservable::UserDeleteWorkspace) + .payload(repeated_workspace) + .build(); Ok(()) } pub(crate) async fn open_workspace(&self, params: QueryWorkspaceParams) -> Result { let user_id = self.user.user_id()?; if let Some(workspace_id) = params.workspace_id.clone() { - self.read_workspaces_on_server(params.clone()); - let result = self.read_workspace_table(Some(workspace_id), user_id)?; - match result.first() { - None => Err(ErrorBuilder::new(ErrorCode::RecordNotFound).build()), - Some(workspace_table) => { - let workspace: Workspace = workspace_table.clone().into(); - set_current_workspace(&workspace.id); - Ok(workspace) - }, - } + let workspace = self.read_local_workspace(workspace_id, &user_id)?; + set_current_workspace(&workspace.id); + Ok(workspace) } else { return Err(ErrorBuilder::new(ErrorCode::WorkspaceIdInvalid) .msg("Opened workspace id should not be empty") @@ -82,25 +92,44 @@ impl WorkspaceController { pub(crate) async fn read_workspaces(&self, params: QueryWorkspaceParams) -> Result { let user_id = self.user.user_id()?; - let workspace_tables = self.read_workspace_table(params.workspace_id.clone(), user_id.clone())?; + let workspaces = self.read_local_workspaces(params.workspace_id.clone(), &user_id)?; + let _ = self.read_workspaces_on_server(user_id, params).await?; + Ok(workspaces) + } + + pub(crate) async fn read_cur_workspace(&self) -> Result { + let workspace_id = get_current_workspace()?; + let user_id = self.user.user_id()?; + let workspace = self.read_local_workspace(workspace_id, &user_id)?; + Ok(workspace) + } + + pub(crate) async fn read_workspace_apps(&self) -> Result { + let workspace_id = get_current_workspace()?; + let apps = self.read_local_apps(&workspace_id)?; + // TODO: read from server + Ok(RepeatedApp { items: apps }) + } + + #[tracing::instrument(level = "debug", skip(self), err)] + fn read_local_workspaces(&self, workspace_id: Option, user_id: &str) -> Result { + let sql = self.sql.clone(); + let workspace_id = workspace_id.to_owned(); + let workspace_tables = sql.read_workspaces(workspace_id, user_id)?; + let mut workspaces = vec![]; for table in workspace_tables { - let apps = self.read_apps(&table.id).await?; + let apps = self.read_local_apps(&table.id)?; let mut workspace: Workspace = table.into(); workspace.apps.items = apps; workspaces.push(workspace); } - - let _ = self.read_workspaces_on_server(user_id, params).await?; Ok(RepeatedWorkspace { items: workspaces }) } - pub(crate) async fn read_cur_workspace(&self) -> Result { - let params = QueryWorkspaceParams { - workspace_id: Some(get_current_workspace()?), - }; - let mut repeated_workspace = self.read_workspaces(params).await?; - + fn read_local_workspace(&self, workspace_id: String, user_id: &str) -> Result { + // Opti: fetch single workspace from local db + let mut repeated_workspace = self.read_local_workspaces(Some(workspace_id), user_id)?; if repeated_workspace.is_empty() { return Err(ErrorBuilder::new(ErrorCode::RecordNotFound).build()); } @@ -110,13 +139,8 @@ impl WorkspaceController { Ok(workspace) } - pub(crate) async fn read_cur_apps(&self) -> Result, WorkspaceError> { - let workspace_id = get_current_workspace()?; - let apps = self.read_apps(&workspace_id).await?; - Ok(apps) - } - - pub(crate) async fn read_apps(&self, workspace_id: &str) -> Result, WorkspaceError> { + #[tracing::instrument(level = "debug", skip(self), err)] + fn read_local_apps(&self, workspace_id: &str) -> Result, WorkspaceError> { let apps = self .sql .read_apps_belong_to_workspace(workspace_id)? @@ -126,13 +150,6 @@ impl WorkspaceController { Ok(apps) } - - fn read_workspace_table(&self, workspace_id: Option, user_id: String) -> Result, WorkspaceError> { - let sql = self.sql.clone(); - let workspace_id = workspace_id.to_owned(); - let workspace = sql.read_workspaces(workspace_id, &user_id)?; - Ok(workspace) - } } impl WorkspaceController { @@ -185,17 +202,27 @@ impl WorkspaceController { #[tracing::instrument(skip(self), err)] async fn read_workspaces_on_server(&self, user_id: String, params: QueryWorkspaceParams) -> Result<(), WorkspaceError> { let (token, server) = self.token_with_server()?; + let sql = self.sql.clone(); + let conn = self.sql.get_db_conn()?; spawn(async move { - match server.read_workspace(&token, params).await { - Ok(workspaces) => { - log::debug!("Workspace list: {:?}", workspaces); - send_observable(&user_id, WorkspaceObservable::UserCreateWorkspace); - }, - Err(e) => { - // TODO: retry? - log::error!("Delete workspace failed: {:?}", e); - }, - } + // Opti: retry? + let workspaces = server.read_workspace(&token, params).await?; + let _ = (&*conn).immediate_transaction::<_, WorkspaceError, _>(|| { + for workspace in &workspaces.items { + let mut m_workspace = workspace.clone(); + let repeated_app = m_workspace.apps.take_items(); + let workspace_table = WorkspaceTable::new(m_workspace, &user_id); + log::debug!("Save workspace: {} to disk", &workspace.id); + let _ = sql.create_workspace_with(workspace_table, &*conn)?; + log::debug!("Save workspace: {} apps to disk", &workspace.id); + let _ = sql.create_apps(repeated_app, &*conn)?; + } + Ok(()) + })?; + ObservableBuilder::new(&user_id, WorkspaceObservable::WorkspaceListUpdated) + .payload(workspaces) + .build(); + Result::<(), WorkspaceError>::Ok(()) }); Ok(()) diff --git a/rust-lib/flowy-workspace/src/sql_tables/app/app_sql.rs b/rust-lib/flowy-workspace/src/sql_tables/app/app_sql.rs index 5a56a44560..37d2346495 100644 --- a/rust-lib/flowy-workspace/src/sql_tables/app/app_sql.rs +++ b/rust-lib/flowy-workspace/src/sql_tables/app/app_sql.rs @@ -6,33 +6,43 @@ use crate::{ use flowy_database::{ prelude::*, schema::{app_table, app_table::dsl}, + SqliteConnection, }; use std::sync::Arc; pub struct AppTableSql { - pub database: Arc, + database: Arc, +} + +impl AppTableSql { + pub fn new(database: Arc) -> Self { Self { database } } } impl AppTableSql { pub(crate) fn create_app(&self, app_table: AppTable) -> Result<(), WorkspaceError> { let conn = self.database.db_connection()?; - let _ = diesel::insert_into(app_table::table) - .values(app_table) - .execute(&*conn)?; + let _ = self.create_app_with(app_table, &*conn)?; + Ok(()) + } + + pub(crate) fn create_app_with(&self, app_table: AppTable, conn: &SqliteConnection) -> Result<(), WorkspaceError> { + match diesel_record_count!(app_table, &app_table.id, conn) { + 0 => diesel_insert_table!(app_table, &app_table, conn), + _ => { + let changeset = AppTableChangeset::from_table(app_table); + diesel_update_table!(app_table, changeset, conn) + }, + } Ok(()) } pub(crate) fn update_app(&self, changeset: AppTableChangeset) -> Result<(), WorkspaceError> { let conn = self.database.db_connection()?; - diesel_update_table!(app_table, changeset, conn); + diesel_update_table!(app_table, changeset, &*conn); Ok(()) } - pub(crate) fn read_app( - &self, - app_id: &str, - is_trash: bool, - ) -> Result { + pub(crate) fn read_app(&self, app_id: &str, is_trash: bool) -> Result { let app_table = dsl::app_table .filter(app_table::id.eq(app_id)) .filter(app_table::is_trash.eq(is_trash)) @@ -41,6 +51,15 @@ impl AppTableSql { Ok(app_table) } + pub(crate) fn read_apps(&self, workspace_id: &str, is_trash: bool) -> Result, WorkspaceError> { + let app_table = dsl::app_table + .filter(app_table::workspace_id.eq(workspace_id)) + .filter(app_table::is_trash.eq(is_trash)) + .load::(&*(self.database.db_connection()?))?; + + Ok(app_table) + } + pub(crate) fn delete_app(&self, app_id: &str) -> Result { let conn = self.database.db_connection()?; // TODO: group into sql transaction diff --git a/rust-lib/flowy-workspace/src/sql_tables/app/app_table.rs b/rust-lib/flowy-workspace/src/sql_tables/app/app_table.rs index 972fb99ae7..4e65067f95 100644 --- a/rust-lib/flowy-workspace/src/sql_tables/app/app_table.rs +++ b/rust-lib/flowy-workspace/src/sql_tables/app/app_table.rs @@ -75,7 +75,7 @@ impl_sql_binary_expression!(ColorStyleCol); #[derive(AsChangeset, Identifiable, Default, Debug)] #[table_name = "app_table"] -pub struct AppTableChangeset { +pub(crate) struct AppTableChangeset { pub id: String, pub name: Option, pub desc: Option, @@ -83,7 +83,7 @@ pub struct AppTableChangeset { } impl AppTableChangeset { - pub fn new(params: UpdateAppParams) -> Self { + pub(crate) fn new(params: UpdateAppParams) -> Self { AppTableChangeset { id: params.app_id, name: params.name, @@ -91,6 +91,15 @@ impl AppTableChangeset { is_trash: params.is_trash, } } + + pub(crate) fn from_table(table: AppTable) -> Self { + AppTableChangeset { + id: table.id, + name: Some(table.name), + desc: Some(table.desc), + is_trash: Some(table.is_trash), + } + } } impl std::convert::Into for AppTable { diff --git a/rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs b/rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs index 34a699e70a..d1d0e15232 100644 --- a/rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs +++ b/rust-lib/flowy-workspace/src/sql_tables/view/view_sql.rs @@ -16,17 +16,11 @@ pub struct ViewTableSql { impl ViewTableSql { pub(crate) fn create_view(&self, view_table: ViewTable) -> Result<(), WorkspaceError> { let conn = self.database.db_connection()?; - let _ = diesel::insert_into(view_table::table) - .values(view_table) - .execute(&*conn)?; + let _ = diesel::insert_into(view_table::table).values(view_table).execute(&*conn)?; Ok(()) } - pub(crate) fn read_view( - &self, - view_id: &str, - is_trash: bool, - ) -> Result { + pub(crate) fn read_view(&self, view_id: &str, is_trash: bool) -> Result { let view_table = dsl::view_table .filter(view_table::id.eq(view_id)) .filter(view_table::is_trash.eq(is_trash)) @@ -35,10 +29,7 @@ impl ViewTableSql { Ok(view_table) } - pub(crate) fn read_views_belong_to( - &self, - belong_to_id: &str, - ) -> Result, WorkspaceError> { + pub(crate) fn read_views_belong_to(&self, belong_to_id: &str) -> Result, WorkspaceError> { let view_tables = dsl::view_table .filter(view_table::belong_to_id.eq(belong_to_id)) .load::(&*(self.database.db_connection()?))?; @@ -48,7 +39,7 @@ impl ViewTableSql { pub(crate) fn update_view(&self, changeset: ViewTableChangeset) -> Result<(), WorkspaceError> { let conn = self.database.db_connection()?; - diesel_update_table!(view_table, changeset, conn); + diesel_update_table!(view_table, changeset, &*conn); Ok(()) } diff --git a/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_sql.rs b/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_sql.rs index 1157ca7ec8..157ecc6594 100644 --- a/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_sql.rs +++ b/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_sql.rs @@ -1,34 +1,71 @@ use crate::{ + entities::app::App, errors::WorkspaceError, module::WorkspaceDatabase, sql_tables::{ - app::AppTable, + app::{AppTable, AppTableSql}, workspace::{WorkspaceTable, WorkspaceTableChangeset}, }, }; +use diesel::SqliteConnection; use flowy_database::{ + macros::*, prelude::*, schema::{workspace_table, workspace_table::dsl}, + DBConnection, }; use std::sync::Arc; -pub struct WorkspaceSql { - pub database: Arc, +pub(crate) struct WorkspaceSql { + database: Arc, + app_sql: Arc, } impl WorkspaceSql { - pub fn create_workspace(&self, workspace_table: WorkspaceTable) -> Result<(), WorkspaceError> { - let _ = diesel::insert_into(workspace_table::table) - .values(workspace_table) - .execute(&*(self.database.db_connection()?))?; + pub fn new(database: Arc) -> Self { + Self { + database: database.clone(), + app_sql: Arc::new(AppTableSql::new(database.clone())), + } + } +} + +impl WorkspaceSql { + pub(crate) fn create_workspace(&self, table: WorkspaceTable) -> Result<(), WorkspaceError> { + let conn = &*self.database.db_connection()?; + //[[immediate_transaction]] + // https://sqlite.org/lang_transaction.html + // IMMEDIATE cause the database connection to start a new write immediately, + // without waiting for a write statement. The BEGIN IMMEDIATE might fail + // with SQLITE_BUSY if another write transaction is already active on another + // database connection. + // + // EXCLUSIVE is similar to IMMEDIATE in that a write transaction is started + // immediately. EXCLUSIVE and IMMEDIATE are the same in WAL mode, but in + // other journaling modes, EXCLUSIVE prevents other database connections from + // reading the database while the transaction is underway. + (conn).immediate_transaction::<_, WorkspaceError, _>(|| self.create_workspace_with(table, conn)) + } + + pub(crate) fn create_workspace_with(&self, table: WorkspaceTable, conn: &SqliteConnection) -> Result<(), WorkspaceError> { + match diesel_record_count!(workspace_table, &table.id, conn) { + 0 => diesel_insert_table!(workspace_table, &table, conn), + _ => { + let changeset = WorkspaceTableChangeset::from_table(table); + diesel_update_table!(workspace_table, changeset, conn); + }, + } Ok(()) } - pub fn read_workspaces( - &self, - workspace_id: Option, - user_id: &str, - ) -> Result, WorkspaceError> { + pub(crate) fn create_apps(&self, apps: Vec, conn: &SqliteConnection) -> Result<(), WorkspaceError> { + for app in apps { + let _ = self.app_sql.create_app_with(AppTable::new(app), conn)?; + } + Ok(()) + } + + pub(crate) fn read_workspaces(&self, workspace_id: Option, user_id: &str) -> Result, WorkspaceError> { let workspaces = match workspace_id { None => dsl::workspace_table .filter(workspace_table::user_id.eq(user_id)) @@ -42,25 +79,19 @@ impl WorkspaceSql { Ok(workspaces) } - pub fn update_workspace( - &self, - changeset: WorkspaceTableChangeset, - ) -> Result<(), WorkspaceError> { + pub(crate) fn update_workspace(&self, changeset: WorkspaceTableChangeset) -> Result<(), WorkspaceError> { let conn = self.database.db_connection()?; - diesel_update_table!(workspace_table, changeset, conn); + diesel_update_table!(workspace_table, changeset, &*conn); Ok(()) } - pub fn delete_workspace(&self, workspace_id: &str) -> Result<(), WorkspaceError> { + pub(crate) fn delete_workspace(&self, workspace_id: &str) -> Result<(), WorkspaceError> { let conn = self.database.db_connection()?; diesel_delete_table!(workspace_table, workspace_id, conn); Ok(()) } - pub(crate) fn read_apps_belong_to_workspace( - &self, - workspace_id: &str, - ) -> Result, WorkspaceError> { + pub(crate) fn read_apps_belong_to_workspace(&self, workspace_id: &str) -> Result, WorkspaceError> { let conn = self.database.db_connection()?; let apps = conn.immediate_transaction::<_, WorkspaceError, _>(|| { @@ -73,4 +104,9 @@ impl WorkspaceSql { Ok(apps) } + + pub(crate) fn get_db_conn(&self) -> Result { + let db = self.database.db_connection()?; + Ok(db) + } } diff --git a/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_table.rs b/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_table.rs index 1be7ecc06f..1409f89b59 100644 --- a/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_table.rs +++ b/rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_table.rs @@ -60,4 +60,12 @@ impl WorkspaceTableChangeset { desc: params.desc, } } + + pub(crate) fn from_table(table: WorkspaceTable) -> Self { + WorkspaceTableChangeset { + id: table.id, + name: Some(table.name), + desc: Some(table.desc), + } + } }