소스 검색

[fluuter]: create & choose workspace after login

appflowy 3 년 전
부모
커밋
76e73ea17b
42개의 변경된 파일1302개의 추가작업 그리고 281개의 파일을 삭제
  1. 1 1
      app_flowy/lib/user/domain/i_auth.dart
  2. 10 3
      app_flowy/lib/user/infrastructure/i_auth_impl.dart
  3. 1 1
      app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart
  4. 1 1
      app_flowy/lib/welcome/domain/i_welcome.dart
  5. 38 2
      app_flowy/lib/welcome/infrastructure/i_welcome_impl.dart
  6. 1 2
      app_flowy/lib/welcome/presentation/welcome_screen.dart
  7. 81 0
      app_flowy/lib/workspace/application/workspace/workspace_list_bloc.dart
  8. 732 0
      app_flowy/lib/workspace/application/workspace/workspace_list_bloc.freezed.dart
  9. 12 8
      app_flowy/lib/workspace/infrastructure/deps_resolver.dart
  10. 1 1
      app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart
  11. 31 3
      app_flowy/lib/workspace/infrastructure/repos/user_repo.dart
  12. 25 12
      app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart
  13. 1 0
      app_flowy/lib/workspace/presentation/app/view_list_page.dart
  14. 3 1
      app_flowy/lib/workspace/presentation/home/home_screen.dart
  15. 13 9
      app_flowy/lib/workspace/presentation/widgets/menu/menu_page.dart
  16. 236 0
      app_flowy/lib/workspace/presentation/workspace/workspace_select_screen.dart
  17. 17 14
      app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart
  18. 7 2
      app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart
  19. 3 5
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart
  20. 3 4
      app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart
  21. 1 0
      rust-lib/dart-ffi/src/c.rs
  22. 0 2
      rust-lib/flowy-dispatch/src/errors/errors.rs
  23. 1 0
      rust-lib/flowy-document/src/sql_tables/doc/doc_sql.rs
  24. 6 23
      rust-lib/flowy-net/src/request/request.rs
  25. 2 0
      rust-lib/flowy-observable/src/dart/stream_sender.rs
  26. 2 3
      rust-lib/flowy-sdk/Cargo.toml
  27. 2 54
      rust-lib/flowy-sdk/src/deps_resolve/user_deps_impl.rs
  28. 6 4
      rust-lib/flowy-test/src/builder.rs
  29. 1 0
      rust-lib/flowy-test/src/tester.rs
  30. 1 0
      rust-lib/flowy-user/src/services/user/user_session.rs
  31. 1 11
      rust-lib/flowy-user/src/services/workspace/action.rs
  32. 1 0
      rust-lib/flowy-workspace/src/entities/workspace/workspace_query.rs
  33. 4 8
      rust-lib/flowy-workspace/src/event.rs
  34. 1 10
      rust-lib/flowy-workspace/src/handlers/workspace_handler.rs
  35. 1 2
      rust-lib/flowy-workspace/src/module.rs
  36. 40 46
      rust-lib/flowy-workspace/src/protobuf/model/event.rs
  37. 2 3
      rust-lib/flowy-workspace/src/protobuf/proto/event.proto
  38. 1 0
      rust-lib/flowy-workspace/src/services/app_controller.rs
  39. 0 12
      rust-lib/flowy-workspace/src/services/workspace_controller.rs
  40. 0 15
      rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_sql.rs
  41. 1 1
      rust-lib/flowy-workspace/tests/event/helper.rs
  42. 11 18
      rust-lib/flowy-workspace/tests/event/workspace_test.rs

+ 1 - 1
app_flowy/lib/user/domain/i_auth.dart

@@ -11,7 +11,7 @@ abstract class IAuth {
 }
 
 abstract class IAuthRouter {
-  void showHomeScreen(BuildContext context, UserDetail user);
+  void showWorkspaceSelectScreen(BuildContext context, UserDetail user);
   void showSignUpScreen(BuildContext context);
   void showForgetPasswordScreen(BuildContext context);
 }

+ 10 - 3
app_flowy/lib/user/infrastructure/i_auth_impl.dart

@@ -1,4 +1,5 @@
-import 'package:app_flowy/workspace/presentation/home/home_screen.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';
@@ -37,8 +38,14 @@ class AuthRouterImpl extends IAuthRouter {
   }
 
   @override
