Merge pull request #41 from krawieck/community-sub-button

This commit is contained in:
Filip Krawczyk 2020-09-16 22:14:35 +02:00 committed by GitHub
commit fc52ce91a8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 166 additions and 61 deletions

View File

@ -0,0 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
AsyncSnapshot<T> useMemoFuture<T>(Future<T> Function() valueBuilder,
[List<Object> keys = const <dynamic>[]]) =>
useFuture(useMemoized<Future<T>>(valueBuilder, keys));

View File

@ -1,3 +1,5 @@
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/gestures.dart';
@ -5,8 +7,11 @@ 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:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart' as ul;
import '../hooks/memo_future.dart';
import '../stores/accounts_store.dart';
import '../util/api_extensions.dart';
import '../util/goto.dart';
import '../util/intl.dart';
@ -17,37 +22,48 @@ import '../widgets/fullscreenable_image.dart';
import '../widgets/markdown_text.dart';
class CommunityPage extends HookWidget {
final Future<FullCommunityView> _fullCommunityFuture;
final CommunityView _community;
final String instanceUrl;
final String communityName;
final int communityId;
CommunityPage.fromName(
{@required String communityName, @required this.instanceUrl})
: assert(communityName != null),
CommunityPage.fromName({
@required this.communityName,
@required this.instanceUrl,
}) : assert(communityName != null),
assert(instanceUrl != null),
_fullCommunityFuture =
LemmyApi(instanceUrl).v1.getCommunity(name: communityName),
communityId = null,
_community = null;
CommunityPage.fromId({@required int communityId, @required this.instanceUrl})
: assert(communityId != null),
CommunityPage.fromId({
@required this.communityId,
@required this.instanceUrl,
}) : assert(communityId != null),
assert(instanceUrl != null),
_fullCommunityFuture =
LemmyApi(instanceUrl).v1.getCommunity(id: communityId),
communityName = null,
_community = null;
CommunityPage.fromCommunityView(this._community)
: instanceUrl = _community.instanceUrl,
_fullCommunityFuture = LemmyApi(_community.instanceUrl)
.v1
.getCommunity(name: _community.name);
void _subscribe() {
print('SUBSCRIBE');
}
communityId = _community.id,
communityName = _community.name;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
var fullCommunitySnap = useFuture(_fullCommunityFuture);
var fullCommunitySnap = useMemoFuture(() {
final token = context.watch<AccountsStore>().defaultTokenFor(instanceUrl);
if (communityId != null) {
return LemmyApi(instanceUrl).v1.getCommunity(
id: communityId,
auth: token?.raw,
);
} else {
return LemmyApi(instanceUrl).v1.getCommunity(
name: communityName,
auth: token?.raw,
);
}
});
final colorOnCard = textColorBasedOnBackground(theme.cardColor);
@ -169,11 +185,8 @@ class CommunityPage extends HookWidget {
icon: Icon(Icons.more_vert), onPressed: _openMoreMenu),
],
flexibleSpace: FlexibleSpaceBar(
background: _CommunityOverview(
community,
instanceUrl: instanceUrl,
subscribe: _subscribe,
),
background:
_CommunityOverview(community, instanceUrl: instanceUrl),
),
),
SliverPersistentHeader(
@ -218,20 +231,16 @@ class CommunityPage extends HookWidget {
class _CommunityOverview extends StatelessWidget {
final CommunityView community;
final String instanceUrl;
final void Function() subscribe;
_CommunityOverview(
this.community, {
@required this.instanceUrl,
@required this.subscribe,
}) : assert(instanceUrl != null),
assert(goToInstance != null),
assert(subscribe != null);
assert(goToInstance != null);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final colorOnTopOfAccent = textColorBasedOnBackground(theme.accentColor);
final shadow = BoxShadow(color: theme.canvasColor, blurRadius: 5);
final icon = community.icon != null
@ -272,7 +281,6 @@ class _CommunityOverview extends StatelessWidget {
],
)
: null;
final subscribed = community.subscribed ?? false;
return Stack(children: [
if (community.banner != null)
@ -359,32 +367,7 @@ class _CommunityOverview extends StatelessWidget {
],
),
),
// SUBSCRIBE BUTTON
Center(
child: SizedBox(
height: 27,
child: RaisedButton.icon(
padding:
EdgeInsets.symmetric(vertical: 0, horizontal: 20),
onPressed: subscribe,
icon: subscribed
? Icon(Icons.remove,
size: 18, color: colorOnTopOfAccent)
: Icon(Icons.add,
size: 18, color: colorOnTopOfAccent),
color: theme.accentColor,
label: Text(
'${subscribed ? 'un' : ''}subscribe',
style: TextStyle(
color: colorOnTopOfAccent,
fontSize: theme.textTheme.subtitle1.fontSize),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
),
),
_FollowButton(community),
],
),
),
@ -541,3 +524,96 @@ class _Divider extends StatelessWidget {
child: Divider(),
);
}
class _FollowButton extends HookWidget {
final CommunityView community;
_FollowButton(this.community);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final isSubbed = useState(community.subscribed ?? false);
final colorOnTopOfAccent = textColorBasedOnBackground(theme.accentColor);
final token =
context.watch<AccountsStore>().defaultTokenFor(community.instanceUrl);
// TODO: use hook for handling spinner and pending
final showSpinner = useState(false);
final isPending = useState(false);
subscribe() async {
if (token == null) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text("can't sub when you're not logged in")));
return;
}
isPending.value = true;
var spinnerTimer =
Timer(Duration(milliseconds: 500), () => showSpinner.value = true);
final api = LemmyApi(community.instanceUrl).v1;
try {
await api.followCommunity(
communityId: community.id,
follow: !isSubbed.value,
auth: token?.raw);
isSubbed.value = !isSubbed.value;
// ignore: avoid_catches_without_on_clauses
} catch (e) {
Scaffold.of(context).showSnackBar(SnackBar(
content: Row(
children: [
Icon(Icons.warning),
SizedBox(width: 10),
Text("couldn't ${isSubbed.value ? 'un' : ''}sub :<"),
],
),
));
}
// clean up
spinnerTimer.cancel();
isPending.value = false;
showSpinner.value = false;
}
return Center(
child: SizedBox(
height: 27,
width: 160,
child: showSpinner.value
? RaisedButton(
onPressed: null,
child: SizedBox(
height: 15,
width: 15,
child: CircularProgressIndicator(),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
)
: RaisedButton.icon(
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 20),
onPressed: isPending.value ? () {} : subscribe,
icon: isSubbed.value
? Icon(Icons.remove, size: 18, color: colorOnTopOfAccent)
: Icon(Icons.add, size: 18, color: colorOnTopOfAccent),
color: theme.accentColor,
label: Text(
'${isSubbed.value ? 'un' : ''}subscribe',
style: TextStyle(
color: colorOnTopOfAccent,
fontSize: theme.textTheme.subtitle1.fontSize),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
),
),
);
}
}

View File

@ -89,21 +89,39 @@ abstract class _AccountsStore with Store {
@computed
User get defaultUser {
if (_defaultAccount == null) {
return null;
}
var userTag = _defaultAccount.split('@');
return users[userTag[1]][userTag[0]];
}
@computed
Jwt get defaultToken {
if (_defaultAccount == null) {
return null;
}
var userTag = _defaultAccount.split('@');
return tokens[userTag[1]][userTag[0]];
}
User defaultUserFor(String instanceUrl) =>
Computed(() => users[instanceUrl][_defaultAccounts[instanceUrl]]).value;
User defaultUserFor(String instanceUrl) => Computed(() {
if (isAnonymousFor(instanceUrl)) {
return null;
}
Jwt defaultTokenFor(String instanceUrl) =>
Computed(() => tokens[instanceUrl][_defaultAccounts[instanceUrl]]).value;
return users[instanceUrl][_defaultAccounts[instanceUrl]];
}).value;
Jwt defaultTokenFor(String instanceUrl) => Computed(() {
if (isAnonymousFor(instanceUrl)) {
return null;
}
return tokens[instanceUrl][_defaultAccounts[instanceUrl]];
}).value;
@action
void setDefaultAccount(String instanceUrl, String username) {
@ -115,8 +133,13 @@ abstract class _AccountsStore with Store {
_defaultAccounts[instanceUrl] = username;
}
bool isAnonymousFor(String instanceUrl) =>
Computed(() => users[instanceUrl].isEmpty).value;
bool isAnonymousFor(String instanceUrl) => Computed(() {
if (!users.containsKey(instanceUrl)) {
return true;
}
return users[instanceUrl].isEmpty;
}).value;
@computed
bool get hasNoAccount => users.values.every((e) => e.isEmpty);