[flutter]: close stream before docpage dispose

This commit is contained in:
appflowy 2021-10-08 16:58:58 +08:00
parent 7730539278
commit c4a6342c96
8 changed files with 68 additions and 333 deletions

View File

@ -28,12 +28,7 @@ class DocBloc extends Bloc<DocEvent, DocState> {
final docOrFail = await iDocImpl.readDoc(); final docOrFail = await iDocImpl.readDoc();
yield docOrFail.fold( yield docOrFail.fold(
(doc) { (doc) {
final flowyDoc = FlowyDoc( final flowyDoc = FlowyDoc(doc: doc, iDocImpl: iDocImpl);
doc: doc,
data: _decodeJsonToDocument(
doc.data,
),
iDocImpl: iDocImpl);
return DocState.loadDoc(flowyDoc); return DocState.loadDoc(flowyDoc);
}, },
(error) { (error) {

View File

@ -16,20 +16,13 @@ class DocEditBloc extends Bloc<DocEditEvent, DocEditState> {
close: (Close value) async* { close: (Close value) async* {
iDocImpl.closeDoc(); iDocImpl.closeDoc();
}, },
changeset: (Changeset changeset) async* { );
iDocImpl.applyChangeset(json: changeset.data);
},
save: (Save save) async* {
// no need to save
});
} }
} }
@freezed @freezed
abstract class DocEditEvent with _$DocEditEvent { abstract class DocEditEvent with _$DocEditEvent {
const factory DocEditEvent.initial() = Initial; const factory DocEditEvent.initial() = Initial;
const factory DocEditEvent.changeset(String data) = Changeset;
const factory DocEditEvent.save(String data) = Save;
const factory DocEditEvent.close() = Close; const factory DocEditEvent.close() = Close;
} }

View File

@ -20,18 +20,6 @@ class _$DocEditEventTearOff {
return const Initial(); return const Initial();
} }
Changeset changeset(String data) {
return Changeset(
data,
);
}
Save save(String data) {
return Save(
data,
);
}
Close close() { Close close() {
return const Close(); return const Close();
} }
@ -45,16 +33,12 @@ mixin _$DocEditEvent {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function(String data) changeset,
required TResult Function(String data) save,
required TResult Function() close, required TResult Function() close,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function(String data)? changeset,
TResult Function(String data)? save,
TResult Function()? close, TResult Function()? close,
required TResult orElse(), required TResult orElse(),
}) => }) =>
@ -62,16 +46,12 @@ mixin _$DocEditEvent {
@optionalTypeArgs @optionalTypeArgs
TResult map<TResult extends Object?>({ TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial, required TResult Function(Initial value) initial,
required TResult Function(Changeset value) changeset,
required TResult Function(Save value) save,
required TResult Function(Close value) close, required TResult Function(Close value) close,
}) => }) =>
throw _privateConstructorUsedError; throw _privateConstructorUsedError;
@optionalTypeArgs @optionalTypeArgs
TResult maybeMap<TResult extends Object?>({ TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial, TResult Function(Initial value)? initial,
TResult Function(Changeset value)? changeset,
TResult Function(Save value)? save,
TResult Function(Close value)? close, TResult Function(Close value)? close,
required TResult orElse(), required TResult orElse(),
}) => }) =>
@ -132,8 +112,6 @@ class _$Initial implements Initial {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function(String data) changeset,
required TResult Function(String data) save,
required TResult Function() close, required TResult Function() close,
}) { }) {
return initial(); return initial();
@ -143,8 +121,6 @@ class _$Initial implements Initial {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function(String data)? changeset,
TResult Function(String data)? save,
TResult Function()? close, TResult Function()? close,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -158,8 +134,6 @@ class _$Initial implements Initial {
@optionalTypeArgs @optionalTypeArgs
TResult map<TResult extends Object?>({ TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial, required TResult Function(Initial value) initial,
required TResult Function(Changeset value) changeset,
required TResult Function(Save value) save,
required TResult Function(Close value) close, required TResult Function(Close value) close,
}) { }) {
return initial(this); return initial(this);
@ -169,8 +143,6 @@ class _$Initial implements Initial {
@optionalTypeArgs @optionalTypeArgs
TResult maybeMap<TResult extends Object?>({ TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial, TResult Function(Initial value)? initial,
TResult Function(Changeset value)? changeset,
TResult Function(Save value)? save,
TResult Function(Close value)? close, TResult Function(Close value)? close,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -185,247 +157,6 @@ abstract class Initial implements DocEditEvent {
const factory Initial() = _$Initial; const factory Initial() = _$Initial;
} }
/// @nodoc
abstract class $ChangesetCopyWith<$Res> {
factory $ChangesetCopyWith(Changeset value, $Res Function(Changeset) then) =
_$ChangesetCopyWithImpl<$Res>;
$Res call({String data});
}
/// @nodoc
class _$ChangesetCopyWithImpl<$Res> extends _$DocEditEventCopyWithImpl<$Res>
implements $ChangesetCopyWith<$Res> {
_$ChangesetCopyWithImpl(Changeset _value, $Res Function(Changeset) _then)
: super(_value, (v) => _then(v as Changeset));
@override
Changeset get _value => super._value as Changeset;
@override
$Res call({
Object? data = freezed,
}) {
return _then(Changeset(
data == freezed
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$Changeset implements Changeset {
const _$Changeset(this.data);
@override
final String data;
@override
String toString() {
return 'DocEditEvent.changeset(data: $data)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is Changeset &&
(identical(other.data, data) ||
const DeepCollectionEquality().equals(other.data, data)));
}
@override
int get hashCode =>
runtimeType.hashCode ^ const DeepCollectionEquality().hash(data);
@JsonKey(ignore: true)
@override
$ChangesetCopyWith<Changeset> get copyWith =>
_$ChangesetCopyWithImpl<Changeset>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function(String data) changeset,
required TResult Function(String data) save,
required TResult Function() close,
}) {
return changeset(data);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String data)? changeset,
TResult Function(String data)? save,
TResult Function()? close,
required TResult orElse(),
}) {
if (changeset != null) {
return changeset(data);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(Changeset value) changeset,
required TResult Function(Save value) save,
required TResult Function(Close value) close,
}) {
return changeset(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(Changeset value)? changeset,
TResult Function(Save value)? save,
TResult Function(Close value)? close,
required TResult orElse(),
}) {
if (changeset != null) {
return changeset(this);
}
return orElse();
}
}
abstract class Changeset implements DocEditEvent {
const factory Changeset(String data) = _$Changeset;
String get data => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ChangesetCopyWith<Changeset> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SaveCopyWith<$Res> {
factory $SaveCopyWith(Save value, $Res Function(Save) then) =
_$SaveCopyWithImpl<$Res>;
$Res call({String data});
}
/// @nodoc
class _$SaveCopyWithImpl<$Res> extends _$DocEditEventCopyWithImpl<$Res>
implements $SaveCopyWith<$Res> {
_$SaveCopyWithImpl(Save _value, $Res Function(Save) _then)
: super(_value, (v) => _then(v as Save));
@override
Save get _value => super._value as Save;
@override
$Res call({
Object? data = freezed,
}) {
return _then(Save(
data == freezed
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$Save implements Save {
const _$Save(this.data);
@override
final String data;
@override
String toString() {
return 'DocEditEvent.save(data: $data)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is Save &&
(identical(other.data, data) ||
const DeepCollectionEquality().equals(other.data, data)));
}
@override
int get hashCode =>
runtimeType.hashCode ^ const DeepCollectionEquality().hash(data);
@JsonKey(ignore: true)
@override
$SaveCopyWith<Save> get copyWith =>
_$SaveCopyWithImpl<Save>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() initial,
required TResult Function(String data) changeset,
required TResult Function(String data) save,
required TResult Function() close,
}) {
return save(data);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial,
TResult Function(String data)? changeset,
TResult Function(String data)? save,
TResult Function()? close,
required TResult orElse(),
}) {
if (save != null) {
return save(data);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial,
required TResult Function(Changeset value) changeset,
required TResult Function(Save value) save,
required TResult Function(Close value) close,
}) {
return save(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial,
TResult Function(Changeset value)? changeset,
TResult Function(Save value)? save,
TResult Function(Close value)? close,
required TResult orElse(),
}) {
if (save != null) {
return save(this);
}
return orElse();
}
}
abstract class Save implements DocEditEvent {
const factory Save(String data) = _$Save;
String get data => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$SaveCopyWith<Save> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc /// @nodoc
abstract class $CloseCopyWith<$Res> { abstract class $CloseCopyWith<$Res> {
factory $CloseCopyWith(Close value, $Res Function(Close) then) = factory $CloseCopyWith(Close value, $Res Function(Close) then) =
@ -464,8 +195,6 @@ class _$Close implements Close {
@optionalTypeArgs @optionalTypeArgs
TResult when<TResult extends Object?>({ TResult when<TResult extends Object?>({
required TResult Function() initial, required TResult Function() initial,
required TResult Function(String data) changeset,
required TResult Function(String data) save,
required TResult Function() close, required TResult Function() close,
}) { }) {
return close(); return close();
@ -475,8 +204,6 @@ class _$Close implements Close {
@optionalTypeArgs @optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({ TResult maybeWhen<TResult extends Object?>({
TResult Function()? initial, TResult Function()? initial,
TResult Function(String data)? changeset,
TResult Function(String data)? save,
TResult Function()? close, TResult Function()? close,
required TResult orElse(), required TResult orElse(),
}) { }) {
@ -490,8 +217,6 @@ class _$Close implements Close {
@optionalTypeArgs @optionalTypeArgs
TResult map<TResult extends Object?>({ TResult map<TResult extends Object?>({
required TResult Function(Initial value) initial, required TResult Function(Initial value) initial,
required TResult Function(Changeset value) changeset,
required TResult Function(Save value) save,
required TResult Function(Close value) close, required TResult Function(Close value) close,
}) { }) {
return close(this); return close(this);
@ -501,8 +226,6 @@ class _$Close implements Close {
@optionalTypeArgs @optionalTypeArgs
TResult maybeMap<TResult extends Object?>({ TResult maybeMap<TResult extends Object?>({
TResult Function(Initial value)? initial, TResult Function(Initial value)? initial,
TResult Function(Changeset value)? changeset,
TResult Function(Save value)? save,
TResult Function(Close value)? close, TResult Function(Close value)? close,
required TResult orElse(), required TResult orElse(),
}) { }) {

View File

@ -1,5 +1,5 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:async';
import 'package:flowy_editor/flowy_editor.dart'; import 'package:flowy_editor/flowy_editor.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
// ignore: implementation_imports // ignore: implementation_imports
@ -8,40 +8,52 @@ 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 implements EditorDeltaSender { class FlowyDoc {
final DocDelta doc; final DocDelta doc;
final IDoc iDocImpl; final IDoc iDocImpl;
Document data; late Document document;
late StreamSubscription _subscription;
FlowyDoc({required this.doc, required this.data, required this.iDocImpl}) { FlowyDoc({required this.doc, required this.iDocImpl}) {
data.sender = this; 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; String get id => doc.docId;
@override Future<void> close() async {
void sendNewDelta(Delta changeset, Delta delta) async { await _subscription.cancel();
final json = jsonEncode(changeset.toJson()); }
void _composeDelta(Delta composedDelta, Delta documentDelta) async {
final json = jsonEncode(composedDelta.toJson());
Log.debug("Send json: $json"); Log.debug("Send json: $json");
final result = await iDocImpl.applyChangeset(json: json); final result = await iDocImpl.composeDelta(json: json);
result.fold((rustDoc) { result.fold((rustDoc) {
// final json = utf8.decode(doc.data); // final json = utf8.decode(doc.data);
final rustDelta = Delta.fromJson(jsonDecode(rustDoc.data)); final rustDelta = Delta.fromJson(jsonDecode(rustDoc.data));
if (documentDelta != rustDelta) {
if (delta != rustDelta) {
Log.error("Receive : $rustDelta"); Log.error("Receive : $rustDelta");
Log.error("Expected : $delta"); Log.error("Expected : $documentDelta");
} else {
Log.info("Receive : $rustDelta");
Log.info("Expected : $delta");
} }
}, (r) => null); }, (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>> applyChangeset( Future<Either<DocDelta, WorkspaceError>> composeDelta({required String json});
{required String json});
Future<Either<Unit, WorkspaceError>> closeDoc(); Future<Either<Unit, WorkspaceError>> closeDoc();
} }

View File

@ -24,9 +24,9 @@ class IDocImpl extends IDoc {
} }
@override @override
Future<Either<DocDelta, WorkspaceError>> applyChangeset( Future<Either<DocDelta, WorkspaceError>> composeDelta(
{required String json}) { {required String json}) {
return repo.applyDelta(data: json); return repo.composeDelta(data: json);
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
import 'package:flowy_log/flowy_log.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart'; import 'package:flowy_sdk/dispatch/dispatch.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';
@ -15,7 +16,8 @@ class DocRepository {
return WorkspaceEventOpenView(request).send(); return WorkspaceEventOpenView(request).send();
} }
Future<Either<DocDelta, WorkspaceError>> applyDelta({required String data}) { Future<Either<DocDelta, WorkspaceError>> composeDelta(
{required String data}) {
final request = DocDelta.create() final request = DocDelta.create()
..docId = docId ..docId = docId
..data = data; ..data = data;
@ -24,6 +26,9 @@ class DocRepository {
Future<Either<Unit, WorkspaceError>> closeDoc( Future<Either<Unit, WorkspaceError>> closeDoc(
{String? name, String? desc, String? text}) { {String? name, String? desc, String? text}) {
throw UnimplementedError(); Log.error('Close the doc');
return Future(() {
return left(unit);
});
} }
} }

View File

@ -8,31 +8,37 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
// ignore: must_be_immutable // ignore: must_be_immutable
class DocPage extends StatelessWidget { class DocPage extends StatefulWidget {
final FocusNode _focusNode = FocusNode();
late EditorController controller; late EditorController controller;
late DocEditBloc editBloc;
final FlowyDoc doc; final FlowyDoc doc;
DocPage({Key? key, required this.doc}) : super(key: key) { DocPage({Key? key, required this.doc}) : super(key: key) {
// getIt<EditorDeltaSender>(param1: doc.id)) editBloc = getIt<DocEditBloc>(param1: doc.id);
controller = EditorController( controller = EditorController(
document: doc.data, document: doc.document,
selection: const TextSelection.collapsed(offset: 0), selection: const TextSelection.collapsed(offset: 0),
); );
} }
@override
State<DocPage> createState() => _DocPageState();
}
class _DocPageState extends State<DocPage> {
final FocusNode _focusNode = FocusNode();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider.value(
create: (context) => getIt<DocEditBloc>(param1: doc.id), value: widget.editBloc,
child: BlocBuilder<DocEditBloc, DocEditState>( child: BlocBuilder<DocEditBloc, DocEditState>(
builder: (ctx, state) { builder: (ctx, state) {
return Column( return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
_renderEditor(controller), _renderEditor(widget.controller),
_renderToolbar(controller), _renderToolbar(widget.controller),
], ],
); );
}, },
@ -40,6 +46,14 @@ class DocPage extends StatelessWidget {
); );
} }
@override
Future<void> dispose() async {
widget.editBloc.add(const DocEditEvent.close());
widget.editBloc.close();
super.dispose();
await widget.doc.close();
}
Widget _renderEditor(EditorController controller) { Widget _renderEditor(EditorController controller) {
final editor = FlowyEditor( final editor = FlowyEditor(
controller: controller, controller: controller,

View File

@ -14,14 +14,9 @@ import 'node/line.dart';
import 'node/node.dart'; import 'node/node.dart';
import 'package:flowy_log/flowy_log.dart'; import 'package:flowy_log/flowy_log.dart';
abstract class EditorDeltaSender {
void sendNewDelta(Delta changeset, Delta delta);
}
/// The rich text document /// The rich text document
class Document { class Document {
EditorDeltaSender? sender; Document() : _delta = Delta()..insert('\n') {
Document({this.sender}) : _delta = Delta()..insert('\n') {
_loadDocument(_delta); _loadDocument(_delta);
} }
@ -170,8 +165,6 @@ class Document {
final changeset = delta; final changeset = delta;
_delta = _delta.compose(delta); _delta = _delta.compose(delta);
sender?.sendNewDelta(changeset, _delta);
} catch (e) { } catch (e) {
throw '_delta compose failed'; throw '_delta compose failed';
} }