-  void showHomeScreen(BuildContext context, UserDetail user) {
-    Navigator.of(context).push(PageRoutes.fade(() => HomeScreen(user)));
+  void showWorkspaceSelectScreen(BuildContext context, UserDetail user) {
+    Navigator.of(context).push(
+      PageRoutes.fade(
+        () => WorkspaceSelectScreen(
+          repo: UserRepo(user: user),
+        ),
+      ),
+    );
   }
 
   @override

+ 1 - 1
app_flowy/lib/user/presentation/sign_in/sign_in_screen.dart

@@ -36,7 +36,7 @@ class SignInScreen extends StatelessWidget {
   void _handleSuccessOrFail(
       Either<UserDetail, UserError> result, BuildContext context) {
     result.fold(
-      (user) => router.showHomeScreen(context, user),
+      (user) => router.showWorkspaceSelectScreen(context, user),
       (error) => _showErrorMessage(context, error.msg),
     );
   }

+ 1 - 1
app_flowy/lib/welcome/domain/i_welcome.dart

@@ -9,5 +9,5 @@ abstract class IWelcomeAuth {
 
 abstract class IWelcomeRoute {
   Widget pushSignInScreen();
-  Widget pushHomeScreen(UserDetail userDetail);
+  Future<void> pushHomeScreen(BuildContext context, UserDetail userDetail);
 }

+ 38 - 2
app_flowy/lib/welcome/infrastructure/i_welcome_impl.dart

@@ -3,9 +3,15 @@ import 'package:app_flowy/user/domain/i_auth.dart';
 import 'package:app_flowy/user/presentation/sign_in/sign_in_screen.dart';
 import 'package:app_flowy/welcome/domain/auth_state.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/home/home_screen.dart';
+import 'package:app_flowy/workspace/presentation/workspace/workspace_select_screen.dart';
+import 'package:flowy_infra/flowy_logger.dart';
+import 'package:flowy_infra/time/duration.dart';
+import 'package:flowy_infra_ui/widget/route/animation.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-user/protobuf.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flutter/material.dart';
 import 'package:flutter/widgets.dart';
 
@@ -30,12 +36,42 @@ class WelcomeAuthImpl implements IWelcomeAuth {
 
 class WelcomeRoute implements IWelcomeRoute {
   @override
-  Widget pushHomeScreen(UserDetail user) {
-    return HomeScreen(user);
+  Future<void> pushHomeScreen(BuildContext context, UserDetail user) async {
+    final repo = UserRepo(user: user);
+    return WorkspaceEventReadCurWorkspace().send().then(
+      (result) {
+        return result.fold(
+          (workspace) =>
+              _pushToScreen(context, HomeScreen(repo.user, workspace.id)),
+          (error) async {
+            // assert(error.code == WsErrCode.RecordNotFound);
+            // error shoule be RecordNotFound
+            Log.error(error);
+            final screen = WorkspaceSelectScreen(repo: repo);
+
+            final workspaceId = await Navigator.of(context).push(
+              PageRoutes.fade(
+                () => screen,
+                RouteDurations.slow.inMilliseconds * .001,
+              ),
+            );
+
+            _pushToScreen(context, HomeScreen(repo.user, workspaceId));
+          },
+        );
+      },
+    );
   }
 
   @override
   Widget pushSignInScreen() {
     return SignInScreen(router: getIt<IAuthRouter>());
   }
+
+  void _pushToScreen(BuildContext context, Widget screen) {
+    Navigator.push(
+        context,
+        PageRoutes.fade(
+            () => screen, RouteDurations.slow.inMilliseconds * .001));
+  }
 }

+ 1 - 2
app_flowy/lib/welcome/presentation/welcome_screen.dart

@@ -41,8 +41,7 @@ class WelcomeScreen extends StatelessWidget {
   }
 
   void _handleAuthenticated(BuildContext context, Authenticated result) {
-    _pushToScreen(
-        context, getIt<IWelcomeRoute>().pushHomeScreen(result.userDetail));
+    getIt<IWelcomeRoute>().pushHomeScreen(context, result.userDetail);
   }
 
   void _handleUnauthenticated(BuildContext context, Unauthenticated result) {

+ 81 - 0
app_flowy/lib/workspace/application/workspace/workspace_list_bloc.dart

@@ -0,0 +1,81 @@
+import 'package:app_flowy/workspace/infrastructure/repos/user_repo.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<WorkspaceListEvent, WorkspaceListState> {
+  UserRepo repo;
+  WorkspaceListBloc(this.repo) : super(WorkspaceListState.initial());
+
+  @override
+  Stream<WorkspaceListState> 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<WorkspaceListState> _fetchWorkspaces() async* {
+    final workspacesOrFailed = await repo.fetchWorkspaces();
+    yield workspacesOrFailed.fold(
+      (workspaces) =>
+          state.copyWith(workspaces: workspaces, successOrFailure: left(unit)),
+      (error) => state.copyWith(successOrFailure: right(error)),
+    );
+  }
+
+  Stream<WorkspaceListState> _openWorkspace(Workspace workspace) async* {
+    final result = await repo.openWorkspace(workspace.id);
+    yield result.fold(
+      (workspaces) => state.copyWith(successOrFailure: left(unit)),
+      (error) => state.copyWith(successOrFailure: right(error)),
+    );
+  }
+
+  Stream<WorkspaceListState> _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) => 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<Workspace> workspaces,
+    required Either<Unit, WorkspaceError> successOrFailure,
+  }) = _WorkspaceListState;
+
+  factory WorkspaceListState.initial() => WorkspaceListState(
+        isLoading: false,
+        workspaces: List.empty(),
+        successOrFailure: left(unit),
+      );
+}

+ 732 - 0
app_flowy/lib/workspace/application/workspace/workspace_list_bloc.freezed.dart

@@ -0,0 +1,732 @@
+// 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';
+
+// **************************************************************************
+// FreezedGenerator
+// **************************************************************************
+
+T _$identity<T>(T value) => value;
+
+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();
+
+  Initial initial() {
+    return const Initial();
+  }
+
+  FetchWorkspace fetchWorkspaces() {
+    return const FetchWorkspace();
+  }
+
+  CreateWorkspace createWorkspace(String name, String desc) {
+    return CreateWorkspace(
+      name,
+      desc,
+    );
+  }
+
+  OpenWorkspace openWorkspace(Workspace workspace) {
+    return OpenWorkspace(
+      workspace,
+    );
+  }
+}
+
+/// @nodoc
+const $WorkspaceListEvent = _$WorkspaceListEventTearOff();
+
+/// @nodoc
+mixin _$WorkspaceListEvent {
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() fetchWorkspaces,
+    required TResult Function(String name, String desc) createWorkspace,
+    required TResult Function(Workspace workspace) openWorkspace,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? fetchWorkspaces,
+    TResult Function(String name, String desc)? createWorkspace,
+    TResult Function(Workspace workspace)? openWorkspace,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    required TResult Function(Initial value) initial,
+    required TResult Function(FetchWorkspace value) fetchWorkspaces,
+    required TResult Function(CreateWorkspace value) createWorkspace,
+    required TResult Function(OpenWorkspace value) openWorkspace,
+  }) =>
+      throw _privateConstructorUsedError;
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(FetchWorkspace value)? fetchWorkspaces,
+    TResult Function(CreateWorkspace value)? createWorkspace,
+    TResult Function(OpenWorkspace value)? openWorkspace,
+    required TResult orElse(),
+  }) =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $WorkspaceListEventCopyWith<$Res> {
+  factory $WorkspaceListEventCopyWith(
+          WorkspaceListEvent value, $Res Function(WorkspaceListEvent) then) =
+      _$WorkspaceListEventCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$WorkspaceListEventCopyWithImpl<$Res>
+    implements $WorkspaceListEventCopyWith<$Res> {
+  _$WorkspaceListEventCopyWithImpl(this._value, this._then);
+
+  final WorkspaceListEvent _value;
+  // ignore: unused_field
+  final $Res Function(WorkspaceListEvent) _then;
+}
+
+/// @nodoc
+abstract class $InitialCopyWith<$Res> {
+  factory $InitialCopyWith(Initial value, $Res Function(Initial) then) =
+      _$InitialCopyWithImpl<$Res>;
+}
+
+/// @nodoc
+class _$InitialCopyWithImpl<$Res> extends _$WorkspaceListEventCopyWithImpl<$Res>
+    implements $InitialCopyWith<$Res> {
+  _$InitialCopyWithImpl(Initial _value, $Res Function(Initial) _then)
+      : super(_value, (v) => _then(v as Initial));
+
+  @override
+  Initial get _value => super._value as Initial;
+}
+
+/// @nodoc
+
+class _$Initial implements Initial {
+  const _$Initial();
+
+  @override
+  String toString() {
+    return 'WorkspaceListEvent.initial()';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) || (other is Initial);
+  }
+
+  @override
+  int get hashCode => runtimeType.hashCode;
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() fetchWorkspaces,
+    required TResult Function(String name, String desc) createWorkspace,
+    required TResult Function(Workspace workspace) openWorkspace,
+  }) {
+    return initial();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? fetchWorkspaces,
+    TResult Function(String name, String desc)? createWorkspace,
+    TResult Function(Workspace workspace)? openWorkspace,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial();
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    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 initial(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(FetchWorkspace value)? fetchWorkspaces,
+    TResult Function(CreateWorkspace value)? createWorkspace,
+    TResult Function(OpenWorkspace value)? openWorkspace,
+    required TResult orElse(),
+  }) {
+    if (initial != null) {
+      return initial(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class Initial implements WorkspaceListEvent {
+  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<TResult extends Object?>({
+    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 extends Object?>({
+    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<TResult extends Object?>({
+    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 extends Object?>({
+    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(
+          CreateWorkspace value, $Res Function(CreateWorkspace) then) =
+      _$CreateWorkspaceCopyWithImpl<$Res>;
+  $Res call({String name, String desc});
+}
+
+/// @nodoc
+class _$CreateWorkspaceCopyWithImpl<$Res>
+    extends _$WorkspaceListEventCopyWithImpl<$Res>
+    implements $CreateWorkspaceCopyWith<$Res> {
+  _$CreateWorkspaceCopyWithImpl(
+      CreateWorkspace _value, $Res Function(CreateWorkspace) _then)
+      : super(_value, (v) => _then(v as CreateWorkspace));
+
+  @override
+  CreateWorkspace get _value => super._value as CreateWorkspace;
+
+  @override
+  $Res call({
+    Object? name = freezed,
+    Object? desc = freezed,
+  }) {
+    return _then(CreateWorkspace(
+      name == freezed
+          ? _value.name
+          : name // ignore: cast_nullable_to_non_nullable
+              as String,
+      desc == freezed
+          ? _value.desc
+          : desc // ignore: cast_nullable_to_non_nullable
+              as String,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$CreateWorkspace implements CreateWorkspace {
+  const _$CreateWorkspace(this.name, this.desc);
+
+  @override
+  final String name;
+  @override
+  final String desc;
+
+  @override
+  String toString() {
+    return 'WorkspaceListEvent.createWorkspace(name: $name, desc: $desc)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is CreateWorkspace &&
+            (identical(other.name, name) ||
+                const DeepCollectionEquality().equals(other.name, name)) &&
+            (identical(other.desc, desc) ||
+                const DeepCollectionEquality().equals(other.desc, desc)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(name) ^
+      const DeepCollectionEquality().hash(desc);
+
+  @JsonKey(ignore: true)
+  @override
+  $CreateWorkspaceCopyWith<CreateWorkspace> get copyWith =>
+      _$CreateWorkspaceCopyWithImpl<CreateWorkspace>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() fetchWorkspaces,
+    required TResult Function(String name, String desc) createWorkspace,
+    required TResult Function(Workspace workspace) openWorkspace,
+  }) {
+    return createWorkspace(name, desc);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? fetchWorkspaces,
+    TResult Function(String name, String desc)? createWorkspace,
+    TResult Function(Workspace workspace)? openWorkspace,
+    required TResult orElse(),
+  }) {
+    if (createWorkspace != null) {
+      return createWorkspace(name, desc);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    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 createWorkspace(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(FetchWorkspace value)? fetchWorkspaces,
+    TResult Function(CreateWorkspace value)? createWorkspace,
+    TResult Function(OpenWorkspace value)? openWorkspace,
+    required TResult orElse(),
+  }) {
+    if (createWorkspace != null) {
+      return createWorkspace(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class CreateWorkspace implements WorkspaceListEvent {
+  const factory CreateWorkspace(String name, String desc) = _$CreateWorkspace;
+
+  String get name => throw _privateConstructorUsedError;
+  String get desc => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $CreateWorkspaceCopyWith<CreateWorkspace> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $OpenWorkspaceCopyWith<$Res> {
+  factory $OpenWorkspaceCopyWith(
+          OpenWorkspace value, $Res Function(OpenWorkspace) then) =
+      _$OpenWorkspaceCopyWithImpl<$Res>;
+  $Res call({Workspace workspace});
+}
+
+/// @nodoc
+class _$OpenWorkspaceCopyWithImpl<$Res>
+    extends _$WorkspaceListEventCopyWithImpl<$Res>
+    implements $OpenWorkspaceCopyWith<$Res> {
+  _$OpenWorkspaceCopyWithImpl(
+      OpenWorkspace _value, $Res Function(OpenWorkspace) _then)
+      : super(_value, (v) => _then(v as OpenWorkspace));
+
+  @override
+  OpenWorkspace get _value => super._value as OpenWorkspace;
+
+  @override
+  $Res call({
+    Object? workspace = freezed,
+  }) {
+    return _then(OpenWorkspace(
+      workspace == freezed
+          ? _value.workspace
+          : workspace // ignore: cast_nullable_to_non_nullable
+              as Workspace,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$OpenWorkspace implements OpenWorkspace {
+  const _$OpenWorkspace(this.workspace);
+
+  @override
+  final Workspace workspace;
+
+  @override
+  String toString() {
+    return 'WorkspaceListEvent.openWorkspace(workspace: $workspace)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is OpenWorkspace &&
+            (identical(other.workspace, workspace) ||
+                const DeepCollectionEquality()
+                    .equals(other.workspace, workspace)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^ const DeepCollectionEquality().hash(workspace);
+
+  @JsonKey(ignore: true)
+  @override
+  $OpenWorkspaceCopyWith<OpenWorkspace> get copyWith =>
+      _$OpenWorkspaceCopyWithImpl<OpenWorkspace>(this, _$identity);
+
+  @override
+  @optionalTypeArgs
+  TResult when<TResult extends Object?>({
+    required TResult Function() initial,
+    required TResult Function() fetchWorkspaces,
+    required TResult Function(String name, String desc) createWorkspace,
+    required TResult Function(Workspace workspace) openWorkspace,
+  }) {
+    return openWorkspace(workspace);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeWhen<TResult extends Object?>({
+    TResult Function()? initial,
+    TResult Function()? fetchWorkspaces,
+    TResult Function(String name, String desc)? createWorkspace,
+    TResult Function(Workspace workspace)? openWorkspace,
+    required TResult orElse(),
+  }) {
+    if (openWorkspace != null) {
+      return openWorkspace(workspace);
+    }
+    return orElse();
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult map<TResult extends Object?>({
+    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 openWorkspace(this);
+  }
+
+  @override
+  @optionalTypeArgs
+  TResult maybeMap<TResult extends Object?>({
+    TResult Function(Initial value)? initial,
+    TResult Function(FetchWorkspace value)? fetchWorkspaces,
+    TResult Function(CreateWorkspace value)? createWorkspace,
+    TResult Function(OpenWorkspace value)? openWorkspace,
+    required TResult orElse(),
+  }) {
+    if (openWorkspace != null) {
+      return openWorkspace(this);
+    }
+    return orElse();
+  }
+}
+
+abstract class OpenWorkspace implements WorkspaceListEvent {
+  const factory OpenWorkspace(Workspace workspace) = _$OpenWorkspace;
+
+  Workspace get workspace => throw _privateConstructorUsedError;
+  @JsonKey(ignore: true)
+  $OpenWorkspaceCopyWith<OpenWorkspace> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+class _$WorkspaceListStateTearOff {
+  const _$WorkspaceListStateTearOff();
+
+  _WorkspaceListState call(
+      {required bool isLoading,
+      required List<Workspace> workspaces,
+      required Either<Unit, WorkspaceError> successOrFailure}) {
+    return _WorkspaceListState(
+      isLoading: isLoading,
+      workspaces: workspaces,
+      successOrFailure: successOrFailure,
+    );
+  }
+}
+
+/// @nodoc
+const $WorkspaceListState = _$WorkspaceListStateTearOff();
+
+/// @nodoc
+mixin _$WorkspaceListState {
+  bool get isLoading => throw _privateConstructorUsedError;
+  List<Workspace> get workspaces => throw _privateConstructorUsedError;
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+
+  @JsonKey(ignore: true)
+  $WorkspaceListStateCopyWith<WorkspaceListState> get copyWith =>
+      throw _privateConstructorUsedError;
+}
+
+/// @nodoc
+abstract class $WorkspaceListStateCopyWith<$Res> {
+  factory $WorkspaceListStateCopyWith(
+          WorkspaceListState value, $Res Function(WorkspaceListState) then) =
+      _$WorkspaceListStateCopyWithImpl<$Res>;
+  $Res call(
+      {bool isLoading,
+      List<Workspace> workspaces,
+      Either<Unit, WorkspaceError> successOrFailure});
+}
+
+/// @nodoc
+class _$WorkspaceListStateCopyWithImpl<$Res>
+    implements $WorkspaceListStateCopyWith<$Res> {
+  _$WorkspaceListStateCopyWithImpl(this._value, this._then);
+
+  final WorkspaceListState _value;
+  // ignore: unused_field
+  final $Res Function(WorkspaceListState) _then;
+
+  @override
+  $Res call({
+    Object? isLoading = freezed,
+    Object? workspaces = freezed,
+    Object? successOrFailure = freezed,
+  }) {
+    return _then(_value.copyWith(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
+      workspaces: workspaces == freezed
+          ? _value.workspaces
+          : workspaces // ignore: cast_nullable_to_non_nullable
+              as List<Workspace>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+abstract class _$WorkspaceListStateCopyWith<$Res>
+    implements $WorkspaceListStateCopyWith<$Res> {
+  factory _$WorkspaceListStateCopyWith(
+          _WorkspaceListState value, $Res Function(_WorkspaceListState) then) =
+      __$WorkspaceListStateCopyWithImpl<$Res>;
+  @override
+  $Res call(
+      {bool isLoading,
+      List<Workspace> workspaces,
+      Either<Unit, WorkspaceError> successOrFailure});
+}
+
+/// @nodoc
+class __$WorkspaceListStateCopyWithImpl<$Res>
+    extends _$WorkspaceListStateCopyWithImpl<$Res>
+    implements _$WorkspaceListStateCopyWith<$Res> {
+  __$WorkspaceListStateCopyWithImpl(
+      _WorkspaceListState _value, $Res Function(_WorkspaceListState) _then)
+      : super(_value, (v) => _then(v as _WorkspaceListState));
+
+  @override
+  _WorkspaceListState get _value => super._value as _WorkspaceListState;
+
+  @override
+  $Res call({
+    Object? isLoading = freezed,
+    Object? workspaces = freezed,
+    Object? successOrFailure = freezed,
+  }) {
+    return _then(_WorkspaceListState(
+      isLoading: isLoading == freezed
+          ? _value.isLoading
+          : isLoading // ignore: cast_nullable_to_non_nullable
+              as bool,
+      workspaces: workspaces == freezed
+          ? _value.workspaces
+          : workspaces // ignore: cast_nullable_to_non_nullable
+              as List<Workspace>,
+      successOrFailure: successOrFailure == freezed
+          ? _value.successOrFailure
+          : successOrFailure // ignore: cast_nullable_to_non_nullable
+              as Either<Unit, WorkspaceError>,
+    ));
+  }
+}
+
+/// @nodoc
+
+class _$_WorkspaceListState implements _WorkspaceListState {
+  const _$_WorkspaceListState(
+      {required this.isLoading,
+      required this.workspaces,
+      required this.successOrFailure});
+
+  @override
+  final bool isLoading;
+  @override
+  final List<Workspace> workspaces;
+  @override
+  final Either<Unit, WorkspaceError> successOrFailure;
+
+  @override
+  String toString() {
+    return 'WorkspaceListState(isLoading: $isLoading, workspaces: $workspaces, successOrFailure: $successOrFailure)';
+  }
+
+  @override
+  bool operator ==(dynamic other) {
+    return identical(this, other) ||
+        (other is _WorkspaceListState &&
+            (identical(other.isLoading, isLoading) ||
+                const DeepCollectionEquality()
+                    .equals(other.isLoading, isLoading)) &&
+            (identical(other.workspaces, workspaces) ||
+                const DeepCollectionEquality()
+                    .equals(other.workspaces, workspaces)) &&
+            (identical(other.successOrFailure, successOrFailure) ||
+                const DeepCollectionEquality()
+                    .equals(other.successOrFailure, successOrFailure)));
+  }
+
+  @override
+  int get hashCode =>
+      runtimeType.hashCode ^
+      const DeepCollectionEquality().hash(isLoading) ^
+      const DeepCollectionEquality().hash(workspaces) ^
+      const DeepCollectionEquality().hash(successOrFailure);
+
+  @JsonKey(ignore: true)
+  @override
+  _$WorkspaceListStateCopyWith<_WorkspaceListState> get copyWith =>
+      __$WorkspaceListStateCopyWithImpl<_WorkspaceListState>(this, _$identity);
+}
+
+abstract class _WorkspaceListState implements WorkspaceListState {
+  const factory _WorkspaceListState(
+          {required bool isLoading,
+          required List<Workspace> workspaces,
+          required Either<Unit, WorkspaceError> successOrFailure}) =
+      _$_WorkspaceListState;
+
+  @override
+  bool get isLoading => throw _privateConstructorUsedError;
+  @override
+  List<Workspace> get workspaces => throw _privateConstructorUsedError;
+  @override
+  Either<Unit, WorkspaceError> get successOrFailure =>
+      throw _privateConstructorUsedError;
+  @override
+  @JsonKey(ignore: true)
+  _$WorkspaceListStateCopyWith<_WorkspaceListState> get copyWith =>
+      throw _privateConstructorUsedError;
+}

+ 12 - 8
app_flowy/lib/workspace/infrastructure/deps_resolver.dart

@@ -37,10 +37,12 @@ class HomeDepsResolver {
         (appId, _) => IAppWatchImpl(repo: AppWatchRepository(appId: appId)));
 
     //workspace
-    getIt.registerFactoryParam<IWorkspace, UserDetail, void>(
-        (user, _) => IWorkspaceImpl(repo: WorkspaceRepo(user: user)));
-    getIt.registerFactoryParam<IWorkspaceWatch, UserDetail, void>(
-        (user, _) => IWorkspaceWatchImpl(repo: WorkspaceWatchRepo(user: user)));
+    getIt.registerFactoryParam<IWorkspace, UserDetail, String>(
+        (user, workspaceId) => IWorkspaceImpl(
+            repo: WorkspaceRepo(user: user, workspaceId: workspaceId)));
+    getIt.registerFactoryParam<IWorkspaceWatch, UserDetail, String>(
+        (user, workspaceId) => IWorkspaceWatchImpl(
+            repo: WorkspaceWatchRepo(user: user, workspaceId: workspaceId)));
 
     // View
     getIt.registerFactoryParam<IView, View, void>(
@@ -59,10 +61,12 @@ class HomeDepsResolver {
         (user, _) => IUserWatchImpl(repo: UserWatchRepo(user: user)));
 
     //Menu Bloc
-    getIt.registerFactoryParam<MenuBloc, UserDetail, void>(
-        (user, _) => MenuBloc(getIt<IWorkspace>(param1: user)));
-    getIt.registerFactoryParam<MenuWatchBloc, UserDetail, void>(
-        (user, _) => MenuWatchBloc(getIt<IWorkspaceWatch>(param1: user)));
+    getIt.registerFactoryParam<MenuBloc, UserDetail, String>(
+        (user, workspaceId) =>
+            MenuBloc(getIt<IWorkspace>(param1: user, param2: workspaceId)));
+    getIt.registerFactoryParam<MenuWatchBloc, UserDetail, String>(
+        (user, workspaceId) => MenuWatchBloc(
+            getIt<IWorkspaceWatch>(param1: user, param2: workspaceId)));
 
     getIt.registerFactoryParam<MenuUserBloc, UserDetail, void>(
         (user, _) => MenuUserBloc(getIt<IUser>(param1: user)));

+ 1 - 1
app_flowy/lib/workspace/infrastructure/i_workspace_impl.dart

@@ -20,7 +20,7 @@ class IWorkspaceImpl extends IWorkspace {
 
   @override
   Future<Either<List<App>, WorkspaceError>> getApps() {
-    return repo.getWorkspace(readApps: true).then((result) {
+    return repo.getWorkspace().then((result) {
       return result.fold(
         (workspace) => left(workspace.apps.items),
         (error) => right(error),

+ 31 - 3
app_flowy/lib/workspace/infrastructure/repos/user_repo.dart

@@ -8,8 +8,8 @@ import 'package:flowy_sdk/protobuf/flowy-user/user_detail.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/observable.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_query.pb.dart';
 import 'package:flowy_sdk/rust_stream.dart';
-
 import 'package:app_flowy/workspace/domain/i_user.dart';
 
 class UserRepo {
@@ -33,10 +33,38 @@ class UserRepo {
   }
 
   Future<Either<List<Workspace>, WorkspaceError>> fetchWorkspaces() {
-    return WorkspaceEventReadAllWorkspace().send().then((result) {
+    final request = QueryWorkspaceRequest.create()..userId = user.id;
+
+    return WorkspaceEventReadWorkspaces(request).send().then((result) {
       return result.fold(
         (workspaces) => left(workspaces.items),
-        (r) => right(r),
+        (error) => right(error),
+      );
+    });
+  }
+
+  Future<Either<Workspace, WorkspaceError>> openWorkspace(String workspaceId) {
+    final request = QueryWorkspaceRequest.create()
+      ..userId = user.id
+      ..workspaceId = workspaceId;
+    return WorkspaceEventOpenWorkspace(request).send().then((result) {
+      return result.fold(
+        (workspace) => left(workspace),
+        (error) => right(error),
+      );
+    });
+  }
+
+  Future<Either<Workspace, WorkspaceError>> createWorkspace(
+      String name, String desc) {
+    final request = CreateWorkspaceRequest.create()
+      ..userId = user.id
+      ..name = name
+      ..desc = desc;
+    return WorkspaceEventCreateWorkspace(request).send().then((result) {
+      return result.fold(
+        (workspace) => left(workspace),
+        (error) => right(error),
       );
     });
   }

+ 25 - 12
app_flowy/lib/workspace/infrastructure/repos/workspace_repo.dart

@@ -1,6 +1,5 @@
 import 'dart:async';
 
-import 'package:app_flowy/workspace/domain/i_workspace.dart';
 import 'package:dartz/dartz.dart';
 import 'package:flowy_infra/flowy_logger.dart';
 import 'package:flowy_sdk/dispatch/dispatch.dart';
@@ -13,10 +12,14 @@ import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_query.pb.dart';
 import 'package:flowy_sdk/rust_stream.dart';
 
+import 'package:app_flowy/workspace/domain/i_workspace.dart';
+
 class WorkspaceRepo {
   UserDetail user;
+  String workspaceId;
   WorkspaceRepo({
     required this.user,
+    required this.workspaceId,
   });
 
   Future<Either<App, WorkspaceError>> createApp(String appName, String desc) {
@@ -36,16 +39,22 @@ class WorkspaceRepo {
     });
   }
 
-  Future<Either<Workspace, WorkspaceError>> getWorkspace(
-      {bool readApps = false}) {
+  Future<Either<Workspace, WorkspaceError>> getWorkspace() {
     final request = QueryWorkspaceRequest.create()
-      ..workspaceId = user.workspace
-      ..user_id = user.id
-      ..readApps = readApps;
+      ..userId = user.id
+      ..workspaceId = workspaceId;
 
-    return WorkspaceEventReadWorkspace(request).send().then((result) {
+    return WorkspaceEventReadWorkspaces(request).send().then((result) {
       return result.fold(
-        (workspace) => left(workspace),
+        (workspaces) {
+          assert(workspaces.items.length == 1);
+
+          if (workspaces.items.isEmpty) {
+            return right(WorkspaceError.create()..msg = "Workspace not found");
+          } else {
+            return left(workspaces.items[0]);
+          }
+        },
         (error) => right(error),
       );
     });
@@ -58,12 +67,14 @@ class WorkspaceWatchRepo {
   WorkspaceDeleteAppCallback? _deleteApp;
   WorkspaceUpdatedCallback? _update;
   final UserDetail user;
+  final String workspaceId;
   late WorkspaceRepo _repo;
 
   WorkspaceWatchRepo({
     required this.user,
+    required this.workspaceId,
   }) {
-    _repo = WorkspaceRepo(user: user);
+    _repo = WorkspaceRepo(user: user, workspaceId: workspaceId);
   }
 
   void startWatching({
@@ -76,7 +87,7 @@ class WorkspaceWatchRepo {
     _update = update;
 
     _subscription = RustStreamReceiver.listen((observable) {
-      if (observable.subjectId != user.workspace) {
+      if (observable.subjectId != workspaceId) {
         return;
       }
 
@@ -104,18 +115,20 @@ class WorkspaceWatchRepo {
         if (_createApp == null) {
           return;
         }
-        _repo.getWorkspace(readApps: true).then((result) {
+
+        _repo.getWorkspace().then((result) {
           result.fold(
             (workspace) => _createApp!(left(workspace.apps.items)),
             (error) => _createApp!(right(error)),
           );
         });
+
         break;
       case WorkspaceObservable.WorkspaceDeleteApp:
         if (_deleteApp == null) {
           return;
         }
-        _repo.getWorkspace(readApps: true).then((result) {
+        _repo.getWorkspace().then((result) {
           result.fold(
             (workspace) => _deleteApp!(left(workspace.apps.items)),
             (error) => _deleteApp!(right(error)),

+ 1 - 0
app_flowy/lib/workspace/presentation/app/view_list_page.dart

@@ -35,6 +35,7 @@ class ViewListPage extends StatelessWidget {
 
   @override
   Widget build(BuildContext context) {
+    // The ViewListNotifier will be updated after ViewListData changed passed by parent widget
     return ChangeNotifierProxyProvider<ViewListData, ViewListNotifier>(
       create: (_) => ViewListNotifier(
         Provider.of<ViewListData>(

+ 3 - 1
app_flowy/lib/workspace/presentation/home/home_screen.dart

@@ -15,7 +15,8 @@ import 'home_layout.dart';
 class HomeScreen extends StatelessWidget {
   static GlobalKey<ScaffoldState> scaffoldKey = GlobalKey();
   final UserDetail user;
-  const HomeScreen(this.user, {Key? key}) : super(key: key);
+  final String workspaceId;
+  const HomeScreen(this.user, this.workspaceId, {Key? key}) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -76,6 +77,7 @@ class HomeScreen extends StatelessWidget {
         homeBloc.add(HomeEvent.forceCollapse(isCollapse));
       },
       user: user,
+      workspaceId: workspaceId,
     );
     homeMenu = RepaintBoundary(child: homeMenu);
     homeMenu = FocusTraversalGroup(child: homeMenu);

+ 13 - 9
app_flowy/lib/workspace/presentation/widgets/menu/menu_page.dart

@@ -23,13 +23,15 @@ class HomeMenu extends StatelessWidget {
   final Function(HomeStackView?) pageContextChanged;
   final Function(bool) isCollapseChanged;
   final UserDetail user;
+  final String workspaceId;
 
-  const HomeMenu(
-      {Key? key,
-      required this.pageContextChanged,
-      required this.isCollapseChanged,
-      required this.user})
-      : super(key: key);
+  const HomeMenu({
+    Key? key,
+    required this.pageContextChanged,
+    required this.isCollapseChanged,
+    required this.user,
+    required this.workspaceId,
+  }) : super(key: key);
 
   @override
   Widget build(BuildContext context) {
@@ -37,10 +39,12 @@ class HomeMenu extends StatelessWidget {
       providers: [
         BlocProvider<MenuBloc>(
             create: (context) =>
-                getIt<MenuBloc>(param1: user)..add(const MenuEvent.initial())),
+                getIt<MenuBloc>(param1: user, param2: workspaceId)
+                  ..add(const MenuEvent.initial())),
         BlocProvider(
-            create: (context) => getIt<MenuWatchBloc>(param1: user)
-              ..add(const MenuWatchEvent.started())),
+            create: (context) =>
+                getIt<MenuWatchBloc>(param1: user, param2: workspaceId)
+                  ..add(const MenuWatchEvent.started())),
       ],
       child: MultiBlocListener(
         listeners: [

+ 236 - 0
app_flowy/lib/workspace/presentation/workspace/workspace_select_screen.dart

@@ -0,0 +1,236 @@
+import 'package:app_flowy/workspace/application/workspace/workspace_list_bloc.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';
+import 'package:flowy_sdk/protobuf/flowy-workspace/workspace_create.pb.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+
+import 'package:app_flowy/workspace/infrastructure/repos/user_repo.dart';
+
+class WorkspaceSelectScreen extends StatelessWidget {
+  final UserRepo repo;
+  const WorkspaceSelectScreen({
+    Key? key,
+    required this.repo,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return BlocProvider(
+      create: (_) =>
+          WorkspaceListBloc(repo)..add(const WorkspaceListEvent.initial()),
+      child: BlocBuilder<WorkspaceListBloc, WorkspaceListState>(
+        builder: (context, state) {
+          return Scaffold(
+            body: Padding(
+              padding: const EdgeInsets.all(60.0),
+              child: Column(
+                children: [
+                  _renderBody(state),
+                  _renderCreateButton(context),
+                ],
+              ),
+            ),
+          );
+        },
+      ),
+    );
+  }
+
+  Widget _renderBody(WorkspaceListState state) {
+    final body = state.successOrFailure.fold(
+      (_) => _renderList(state.workspaces),
+      (error) => FlowyErrorPage(error.toString()),
+    );
+    return body;
+  }
+
+  Widget _renderCreateButton(BuildContext context) {
+    return SizedBox(
+      width: 200,
+      height: 40,
+      child: FlowyTextButton(
+        "Create workspace",
+        fontSize: 14,
+        onPressed: () {
+          context
+              .read<WorkspaceListBloc>()
+              .add(const WorkspaceListEvent.createWorkspace("workspace", ""));
+        },
+      ),
+    );
+  }
+
+  Widget _renderList(List<Workspace> workspaces) {
+    return Expanded(
+      child: StyledListView(
+        itemBuilder: (BuildContext context, int index) {
+          final workspace = workspaces[index];
+          return WorkspaceItem(
+            workspace: workspace,
+            onPressed: (workspace) => _handleOnPress(context, workspace),
+          );
+        },
+        itemCount: workspaces.length,
+      ),
+    );
+  }
+
+  void _handleOnPress(BuildContext context, Workspace workspace) {
+    context
+        .read<WorkspaceListBloc>()
+        .add(WorkspaceListEvent.openWorkspace(workspace));
+
+    Navigator.of(context).pop(workspace.id);
+  }
+}
+
+class WorkspaceItem extends StatelessWidget {
+  final Workspace workspace;
+  final void Function(Workspace workspace) onPressed;
+  const WorkspaceItem(
+      {Key? key, required this.workspace, required this.onPressed})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return SizedBox(
+      height: 46,
+      child: FlowyTextButton(
+        workspace.name,
+        fontSize: 14,
+        onPressed: () => onPressed(workspace),
+      ),
+    );
+  }
+}
+
+
+// Bloc and Provider
+
+// *************Provider***************
+// class WorkspaceListNotifier with ChangeNotifier {
+//   UserRepo repo;
+//   List<Workspace> workspaces = [];
+//   WorkspaceListNotifier({
+//     required this.repo,
+//   }) {
+//     fetch();
+//   }
+
+//   void fetch() {
+//     repo.fetchWorkspaces().then((result) {
+//       result.fold((workspaces) {
+//         this.workspaces = workspaces;
+//         notifyListeners();
+//       }, (error) {
+//         Log.error(error);
+//       });
+//     });
+//   }
+// }
+
+// class WorkspaceSelectScreen extends StatelessWidget {
+//   final UserDetail user;
+//   const WorkspaceSelectScreen({
+//     Key? key,
+//     required this.user,
+//   }) : super(key: key);
+
+//   @override
+//   Widget build(BuildContext context) {
+//     return MultiProvider(
+//         providers: [
+//           ChangeNotifierProvider(
+//             create: (_) => WorkspaceListNotifier(repo: UserRepo(user: user)),
+//           )
+//         ],
+//         child: Consumer<WorkspaceListNotifier>(builder: (ctx, notifier, child) {
+//           return StyledListView(
+//             itemBuilder: (BuildContext context, int index) {
+//               final workspace = notifier.workspaces[index];
+//               return WorkspaceItem(workspace);
+//             },
+//             itemCount: notifier.workspaces.length,
+//           );
+//         }));
+//   }
+// }
+
+// *************Bloc***************
+//
+// class WorkspaceListBloc extends Bloc<WorkspaceListEvent, WorkspaceListState> {
+//   UserRepo repo;
+//   WorkspaceListBloc(this.repo) : super(WorkspaceListState.initial());
+
+//   @override
+//   Stream<WorkspaceListState> mapEventToState(
+//     WorkspaceListEvent event,
+//   ) async* {
+//     yield* event.map(
+//       initial: (e) async* {
+//         yield* _fetchWorkspaces();
+//       },
+//     );
+//   }
+
+//   Stream<WorkspaceListState> _fetchWorkspaces() async* {
+//     final workspacesOrFailed = await repo.fetchWorkspaces();
+
+//     yield workspacesOrFailed.fold(
+//         (workspaces) => state.copyWith(
+//             workspaces: workspaces, successOrFailure: left(unit)),
+//         (error) => state.copyWith(successOrFailure: right(error)));
+//   }
+// }
+
+// @freezed
+// abstract class WorkspaceListEvent with _$WorkspaceListEvent {
+//   const factory WorkspaceListEvent.initial() = Initial;
+// }
+
+// @freezed
+// abstract class WorkspaceListState implements _$WorkspaceListState {
+//   const factory WorkspaceListState({
+//     required bool isLoading,
+//     required List<Workspace> workspaces,
+//     required Either<Unit, WorkspaceError> successOrFailure,
+//   }) = _WorkspaceListState;
+
+//   factory WorkspaceListState.initial() => WorkspaceListState(
+//         isLoading: false,
+//         workspaces: List.empty(),
+//         successOrFailure: left(unit),
+//       );
+// }
+//
+// class WorkspaceSelectScreen extends StatelessWidget {
+//   final UserDetail user;
+//   const WorkspaceSelectScreen({
+//     Key? key,
+//     required this.user,
+//   }) : super(key: key);
+
+//   @override
+//   Widget build(BuildContext context) {
+//     return BlocProvider(
+//       create: (_) => WorkspaceListBloc(UserRepo(user: user))
+//         ..add(const WorkspaceListEvent.initial()),
+//       child: BlocBuilder<WorkspaceListBloc, WorkspaceListState>(
+//         builder: (context, state) {
+//           return state.successOrFailure.fold(
+//             (_) => StyledListView(
+//               itemBuilder: (BuildContext context, int index) {
+//                 final workspace = state.workspaces[index];
+//                 return WorkspaceItem(workspace);
+//               },
+//               itemCount: state.workspaces.length,
+//             ),
+//             (error) => Container(),
+//           );
+//         },
+//       ),
+//     );
+//   }
+// }

+ 17 - 14
app_flowy/packages/flowy_sdk/lib/dispatch/code_gen.dart

@@ -33,18 +33,18 @@ class WorkspaceEventReadCurWorkspace {
     }
 }
 
-class WorkspaceEventReadWorkspace {
+class WorkspaceEventReadWorkspaces {
      QueryWorkspaceRequest request;
-     WorkspaceEventReadWorkspace(this.request);
+     WorkspaceEventReadWorkspaces(this.request);
 
-    Future<Either<Workspace, WorkspaceError>> send() {
+    Future<Either<RepeatedWorkspace, WorkspaceError>> send() {
     final request = FFIRequest.create()
-          ..event = WorkspaceEvent.ReadWorkspace.toString()
+          ..event = WorkspaceEvent.ReadWorkspaces.toString()
           ..payload = requestToBytes(this.request);
 
     return Dispatch.asyncRequest(request)
         .then((bytesResult) => bytesResult.fold(
-           (okBytes) => left(Workspace.fromBuffer(okBytes)),
+           (okBytes) => left(RepeatedWorkspace.fromBuffer(okBytes)),
            (errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
         ));
     }
@@ -67,17 +67,20 @@ class WorkspaceEventDeleteWorkspace {
     }
 }
 
-class WorkspaceEventReadAllWorkspace {
-    WorkspaceEventReadAllWorkspace();
+class WorkspaceEventOpenWorkspace {
+     QueryWorkspaceRequest request;
+     WorkspaceEventOpenWorkspace(this.request);
 
-    Future<Either<RepeatedWorkspace, WorkspaceError>> send() {
-     final request = FFIRequest.create()
-        ..event = WorkspaceEvent.ReadAllWorkspace.toString();
+    Future<Either<Workspace, WorkspaceError>> send() {
+    final request = FFIRequest.create()
+          ..event = WorkspaceEvent.OpenWorkspace.toString()
+          ..payload = requestToBytes(this.request);
 
-     return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
-        (okBytes) => left(RepeatedWorkspace.fromBuffer(okBytes)),
-        (errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
-      ));
+    return Dispatch.asyncRequest(request)
+        .then((bytesResult) => bytesResult.fold(
+           (okBytes) => left(Workspace.fromBuffer(okBytes)),
+           (errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
+        ));
     }
 }
 

+ 7 - 2
app_flowy/packages/flowy_sdk/lib/dispatch/dispatch.dart

@@ -17,7 +17,7 @@ import 'package:flowy_sdk/protobuf/flowy-document/protobuf.dart';
 // ignore: unused_import
 import 'package:flowy_sdk/protobuf/flowy-infra/protobuf.dart';
 import 'package:protobuf/protobuf.dart';
-
+import 'dart:convert' show utf8;
 import 'error.dart';
 
 part 'code_gen.dart';
@@ -54,10 +54,15 @@ Future<Either<Uint8List, Uint8List>> _extractPayload(
         if (response.code == FFIStatusCode.Ok) {
           return left(Uint8List.fromList(response.payload));
         } else {
+          // final error = utf8.decode(response.payload);
+          // Log.error("Dispatch error: $error");
           return right(Uint8List.fromList(response.payload));
         }
       },
-      (error) => right(emptyBytes()),
+      (error) {
+        Log.error("Response should not be empty $error");
+        return right(emptyBytes());
+      },
     );
   });
 }

+ 3 - 5
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbenum.dart

@@ -12,10 +12,9 @@ import 'package:protobuf/protobuf.dart' as $pb;
 class WorkspaceEvent extends $pb.ProtobufEnum {
   static const WorkspaceEvent CreateWorkspace = WorkspaceEvent._(0, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CreateWorkspace');
   static const WorkspaceEvent ReadCurWorkspace = WorkspaceEvent._(1, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadCurWorkspace');
-  static const WorkspaceEvent ReadWorkspace = WorkspaceEvent._(2, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadWorkspace');
+  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 ReadAllWorkspace = WorkspaceEvent._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadAllWorkspace');
-  static const WorkspaceEvent OpenWorkspace = WorkspaceEvent._(5, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenWorkspace');
+  static const WorkspaceEvent OpenWorkspace = WorkspaceEvent._(4, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenWorkspace');
   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');
@@ -28,9 +27,8 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
   static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
     CreateWorkspace,
     ReadCurWorkspace,
-    ReadWorkspace,
+    ReadWorkspaces,
     DeleteWorkspace,
-    ReadAllWorkspace,
     OpenWorkspace,
     CreateApp,
     DeleteApp,

+ 3 - 4
app_flowy/packages/flowy_sdk/lib/protobuf/flowy-workspace/event.pbjson.dart

@@ -14,10 +14,9 @@ const WorkspaceEvent$json = const {
   '2': const [
     const {'1': 'CreateWorkspace', '2': 0},
     const {'1': 'ReadCurWorkspace', '2': 1},
-    const {'1': 'ReadWorkspace', '2': 2},
+    const {'1': 'ReadWorkspaces', '2': 2},
     const {'1': 'DeleteWorkspace', '2': 3},
-    const {'1': 'ReadAllWorkspace', '2': 4},
-    const {'1': 'OpenWorkspace', '2': 5},
+    const {'1': 'OpenWorkspace', '2': 4},
     const {'1': 'CreateApp', '2': 101},
     const {'1': 'DeleteApp', '2': 102},
     const {'1': 'ReadApp', '2': 103},
@@ -30,4 +29,4 @@ const WorkspaceEvent$json = const {
 };
 
 /// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
-final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEQoNUmVhZFdvcmtzcGFjZRACEhMKD0RlbGV0ZVdvcmtzcGFjZRADEhQKEFJlYWRBbGxXb3Jrc3BhY2UQBBIRCg1PcGVuV29ya3NwYWNlEAUSDQoJQ3JlYXRlQXBwEGUSDQoJRGVsZXRlQXBwEGYSCwoHUmVhZEFwcBBnEg0KCVVwZGF0ZUFwcBBoEg8KCkNyZWF0ZVZpZXcQyQESDQoIUmVhZFZpZXcQygESDwoKVXBkYXRlVmlldxDLARIPCgpEZWxldGVWaWV3EMwB');
+final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSDQoJQ3JlYXRlQXBwEGUSDQoJRGVsZXRlQXBwEGYSCwoHUmVhZEFwcBBnEg0KCVVwZGF0ZUFwcBBoEg8KCkNyZWF0ZVZpZXcQyQESDQoIUmVhZFZpZXcQygESDwoKVXBkYXRlVmlldxDLARIPCgpEZWxldGVWaWV3EMwB');

+ 1 - 0
rust-lib/dart-ffi/src/c.rs

@@ -8,6 +8,7 @@ pub fn forget_rust(buf: Vec<u8>) -> *const u8 {
 }
 
 #[allow(unused_attributes)]
+#[allow(dead_code)]
 pub fn reclaim_rust(ptr: *mut u8, length: u32) {
     unsafe {
         let len: usize = length as usize;

+ 0 - 2
rust-lib/flowy-dispatch/src/errors/errors.rs

@@ -89,7 +89,6 @@ pub(crate) enum InternalError {
     ProtobufError(String),
     UnexpectedNone(String),
     DeserializeFromBytes(String),
-    SerializeToBytes(String),
     JoinError(String),
     Lock(String),
     ServiceNotFound(String),
@@ -103,7 +102,6 @@ impl fmt::Display for InternalError {
             InternalError::ProtobufError(s) => fmt::Display::fmt(&s, f),
             InternalError::UnexpectedNone(s) => fmt::Display::fmt(&s, f),
             InternalError::DeserializeFromBytes(s) => fmt::Display::fmt(&s, f),
-            InternalError::SerializeToBytes(s) => fmt::Display::fmt(&s, f),
             InternalError::JoinError(s) => fmt::Display::fmt(&s, f),
             InternalError::Lock(s) => fmt::Display::fmt(&s, f),
             InternalError::ServiceNotFound(s) => fmt::Display::fmt(&s, f),

+ 1 - 0
rust-lib/flowy-document/src/sql_tables/doc/doc_sql.rs

@@ -36,5 +36,6 @@ impl DocTableSql {
         Ok(doc_table)
     }
 
+    #[allow(dead_code)]
     pub(crate) fn delete_doc(&self, _view_id: &str) -> Result<(), DocError> { unimplemented!() }
 }

+ 6 - 23
rust-lib/flowy-net/src/request/request.rs

@@ -79,7 +79,12 @@ impl HttpRequestBuilder {
             }
 
             let response = builder.send().await;
-            tx.send(response);
+            match tx.send(response) {
+                Ok(_) => {},
+                Err(e) => {
+                    log::error!("Send http response failed: {:?}", e)
+                },
+            }
         });
 
         let response = rx.await??;
@@ -104,28 +109,6 @@ impl HttpRequestBuilder {
     }
 }
 
-#[allow(dead_code)]
-pub async fn http_post<T1, T2>(url: &str, data: T1) -> Result<T2, ServerError>
-where
-    T1: TryInto<Bytes, Error = ProtobufError>,
-    T2: TryFrom<Bytes, Error = ProtobufError>,
-{
-    let body: Bytes = data.try_into()?;
-    let url = url.to_owned();
-    let (tx, rx) = oneshot::channel::<Result<Response, _>>();
-
-    // reqwest client is not 'Sync' by channel is.
-    tokio::spawn(async move {
-        let client = default_client();
-        let response = client.post(&url).body(body).send().await;
-        tx.send(response);
-    });
-
-    let response = rx.await??;
-    let data = get_response_data(response).await?;
-    Ok(T2::try_from(data)?)
-}
-
 async fn get_response_data(original: Response) -> Result<Bytes, ServerError> {
     if original.status() == http::StatusCode::OK {
         let bytes = original.bytes().await?;

+ 2 - 0
rust-lib/flowy-observable/src/dart/stream_sender.rs

@@ -8,6 +8,7 @@ lazy_static! {
 }
 
 pub struct RustStreamSender {
+    #[allow(dead_code)]
     isolate: Option<allo_isolate::Isolate>,
 }
 
@@ -19,6 +20,7 @@ impl RustStreamSender {
         self.isolate = Some(allo_isolate::Isolate::new(port));
     }
 
+    #[allow(dead_code)]
     fn inner_post(&self, observable_subject: ObservableSubject) -> Result<(), String> {
         match self.isolate {
             Some(ref isolate) => {

+ 2 - 3
rust-lib/flowy-sdk/Cargo.toml

@@ -7,8 +7,8 @@ edition = "2018"
 
 [dependencies]
 flowy-dispatch = { path = "../flowy-dispatch", features = ["use_tracing"]}
-flowy-log = { path = "../flowy-log" }
-#flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
+#flowy-log = { path = "../flowy-log" }
+flowy-log = { path = "../flowy-log", features = ["use_bunyan"] }
 flowy-user = { path = "../flowy-user" }
 flowy-infra = { path = "../flowy-infra" }
 flowy-workspace = { path = "../flowy-workspace" }
@@ -19,7 +19,6 @@ log = "0.4.14"
 futures-core = { version = "0.3", default-features = false }
 color-eyre = { version = "0.5", default-features = false }
 bytes = "1.0"
-
 [dev-dependencies]
 serde = { version = "1.0", features = ["derive"] }
 bincode = { version = "1.3"}

+ 2 - 54
rust-lib/flowy-sdk/src/deps_resolve/user_deps_impl.rs

@@ -1,56 +1,4 @@
-use bytes::Bytes;
-use flowy_dispatch::prelude::{
-    DispatchError,
-    DispatchFuture,
-    EventDispatch,
-    ModuleRequest,
-    ToBytes,
-};
-use flowy_user::{
-    errors::{ErrorBuilder, UserErrCode, UserError},
-    prelude::UserWorkspaceController,
-};
-use flowy_workspace::{
-    entities::workspace::{CreateWorkspaceRequest, Workspace},
-    event::WorkspaceEvent::CreateWorkspace,
-};
+use flowy_user::services::workspace::UserWorkspaceController;
 
 pub struct UserWorkspaceControllerImpl {}
-impl UserWorkspaceController for UserWorkspaceControllerImpl {
-    fn create_workspace(
-        &self,
-        name: &str,
-        desc: &str,
-        user_id: &str,
-    ) -> DispatchFuture<Result<String, UserError>> {
-        log::info!("Create new workspace: {:?}", name);
-        let payload: Bytes = CreateWorkspaceRequest {
-            name: name.to_string(),
-            desc: desc.to_string(),
-            user_id: user_id.to_string(),
-        }
-        .into_bytes()
-        .unwrap();
-
-        let request = ModuleRequest::new(CreateWorkspace).payload(payload);
-        DispatchFuture {
-            fut: Box::pin(async move {
-                let result = EventDispatch::async_send(request)
-                    .await
-                    .parse::<Workspace, DispatchError>()
-                    .map_err(|e| {
-                        ErrorBuilder::new(UserErrCode::CreateDefaultWorkspaceFailed)
-                            .error(e)
-                            .build()
-                    })?;
-
-                let workspace = result.map_err(|e| {
-                    ErrorBuilder::new(UserErrCode::CreateDefaultWorkspaceFailed)
-                        .error(e)
-                        .build()
-                })?;
-                Ok(workspace.id)
-            }),
-        }
-    }
-}
+impl UserWorkspaceController for UserWorkspaceControllerImpl {}

+ 6 - 4
rust-lib/flowy-test/src/builder.rs

@@ -17,12 +17,14 @@ pub type SingleUserTestBuilder = TestBuilder<FixedUserTester<WorkspaceError>>;
 impl SingleUserTestBuilder {
     pub fn new() -> Self {
         let mut builder = TestBuilder::test(Box::new(FixedUserTester::<WorkspaceError>::new()));
-        builder.login_if_need();
+        builder.setup_default_workspace();
+        builder
+    }
 
-        let user_id = builder.user_detail.as_ref().unwrap().id.clone();
+    pub fn setup_default_workspace(&mut self) {
+        self.login_if_need();
+        let user_id = self.user_detail.as_ref().unwrap().id.clone();
         let _ = create_default_workspace_if_need(&user_id);
-
-        builder
     }
 }
 pub type UserTestBuilder = TestBuilder<RandomUserTester<UserError>>;

+ 1 - 0
rust-lib/flowy-test/src/tester.rs

@@ -15,6 +15,7 @@ use std::{
     hash::Hash,
 };
 
+#[allow(dead_code)]
 pub struct TesterContext {
     request: Option<ModuleRequest>,
     response: Option<EventResponse>,

+ 1 - 0
rust-lib/flowy-user/src/services/user/user_session.rs

@@ -34,6 +34,7 @@ impl UserSessionConfig {
 pub struct UserSession {
     database: UserDB,
     config: UserSessionConfig,
+    #[allow(dead_code)]
     workspace_controller: Arc<dyn UserWorkspaceController + Send + Sync>,
     server: Arc<dyn UserServerAPI + Send + Sync>,
     user_id: RwLock<Option<String>>,

+ 1 - 11
rust-lib/flowy-user/src/services/workspace/action.rs

@@ -1,11 +1 @@
-use crate::errors::UserError;
-use flowy_dispatch::prelude::DispatchFuture;
-
-pub trait UserWorkspaceController {
-    fn create_workspace(
-        &self,
-        name: &str,
-        desc: &str,
-        user_id: &str,
-    ) -> DispatchFuture<Result<String, UserError>>;
-}
+pub trait UserWorkspaceController {}

+ 1 - 0
rust-lib/flowy-workspace/src/entities/workspace/workspace_query.rs

@@ -4,6 +4,7 @@ use std::convert::TryInto;
 
 #[derive(Default, ProtoBuf)]
 pub struct QueryWorkspaceRequest {
+    // return all workspace if workspace_id is None
     #[pb(index = 1, one_of)]
     pub workspace_id: Option<String>,
 

+ 4 - 8
rust-lib/flowy-workspace/src/event.rs

@@ -12,21 +12,17 @@ pub enum WorkspaceEvent {
     #[event(output = "Workspace")]
     ReadCurWorkspace = 1,
 
-    #[display(fmt = "ReadWorkspace")]
-    #[event(input = "QueryWorkspaceRequest", output = "Workspace")]
-    ReadWorkspace    = 2,
+    #[display(fmt = "ReadWorkspaces")]
+    #[event(input = "QueryWorkspaceRequest", output = "RepeatedWorkspace")]
+    ReadWorkspaces   = 2,
 
     #[display(fmt = "DeleteWorkspace")]
     #[event(input = "DeleteWorkspaceRequest")]
     DeleteWorkspace  = 3,
 
-    #[display(fmt = "ReadAllWorkspace")]
-    #[event(output = "RepeatedWorkspace")]
-    ReadAllWorkspace = 4,
-
     #[display(fmt = "OpenWorkspace")]
     #[event(input = "QueryWorkspaceRequest", output = "Workspace")]
-    OpenWorkspace    = 5,
+    OpenWorkspace    = 4,
 
     #[display(fmt = "CreateApp")]
     #[event(input = "CreateAppRequest", output = "App")]

+ 1 - 10
rust-lib/flowy-workspace/src/handlers/workspace_handler.rs

@@ -26,7 +26,7 @@ pub async fn read_cur_workspace(
 }
 
 #[tracing::instrument(name = "read_workspace", skip(data, controller))]
-pub async fn read_workspace(
+pub async fn read_workspaces(
     data: Data<QueryWorkspaceRequest>,
     controller: Unit<Arc<WorkspaceController>>,
 ) -> DataResult<RepeatedWorkspace, WorkspaceError> {
@@ -49,12 +49,3 @@ pub async fn open_workspace(
         },
     }
 }
-
-#[tracing::instrument(name = "get_all_workspaces", skip(controller))]
-pub async fn read_all_workspaces(
-    controller: Unit<Arc<WorkspaceController>>,
-) -> DataResult<RepeatedWorkspace, WorkspaceError> {
-    let workspaces = controller.read_workspaces_belong_to_user().await?;
-
-    data_result(RepeatedWorkspace { items: workspaces })
-}

+ 1 - 2
rust-lib/flowy-workspace/src/module.rs

@@ -45,10 +45,9 @@ pub fn create(user: Arc<dyn WorkspaceUser>, database: Arc<dyn WorkspaceDatabase>
         .data(view_controller);
 
     module = module
-        .event(WorkspaceEvent::ReadAllWorkspace, read_all_workspaces)
         .event(WorkspaceEvent::CreateWorkspace, create_workspace)
         .event(WorkspaceEvent::ReadCurWorkspace, read_cur_workspace)
-        .event(WorkspaceEvent::ReadWorkspace, read_workspace)
+        .event(WorkspaceEvent::ReadWorkspaces, read_workspaces)
         .event(WorkspaceEvent::OpenWorkspace, open_workspace);
 
     module = module

+ 40 - 46
rust-lib/flowy-workspace/src/protobuf/model/event.rs

@@ -27,10 +27,9 @@
 pub enum WorkspaceEvent {
     CreateWorkspace = 0,
     ReadCurWorkspace = 1,
-    ReadWorkspace = 2,
+    ReadWorkspaces = 2,
     DeleteWorkspace = 3,
-    ReadAllWorkspace = 4,
-    OpenWorkspace = 5,
+    OpenWorkspace = 4,
     CreateApp = 101,
     DeleteApp = 102,
     ReadApp = 103,
@@ -50,10 +49,9 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
         match value {
             0 => ::std::option::Option::Some(WorkspaceEvent::CreateWorkspace),
             1 => ::std::option::Option::Some(WorkspaceEvent::ReadCurWorkspace),
-            2 => ::std::option::Option::Some(WorkspaceEvent::ReadWorkspace),
+            2 => ::std::option::Option::Some(WorkspaceEvent::ReadWorkspaces),
             3 => ::std::option::Option::Some(WorkspaceEvent::DeleteWorkspace),
-            4 => ::std::option::Option::Some(WorkspaceEvent::ReadAllWorkspace),
-            5 => ::std::option::Option::Some(WorkspaceEvent::OpenWorkspace),
+            4 => ::std::option::Option::Some(WorkspaceEvent::OpenWorkspace),
             101 => ::std::option::Option::Some(WorkspaceEvent::CreateApp),
             102 => ::std::option::Option::Some(WorkspaceEvent::DeleteApp),
             103 => ::std::option::Option::Some(WorkspaceEvent::ReadApp),
@@ -70,9 +68,8 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
         static values: &'static [WorkspaceEvent] = &[
             WorkspaceEvent::CreateWorkspace,
             WorkspaceEvent::ReadCurWorkspace,
-            WorkspaceEvent::ReadWorkspace,
+            WorkspaceEvent::ReadWorkspaces,
             WorkspaceEvent::DeleteWorkspace,
-            WorkspaceEvent::ReadAllWorkspace,
             WorkspaceEvent::OpenWorkspace,
             WorkspaceEvent::CreateApp,
             WorkspaceEvent::DeleteApp,
@@ -110,44 +107,41 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
 }
 
 static file_descriptor_proto_data: &'static [u8] = b"\
-    \n\x0bevent.proto*\x88\x02\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
-    ace\x10\0\x12\x14\n\x10ReadCurWorkspace\x10\x01\x12\x11\n\rReadWorkspace\
-    \x10\x02\x12\x13\n\x0fDeleteWorkspace\x10\x03\x12\x14\n\x10ReadAllWorksp\
-    ace\x10\x04\x12\x11\n\rOpenWorkspace\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\x16\n\x0c\n\x05\x05\0\x02\
-    \x02\x01\x12\x03\x05\x04\x11\n\x0c\n\x05\x05\0\x02\x02\x02\x12\x03\x05\
-    \x14\x15\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\x19\n\x0c\n\x05\
-    \x05\0\x02\x04\x01\x12\x03\x07\x04\x14\n\x0c\n\x05\x05\0\x02\x04\x02\x12\
-    \x03\x07\x17\x18\n\x0b\n\x04\x05\0\x02\x05\x12\x03\x08\x04\x16\n\x0c\n\
-    \x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x11\n\x0c\n\x05\x05\0\x02\x05\x02\
-    \x12\x03\x08\x14\x15\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\
+    \n\x0bevent.proto*\xf3\x01\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\
 ";
 
 static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

+ 2 - 3
rust-lib/flowy-workspace/src/protobuf/proto/event.proto

@@ -3,10 +3,9 @@ syntax = "proto3";
 enum WorkspaceEvent {
     CreateWorkspace = 0;
     ReadCurWorkspace = 1;
-    ReadWorkspace = 2;
+    ReadWorkspaces = 2;
     DeleteWorkspace = 3;
-    ReadAllWorkspace = 4;
-    OpenWorkspace = 5;
+    OpenWorkspace = 4;
     CreateApp = 101;
     DeleteApp = 102;
     ReadApp = 103;

+ 1 - 0
rust-lib/flowy-workspace/src/services/app_controller.rs

@@ -13,6 +13,7 @@ use std::sync::Arc;
 pub struct AppController {
     user: Arc<dyn WorkspaceUser>,
     sql: Arc<AppTableSql>,
+    #[allow(dead_code)]
     view_controller: Arc<ViewController>,
 }
 

+ 0 - 12
rust-lib/flowy-workspace/src/services/workspace_controller.rs

@@ -94,18 +94,6 @@ impl WorkspaceController {
         Ok(RepeatedWorkspace { items: workspaces })
     }
 
-    pub async fn read_workspaces_belong_to_user(&self) -> Result<Vec<Workspace>, WorkspaceError> {
-        let user_id = self.user.user_id()?;
-        let workspace = self
-            .sql
-            .read_workspaces_belong_to_user(&user_id)?
-            .into_iter()
-            .map(|workspace_table| workspace_table.into())
-            .collect::<Vec<Workspace>>();
-
-        Ok(workspace)
-    }
-
     pub async fn read_cur_workspace(&self) -> Result<Workspace, WorkspaceError> {
         let workspace_id = get_current_workspace()?;
         let mut repeated_workspace = self.read_workspaces(Some(workspace_id.clone())).await?;

+ 0 - 15
rust-lib/flowy-workspace/src/sql_tables/workspace/workspace_sql.rs

@@ -73,19 +73,4 @@ impl WorkspaceSql {
 
         Ok(apps)
     }
-
-    pub(crate) fn read_workspaces_belong_to_user(
-        &self,
-        user_id: &str,
-    ) -> Result<Vec<WorkspaceTable>, WorkspaceError> {
-        let conn = self.database.db_connection()?;
-        let workspaces = conn.immediate_transaction::<_, WorkspaceError, _>(|| {
-            let workspaces = dsl::workspace_table
-                .filter(workspace_table::user_id.eq(user_id))
-                .load::<WorkspaceTable>(&*(conn))?;
-            Ok(workspaces)
-        })?;
-
-        Ok(workspaces)
-    }
 }

+ 1 - 1
rust-lib/flowy-workspace/tests/event/helper.rs

@@ -32,7 +32,7 @@ pub fn create_workspace(name: &str, desc: &str) -> (String, Workspace) {
 
 pub fn read_workspaces(request: QueryWorkspaceRequest) -> Option<Workspace> {
     let mut repeated_workspace = SingleUserTestBuilder::new()
-        .event(ReadWorkspace)
+        .event(ReadWorkspaces)
         .request(request)
         .sync_send()
         .parse::<RepeatedWorkspace>();

+ 11 - 18
rust-lib/flowy-workspace/tests/event/workspace_test.rs

@@ -13,27 +13,20 @@ use flowy_workspace::{
 #[test]
 fn workspace_create_success() { let _ = create_workspace("First workspace", ""); }
 
-#[test]
-fn workspace_get_success() {
-    let builder = SingleUserTestBuilder::new();
-
-    let _workspaces = SingleUserTestBuilder::new()
-        .event(ReadAllWorkspace)
-        .sync_send()
-        .parse::<RepeatedWorkspace>();
-
-    let workspace = builder
-        .event(ReadCurWorkspace)
-        .sync_send()
-        .parse::<Workspace>();
-
-    dbg!(&workspace);
-}
-
 #[test]
 fn workspace_read_all_success() {
+    let (user_id, _) = create_workspace(
+        "Workspace A",
+        "workspace_create_and_then_get_workspace_success",
+    );
+    let request = QueryWorkspaceRequest {
+        workspace_id: None,
+        user_id,
+    };
+
     let workspaces = SingleUserTestBuilder::new()
-        .event(ReadAllWorkspace)
+        .event(ReadWorkspaces)
+        .request(request)
         .sync_send()
         .parse::<RepeatedWorkspace>();