[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();
yield docOrFail.fold(
(doc) {
final flowyDoc = FlowyDoc(
doc: doc,
data: _decodeJsonToDocument(
doc.data,
),
iDocImpl: iDocImpl);
final flowyDoc = FlowyDoc(doc: doc, iDocImpl: iDocImpl);
return DocState.loadDoc(flowyDoc);
},
(error) {

View File

@ -12,24 +12,17 @@ class DocEditBloc extends Bloc<DocEditEvent, DocEditState> {
@override
Stream<DocEditState> mapEventToState(DocEditEvent event) async* {
yield* event.map(
initial: (e) async* {},
close: (Close value) async* {
iDocImpl.closeDoc();
},
changeset: (Changeset changeset) async* {
iDocImpl.applyChangeset(json: changeset.data);
},
save: (Save save) async* {
// no need to save
});
initial: (e) async* {},
close: (Close value) async* {
iDocImpl.closeDoc();
},
);
}
}
@freezed
abstract class DocEditEvent with _$DocEditEvent {
const factory DocEditEvent.initial() = Initial;
const factory DocEditEvent.changeset(String data) = Changeset;
const factory DocEditEvent.save(String data) = Save;
const factory DocEditEvent.close() = Close;
}

View File

@ -20,18 +20,6 @@ class _$DocEditEventTearOff {
return const Initial();
}
Changeset changeset(String data) {
return Changeset(
data,
);
}
Save save(String data) {
return Save(
data,
);
}
Close close() {
return const Close();
}
@ -45,16 +33,12 @@ mixin _$DocEditEvent {
@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,
}) =>
throw _privateConstructorUsedError;
@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(),
}) =>
@ -62,16 +46,12 @@ mixin _$DocEditEvent {
@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,
}) =>
throw _privateConstructorUsedError;
@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(),
}) =>
@ -132,8 +112,6 @@ class _$Initial implements Initial {
@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 initial();
@ -143,8 +121,6 @@ class _$Initial implements Initial {
@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(),
}) {
@ -158,8 +134,6 @@ class _$Initial implements Initial {
@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 initial(this);
@ -169,8 +143,6 @@ class _$Initial implements Initial {
@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(),
}) {
@ -185,247 +157,6 @@ abstract class Initial implements DocEditEvent {
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
abstract class $CloseCopyWith<$Res> {
factory $CloseCopyWith(Close value, $Res Function(Close) then) =
@ -464,8 +195,6 @@ class _$Close implements Close {
@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 close();
@ -475,8 +204,6 @@ class _$Close implements Close {
@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(),
}) {
@ -490,8 +217,6 @@ class _$Close implements Close {
@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 close(this);
@ -501,8 +226,6 @@ class _$Close implements Close {
@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(),
}) {

View File

@ -1,5 +1,5 @@
import 'dart:convert';
import 'dart:async';
import 'package:flowy_editor/flowy_editor.dart';
import 'package:dartz/dartz.dart';
// 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-workspace/errors.pb.dart';
class FlowyDoc implements EditorDeltaSender {
class FlowyDoc {
final DocDelta doc;
final IDoc iDocImpl;
Document data;
late Document document;
late StreamSubscription _subscription;
FlowyDoc({required this.doc, required this.data, required this.iDocImpl}) {
data.sender = this;
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;
@override
void sendNewDelta(Delta changeset, Delta delta) async {
final json = jsonEncode(changeset.toJson());
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.applyChangeset(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 (delta != rustDelta) {
if (documentDelta != rustDelta) {
Log.error("Receive : $rustDelta");
Log.error("Expected : $delta");
} else {
Log.info("Receive : $rustDelta");
Log.info("Expected : $delta");
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>> applyChangeset(
{required String json});
Future<Either<DocDelta, WorkspaceError>> composeDelta({required String json});
Future<Either<Unit, WorkspaceError>> closeDoc();
}

View File

@ -24,9 +24,9 @@ class IDocImpl extends IDoc {
}
@override
Future<Either<DocDelta, WorkspaceError>> applyChangeset(
Future<Either<DocDelta, WorkspaceError>> composeDelta(
{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:flowy_log/flowy_log.dart';
import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-document/doc.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-workspace/errors.pb.dart';
@ -15,7 +16,8 @@ class DocRepository {
return WorkspaceEventOpenView(request).send();
}
Future<Either<DocDelta, WorkspaceError>> applyDelta({required String data}) {
Future<Either<DocDelta, WorkspaceError>> composeDelta(
{required String data}) {
final request = DocDelta.create()
..docId = docId
..data = data;
@ -24,6 +26,9 @@ class DocRepository {
Future<Either<Unit, WorkspaceError>> closeDoc(
{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';
// ignore: must_be_immutable
class DocPage extends StatelessWidget {
final FocusNode _focusNode = FocusNode();
class DocPage extends StatefulWidget {
late EditorController controller;
late DocEditBloc editBloc;
final FlowyDoc doc;
DocPage({Key? key, required this.doc}) : super(key: key) {
// getIt<EditorDeltaSender>(param1: doc.id))
editBloc = getIt<DocEditBloc>(param1: doc.id);
controller = EditorController(
document: doc.data,
document: doc.document,
selection: const TextSelection.collapsed(offset: 0),
);
}
@override
State<DocPage> createState() => _DocPageState();
}
class _DocPageState extends State<DocPage> {
final FocusNode _focusNode = FocusNode();
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => getIt<DocEditBloc>(param1: doc.id),
return BlocProvider.value(
value: widget.editBloc,
child: BlocBuilder<DocEditBloc, DocEditState>(
builder: (ctx, state) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
_renderEditor(controller),
_renderToolbar(controller),
_renderEditor(widget.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) {
final editor = FlowyEditor(
controller: controller,

View File

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