batch of changes adressing CR
comment_section.dart * _SortSelection: text -> term * assign fullPostView at the beggining of builder for later ease of use full_post.dart * use Providers in route methods since it makes the most sense * merge _FPP & FPP to one widget * assign postView earlier for ease of use * Provider -> Provider.value goto.dart * switch goToPost to use Navigator with route instead of goTo post_actions.dart * padding fromLTRB -> symmetric post_more_menu.dart * turn showPostMoreMenu into a static method * Provider -> Provider.value * Hook -> Stateless * goTo -> Navigator.push
This commit is contained in:
parent
d223d71c59
commit
a884f07860
|
@ -13,9 +13,9 @@ import 'full_post_store.dart';
|
||||||
|
|
||||||
class _SortSelection {
|
class _SortSelection {
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String text;
|
final String term;
|
||||||
|
|
||||||
const _SortSelection(this.icon, this.text);
|
const _SortSelection(this.icon, this.term);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Manages comments section, sorts them
|
/// Manages comments section, sorts them
|
||||||
|
@ -34,8 +34,10 @@ class CommentSection extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return ObserverBuilder<FullPostStore>(
|
return ObserverBuilder<FullPostStore>(
|
||||||
builder: (context, store) {
|
builder: (context, store) {
|
||||||
|
final fullPostView = store.fullPostView;
|
||||||
|
|
||||||
// error & spinner handling
|
// error & spinner handling
|
||||||
if (store.fullPostView == null) {
|
if (fullPostView == null) {
|
||||||
if (store.fullPostState.errorTerm != null) {
|
if (store.fullPostState.errorTerm != null) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 30),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 30),
|
||||||
|
@ -49,7 +51,7 @@ class CommentSection extends StatelessWidget {
|
||||||
} else {
|
} else {
|
||||||
return const Padding(
|
return const Padding(
|
||||||
padding: EdgeInsets.only(top: 40),
|
padding: EdgeInsets.only(top: 40),
|
||||||
child: Center(child: CircularProgressIndicator()),
|
child: Center(child: CircularProgressIndicator.adaptive()),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +72,7 @@ class CommentSection extends StatelessWidget {
|
||||||
for (final e in sortPairs.entries)
|
for (final e in sortPairs.entries)
|
||||||
ListTile(
|
ListTile(
|
||||||
leading: Icon(e.value.icon),
|
leading: Icon(e.value.icon),
|
||||||
title: Text((e.value.text).tr(context)),
|
title: Text((e.value.term).tr(context)),
|
||||||
trailing: store.sorting == e.key
|
trailing: store.sorting == e.key
|
||||||
? const Icon(Icons.check)
|
? const Icon(Icons.check)
|
||||||
: null,
|
: null,
|
||||||
|
@ -85,7 +87,7 @@ class CommentSection extends StatelessWidget {
|
||||||
},
|
},
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text((sortPairs[store.sorting]!.text).tr(context)),
|
Text((sortPairs[store.sorting]!.term).tr(context)),
|
||||||
const Icon(Icons.arrow_drop_down),
|
const Icon(Icons.arrow_drop_down),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -95,7 +97,7 @@ class CommentSection extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
// sorting menu goes here
|
// sorting menu goes here
|
||||||
if (store.fullPostView!.comments.isEmpty)
|
if (fullPostView.comments.isEmpty)
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 50),
|
padding: EdgeInsets.symmetric(vertical: 50),
|
||||||
child: Text(
|
child: Text(
|
||||||
|
@ -111,7 +113,7 @@ class CommentSection extends StatelessWidget {
|
||||||
key: ValueKey(com),
|
key: ValueKey(com),
|
||||||
),
|
),
|
||||||
if (store.sorting == CommentSortType.chat)
|
if (store.sorting == CommentSortType.chat)
|
||||||
for (final com in store.fullPostView!.comments)
|
for (final com in fullPostView.comments)
|
||||||
CommentWidget.fromCommentView(
|
CommentWidget.fromCommentView(
|
||||||
com,
|
com,
|
||||||
detached: false,
|
detached: false,
|
||||||
|
|
|
@ -29,8 +29,10 @@ class FullPostPage extends StatelessWidget {
|
||||||
final PostView? postView;
|
final PostView? postView;
|
||||||
final PostStore? postStore;
|
final PostStore? postStore;
|
||||||
|
|
||||||
const FullPostPage({required int this.id, required String this.instanceHost})
|
const FullPostPage({
|
||||||
: postView = null,
|
required int this.id,
|
||||||
|
required String this.instanceHost,
|
||||||
|
}) : postView = null,
|
||||||
postStore = null;
|
postStore = null;
|
||||||
const FullPostPage.fromPostView(PostView this.postView)
|
const FullPostPage.fromPostView(PostView this.postView)
|
||||||
: id = null,
|
: id = null,
|
||||||
|
@ -43,29 +45,52 @@ class FullPostPage extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Provider(
|
return AsyncStoreListener(
|
||||||
create: (context) {
|
asyncStore: context.read<FullPostStore>().fullPostState,
|
||||||
if (postStore != null) {
|
child: AsyncStoreListener<BlockedCommunity>(
|
||||||
return FullPostStore.fromPostStore(postStore!);
|
asyncStore: context.read<FullPostStore>().communityBlockingState,
|
||||||
} else if (postView != null) {
|
successMessageBuilder: (context, data) {
|
||||||
return FullPostStore.fromPostView(postView!);
|
final name = data.communityView.community.originPreferredName;
|
||||||
} else {
|
return '${data.blocked ? 'Blocked' : 'Unblocked'} $name';
|
||||||
return FullPostStore(instanceHost: instanceHost!, postId: id!);
|
},
|
||||||
}
|
child: const _FullPostPage(),
|
||||||
},
|
|
||||||
builder: (context, store) => AsyncStoreListener(
|
|
||||||
asyncStore: context.read<FullPostStore>().fullPostState,
|
|
||||||
child: AsyncStoreListener<BlockedCommunity>(
|
|
||||||
asyncStore: context.read<FullPostStore>().communityBlockingState,
|
|
||||||
successMessageBuilder: (context, asyncStore) {
|
|
||||||
final name = asyncStore.communityView.community.originPreferredName;
|
|
||||||
return '${asyncStore.blocked ? 'Blocked' : 'Unblocked'} $name';
|
|
||||||
},
|
|
||||||
child: const _FullPostPage(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Jwt? _tryGetJwt(BuildContext context) {
|
||||||
|
final store = context.read<FullPostStore>();
|
||||||
|
return context
|
||||||
|
.read<AccountsStore>()
|
||||||
|
.defaultUserDataFor(store.instanceHost)
|
||||||
|
?.jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Route route(int id, String instanceHost) => MaterialPageRoute(
|
||||||
|
builder: (context) => Provider(
|
||||||
|
create: (context) =>
|
||||||
|
FullPostStore(instanceHost: instanceHost, postId: id)
|
||||||
|
..refresh(_tryGetJwt(context)),
|
||||||
|
child: FullPostPage(
|
||||||
|
id: id,
|
||||||
|
instanceHost: instanceHost,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
static Route fromPostViewRoute(PostView postView) => MaterialPageRoute(
|
||||||
|
builder: (context) => Provider(
|
||||||
|
create: (context) => FullPostStore.fromPostView(postView)
|
||||||
|
..refresh(_tryGetJwt(context)),
|
||||||
|
child: FullPostPage.fromPostView(postView),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
static Route fromPostStoreRoute(PostStore postStore) => MaterialPageRoute(
|
||||||
|
builder: (context) => Provider(
|
||||||
|
create: (context) => FullPostStore.fromPostStore(postStore)
|
||||||
|
..refresh(_tryGetJwt(context)),
|
||||||
|
child: FullPostPage.fromPostStore(postStore)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays a post with its comment section
|
/// Displays a post with its comment section
|
||||||
|
@ -76,102 +101,103 @@ class _FullPostPage extends HookWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final scrollController = useScrollController();
|
final scrollController = useScrollController();
|
||||||
|
|
||||||
useMemoized(() {
|
|
||||||
final store = context.read<FullPostStore>();
|
|
||||||
final token = context
|
|
||||||
.read<AccountsStore>()
|
|
||||||
.defaultUserDataFor(store.instanceHost)
|
|
||||||
?.jwt;
|
|
||||||
store.refresh(token);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
final loggedInAction =
|
final loggedInAction =
|
||||||
useLoggedInAction(context.read<FullPostStore>().instanceHost);
|
useLoggedInAction(context.read<FullPostStore>().instanceHost);
|
||||||
|
|
||||||
return ObserverBuilder<FullPostStore>(
|
return AsyncStoreListener(
|
||||||
builder: (context, store) {
|
asyncStore: context.read<FullPostStore>().fullPostState,
|
||||||
Future<void> refresh() async {
|
child: AsyncStoreListener<BlockedCommunity>(
|
||||||
unawaited(HapticFeedback.mediumImpact());
|
asyncStore: context.read<FullPostStore>().communityBlockingState,
|
||||||
await store.refresh(context
|
successMessageBuilder: (context, data) {
|
||||||
.read<AccountsStore>()
|
final name = data.communityView.community.originPreferredName;
|
||||||
.defaultUserDataFor(store.instanceHost)
|
return '${data.blocked ? 'Blocked' : 'Unblocked'} $name';
|
||||||
?.jwt);
|
},
|
||||||
}
|
child: ObserverBuilder<FullPostStore>(
|
||||||
|
builder: (context, store) {
|
||||||
|
Future<void> refresh() async {
|
||||||
|
unawaited(HapticFeedback.mediumImpact());
|
||||||
|
await store.refresh(context
|
||||||
|
.read<AccountsStore>()
|
||||||
|
.defaultUserDataFor(store.instanceHost)
|
||||||
|
?.jwt);
|
||||||
|
}
|
||||||
|
|
||||||
if (store.postView == null) {
|
final post = store.postView;
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(),
|
|
||||||
body: Center(
|
|
||||||
child: (store.fullPostState.isLoading)
|
|
||||||
? const CircularProgressIndicator.adaptive()
|
|
||||||
: FailedToLoad(
|
|
||||||
message: 'Post failed to load', refresh: refresh),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// VARIABLES
|
if (post == null) {
|
||||||
|
return Scaffold(
|
||||||
final post = store.postView!;
|
appBar: AppBar(),
|
||||||
|
body: Center(
|
||||||
sharePost() => share(post.post.apId, context: context);
|
child: (store.fullPostState.isLoading)
|
||||||
|
? const CircularProgressIndicator.adaptive()
|
||||||
comment() async {
|
: FailedToLoad(
|
||||||
final newComment = await Navigator.of(context).push(
|
message: 'Post failed to load', refresh: refresh),
|
||||||
WriteComment.toPostRoute(post.post),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (newComment != null) {
|
|
||||||
store.addComment(newComment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
centerTitle: false,
|
|
||||||
title: RevealAfterScroll(
|
|
||||||
scrollController: scrollController,
|
|
||||||
after: 65,
|
|
||||||
child: Text(
|
|
||||||
post.community.originPreferredName,
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
actions: [
|
}
|
||||||
IconButton(icon: Icon(shareIcon), onPressed: sharePost),
|
|
||||||
Provider<PostStore>(
|
// VARIABLES
|
||||||
create: (context) => store.postStore!,
|
|
||||||
child: const SavePostButton(),
|
sharePost() => share(post.post.apId, context: context);
|
||||||
),
|
|
||||||
IconButton(
|
comment() async {
|
||||||
icon: Icon(moreIcon),
|
final newComment = await Navigator.of(context).push(
|
||||||
onPressed: () => showPostMoreMenu(
|
WriteComment.toPostRoute(post.post),
|
||||||
context: context,
|
);
|
||||||
postStore: store.postStore!,
|
|
||||||
fullPostStore: store,
|
if (newComment != null) {
|
||||||
|
store.addComment(newComment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
centerTitle: false,
|
||||||
|
title: RevealAfterScroll(
|
||||||
|
scrollController: scrollController,
|
||||||
|
after: 65,
|
||||||
|
child: Text(
|
||||||
|
post.community.originPreferredName,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
actions: [
|
||||||
|
IconButton(icon: Icon(shareIcon), onPressed: sharePost),
|
||||||
|
Provider.value(
|
||||||
|
value: store.postStore!,
|
||||||
|
child: const SavePostButton(),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
icon: Icon(moreIcon),
|
||||||
|
onPressed: () => PostMoreMenuButton.show(
|
||||||
|
context: context,
|
||||||
|
postStore: store.postStore!,
|
||||||
|
fullPostStore: store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
floatingActionButton: post.post.locked
|
||||||
),
|
? null
|
||||||
floatingActionButton: post.post.locked
|
: FloatingActionButton(
|
||||||
? null
|
onPressed: loggedInAction((_) => comment()),
|
||||||
: FloatingActionButton(
|
child: const Icon(Icons.comment),
|
||||||
onPressed: loggedInAction((_) => comment()),
|
),
|
||||||
child: const Icon(Icons.comment),
|
body: RefreshIndicator(
|
||||||
|
onRefresh: refresh,
|
||||||
|
child: ListView(
|
||||||
|
controller: scrollController,
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
PostTile.fromPostStore(store.postStore!),
|
||||||
|
const CommentSection(),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
));
|
||||||
onRefresh: refresh,
|
},
|
||||||
child: ListView(
|
),
|
||||||
controller: scrollController,
|
),
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
|
||||||
children: [
|
|
||||||
const SizedBox(height: 15),
|
|
||||||
PostTile.fromPostStore(store.postStore!),
|
|
||||||
const CommentSection(),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
));
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,8 +62,8 @@ abstract class goToUser {
|
||||||
goToUser.byId(context, personSafe.instanceHost, personSafe.id);
|
goToUser.byId(context, personSafe.instanceHost, personSafe.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void goToPost(BuildContext context, String instanceHost, int postId) => goTo(
|
void goToPost(BuildContext context, String instanceHost, int postId) =>
|
||||||
context, (context) => FullPostPage(instanceHost: instanceHost, id: postId));
|
Navigator.of(context).push(FullPostPage.route(postId, instanceHost));
|
||||||
|
|
||||||
void goToMedia(BuildContext context, String url) => Navigator.push(
|
void goToMedia(BuildContext context, String url) => Navigator.push(
|
||||||
context,
|
context,
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
|
||||||
import 'package:lemmy_api_client/v3.dart';
|
import 'package:lemmy_api_client/v3.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import '../../pages/full_post/full_post.dart';
|
import '../../pages/full_post/full_post.dart';
|
||||||
import '../../util/async_store_listener.dart';
|
import '../../util/async_store_listener.dart';
|
||||||
import '../../util/extensions/api.dart';
|
import '../../util/extensions/api.dart';
|
||||||
import '../../util/goto.dart';
|
|
||||||
import 'post_actions.dart';
|
import 'post_actions.dart';
|
||||||
import 'post_body.dart';
|
import 'post_body.dart';
|
||||||
import 'post_info_section.dart';
|
import 'post_info_section.dart';
|
||||||
|
@ -29,8 +27,8 @@ class PostTile extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MultiProvider(
|
return MultiProvider(
|
||||||
providers: [
|
providers: [
|
||||||
Provider<PostStore>(create: (_) => postStore),
|
Provider.value(value: postStore),
|
||||||
Provider<IsFullPost>(create: (_) => fullPost),
|
Provider.value(value: fullPost),
|
||||||
],
|
],
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
return AsyncStoreListener(
|
return AsyncStoreListener(
|
||||||
|
@ -53,7 +51,7 @@ class PostTile extends StatelessWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A post overview card
|
/// A post overview card
|
||||||
class _Post extends HookWidget {
|
class _Post extends StatelessWidget {
|
||||||
const _Post();
|
const _Post();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -72,10 +70,8 @@ class _Post extends HookWidget {
|
||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
final postStore = context.read<PostStore>();
|
final postStore = context.read<PostStore>();
|
||||||
goTo(
|
Navigator.of(context)
|
||||||
context,
|
.push(FullPostPage.fromPostStoreRoute(postStore));
|
||||||
(context) => FullPostPage.fromPostStore(postStore),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
child: Material(
|
child: Material(
|
||||||
type: MaterialType.transparency,
|
type: MaterialType.transparency,
|
||||||
|
|
|
@ -22,7 +22,7 @@ class PostActions extends HookWidget {
|
||||||
// assemble actions section
|
// assemble actions section
|
||||||
return ObserverBuilder<PostStore>(builder: (context, store) {
|
return ObserverBuilder<PostStore>(builder: (context, store) {
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
|
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
const Icon(Icons.comment),
|
const Icon(Icons.comment),
|
||||||
|
|
|
@ -17,18 +17,31 @@ import 'post_store.dart';
|
||||||
class PostMoreMenuButton extends StatelessWidget {
|
class PostMoreMenuButton extends StatelessWidget {
|
||||||
const PostMoreMenuButton();
|
const PostMoreMenuButton();
|
||||||
|
|
||||||
|
static void show({
|
||||||
|
required BuildContext context,
|
||||||
|
required PostStore postStore,
|
||||||
|
required FullPostStore? fullPostStore,
|
||||||
|
}) {
|
||||||
|
// TODO: add blocking!
|
||||||
|
showBottomModal(
|
||||||
|
context: context,
|
||||||
|
builder: (context) =>
|
||||||
|
PostMoreMenu(postStore: postStore, fullPostStore: fullPostStore),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
children: [
|
children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () => showPostMoreMenu(
|
onPressed: () => show(
|
||||||
context: context,
|
context: context,
|
||||||
postStore: context.read<PostStore>(),
|
postStore: context.read<PostStore>(),
|
||||||
fullPostStore: null,
|
fullPostStore: null,
|
||||||
),
|
),
|
||||||
icon: Icon(moreIcon),
|
icon: Icon(moreIcon),
|
||||||
padding: const EdgeInsets.all(0),
|
padding: EdgeInsets.zero,
|
||||||
visualDensity: VisualDensity.compact,
|
visualDensity: VisualDensity.compact,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -36,19 +49,6 @@ class PostMoreMenuButton extends StatelessWidget {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void showPostMoreMenu({
|
|
||||||
required BuildContext context,
|
|
||||||
required PostStore postStore,
|
|
||||||
required FullPostStore? fullPostStore,
|
|
||||||
}) {
|
|
||||||
// TODO: add blocking!
|
|
||||||
showBottomModal(
|
|
||||||
context: context,
|
|
||||||
builder: (context) =>
|
|
||||||
PostMoreMenu(postStore: postStore, fullPostStore: fullPostStore),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class PostMoreMenu extends HookWidget {
|
class PostMoreMenu extends HookWidget {
|
||||||
final PostStore postStore;
|
final PostStore postStore;
|
||||||
final FullPostStore? fullPostStore;
|
final FullPostStore? fullPostStore;
|
||||||
|
|
Loading…
Reference in New Issue