From 66762ef9a0f7dd518c31f31e78c1ce1a37273413 Mon Sep 17 00:00:00 2001 From: Mathias Mogensen <42929161+Xazin@users.noreply.github.com> Date: Mon, 12 Feb 2024 00:27:10 +0100 Subject: [PATCH] feat: debounce on word count service (#711) * feat: debounce on word count service * test: add pump for debounce in test --- .../word_count/word_counter_service.dart | 70 +++++++++++++++---- .../plugins/word_count/word_counter_test.dart | 7 +- 2 files changed, 62 insertions(+), 15 deletions(-) diff --git a/lib/src/plugins/word_count/word_counter_service.dart b/lib/src/plugins/word_count/word_counter_service.dart index 9e50a6cfa..0f7ab8efb 100644 --- a/lib/src/plugins/word_count/word_counter_service.dart +++ b/lib/src/plugins/word_count/word_counter_service.dart @@ -48,10 +48,26 @@ class Counters { /// the counter stats. /// class WordCountService with ChangeNotifier { - WordCountService({required this.editorState}); + WordCountService({ + required this.editorState, + this.debounceDuration = const Duration(milliseconds: 300), + }); final EditorState editorState; + /// The time to wait before input stops, to recalculate + /// word and character count. + /// + /// This duration is used for debouncing both document + /// and selection changes. + /// + /// If 0 no debouncing will occur + /// + final Duration debounceDuration; + + Timer? _selectionTimer; + Timer? _documentTimer; + /// Number of words and characters in the [Document]. /// Counters get documentCounters => _documentCounters; @@ -111,9 +127,8 @@ class WordCountService with ChangeNotifier { notifyListeners(); } - _streamSubscription = - editorState.transactionStream.listen(_recountOnTransactionUpdate); - editorState.selectionNotifier.addListener(_recountOnSelectionUpdate); + _streamSubscription = editorState.transactionStream.listen(_onDocUpdate); + editorState.selectionNotifier.addListener(_onSelUpdate); } /// Stops the Word Counter and resets the counts. @@ -123,6 +138,10 @@ class WordCountService with ChangeNotifier { return; } + _documentTimer?.cancel(); + _documentTimer = null; + _selectionTimer?.cancel(); + _selectionTimer = null; _streamSubscription?.cancel(); _documentCounters = const Counters(); _selectionCounters = const Counters(); @@ -133,11 +152,27 @@ class WordCountService with ChangeNotifier { @override void dispose() { - editorState.selectionNotifier.removeListener(_recountOnSelectionUpdate); + editorState.selectionNotifier.removeListener(_onSelUpdate); _streamSubscription?.cancel(); + _documentTimer?.cancel(); + _selectionTimer?.cancel(); + _documentTimer = null; + _selectionTimer = null; super.dispose(); } + void _onSelUpdate() { + if (debounceDuration.inMilliseconds == 0) { + return _recountOnSelectionUpdate(); + } + + if (_selectionTimer?.isActive ?? false) { + _selectionTimer!.cancel(); + } + + _selectionTimer = Timer(debounceDuration, _recountOnSelectionUpdate); + } + void _recountOnSelectionUpdate() { // If collapsed or null, reset count if (editorState.selection?.isCollapsed ?? true) { @@ -146,7 +181,6 @@ class WordCountService with ChangeNotifier { } _selectionCounters = const Counters(); - return notifyListeners(); } @@ -169,16 +203,26 @@ class WordCountService with ChangeNotifier { charCount += counters.charCount; } - return Counters( - wordCount: wordCount, - charCount: charCount, + return Counters(wordCount: wordCount, charCount: charCount); + } + + void _onDocUpdate((TransactionTime time, Transaction t) event) { + if (debounceDuration.inMilliseconds == 0) { + return _recountOnTransactionUpdate(event.$1); + } + + if (_documentTimer?.isActive ?? false) { + _documentTimer!.cancel(); + } + + _documentTimer = Timer( + debounceDuration, + () => _recountOnTransactionUpdate(event.$1), ); } - void _recountOnTransactionUpdate( - (TransactionTime time, Transaction t) event, - ) { - if (event.$1 != TransactionTime.after) { + void _recountOnTransactionUpdate(TransactionTime time) { + if (time != TransactionTime.after) { return; } diff --git a/test/plugins/word_count/word_counter_test.dart b/test/plugins/word_count/word_counter_test.dart index a3296f17e..c4d55965d 100644 --- a/test/plugins/word_count/word_counter_test.dart +++ b/test/plugins/word_count/word_counter_test.dart @@ -1,6 +1,7 @@ -import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:appflowy_editor/appflowy_editor.dart'; + import '../../new/infra/testable_editor.dart'; void main() async { @@ -44,7 +45,7 @@ void main() async { await editor.editorState.apply(transaction); - await tester.pumpAndSettle(); + await tester.pumpAndSettle(const Duration(milliseconds: 300)); expect(service.documentCounters.wordCount, 3 * 4); expect(service.documentCounters.charCount, text.length * 4); @@ -53,6 +54,8 @@ void main() async { service.stop(); + await tester.pumpAndSettle(const Duration(milliseconds: 300)); + expect(service.documentCounters.wordCount, 0); expect(service.documentCounters.charCount, 0); expect(wordCount, 0);