[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:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/workspace/domain/i_doc.dart'; import 'package:app_flowy/workspace/domain/i_doc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'dart:async';
part 'doc_bloc.freezed.dart'; part 'doc_bloc.freezed.dart';
class DocBloc extends Bloc<DocEvent, DocState> { class DocBloc extends Bloc<DocEvent, DocState> {
final IDoc docManager; final IDoc docManager;
late Document document;
late StreamSubscription _subscription;
DocBloc({required this.docManager}) : super(DocState.initial()); DocBloc({required this.docManager}) : super(DocState.initial());
@override @override
Stream<DocState> mapEventToState(DocEvent event) async* { Stream<DocState> mapEventToState(DocEvent event) async* {
yield* event.map( yield* event.map(initial: _initial);
initial: _initial,
);
} }
@override @override
Future<void> close() async { Future<void> close() async {
await _subscription.cancel();
docManager.closeDoc(); docManager.closeDoc();
await state.doc.fold(() => null, (doc) async {
await doc.close();
});
return super.close(); return super.close();
} }
@ -32,17 +33,16 @@ class DocBloc extends Bloc<DocEvent, DocState> {
final result = await docManager.readDoc(); final result = await docManager.readDoc();
yield result.fold( yield result.fold(
(doc) { (doc) {
final flowyDoc = FlowyDoc(doc: doc, iDocImpl: docManager); document = _decodeJsonToDocument(doc.data);
return state.copyWith( _subscription = document.changes.listen((event) {
doc: some(flowyDoc), final delta = event.item2;
loadState: DocLoadState.finish(left(flowyDoc)), final documentDelta = document.toDelta();
); _composeDelta(delta, documentDelta);
});
return state.copyWith(loadState: DocLoadState.finish(left(unit)));
}, },
(err) { (err) {
return state.copyWith( return state.copyWith(loadState: DocLoadState.finish(right(err)));
doc: none(),
loadState: DocLoadState.finish(right(err)),
);
}, },
); );
} }
@ -53,11 +53,26 @@ class DocBloc extends Bloc<DocEvent, DocState> {
// return document; // return document;
// } // }
// Document _decodeJsonToDocument(String data) { void _composeDelta(Delta composedDelta, Delta documentDelta) async {
// final json = jsonDecode(data); final json = jsonEncode(composedDelta.toJson());
// final document = Document.fromJson(json); Log.debug("Send json: $json");
// return document; 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 @freezed
@ -67,13 +82,15 @@ class DocEvent with _$DocEvent {
@freezed @freezed
class DocState with _$DocState { 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 @freezed
class DocLoadState with _$DocLoadState { class DocLoadState with _$DocLoadState {
const factory DocLoadState.loading() = _Loading; 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 { class _$DocStateTearOff {
const _$DocStateTearOff(); const _$DocStateTearOff();
_DocState call( _DocState call({required DocLoadState loadState}) {
{required Option<FlowyDoc> doc, required DocLoadState loadState}) {
return _DocState( return _DocState(
doc: doc,
loadState: loadState, loadState: loadState,
); );
} }
@ -162,7 +160,6 @@ const $DocState = _$DocStateTearOff();
/// @nodoc /// @nodoc
mixin _$DocState { mixin _$DocState {
Option<FlowyDoc> get doc => throw _privateConstructorUsedError;
DocLoadState get loadState => throw _privateConstructorUsedError; DocLoadState get loadState => throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
@ -174,7 +171,7 @@ mixin _$DocState {
abstract class $DocStateCopyWith<$Res> { abstract class $DocStateCopyWith<$Res> {
factory $DocStateCopyWith(DocState value, $Res Function(DocState) then) = factory $DocStateCopyWith(DocState value, $Res Function(DocState) then) =
_$DocStateCopyWithImpl<$Res>; _$DocStateCopyWithImpl<$Res>;
$Res call({Option<FlowyDoc> doc, DocLoadState loadState}); $Res call({DocLoadState loadState});
$DocLoadStateCopyWith<$Res> get loadState; $DocLoadStateCopyWith<$Res> get loadState;
} }
@ -189,14 +186,9 @@ class _$DocStateCopyWithImpl<$Res> implements $DocStateCopyWith<$Res> {
@override @override
$Res call({ $Res call({
Object? doc = freezed,
Object? loadState = freezed, Object? loadState = freezed,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
doc: doc == freezed
? _value.doc
: doc // ignore: cast_nullable_to_non_nullable
as Option<FlowyDoc>,
loadState: loadState == freezed loadState: loadState == freezed
? _value.loadState ? _value.loadState
: loadState // ignore: cast_nullable_to_non_nullable : 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) = factory _$DocStateCopyWith(_DocState value, $Res Function(_DocState) then) =
__$DocStateCopyWithImpl<$Res>; __$DocStateCopyWithImpl<$Res>;
@override @override
$Res call({Option<FlowyDoc> doc, DocLoadState loadState}); $Res call({DocLoadState loadState});
@override @override
$DocLoadStateCopyWith<$Res> get loadState; $DocLoadStateCopyWith<$Res> get loadState;
@ -234,14 +226,9 @@ class __$DocStateCopyWithImpl<$Res> extends _$DocStateCopyWithImpl<$Res>
@override @override
$Res call({ $Res call({
Object? doc = freezed,
Object? loadState = freezed, Object? loadState = freezed,
}) { }) {
return _then(_DocState( return _then(_DocState(
doc: doc == freezed
? _value.doc
: doc // ignore: cast_nullable_to_non_nullable
as Option<FlowyDoc>,
loadState: loadState == freezed loadState: loadState == freezed
? _value.loadState ? _value.loadState
: loadState // ignore: cast_nullable_to_non_nullable : loadState // ignore: cast_nullable_to_non_nullable
@ -253,24 +240,20 @@ class __$DocStateCopyWithImpl<$Res> extends _$DocStateCopyWithImpl<$Res>
/// @nodoc /// @nodoc
class _$_DocState implements _DocState { class _$_DocState implements _DocState {
const _$_DocState({required this.doc, required this.loadState}); const _$_DocState({required this.loadState});
@override
final Option<FlowyDoc> doc;
@override @override
final DocLoadState loadState; final DocLoadState loadState;
@override @override
String toString() { String toString() {
return 'DocState(doc: $doc, loadState: $loadState)'; return 'DocState(loadState: $loadState)';
} }
@override @override
bool operator ==(dynamic other) { bool operator ==(dynamic other) {
return identical(this, other) || return identical(this, other) ||
(other is _DocState && (other is _DocState &&
(identical(other.doc, doc) ||
const DeepCollectionEquality().equals(other.doc, doc)) &&
(identical(other.loadState, loadState) || (identical(other.loadState, loadState) ||
const DeepCollectionEquality() const DeepCollectionEquality()
.equals(other.loadState, loadState))); .equals(other.loadState, loadState)));
@ -278,9 +261,7 @@ class _$_DocState implements _DocState {
@override @override
int get hashCode => int get hashCode =>
runtimeType.hashCode ^ runtimeType.hashCode ^ const DeepCollectionEquality().hash(loadState);
const DeepCollectionEquality().hash(doc) ^
const DeepCollectionEquality().hash(loadState);
@JsonKey(ignore: true) @JsonKey(ignore: true)
@override @override
@ -289,12 +270,8 @@ class _$_DocState implements _DocState {
} }
abstract class _DocState implements DocState { abstract class _DocState implements DocState {
const factory _DocState( const factory _DocState({required DocLoadState loadState}) = _$_DocState;
{required Option<FlowyDoc> doc,
required DocLoadState loadState}) = _$_DocState;
@override
Option<FlowyDoc> get doc => throw _privateConstructorUsedError;
@override @override
DocLoadState get loadState => throw _privateConstructorUsedError; DocLoadState get loadState => throw _privateConstructorUsedError;
@override @override
@ -311,7 +288,7 @@ class _$DocLoadStateTearOff {
return const _Loading(); return const _Loading();
} }
_Finish finish(Either<FlowyDoc, WorkspaceError> successOrFail) { _Finish finish(Either<Unit, WorkspaceError> successOrFail) {
return _Finish( return _Finish(
successOrFail, successOrFail,
); );
@ -326,14 +303,14 @@ mixin _$DocLoadState {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() loading, required TResult Function() loading,
required TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail) required TResult Function(Either<Unit, WorkspaceError> successOrFail)
finish, finish,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading, TResult Function()? loading,
TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)? finish, TResult Function(Either<Unit, WorkspaceError> successOrFail)? finish,
required TResult orElse(), required TResult orElse(),
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -406,7 +383,7 @@ class _$_Loading implements _Loading {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() loading, required TResult Function() loading,
required TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail) required TResult Function(Either<Unit, WorkspaceError> successOrFail)
finish, finish,
}) { }) {
return loading(); return loading();
@ -416,7 +393,7 @@ class _$_Loading implements _Loading {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading, TResult Function()? loading,
TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)? finish, TResult Function(Either<Unit, WorkspaceError> successOrFail)? finish,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (loading != null) { if (loading != null) {
@ -456,7 +433,7 @@ abstract class _Loading implements DocLoadState {
abstract class _$FinishCopyWith<$Res> { abstract class _$FinishCopyWith<$Res> {
factory _$FinishCopyWith(_Finish value, $Res Function(_Finish) then) = factory _$FinishCopyWith(_Finish value, $Res Function(_Finish) then) =
__$FinishCopyWithImpl<$Res>; __$FinishCopyWithImpl<$Res>;
$Res call({Either<FlowyDoc, WorkspaceError> successOrFail}); $Res call({Either<Unit, WorkspaceError> successOrFail});
} }
/// @nodoc /// @nodoc
@ -476,7 +453,7 @@ class __$FinishCopyWithImpl<$Res> extends _$DocLoadStateCopyWithImpl<$Res>
successOrFail == freezed successOrFail == freezed
? _value.successOrFail ? _value.successOrFail
: successOrFail // ignore: cast_nullable_to_non_nullable : 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); const _$_Finish(this.successOrFail);
@override @override
final Either<FlowyDoc, WorkspaceError> successOrFail; final Either<Unit, WorkspaceError> successOrFail;
@override @override
String toString() { String toString() {
@ -516,7 +493,7 @@ class _$_Finish implements _Finish {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() loading, required TResult Function() loading,
required TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail) required TResult Function(Either<Unit, WorkspaceError> successOrFail)
finish, finish,
}) { }) {
return finish(successOrFail); return finish(successOrFail);
@ -526,7 +503,7 @@ class _$_Finish implements _Finish {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? loading, TResult Function()? loading,
TResult Function(Either<FlowyDoc, WorkspaceError> successOrFail)? finish, TResult Function(Either<Unit, WorkspaceError> successOrFail)? finish,
required TResult orElse(), required TResult orElse(),
}) { }) {
if (finish != null) { if (finish != null) {
@ -559,10 +536,9 @@ class _$_Finish implements _Finish {
} }
abstract class _Finish implements DocLoadState { abstract class _Finish implements DocLoadState {
const factory _Finish(Either<FlowyDoc, WorkspaceError> successOrFail) = const factory _Finish(Either<Unit, WorkspaceError> successOrFail) = _$_Finish;
_$_Finish;
Either<FlowyDoc, WorkspaceError> get successOrFail => Either<Unit, WorkspaceError> get successOrFail =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@JsonKey(ignore: true) @JsonKey(ignore: true)
_$FinishCopyWith<_Finish> get copyWith => throw _privateConstructorUsedError; _$FinishCopyWith<_Finish> get copyWith => throw _privateConstructorUsedError;

View File

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

View File

@ -36,6 +36,10 @@ class _$ViewEventTearOff {
return const Delete(); return const Delete();
} }
Duplicate duplicate() {
return const Duplicate();
}
ViewDidUpdate viewDidUpdate(Either<View, WorkspaceError> result) { ViewDidUpdate viewDidUpdate(Either<View, WorkspaceError> result) {
return ViewDidUpdate( return ViewDidUpdate(
result, result,
@ -54,6 +58,7 @@ mixin _$ViewEvent {
required TResult Function(bool isEditing) setIsEditing, required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename, required TResult Function(String newName) rename,
required TResult Function() delete, required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result) required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate, viewDidUpdate,
}) => }) =>
@ -64,6 +69,7 @@ mixin _$ViewEvent {
TResult Function(bool isEditing)? setIsEditing, TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename, TResult Function(String newName)? rename,
TResult Function()? delete, TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate, TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) => }) =>
@ -74,6 +80,7 @@ mixin _$ViewEvent {
required TResult Function(SetEditing value) setIsEditing, required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename, required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete, required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate, required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@ -83,6 +90,7 @@ mixin _$ViewEvent {
TResult Function(SetEditing value)? setIsEditing, TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename, TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete, TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate, TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) => }) =>
@ -145,6 +153,7 @@ class _$Initial implements Initial {
required TResult Function(bool isEditing) setIsEditing, required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename, required TResult Function(String newName) rename,
required TResult Function() delete, required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result) required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate, viewDidUpdate,
}) { }) {
@ -158,6 +167,7 @@ class _$Initial implements Initial {
TResult Function(bool isEditing)? setIsEditing, TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename, TResult Function(String newName)? rename,
TResult Function()? delete, TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate, TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -174,6 +184,7 @@ class _$Initial implements Initial {
required TResult Function(SetEditing value) setIsEditing, required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename, required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete, required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate, required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) { }) {
return initial(this); return initial(this);
@ -186,6 +197,7 @@ class _$Initial implements Initial {
TResult Function(SetEditing value)? setIsEditing, TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename, TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete, TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate, TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -268,6 +280,7 @@ class _$SetEditing implements SetEditing {
required TResult Function(bool isEditing) setIsEditing, required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename, required TResult Function(String newName) rename,
required TResult Function() delete, required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result) required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate, viewDidUpdate,
}) { }) {
@ -281,6 +294,7 @@ class _$SetEditing implements SetEditing {
TResult Function(bool isEditing)? setIsEditing, TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename, TResult Function(String newName)? rename,
TResult Function()? delete, TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate, TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -297,6 +311,7 @@ class _$SetEditing implements SetEditing {
required TResult Function(SetEditing value) setIsEditing, required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename, required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete, required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate, required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) { }) {
return setIsEditing(this); return setIsEditing(this);
@ -309,6 +324,7 @@ class _$SetEditing implements SetEditing {
TResult Function(SetEditing value)? setIsEditing, TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename, TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete, TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate, TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -394,6 +410,7 @@ class _$Rename implements Rename {
required TResult Function(bool isEditing) setIsEditing, required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename, required TResult Function(String newName) rename,
required TResult Function() delete, required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result) required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate, viewDidUpdate,
}) { }) {
@ -407,6 +424,7 @@ class _$Rename implements Rename {
TResult Function(bool isEditing)? setIsEditing, TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename, TResult Function(String newName)? rename,
TResult Function()? delete, TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate, TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -423,6 +441,7 @@ class _$Rename implements Rename {
required TResult Function(SetEditing value) setIsEditing, required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename, required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete, required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate, required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) { }) {
return rename(this); return rename(this);
@ -435,6 +454,7 @@ class _$Rename implements Rename {
TResult Function(SetEditing value)? setIsEditing, TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename, TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete, TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate, TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -494,6 +514,7 @@ class _$Delete implements Delete {
required TResult Function(bool isEditing) setIsEditing, required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename, required TResult Function(String newName) rename,
required TResult Function() delete, required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result) required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate, viewDidUpdate,
}) { }) {
@ -507,6 +528,7 @@ class _$Delete implements Delete {
TResult Function(bool isEditing)? setIsEditing, TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename, TResult Function(String newName)? rename,
TResult Function()? delete, TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate, TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -523,6 +545,7 @@ class _$Delete implements Delete {
required TResult Function(SetEditing value) setIsEditing, required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename, required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete, required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate, required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) { }) {
return delete(this); return delete(this);
@ -535,6 +558,7 @@ class _$Delete implements Delete {
TResult Function(SetEditing value)? setIsEditing, TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename, TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete, TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate, TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -549,6 +573,106 @@ abstract class Delete implements ViewEvent {
const factory Delete() = _$Delete; 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 /// @nodoc
abstract class $ViewDidUpdateCopyWith<$Res> { abstract class $ViewDidUpdateCopyWith<$Res> {
factory $ViewDidUpdateCopyWith( factory $ViewDidUpdateCopyWith(
@ -617,6 +741,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
required TResult Function(bool isEditing) setIsEditing, required TResult Function(bool isEditing) setIsEditing,
required TResult Function(String newName) rename, required TResult Function(String newName) rename,
required TResult Function() delete, required TResult Function() delete,
required TResult Function() duplicate,
required TResult Function(Either<View, WorkspaceError> result) required TResult Function(Either<View, WorkspaceError> result)
viewDidUpdate, viewDidUpdate,
}) { }) {
@ -630,6 +755,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
TResult Function(bool isEditing)? setIsEditing, TResult Function(bool isEditing)? setIsEditing,
TResult Function(String newName)? rename, TResult Function(String newName)? rename,
TResult Function()? delete, TResult Function()? delete,
TResult Function()? duplicate,
TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate, TResult Function(Either<View, WorkspaceError> result)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -646,6 +772,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
required TResult Function(SetEditing value) setIsEditing, required TResult Function(SetEditing value) setIsEditing,
required TResult Function(Rename value) rename, required TResult Function(Rename value) rename,
required TResult Function(Delete value) delete, required TResult Function(Delete value) delete,
required TResult Function(Duplicate value) duplicate,
required TResult Function(ViewDidUpdate value) viewDidUpdate, required TResult Function(ViewDidUpdate value) viewDidUpdate,
}) { }) {
return viewDidUpdate(this); return viewDidUpdate(this);
@ -658,6 +785,7 @@ class _$ViewDidUpdate implements ViewDidUpdate {
TResult Function(SetEditing value)? setIsEditing, TResult Function(SetEditing value)? setIsEditing,
TResult Function(Rename value)? rename, TResult Function(Rename value)? rename,
TResult Function(Delete value)? delete, TResult Function(Delete value)? delete,
TResult Function(Duplicate value)? duplicate,
TResult Function(ViewDidUpdate value)? viewDidUpdate, TResult Function(ViewDidUpdate value)? viewDidUpdate,
required TResult orElse(), required TResult orElse(),
}) { }) {

View File

@ -1,57 +1,8 @@
import 'dart:convert';
import 'dart:async'; import 'dart:async';
import 'package:dartz/dartz.dart'; 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-document/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.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 { abstract class IDoc {
Future<Either<DocDelta, WorkspaceError>> readDoc(); Future<Either<DocDelta, WorkspaceError>> readDoc();
Future<Either<DocDelta, WorkspaceError>> composeDelta({required String json}); Future<Either<DocDelta, WorkspaceError>> composeDelta({required String json});

View File

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

View File

@ -1,6 +1,10 @@
import 'package:flowy_infra/image.dart';
import 'package:flutter/material.dart';
enum ViewAction { enum ViewAction {
rename, rename,
delete, delete,
duplicate,
} }
extension ViewActionExtension on ViewAction { extension ViewActionExtension on ViewAction {
@ -10,8 +14,19 @@ extension ViewActionExtension on ViewAction {
return 'rename'; return 'rename';
case ViewAction.delete: case ViewAction.delete:
return 'delete'; return 'delete';
default: case ViewAction.duplicate:
return ''; 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) { Future<Either<View, WorkspaceError>> rename(String newName) {
return repo.updateView(name: newName); return repo.updateView(name: newName);
} }
@override
Future<Either<Unit, WorkspaceError>> duplicate() {
return repo.duplicate();
}
} }
class IViewListenerImpl extends IViewListener { class IViewListenerImpl extends IViewListener {

View File

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

View File

@ -1,13 +1,13 @@
import 'dart:io'; import 'dart:io';
import 'package:app_flowy/startup/startup.dart'; import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/workspace/application/doc/doc_bloc.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:editor/flutter_quill.dart';
import 'package:flowy_infra_ui/style_widget/progress_indicator.dart'; import 'package:flowy_infra_ui/style_widget/progress_indicator.dart';
import 'package:flowy_infra_ui/widget/error_page.dart'; import 'package:flowy_infra_ui/widget/error_page.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart'; import 'package:flowy_sdk/protobuf/flowy-workspace/view_create.pb.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:styled_widget/styled_widget.dart';
class DocPage extends StatefulWidget { class DocPage extends StatefulWidget {
final View view; final View view;
@ -38,7 +38,7 @@ class _DocPageState extends State<DocPage> {
return state.loadState.map( return state.loadState.map(
loading: (_) => const FlowyProgressIndicator(), loading: (_) => const FlowyProgressIndicator(),
finish: (result) => result.successOrFail.fold( finish: (result) => result.successOrFail.fold(
(doc) => _renderDoc(context, doc), (_) => _renderDoc(context),
(err) => FlowyErrorPage(err.toString()), (err) => FlowyErrorPage(err.toString()),
), ),
); );
@ -52,9 +52,9 @@ class _DocPageState extends State<DocPage> {
super.dispose(); super.dispose();
} }
Widget _renderDoc(BuildContext context, FlowyDoc doc) { Widget _renderDoc(BuildContext context) {
QuillController controller = QuillController( QuillController controller = QuillController(
document: doc.document, document: context.read<DocBloc>().document,
selection: const TextSelection.collapsed(offset: 0), selection: const TextSelection.collapsed(offset: 0),
); );
return Column( return Column(
@ -63,7 +63,7 @@ class _DocPageState extends State<DocPage> {
_renderEditor(controller), _renderEditor(controller),
_renderToolbar(controller), _renderToolbar(controller),
], ],
); ).padding(horizontal: 80, vertical: 48);
} }
Widget _renderEditor(QuillController controller) { 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/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/hover.dart'; import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flowy_infra_ui/style_widget/text.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/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -33,8 +34,8 @@ class ViewActionList implements FlowyOverlayDelegate {
itemBuilder: (context, index) => items[index], itemBuilder: (context, index) => items[index],
anchorContext: anchorContext, anchorContext: anchorContext,
anchorDirection: AnchorDirection.bottomRight, anchorDirection: AnchorDirection.bottomRight,
maxWidth: 120, maxWidth: 162,
maxHeight: 80, maxHeight: ViewAction.values.length * 32,
delegate: this, delegate: this,
); );
} }
@ -63,11 +64,17 @@ class ActionItem extends StatelessWidget {
builder: (context, onHover) { builder: (context, onHover) {
return GestureDetector( return GestureDetector(
onTap: () => onSelected(action), onTap: () => onSelected(action),
child: FlowyText.medium( child: Row(
action.name, children: [
fontSize: 12, action.icon,
const HSpace(10),
FlowyText.medium(
action.name,
fontSize: 12,
),
],
).padding( ).padding(
horizontal: 10, horizontal: 6,
vertical: 6, vertical: 6,
), ),
); );

View File

@ -103,6 +103,9 @@ class ViewSectionItem extends StatelessWidget {
case ViewAction.delete: case ViewAction.delete:
context.read<ViewBloc>().add(const ViewEvent.delete()); context.read<ViewBloc>().add(const ViewEvent.delete());
break; 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, FlowyOverlayDelegate? delegate,
OverlapBehaviour? overlapBehaviour, OverlapBehaviour? overlapBehaviour,
FlowyOverlayStyle? style, FlowyOverlayStyle? style,
Offset? anchorPosition,
}) { }) {
this.style = style ?? FlowyOverlayStyle(); this.style = style ?? FlowyOverlayStyle();
@ -174,6 +175,7 @@ class FlowyOverlayState extends State<FlowyOverlay> {
anchorContext: anchorContext, anchorContext: anchorContext,
anchorDirection: anchorDirection, anchorDirection: anchorDirection,
overlapBehaviour: overlapBehaviour, overlapBehaviour: overlapBehaviour,
anchorPosition: anchorPosition,
); );
} }

View File

@ -108,7 +108,7 @@ class StyledDialogRoute<T> extends PopupRoute<T> {
StyledDialogRoute({ StyledDialogRoute({
required RoutePageBuilder pageBuilder, required RoutePageBuilder pageBuilder,
required this.barrier, required this.barrier,
Duration transitionDuration = const Duration(milliseconds: 360), Duration transitionDuration = const Duration(milliseconds: 300),
RouteTransitionsBuilder? transitionBuilder, RouteTransitionsBuilder? transitionBuilder,
RouteSettings? settings, RouteSettings? settings,
}) : _pageBuilder = pageBuilder, }) : _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 { class WorkspaceEventOpenView {
QueryViewRequest request; QueryViewRequest request;
WorkspaceEventOpenView(this.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 { class WorkspaceEventReadTrash {
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 { class WorkspaceEventInitWorkspace {
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 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 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 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 DuplicateView = WorkspaceEvent._(205, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'DuplicateView');
static const WorkspaceEvent CloseView = WorkspaceEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CloseView'); static const WorkspaceEvent CopyLink = WorkspaceEvent._(206, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'CopyLink');
static const WorkspaceEvent ApplyDocDelta = WorkspaceEvent._(207, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'ApplyDocDelta'); 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 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 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 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 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 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 WorkspaceEvent InitWorkspace = WorkspaceEvent._(1000, const $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'InitWorkspace');
static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [ static const $core.List<WorkspaceEvent> values = <WorkspaceEvent> [
@ -49,14 +51,16 @@ class WorkspaceEvent extends $pb.ProtobufEnum {
ReadView, ReadView,
UpdateView, UpdateView,
DeleteView, DeleteView,
DuplicateView,
CopyLink,
OpenView, OpenView,
CloseView, CloseView,
ApplyDocDelta,
ReadTrash, ReadTrash,
PutbackTrash, PutbackTrash,
DeleteTrash, DeleteTrash,
RestoreAll, RestoreAll,
DeleteAll, DeleteAll,
ApplyDocDelta,
InitWorkspace, InitWorkspace,
]; ];

View File

@ -26,17 +26,19 @@ const WorkspaceEvent$json = const {
const {'1': 'ReadView', '2': 202}, const {'1': 'ReadView', '2': 202},
const {'1': 'UpdateView', '2': 203}, const {'1': 'UpdateView', '2': 203},
const {'1': 'DeleteView', '2': 204}, const {'1': 'DeleteView', '2': 204},
const {'1': 'OpenView', '2': 205}, const {'1': 'DuplicateView', '2': 205},
const {'1': 'CloseView', '2': 206}, const {'1': 'CopyLink', '2': 206},
const {'1': 'ApplyDocDelta', '2': 207}, const {'1': 'OpenView', '2': 207},
const {'1': 'CloseView', '2': 208},
const {'1': 'ReadTrash', '2': 300}, const {'1': 'ReadTrash', '2': 300},
const {'1': 'PutbackTrash', '2': 301}, const {'1': 'PutbackTrash', '2': 301},
const {'1': 'DeleteTrash', '2': 302}, const {'1': 'DeleteTrash', '2': 302},
const {'1': 'RestoreAll', '2': 303}, const {'1': 'RestoreAll', '2': 303},
const {'1': 'DeleteAll', '2': 304}, const {'1': 'DeleteAll', '2': 304},
const {'1': 'ApplyDocDelta', '2': 400},
const {'1': 'InitWorkspace', '2': 1000}, const {'1': 'InitWorkspace', '2': 1000},
], ],
}; };
/// Descriptor for `WorkspaceEvent`. Decode as a `google.protobuf.EnumDescriptorProto`. /// 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(()) 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> { 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 // workaround: compare the rust's delta with flutter's delta. Will be removed
// very soon // very soon

View File

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

View File

@ -102,3 +102,13 @@ pub(crate) async fn close_view_handler(
let _ = controller.close_view(params.into()).await?; let _ = controller.close_view(params.into()).await?;
Ok(()) 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::ReadView, read_view_handler)
.event(WorkspaceEvent::UpdateView, update_view_handler) .event(WorkspaceEvent::UpdateView, update_view_handler)
.event(WorkspaceEvent::DeleteView, delete_view_handler) .event(WorkspaceEvent::DeleteView, delete_view_handler)
.event(WorkspaceEvent::DuplicateView, duplicate_view_handler)
.event(WorkspaceEvent::OpenView, open_view_handler) .event(WorkspaceEvent::OpenView, open_view_handler)
.event(WorkspaceEvent::CloseView, close_view_handler) .event(WorkspaceEvent::CloseView, close_view_handler)
.event(WorkspaceEvent::ApplyDocDelta, apply_doc_delta_handler); .event(WorkspaceEvent::ApplyDocDelta, apply_doc_delta_handler);

View File

@ -39,14 +39,16 @@ pub enum WorkspaceEvent {
ReadView = 202, ReadView = 202,
UpdateView = 203, UpdateView = 203,
DeleteView = 204, DeleteView = 204,
OpenView = 205, DuplicateView = 205,
CloseView = 206, CopyLink = 206,
ApplyDocDelta = 207, OpenView = 207,
CloseView = 208,
ReadTrash = 300, ReadTrash = 300,
PutbackTrash = 301, PutbackTrash = 301,
DeleteTrash = 302, DeleteTrash = 302,
RestoreAll = 303, RestoreAll = 303,
DeleteAll = 304, DeleteAll = 304,
ApplyDocDelta = 400,
InitWorkspace = 1000, InitWorkspace = 1000,
} }
@ -71,14 +73,16 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
202 => ::std::option::Option::Some(WorkspaceEvent::ReadView), 202 => ::std::option::Option::Some(WorkspaceEvent::ReadView),
203 => ::std::option::Option::Some(WorkspaceEvent::UpdateView), 203 => ::std::option::Option::Some(WorkspaceEvent::UpdateView),
204 => ::std::option::Option::Some(WorkspaceEvent::DeleteView), 204 => ::std::option::Option::Some(WorkspaceEvent::DeleteView),
205 => ::std::option::Option::Some(WorkspaceEvent::OpenView), 205 => ::std::option::Option::Some(WorkspaceEvent::DuplicateView),
206 => ::std::option::Option::Some(WorkspaceEvent::CloseView), 206 => ::std::option::Option::Some(WorkspaceEvent::CopyLink),
207 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta), 207 => ::std::option::Option::Some(WorkspaceEvent::OpenView),
208 => ::std::option::Option::Some(WorkspaceEvent::CloseView),
300 => ::std::option::Option::Some(WorkspaceEvent::ReadTrash), 300 => ::std::option::Option::Some(WorkspaceEvent::ReadTrash),
301 => ::std::option::Option::Some(WorkspaceEvent::PutbackTrash), 301 => ::std::option::Option::Some(WorkspaceEvent::PutbackTrash),
302 => ::std::option::Option::Some(WorkspaceEvent::DeleteTrash), 302 => ::std::option::Option::Some(WorkspaceEvent::DeleteTrash),
303 => ::std::option::Option::Some(WorkspaceEvent::RestoreAll), 303 => ::std::option::Option::Some(WorkspaceEvent::RestoreAll),
304 => ::std::option::Option::Some(WorkspaceEvent::DeleteAll), 304 => ::std::option::Option::Some(WorkspaceEvent::DeleteAll),
400 => ::std::option::Option::Some(WorkspaceEvent::ApplyDocDelta),
1000 => ::std::option::Option::Some(WorkspaceEvent::InitWorkspace), 1000 => ::std::option::Option::Some(WorkspaceEvent::InitWorkspace),
_ => ::std::option::Option::None _ => ::std::option::Option::None
} }
@ -100,14 +104,16 @@ impl ::protobuf::ProtobufEnum for WorkspaceEvent {
WorkspaceEvent::ReadView, WorkspaceEvent::ReadView,
WorkspaceEvent::UpdateView, WorkspaceEvent::UpdateView,
WorkspaceEvent::DeleteView, WorkspaceEvent::DeleteView,
WorkspaceEvent::DuplicateView,
WorkspaceEvent::CopyLink,
WorkspaceEvent::OpenView, WorkspaceEvent::OpenView,
WorkspaceEvent::CloseView, WorkspaceEvent::CloseView,
WorkspaceEvent::ApplyDocDelta,
WorkspaceEvent::ReadTrash, WorkspaceEvent::ReadTrash,
WorkspaceEvent::PutbackTrash, WorkspaceEvent::PutbackTrash,
WorkspaceEvent::DeleteTrash, WorkspaceEvent::DeleteTrash,
WorkspaceEvent::RestoreAll, WorkspaceEvent::RestoreAll,
WorkspaceEvent::DeleteAll, WorkspaceEvent::DeleteAll,
WorkspaceEvent::ApplyDocDelta,
WorkspaceEvent::InitWorkspace, WorkspaceEvent::InitWorkspace,
]; ];
values values
@ -137,38 +143,39 @@ impl ::protobuf::reflect::ProtobufValue for WorkspaceEvent {
} }
static file_descriptor_proto_data: &'static [u8] = b"\ 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\ 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\ 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\ 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\ \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\ \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\ \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\ \x12\n\rDuplicateView\x10\xcd\x01\x12\r\n\x08CopyLink\x10\xce\x01\x12\r\
\rApplyDocDelta\x10\xcf\x01\x12\x0e\n\tReadTrash\x10\xac\x02\x12\x11\n\ \n\x08OpenView\x10\xcf\x01\x12\x0e\n\tCloseView\x10\xd0\x01\x12\x0e\n\tR\
\x0cPutbackTrash\x10\xad\x02\x12\x10\n\x0bDeleteTrash\x10\xae\x02\x12\ eadTrash\x10\xac\x02\x12\x11\n\x0cPutbackTrash\x10\xad\x02\x12\x10\n\x0b\
\x0f\n\nRestoreAll\x10\xaf\x02\x12\x0e\n\tDeleteAll\x10\xb0\x02\x12\x12\ DeleteTrash\x10\xae\x02\x12\x0f\n\nRestoreAll\x10\xaf\x02\x12\x0e\n\tDel\
\n\rInitWorkspace\x10\xe8\x07J\xd9\x07\n\x06\x12\x04\0\0\x1a\x01\n\x08\n\ eteAll\x10\xb0\x02\x12\x12\n\rApplyDocDelta\x10\x90\x03\x12\x12\n\rInitW\
\x01\x0c\x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x1a\x01\n\n\n\x03\ orkspace\x10\xe8\x07J\xab\x08\n\x06\x12\x04\0\0\x1c\x01\n\x08\n\x01\x0c\
\x05\0\x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\ \x12\x03\0\0\x12\n\n\n\x02\x05\0\x12\x04\x02\0\x1c\x01\n\n\n\x03\x05\0\
\x18\n\x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\ \x01\x12\x03\x02\x05\x13\n\x0b\n\x04\x05\0\x02\0\x12\x03\x03\x04\x18\n\
\x02\0\x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\ \x0c\n\x05\x05\0\x02\0\x01\x12\x03\x03\x04\x13\n\x0c\n\x05\x05\0\x02\0\
\x19\n\x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\ \x02\x12\x03\x03\x16\x17\n\x0b\n\x04\x05\0\x02\x01\x12\x03\x04\x04\x19\n\
\x02\x01\x02\x12\x03\x04\x17\x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\ \x0c\n\x05\x05\0\x02\x01\x01\x12\x03\x04\x04\x14\n\x0c\n\x05\x05\0\x02\
\x04\x17\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x12\n\x0c\n\x05\ \x01\x02\x12\x03\x04\x17\x18\n\x0b\n\x04\x05\0\x02\x02\x12\x03\x05\x04\
\x05\0\x02\x02\x02\x12\x03\x05\x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\ \x17\n\x0c\n\x05\x05\0\x02\x02\x01\x12\x03\x05\x04\x12\n\x0c\n\x05\x05\0\
\x06\x04\x18\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\ \x02\x02\x02\x12\x03\x05\x15\x16\n\x0b\n\x04\x05\0\x02\x03\x12\x03\x06\
\x05\x05\0\x02\x03\x02\x12\x03\x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\x12\ \x04\x18\n\x0c\n\x05\x05\0\x02\x03\x01\x12\x03\x06\x04\x13\n\x0c\n\x05\
\x03\x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\ \x05\0\x02\x03\x02\x12\x03\x06\x16\x17\n\x0b\n\x04\x05\0\x02\x04\x12\x03\
\n\x05\x05\0\x02\x04\x02\x12\x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\ \x07\x04\x16\n\x0c\n\x05\x05\0\x02\x04\x01\x12\x03\x07\x04\x11\n\x0c\n\
\x12\x03\x08\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\ \x05\x05\0\x02\x04\x02\x12\x03\x07\x14\x15\n\x0b\n\x04\x05\0\x02\x05\x12\
\x0c\n\x05\x05\0\x02\x05\x02\x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\x02\ \x03\x08\x04\x1a\n\x0c\n\x05\x05\0\x02\x05\x01\x12\x03\x08\x04\x15\n\x0c\
\x06\x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\n\ \n\x05\x05\0\x02\x05\x02\x12\x03\x08\x18\x19\n\x0b\n\x04\x05\0\x02\x06\
\x0c\n\x05\x05\0\x02\x06\x02\x12\x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\ \x12\x03\t\x04\x14\n\x0c\n\x05\x05\0\x02\x06\x01\x12\x03\t\x04\r\n\x0c\n\
\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\x06\x02\x12\x03\t\x10\x13\n\x0b\n\x04\x05\0\x02\x07\x12\
\x05\x05\0\x02\x07\x02\x12\x03\n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\ \x03\n\x04\x14\n\x0c\n\x05\x05\0\x02\x07\x01\x12\x03\n\x04\r\n\x0c\n\x05\
\x03\x0b\x04\x12\n\x0c\n\x05\x05\0\x02\x08\x01\x12\x03\x0b\x04\x0b\n\x0c\ \x05\0\x02\x07\x02\x12\x03\n\x10\x13\n\x0b\n\x04\x05\0\x02\x08\x12\x03\
\n\x05\x05\0\x02\x08\x02\x12\x03\x0b\x0e\x11\n\x0b\n\x04\x05\0\x02\t\x12\ \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\ \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\ \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\ \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\ \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\ \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\ \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\ \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\x0f\x12\n\x0b\n\x04\x05\0\x02\x0f\x12\ \x05\x05\0\x02\x0e\x02\x12\x03\x11\x14\x17\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\ \x03\x12\x04\x13\n\x0c\n\x05\x05\0\x02\x0f\x01\x12\x03\x12\x04\x0c\n\x0c\
\x05\x05\0\x02\x0f\x02\x12\x03\x12\x10\x13\n\x0b\n\x04\x05\0\x02\x10\x12\ \n\x05\x05\0\x02\x0f\x02\x12\x03\x12\x0f\x12\n\x0b\n\x04\x05\0\x02\x10\
\x03\x13\x04\x18\n\x0c\n\x05\x05\0\x02\x10\x01\x12\x03\x13\x04\x11\n\x0c\ \x12\x03\x13\x04\x13\n\x0c\n\x05\x05\0\x02\x10\x01\x12\x03\x13\x04\x0c\n\
\n\x05\x05\0\x02\x10\x02\x12\x03\x13\x14\x17\n\x0b\n\x04\x05\0\x02\x11\ \x0c\n\x05\x05\0\x02\x10\x02\x12\x03\x13\x0f\x12\n\x0b\n\x04\x05\0\x02\
\x12\x03\x14\x04\x14\n\x0c\n\x05\x05\0\x02\x11\x01\x12\x03\x14\x04\r\n\ \x11\x12\x03\x14\x04\x14\n\x0c\n\x05\x05\0\x02\x11\x01\x12\x03\x14\x04\r\
\x0c\n\x05\x05\0\x02\x11\x02\x12\x03\x14\x10\x13\n\x0b\n\x04\x05\0\x02\ \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\ \x12\x12\x03\x15\x04\x14\n\x0c\n\x05\x05\0\x02\x12\x01\x12\x03\x15\x04\r\
\x10\n\x0c\n\x05\x05\0\x02\x12\x02\x12\x03\x15\x13\x16\n\x0b\n\x04\x05\0\ \n\x0c\n\x05\x05\0\x02\x12\x02\x12\x03\x15\x10\x13\n\x0b\n\x04\x05\0\x02\
\x02\x13\x12\x03\x16\x04\x16\n\x0c\n\x05\x05\0\x02\x13\x01\x12\x03\x16\ \x13\x12\x03\x16\x04\x17\n\x0c\n\x05\x05\0\x02\x13\x01\x12\x03\x16\x04\
\x04\x0f\n\x0c\n\x05\x05\0\x02\x13\x02\x12\x03\x16\x12\x15\n\x0b\n\x04\ \x10\n\x0c\n\x05\x05\0\x02\x13\x02\x12\x03\x16\x13\x16\n\x0b\n\x04\x05\0\
\x05\0\x02\x14\x12\x03\x17\x04\x15\n\x0c\n\x05\x05\0\x02\x14\x01\x12\x03\ \x02\x14\x12\x03\x17\x04\x16\n\x0c\n\x05\x05\0\x02\x14\x01\x12\x03\x17\
\x17\x04\x0e\n\x0c\n\x05\x05\0\x02\x14\x02\x12\x03\x17\x11\x14\n\x0b\n\ \x04\x0f\n\x0c\n\x05\x05\0\x02\x14\x02\x12\x03\x17\x12\x15\n\x0b\n\x04\
\x04\x05\0\x02\x15\x12\x03\x18\x04\x14\n\x0c\n\x05\x05\0\x02\x15\x01\x12\ \x05\0\x02\x15\x12\x03\x18\x04\x15\n\x0c\n\x05\x05\0\x02\x15\x01\x12\x03\
\x03\x18\x04\r\n\x0c\n\x05\x05\0\x02\x15\x02\x12\x03\x18\x10\x13\n\x0b\n\ \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\x19\n\x0c\n\x05\x05\0\x02\x16\x01\x12\ \x04\x05\0\x02\x16\x12\x03\x19\x04\x14\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\ \x03\x19\x04\r\n\x0c\n\x05\x05\0\x02\x16\x02\x12\x03\x19\x10\x13\n\x0b\n\
roto3\ \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; 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; ReadView = 202;
UpdateView = 203; UpdateView = 203;
DeleteView = 204; DeleteView = 204;
OpenView = 205; DuplicateView = 205;
CloseView = 206; CopyLink = 206;
ApplyDocDelta = 207; OpenView = 207;
CloseView = 208;
ReadTrash = 300; ReadTrash = 300;
PutbackTrash = 301; PutbackTrash = 301;
DeleteTrash = 302; DeleteTrash = 302;
RestoreAll = 303; RestoreAll = 303;
DeleteAll = 304; DeleteAll = 304;
ApplyDocDelta = 400;
InitWorkspace = 1000; InitWorkspace = 1000;
} }

View File

@ -119,6 +119,27 @@ impl ViewController {
Ok(()) 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. // belong_to_id will be the app_id or view_id.
#[tracing::instrument(level = "debug", skip(self), err)] #[tracing::instrument(level = "debug", skip(self), err)]
pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, WorkspaceError> { pub(crate) async fn read_views_belong_to(&self, belong_to_id: &str) -> Result<RepeatedView, WorkspaceError> {