Skip to content

Commit

Permalink
feat: debounce on word count service (AppFlowy-IO#711)
Browse files Browse the repository at this point in the history
* feat: debounce on word count service

* test: add pump for debounce in test
  • Loading branch information
Xazin authored Feb 11, 2024
1 parent e09bde4 commit 66762ef
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 15 deletions.
70 changes: 57 additions & 13 deletions lib/src/plugins/word_count/word_counter_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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();
Expand All @@ -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) {
Expand All @@ -146,7 +181,6 @@ class WordCountService with ChangeNotifier {
}

_selectionCounters = const Counters();

return notifyListeners();
}

Expand All @@ -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;
}

Expand Down
7 changes: 5 additions & 2 deletions test/plugins/word_count/word_counter_test.dart
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 66762ef

Please sign in to comment.