refactor extract observable payload

This commit is contained in:
appflowy 2021-09-07 17:12:03 +08:00
parent 024ab85864
commit f18cc717ea
47 changed files with 1220 additions and 751 deletions

View File

@ -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';

View File

@ -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<WorkspaceListBloc, WorkspaceListState>(
create: (_) => getIt<WelcomeBloc>(param1: repo.user)
..add(const WelcomeEvent.initial()),
child: BlocBuilder<WelcomeBloc, WelcomeState>(
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<WorkspaceListBloc>()
.add(const WorkspaceListEvent.createWorkspace("workspace", ""));
.read<WelcomeBloc>()
.add(const WelcomeEvent.createWorkspace("workspace", ""));
},
),
);
@ -77,9 +79,7 @@ class WelcomeScreen extends StatelessWidget {
}
void _handleOnPress(BuildContext context, Workspace workspace) {
context
.read<WorkspaceListBloc>()
.add(WorkspaceListEvent.openWorkspace(workspace));
context.read<WelcomeBloc>().add(WelcomeEvent.openWorkspace(workspace));
Navigator.of(context).pop(workspace.id);
}

View File

@ -52,6 +52,7 @@ class MenuBloc extends Bloc<MenuEvent, MenuState> {
);
}
// ignore: unused_element
Stream<MenuState> _fetchApps() async* {
final appsOrFail = await workspace.getApps();
yield appsOrFail.fold(

View File

@ -16,17 +16,17 @@ class MenuUserBloc extends Bloc<MenuUserEvent, MenuUserState> {
Stream<MenuUserState> 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* {},
);

View File

@ -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<WelcomeEvent, WelcomeState> {
UserRepo repo;
IUserWorkspaceListWatch watcher;
WelcomeBloc({required this.repo, required this.watcher})
: super(WelcomeState.initial());
@override
Stream<WelcomeState> 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<WelcomeState> _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<WelcomeState> _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<WelcomeState> _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<List<Workspace>, 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<List<Workspace>, WorkspaceError> workspacesOrFail) =
WorkspacesReceived;
}
@freezed
abstract class WelcomeState implements _$WelcomeState {
const factory WelcomeState({
required bool isLoading,
required List<Workspace> workspaces,
required Either<Unit, WorkspaceError> successOrFailure,
}) = _WelcomeState;
factory WelcomeState.initial() => WelcomeState(
isLoading: false,
workspaces: List.empty(),
successOrFailure: left(unit),
);
}

View File

@ -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<List<Workspace>, WorkspaceError> workspacesOrFail) {
return WorkspacesReceived(
workspacesOrFail,
);
}
}
/// @nodoc
const $WorkspaceListEvent = _$WorkspaceListEventTearOff();
const $WelcomeEvent = _$WelcomeEventTearOff();
/// @nodoc
mixin _$WorkspaceListEvent {
mixin _$WelcomeEvent {
@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,
required TResult Function(
Either<List<Workspace>, WorkspaceError> workspacesOrFail)
workspacesReveived,
}) =>
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,
TResult Function(Either<List<Workspace>, WorkspaceError> workspacesOrFail)?
workspacesReveived,
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,
required TResult Function(WorkspacesReceived value) workspacesReveived,
}) =>
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,
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<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,
required TResult Function(
Either<List<Workspace>, WorkspaceError> workspacesOrFail)
workspacesReveived,
}) {
return initial();
}
@ -145,9 +152,10 @@ class _$Initial implements Initial {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? fetchWorkspaces,
TResult Function(String name, String desc)? createWorkspace,
TResult Function(Workspace workspace)? openWorkspace,
TResult Function(Either<List<Workspace>, WorkspaceError> workspacesOrFail)?
workspacesReveived,
required TResult orElse(),
}) {
if (initial != null) {
@ -160,9 +168,9 @@ class _$Initial implements Initial {
@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,
required TResult Function(WorkspacesReceived value) workspacesReveived,
}) {
return initial(this);
}
@ -171,9 +179,9 @@ class _$Initial implements Initial {
@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,
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<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(
@ -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<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,
required TResult Function(
Either<List<Workspace>, WorkspaceError> workspacesOrFail)
workspacesReveived,
}) {
return createWorkspace(name, desc);
}
@ -369,9 +285,10 @@ class _$CreateWorkspace implements CreateWorkspace {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? fetchWorkspaces,
TResult Function(String name, String desc)? createWorkspace,
TResult Function(Workspace workspace)? openWorkspace,
TResult Function(Either<List<Workspace>, WorkspaceError> workspacesOrFail)?
workspacesReveived,
required TResult orElse(),
}) {
if (createWorkspace != null) {
@ -384,9 +301,9 @@ class _$CreateWorkspace implements CreateWorkspace {
@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,
required TResult Function(WorkspacesReceived value) workspacesReveived,
}) {
return createWorkspace(this);
}
@ -395,9 +312,9 @@ class _$CreateWorkspace implements CreateWorkspace {
@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,
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<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,
required TResult Function(
Either<List<Workspace>, WorkspaceError> workspacesOrFail)
workspacesReveived,
}) {
return openWorkspace(workspace);
}
@ -495,9 +413,10 @@ class _$OpenWorkspace implements OpenWorkspace {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function()? fetchWorkspaces,
TResult Function(String name, String desc)? createWorkspace,
TResult Function(Workspace workspace)? openWorkspace,
TResult Function(Either<List<Workspace>, WorkspaceError> workspacesOrFail)?
workspacesReveived,
required TResult orElse(),
}) {
if (openWorkspace != null) {
@ -510,9 +429,9 @@ class _$OpenWorkspace implements OpenWorkspace {
@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,
required TResult Function(WorkspacesReceived value) workspacesReveived,
}) {
return openWorkspace(this);
}
@ -521,9 +440,9 @@ class _$OpenWorkspace implements OpenWorkspace {
@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,
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<List<Workspace>, 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<List<Workspace>, WorkspaceError>,
));
}
}
/// @nodoc
class _$WorkspacesReceived implements WorkspacesReceived {
const _$WorkspacesReceived(this.workspacesOrFail);
@override
final Either<List<Workspace>, 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<WorkspacesReceived> get copyWith =>
_$WorkspacesReceivedCopyWithImpl<WorkspacesReceived>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function(String name, String desc) createWorkspace,
required TResult Function(Workspace workspace) openWorkspace,
required TResult Function(
Either<List<Workspace>, WorkspaceError> workspacesOrFail)
workspacesReveived,
}) {
return workspacesReveived(workspacesOrFail);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String name, String desc)? createWorkspace,
TResult Function(Workspace workspace)? openWorkspace,
TResult Function(Either<List<Workspace>, WorkspaceError> workspacesOrFail)?
workspacesReveived,
required TResult orElse(),
}) {
if (workspacesReveived != null) {
return workspacesReveived(workspacesOrFail);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
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 extends Object?>({
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<List<Workspace>, WorkspaceError> workspacesOrFail) =
_$WorkspacesReceived;
Either<List<Workspace>, WorkspaceError> get workspacesOrFail =>
throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$WorkspacesReceivedCopyWith<WorkspacesReceived> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
class _$WelcomeStateTearOff {
const _$WelcomeStateTearOff();
_WelcomeState call(
{required bool isLoading,
required List<Workspace> workspaces,
required Either<Unit, WorkspaceError> 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<Workspace> get workspaces => throw _privateConstructorUsedError;
Either<Unit, WorkspaceError> get successOrFailure =>
throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$WorkspaceListStateCopyWith<WorkspaceListState> get copyWith =>
$WelcomeStateCopyWith<WelcomeState> 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<Workspace> 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<Workspace> workspaces,
required Either<Unit, WorkspaceError> 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;
}

View File

@ -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<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) {
Log.error(error);
return 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) {
Log.error(error);
return 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) {
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<Workspace> workspaces,
required Either<Unit, WorkspaceError> successOrFailure,
}) = _WorkspaceListState;
factory WorkspaceListState.initial() => WorkspaceListState(
isLoading: false,
workspaces: List.empty(),
successOrFailure: left(unit),
);
}

View File

@ -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<List<Workspace>, WorkspaceError> workspacesOrFailed);
typedef UserDeleteWorkspaceCallback = void Function(
typedef WorkspaceListUpdatedCallback = void Function(
Either<List<Workspace>, WorkspaceError> workspacesOrFailed);
abstract class IUser {
@ -21,10 +19,9 @@ abstract class IUser {
Future<Either<Unit, UserError>> signOut();
}
abstract class IUserWatch {
abstract class IUserWorkspaceListWatch {
void startWatching(
{UserCreateWorkspaceCallback? createWorkspaceCallback,
UserDeleteWorkspaceCallback? deleteWorkspaceCallback});
{WorkspaceListUpdatedCallback? workspaceListUpdatedCallback});
Future<void> stopWatching();
}

View File

@ -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<View, WorkspaceError>);
abstract class IView {
Future<Either<View, WorkspaceError>> readView();

View File

@ -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<IUser, UserProfile, void>(
(user, _) => IUserImpl(repo: UserRepo(user: user)));
getIt.registerFactoryParam<IUserWatch, UserProfile, void>(
(user, _) => IUserWatchImpl(repo: UserWatchRepo(user: user)));
getIt.registerFactoryParam<IUserWorkspaceListWatch, UserProfile, void>(
(user, _) =>
IUserWorkspaceListWatchImpl(repo: UserWatchRepo(user: user)));
//Menu Bloc
getIt.registerFactoryParam<MenuBloc, UserProfile, String>(
@ -93,6 +95,13 @@ class HomeDepsResolver {
getIt.registerFactoryParam<ViewListBloc, List<View>, void>(
(views, _) => ViewListBloc(views: views));
getIt.registerFactoryParam<WelcomeBloc, UserProfile, void>(
(user, _) => WelcomeBloc(
repo: UserRepo(user: user),
watcher: getIt<IUserWorkspaceListWatch>(param1: user),
),
);
// getIt.registerFactoryParam<ViewBloc, String, void>(
// (viewId, _) => ViewBloc(iViewImpl: getIt<IView>(param1: viewId)));
}

View File

@ -31,22 +31,20 @@ class IUserImpl extends IUser {
@override
Future<Either<List<Workspace>, 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

View File

@ -20,9 +20,9 @@ class IWorkspaceImpl extends IWorkspace {
@override
Future<Either<List<App>, 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),
);
});

View File

@ -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<Uint8List, WorkspaceError> 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;

View File

@ -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<Uint8List, WorkspaceError>)
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
}
}
}

