diff --git a/packages/neon/lib/src/apps/notes/app.dart b/packages/neon/lib/src/apps/notes/app.dart index 002e7943..fd7b30d8 100644 --- a/packages/neon/lib/src/apps/notes/app.dart +++ b/packages/neon/lib/src/apps/notes/app.dart @@ -1,6 +1,5 @@ library notes; -import 'dart:async'; import 'dart:convert'; import 'package:crypto/crypto.dart'; diff --git a/packages/neon/lib/src/apps/notes/blocs/note.dart b/packages/neon/lib/src/apps/notes/blocs/note.dart index 44426cd3..66f680cb 100644 --- a/packages/neon/lib/src/apps/notes/blocs/note.dart +++ b/packages/neon/lib/src/apps/notes/blocs/note.dart @@ -4,20 +4,18 @@ import 'package:neon/src/apps/notes/app.dart'; import 'package:neon/src/apps/notes/blocs/notes.dart'; import 'package:neon/src/neon.dart'; import 'package:nextcloud/nextcloud.dart'; +import 'package:queue/queue.dart'; import 'package:rx_bloc/rx_bloc.dart'; import 'package:rxdart/rxdart.dart'; part 'note.rxb.g.dart'; abstract class NotesNoteBlocEvents { - void updateNote( - final int id, - final String etag, { - final String? title, - final String? category, - final String? content, - final bool? favorite, - }); + void updateContent(final String content); + + void updateTitle(final String title); + + void updateCategory(final String category); } abstract class NotesNoteBlocStates { @@ -27,8 +25,6 @@ abstract class NotesNoteBlocStates { BehaviorSubject get category; - BehaviorSubject get etag; - Stream get errors; } @@ -41,52 +37,69 @@ class NotesNoteBloc extends $NotesNoteBloc { this._notesBloc, final NotesNote note, ) { - _$updateNoteEvent.listen((final event) { + _$updateContentEvent.debounceTime(const Duration(seconds: 1)).listen((final content) { + _wrapAction( + (final etag) async => _client.notes.updateNote( + id: id, + content: content, + ifMatch: '"$etag"', + ), + ); + }); + + _$updateTitleEvent.debounceTime(const Duration(seconds: 1)).listen((final title) { + _wrapAction( + (final etag) async => _client.notes.updateNote( + id: id, + title: title, + ifMatch: '"$etag"', + ), + ); + }); + + _$updateCategoryEvent.listen((final category) { _wrapAction( - () async { - _emitNote( - await _client.notes.updateNote( - id: event.id, - title: event.title, - category: event.category, - content: event.content, - favorite: event.favorite ?? false ? 1 : 0, - ifMatch: '"${event.etag}"', - ), - ); - }, + (final etag) async => _client.notes.updateNote( + id: id, + category: category, + ifMatch: '"$etag"', + ), ); }); + _contentSubject.add(note.content); + _titleSubject.add(note.title); _emitNote(note); id = note.id; } void _emitNote(final NotesNote note) { - _contentSubject.add(note.content); - _titleSubject.add(note.title); _categorySubject.add(note.category); - _etagSubject.add(note.etag); + _etag = note.etag; } - void _wrapAction(final Future Function() call) { - final stream = _requestManager.wrapWithoutCache(call).asBroadcastStream(); - stream.whereError().listen(_errorsStreamController.add); - stream.whereSuccess().listen((final _) async { - _notesBloc.refresh(); - }); + void _wrapAction(final Future Function(String etag) call) { + unawaited( + _updateQueue.add(() async { + final stream = _requestManager.wrapWithoutCache(() async => _emitNote(await call(_etag))).asBroadcastStream(); + stream.whereError().listen(_errorsStreamController.add); + stream.whereSuccess().listen((final _) => _notesBloc.refresh()); + await stream.last; + }), + ); } final NotesAppSpecificOptions options; final RequestManager _requestManager; final NextcloudClient _client; final NotesBloc _notesBloc; + final _updateQueue = Queue(); late final int id; + late String _etag; final _contentSubject = BehaviorSubject(); final _titleSubject = BehaviorSubject(); final _categorySubject = BehaviorSubject(); - final _etagSubject = BehaviorSubject(); final _errorsStreamController = StreamController(); @override @@ -94,7 +107,6 @@ class NotesNoteBloc extends $NotesNoteBloc { unawaited(_contentSubject.close()); unawaited(_titleSubject.close()); unawaited(_categorySubject.close()); - unawaited(_etagSubject.close()); unawaited(_errorsStreamController.close()); super.dispose(); } @@ -110,7 +122,4 @@ class NotesNoteBloc extends $NotesNoteBloc { @override BehaviorSubject _mapToCategoryState() => _categorySubject; - - @override - BehaviorSubject _mapToEtagState() => _etagSubject; } diff --git a/packages/neon/lib/src/apps/notes/blocs/note.rxb.g.dart b/packages/neon/lib/src/apps/notes/blocs/note.rxb.g.dart index 6daf4dba..c8cb7bb2 100644 --- a/packages/neon/lib/src/apps/notes/blocs/note.rxb.g.dart +++ b/packages/neon/lib/src/apps/notes/blocs/note.rxb.g.dart @@ -19,8 +19,14 @@ abstract class $NotesNoteBloc extends RxBlocBase implements NotesNoteBlocEvents, NotesNoteBlocStates, NotesNoteBlocType { final _compositeSubscription = CompositeSubscription(); - /// Тhe [Subject] where events sink to by calling [updateNote] - final _$updateNoteEvent = PublishSubject<_UpdateNoteEventArgs>(); + /// Тhe [Subject] where events sink to by calling [updateContent] + final _$updateContentEvent = PublishSubject(); + + /// Тhe [Subject] where events sink to by calling [updateTitle] + final _$updateTitleEvent = PublishSubject(); + + /// Тhe [Subject] where events sink to by calling [updateCategory] + final _$updateCategoryEvent = PublishSubject(); /// The state of [content] implemented in [_mapToContentState] late final BehaviorSubject _contentState = _mapToContentState(); @@ -31,29 +37,17 @@ abstract class $NotesNoteBloc extends RxBlocBase /// The state of [category] implemented in [_mapToCategoryState] late final BehaviorSubject _categoryState = _mapToCategoryState(); - /// The state of [etag] implemented in [_mapToEtagState] - late final BehaviorSubject _etagState = _mapToEtagState(); - /// The state of [errors] implemented in [_mapToErrorsState] late final Stream _errorsState = _mapToErrorsState(); @override - void updateNote( - int id, - String etag, { - String? title, - String? category, - String? content, - bool? favorite, - }) => - _$updateNoteEvent.add(_UpdateNoteEventArgs( - id, - etag, - title: title, - category: category, - content: content, - favorite: favorite, - )); + void updateContent(String content) => _$updateContentEvent.add(content); + + @override + void updateTitle(String title) => _$updateTitleEvent.add(title); + + @override + void updateCategory(String category) => _$updateCategoryEvent.add(category); @override BehaviorSubject get content => _contentState; @@ -64,9 +58,6 @@ abstract class $NotesNoteBloc extends RxBlocBase @override BehaviorSubject get category => _categoryState; - @override - BehaviorSubject get etag => _etagState; - @override Stream get errors => _errorsState; @@ -76,8 +67,6 @@ abstract class $NotesNoteBloc extends RxBlocBase BehaviorSubject _mapToCategoryState(); - BehaviorSubject _mapToEtagState(); - Stream _mapToErrorsState(); @override @@ -88,33 +77,10 @@ abstract class $NotesNoteBloc extends RxBlocBase @override void dispose() { - _$updateNoteEvent.close(); + _$updateContentEvent.close(); + _$updateTitleEvent.close(); + _$updateCategoryEvent.close(); _compositeSubscription.dispose(); super.dispose(); } } - -/// Helps providing the arguments in the [Subject.add] for -/// [NotesNoteBlocEvents.updateNote] event -class _UpdateNoteEventArgs { - const _UpdateNoteEventArgs( - this.id, - this.etag, { - this.title, - this.category, - this.content, - this.favorite, - }); - - final int id; - - final String etag; - - final String? title; - - final String? category; - - final String? content; - - final bool? favorite; -} diff --git a/packages/neon/lib/src/apps/notes/pages/note.dart b/packages/neon/lib/src/apps/notes/pages/note.dart index 2b91af83..31e621a5 100644 --- a/packages/neon/lib/src/apps/notes/pages/note.dart +++ b/packages/neon/lib/src/apps/notes/pages/note.dart @@ -19,7 +19,6 @@ class _NotesNotePageState extends State { late final _titleController = TextEditingController(); final _contentFocusNode = FocusNode(); final _titleFocusNode = FocusNode(); - final _updateController = StreamController(); bool _showEditor = false; void _focusEditor() { @@ -27,24 +26,6 @@ class _NotesNotePageState extends State { _contentController.selection = TextSelection.collapsed(offset: _contentController.text.length); } - Future _update({ - final String? category, - }) async { - final updatedTitle = await widget.bloc.title.first != _titleController.text ? _titleController.text : null; - final updatedCategory = category != null && await widget.bloc.category.first != category ? category : null; - final updatedContent = await widget.bloc.content.first != _contentController.text ? _contentController.text : null; - - if (updatedTitle != null || updatedCategory != null || updatedContent != null) { - widget.bloc.updateNote( - widget.bloc.id, - await widget.bloc.etag.first, - title: updatedTitle, - category: updatedCategory, - content: updatedContent, - ); - } - } - @override void initState() { super.initState(); @@ -71,9 +52,16 @@ class _NotesNotePageState extends State { } }); - _contentController.addListener(() => _updateController.add(null)); - _titleController.addListener(() => _updateController.add(null)); - _updateController.stream.debounceTime(const Duration(seconds: 1)).listen((final _) async => _update()); + _contentController.addListener(() async { + if (await widget.bloc.content.first != _contentController.text) { + widget.bloc.updateContent(_contentController.text); + } + }); + _titleController.addListener(() async { + if (await widget.bloc.title.first != _titleController.text) { + widget.bloc.updateTitle(_titleController.text); + } + }); WidgetsBinding.instance.addPostFrameCallback((final _) async { if (Provider.of(context, listen: false).canUseWakelock) { @@ -89,27 +77,16 @@ class _NotesNotePageState extends State { }); } - @override - void dispose() { - unawaited(_updateController.close()); - super.dispose(); - } - @override Widget build(final BuildContext context) => WillPopScope( onWillPop: () async { - await _update(); - - if (!mounted) { - return true; - } if (Provider.of(context, listen: false).canUseWakelock) { await Wakelock.disable(); } return true; }, child: Scaffold( - resizeToAvoidBottomInset: false, + resizeToAvoidBottomInset: true, appBar: AppBar( titleSpacing: 0, title: TextField( @@ -159,9 +136,7 @@ class _NotesNotePageState extends State { ), ); if (result != null) { - await _update( - category: result, - ); + widget.bloc.updateCategory(result); } }, icon: Icon(