feat: debounce on word count service (#711)

* feat: debounce on word count service

* test: add pump for debounce in test
This commit is contained in:
Mathias Mogensen 2024-02-12 00:27:10 +01:00 • committed by GitHub
commit 66762ef9a0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 15 deletions

View file

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

View file

@ -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);