View File

@ -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<Either<List<Workspace>, WorkspaceError>> fetchWorkspaces() {
Future<Either<List<Workspace>, WorkspaceError>> getWorkspaces() {
final request = QueryWorkspaceRequest.create();
return WorkspaceEventReadWorkspaces(request).send().then((result) {
@ -69,8 +70,7 @@ class UserRepo {
class UserWatchRepo {
StreamSubscription<ObservableSubject>? _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;
}

View File

@ -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<ObservableSubject>? _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<Uint8List, WorkspaceError> 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;

View File

@ -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<Either<App, WorkspaceError>> 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<Either<Workspace, WorkspaceError>> getWorkspace() {
final request = QueryWorkspaceRequest.create()..workspaceId = workspaceId;
return WorkspaceEventReadWorkspaces(request).send().then((result) {
return result.fold(
(workspaces) {
@ -57,6 +50,16 @@ class WorkspaceRepo {
);
});
}
Future<Either<List<App>, 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<Uint8List, WorkspaceError> 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;

View File

@ -84,6 +84,23 @@ class WorkspaceEventOpenWorkspace {
}
}
class WorkspaceEventReadWorkspaceApps {
QueryWorkspaceRequest request;
WorkspaceEventReadWorkspaceApps(this.request);
Future<Either<RepeatedApp, WorkspaceError>> 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);

View File

@ -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<ObservableSubject>(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);
}

View File

@ -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');

View File

@ -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,

View File

@ -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=');

View File

@ -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]

View File

@ -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(())
}

View File

@ -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<Vec<u8>>,
pub payload: Option<Vec<u8>>,
#[pb(index = 5, one_of)]
pub error: Option<Vec<u8>>,
}
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,
}
}
}

