create view with doc, ignore thumbnail for now

This commit is contained in:
appflowy 2021-09-11 20:09:46 +08:00
parent 59d447e27b
commit 9ade419b22
46 changed files with 295 additions and 412 deletions

View File

@ -21,8 +21,12 @@ class AppBloc extends Bloc<AppEvent, AppState> {
yield* _fetchViews();
},
createView: (CreateView value) async* {
iAppImpl.createView(
final viewOrFailed = await iAppImpl.createView(
name: value.name, desc: value.desc, viewType: value.viewType);
yield viewOrFailed.fold((view) => state, (error) {
Log.error(error);
return state.copyWith(successOrFailure: right(error));
});
},
);
}

View File

@ -1,7 +1,7 @@
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/workspace/domain/i_doc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flowy_sdk/protobuf/flowy-document/errors.pb.dart';
part 'doc_watch_bloc.freezed.dart';
class DocWatchBloc extends Bloc<DocWatchEvent, DocWatchState> {
@ -24,7 +24,9 @@ class DocWatchBloc extends Bloc<DocWatchEvent, DocWatchState> {
final docOrFail = await iDocImpl.readDoc();
yield docOrFail.fold(
(doc) => DocWatchState.loadDoc(doc),
(error) => DocWatchState.loadFail(error),
(error) {
return DocWatchState.loadFail(error);
},
);
}
}
@ -37,6 +39,6 @@ class DocWatchEvent with _$DocWatchEvent {
@freezed
class DocWatchState with _$DocWatchState {
const factory DocWatchState.loading() = Loading;
const factory DocWatchState.loadDoc(Doc doc) = LoadDoc;
const factory DocWatchState.loadFail(DocError error) = LoadFail;
const factory DocWatchState.loadDoc(FlowyDoc doc) = LoadDoc;
const factory DocWatchState.loadFail(WorkspaceError error) = LoadFail;
}

View File

@ -154,13 +154,13 @@ class _$DocWatchStateTearOff {
return const Loading();
}
LoadDoc loadDoc(Doc doc) {
LoadDoc loadDoc(FlowyDoc doc) {
return LoadDoc(
doc,
);
}
LoadFail loadFail(DocError error) {
LoadFail loadFail(WorkspaceError error) {
return LoadFail(
error,
);
@ -175,15 +175,15 @@ mixin _$DocWatchState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() loading,
required TResult Function(Doc doc) loadDoc,
required TResult Function(DocError error) loadFail,
required TResult Function(FlowyDoc doc) loadDoc,
required TResult Function(WorkspaceError error) loadFail,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading,
TResult Function(Doc doc)? loadDoc,
TResult Function(DocError error)? loadFail,
TResult Function(FlowyDoc doc)? loadDoc,
TResult Function(WorkspaceError error)? loadFail,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@ -259,8 +259,8 @@ class _$Loading implements Loading {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() loading,
required TResult Function(Doc doc) loadDoc,
required TResult Function(DocError error) loadFail,
required TResult Function(FlowyDoc doc) loadDoc,
required TResult Function(WorkspaceError error) loadFail,
}) {
return loading();
}
@ -269,8 +269,8 @@ class _$Loading implements Loading {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading,
TResult Function(Doc doc)? loadDoc,
TResult Function(DocError error)? loadFail,
TResult Function(FlowyDoc doc)? loadDoc,
TResult Function(WorkspaceError error)? loadFail,
required TResult orElse(),
}) {
if (loading != null) {
@ -312,7 +312,7 @@ abstract class Loading implements DocWatchState {
abstract class $LoadDocCopyWith<$Res> {
factory $LoadDocCopyWith(LoadDoc value, $Res Function(LoadDoc) then) =
_$LoadDocCopyWithImpl<$Res>;
$Res call({Doc doc});
$Res call({FlowyDoc doc});
}
/// @nodoc
@ -332,7 +332,7 @@ class _$LoadDocCopyWithImpl<$Res> extends _$DocWatchStateCopyWithImpl<$Res>
doc == freezed
? _value.doc
: doc // ignore: cast_nullable_to_non_nullable
as Doc,
as FlowyDoc,
));
}
}
@ -343,7 +343,7 @@ class _$LoadDoc implements LoadDoc {
const _$LoadDoc(this.doc);
@override
final Doc doc;
final FlowyDoc doc;
@override
String toString() {
@ -371,8 +371,8 @@ class _$LoadDoc implements LoadDoc {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() loading,
required TResult Function(Doc doc) loadDoc,
required TResult Function(DocError error) loadFail,
required TResult Function(FlowyDoc doc) loadDoc,
required TResult Function(WorkspaceError error) loadFail,
}) {
return loadDoc(doc);
}
@ -381,8 +381,8 @@ class _$LoadDoc implements LoadDoc {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading,
TResult Function(Doc doc)? loadDoc,
TResult Function(DocError error)? loadFail,
TResult Function(FlowyDoc doc)? loadDoc,
TResult Function(WorkspaceError error)? loadFail,
required TResult orElse(),
}) {
if (loadDoc != null) {
@ -417,9 +417,9 @@ class _$LoadDoc implements LoadDoc {
}
abstract class LoadDoc implements DocWatchState {
const factory LoadDoc(Doc doc) = _$LoadDoc;
const factory LoadDoc(FlowyDoc doc) = _$LoadDoc;
Doc get doc => throw _privateConstructorUsedError;
FlowyDoc get doc => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$LoadDocCopyWith<LoadDoc> get copyWith => throw _privateConstructorUsedError;
}
@ -428,7 +428,7 @@ abstract class LoadDoc implements DocWatchState {
abstract class $LoadFailCopyWith<$Res> {
factory $LoadFailCopyWith(LoadFail value, $Res Function(LoadFail) then) =
_$LoadFailCopyWithImpl<$Res>;
$Res call({DocError error});
$Res call({WorkspaceError error});
}
/// @nodoc
@ -448,7 +448,7 @@ class _$LoadFailCopyWithImpl<$Res> extends _$DocWatchStateCopyWithImpl<$Res>
error == freezed
? _value.error
: error // ignore: cast_nullable_to_non_nullable
as DocError,
as WorkspaceError,
));
}
}
@ -459,7 +459,7 @@ class _$LoadFail implements LoadFail {
const _$LoadFail(this.error);
@override
final DocError error;
final WorkspaceError error;
@override
String toString() {
@ -487,8 +487,8 @@ class _$LoadFail implements LoadFail {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() loading,
required TResult Function(Doc doc) loadDoc,
required TResult Function(DocError error) loadFail,
required TResult Function(FlowyDoc doc) loadDoc,
required TResult Function(WorkspaceError error) loadFail,
}) {
return loadFail(error);
}
@ -497,8 +497,8 @@ class _$LoadFail implements LoadFail {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading,
TResult Function(Doc doc)? loadDoc,
TResult Function(DocError error)? loadFail,
TResult Function(FlowyDoc doc)? loadDoc,
TResult Function(WorkspaceError error)? loadFail,
required TResult orElse(),
}) {
if (loadFail != null) {
@ -533,9 +533,9 @@ class _$LoadFail implements LoadFail {
}
abstract class LoadFail implements DocWatchState {
const factory LoadFail(DocError error) = _$LoadFail;
const factory LoadFail(WorkspaceError error) = _$LoadFail;
DocError get error => throw _privateConstructorUsedError;
WorkspaceError get error => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$LoadFailCopyWith<LoadFail> get copyWith =>
throw _privateConstructorUsedError;

View File

@ -16,7 +16,7 @@ class ViewListBloc extends Bloc<ViewListEvent, ViewListState> {
yield ViewListState.initial(s.views);
},
openView: (s) async* {
yield state.copyWith(selectedView: some(s.view.id));
yield state.copyWith(openedView: some(s.view.id));
},
);
}
@ -32,13 +32,13 @@ class ViewListEvent with _$ViewListEvent {
abstract class ViewListState implements _$ViewListState {
const factory ViewListState({
required bool isLoading,
required Option<String> selectedView,
required Option<String> openedView,
required Option<List<View>> views,
}) = _ViewListState;
factory ViewListState.initial(List<View> views) => ViewListState(
isLoading: false,
selectedView: none(),
openedView: none(),
views: some(views),
);
}

View File

@ -310,11 +310,11 @@ class _$ViewListStateTearOff {
_ViewListState call(
{required bool isLoading,
required Option<String> selectedView,
required Option<String> openedView,
required Option<List<View>> views}) {
return _ViewListState(
isLoading: isLoading,
selectedView: selectedView,
openedView: openedView,
views: views,
);
}
@ -326,7 +326,7 @@ const $ViewListState = _$ViewListStateTearOff();
/// @nodoc
mixin _$ViewListState {
bool get isLoading => throw _privateConstructorUsedError;
Option<String> get selectedView => throw _privateConstructorUsedError;
Option<String> get openedView => throw _privateConstructorUsedError;
Option<List<View>> get views => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@ -340,7 +340,7 @@ abstract class $ViewListStateCopyWith<$Res> {
ViewListState value, $Res Function(ViewListState) then) =
_$ViewListStateCopyWithImpl<$Res>;
$Res call(
{bool isLoading, Option<String> selectedView, Option<List<View>> views});
{bool isLoading, Option<String> openedView, Option<List<View>> views});
}
/// @nodoc
@ -355,7 +355,7 @@ class _$ViewListStateCopyWithImpl<$Res>
@override
$Res call({
Object? isLoading = freezed,
Object? selectedView = freezed,
Object? openedView = freezed,
Object? views = freezed,
}) {
return _then(_value.copyWith(
@ -363,9 +363,9 @@ class _$ViewListStateCopyWithImpl<$Res>
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
selectedView: selectedView == freezed
? _value.selectedView
: selectedView // ignore: cast_nullable_to_non_nullable
openedView: openedView == freezed
? _value.openedView
: openedView // ignore: cast_nullable_to_non_nullable
as Option<String>,
views: views == freezed
? _value.views
@ -383,7 +383,7 @@ abstract class _$ViewListStateCopyWith<$Res>
__$ViewListStateCopyWithImpl<$Res>;
@override
$Res call(
{bool isLoading, Option<String> selectedView, Option<List<View>> views});
{bool isLoading, Option<String> openedView, Option<List<View>> views});
}
/// @nodoc
@ -400,7 +400,7 @@ class __$ViewListStateCopyWithImpl<$Res>
@override
$Res call({
Object? isLoading = freezed,
Object? selectedView = freezed,
Object? openedView = freezed,
Object? views = freezed,
}) {
return _then(_ViewListState(
@ -408,9 +408,9 @@ class __$ViewListStateCopyWithImpl<$Res>
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
selectedView: selectedView == freezed
? _value.selectedView
: selectedView // ignore: cast_nullable_to_non_nullable
openedView: openedView == freezed
? _value.openedView
: openedView // ignore: cast_nullable_to_non_nullable
as Option<String>,
views: views == freezed
? _value.views
@ -424,20 +424,18 @@ class __$ViewListStateCopyWithImpl<$Res>
class _$_ViewListState implements _ViewListState {
const _$_ViewListState(
{required this.isLoading,
required this.selectedView,
required this.views});
{required this.isLoading, required this.openedView, required this.views});
@override
final bool isLoading;
@override
final Option<String> selectedView;
final Option<String> openedView;
@override
final Option<List<View>> views;
@override
String toString() {
return 'ViewListState(isLoading: $isLoading, selectedView: $selectedView, views: $views)';
return 'ViewListState(isLoading: $isLoading, openedView: $openedView, views: $views)';
}
@override
@ -447,9 +445,9 @@ class _$_ViewListState implements _ViewListState {
(identical(other.isLoading, isLoading) ||
const DeepCollectionEquality()
.equals(other.isLoading, isLoading)) &&
(identical(other.selectedView, selectedView) ||
(identical(other.openedView, openedView) ||
const DeepCollectionEquality()
.equals(other.selectedView, selectedView)) &&
.equals(other.openedView, openedView)) &&
(identical(other.views, views) ||
const DeepCollectionEquality().equals(other.views, views)));
}
@ -458,7 +456,7 @@ class _$_ViewListState implements _ViewListState {
int get hashCode =>
runtimeType.hashCode ^
const DeepCollectionEquality().hash(isLoading) ^
const DeepCollectionEquality().hash(selectedView) ^
const DeepCollectionEquality().hash(openedView) ^
const DeepCollectionEquality().hash(views);
@JsonKey(ignore: true)
@ -470,13 +468,13 @@ class _$_ViewListState implements _ViewListState {
abstract class _ViewListState implements ViewListState {
const factory _ViewListState(
{required bool isLoading,
required Option<String> selectedView,
required Option<String> openedView,
required Option<List<View>> views}) = _$_ViewListState;
@override
bool get isLoading => throw _privateConstructorUsedError;
@override
Option<String> get selectedView => throw _privateConstructorUsedError;
Option<String> get openedView => throw _privateConstructorUsedError;
@override
Option<List<View>> get views => throw _privateConstructorUsedError;
@override

View File

@ -1,18 +1,17 @@
import 'package:flowy_editor/flowy_editor.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc_create.pb.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-document/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
class Doc {
final DocInfo info;
class FlowyDoc {
final Doc doc;
final Document data;
Doc({required this.info, required this.data});
FlowyDoc({required this.doc, required this.data});
}
abstract class IDoc {
Future<Either<Doc, DocError>> readDoc();
Future<Either<Unit, DocError>> updateDoc(
{String? name, String? desc, String? text});
Future<Either<Unit, DocError>> closeDoc();
Future<Either<FlowyDoc, WorkspaceError>> readDoc();
Future<Either<Unit, WorkspaceError>> updateDoc({String? text});
Future<Either<Unit, WorkspaceError>> closeDoc();
}

View File

@ -1,5 +1,4 @@
import 'package:app_flowy/workspace/infrastructure/repos/app_repo.dart';
import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart' as workspace;
import 'package:app_flowy/workspace/domain/i_app.dart';
@ -22,26 +21,11 @@ class IAppImpl extends IApp {
{required String name, String? desc, required ViewType viewType}) {
return repo.createView(name, desc ?? "", viewType).then((result) {
return result.fold(
(view) => _createDoc(view),
(view) => left(view),
(r) => right(r),
);
});
}
Future<Either<View, workspace.WorkspaceError>> _createDoc(View view) async {
switch (view.viewType) {
case ViewType.Doc:
final docRepo = DocRepository(docId: view.id);
final result = await docRepo.createDoc(
name: view.name, desc: "", text: "[{\"insert\":\"\\n\"}]");
return result.fold((l) => left(view), (r) {
return right(workspace.WorkspaceError(
code: workspace.ErrorCode.Unknown, msg: r.msg));
});
default:
return left(view);
}
}
}
class IAppWatchImpl extends IAppWatch {

View File

@ -2,10 +2,9 @@ import 'dart:convert';
import 'package:dartz/dartz.dart';
import 'package:flowy_editor/flowy_editor.dart';
import 'package:flowy_sdk/protobuf/flowy-document/errors.pb.dart';
import 'package:app_flowy/workspace/domain/i_doc.dart';
import 'package:app_flowy/workspace/infrastructure/repos/doc_repo.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
class IDocImpl extends IDoc {
DocRepository repo;
@ -13,35 +12,23 @@ class IDocImpl extends IDoc {
IDocImpl({required this.repo});
@override
Future<Either<Unit, DocError>> closeDoc() {
Future<Either<Unit, WorkspaceError>> closeDoc() {
return repo.closeDoc();
}
@override
Future<Either<Doc, DocError>> readDoc() async {
final docInfoOrFail = await repo.readDoc();
return docInfoOrFail.fold(
(info) => _loadDocument(info.path).then((result) => result.fold(
(document) => left(Doc(info: info, data: document)),
(error) => right(error))),
(error) => right(error),
);
Future<Either<FlowyDoc, WorkspaceError>> readDoc() async {
final docOrFail = await repo.readDoc();
return docOrFail.fold((doc) {
return left(FlowyDoc(doc: doc, data: _decodeToDocument(doc.data)));
}, (error) => right(error));
}
@override
Future<Either<Unit, DocError>> updateDoc(
{String? name, String? desc, String? text}) {
Future<Either<Unit, WorkspaceError>> updateDoc({String? text}) {
final json = jsonEncode(text ?? "");
return repo.updateDoc(name: name, desc: desc, text: json);
}
Future<Either<Document, DocError>> _loadDocument(String path) {
return repo.readDocData(path).then((docDataOrFail) {
return docDataOrFail.fold(
(docData) => left(_decodeToDocument(docData.text)),
(error) => right(error),
);
});
return repo.updateDoc(text: json);
}
Document _decodeToDocument(String text) {

View File

@ -1,9 +1,9 @@
import 'package:dartz/dartz.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc_create.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc_modify.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc_query.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-document/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_query.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_update.pb.dart';
class DocRepository {
final String docId;
@ -11,34 +11,19 @@ class DocRepository {
required this.docId,
});
Future<Either<DocInfo, DocError>> createDoc(
{required String name, String? desc, String? text}) {
final request =
CreateDocRequest(id: docId, name: name, desc: desc, text: text);
return EditorEventCreateDoc(request).send();
Future<Either<Doc, WorkspaceError>> readDoc() {
final request = OpenViewRequest.create()..viewId = docId;
return WorkspaceEventOpenView(request).send();
}
Future<Either<DocInfo, DocError>> readDoc() {
final request = QueryDocRequest.create()..docId = docId;
return EditorEventReadDocInfo(request).send();
Future<Either<Unit, WorkspaceError>> updateDoc({String? text}) {
final request = UpdateViewDataRequest.create()
..viewId = docId
..data = text ?? "";
return WorkspaceEventUpdateViewData(request).send();
}
Future<Either<DocData, DocError>> readDocData(String path) {
final request = QueryDocDataRequest.create()
..docId = docId
..path = path;
return EditorEventReadDocData(request).send();
}
Future<Either<Unit, DocError>> updateDoc(
{String? name, String? desc, String? text}) {
final request = UpdateDocRequest(id: docId, name: name, text: text);
return EditorEventUpdateDoc(request).send();
}
Future<Either<Unit, DocError>> closeDoc(
Future<Either<Unit, WorkspaceError>> closeDoc(
{String? name, String? desc, String? text}) {
throw UnimplementedError();
}

View File

@ -1,4 +1,5 @@
import 'package:app_flowy/workspace/presentation/app/app_page.dart';
import 'package:flowy_infra/flowy_logger.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@ -58,6 +59,7 @@ class ViewListPage extends StatelessWidget {
viewCtx: viewCtx,
isSelected: _isViewSelected(context, view.id),
onOpen: (view) {
Log.debug("Open view: $view");
context.read<ViewListNotifier>().setSelectedView(view);
final stackView = stackViewFromView(viewCtx.view);
getIt<HomePageStack>().setStackView(stackView);

View File

@ -11,20 +11,20 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class EditorPage extends StatelessWidget {
final FocusNode _focusNode = FocusNode();
late EditorController controller;
final Doc doc;
final FlowyDoc doc;
EditorPage({Key? key, required this.doc}) : super(key: key) {
controller = EditorController(
document: doc.data,
selection: const TextSelection.collapsed(offset: 0),
persistence: getIt<EditorPersistence>(param1: doc.info.id),
persistence: getIt<EditorPersistence>(param1: doc.doc.id),
);
}
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<DocBloc>(param1: doc.info.id),
create: (context) => getIt<DocBloc>(param1: doc.doc.id),
child: BlocBuilder<DocBloc, DocState>(
builder: (ctx, state) {
return Column(

View File

@ -36,6 +36,7 @@ class HomeScreen extends StatelessWidget {
loading: (_) {},
unauthorized: (unauthorized) {
// TODO: push to login screen when user token was invalid
Log.error("Push to login screen when user token was invalid");
},
);
},

View File

@ -8,7 +8,7 @@ class Log {
Log() {
_logger = Logger(
printer: PrettyPrinter(
methodCount: 0, // number of method calls to be displayed
methodCount: 2, // number of method calls to be displayed
errorMethodCount:
8, // number of method calls if stacktrace is provided
lineLength: 120, // width of the output

View File

@ -237,70 +237,36 @@ class WorkspaceEventDeleteView {
}
}
class EditorEventCreateDoc {
CreateDocRequest request;
EditorEventCreateDoc(this.request);
class WorkspaceEventOpenView {
OpenViewRequest request;
WorkspaceEventOpenView(this.request);
Future<Either<DocInfo, DocError>> send() {
Future<Either<Doc, WorkspaceError>> send() {
final request = FFIRequest.create()
..event = EditorEvent.CreateDoc.toString()
..event = WorkspaceEvent.OpenView.toString()
..payload = requestToBytes(this.request);
return Dispatch.asyncRequest(request)
.then((bytesResult) => bytesResult.fold(
(okBytes) => left(DocInfo.fromBuffer(okBytes)),
(errBytes) => right(DocError.fromBuffer(errBytes)),
(okBytes) => left(Doc.fromBuffer(okBytes)),
(errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
));
}
}
class EditorEventUpdateDoc {
UpdateDocRequest request;
EditorEventUpdateDoc(this.request);
class WorkspaceEventUpdateViewData {
UpdateViewDataRequest request;
WorkspaceEventUpdateViewData(this.request);
Future<Either<Unit, DocError>> send() {
Future<Either<Unit, WorkspaceError>> send() {
final request = FFIRequest.create()
..event = EditorEvent.UpdateDoc.toString()
..event = WorkspaceEvent.UpdateViewData.toString()
..payload = requestToBytes(this.request);
return Dispatch.asyncRequest(request)
.then((bytesResult) => bytesResult.fold(
(bytes) => left(unit),
(errBytes) => right(DocError.fromBuffer(errBytes)),
));
}
}
class EditorEventReadDocInfo {
QueryDocRequest request;
EditorEventReadDocInfo(this.request);
Future<Either<DocInfo, DocError>> send() {
final request = FFIRequest.create()
..event = EditorEvent.ReadDocInfo.toString()
..payload = requestToBytes(this.request);
return Dispatch.asyncRequest(request)
.then((bytesResult) => bytesResult.fold(
(okBytes) => left(DocInfo.fromBuffer(okBytes)),
(errBytes) => right(DocError.fromBuffer(errBytes)),
));
}
}
class EditorEventReadDocData {
QueryDocDataRequest request;
EditorEventReadDocData(this.request);
Future<Either<DocData, DocError>> send() {
final request = FFIRequest.create()
..event = EditorEvent.ReadDocData.toString()
..payload = requestToBytes(this.request);
return Dispatch.asyncRequest(request)
.then((bytesResult) => bytesResult.fold(
(okBytes) => left(DocData.fromBuffer(okBytes)),
(errBytes) => right(DocError.fromBuffer(errBytes)),
(errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
));
}
}

View File

@ -4,7 +4,7 @@ use actix_web::{
};
use sqlx::PgPool;
use flowy_document::protobuf::{CreateDocParams, QueryDocParams, UpdateDocParams};
use flowy_document::protobuf::{QueryDocParams, UpdateDocParams};
use flowy_net::errors::ServerError;
use crate::service::{

View File

@ -1,10 +1,10 @@
use flowy_net::errors::ServerError;
use flowy_workspace::protobuf::{App, View, ViewType, Workspace};
use flowy_workspace::protobuf::{App, CreateViewParams, View, ViewType, Workspace};
use crate::{
service::workspace_service::{
app::sql_builder::NewAppSqlBuilder as AppBuilder,
view::sql_builder::NewViewSqlBuilder as ViewBuilder,
view::{create_view_with_transaction, sql_builder::NewViewSqlBuilder as ViewBuilder},
workspace::sql_builder::NewWorkspaceBuilder as WorkspaceBuilder,
},
sqlx_ext::{map_sqlx_error, DBTransaction},
@ -57,16 +57,18 @@ async fn create_app(
}
async fn create_view(transaction: &mut DBTransaction<'_>, app: &App) -> Result<View, ServerError> {
let (sql, args, view) = ViewBuilder::new(&app.id)
.name("DefaultView")
.desc("View created by AppFlowy")
.thumbnail("https://view.png")
.view_type(ViewType::Doc)
.build()?;
let params = CreateViewParams {
belong_to_id: app.id.clone(),
name: "DefaultView".to_string(),
desc: "View created by AppFlowy".to_string(),
thumbnail: "123.png".to_string(),
view_type: ViewType::Doc,
data: "[{\"insert\":\"\\n\"}]".to_string(),
unknown_fields: Default::default(),
cached_size: Default::default(),
};
let view = create_view_with_transaction(transaction, params).await?;
let _ = sqlx::query_with(&sql, args)
.execute(transaction)
.await
.map_err(map_sqlx_error)?;
Ok(view)
}

View File

@ -28,15 +28,27 @@ pub(crate) async fn create_view(
pool: &PgPool,
params: CreateViewParams,
) -> Result<FlowyResponse, ServerError> {
let name = ViewName::parse(params.name).map_err(invalid_params)?;
let belong_to_id = AppId::parse(params.belong_to_id).map_err(invalid_params)?;
let thumbnail = ViewThumbnail::parse(params.thumbnail).map_err(invalid_params)?;
let desc = ViewDesc::parse(params.desc).map_err(invalid_params)?;
let mut transaction = pool
.begin()
.await
.context("Failed to acquire a Postgres connection to create view")?;
let view = create_view_with_transaction(&mut transaction, params).await?;
transaction
.commit()
.await
.context("Failed to commit SQL transaction to create view.")?;
FlowyResponse::success().pb(view)
}
pub(crate) async fn create_view_with_transaction(
transaction: &mut DBTransaction<'_>,
params: CreateViewParams,
) -> Result<View, ServerError> {
let name = ViewName::parse(params.name).map_err(invalid_params)?;
let belong_to_id = AppId::parse(params.belong_to_id).map_err(invalid_params)?;
let thumbnail = ViewThumbnail::parse(params.thumbnail).map_err(invalid_params)?;
let desc = ViewDesc::parse(params.desc).map_err(invalid_params)?;
let (sql, args, view) = NewViewSqlBuilder::new(belong_to_id.as_ref())
.name(name.as_ref())
@ -46,21 +58,15 @@ pub(crate) async fn create_view(
.build()?;
let _ = sqlx::query_with(&sql, args)
.execute(&mut transaction)
.execute(transaction as &mut DBTransaction<'_>)
.await
.map_err(map_sqlx_error)?;
let mut create_doc_params = CreateDocParams::new();
create_doc_params.set_data(params.data);
create_doc_params.set_id(view.id.clone());
let _ = create_doc(&mut transaction, create_doc_params).await?;
transaction
.commit()
.await
.context("Failed to commit SQL transaction to create view.")?;
FlowyResponse::success().pb(view)
let _ = create_doc(transaction, create_doc_params).await?;
Ok(view)
}
pub(crate) async fn read_view(

View File

@ -4,7 +4,7 @@ use backend::{
};
use flowy_document::{
entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
entities::doc::{Doc, QueryDocParams, UpdateDocParams},
prelude::*,
};
use flowy_user::{errors::UserError, prelude::*};

View File

@ -1,7 +1,7 @@
use crate::helper::*;
use flowy_workspace::entities::{
app::{DeleteAppParams, QueryAppParams, UpdateAppParams},
view::{CreateViewParams, DeleteViewParams, QueryViewParams, UpdateViewParams, View, ViewType},
view::{DeleteViewParams, QueryViewParams, UpdateViewParams},
workspace::{
CreateWorkspaceParams,
DeleteWorkspaceParams,

View File

@ -14,7 +14,7 @@ pub use diesel_derives::*;
#[macro_use]
extern crate diesel_migrations;
pub use flowy_sqlite::{DBConnection, Database};
pub use flowy_sqlite::{ConnectionPool, DBConnection, Database};
pub type Error = diesel::result::Error;
use diesel_migrations::*;

View File

@ -4,10 +4,8 @@ use std::sync::Once;
#[allow(dead_code)]
pub fn setup_env() {
static INIT: Once = Once::new();
INIT.call_once(|| {
std::env::set_var("RUST_LOG", "flowy_dispatch=debug,debug");
env_logger::init();
});
std::env::);
INIT.call_once(|| env_logger::init());
}
pub fn init_dispatch<F>(module_factory: F) -> EventDispatch

View File

@ -1,3 +1,3 @@
proto_crates = ["src/entities", "src/event.rs", "src/errors.rs", "src/observable"]
event_files = ["src/event.rs"]
event_files = []

View File

@ -1,7 +1,3 @@
use crate::{
entities::doc::parser::*,
errors::{ErrorBuilder, *},
};
use flowy_derive::ProtoBuf;
use std::convert::TryInto;

View File

@ -17,6 +17,8 @@ pub struct DocError {
impl DocError {
fn new(code: ErrorCode, msg: &str) -> Self { Self { code, msg: msg.to_owned() } }
pub fn is_record_not_found(&self) -> bool { self.code == ErrorCode::DocNotfound }
}
#[derive(Debug, Clone, ProtoBuf_Enum, Display, PartialEq, Eq)]
@ -39,13 +41,26 @@ impl std::default::Default for ErrorCode {
}
impl std::convert::From<flowy_database::Error> for DocError {
fn from(error: flowy_database::Error) -> Self { ErrorBuilder::new(ErrorCode::InternalError).error(error).build() }
fn from(error: flowy_database::Error) -> Self {
match error {
flowy_database::Error::NotFound => ErrorBuilder::new(ErrorCode::DocNotfound).error(error).build(),
_ => ErrorBuilder::new(ErrorCode::InternalError).error(error).build(),
}
}
}
// impl std::convert::From<::r2d2::Error> for DocError {
// fn from(error: r2d2::Error) -> Self {
// ErrorBuilder::new(ErrorCode::InternalError).error(error).build() } }
impl std::convert::From<FileError> for DocError {
fn from(error: FileError) -> Self { ErrorBuilder::new(ErrorCode::InternalError).error(error).build() }
}
// impl std::convert::From<flowy_sqlite::Error> for DocError {
// fn from(error: flowy_sqlite::Error) -> Self {
// ErrorBuilder::new(ErrorCode::InternalError).error(error).build() } }
impl std::convert::From<flowy_net::errors::ServerError> for DocError {
fn from(error: ServerError) -> Self {
let code = server_error_to_doc_error(error.code);
@ -54,6 +69,7 @@ impl std::convert::From<flowy_net::errors::ServerError> for DocError {
}
use flowy_net::errors::ErrorCode as ServerErrorCode;
fn server_error_to_doc_error(code: ServerErrorCode) -> ErrorCode {
match code {
ServerErrorCode::UserUnauthorized => ErrorCode::UserUnauthorized,

View File

@ -1,18 +0,0 @@
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
use strum_macros::Display;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
#[event_err = "DocError"]
pub enum EditorEvent {
#[event(input = "CreateDocRequest")]
CreateDoc = 0,
#[event(input = "UpdateDocRequest")]
UpdateDoc = 1,
#[event(input = "QueryDocRequest", output = "Doc")]
ReadDoc = 2,
#[event(input = "QueryDocRequest")]
DeleteDoc = 3,
}

View File

@ -1,6 +1,5 @@
pub mod entities;
pub mod errors;
pub mod event;
pub mod module;
mod observable;
pub mod protobuf;

View File

@ -1,10 +1,8 @@
use crate::{
errors::DocError,
event::EditorEvent,
services::{doc_controller::DocController, file_manager::FileManager, server::construct_doc_server},
};
use flowy_database::DBConnection;
use flowy_dispatch::prelude::*;
use std::sync::Arc;
use tokio::sync::RwLock;

View File

@ -1,12 +1,14 @@
use crate::{
entities::doc::{CreateDocParams, Doc, QueryDocParams, UpdateDocParams},
errors::DocError,
errors::{DocError, ErrorBuilder, ErrorCode},
module::DocumentUser,
services::server::Server,
sql_tables::doc::{DocTable, DocTableChangeset, DocTableSql},
};
use flowy_database::SqliteConnection;
use flowy_database::{ConnectionPool, SqliteConnection};
use std::sync::Arc;
use tokio::task::JoinHandle;
pub struct DocController {
server: Server,
@ -39,11 +41,12 @@ impl DocController {
Ok(())
}
#[tracing::instrument(level = "debug", skip(self, conn), err)]
pub fn open(&self, params: QueryDocParams, conn: &SqliteConnection) -> Result<Doc, DocError> {
let doc: Doc = self.sql.read_doc_table(&params.doc_id, conn)?.into();
let _ = self.read_doc_on_server(params)?;
Ok(doc)
#[tracing::instrument(level = "debug", skip(self, pool), err)]
pub async fn open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
match self._open(params.clone(), pool.clone()) {
Ok(doc_table) => Ok(doc_table.into()),
Err(error) => self.try_read_on_server(params, pool.clone(), error).await,
}
}
#[tracing::instrument(level = "debug", skip(self, conn), err)]
@ -71,19 +74,36 @@ impl DocController {
Ok(())
}
#[tracing::instrument(level = "debug", skip(self), err)]
fn read_doc_on_server(&self, params: QueryDocParams) -> Result<(), DocError> {
#[tracing::instrument(level = "debug", skip(self, pool), err)]
fn read_doc_from_server(
&self,
params: QueryDocParams,
pool: Arc<ConnectionPool>,
) -> Result<JoinHandle<Result<Doc, DocError>>, DocError> {
let token = self.user.token()?;
let server = self.server.clone();
tokio::spawn(async move {
// Opti: handle the error and retry?
let _doc = server.read_doc(&token, params).await?;
// save to disk
// notify
let sql = self.sql.clone();
Result::<(), DocError>::Ok(())
});
Ok(())
Ok(tokio::spawn(async move {
match server.read_doc(&token, params).await? {
None => Err(ErrorBuilder::new(ErrorCode::DocNotfound).build()),
Some(doc) => {
let doc_table = DocTable::new(doc.clone());
let _ = sql.create_doc_table(doc_table, &*(pool.get().unwrap()))?;
// TODO: notify
Ok(doc)
},
}
}))
}
#[tracing::instrument(level = "debug", skip(self), err)]
async fn sync_read_doc_from_server(&self, params: QueryDocParams) -> Result<Doc, DocError> {
let token = self.user.token()?;
match self.server.read_doc(&token, params).await? {
None => Err(ErrorBuilder::new(ErrorCode::DocNotfound).build()),
Some(doc) => Ok(doc),
}
}
#[tracing::instrument(level = "debug", skip(self), err)]
@ -101,4 +121,27 @@ impl DocController {
});
Ok(())
}
fn _open(&self, params: QueryDocParams, pool: Arc<ConnectionPool>) -> Result<Doc, DocError> {
let doc_table = self.sql.read_doc_table(&params.doc_id, &*(pool.get().unwrap()))?;
let doc: Doc = doc_table.into();
let _ = self.read_doc_from_server(params, pool.clone())?;
Ok(doc)
}
async fn try_read_on_server(&self, params: QueryDocParams, pool: Arc<ConnectionPool>, error: DocError) -> Result<Doc, DocError> {
if error.is_record_not_found() {
log::debug!("Doc:{} don't exist, reading from server", params.doc_id);
self.read_doc_from_server(params, pool)?.await.map_err(internal_error)?
} else {
Err(error)
}
}
}
fn internal_error<T>(e: T) -> DocError
where
T: std::fmt::Debug,
{
ErrorBuilder::new(ErrorCode::InternalError).error(e).build()
}

View File

@ -1,36 +0,0 @@
use crate::helper::*;
use flowy_test::FlowyEnv;
#[test]
fn doc_create_test() {
let sdk = FlowyEnv::setup().sdk;
let doc = create_doc(&sdk, "flutter ❤️ rust");
dbg!(&doc);
let doc = read_doc_data(&sdk, &doc.id);
assert_eq!(doc.data, "flutter ❤️ rust".to_owned());
}
#[test]
fn doc_update_test() {
let sdk = FlowyEnv::setup().sdk;
let doc_desc = create_doc(&sdk, "flutter ❤️ rust");
dbg!(&doc_desc);
let content = "😁😁😁😁😁😁😁😁😁😁".to_owned();
save_doc(&sdk, &doc_desc, &content);
let doc = read_doc_data(&sdk, &doc_desc.id);
assert_eq!(doc.data, content);
}
#[test]
fn doc_update_big_data_test() {
let sdk = FlowyEnv::setup().sdk;
let doc_desc = create_doc(&sdk, "");
let content = "flutter ❤️ rust".repeat(1000000);
save_doc(&sdk, &doc_desc, &content);
let doc = read_doc_data(&sdk, &doc_desc.id);
assert_eq!(doc.data, content);
}

View File

@ -1,53 +0,0 @@
use flowy_test::builder::DocTest;
use flowy_document::{entities::doc::*, event::EditorEvent::*};
use flowy_infra::uuid;
use flowy_test::prelude::*;
pub fn create_doc(sdk: &FlowyTestSDK, text: &str) -> Doc {
let request = CreateDocRequest {
id: uuid(),
data: text.to_owned(),
};
let doc = DocTest::new(sdk.clone())
.event(CreateDoc)
.request(request)
.sync_send()
.parse::<Doc>();
doc
}
pub fn save_doc(sdk: &FlowyTestSDK, doc: &Doc, content: &str) {
let request = UpdateDocRequest {
id: doc.id.clone(),
data: Some(content.to_owned()),
};
let _ = DocTest::new(sdk.clone()).event(UpdateDoc).request(request).sync_send();
}
// #[allow(dead_code)]
// pub fn read_doc(doc_id: &str) -> DocInfo {
// let request = QueryDocRequest {
// doc_id: doc_id.to_string(),
// };
//
// let doc = AnnieTestBuilder::new()
// .event(ReadDocInfo)
// .request(request)
// .sync_send()
// .parse::<DocInfo>();
//
// doc
// }
pub(crate) fn read_doc_data(sdk: &FlowyTestSDK, doc_id: &str) -> Doc {
let request = QueryDocRequest {
doc_id: doc_id.to_string(),
};
let doc = DocTest::new(sdk.clone()).event(ReadDoc).request(request).sync_send().parse::<Doc>();
doc
}

View File

@ -1,2 +1 @@
// mod doc_test;
// mod helper;

View File

@ -1,4 +1,3 @@
use flowy_database::DBConnection;
use flowy_document::{
errors::{DocError, ErrorBuilder, ErrorCode},
module::DocumentUser,

View File

@ -1,4 +1,4 @@
use flowy_database::DBConnection;
use flowy_database::ConnectionPool;
use flowy_user::services::user::UserSession;
use flowy_workspace::{
errors::{ErrorBuilder, ErrorCode, WorkspaceError},
@ -29,9 +29,9 @@ pub struct WorkspaceDatabaseImpl {
}
impl WorkspaceDatabase for WorkspaceDatabaseImpl {
fn db_connection(&self) -> Result<DBConnection, WorkspaceError> {
fn db_pool(&self) -> Result<Arc<ConnectionPool>, WorkspaceError> {
self.user_session
.db_conn()
.db_pool()
.map_err(|e| ErrorBuilder::new(ErrorCode::InternalError).error(e).build())
}
}

View File

@ -1,5 +1,4 @@
use flowy_dispatch::prelude::Module;
use flowy_user::prelude::*;
use crate::deps_resolve::{EditorUserImpl, WorkspaceDatabaseImpl, WorkspaceUserImpl};
use flowy_document::module::Document;

View File

@ -7,7 +7,7 @@ use std::{
use crate::FlowyTestSDK;
use flowy_dispatch::prelude::*;
use flowy_document::errors::DocError;
use flowy_sdk::*;
use flowy_user::errors::UserError;
use flowy_workspace::errors::WorkspaceError;

View File

@ -83,7 +83,12 @@ impl std::default::Default for ErrorCode {
}
impl std::convert::From<flowy_database::Error> for UserError {
fn from(error: flowy_database::Error) -> Self { ErrorBuilder::new(ErrorCode::InternalError).error(error).build() }
fn from(error: flowy_database::Error) -> Self {
match error {
flowy_database::Error::NotFound => ErrorBuilder::new(ErrorCode::UserNotExist).error(error).build(),
_ => ErrorBuilder::new(ErrorCode::InternalError).error(error).build(),
}
}
}
impl std::convert::From<::r2d2::Error> for UserError {

View File

@ -54,7 +54,7 @@ impl UserSession {
}
}
pub fn db_conn(&self) -> Result<DBConnection, UserError> {
pub fn db_connection(&self) -> Result<DBConnection, UserError> {
let user_id = self.get_session()?.user_id;
self.database.get_connection(&user_id)
}
@ -101,7 +101,7 @@ impl UserSession {
#[tracing::instrument(level = "debug", skip(self))]
pub async fn sign_out(&self) -> Result<(), UserError> {
let session = self.get_session()?;
let _ = diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*(self.db_conn()?))?;
let _ = diesel::delete(dsl::user_table.filter(dsl::id.eq(&session.user_id))).execute(&*(self.db_connection()?))?;
let _ = self.database.close_user_db(&session.user_id)?;
let _ = self.set_session(None)?;
let _ = self.sign_out_on_server(&session.token).await?;
@ -113,7 +113,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_connection()?);
let _ = self.update_user_on_server(&session.token, params).await?;
Ok(())
@ -123,7 +123,7 @@ impl UserSession {
let (user_id, token) = self.get_session()?.into_part();
let user = dsl::user_table
.filter(user_table::id.eq(&user_id))
.first::<UserTable>(&*(self.db_conn()?))?;
.first::<UserTable>(&*(self.db_connection()?))?;
let _ = self.read_user_profile_on_server(&token).await?;
Ok(UserProfile::from(user))
@ -187,7 +187,7 @@ impl UserSession {
}
async fn save_user(&self, user: UserTable) -> Result<UserTable, UserError> {
let conn = self.db_conn()?;
let conn = self.db_connection()?;
let _ = diesel::insert_into(user_table::table).values(user.clone()).execute(&*conn)?;
Ok(user)
}
@ -236,7 +236,7 @@ pub async fn update_user(_server: Server, pool: Arc<ConnectionPool>, params: Upd
}
impl UserDatabaseConnection for UserSession {
fn get_connection(&self) -> Result<DBConnection, String> { self.db_conn().map_err(|e| format!("{:?}", e)) }
fn get_connection(&self) -> Result<DBConnection, String> { self.db_connection().map_err(|e| format!("{:?}", e)) }
}
const SESSION_CACHE_KEY: &str = "session_cache_key";

View File

@ -3,10 +3,9 @@ pub struct ViewThumbnail(pub String);
impl ViewThumbnail {
pub fn parse(s: String) -> Result<ViewThumbnail, String> {
if s.trim().is_empty() {
return Err(format!("View thumbnail can not be empty or whitespace"));
}
// if s.trim().is_empty() {
// return Err(format!("View thumbnail can not be empty or whitespace"));
// }
// TODO: verify the thumbnail url is valid or not
Ok(Self(s))

View File

@ -95,7 +95,7 @@ impl TryInto<CreateViewParams> for CreateViewRequest {
desc: self.desc,
thumbnail,
view_type: self.view_type,
data: "".to_owned(),
data: "[{\"insert\":\"\\n\"}]".to_owned(),
})
}
}

View File

@ -1,17 +1,13 @@
use flowy_dispatch::prelude::*;
use crate::{
errors::WorkspaceError,
errors::{ErrorBuilder, ErrorCode, WorkspaceError},
event::WorkspaceEvent,
services::{AppController, WorkspaceController},
handlers::*,
services::{server::construct_workspace_server, AppController, ViewController, WorkspaceController},
};
use flowy_database::DBConnection;
use crate::{
handlers::*,
services::{server::construct_workspace_server, ViewController},
};
use flowy_dispatch::prelude::*;
use flowy_document::module::Document;
use flowy_sqlite::ConnectionPool;
use std::sync::Arc;
pub trait WorkspaceDeps: WorkspaceUser + WorkspaceDatabase {}
@ -22,19 +18,27 @@ pub trait WorkspaceUser: Send + Sync {
}
pub trait WorkspaceDatabase: Send + Sync {
fn db_connection(&self) -> Result<DBConnection, WorkspaceError>;
fn db_pool(&self) -> Result<Arc<ConnectionPool>, WorkspaceError>;
fn db_connection(&self) -> Result<DBConnection, WorkspaceError> {
let pool = self.db_pool()?;
let conn = pool
.get()
.map_err(|e| ErrorBuilder::new(ErrorCode::InternalError).error(e).build())?;
Ok(conn)
}
}
pub fn create(user: Arc<dyn WorkspaceUser>, database: Arc<dyn WorkspaceDatabase>, document: Arc<Document>) -> Module {
let server = construct_workspace_server();
let view_controller = Arc::new(ViewController::new(user.clone(), database.clone(), server.clone(), document));
let app_controller = Arc::new(AppController::new(user.clone(), database.clone(), server.clone()));
let workspace_controller = Arc::new(WorkspaceController::new(
user.clone(),
database.clone(),
app_controller.clone(),
view_controller.clone(),
server.clone(),
));

View File

@ -31,11 +31,10 @@ impl AppController {
#[tracing::instrument(level = "debug", skip(self), err)]
pub(crate) async fn create_app(&self, params: CreateAppParams) -> Result<App, WorkspaceError> {
let app = self.create_app_on_server(params).await?;
let app_table = AppTable::new(app.clone());
let conn = &*self.database.db_connection()?;
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
let _ = self.sql.create_app(app_table, &*conn)?;
let _ = self.save_app(app.clone(), &*conn)?;
let apps = self.read_local_apps(&app.workspace_id, &*conn)?;
notify(&app.workspace_id, WorkspaceObservable::WorkspaceCreateApp)
.payload(apps)
@ -46,6 +45,12 @@ impl AppController {
Ok(app)
}
pub(crate) fn save_app(&self, app: App, conn: &SqliteConnection) -> Result<(), WorkspaceError> {
let app_table = AppTable::new(app.clone());
let _ = self.sql.create_app(app_table, &*conn)?;
Ok(())
}
pub(crate) async fn read_app(&self, params: QueryAppParams) -> Result<App, WorkspaceError> {
let app_table = self
.sql

View File

@ -42,11 +42,9 @@ impl ViewController {
pub(crate) async fn create_view(&self, params: CreateViewParams) -> Result<View, WorkspaceError> {
let view = self.create_view_on_server(params.clone()).await?;
let conn = &*self.database.db_connection()?;
let view_table = ViewTable::new(view.clone());
// TODO: rollback anything created before if failed?
conn.immediate_transaction::<_, WorkspaceError, _>(|| {
let _ = self.sql.create_view(view_table, conn)?;
let _ = self.save_view(view.clone(), conn)?;
self.document.doc.create(CreateDocParams::new(&view.id, &params.data), conn)?;
let repeated_view = self.read_local_views_belong_to(&view.belong_to_id, conn)?;
@ -59,6 +57,12 @@ impl ViewController {
Ok(view)
}
pub(crate) fn save_view(&self, view: View, conn: &SqliteConnection) -> Result<(), WorkspaceError> {
let view_table = ViewTable::new(view);
let _ = self.sql.create_view(view_table, conn)?;
Ok(())
}
pub(crate) async fn read_view(&self, params: QueryViewParams) -> Result<View, WorkspaceError> {
let conn = self.database.db_connection()?;
let view_table = self.sql.read_view(&params.view_id, Some(params.is_trash), &*conn)?;
@ -68,8 +72,7 @@ impl ViewController {
}
pub(crate) async fn open_view(&self, params: QueryDocParams) -> Result<Doc, WorkspaceError> {
let conn = self.database.db_connection()?;
let doc = self.document.doc.open(params, &*conn)?;
let doc = self.document.doc.open(params, self.database.db_pool()?).await?;
Ok(doc)
}

View File

@ -6,12 +6,8 @@ use crate::{
errors::*,
module::{WorkspaceDatabase, WorkspaceUser},
observable::*,
services::{helper::spawn, server::Server, AppController},
sql_tables::{
app::{AppTable, AppTableSql},
view::{ViewTable, ViewTableSql},
workspace::{WorkspaceTable, WorkspaceTableChangeset, WorkspaceTableSql},
},
services::{helper::spawn, server::Server, AppController, ViewController},
sql_tables::workspace::{WorkspaceTable, WorkspaceTableChangeset, WorkspaceTableSql},
};
use flowy_database::SqliteConnection;
use flowy_infra::kv::KV;
@ -20,8 +16,7 @@ use std::sync::Arc;
pub(crate) struct WorkspaceController {
pub user: Arc<dyn WorkspaceUser>,
pub workspace_sql: Arc<WorkspaceTableSql>,
pub app_sql: Arc<AppTableSql>,
pub view_sql: Arc<ViewTableSql>,
pub view_controller: Arc<ViewController>,
pub database: Arc<dyn WorkspaceDatabase>,
pub app_controller: Arc<AppController>,
server: Server,
@ -32,18 +27,16 @@ impl WorkspaceController {
user: Arc<dyn WorkspaceUser>,
database: Arc<dyn WorkspaceDatabase>,
app_controller: Arc<AppController>,
view_controller: Arc<ViewController>,
server: Server,
) -> Self {
let workspace_sql = Arc::new(WorkspaceTableSql {});
let app_sql = Arc::new(AppTableSql {});
let view_sql = Arc::new(ViewTableSql {});
Self {
user,
workspace_sql,
app_sql,
view_sql,
database,
app_controller,
view_controller,
server,
}
}
@ -253,8 +246,8 @@ impl WorkspaceController {
fn read_workspaces_on_server(&self, user_id: String, params: QueryWorkspaceParams) -> Result<(), WorkspaceError> {
let (token, server) = self.token_with_server()?;
let workspace_sql = self.workspace_sql.clone();
let app_sql = self.app_sql.clone();
let view_sql = self.view_sql.clone();
let app_ctrl = self.app_controller.clone();
let view_ctrl = self.view_controller.clone();
let conn = self.database.db_connection()?;
spawn(async move {
// Opti: handle the error and retry?
@ -270,14 +263,14 @@ impl WorkspaceController {
log::debug!("Save {} apps", apps.len());
for mut app in apps {
let views = app.belongings.take_items();
match app_sql.create_app(AppTable::new(app), &*conn) {
match app_ctrl.save_app(app, &*conn) {
Ok(_) => {},
Err(e) => log::error!("create app failed: {:?}", e),
}
log::debug!("Save {} views", views.len());
for view in views {
match view_sql.create_view(ViewTable::new(view), &*conn) {
match view_ctrl.save_view(view, &*conn) {
Ok(_) => {},
Err(e) => log::error!("create view failed: {:?}", e),
}

View File

@ -1,5 +1,4 @@
use crate::helper::*;
use flowy_test::prelude::*;
use flowy_workspace::entities::{app::QueryAppRequest, view::*};

View File

@ -1,6 +1,5 @@
use crate::helper::*;
use flowy_test::{FlowyEnv, FlowyTestSDK};
use flowy_workspace::entities::view::*;
#[test]

View File

@ -1,7 +1,7 @@
use crate::helper::*;
use flowy_test::{builder::*, FlowyEnv};
use flowy_workspace::{
entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest, RepeatedWorkspace},
entities::workspace::{CreateWorkspaceRequest, QueryWorkspaceRequest},
event::WorkspaceEvent::*,
prelude::*,
};