Browse Source

Merge pull request #120 from provokateurin/fix/notes-editing

Fix note editing problems
pull/122/head
Kate 2 years ago committed by GitHub
parent
commit
60165edccf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      packages/neon/lib/src/apps/notes/app.dart
  2. 83
      packages/neon/lib/src/apps/notes/blocs/note.dart
  3. 70
      packages/neon/lib/src/apps/notes/blocs/note.rxb.g.dart
  4. 49
      packages/neon/lib/src/apps/notes/pages/note.dart

1
packages/neon/lib/src/apps/notes/app.dart

@ -1,6 +1,5 @@
library notes; library notes;
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';

83
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/apps/notes/blocs/notes.dart';
import 'package:neon/src/neon.dart'; import 'package:neon/src/neon.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:queue/queue.dart';
import 'package:rx_bloc/rx_bloc.dart'; import 'package:rx_bloc/rx_bloc.dart';
import 'package:rxdart/rxdart.dart'; import 'package:rxdart/rxdart.dart';
part 'note.rxb.g.dart'; part 'note.rxb.g.dart';
abstract class NotesNoteBlocEvents { abstract class NotesNoteBlocEvents {
void updateNote( void updateContent(final String content);
final int id,
final String etag, { void updateTitle(final String title);
final String? title,
final String? category, void updateCategory(final String category);
final String? content,
final bool? favorite,
});
} }
abstract class NotesNoteBlocStates { abstract class NotesNoteBlocStates {
@ -27,8 +25,6 @@ abstract class NotesNoteBlocStates {
BehaviorSubject<String> get category; BehaviorSubject<String> get category;
BehaviorSubject<String> get etag;
Stream<Exception> get errors; Stream<Exception> get errors;
} }
@ -41,52 +37,69 @@ class NotesNoteBloc extends $NotesNoteBloc {
this._notesBloc, this._notesBloc,
final NotesNote note, 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( _wrapAction(
() async { (final etag) async => _client.notes.updateNote(
_emitNote( id: id,
await _client.notes.updateNote( category: category,
id: event.id, ifMatch: '"$etag"',
title: event.title, ),
category: event.category,
content: event.content,
favorite: event.favorite ?? false ? 1 : 0,
ifMatch: '"${event.etag}"',
),
);
},
); );
}); });
_contentSubject.add(note.content);
_titleSubject.add(note.title);
_emitNote(note); _emitNote(note);
id = note.id; id = note.id;
} }
void _emitNote(final NotesNote note) { void _emitNote(final NotesNote note) {
_contentSubject.add(note.content);
_titleSubject.add(note.title);
_categorySubject.add(note.category); _categorySubject.add(note.category);
_etagSubject.add(note.etag); _etag = note.etag;
} }
void _wrapAction(final Future Function() call) { void _wrapAction(final Future<NotesNote> Function(String etag) call) {
final stream = _requestManager.wrapWithoutCache(call).asBroadcastStream(); unawaited(
stream.whereError().listen(_errorsStreamController.add); _updateQueue.add(() async {
stream.whereSuccess().listen((final _) async { final stream = _requestManager.wrapWithoutCache(() async => _emitNote(await call(_etag))).asBroadcastStream();
_notesBloc.refresh(); stream.whereError().listen(_errorsStreamController.add);
}); stream.whereSuccess().listen((final _) => _notesBloc.refresh());
await stream.last;
}),
);
} }
final NotesAppSpecificOptions options; final NotesAppSpecificOptions options;
final RequestManager _requestManager; final RequestManager _requestManager;
final NextcloudClient _client; final NextcloudClient _client;
final NotesBloc _notesBloc; final NotesBloc _notesBloc;
final _updateQueue = Queue();
late final int id; late final int id;
late String _etag;
final _contentSubject = BehaviorSubject<String>(); final _contentSubject = BehaviorSubject<String>();
final _titleSubject = BehaviorSubject<String>(); final _titleSubject = BehaviorSubject<String>();
final _categorySubject = BehaviorSubject<String>(); final _categorySubject = BehaviorSubject<String>();
final _etagSubject = BehaviorSubject<String>();
final _errorsStreamController = StreamController<Exception>(); final _errorsStreamController = StreamController<Exception>();
@override @override
@ -94,7 +107,6 @@ class NotesNoteBloc extends $NotesNoteBloc {
unawaited(_contentSubject.close()); unawaited(_contentSubject.close());
unawaited(_titleSubject.close()); unawaited(_titleSubject.close());
unawaited(_categorySubject.close()); unawaited(_categorySubject.close());
unawaited(_etagSubject.close());
unawaited(_errorsStreamController.close()); unawaited(_errorsStreamController.close());
super.dispose(); super.dispose();
} }
@ -110,7 +122,4 @@ class NotesNoteBloc extends $NotesNoteBloc {
@override @override
BehaviorSubject<String> _mapToCategoryState() => _categorySubject; BehaviorSubject<String> _mapToCategoryState() => _categorySubject;
@override
BehaviorSubject<String> _mapToEtagState() => _etagSubject;
} }

70
packages/neon/lib/src/apps/notes/blocs/note.rxb.g.dart

