Merge branch 'master' into infinite-scroll
This commit is contained in:
commit
aa5b06a676
|
@ -0,0 +1,32 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:lemmy_api_client/lemmy_api_client.dart';
|
||||
|
||||
import '../pages/settings.dart';
|
||||
import '../util/goto.dart';
|
||||
import 'stores.dart';
|
||||
|
||||
Function(
|
||||
Function(Jwt token) action, [
|
||||
String message,
|
||||
]) useLoggedInAction(
|
||||
String instanceUrl,
|
||||
) {
|
||||
final context = useContext();
|
||||
final store = useAccountsStore();
|
||||
|
||||
return (Function(Jwt token) action, [message]) {
|
||||
if (store.isAnonymousFor(instanceUrl)) {
|
||||
return () {
|
||||
Scaffold.of(context).showSnackBar(SnackBar(
|
||||
content: Text(message ?? 'you have to be logged in to do that'),
|
||||
action: SnackBarAction(
|
||||
label: 'log in',
|
||||
onPressed: () => goTo(context, (_) => AccountsConfigPage())),
|
||||
));
|
||||
};
|
||||
}
|
||||
final token = store.defaultTokenFor(instanceUrl);
|
||||
return () => action(token);
|
||||
};
|
||||
}
|
|
@ -8,6 +8,7 @@ import 'package:lemmy_api_client/lemmy_api_client.dart';
|
|||
import 'package:url_launcher/url_launcher.dart' as ul;
|
||||
|
||||
import '../hooks/delayed_loading.dart';
|
||||
import '../hooks/logged_in_action.dart';
|
||||
import '../hooks/memo_future.dart';
|
||||
import '../hooks/stores.dart';
|
||||
import '../util/extensions/api.dart';
|
||||
|
@ -533,28 +534,20 @@ class _FollowButton extends HookWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final isSubbed = useState(community.subscribed ?? false);
|
||||
|
||||
final token = useAccountsStore().defaultTokenFor(community.instanceUrl);
|
||||
|
||||
final delayed = useDelayedLoading(const Duration(milliseconds: 500));
|
||||
final loggedInAction = useLoggedInAction(community.instanceUrl);
|
||||
|
||||
final colorOnTopOfAccent = textColorBasedOnBackground(theme.accentColor);
|
||||
|
||||
subscribe() async {
|
||||
if (token == null) {
|
||||
Scaffold.of(context).showSnackBar(
|
||||
SnackBar(content: Text("can't sub when you're not logged in")));
|
||||
return;
|
||||
}
|
||||
|
||||
subscribe(Jwt token) async {
|
||||
delayed.start();
|
||||
|
||||
try {
|
||||
await LemmyApi(community.instanceUrl).v1.followCommunity(
|
||||
communityId: community.id,
|
||||
follow: !isSubbed.value,
|
||||
auth: token?.raw);
|
||||
auth: token.raw);
|
||||
isSubbed.value = !isSubbed.value;
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
|
@ -590,7 +583,7 @@ class _FollowButton extends HookWidget {
|
|||
)
|
||||
: RaisedButton.icon(
|
||||
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 20),
|
||||
onPressed: delayed.pending ? () {} : subscribe,
|
||||
onPressed: loggedInAction(delayed.pending ? (_) {} : subscribe),
|
||||
icon: isSubbed.value
|
||||
? Icon(Icons.remove, size: 18, color: colorOnTopOfAccent)
|
||||
: Icon(Icons.add, size: 18, color: colorOnTopOfAccent),
|
||||
|
|
|
@ -3,25 +3,32 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:lemmy_api_client/lemmy_api_client.dart';
|
||||
|
||||
import '../hooks/memo_future.dart';
|
||||
import '../hooks/stores.dart';
|
||||
import '../util/extensions/api.dart';
|
||||
import '../widgets/comment_section.dart';
|
||||
import '../widgets/post.dart';
|
||||
import '../widgets/save_post_button.dart';
|
||||
|
||||
class FullPostPage extends HookWidget {
|
||||
final Future<FullPostView> fullPost;
|
||||
final int id;
|
||||
final String instanceUrl;
|
||||
final PostView post;
|
||||
|
||||
FullPostPage({@required int id, @required String instanceUrl})
|
||||
FullPostPage({@required this.id, @required this.instanceUrl})
|
||||
: assert(id != null),
|
||||
assert(instanceUrl != null),
|
||||
fullPost = LemmyApi(instanceUrl).v1.getPost(id: id),
|
||||
post = null;
|
||||
FullPostPage.fromPostView(this.post)
|
||||
: fullPost = LemmyApi(post.instanceUrl).v1.getPost(id: post.id);
|
||||
: id = post.id,
|
||||
instanceUrl = post.instanceUrl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final fullPostSnap = useFuture(this.fullPost);
|
||||
final accStore = useAccountsStore();
|
||||
final fullPostSnap = useMemoFuture(() => LemmyApi(instanceUrl)
|
||||
.v1
|
||||
.getPost(id: id, auth: accStore.defaultTokenFor(instanceUrl)?.raw));
|
||||
|
||||
// FALLBACK VIEW
|
||||
|
||||
|
@ -48,24 +55,16 @@ class FullPostPage extends HookWidget {
|
|||
|
||||
final fullPost = fullPostSnap.data;
|
||||
|
||||
final savedIcon = (post.saved == null || !post.saved)
|
||||
? Icons.bookmark_border
|
||||
: Icons.bookmark;
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
sharePost() => Share.text('Share post', post.apId, 'text/plain');
|
||||
|
||||
savePost() {
|
||||
print('SAVE POST');
|
||||
}
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
leading: BackButton(),
|
||||
actions: [
|
||||
IconButton(icon: Icon(Icons.share), onPressed: sharePost),
|
||||
IconButton(icon: Icon(savedIcon), onPressed: savePost),
|
||||
SavePostButton(post),
|
||||
IconButton(
|
||||
icon: Icon(Icons.more_vert),
|
||||
onPressed: () => Post.showMoreMenu(context, post)),
|
||||
|
@ -78,6 +77,17 @@ class FullPostPage extends HookWidget {
|
|||
if (fullPostSnap.hasData)
|
||||
CommentSection(fullPost.comments,
|
||||
postCreatorId: fullPost.post.creatorId)
|
||||
else if (fullPostSnap.hasError)
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 30),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(Icons.error),
|
||||
Text('Error: ${fullPostSnap.error}')
|
||||
],
|
||||
),
|
||||
)
|
||||
else
|
||||
Container(
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
|
|
|
@ -3,11 +3,14 @@ import 'package:esys_flutter_share/esys_flutter_share.dart';
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:lemmy_api_client/lemmy_api_client.dart';
|
||||
import 'package:timeago/timeago.dart' as timeago;
|
||||
import 'package:url_launcher/url_launcher.dart' as ul;
|
||||
|
||||
import '../hooks/delayed_loading.dart';
|
||||
import '../hooks/logged_in_action.dart';
|
||||
import '../pages/full_post.dart';
|
||||
import '../url_launcher.dart';
|
||||
import '../util/extensions/api.dart';
|
||||
|
@ -15,6 +18,7 @@ import '../util/goto.dart';
|
|||
import 'bottom_modal.dart';
|
||||
import 'fullscreenable_image.dart';
|
||||
import 'markdown_text.dart';
|
||||
import 'save_post_button.dart';
|
||||
|
||||
enum MediaType {
|
||||
image,
|
||||
|
@ -39,7 +43,7 @@ MediaType whatType(String url) {
|
|||
return MediaType.other;
|
||||
}
|
||||
|
||||
class Post extends StatelessWidget {
|
||||
class Post extends HookWidget {
|
||||
final PostView post;
|
||||
final String instanceUrl;
|
||||
final bool fullPost;
|
||||
|
@ -48,18 +52,6 @@ class Post extends StatelessWidget {
|
|||
|
||||
// == ACTIONS ==
|
||||
|
||||
void _savePost() {
|
||||
print('SAVE POST');
|
||||
}
|
||||
|
||||
void _upvotePost() {
|
||||
print('UPVOTE POST');
|
||||
}
|
||||
|
||||
void _downvotePost() {
|
||||
print('DOWNVOTE POST');
|
||||
}
|
||||
|
||||
static void showMoreMenu(BuildContext context, PostView post) {
|
||||
showModalBottomSheet(
|
||||
backgroundColor: Colors.transparent,
|
||||
|
@ -156,7 +148,6 @@ class Post extends StatelessWidget {
|
|||
return url;
|
||||
}();
|
||||
|
||||
// TODO: add NSFW, locked, removed, deleted, stickied
|
||||
/// assemble info section
|
||||
Widget info() => Column(children: [
|
||||
Padding(
|
||||
|
@ -402,17 +393,8 @@ class Post extends StatelessWidget {
|
|||
icon: Icon(Icons.share),
|
||||
onPressed: () => Share.text('Share post url', post.apId,
|
||||
'text/plain')), // TODO: find a way to mark it as url
|
||||
if (!fullPost)
|
||||
IconButton(
|
||||
icon: post.saved == true
|
||||
? Icon(Icons.bookmark)
|
||||
: Icon(Icons.bookmark_border),
|
||||
onPressed: _savePost),
|
||||
IconButton(
|
||||
icon: Icon(Icons.arrow_upward), onPressed: _upvotePost),
|
||||
Text(NumberFormat.compact().format(post.score)),
|
||||
IconButton(
|
||||
icon: Icon(Icons.arrow_downward), onPressed: _downvotePost),
|
||||
if (!fullPost) SavePostButton(post),
|
||||
_Voting(post),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
@ -449,3 +431,65 @@ class Post extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Voting extends HookWidget {
|
||||
final PostView post;
|
||||
|
||||
_Voting(this.post);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final myVote = useState(post.myVote ?? VoteType.none);
|
||||
final loading = useDelayedLoading(Duration(milliseconds: 500));
|
||||
final loggedInAction = useLoggedInAction(post.instanceUrl);
|
||||
|
||||
vote(VoteType vote, Jwt token) async {
|
||||
final api = LemmyApi(post.instanceUrl).v1;
|
||||
|
||||
loading.start();
|
||||
try {
|
||||
final res = await api.createPostLike(
|
||||
postId: post.id, score: vote, auth: token.raw);
|
||||
myVote.value = res.myVote;
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
Scaffold.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('voting failed :(')));
|
||||
return;
|
||||
}
|
||||
loading.cancel();
|
||||
}
|
||||
|
||||
return Row(
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_upward,
|
||||
color: myVote.value == VoteType.up ? theme.accentColor : null,
|
||||
),
|
||||
onPressed: loggedInAction(
|
||||
(token) => vote(
|
||||
myVote.value == VoteType.up ? VoteType.none : VoteType.up,
|
||||
token,
|
||||
),
|
||||
)),
|
||||
if (loading.loading)
|
||||
SizedBox(child: CircularProgressIndicator(), width: 20, height: 20)
|
||||
else
|
||||
Text(NumberFormat.compact().format(post.score + myVote.value.value)),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.arrow_downward,
|
||||
color: myVote.value == VoteType.down ? Colors.red : null,
|
||||
),
|
||||
onPressed: loggedInAction(
|
||||
(token) => vote(
|
||||
myVote.value == VoteType.down ? VoteType.none : VoteType.down,
|
||||
token,
|
||||
),
|
||||
)),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:lemmy_api_client/lemmy_api_client.dart';
|
||||
|
||||
import '../hooks/delayed_loading.dart';
|
||||
import '../hooks/logged_in_action.dart';
|
||||
import '../util/api_extensions.dart';
|
||||
|
||||
// TODO: sync this button between post and fullpost. the same with voting
|
||||
|
||||
class SavePostButton extends HookWidget {
|
||||
final PostView post;
|
||||
|
||||
SavePostButton(this.post);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isSaved = useState(post.saved ?? false);
|
||||
final savedIcon = isSaved.value ? Icons.bookmark : Icons.bookmark_border;
|
||||
final loading = useDelayedLoading(Duration(milliseconds: 500));
|
||||
final loggedInAction = useLoggedInAction(post.instanceUrl);
|
||||
|
||||
savePost(Jwt token) async {
|
||||
final api = LemmyApi(post.instanceUrl).v1;
|
||||
|
||||
loading.start();
|
||||
try {
|
||||
final res = await api.savePost(
|
||||
postId: post.id, save: !isSaved.value, auth: token.raw);
|
||||
isSaved.value = res.saved;
|
||||
// ignore: avoid_catches_without_on_clauses
|
||||
} catch (e) {
|
||||
Scaffold.of(context)
|
||||
.showSnackBar(SnackBar(content: Text('saving failed :(')));
|
||||
}
|
||||
loading.cancel();
|
||||
}
|
||||
|
||||
if (loading.loading) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 5),
|
||||
child: SizedBox(
|
||||
width: 30,
|
||||
height: 30,
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor: Theme.of(context).iconTheme.color,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
return IconButton(
|
||||
tooltip: 'Save post',
|
||||
icon: Icon(savedIcon),
|
||||
onPressed: loggedInAction(loading.pending ? (_) {} : savePost),
|
||||
);
|
||||
}
|
||||
}
|
48
pubspec.lock
48
pubspec.lock
|
@ -98,7 +98,7 @@ packages:
|
|||
name: cached_network_image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.3.2+1"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -204,6 +204,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -236,7 +243,7 @@ packages:
|
|||
name: flutter_cache_manager
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
version: "1.4.2"
|
||||
flutter_hooks:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -358,7 +365,7 @@ packages:
|
|||
name: lemmy_api_client
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
version: "0.5.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -435,7 +442,7 @@ packages:
|
|||
name: octo_image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
version: "0.3.0"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -456,7 +463,7 @@ packages:
|
|||
name: path_provider
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.6.14"
|
||||
version: "1.6.16"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -470,7 +477,7 @@ packages:
|
|||
name: path_provider_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.4+3"
|
||||
version: "0.0.4+4"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -478,6 +485,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.2"
|
||||
pedantic:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -720,7 +734,7 @@ packages:
|
|||
name: url_launcher
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "5.5.2"
|
||||
version: "5.6.0"
|
||||
url_launcher_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -734,7 +748,7 @@ packages:
|
|||
name: url_launcher_macos
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.1+7"
|
||||
version: "0.0.1+8"
|
||||
url_launcher_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -748,7 +762,14 @@ packages:
|
|||
name: url_launcher_web
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.1.3+1"
|
||||
version: "0.1.3+2"
|
||||
url_launcher_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_windows
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.0.1+1"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -777,6 +798,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -792,5 +820,5 @@ packages:
|
|||
source: hosted
|
||||
version: "2.2.1"
|
||||
sdks:
|
||||
dart: ">=2.9.0-14.0.dev <3.0.0"
|
||||
dart: ">=2.9.0 <3.0.0"
|
||||
flutter: ">=1.20.0 <2.0.0"
|
||||
|
|
|
@ -29,7 +29,7 @@ dependencies:
|
|||
flutter_hooks: ^0.13.2
|
||||
cached_network_image: ^2.2.0+1
|
||||
timeago: ^2.0.27
|
||||
lemmy_api_client: ^0.4.0
|
||||
lemmy_api_client: ^0.5.0
|
||||
fuzzy: <1.0.0
|
||||
|
||||
mobx: ^1.2.1
|
||||
|
|
Loading…
Reference in New Issue