Fix minor post errors (#273)
* Use nested to decrease indentation * Fix post tap area * Fix overflow errors * Add changelog entry
This commit is contained in:
parent
18b39be0a9
commit
2b72fcce10
|
@ -10,6 +10,7 @@
|
|||
|
||||
- Fixed a bug where post would go out of sync with full version of the post
|
||||
- Fixed a bug where making a comment selectable would not always result in making the comment selectable
|
||||
- Full post will now open no matter where you press on the post card
|
||||
|
||||
## v0.6.0 - 2021-09-06
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:lemmy_api_client/v3.dart';
|
||||
import 'package:nested/nested.dart';
|
||||
|
||||
import '../../hooks/logged_in_action.dart';
|
||||
import '../../stores/accounts_store.dart';
|
||||
|
@ -33,101 +34,106 @@ class FullPostPage extends HookWidget {
|
|||
final loggedInAction =
|
||||
useLoggedInAction(context.read<FullPostStore>().instanceHost);
|
||||
|
||||
return AsyncStoreListener(
|
||||
asyncStore: context.read<FullPostStore>().fullPostState,
|
||||
child: AsyncStoreListener<BlockedCommunity>(
|
||||
asyncStore: context.read<FullPostStore>().communityBlockingState,
|
||||
successMessageBuilder: (context, data) {
|
||||
final name = data.communityView.community.originPreferredName;
|
||||
return '${data.blocked ? 'Blocked' : 'Unblocked'} $name';
|
||||
},
|
||||
child: ObserverBuilder<FullPostStore>(
|
||||
builder: (context, store) {
|
||||
Future<void> refresh() async {
|
||||
unawaited(HapticFeedback.mediumImpact());
|
||||
await store.refresh(context
|
||||
.read<AccountsStore>()
|
||||
.defaultUserDataFor(store.instanceHost)
|
||||
?.jwt);
|
||||
}
|
||||
|
||||
final postStore = store.postStore;
|
||||
|
||||
if (postStore == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Center(
|
||||
child: (store.fullPostState.isLoading)
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: FailedToLoad(
|
||||
message: 'Post failed to load', refresh: refresh),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final post = postStore.postView;
|
||||
|
||||
// VARIABLES
|
||||
|
||||
sharePost() => share(post.post.apId, context: context);
|
||||
|
||||
comment() async {
|
||||
final newComment = await Navigator.of(context).push(
|
||||
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.value(
|
||||
value: postStore,
|
||||
child: const SavePostButton(),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(moreIcon),
|
||||
onPressed: () => PostMoreMenuButton.show(
|
||||
context: context,
|
||||
postStore: postStore,
|
||||
fullPostStore: store,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: post.post.locked
|
||||
? null
|
||||
: FloatingActionButton(
|
||||
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(postStore),
|
||||
const CommentSection(),
|
||||
],
|
||||
),
|
||||
));
|
||||
return Nested(
|
||||
children: [
|
||||
AsyncStoreListener(
|
||||
asyncStore: context.read<FullPostStore>().fullPostState,
|
||||
),
|
||||
AsyncStoreListener<BlockedCommunity>(
|
||||
asyncStore: context.read<FullPostStore>().communityBlockingState,
|
||||
successMessageBuilder: (context, data) {
|
||||
final name = data.communityView.community.originPreferredName;
|
||||
return '${data.blocked ? 'Blocked' : 'Unblocked'} $name';
|
||||
},
|
||||
),
|
||||
],
|
||||
child: ObserverBuilder<FullPostStore>(
|
||||
builder: (context, store) {
|
||||
Future<void> refresh() async {
|
||||
unawaited(HapticFeedback.mediumImpact());
|
||||
await store.refresh(context
|
||||
.read<AccountsStore>()
|
||||
.defaultUserDataFor(store.instanceHost)
|
||||
?.jwt);
|
||||
}
|
||||
|
||||
final postStore = store.postStore;
|
||||
|
||||
if (postStore == null) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Center(
|
||||
child: (store.fullPostState.isLoading)
|
||||
? const CircularProgressIndicator.adaptive()
|
||||
: FailedToLoad(
|
||||
message: 'Post failed to load', refresh: refresh),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final post = postStore.postView;
|
||||
|
||||
// VARIABLES
|
||||
|
||||
sharePost() => share(post.post.apId, context: context);
|
||||
|
||||
comment() async {
|
||||
final newComment = await Navigator.of(context).push(
|
||||
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.value(
|
||||
value: postStore,
|
||||
child: const SavePostButton(),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(moreIcon),
|
||||
onPressed: () => PostMoreMenuButton.show(
|
||||
context: context,
|
||||
postStore: postStore,
|
||||
fullPostStore: store,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: post.post.locked
|
||||
? null
|
||||
: FloatingActionButton(
|
||||
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(postStore),
|
||||
const CommentSection(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,27 +1,26 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/single_child_widget.dart';
|
||||
|
||||
import '../l10n/l10n_from_string.dart';
|
||||
import 'async_store.dart';
|
||||
import 'observer_consumers.dart';
|
||||
|
||||
class AsyncStoreListener<T> extends StatelessWidget {
|
||||
class AsyncStoreListener<T> extends SingleChildStatelessWidget {
|
||||
final AsyncStore<T> asyncStore;
|
||||
final String Function(
|
||||
BuildContext context,
|
||||
T data,
|
||||
)? successMessageBuilder;
|
||||
final Widget child;
|
||||
|
||||
const AsyncStoreListener({
|
||||
Key? key,
|
||||
required this.asyncStore,
|
||||
this.successMessageBuilder,
|
||||
required this.child,
|
||||
}) : super(key: key);
|
||||
Widget? child,
|
||||
}) : super(key: key, child: child);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget buildWithChild(BuildContext context, Widget? child) {
|
||||
return ObserverListener<AsyncStore<T>>(
|
||||
store: asyncStore,
|
||||
listener: (context, store) {
|
||||
|
@ -40,7 +39,7 @@ class AsyncStoreListener<T> extends StatelessWidget {
|
|||
context, (store.asyncState as AsyncStateData).data))));
|
||||
}
|
||||
},
|
||||
child: child,
|
||||
child: child ?? const SizedBox(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:math';
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lemmy_api_client/v3.dart';
|
||||
import 'package:nested/nested.dart';
|
||||
|
||||
import '../../comment_tree.dart';
|
||||
import '../../l10n/l10n.dart';
|
||||
|
@ -89,26 +90,30 @@ class CommentWidget extends StatelessWidget {
|
|||
detached: detached,
|
||||
hideOnRead: hideOnRead,
|
||||
),
|
||||
builder: (context, child) => AsyncStoreListener<BlockedPerson>(
|
||||
asyncStore: context.read<CommentStore>().blockingState,
|
||||
successMessageBuilder: (context, state) {
|
||||
final name = state.personView.person.preferredName;
|
||||
return state.blocked ? '$name blocked' : '$name unblocked';
|
||||
},
|
||||
child: AsyncStoreListener(
|
||||
asyncStore: context.read<CommentStore>().votingState,
|
||||
child: AsyncStoreListener(
|
||||
asyncStore: context.read<CommentStore>().deletingState,
|
||||
child: AsyncStoreListener(
|
||||
asyncStore: context.read<CommentStore>().savingState,
|
||||
child: AsyncStoreListener<CommentReportView>(
|
||||
asyncStore: context.read<CommentStore>().reportingState,
|
||||
successMessageBuilder: (context, data) => 'Comment reported',
|
||||
child: const _CommentWidget(),
|
||||
),
|
||||
),
|
||||
builder: (context, child) => Nested(
|
||||
children: [
|
||||
AsyncStoreListener<BlockedPerson>(
|
||||
asyncStore: context.read<CommentStore>().blockingState,
|
||||
successMessageBuilder: (context, state) {
|
||||
final name = state.personView.person.preferredName;
|
||||
return state.blocked ? '$name blocked' : '$name unblocked';
|
||||
},
|
||||
),
|
||||
),
|
||||
AsyncStoreListener(
|
||||
asyncStore: context.read<CommentStore>().votingState,
|
||||
),
|
||||
AsyncStoreListener(
|
||||
asyncStore: context.read<CommentStore>().deletingState,
|
||||
),
|
||||
AsyncStoreListener(
|
||||
asyncStore: context.read<CommentStore>().savingState,
|
||||
),
|
||||
AsyncStoreListener<CommentReportView>(
|
||||
asyncStore: context.read<CommentStore>().reportingState,
|
||||
successMessageBuilder: (context, data) => 'Comment reported',
|
||||
),
|
||||
],
|
||||
child: const _CommentWidget(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:lemmy_api_client/v3.dart';
|
||||
import 'package:nested/nested.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../../pages/full_post/full_post.dart';
|
||||
|
@ -25,31 +26,25 @@ class PostTile extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
return Nested(
|
||||
children: [
|
||||
Provider.value(value: postStore),
|
||||
Provider.value(value: fullPost),
|
||||
AsyncStoreListener(asyncStore: postStore.savingState),
|
||||
AsyncStoreListener(asyncStore: postStore.votingState),
|
||||
AsyncStoreListener<BlockedPerson>(
|
||||
asyncStore: postStore.userBlockingState,
|
||||
successMessageBuilder: (context, state) {
|
||||
final name = state.personView.person.preferredName;
|
||||
return state.blocked ? '$name blocked' : '$name unblocked';
|
||||
},
|
||||
),
|
||||
AsyncStoreListener<PostReportView>(
|
||||
asyncStore: postStore.reportingState,
|
||||
successMessageBuilder: (context, data) => 'Post reported',
|
||||
),
|
||||
],
|
||||
builder: (context, child) {
|
||||
return AsyncStoreListener(
|
||||
asyncStore: context.read<PostStore>().savingState,
|
||||
child: AsyncStoreListener(
|
||||
asyncStore: context.read<PostStore>().votingState,
|
||||
child: AsyncStoreListener<BlockedPerson>(
|
||||
asyncStore: context.read<PostStore>().userBlockingState,
|
||||
successMessageBuilder: (context, state) {
|
||||
final name = state.personView.person.preferredName;
|
||||
return state.blocked ? '$name blocked' : '$name unblocked';
|
||||
},
|
||||
child: AsyncStoreListener<PostReportView>(
|
||||
asyncStore: context.read<PostStore>().reportingState,
|
||||
successMessageBuilder: (context, data) => 'Post reported',
|
||||
child: const _Post(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: const _Post(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -63,20 +58,20 @@ class _Post extends StatelessWidget {
|
|||
final theme = Theme.of(context);
|
||||
final isFullPost = context.read<IsFullPost>();
|
||||
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: const [BoxShadow(blurRadius: 10, color: Colors.black45)],
|
||||
color: theme.cardColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: GestureDetector(
|
||||
onTap: isFullPost
|
||||
? null
|
||||
: () {
|
||||
final postStore = context.read<PostStore>();
|
||||
Navigator.of(context)
|
||||
.push(FullPostPage.fromPostStoreRoute(postStore));
|
||||
},
|
||||
return GestureDetector(
|
||||
onTap: isFullPost
|
||||
? null
|
||||
: () {
|
||||
final postStore = context.read<PostStore>();
|
||||
Navigator.of(context)
|
||||
.push(FullPostPage.fromPostStoreRoute(postStore));
|
||||
},
|
||||
child: DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
boxShadow: const [BoxShadow(blurRadius: 10, color: Colors.black45)],
|
||||
color: theme.cardColor,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
child: Material(
|
||||
type: MaterialType.transparency,
|
||||
child: Column(
|
||||
|
|
|
@ -30,12 +30,9 @@ class PostBody extends StatelessWidget {
|
|||
} else {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final span = TextSpan(
|
||||
text: body,
|
||||
);
|
||||
final tp = TextPainter(
|
||||
text: span,
|
||||
maxLines: 10,
|
||||
text: TextSpan(text: body),
|
||||
maxLines: 8,
|
||||
textDirection: Directionality.of(context),
|
||||
)..layout(maxWidth: constraints.maxWidth - 20);
|
||||
|
||||
|
@ -43,29 +40,30 @@ class PostBody extends StatelessWidget {
|
|||
return ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: tp.height),
|
||||
child: Stack(
|
||||
alignment: Alignment.bottomCenter,
|
||||
children: [
|
||||
ClipRect(
|
||||
child: Align(
|
||||
alignment: Alignment.topCenter,
|
||||
heightFactor: 0.8,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: MarkdownText(body,
|
||||
instanceHost: store.postView.instanceHost),
|
||||
Positioned.fill(
|
||||
bottom: null,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: MarkdownText(
|
||||
body,
|
||||
instanceHost: store.postView.instanceHost,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: tp.preferredLineHeight * 2.5,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
theme.cardColor.withAlpha(0),
|
||||
theme.cardColor,
|
||||
],
|
||||
Positioned.fill(
|
||||
top: null,
|
||||
child: Container(
|
||||
height: tp.preferredLineHeight * 2.5,
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: [
|
||||
theme.cardColor.withAlpha(0),
|
||||
theme.cardColor,
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -74,9 +72,12 @@ class PostBody extends StatelessWidget {
|
|||
);
|
||||
} else {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: MarkdownText(body,
|
||||
instanceHost: store.postView.instanceHost));
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: MarkdownText(
|
||||
body,
|
||||
instanceHost: store.postView.instanceHost,
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -505,7 +505,7 @@ packages:
|
|||
source: hosted
|
||||
version: "2.0.0"
|
||||
nested:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: nested
|
||||
url: "https://pub.dartlang.org"
|
||||
|
|
|
@ -52,6 +52,7 @@ dependencies:
|
|||
keyboard_dismisser: ^2.0.0
|
||||
freezed_annotation: ^0.15.0
|
||||
logging: ^1.0.1
|
||||
nested: ^1.0.0
|
||||
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
|
Loading…
Reference in New Issue