@ -19,8 +19,14 @@ abstract class $NotesNoteBloc extends RxBlocBase
implements NotesNoteBlocEvents, NotesNoteBlocStates, NotesNoteBlocType { implements NotesNoteBlocEvents, NotesNoteBlocStates, NotesNoteBlocType {
final _compositeSubscription = CompositeSubscription(); final _compositeSubscription = CompositeSubscription();
/// Тhe [Subject] where events sink to by calling [updateNote] /// Тhe [Subject] where events sink to by calling [updateContent]
final _$updateNoteEvent = PublishSubject<_UpdateNoteEventArgs>(); final _$updateContentEvent = PublishSubject<String>();
/// Тhe [Subject] where events sink to by calling [updateTitle]
final _$updateTitleEvent = PublishSubject<String>();
/// Тhe [Subject] where events sink to by calling [updateCategory]
final _$updateCategoryEvent = PublishSubject<String>();
/// The state of [content] implemented in [_mapToContentState] /// The state of [content] implemented in [_mapToContentState]
late final BehaviorSubject<String> _contentState = _mapToContentState(); late final BehaviorSubject<String> _contentState = _mapToContentState();
@ -31,29 +37,17 @@ abstract class $NotesNoteBloc extends RxBlocBase
/// The state of [category] implemented in [_mapToCategoryState] /// The state of [category] implemented in [_mapToCategoryState]
late final BehaviorSubject<String> _categoryState = _mapToCategoryState(); late final BehaviorSubject<String> _categoryState = _mapToCategoryState();
/// The state of [etag] implemented in [_mapToEtagState]
late final BehaviorSubject<String> _etagState = _mapToEtagState();
/// The state of [errors] implemented in [_mapToErrorsState] /// The state of [errors] implemented in [_mapToErrorsState]
late final Stream<Exception> _errorsState = _mapToErrorsState(); late final Stream<Exception> _errorsState = _mapToErrorsState();
@override @override
void updateNote( void updateContent(String content) => _$updateContentEvent.add(content);
int id,
String etag, { @override
String? title, void updateTitle(String title) => _$updateTitleEvent.add(title);
String? category,
String? content, @override
bool? favorite, void updateCategory(String category) => _$updateCategoryEvent.add(category);
}) =>
_$updateNoteEvent.add(_UpdateNoteEventArgs(
id,
etag,
title: title,
category: category,
content: content,
favorite: favorite,
));
@override @override
BehaviorSubject<String> get content => _contentState; BehaviorSubject<String> get content => _contentState;
@ -64,9 +58,6 @@ abstract class $NotesNoteBloc extends RxBlocBase
@override @override
BehaviorSubject<String> get category => _categoryState; BehaviorSubject<String> get category => _categoryState;
@override
BehaviorSubject<String> get etag => _etagState;
@override @override
Stream<Exception> get errors => _errorsState; Stream<Exception> get errors => _errorsState;
@ -76,8 +67,6 @@ abstract class $NotesNoteBloc extends RxBlocBase
BehaviorSubject<String> _mapToCategoryState(); BehaviorSubject<String> _mapToCategoryState();
BehaviorSubject<String> _mapToEtagState();
Stream<Exception> _mapToErrorsState(); Stream<Exception> _mapToErrorsState();
@override @override
@ -88,33 +77,10 @@ abstract class $NotesNoteBloc extends RxBlocBase
@override @override
void dispose() { void dispose() {
_$updateNoteEvent.close(); _$updateContentEvent.close();
_$updateTitleEvent.close();
_$updateCategoryEvent.close();
_compositeSubscription.dispose(); _compositeSubscription.dispose();
super.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;
}

49
packages/neon/lib/src/apps/notes/pages/note.dart

@ -19,7 +19,6 @@ class _NotesNotePageState extends State<NotesNotePage> {
late final _titleController = TextEditingController(); late final _titleController = TextEditingController();
final _contentFocusNode = FocusNode(); final _contentFocusNode = FocusNode();
final _titleFocusNode = FocusNode(); final _titleFocusNode = FocusNode();
final _updateController = StreamController();
bool _showEditor = false; bool _showEditor = false;
void _focusEditor() { void _focusEditor() {
@ -27,24 +26,6 @@ class _NotesNotePageState extends State<NotesNotePage> {
_contentController.selection = TextSelection.collapsed(offset: _contentController.text.length); _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 @override
void initState() { void initState() {
super.initState(); super.initState();
@ -71,9 +52,16 @@ class _NotesNotePageState extends State<NotesNotePage> {
} }
}); });
_contentController.addListener(() => _updateController.add(null)); _contentController.addListener(() async {
_titleController.addListener(() => _updateController.add(null)); if (await widget.bloc.content.first != _contentController.text) {
_updateController.stream.debounceTime(const Duration(seconds: 1)).listen((final _) async => _update()); 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 { WidgetsBinding.instance.addPostFrameCallback((final _) async {
if (Provider.of<NeonPlatform>(context, listen: false).canUseWakelock) { if (Provider.of<NeonPlatform>(context, listen: false).canUseWakelock) {
@ -89,27 +77,16 @@ class _NotesNotePageState extends State<NotesNotePage> {
}); });
} }
@override
void dispose() {
unawaited(_updateController.close());
super.dispose();
}
@override @override
Widget build(final BuildContext context) => WillPopScope( Widget build(final BuildContext context) => WillPopScope(
onWillPop: () async { onWillPop: () async {
await _update();
if (!mounted) {
return true;
}
if (Provider.of<NeonPlatform>(context, listen: false).canUseWakelock) { if (Provider.of<NeonPlatform>(context, listen: false).canUseWakelock) {
await Wakelock.disable(); await Wakelock.disable();
} }
return true; return true;
}, },
child: Scaffold( child: Scaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: true,
appBar: AppBar( appBar: AppBar(
titleSpacing: 0, titleSpacing: 0,
title: TextField( title: TextField(
@ -159,9 +136,7 @@ class _NotesNotePageState extends State<NotesNotePage> {
), ),
); );
if (result != null) { if (result != null) {
await _update( widget.bloc.updateCategory(result);
category: result,
);
} }
}, },
icon: Icon( icon: Icon(

Loading…
Cancel
Save