From a8216819037b6c58d207fe5ee18c45e8d50029b2 Mon Sep 17 00:00:00 2001 From: shilangyu Date: Sat, 15 Jan 2022 16:15:55 +0100 Subject: [PATCH] Initial refactor --- assets/l10n/intl_en.arb | 4 +- lib/pages/community/community.dart | 2 +- lib/pages/create_post/create_post_fab.dart | 36 +++++ .../create_post_instance_picker.dart | 34 +++++ lib/pages/create_post/create_post_store.dart | 63 ++++++++ .../create_post/create_post_store.g.dart | 137 ++++++++++++++++++ lib/pages/home_page.dart | 2 +- lib/util/async_store_listener.dart | 39 +++-- lib/widgets/editor.dart | 3 + lib/widgets/post/post_more_menu.dart | 2 +- pubspec.lock | 7 + pubspec.yaml | 1 + 12 files changed, 313 insertions(+), 17 deletions(-) create mode 100644 lib/pages/create_post/create_post_fab.dart create mode 100644 lib/pages/create_post/create_post_instance_picker.dart create mode 100644 lib/pages/create_post/create_post_store.dart create mode 100644 lib/pages/create_post/create_post_store.g.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index eb54d4e..6f51827 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -345,5 +345,7 @@ "add_instance": "Add instance", "@add_instance": {}, "instance_added": "Instance successfully added", - "@instance_added": {} + "@instance_added": {}, + "required_field": "required field", + "@required_field": {} } diff --git a/lib/pages/community/community.dart b/lib/pages/community/community.dart index a648763..b6d63d7 100644 --- a/lib/pages/community/community.dart +++ b/lib/pages/community/community.dart @@ -16,7 +16,7 @@ import '../../util/share.dart'; import '../../widgets/failed_to_load.dart'; import '../../widgets/reveal_after_scroll.dart'; import '../../widgets/sortable_infinite_list.dart'; -import '../create_post.dart'; +import '../create_post/create_post_fab.dart'; import 'community_about_tab.dart'; import 'community_more_menu.dart'; import 'community_overview.dart'; diff --git a/lib/pages/create_post/create_post_fab.dart b/lib/pages/create_post/create_post_fab.dart new file mode 100644 index 0000000..26c190f --- /dev/null +++ b/lib/pages/create_post/create_post_fab.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:lemmy_api_client/v3.dart'; + +import '../../hooks/logged_in_action.dart'; +import '../full_post/full_post.dart'; +import 'create_post.dart'; + +/// Fab that triggers the [CreatePost] modal +/// After creation it will navigate to the newly created post +class CreatePostFab extends HookWidget { + final CommunityView? community; + + const CreatePostFab({this.community}); + + @override + Widget build(BuildContext context) { + final loggedInAction = useAnyLoggedInAction(); + + return FloatingActionButton( + onPressed: loggedInAction((_) async { + final postView = await Navigator.of(context).push( + community == null + ? CreatePostPage.route() + : CreatePostPage.toCommunityRoute(community!), + ); + + if (postView != null) { + await Navigator.of(context) + .push(FullPostPage.fromPostViewRoute(postView)); + } + }), + child: const Icon(Icons.add), + ); + } +} diff --git a/lib/pages/create_post/create_post_instance_picker.dart b/lib/pages/create_post/create_post_instance_picker.dart new file mode 100644 index 0000000..641aa50 --- /dev/null +++ b/lib/pages/create_post/create_post_instance_picker.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import '../../stores/accounts_store.dart'; +import '../../util/observer_consumers.dart'; +import '../../widgets/radio_picker.dart'; +import 'create_post_store.dart'; + +class CreatePostInstancePicker extends StatelessWidget { + const CreatePostInstancePicker({Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final loggedInInstances = + context.watch().loggedInInstances.toList(); + + return ObserverBuilder( + builder: (context, store) => RadioPicker( + values: loggedInInstances, + groupValue: store.instanceHost, + onChanged: store.isEdit ? null : (value) => store.instanceHost = value, + buttonBuilder: (context, displayValue, onPressed) => TextButton( + onPressed: onPressed, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(displayValue), + const Icon(Icons.arrow_drop_down), + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/create_post/create_post_store.dart b/lib/pages/create_post/create_post_store.dart new file mode 100644 index 0000000..a386d18 --- /dev/null +++ b/lib/pages/create_post/create_post_store.dart @@ -0,0 +1,63 @@ +import 'package:lemmy_api_client/v3.dart'; +import 'package:mobx/mobx.dart'; + +import '../../util/async_store.dart'; + +part 'create_post_store.g.dart'; + +class CreatePostStore = _CreatePostStore with _$CreatePostStore; + +abstract class _CreatePostStore with Store { + final Post? postToEdit; + bool get isEdit => postToEdit != null; + + _CreatePostStore({ + required this.instanceHost, + this.postToEdit, + this.selectedCommunity, + }) : title = postToEdit?.name ?? '', + nsfw = postToEdit?.nsfw ?? false, + body = postToEdit?.body ?? '', + url = postToEdit?.url ?? ''; + + @observable + bool showFancy = false; + @observable + String instanceHost; + @observable + CommunityView? selectedCommunity; + @observable + String url; + @observable + String title; + @observable + String body; + @observable + bool nsfw; + + final submitState = AsyncStore(); + + @action + Future submit(Jwt token) async { + await submitState.runLemmy( + instanceHost, + isEdit + ? EditPost( + url: url.isEmpty ? null : url, + body: body.isEmpty ? null : body, + nsfw: nsfw, + name: title, + postId: postToEdit!.id, + auth: token.raw, + ) + : CreatePost( + url: url.isEmpty ? null : url, + body: body.isEmpty ? null : body, + nsfw: nsfw, + name: title, + communityId: selectedCommunity!.community.id, + auth: token.raw, + ), + ); + } +} diff --git a/lib/pages/create_post/create_post_store.g.dart b/lib/pages/create_post/create_post_store.g.dart new file mode 100644 index 0000000..3c4c81e --- /dev/null +++ b/lib/pages/create_post/create_post_store.g.dart @@ -0,0 +1,137 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'create_post_store.dart'; + +// ************************************************************************** +// StoreGenerator +// ************************************************************************** + +// ignore_for_file: non_constant_identifier_names, unnecessary_brace_in_string_interps, unnecessary_lambdas, prefer_expression_function_bodies, lines_longer_than_80_chars, avoid_as, avoid_annotating_with_dynamic + +mixin _$CreatePostStore on _CreatePostStore, Store { + final _$showFancyAtom = Atom(name: '_CreatePostStore.showFancy'); + + @override + bool get showFancy { + _$showFancyAtom.reportRead(); + return super.showFancy; + } + + @override + set showFancy(bool value) { + _$showFancyAtom.reportWrite(value, super.showFancy, () { + super.showFancy = value; + }); + } + + final _$instanceHostAtom = Atom(name: '_CreatePostStore.instanceHost'); + + @override + String get instanceHost { + _$instanceHostAtom.reportRead(); + return super.instanceHost; + } + + @override + set instanceHost(String value) { + _$instanceHostAtom.reportWrite(value, super.instanceHost, () { + super.instanceHost = value; + }); + } + + final _$selectedCommunityAtom = + Atom(name: '_CreatePostStore.selectedCommunity'); + + @override + CommunityView? get selectedCommunity { + _$selectedCommunityAtom.reportRead(); + return super.selectedCommunity; + } + + @override + set selectedCommunity(CommunityView? value) { + _$selectedCommunityAtom.reportWrite(value, super.selectedCommunity, () { + super.selectedCommunity = value; + }); + } + + final _$urlAtom = Atom(name: '_CreatePostStore.url'); + + @override + String get url { + _$urlAtom.reportRead(); + return super.url; + } + + @override + set url(String value) { + _$urlAtom.reportWrite(value, super.url, () { + super.url = value; + }); + } + + final _$titleAtom = Atom(name: '_CreatePostStore.title'); + + @override + String get title { + _$titleAtom.reportRead(); + return super.title; + } + + @override + set title(String value) { + _$titleAtom.reportWrite(value, super.title, () { + super.title = value; + }); + } + + final _$bodyAtom = Atom(name: '_CreatePostStore.body'); + + @override + String get body { + _$bodyAtom.reportRead(); + return super.body; + } + + @override + set body(String value) { + _$bodyAtom.reportWrite(value, super.body, () { + super.body = value; + }); + } + + final _$nsfwAtom = Atom(name: '_CreatePostStore.nsfw'); + + @override + bool get nsfw { + _$nsfwAtom.reportRead(); + return super.nsfw; + } + + @override + set nsfw(bool value) { + _$nsfwAtom.reportWrite(value, super.nsfw, () { + super.nsfw = value; + }); + } + + final _$submitAsyncAction = AsyncAction('_CreatePostStore.submit'); + + @override + Future submit(Jwt token) { + return _$submitAsyncAction.run(() => super.submit(token)); + } + + @override + String toString() { + return ''' +showFancy: ${showFancy}, +instanceHost: ${instanceHost}, +selectedCommunity: ${selectedCommunity}, +url: ${url}, +title: ${title}, +body: ${body}, +nsfw: ${nsfw} + '''; + } +} diff --git a/lib/pages/home_page.dart b/lib/pages/home_page.dart index 79794dd..6b3ecb5 100644 --- a/lib/pages/home_page.dart +++ b/lib/pages/home_page.dart @@ -4,7 +4,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import '../util/extensions/brightness.dart'; import 'communities_tab.dart'; -import 'create_post.dart'; +import 'create_post/create_post_fab.dart'; import 'home_tab.dart'; import 'profile_tab.dart'; import 'search_tab.dart'; diff --git a/lib/util/async_store_listener.dart b/lib/util/async_store_listener.dart index a087710..88e4286 100644 --- a/lib/util/async_store_listener.dart +++ b/lib/util/async_store_listener.dart @@ -12,10 +12,16 @@ class AsyncStoreListener extends SingleChildStatelessWidget { T data, )? successMessageBuilder; + final void Function( + BuildContext context, + T data, + )? onSuccess; + const AsyncStoreListener({ Key? key, required this.asyncStore, this.successMessageBuilder, + this.onSuccess, Widget? child, }) : super(key: key, child: child); @@ -24,20 +30,27 @@ class AsyncStoreListener extends SingleChildStatelessWidget { return ObserverListener>( store: asyncStore, listener: (context, store) { - final errorTerm = store.errorTerm; + store.map( + loading: () {}, + error: (errorTerm) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar(SnackBar(content: Text(errorTerm.tr(context)))); + }, + data: (data) { + onSuccess?.call(context, data); - if (errorTerm != null) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar(SnackBar(content: Text(errorTerm.tr(context)))); - } else if (store.asyncState is AsyncStateData && - (successMessageBuilder != null)) { - ScaffoldMessenger.of(context) - ..hideCurrentSnackBar() - ..showSnackBar(SnackBar( - content: Text(successMessageBuilder!( - context, (store.asyncState as AsyncStateData).data)))); - } + if (successMessageBuilder != null) { + ScaffoldMessenger.of(context) + ..hideCurrentSnackBar() + ..showSnackBar( + SnackBar( + content: Text(successMessageBuilder!(context, data)), + ), + ); + } + }, + ); }, child: child ?? const SizedBox(), ); diff --git a/lib/widgets/editor.dart b/lib/widgets/editor.dart index af6e29b..401161e 100644 --- a/lib/widgets/editor.dart +++ b/lib/widgets/editor.dart @@ -8,6 +8,7 @@ class Editor extends HookWidget { final TextEditingController? controller; final FocusNode? focusNode; final ValueChanged? onSubmitted; + final ValueChanged? onChanged; final int? minLines; final int? maxLines; final String? labelText; @@ -22,6 +23,7 @@ class Editor extends HookWidget { this.controller, this.focusNode, this.onSubmitted, + this.onChanged, this.minLines = 5, this.maxLines, this.labelText, @@ -51,6 +53,7 @@ class Editor extends HookWidget { autofocus: autofocus, keyboardType: TextInputType.multiline, textCapitalization: TextCapitalization.sentences, + onChanged: onChanged, onSubmitted: onSubmitted, maxLines: maxLines, minLines: minLines, diff --git a/lib/widgets/post/post_more_menu.dart b/lib/widgets/post/post_more_menu.dart index 5a3720a..1743f3d 100644 --- a/lib/widgets/post/post_more_menu.dart +++ b/lib/widgets/post/post_more_menu.dart @@ -3,7 +3,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:url_launcher/url_launcher.dart' as ul; import '../../hooks/logged_in_action.dart'; -import '../../pages/create_post.dart'; +import '../../pages/create_post/create_post.dart'; import '../../pages/full_post/full_post_store.dart'; import '../../stores/accounts_store.dart'; import '../../util/icons.dart'; diff --git a/pubspec.lock b/pubspec.lock index f16fa8f..b216c05 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -985,6 +985,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.1" + wc_form_validators: + dependency: "direct main" + description: + name: wc_form_validators + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.0" web_socket_channel: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 33cb380..85d77d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -60,6 +60,7 @@ dependencies: sdk: flutter flutter_localizations: sdk: flutter + wc_form_validators: ^1.0.0 dev_dependencies: flutter_test: