[flutter]: config document delta data flow

This commit is contained in:
appflowy 2021-10-20 22:19:01 +08:00
parent ba01235509
commit 538ab20c5f
24 changed files with 459 additions and 236 deletions

View File

@ -1,30 +1,31 @@
import 'dart:convert';
import 'package:editor/flutter_quill.dart';
import 'package:flowy_log/flowy_log.dart';
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:dartz/dartz.dart';
import 'dart:async';
part 'doc_bloc.freezed.dart';
class DocBloc extends Bloc<DocEvent, DocState> {
final IDoc docManager;
late Document document;
late StreamSubscription _subscription;
DocBloc({required this.docManager}) : super(DocState.initial());
@override
Stream<DocState> mapEventToState(DocEvent event) async* {
yield* event.map(
initial: _initial,
);
yield* event.map(initial: _initial);
}
@override
Future<void> close() async {
await _subscription.cancel();
docManager.closeDoc();
await state.doc.fold(() => null, (doc) async {
await doc.close();
});
return super.close();
}
@ -32,17 +33,16 @@ class DocBloc extends Bloc<DocEvent, DocState> {
final result = await docManager.readDoc();
yield result.fold(
(doc) {
final flowyDoc = FlowyDoc(doc: doc, iDocImpl: docManager);
return state.copyWith(
doc: some(flowyDoc),
loadState: DocLoadState.finish(left(flowyDoc)),
);
document = _decodeJsonToDocument(doc.data);
_subscription = document.changes.listen((event) {
final delta = event.item2;
final documentDelta = document.toDelta();
_composeDelta(delta, documentDelta);
});
return state.copyWith(loadState: DocLoadState.finish(left(unit)));
},
(err) {
return state.copyWith(
doc: none(),
loadState: DocLoadState.finish(right(err)),
);
return state.copyWith(loadState: DocLoadState.finish(right(err)));
},
);
}
@ -53,11 +53,26 @@ class DocBloc extends Bloc<DocEvent, DocState> {
// return document;
// }
// Document _decodeJsonToDocument(String data) {
// final json = jsonDecode(data);
// final document = Document.fromJson(json);
// return document;
// }
void _composeDelta(Delta composedDelta, Delta documentDelta) async {
final json = jsonEncode(composedDelta.toJson());
Log.debug("Send json: $json");
final result = await docManager.composeDelta(json: json);
result.fold((rustDoc) {
// final json = utf8.decode(doc.data);
final rustDelta = Delta.fromJson(jsonDecode(rustDoc.data));
if (documentDelta != rustDelta) {
Log.error("Receive : $rustDelta");
Log.error("Expected : $documentDelta");
}
}, (r) => null);
}
Document _decodeJsonToDocument(String data) {
final json = jsonDecode(data);
final document = Document.fromJson(json);
return document;
}
}
@freezed
@ -67,13 +82,15 @@ class DocEvent with _$DocEvent {
@freezed
class DocState with _$DocState {
const factory DocState({required Option<FlowyDoc> doc, required DocLoadState loadState}) = _DocState;
const factory DocState({
required DocLoadState loadState,
}) = _DocState;
factory DocState.initial() => DocState(doc: none(), loadState: const _Loading());
factory DocState.initial() => const DocState(loadState: _Loading());
}
@freezed
class DocLoadState with _$DocLoadState {
const factory DocLoadState.loading() = _Loading;
const factory DocLoadState.finish(Either<FlowyDoc, WorkspaceError> successOrFail) = _Finish;
const factory DocLoadState.finish(Either<Unit, WorkspaceError> successOrFail) = _Finish;
}

View File

@ -148,10 +148,8 @@ abstract class Initial implements DocEvent {
class _$DocStateTearOff {
const _$DocStateTearOff();
_DocState call(
{required Option<FlowyDoc> doc, required DocLoadState loadState}) {
_DocState call({required DocLoadState loadState}) {
return _DocState(
doc: doc,
loadState: loadState,
);
}
@ -162,7 +160,6 @@ const $DocState = _$DocStateTearOff();
/// @nodoc
mixin _$DocState {
Option<FlowyDoc> get doc => throw _privateConstructorUsedError;
DocLoadState get loadState => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
@ -174,7 +171,7 @@ mixin _$DocState {
abstract class $DocStateCopyWith<$Res> {
factory $DocStateCopyWith(DocState value, $Res Function(DocState) then) =
_$DocStateCopyWithImpl<$Res>;
$Res call({Option<FlowyDoc> doc, DocLoadState loadState});
$Res call({DocLoadState loadState});
$DocLoadStateCopyWith<$Res> get loadState;
}
@ -189,14 +186,9 @@ class _$DocStateCopyWithImpl<$Res> implements $DocStateCopyWith<$Res> {
@override
$Res call({
Object? doc = freezed,
Object? loadState = freezed,
}) {
return _then(_value.copyWith(
doc: doc == freezed
? _value.doc
: doc // ignore: cast_nullable_to_non_nullable
as Option<FlowyDoc>,
loadState: loadState == freezed
? _value.loadState
: loadState // ignore: cast_nullable_to_non_nullable
@ -217,7 +209,7 @@ abstract class _$DocStateCopyWith<$Res> implements $DocStateCopyWith<$Res> {
factory _$DocStateCopyWith(_DocState value, $Res Function(_DocState) then) =
__$DocStateCopyWithImpl<$Res>;
@override
$Res call({Option<FlowyDoc> doc, DocLoadState loadState});
$Res call({DocLoadState loadState});
@override
$DocLoadStateCopyWith<$Res> get loadState;
@ -234,14 +226,9 @@ class __$DocStateCopyWithImpl<$Res> extends _$DocStateCopyWithImpl<$Res>
@override
$Res call({
Object? doc = freezed,
Object? loadState = freezed,
}) {
return _then(_DocState(
doc: doc == freezed
? _value.doc
: doc // ignore: cast_nullable_to_non_nullable
as Option<FlowyDoc>,
loadState: loadState == freezed
? _value.loadState
: loadState // ignore: cast_nullable_to_non_nullable
@ -253,24 +240,20 @@ class __$DocStateCopyWithImpl<$Res> extends _$DocStateCopyWithImpl<$Res>
/// @nodoc
class _$_DocState implements _DocState {
const _$_DocState({required this.doc, required this.loadState});
const _$_DocState({required this.loadState});
@override
final Option<FlowyDoc> doc;
@override
final DocLoadState loadState;
@override
String toString() {
return 'DocState(doc: $doc, loadState: $loadState)';
return 'DocState(loadState: $loadState)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is _DocState &&
(identical(other.doc, doc) ||
const DeepCollectionEquality().equals(other.doc, doc)) &&
(identical(other.loadState, loadState) ||
const DeepCollectionEquality()
.equals(other.loadState, loadState)));
@ -278,9 +261,7 @@ class _$_DocState implements _DocState {
@override
int get hashCode =>
runtimeType.hashCode ^
const DeepCollectionEquality().hash(doc) ^
const DeepCollectionEquality().hash(loadState);
runtimeType.hashCode ^ const DeepCollectionEquality().hash(loadState);
@JsonKey(ignore: true)
@override
@ -289,12 +270,8 @@ class _$_DocState implements _DocState {
}
abstract class _DocState implements DocState {
const factory _DocState(
{required Option<FlowyDoc> doc,
required DocLoadState loadState}) = _$_DocState;
const factory _DocState({required DocLoadState loadState}) = _$_DocState;
@override
Option<FlowyDoc> get doc => throw _privateConstructorUsedError;
@override
DocLoadState get loadState => throw _privateConstructorUsedError;
@override
@ -311,7 +288,7 @@ class _$DocLoadStateTearOff {
return const _Loading();
}
_Finish finish(Either<FlowyDoc, WorkspaceError> successOrFail) {
_Finish finish(Either<Unit, WorkspaceError> successOrFail) {
return _Finish(
successOrFail,
);
@ -326,14 +303,14 @@ mixin _$DocLoadState {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() loading,
required TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)
required TResult Function(Either<Unit, WorkspaceError> successOrFail)
finish,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading,
TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)? finish,
TResult Function(Either<Unit, WorkspaceError> successOrFail)? finish,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@ -406,7 +383,7 @@ class _$_Loading implements _Loading {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() loading,
required TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)
required TResult Function(Either<Unit, WorkspaceError> successOrFail)
finish,
}) {
return loading();
@ -416,7 +393,7 @@ class _$_Loading implements _Loading {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading,
TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)? finish,
TResult Function(Either<Unit, WorkspaceError> successOrFail)? finish,
required TResult orElse(),
}) {
if (loading != null) {
@ -456,7 +433,7 @@ abstract class _Loading implements DocLoadState {
abstract class _$FinishCopyWith<$Res> {
factory _$FinishCopyWith(_Finish value, $Res Function(_Finish) then) =
__$FinishCopyWithImpl<$Res>;
$Res call({Either<FlowyDoc, WorkspaceError> successOrFail});
$Res call({Either<Unit, WorkspaceError> successOrFail});
}
/// @nodoc
@ -476,7 +453,7 @@ class __$FinishCopyWithImpl<$Res> extends _$DocLoadStateCopyWithImpl<$Res>
successOrFail == freezed
? _value.successOrFail
: successOrFail // ignore: cast_nullable_to_non_nullable
as Either<FlowyDoc, WorkspaceError>,
as Either<Unit, WorkspaceError>,
));
}
}
@ -487,7 +464,7 @@ class _$_Finish implements _Finish {
const _$_Finish(this.successOrFail);
@override
final Either<FlowyDoc, WorkspaceError> successOrFail;
final Either<Unit, WorkspaceError> successOrFail;
@override
String toString() {
@ -516,7 +493,7 @@ class _$_Finish implements _Finish {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() loading,
required TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)
required TResult Function(Either<Unit, WorkspaceError> successOrFail)
finish,
}) {
return finish(successOrFail);
@ -526,7 +503,7 @@ class _$_Finish implements _Finish {
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading,
TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)? finish,
TResult Function(Either<Unit, WorkspaceError> successOrFail)? finish,
required TResult orElse(),
}) {
if (finish != null) {
@ -559,10 +536,9 @@ class _$_Finish implements _Finish {
}
abstract class _Finish implements DocLoadState {
const factory _Finish(Either<FlowyDoc, WorkspaceError> successOrFail) =
_$_Finish;
const factory _Finish(Either<Unit, WorkspaceError> successOrFail) = _$_Finish;
Either<FlowyDoc, WorkspaceError> get successOrFail =>
Either<Unit, WorkspaceError> get successOrFail =>
throw _privateConstructorUsedError;
@JsonKey(ignore: true)
_$FinishCopyWith<_Finish> get copyWith => throw _privateConstructorUsedError;

View File

@ -18,26 +18,39 @@ class ViewBloc extends Bloc<ViewEvent, ViewState> {
@override
Stream<ViewState> mapEventToState(ViewEvent event) async* {
yield* event.map(initial: (e) async* {
listener.start(updatedCallback: (result) => add(ViewEvent.viewDidUpdate(result)));
yield state;
}, setIsEditing: (e) async* {
yield state.copyWith(isEditing: e.isEditing);
}, viewDidUpdate: (e) async* {
yield* _handleViewDidUpdate(e.result);
}, rename: (e) async* {
final result = await viewManager.rename(e.newName);
yield result.fold(
(l) => state.copyWith(successOrFailure: left(unit)),
(error) => state.copyWith(successOrFailure: right(error)),
);
}, delete: (e) async* {
final result = await viewManager.delete();
yield result.fold(
(l) => state.copyWith(successOrFailure: left(unit)),
(error) => state.copyWith(successOrFailure: right(error)),
);
});
yield* event.map(
initial: (e) async* {
listener.start(updatedCallback: (result) => add(ViewEvent.viewDidUpdate(result)));
yield state;
},
setIsEditing: (e) async* {
yield state.copyWith(isEditing: e.isEditing);
},
viewDidUpdate: (e) async* {
yield* _handleViewDidUpdate(e.result);
},
rename: (e) async* {
final result = await viewManager.rename(e.newName);
yield result.fold(
(l) => state.copyWith(successOrFailure: left(unit)),
(error) => state.copyWith(successOrFailure: right(error)),
);
},
delete: (e) async* {
final result = await viewManager.delete();
yield result.fold(
(l) => state.copyWith(successOrFailure: left(unit)),
(error) => state.copyWith(successOrFailure: right(error)),
);
},
duplicate: (e) async* {
final result = await viewManager.duplicate();
yield result.fold(
(l) => state.copyWith(successOrFailure: left(unit)),
(error) => state.copyWith(successOrFailure: right(error)),
);
},
);
}
Stream<ViewState> _handleViewDidUpdate(Either<View, WorkspaceError> result) async* {
@ -60,6 +73,7 @@ class ViewEvent with _$ViewEvent {
const factory ViewEvent.setIsEditing(bool isEditing) = SetEditing;
const factory ViewEvent.rename(String newName) = Rename;
const factory ViewEvent.delete() = Delete;
const factory ViewEvent.duplicate() = Duplicate;
const factory ViewEvent.viewDidUpdate(Either<View, WorkspaceError> result) = ViewDidUpdate;
}

View File

@ -36,6 +36,10 @@ class _$ViewEventTearOff {
return const Delete();
}
Duplicate duplicate() {
return const Duplicate();
}
ViewDidUpdate viewDidUpdate(Either<View, WorkspaceError> result) {
return ViewDidUpdate(
result,
@ -54,6 +58,7 @@ mixin _$ViewEvent {
required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename,
required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate,
}) =>
@ -64,6 +69,7 @@ mixin _$ViewEvent {
TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename,
TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(),
}) =>
@ -74,6 +80,7 @@ mixin _$ViewEvent {
required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) =>
throw _privateConstructorUsedError;
@ -83,6 +90,7 @@ mixin _$ViewEvent {
TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(),
}) =>
@ -145,6 +153,7 @@ class _$Initial implements Initial {
required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename,
required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate,
}) {
@ -158,6 +167,7 @@ class _$Initial implements Initial {
TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename,
TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(),
}) {
@ -174,6 +184,7 @@ class _$Initial implements Initial {
required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) {
return initial(this);
@ -186,6 +197,7 @@ class _$Initial implements Initial {
TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(),
}) {
@ -268,6 +280,7 @@ class _$SetEditing implements SetEditing {
required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename,
required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate,
}) {
@ -281,6 +294,7 @@ class _$SetEditing implements SetEditing {
TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename,
TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(),
}) {
@ -297,6 +311,7 @@ class _$SetEditing implements SetEditing {
required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) {
return setIsEditing(this);
@ -309,6 +324,7 @@ class _$SetEditing implements SetEditing {
TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(),
}) {
@ -394,6 +410,7 @@ class _$Rename implements Rename {
required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename,
required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate,
}) {
@ -407,6 +424,7 @@ class _$Rename implements Rename {
TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename,
TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(),
}) {
@ -423,6 +441,7 @@ class _$Rename implements Rename {
required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) {
return rename(this);
@ -435,6 +454,7 @@ class _$Rename implements Rename {
TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(),
}) {
@ -494,6 +514,7 @@ class _$Delete implements Delete {
required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename,
required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate,
}) {
@ -507,6 +528,7 @@ class _$Delete implements Delete {
TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename,
TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(),
}) {
@ -523,6 +545,7 @@ class _$Delete implements Delete {
required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) {
return delete(this);
@ -535,6 +558,7 @@ class _$Delete implements Delete {
TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(),
}) {
@ -549,6 +573,106 @@ abstract class Delete implements ViewEvent {
const factory Delete() = _$Delete;
}
/// @nodoc
abstract class $DuplicateCopyWith<$Res> {
factory $DuplicateCopyWith(Duplicate value, $Res Function(Duplicate) then) =
_$DuplicateCopyWithImpl<$Res>;
}
/// @nodoc
class _$DuplicateCopyWithImpl<$Res> extends _$ViewEventCopyWithImpl<$Res>
implements $DuplicateCopyWith<$Res> {
_$DuplicateCopyWithImpl(Duplicate _value, $Res Function(Duplicate) _then)
: super(_value, (v) => _then(v as Duplicate));
@override
Duplicate get _value => super._value as Duplicate;
}
/// @nodoc
class _$Duplicate implements Duplicate {
const _$Duplicate();
@override
String toString() {
return 'ViewEvent.duplicate()';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) || (other is Duplicate);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename,
required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate,
}) {
return duplicate();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename,
TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(),
}) {
if (duplicate != null) {
return duplicate();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) {
return duplicate(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(),
}) {
if (duplicate != null) {
return duplicate(this);
}
return orElse();
}
}
abstract class Duplicate implements ViewEvent {
const factory Duplicate() = _$Duplicate;
}
/// @nodoc
abstract class $ViewDidUpdateCopyWith<$Res> {
factory $ViewDidUpdateCopyWith(
@ -617,6 +741,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename,
required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate,
}) {
@ -630,6 +755,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename,
TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(),
}) {
@ -646,6 +772,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) {
return viewDidUpdate(this);
@ -658,6 +785,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(),
}) {

View File

@ -1,57 +1,8 @@
import 'dart:convert';
import 'dart:async';
import 'package:dartz/dartz.dart';
// ignore: implementation_imports
import 'package:editor/flutter_quill.dart';
// import 'package:flowy_editor/flowy_editor.dart';
import 'package:flowy_log/flowy_log.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
class FlowyDoc {
final DocDelta doc;
final IDoc iDocImpl;
late Document document;
late StreamSubscription _subscription;
FlowyDoc({required this.doc, required this.iDocImpl}) {
document = _decodeJsonToDocument(doc.data);
_subscription = document.changes.listen((event) {
final delta = event.item2;
final documentDelta = document.toDelta();
_composeDelta(delta, documentDelta);
});
}
String get id => doc.docId;
Future<void> close() async {
await _subscription.cancel();
}
void _composeDelta(Delta composedDelta, Delta documentDelta) async {
final json = jsonEncode(composedDelta.toJson());
Log.debug("Send json: $json");
final result = await iDocImpl.composeDelta(json: json);
result.fold((rustDoc) {
// final json = utf8.decode(doc.data);
final rustDelta = Delta.fromJson(jsonDecode(rustDoc.data));
if (documentDelta != rustDelta) {
Log.error("Receive : $rustDelta");
Log.error("Expected : $documentDelta");
}
}, (r) => null);
}
Document _decodeJsonToDocument(String data) {
final json = jsonDecode(data);
final document = Document.fromJson(json);
return document;
}
}
abstract class IDoc {
Future<Either<DocDelta, WorkspaceError>> readDoc();
Future<Either<DocDelta, WorkspaceError>> composeDelta({required String json});

View File

@ -10,6 +10,8 @@ abstract class IView {
Future<Either<Unit, WorkspaceError>> delete();
Future<Either<View, WorkspaceError>> rename(String newName);
Future<Either<Unit, WorkspaceError>> duplicate();
}
abstract class IViewListener {

View File

@ -1,6 +1,10 @@
import 'package:flowy_infra/image.dart';
import 'package:flutter/material.dart';
enum ViewAction {
rename,
delete,
duplicate,
}
extension ViewActionExtension on ViewAction {
@ -10,8 +14,19 @@ extension ViewActionExtension on ViewAction {
return 'rename';
case ViewAction.delete:
return 'delete';
default:
return '';
case ViewAction.duplicate:
return 'duplicate';
}
}
Widget get icon {
switch (this) {
case ViewAction.rename:
return svg('editor/edit');
case ViewAction.delete:
return svg('editor/delete');
case ViewAction.duplicate:
return svg('editor/copy');
}
}
}

View File

@ -26,6 +26,11 @@ class IViewImpl extends IView {
Future<Either<View, WorkspaceError>> rename(String newName) {
return repo.updateView(name: newName);
}
@override
Future<Either<Unit, WorkspaceError>> duplicate() {
return repo.duplicate();
}
}
class IViewListenerImpl extends IViewListener {

View File

@ -43,6 +43,11 @@ class ViewRepository {
final request = QueryViewRequest.create()..viewIds.add(view.id);
return WorkspaceEventDeleteView(request).send();
}
Future<Either<Unit, WorkspaceError>> duplicate() {
final request = QueryViewRequest.create()..viewIds.add(view.id);
return WorkspaceEventDuplicateView(request).send();
}
}
class ViewListenerRepository {

View File

@ -1,13 +1,13 @@
import 'dart:io';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/doc/doc_bloc.dart';
import 'package:app_flowy/workspace/domain/i_doc.dart';
import 'package:editor/flutter_quill.dart';
import 'package:flowy_infra_ui/style_widget/progress_indicator.dart';
import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
class DocPage extends StatefulWidget {
final View view;
@ -38,7 +38,7 @@ class _DocPageState extends State<DocPage> {
return state.loadState.map(
loading: (_) => const FlowyProgressIndicator(),
finish: (result) => result.successOrFail.fold(
(doc) => _renderDoc(context, doc),
(_) => _renderDoc(context),
(err) => FlowyErrorPage(err.toString()),
),
);
@ -52,9 +52,9 @@ class _DocPageState extends State<DocPage> {
super.dispose();
}
Widget _renderDoc(BuildContext context, FlowyDoc doc) {
Widget _renderDoc(BuildContext context) {
QuillController controller = QuillController(
document: doc.document,
document: context.read<DocBloc>().document,
selection: const TextSelection.collapsed(offset: 0),
);
return Column(
@ -63,7 +63,7 @@ class _DocPageState extends State<DocPage> {
_renderEditor(controller),
_renderToolbar(controller),
],
);
).padding(horizontal: 80, vertical: 48);
}
Widget _renderEditor(QuillController controller) {

View File

@ -3,6 +3,7 @@ import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
@ -33,8 +34,8 @@ class ViewActionList implements FlowyOverlayDelegate {
itemBuilder: (context, index) => items[index],
anchorContext: anchorContext,
anchorDirection: AnchorDirection.bottomRight,
maxWidth: 120,
maxHeight: 80,
maxWidth: 162,
maxHeight: ViewAction.values.length * 32,
delegate: this,
);
}
@ -63,11 +64,17 @@ class ActionItem extends StatelessWidget {
builder: (context, onHover) {
return GestureDetector(
onTap: () => onSelected(action),
child: FlowyText.medium(
action.name,
fontSize: 12,
child: Row(
children: [
action.icon,
const HSpace(10),
FlowyText.medium(
action.name,
fontSize: 12,
),
],
).padding(
horizontal: 10,
horizontal: 6,
vertical: 6,
),
);

View File

@ -103,6 +103,9 @@ class ViewSectionItem extends StatelessWidget {
case ViewAction.delete:
context.read<ViewBloc>().add(const ViewEvent.delete());
break;
case ViewAction.duplicate:
context.read<ViewBloc>().add(const ViewEvent.duplicate());
break;
}
});
}

View File

@ -163,6 +163,7 @@ class FlowyOverlayState extends State<FlowyOverlay> {
FlowyOverlayDelegate? delegate,
OverlapBehaviour? overlapBehaviour,
FlowyOverlayStyle? style,
Offset? anchorPosition,
}) {
this.style = style ?? FlowyOverlayStyle();
@ -174,6 +175,7 @@ class FlowyOverlayState extends State<FlowyOverlay> {
anchorContext: anchorContext,
anchorDirection: anchorDirection,
overlapBehaviour: overlapBehaviour,
anchorPosition: anchorPosition,
);
}

View File

@ -108,7 +108,7 @@ class StyledDialogRoute<T> extends PopupRoute<T> {
StyledDialogRoute({
required RoutePageBuilder pageBuilder,
required this.barrier,
Duration transitionDuration = const Duration(milliseconds: 360),
Duration transitionDuration = const Duration(milliseconds: 300),
RouteTransitionsBuilder? transitionBuilder,
RouteSettings? settings,
}) : _pageBuilder = pageBuilder,

View File

@ -237,6 +237,37 @@ class WorkspaceEventDeleteView {
}
}
class WorkspaceEventDuplicateView {
QueryViewRequest request;
WorkspaceEventDuplicateView(this.request);
Future<Either<Unit, WorkspaceError>> send() {
final request = FFIRequest.create()
..event = WorkspaceEvent.DuplicateView.toString()
..payload = requestToBytes(this.request);
return Dispatch.asyncRequest(request)
.then((bytesResult) => bytesResult.fold(
(bytes) => left(unit),
(errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
));
}
}
class WorkspaceEventCopyLink {
WorkspaceEventCopyLink();
Future<Either<Unit, WorkspaceError>> send() {
final request = FFIRequest.create()
..event = WorkspaceEvent.CopyLink.toString();
return Dispatch.asyncRequest(request).then((bytesResult) => bytesResult.fold(
(bytes) => left(unit),
(errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
));
}
}
class WorkspaceEventOpenView {
QueryViewRequest request;
WorkspaceEventOpenView(this.request);
@ -271,23 +302,6 @@ class WorkspaceEventCloseView {
}
}
class WorkspaceEventApplyDocDelta {
DocDelta request;
WorkspaceEventApplyDocDelta(this.request);
Future<Either<DocDelta, WorkspaceError>> send() {
final request = FFIRequest.create()
..event = WorkspaceEvent.ApplyDocDelta.toString()
..payload = requestToBytes(this.request);
return Dispatch.asyncRequest(request)
.then((bytesResult) => bytesResult.fold(
(okBytes) => left(DocDelta.fromBuffer(okBytes)),
(errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
));
}
}
class WorkspaceEventReadTrash {
WorkspaceEventReadTrash();
@ -364,6 +378,23 @@ class WorkspaceEventDeleteAll {
}
}
class WorkspaceEventApplyDocDelta {
DocDelta request;
WorkspaceEventApplyDocDelta(this.request);
Future<Either<DocDelta, WorkspaceError>> send() {
final request = FFIRequest.create()
..event = WorkspaceEvent.ApplyDocDelta.toString()
..payload = requestToBytes(this.request);
return Dispatch.asyncRequest(request)
.then((bytesResult) => bytesResult.fold(
(okBytes) => left(DocDelta.fromBuffer(okBytes)),
(errBytes) => right(WorkspaceError.fromBuffer(errBytes)),
));
}
}
class WorkspaceEventInitWorkspace {
WorkspaceEventInitWorkspace();

View File

@ -24,14 +24,16 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
static const WorkspaceEvent ReadView = WorkspaceEvent._(202, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadView');
static const WorkspaceEvent UpdateView = WorkspaceEvent._(203, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'UpdateView');
static const WorkspaceEvent DeleteView = WorkspaceEvent._(204, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteView');
static const WorkspaceEvent OpenView = WorkspaceEvent._(205, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenView');
static const WorkspaceEvent CloseView = WorkspaceEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CloseView');
static const WorkspaceEvent ApplyDocDelta = WorkspaceEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDocDelta');
static const WorkspaceEvent DuplicateView = WorkspaceEvent._(205, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateView');
static const WorkspaceEvent CopyLink = WorkspaceEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CopyLink');
static const WorkspaceEvent OpenView = WorkspaceEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'OpenView');
static const WorkspaceEvent CloseView = WorkspaceEvent._(208, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CloseView');
static const WorkspaceEvent ReadTrash = WorkspaceEvent._(300, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ReadTrash');
static const WorkspaceEvent PutbackTrash = WorkspaceEvent._(301, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'PutbackTrash');
static const WorkspaceEvent DeleteTrash = WorkspaceEvent._(302, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteTrash');
static const WorkspaceEvent RestoreAll = WorkspaceEvent._(303, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'RestoreAll');
static const WorkspaceEvent DeleteAll = WorkspaceEvent._(304, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DeleteAll');
static const WorkspaceEvent ApplyDocDelta = WorkspaceEvent._(400, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDocDelta');
static const WorkspaceEvent InitWorkspace = WorkspaceEvent._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InitWorkspace');
static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
@ -49,14 +51,16 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
ReadView,
UpdateView,
DeleteView,
DuplicateView,
CopyLink,
OpenView,
CloseView,
ApplyDocDelta,
ReadTrash,
PutbackTrash,
DeleteTrash,
RestoreAll,
DeleteAll,
ApplyDocDelta,
InitWorkspace,
];

View File

@ -26,17 +26,19 @@ const WorkspaceEvent$json = const {
const {'1': 'ReadView', '2': 202},
const {'1': 'UpdateView', '2': 203},
const {'1': 'DeleteView', '2': 204},
const {'1': 'OpenView', '2': 205},
const {'1': 'CloseView', '2': 206},
const {'1': 'ApplyDocDelta', '2': 207},
const {'1': 'DuplicateView', '2': 205},
const {'1': 'CopyLink', '2': 206},
const {'1': 'OpenView', '2': 207},
const {'1': 'CloseView', '2': 208},
const {'1': 'ReadTrash', '2': 300},
const {'1': 'PutbackTrash', '2': 301},
const {'1': 'DeleteTrash', '2': 302},
const {'1': 'RestoreAll', '2': 303},
const {'1': 'DeleteAll', '2': 304},
const {'1': 'ApplyDocDelta', '2': 400},
const {'1': 'InitWorkspace', '2': 1000},
],
};
/// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`.
final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESDQoIT3BlblZpZXcQzQESDgoJQ2xvc2VWaWV3EM4BEhIKDUFwcGx5RG9jRGVsdGEQzwESDgoJUmVhZFRyYXNoEKwCEhEKDFB1dGJhY2tUcmFzaBCtAhIQCgtEZWxldGVUcmFzaBCuAhIPCgpSZXN0b3JlQWxsEK8CEg4KCURlbGV0ZUFsbBCwAhISCg1Jbml0V29ya3NwYWNlEOgH');
final $typed_data.Uint8List workspaceEventDescriptor = $convert.base64Decode('Cg5Xb3Jrc3BhY2VFdmVudBITCg9DcmVhdGVXb3Jrc3BhY2UQABIUChBSZWFkQ3VyV29ya3NwYWNlEAESEgoOUmVhZFdvcmtzcGFjZXMQAhITCg9EZWxldGVXb3Jrc3BhY2UQAxIRCg1PcGVuV29ya3NwYWNlEAQSFQoRUmVhZFdvcmtzcGFjZUFwcHMQBRINCglDcmVhdGVBcHAQZRINCglEZWxldGVBcHAQZhILCgdSZWFkQXBwEGcSDQoJVXBkYXRlQXBwEGgSDwoKQ3JlYXRlVmlldxDJARINCghSZWFkVmlldxDKARIPCgpVcGRhdGVWaWV3EMsBEg8KCkRlbGV0ZVZpZXcQzAESEgoNRHVwbGljYXRlVmlldxDNARINCghDb3B5TGluaxDOARINCghPcGVuVmlldxDPARIOCglDbG9zZVZpZXcQ0AESDgoJUmVhZFRyYXNoEKwCEhEKDFB1dGJhY2tUcmFzaBCtAhIQCgtEZWxldGVUcmFzaBCuAhIPCgpSZXN0b3JlQWxsEK8CEg4KCURlbGV0ZUFsbBCwAhISCg1BcHBseURvY0RlbHRhEJADEhIKDUluaXRXb3Jrc3BhY2UQ6Ac=');

View File

@ -52,6 +52,16 @@ impl FlowyDocument {
Ok(())
}
pub async fn read_document_data(
&self,
params: DocIdentifier,
pool: Arc<ConnectionPool>,
) -> Result<DocDelta, DocError> {
let edit_context = self.doc_ctrl.open(params, pool).await?;
let delta = edit_context.delta().await?;
Ok(delta)
}
pub async fn apply_doc_delta(&self, params: DocDelta) -> Result<DocDelta, DocError> {
// workaround: compare the rust's delta with flutter's delta. Will be removed
// very soon

View File

@ -46,14 +46,17 @@ pub enum WorkspaceEvent {
#[event(input = "QueryViewRequest")]
DeleteView = 204,
#[event(input = "QueryViewRequest")]
DuplicateView = 205,
#[event()]
CopyLink = 206,
#[event(input = "QueryViewRequest", output = "DocDelta")]
OpenView = 205,
OpenView = 207,
#[event(input = "QueryViewRequest")]
CloseView = 206,
#[event(input = "DocDelta", output = "DocDelta")]
ApplyDocDelta = 207,
CloseView = 208,
#[event(output = "RepeatedTrash")]
ReadTrash = 300,
@ -70,6 +73,9 @@ pub enum WorkspaceEvent {
#[event()]
DeleteAll = 304,
#[event(input = "DocDelta", output = "DocDelta")]
ApplyDocDelta = 400,
#[event()]
InitWorkspace = 1000,
}

View File

@ -102,3 +102,13 @@ pub(crate) async fn close_view_handler(
let _ = controller.close_view(params.into()).await?;
Ok(())
}
#[tracing::instrument(skip(data, controller), err)]
pub(crate) async fn duplicate_view_handler(
data: Data<QueryViewRequest>,
controller: Unit<Arc<ViewController>>,
) -> Result<(), WorkspaceError> {
let params: ViewIdentifier = data.into_inner().try_into()?;
let _ = controller.duplicate_view(params.into()).await?;
Ok(())
}

View File

@ -88,6 +88,7 @@ pub fn create(workspace: Arc<WorkspaceController>) -> Module {
.event(WorkspaceEvent::ReadView, read_view_handler)
.event(WorkspaceEvent::UpdateView, update_view_handler)
.event(WorkspaceEvent::DeleteView, delete_view_handler)
.event(WorkspaceEvent::DuplicateView, duplicate_view_handler)
.event(WorkspaceEvent::OpenView, open_view_handler)
.event(WorkspaceEvent::CloseView, close_view_handler)
.event(WorkspaceEvent::ApplyDocDelta, apply_doc_delta_handler);

View File

@ -39,14 +39,16 @@ pub enum WorkspaceEvent {
ReadView = 202,
UpdateView = 203,
DeleteView = 204,
OpenView = 205,
CloseView = 206,
ApplyDocDelta = 207,
DuplicateView = 205,
CopyLink = 206,
OpenView = 207,
CloseView = 208,
ReadTrash = 300,
PutbackTrash = 301,
DeleteTrash = 302,
RestoreAll = 303,
DeleteAll = 304,
ApplyDocDelta = 400,
InitWorkspace = 1000,
}
@ -71,14 +73,16 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
202 => ::std::option::Option::Some(WorkspaceEvent::ReadView),
203 => ::std::option::Option::Some(WorkspaceEvent::UpdateView),
204 => ::std::option::Option::Some(WorkspaceEvent::DeleteView),
205 => ::std::option::Option::Some(WorkspaceEvent::OpenView),
206 => ::std::option::Option::Some(WorkspaceEvent::CloseView),
207 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta),
205 => ::std::option::Option::Some(WorkspaceEvent::DuplicateView),
206 => ::std::option::Option::Some(WorkspaceEvent::CopyLink),
207 => ::std::option::Option::Some(WorkspaceEvent::OpenView),
208 => ::std::option::Option::Some(WorkspaceEvent::CloseView),
300 => ::std::option::Option::Some(WorkspaceEvent::ReadTrash),
301 => ::std::option::Option::Some(WorkspaceEvent::PutbackTrash),
302 => ::std::option::Option::Some(WorkspaceEvent::DeleteTrash),
303 => ::std::option::Option::Some(WorkspaceEvent::RestoreAll),
304 => ::std::option::Option::Some(WorkspaceEvent::DeleteAll),
400 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta),
1000 => ::std::option::Option::Some(WorkspaceEvent::InitWorkspace),
_ => ::std::option::Option::None
}
@ -100,14 +104,16 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
WorkspaceEvent::ReadView,
WorkspaceEvent::UpdateView,
WorkspaceEvent::DeleteView,
WorkspaceEvent::DuplicateView,
WorkspaceEvent::CopyLink,
WorkspaceEvent::OpenView,
WorkspaceEvent::CloseView,
WorkspaceEvent::ApplyDocDelta,
WorkspaceEvent::ReadTrash,
WorkspaceEvent::PutbackTrash,
WorkspaceEvent::DeleteTrash,
WorkspaceEvent::RestoreAll,
WorkspaceEvent::DeleteAll,
WorkspaceEvent::ApplyDocDelta,
WorkspaceEvent::InitWorkspace,
];
values
@ -137,38 +143,39 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
}
static file_descriptor_proto_data: &'static [u8] = b"\
\n\x0bevent.proto*\xa7\x03\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
\n\x0bevent.proto*\xca\x03\n\x0eWorkspaceEvent\x12\x13\n\x0fCreateWorksp\
ace\x10\0\x12\x14\n\x10ReadCurWorkspace\x10\x01\x12\x12\n\x0eReadWorkspa\
ces\x10\x02\x12\x13\n\x0fDeleteWorkspace\x10\x03\x12\x11\n\rOpenWorkspac\
e\x10\x04\x12\x15\n\x11ReadWorkspaceApps\x10\x05\x12\r\n\tCreateApp\x10e\
\x12\r\n\tDeleteApp\x10f\x12\x0b\n\x07ReadApp\x10g\x12\r\n\tUpdateApp\
\x10h\x12\x0f\n\nCreateView\x10\xc9\x01\x12\r\n\x08ReadView\x10\xca\x01\
\x12\x0f\n\nUpdateView\x10\xcb\x01\x12\x0f\n\nDeleteView\x10\xcc\x01\x12\
\r\n\x08OpenView\x10\xcd\x01\x12\x0e\n\tCloseView\x10\xce\x01\x12\x12\n\
\rApplyDocDelta\x10\xcf\x01\x12\x0e\n\tReadTrash\x10\xac\x02\x12\x11\n\
\x0cPutbackTrash\x10\xad\x02\x12\x10\n\x0bDeleteTrash\x10\xae\x02\x12\
\x0f\n\nRestoreAll\x10\xaf\x02\x12\x0e\n\tDeleteAll\x10\xb0\x02\x12\x12\
\n\rInitWorkspace\x10\xe8\x07J\xd9\x07\n\x06\x12\x04\0\0\x1a\x01\n\x08\n\
\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x1a\x01\n\n\n\x03\
\x05\0\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\
\x18\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\
\x02\0\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\
\x19\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\
\x02\x01\x02\x12\x03\x04\x17\x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\
\x04\x17\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x12\n\x0c\n\x05\
\x05\0\x02\x02\x02\x12\x03\x05\x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\
\x06\x04\x18\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\
\x05\x05\0\x02\x03\x02\x12\x03\x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\x12\
\x03\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\
\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\
\x12\x03\x08\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\
\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\x02\
\x06\x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\n\
\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\
\x12\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\r\n\x0c\n\
\x05\x05\0\x02\x07\x02\x12\x03\n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\
\x03\x0b\x04\x12\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\x0c\
\n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\x12\
\x12\n\rDuplicateView\x10\xcd\x01\x12\r\n\x08CopyLink\x10\xce\x01\x12\r\
\n\x08OpenView\x10\xcf\x01\x12\x0e\n\tCloseView\x10\xd0\x01\x12\x0e\n\tR\
eadTrash\x10\xac\x02\x12\x11\n\x0cPutbackTrash\x10\xad\x02\x12\x10\n\x0b\
DeleteTrash\x10\xae\x02\x12\x0f\n\nRestoreAll\x10\xaf\x02\x12\x0e\n\tDel\
eteAll\x10\xb0\x02\x12\x12\n\rApplyDocDelta\x10\x90\x03\x12\x12\n\rInitW\
orkspace\x10\xe8\x07J\xab\x08\n\x06\x12\x04\0\0\x1c\x01\n\x08\n\x01\x0c\
\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x1c\x01\n\n\n\x03\x05\0\
\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x18\n\
\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\x02\0\
\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x19\n\
\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\x02\
\x01\x02\x12\x03\x04\x17\x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\
\x17\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x12\n\x0c\n\x05\x05\0\
\x02\x02\x02\x12\x03\x05\x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\
\x04\x18\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\x05\
\x05\0\x02\x03\x02\x12\x03\x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\x12\x03\
\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\n\
\x05\x05\0\x02\x04\x02\x12\x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\x12\
\x03\x08\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\x0c\
\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\x02\x06\
\x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\n\x0c\n\
\x05\x05\0\x02\x06\x02\x12\x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\x12\
\x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\r\n\x0c\n\x05\
\x05\0\x02\x07\x02\x12\x03\n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\x03\
\x0b\x04\x12\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\x0c\n\
\x05\x05\0\x02\x08\x02\x12\x03\x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\x12\
\x03\x0c\x04\x14\n\x0c\n\x05\x05\0\x02\t\x01\x12\x03\x0c\x04\r\n\x0c\n\
\x05\x05\0\x02\t\x02\x12\x03\x0c\x10\x13\n\x0b\n\x04\x05\0\x02\n\x12\x03\
\r\x04\x15\n\x0c\n\x05\x05\0\x02\n\x01\x12\x03\r\x04\x0e\n\x0c\n\x05\x05\
@ -179,25 +186,29 @@ static file_descriptor_proto_data: &'static [u8] = b"\
\x05\0\x02\x0c\x02\x12\x03\x0f\x11\x14\n\x0b\n\x04\x05\0\x02\r\x12\x03\
\x10\x04\x15\n\x0c\n\x05\x05\0\x02\r\x01\x12\x03\x10\x04\x0e\n\x0c\n\x05\
\x05\0\x02\r\x02\x12\x03\x10\x11\x14\n\x0b\n\x04\x05\0\x02\x0e\x12\x03\
\x11\x04\x13\n\x0c\n\x05\x05\0\x02\x0e\x01\x12\x03\x11\x04\x0c\n\x0c\n\
\x05\x05\0\x02\x0e\x02\x12\x03\x11\x0f\x12\n\x0b\n\x04\x05\0\x02\x0f\x12\
\x03\x12\x04\x14\n\x0c\n\x05\x05\0\x02\x0f\x01\x12\x03\x12\x04\r\n\x0c\n\
\x05\x05\0\x02\x0f\x02\x12\x03\x12\x10\x13\n\x0b\n\x04\x05\0\x02\x10\x12\
\x03\x13\x04\x18\n\x0c\n\x05\x05\0\x02\x10\x01\x12\x03\x13\x04\x11\n\x0c\
\n\x05\x05\0\x02\x10\x02\x12\x03\x13\x14\x17\n\x0b\n\x04\x05\0\x02\x11\
\x12\x03\x14\x04\x14\n\x0c\n\x05\x05\0\x02\x11\x01\x12\x03\x14\x04\r\n\
\x0c\n\x05\x05\0\x02\x11\x02\x12\x03\x14\x10\x13\n\x0b\n\x04\x05\0\x02\
\x12\x12\x03\x15\x04\x17\n\x0c\n\x05\x05\0\x02\x12\x01\x12\x03\x15\x04\
\x10\n\x0c\n\x05\x05\0\x02\x12\x02\x12\x03\x15\x13\x16\n\x0b\n\x04\x05\0\
\x02\x13\x12\x03\x16\x04\x16\n\x0c\n\x05\x05\0\x02\x13\x01\x12\x03\x16\
\x04\x0f\n\x0c\n\x05\x05\0\x02\x13\x02\x12\x03\x16\x12\x15\n\x0b\n\x04\
\x05\0\x02\x14\x12\x03\x17\x04\x15\n\x0c\n\x05\x05\0\x02\x14\x01\x12\x03\
\x17\x04\x0e\n\x0c\n\x05\x05\0\x02\x14\x02\x12\x03\x17\x11\x14\n\x0b\n\
\x04\x05\0\x02\x15\x12\x03\x18\x04\x14\n\x0c\n\x05\x05\0\x02\x15\x01\x12\
\x03\x18\x04\r\n\x0c\n\x05\x05\0\x02\x15\x02\x12\x03\x18\x10\x13\n\x0b\n\
\x04\x05\0\x02\x16\x12\x03\x19\x04\x19\n\x0c\n\x05\x05\0\x02\x16\x01\x12\
\x03\x19\x04\x11\n\x0c\n\x05\x05\0\x02\x16\x02\x12\x03\x19\x14\x18b\x06p\
roto3\
\x11\x04\x18\n\x0c\n\x05\x05\0\x02\x0e\x01\x12\x03\x11\x04\x11\n\x0c\n\
\x05\x05\0\x02\x0e\x02\x12\x03\x11\x14\x17\n\x0b\n\x04\x05\0\x02\x0f\x12\
\x03\x12\x04\x13\n\x0c\n\x05\x05\0\x02\x0f\x01\x12\x03\x12\x04\x0c\n\x0c\
\n\x05\x05\0\x02\x0f\x02\x12\x03\x12\x0f\x12\n\x0b\n\x04\x05\0\x02\x10\
\x12\x03\x13\x04\x13\n\x0c\n\x05\x05\0\x02\x10\x01\x12\x03\x13\x04\x0c\n\
\x0c\n\x05\x05\0\x02\x10\x02\x12\x03\x13\x0f\x12\n\x0b\n\x04\x05\0\x02\
\x11\x12\x03\x14\x04\x14\n\x0c\n\x05\x05\0\x02\x11\x01\x12\x03\x14\x04\r\
\n\x0c\n\x05\x05\0\x02\x11\x02\x12\x03\x14\x10\x13\n\x0b\n\x04\x05\0\x02\
\x12\x12\x03\x15\x04\x14\n\x0c\n\x05\x05\0\x02\x12\x01\x12\x03\x15\x04\r\
\n\x0c\n\x05\x05\0\x02\x12\x02\x12\x03\x15\x10\x13\n\x0b\n\x04\x05\0\x02\
\x13\x12\x03\x16\x04\x17\n\x0c\n\x05\x05\0\x02\x13\x01\x12\x03\x16\x04\
\x10\n\x0c\n\x05\x05\0\x02\x13\x02\x12\x03\x16\x13\x16\n\x0b\n\x04\x05\0\
\x02\x14\x12\x03\x17\x04\x16\n\x0c\n\x05\x05\0\x02\x14\x01\x12\x03\x17\
\x04\x0f\n\x0c\n\x05\x05\0\x02\x14\x02\x12\x03\x17\x12\x15\n\x0b\n\x04\
\x05\0\x02\x15\x12\x03\x18\x04\x15\n\x0c\n\x05\x05\0\x02\x15\x01\x12\x03\
\x18\x04\x0e\n\x0c\n\x05\x05\0\x02\x15\x02\x12\x03\x18\x11\x14\n\x0b\n\
\x04\x05\0\x02\x16\x12\x03\x19\x04\x14\n\x0c\n\x05\x05\0\x02\x16\x01\x12\
\x03\x19\x04\r\n\x0c\n\x05\x05\0\x02\x16\x02\x12\x03\x19\x10\x13\n\x0b\n\
\x04\x05\0\x02\x17\x12\x03\x1a\x04\x18\n\x0c\n\x05\x05\0\x02\x17\x01\x12\
\x03\x1a\x04\x11\n\x0c\n\x05\x05\0\x02\x17\x02\x12\x03\x1a\x14\x17\n\x0b\
\n\x04\x05\0\x02\x18\x12\x03\x1b\x04\x19\n\x0c\n\x05\x05\0\x02\x18\x01\
\x12\x03\x1b\x04\x11\n\x0c\n\x05\x05\0\x02\x18\x02\x12\x03\x1b\x14\x18b\
\x06proto3\
";
static file_descriptor_proto_lazy: ::protobuf::rt::LazyV2<::protobuf::descriptor::FileDescriptorProto> = ::protobuf::rt::LazyV2::INIT;

View File

@ -15,13 +15,15 @@ enum WorkspaceEvent {
ReadView = 202;
UpdateView = 203;
DeleteView = 204;
OpenView = 205;
CloseView = 206;
ApplyDocDelta = 207;
DuplicateView = 205;
CopyLink = 206;
OpenView = 207;
CloseView = 208;
ReadTrash = 300;
PutbackTrash = 301;
DeleteTrash = 302;
RestoreAll = 303;
DeleteAll = 304;
ApplyDocDelta = 400;
InitWorkspace = 1000;
}

View File

@ -119,6 +119,27 @@ impl ViewController {
Ok(())
}
#[tracing::instrument(level = "debug", skip(self), err)]
pub(crate) async fn duplicate_view(&self, params: DocIdentifier) -> Result<(), WorkspaceError> {
let view: View = ViewTableSql::read_view(&params.doc_id, &*self.database.db_connection()?)?.into();
let delta_data = self
.document
.read_document_data(params, self.database.db_pool()?)
.await?;
let duplicate_params = CreateViewParams {
belong_to_id: view.belong_to_id.clone(),
name: format!("{}_copy", &view.name),
desc: view.desc.clone(),
thumbnail: "".to_owned(),
view_type: view.view_type.clone(),
data: delta_data.data,
};
let _ = self.create_view(duplicate_params).await?;
Ok(())
}
// belong_to_id will be the app_id or view_id.
#[tracing::instrument(level = "debug", skip(self), err)]
pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, WorkspaceError> {