mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
create view with doc, ignore thumbnail for now
This commit is contained in:
parent
59d447e27b
commit
9ade419b22
@ -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));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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");
|
||||
},
|
||||
);
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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)),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -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::{
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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::*};
|
||||
|
@ -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,
|
||||
|
@ -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::*;
|
||||
|
@ -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
|
||||
|
@ -1,3 +1,3 @@
|
||||
|
||||
proto_crates = ["src/entities", "src/event.rs", "src/errors.rs", "src/observable"]
|
||||
event_files = ["src/event.rs"]
|
||||
event_files = []
|
@ -1,7 +1,3 @@
|
||||
use crate::{
|
||||
entities::doc::parser::*,
|
||||
errors::{ErrorBuilder, *},
|
||||
};
|
||||
use flowy_derive::ProtoBuf;
|
||||
use std::convert::TryInto;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
pub mod entities;
|
||||
pub mod errors;
|
||||
pub mod event;
|
||||
pub mod module;
|
||||
mod observable;
|
||||
pub mod protobuf;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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(¶ms.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(¶ms.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()
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
}
|
@ -1,2 +1 @@
|
||||
// mod doc_test;
|
||||
// mod helper;
|
||||
|
||||
|
@ -1,4 +1,3 @@
|
||||
use flowy_database::DBConnection;
|
||||
use flowy_document::{
|
||||
errors::{DocError, ErrorBuilder, ErrorCode},
|
||||
module::DocumentUser,
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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";
|
||||
|
@ -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))
|
||||
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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(),
|
||||
));
|
||||
|
||||
|
@ -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
|
||||
|
@ -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, ¶ms.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(¶ms.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)
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use crate::helper::*;
|
||||
use flowy_test::prelude::*;
|
||||
|
||||
use flowy_workspace::entities::{app::QueryAppRequest, view::*};
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::helper::*;
|
||||
|
||||
use flowy_test::{FlowyEnv, FlowyTestSDK};
|
||||
use flowy_workspace::entities::view::*;
|
||||
|
||||
#[test]
|
||||
|
@ -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::*,
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user