From 5b3878dcc5876ae7a329b308ff82763f02cf8c5f Mon Sep 17 00:00:00 2001 From: Lucas Date: Mon, 25 Nov 2024 11:07:47 +0800 Subject: [PATCH] feat: support in memory transaction update (#972) * feat: support in memory transaction update * test: support in memory transaction update --- lib/src/editor_state.dart | 28 +++++++++++++------ .../word_count/word_counter_service.dart | 11 ++++---- test/editor_state_test.dart | 17 +++++++++++ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/lib/src/editor_state.dart b/lib/src/editor_state.dart index 3ea1abe17..f36afe462 100644 --- a/lib/src/editor_state.dart +++ b/lib/src/editor_state.dart @@ -8,6 +8,12 @@ import 'package:appflowy_editor/src/history/undo_manager.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +typedef EditorTransactionValue = ( + TransactionTime time, + Transaction transaction, + ApplyOptions options, +); + /// the type of this value is bool. /// /// set true to this key to prevent attaching the text service when selection is changed. @@ -15,15 +21,20 @@ const selectionExtraInfoDoNotAttachTextService = 'selectionExtraInfoDoNotAttachTextService'; class ApplyOptions { + const ApplyOptions({ + this.recordUndo = true, + this.recordRedo = false, + this.inMemoryUpdate = false, + }); + /// This flag indicates that /// whether the transaction should be recorded into /// the undo stack final bool recordUndo; final bool recordRedo; - const ApplyOptions({ - this.recordUndo = true, - this.recordRedo = false, - }); + + /// This flag used to determine whether the transaction is in-memory update. + final bool inMemoryUpdate; } @Deprecated('use SelectionUpdateReason instead') @@ -172,9 +183,8 @@ class EditorState { List toolbarItems = []; /// listen to this stream to get notified when the transaction applies. - Stream<(TransactionTime, Transaction)> get transactionStream => - _observer.stream; - final StreamController<(TransactionTime, Transaction)> _observer = + Stream get transactionStream => _observer.stream; + final StreamController _observer = StreamController.broadcast(sync: true); /// Store the toggled format style, like bold, italic, etc. @@ -337,14 +347,14 @@ class EditorState { } else { // broadcast to other users here, before applying the transaction if (!_observer.isClosed) { - _observer.add((TransactionTime.before, transaction)); + _observer.add((TransactionTime.before, transaction, options)); } _applyTransactionInLocal(transaction); // broadcast to other users here, after applying the transaction if (!_observer.isClosed) { - _observer.add((TransactionTime.after, transaction)); + _observer.add((TransactionTime.after, transaction, options)); } _recordRedoOrUndo(options, transaction, skipHistoryDebounce); diff --git a/lib/src/plugins/word_count/word_counter_service.dart b/lib/src/plugins/word_count/word_counter_service.dart index 9b45af73b..ee7e71ceb 100644 --- a/lib/src/plugins/word_count/word_counter_service.dart +++ b/lib/src/plugins/word_count/word_counter_service.dart @@ -1,8 +1,7 @@ import 'dart:async'; -import 'package:flutter/widgets.dart'; - import 'package:appflowy_editor/appflowy_editor.dart'; +import 'package:flutter/widgets.dart'; const _emptyCounters = Counters(); @@ -99,7 +98,7 @@ class WordCountService with ChangeNotifier { /// bool isRunning = false; - StreamSubscription<(TransactionTime, Transaction)>? _streamSubscription; + StreamSubscription? _streamSubscription; /// This method can be used to get the word and character /// count of the [Document] of the [EditorState]. @@ -221,9 +220,9 @@ class WordCountService with ChangeNotifier { return Counters(wordCount: wordCount, charCount: charCount); } - void _onDocUpdate((TransactionTime time, Transaction t) event) { + void _onDocUpdate(EditorTransactionValue value) { if (debounceDuration.inMilliseconds == 0) { - return _recountOnTransactionUpdate(event.$1); + return _recountOnTransactionUpdate(value.$1); } if (_documentTimer?.isActive ?? false) { @@ -232,7 +231,7 @@ class WordCountService with ChangeNotifier { _documentTimer = Timer( debounceDuration, - () => _recountOnTransactionUpdate(event.$1), + () => _recountOnTransactionUpdate(value.$1), ); } diff --git a/test/editor_state_test.dart b/test/editor_state_test.dart index 4796f015d..b9fcf635d 100644 --- a/test/editor_state_test.dart +++ b/test/editor_state_test.dart @@ -28,5 +28,22 @@ void main() async { await editorState.apply(transaction); expect(count, 2); }); + + test('transaction stream', () async { + final editorState = EditorState.blank( + withInitialText: false, + ); + var isInMemoryUpdate = false; + editorState.transactionStream.listen((event) { + isInMemoryUpdate = event.$3.inMemoryUpdate; + }); + final transaction = editorState.transaction; + transaction.insertNode([0], paragraphNode(text: 'Hello World!')); + await editorState.apply( + transaction, + options: const ApplyOptions(inMemoryUpdate: true), + ); + expect(isInMemoryUpdate, true); + }); }); }