View File

@ -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<ObservableSubject_oneof_one_of_subject_payload>,
pub one_of_payload: ::std::option::Option<ObservableSubject_oneof_one_of_payload>,
pub one_of_error: ::std::option::Option<ObservableSubject_oneof_one_of_error>,
// 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<u8>),
pub enum ObservableSubject_oneof_one_of_payload {
payload(::std::vec::Vec<u8>),
}
#[derive(Clone,PartialEq,Debug)]
pub enum ObservableSubject_oneof_one_of_error {
error(::std::vec::Vec<u8>),
}
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<u8>) {
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<u8>) {
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<u8> {
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<u8> {
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<u8> {
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<u8> {
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<u8>) {
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<u8> {
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<u8> {
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>(
"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;<b\x06p\
roto3\
\x02ty\x12\x0e\n\x02id\x18\x03\x20\x01(\tR\x02id\x12\x1a\n\x07payload\
\x18\x04\x20\x01(\x0cH\0R\x07payload\x12\x16\n\x05error\x18\x05\x20\x01(\
\x0cH\x01R\x05errorB\x10\n\x0eone_of_payloadB\x0e\n\x0cone_of_errorJ\xf3\
\x02\n\x06\x12\x04\0\0\x08\x01\n\x08\n\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\
\x04\0\x12\x04\x02\0\x08\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\x12\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\r\n\x0c\n\x05\x04\0\x02\x02\x03\x12\x03\x05\x10\x11\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\x18\n\x0b\n\x04\x04\0\x02\x03\x12\x03\x06\x1b-\n\x0c\n\x05\
\x04\0\x02\x03\x05\x12\x03\x06\x1b\x20\n\x0c\n\x05\x04\0\x02\x03\x01\x12\
\x03\x06!(\n\x0c\n\x05\x04\0\x02\x03\x03\x12\x03\x06+,\n\x0b\n\x04\x04\0\
\x08\x01\x12\x03\x07\x04+\n\x0c\n\x05\x04\0\x08\x01\x01\x12\x03\x07\n\
\x16\n\x0b\n\x04\x04\0\x02\x04\x12\x03\x07\x19)\n\x0c\n\x05\x04\0\x02\
\x04\x05\x12\x03\x07\x19\x1e\n\x0c\n\x05\x04\0\x02\x04\x01\x12\x03\x07\
\x1f$\n\x0c\n\x05\x04\0\x02\x04\x03\x12\x03\x07'(b\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -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; };
}

View File

@ -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>) -> 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,

View File

@ -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<ConnectionPool>, 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(())
}

View File

@ -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"

View File

@ -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,
}

View File

@ -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(&params.app_id).await?;
app.belongings = RepeatedView { items: views };
app.belongings = view_controller.read_views_belong_to(&params.app_id).await?;
}
data_result(app)

View File

@ -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(&params.view_id).await?;
view.belongings = RepeatedView { items: views }
view.belongings = controller.read_views_belong_to(&params.view_id).await?;
}
data_result(view)

View File

@ -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<Arc<WorkspaceCon
data_result(workspace)
}
#[tracing::instrument(skip(controller), err)]
pub(crate) async fn read_workspace_apps_handler(controller: Unit<Arc<WorkspaceController>>) -> DataResult<RepeatedApp, WorkspaceError> {
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<QueryWorkspaceRequest>,

View File

@ -52,7 +52,8 @@ pub fn create(user: Arc<dyn WorkspaceUser>, database: Arc<dyn WorkspaceDatabase>
.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)

View File

@ -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<Bytes>,
error: Option<Bytes>,
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<T>(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<T>(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<T>(id: &str, ty: WorkspaceObservable, payload: T)
where
T: ToBytes,
{
ObservableSender::new(id, ty).payload(payload).send();
}

View File

@ -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;

View File

@ -6,6 +6,7 @@ enum WorkspaceEvent {
ReadWorkspaces = 2;
DeleteWorkspace = 3;
OpenWorkspace = 4;
ReadWorkspaceApps = 5;
CreateApp = 101;
DeleteApp = 102;
ReadApp = 103;

View File

@ -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<ViewController>,
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<RepeatedApp, WorkspaceError> {
let app_tables = self.sql.read_apps(workspace_id, false)?;
let apps = app_tables.into_iter().map(|table| table.into()).collect::<Vec<App>>();
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(())

View File

@ -1,7 +1,9 @@
pub fn spawn<F>(f: F)
use tokio::task::JoinHandle;
pub fn spawn<F>(f: F) -> JoinHandle<F::Output>
where
F: std::future::Future + Send + 'static,
F::Output: Send + 'static,
{
let _ = tokio::spawn(f);
tokio::spawn(f)
}

View File

@ -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<Vec<View>, WorkspaceError> {
pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, WorkspaceError> {
// 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::<Vec<View>>();
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<RepeatedView, WorkspaceError> {
let views = self
.sql
.read_views_belong_to(belong_to_id)?
.into_iter()
.map(|view_table| view_table.into())
.collect::<Vec<View>>();
Ok(RepeatedView { items: views })
}
}

View File

@ -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<AppController>,
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<Workspace, WorkspaceError> {
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<RepeatedWorkspace, WorkspaceError> {
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<Workspace, WorkspaceError> {
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<RepeatedApp, WorkspaceError> {
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<String>, user_id: &str) -> Result<RepeatedWorkspace, WorkspaceError> {
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<Workspace, WorkspaceError> {
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<Workspace, WorkspaceError> {
// 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<Vec<App>, 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<Vec<App>, WorkspaceError> {
#[tracing::instrument(level = "debug", skip(self), err)]
fn read_local_apps(&self, workspace_id: &str) -> Result<Vec<App>, 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<String>, user_id: String) -> Result<Vec<WorkspaceTable>, 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(())

View File

@ -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<dyn WorkspaceDatabase>,
database: Arc<dyn WorkspaceDatabase>,
}
impl AppTableSql {
pub fn new(database: Arc<dyn WorkspaceDatabase>) -> 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<AppTable, WorkspaceError> {
pub(crate) fn read_app(&self, app_id: &str, is_trash: bool) -> Result<AppTable, WorkspaceError> {
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<Vec<AppTable>, WorkspaceError> {
let app_table = dsl::app_table
.filter(app_table::workspace_id.eq(workspace_id))
.filter(app_table::is_trash.eq(is_trash))
.load::<AppTable>(&*(self.database.db_connection()?))?;
Ok(app_table)
}
pub(crate) fn delete_app(&self, app_id: &str) -> Result<AppTable, WorkspaceError> {
let conn = self.database.db_connection()?;
// TODO: group into sql transaction

View File

@ -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<String>,
pub desc: Option<String>,
@ -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<App> for AppTable {

View File

@ -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<ViewTable, WorkspaceError> {
pub(crate) fn read_view(&self, view_id: &str, is_trash: bool) -> Result<ViewTable, WorkspaceError> {
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<Vec<ViewTable>, WorkspaceError> {
pub(crate) fn read_views_belong_to(&self, belong_to_id: &str) -> Result<Vec<ViewTable>, WorkspaceError> {
let view_tables = dsl::view_table
.filter(view_table::belong_to_id.eq(belong_to_id))
.load::<ViewTable>(&*(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(())
}

View File

@ -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<dyn WorkspaceDatabase>,
pub(crate) struct WorkspaceSql {
database: Arc<dyn WorkspaceDatabase>,
app_sql: Arc<AppTableSql>,
}
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<dyn WorkspaceDatabase>) -> 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<String>,
user_id: &str,
) -> Result<Vec<WorkspaceTable>, WorkspaceError> {
pub(crate) fn create_apps(&self, apps: Vec<App>, 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<String>, user_id: &str) -> Result<Vec<WorkspaceTable>, 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<Vec<AppTable>, WorkspaceError> {
pub(crate) fn read_apps_belong_to_workspace(&self, workspace_id: &str) -> Result<Vec<AppTable>, 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<DBConnection, WorkspaceError> {
let db = self.database.db_connection()?;
Ok(db)
}
}

View File

@ -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),
}
}
}