1
0
mirror of https://github.com/krawieck/lemmur/ synced 2024-12-18 11:28:45 +01:00
lemmur-app-android/lib/pages/instance.dart

415 lines
14 KiB
Dart
Raw Normal View History

import 'package:cached_network_image/cached_network_image.dart';
2020-09-08 21:18:17 +02:00
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart';
import '../hooks/stores.dart';
import '../util/goto.dart';
import '../util/more_icon.dart';
import '../util/text_color.dart';
import '../widgets/badge.dart';
import '../widgets/fullscreenable_image.dart';
2020-10-26 18:47:49 +01:00
import '../widgets/info_table_popup.dart';
import '../widgets/markdown_text.dart';
2020-09-29 10:27:36 +02:00
import '../widgets/sortable_infinite_list.dart';
2020-09-08 23:01:40 +02:00
import 'communities_list.dart';
import 'users_list.dart';
2020-09-30 19:05:00 +02:00
/// Displays posts, comments, and general info about the given instance
class InstancePage extends HookWidget {
final String instanceHost;
final Future<FullSiteView> siteFuture;
2020-09-08 18:57:48 +02:00
final Future<List<CommunityView>> communitiesFuture;
void _share() =>
Share.text('Share instance', 'https://$instanceHost', 'text/plain');
InstancePage({@required this.instanceHost})
: assert(instanceHost != null),
siteFuture = LemmyApi(instanceHost).v1.getSite(),
2020-09-08 18:57:48 +02:00
communitiesFuture =
LemmyApi(instanceHost).v1.listCommunities(sort: SortType.hot);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final siteSnap = useFuture(siteFuture);
final colorOnCard = textColorBasedOnBackground(theme.cardColor);
2020-09-29 10:53:40 +02:00
final accStore = useAccountsStore();
if (!siteSnap.hasData) {
return Scaffold(
appBar: AppBar(
iconTheme: theme.iconTheme,
brightness: theme.brightness,
backgroundColor: theme.cardColor,
elevation: 0,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (siteSnap.hasError) ...[
2021-01-03 19:43:39 +01:00
const Icon(Icons.error),
Padding(
padding: const EdgeInsets.all(8),
child: Text('ERROR: ${siteSnap.error}'),
)
] else
2021-01-03 19:43:39 +01:00
const CircularProgressIndicator(semanticsLabel: 'loading')
],
),
),
);
}
final site = siteSnap.data;
void _openMoreMenu(BuildContext c) {
2020-10-26 18:47:49 +01:00
showInfoTablePopup(context, {
'url': instanceHost,
'creator': '@${site.site.creatorName}',
'version': site.version,
'enableDownvotes': site.site.enableDownvotes,
'enableNsfw': site.site.enableNsfw,
'published': site.site.published,
'updated': site.site.updated,
2020-10-26 18:47:49 +01:00
});
}
return Scaffold(
body: DefaultTabController(
length: 3,
child: NestedScrollView(
2020-09-08 23:10:40 +02:00
headerSliverBuilder: (context, innerBoxIsScrolled) => <Widget>[
SliverAppBar(
brightness: theme.brightness,
2020-09-08 23:10:40 +02:00
expandedHeight: 200,
pinned: true,
elevation: 0,
backgroundColor: theme.cardColor,
iconTheme: theme.iconTheme,
title: Text(
site.site.name,
2020-09-08 23:10:40 +02:00
style: TextStyle(color: colorOnCard),
),
actions: [
2021-01-03 19:43:39 +01:00
IconButton(icon: const Icon(Icons.share), onPressed: _share),
2020-09-08 23:10:40 +02:00
IconButton(
icon: Icon(moreIcon),
2020-09-08 23:10:40 +02:00
onPressed: () => _openMoreMenu(context)),
],
flexibleSpace: FlexibleSpaceBar(
background: Stack(children: [
if (site.site.banner != null)
2020-09-11 21:18:45 +02:00
FullscreenableImage(
url: site.site.banner,
child: CachedNetworkImage(
imageUrl: site.site.banner,
2021-01-03 19:43:39 +01:00
errorWidget: (_, __, ___) => const SizedBox.shrink(),
),
2020-09-11 21:18:45 +02:00
),
2020-09-08 23:10:40 +02:00
SafeArea(
child: Center(
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 40),
child: site.site.icon == null
? const SizedBox(height: 100, width: 100)
: FullscreenableImage(
url: site.site.icon,
child: CachedNetworkImage(
width: 100,
height: 100,
imageUrl: site.site.icon,
errorWidget: (_, __, ___) =>
const Icon(Icons.warning),
),
),
2020-09-08 23:10:40 +02:00
),
Text(site.site.name,
style: theme.textTheme.headline6),
Text(instanceHost, style: theme.textTheme.caption)
2020-09-08 23:10:40 +02:00
],
),
),
),
2020-09-08 23:10:40 +02:00
]),
),
),
SliverPersistentHeader(
delegate: _SliverAppBarDelegate(
TabBar(
labelColor: theme.textTheme.bodyText1.color,
unselectedLabelColor: Colors.grey,
2021-01-03 18:21:56 +01:00
tabs: const [
2020-09-08 23:10:40 +02:00
Tab(text: 'Posts'),
Tab(text: 'Comments'),
Tab(text: 'About'),
],
),
),
2020-09-08 23:10:40 +02:00
pinned: true,
),
],
body: TabBarView(
children: [
InfinitePostList(
fetcher: (page, batchSize, sort) =>
LemmyApi(instanceHost).v1.getPosts(
// TODO: switch between all and subscribed
type: PostListingType.all,
sort: sort,
2020-09-29 10:53:40 +02:00
limit: batchSize,
page: page,
auth: accStore.defaultTokenFor(instanceHost)?.raw,
2020-09-29 10:53:40 +02:00
)),
InfiniteCommentList(
fetcher: (page, batchSize, sort) =>
LemmyApi(instanceHost).v1.getComments(
2020-09-29 10:53:40 +02:00
type: CommentListingType.all,
sort: sort,
limit: batchSize,
page: page,
auth: accStore.defaultTokenFor(instanceHost)?.raw,
)),
_AboutTab(site,
communitiesFuture: communitiesFuture,
instanceHost: instanceHost),
],
),
),
),
);
}
}
class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate {
final TabBar _tabBar;
2021-01-03 19:43:39 +01:00
const _SliverAppBarDelegate(this._tabBar);
@override
double get minExtent => _tabBar.preferredSize.height;
@override
double get maxExtent => _tabBar.preferredSize.height;
@override
Widget build(
BuildContext context, double shrinkOffset, bool overlapsContent) {
final theme = Theme.of(context);
2021-01-03 18:13:25 +01:00
return Container(color: theme.cardColor, child: _tabBar);
}
@override
2020-09-08 23:10:40 +02:00
bool shouldRebuild(_SliverAppBarDelegate oldDelegate) => false;
}
2020-09-08 18:57:48 +02:00
class _AboutTab extends HookWidget {
final FullSiteView site;
2020-09-08 18:57:48 +02:00
final Future<List<CommunityView>> communitiesFuture;
final String instanceHost;
const _AboutTab(this.site,
{@required this.communitiesFuture, @required this.instanceHost})
: assert(communitiesFuture != null),
assert(instanceHost != null);
2020-09-08 19:52:09 +02:00
void goToUser(int id) {
print('GO TO USER $id');
}
void goToModLog() {
print('GO TO MODLOG');
}
void goToBannedUsers(BuildContext c) {
goTo(
c,
(_) => UsersListPage(
users: site.banned.reversed.toList(), title: 'Banned users'),
);
2020-09-08 19:52:09 +02:00
}
@override
Widget build(BuildContext context) {
2020-09-08 18:57:48 +02:00
final theme = Theme.of(context);
final commSnap = useFuture(communitiesFuture);
final accStore = useAccountsStore();
2020-09-08 18:57:48 +02:00
2020-09-08 23:01:40 +02:00
void goToCommunities() {
goTo(
context,
(_) => CommunitiesListPage(
fetcher: (page, batchSize, sortType) =>
LemmyApi(instanceHost).v1.listCommunities(
sort: sortType,
limit: batchSize,
page: page,
auth: accStore.defaultTokenFor(instanceHost)?.raw,
),
title: 'Communities of ${site.site.name}',
),
);
2020-09-08 23:01:40 +02:00
}
return SingleChildScrollView(
child: SafeArea(
top: false,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 15, vertical: 15),
child: MarkdownText(
site.site.description,
instanceHost: instanceHost,
),
),
2021-01-03 19:43:39 +01:00
const _Divider(),
SizedBox(
height: 25,
child: ListView(
scrollDirection: Axis.horizontal,
children: [
2021-01-03 19:43:39 +01:00
const SizedBox(width: 7),
const _Badge('X users online'),
_Badge('${site.site.numberOfUsers} users'),
_Badge('${site.site.numberOfCommunities} communities'),
_Badge('${site.site.numberOfPosts} posts'),
_Badge('${site.site.numberOfComments} comments'),
2021-01-03 19:43:39 +01:00
const SizedBox(width: 15),
],
),
),
2021-01-03 19:43:39 +01:00
const _Divider(),
2020-09-08 21:20:29 +02:00
ListTile(
title: Center(
child: Text(
'Trending communities:',
style: theme.textTheme.headline6.copyWith(fontSize: 18),
),
),
2020-09-08 18:57:48 +02:00
),
if (commSnap.hasData)
...commSnap.data.take(6).map((e) => ListTile(
onTap: () =>
goToCommunity.byId(context, e.instanceHost, e.id),
2020-09-08 18:57:48 +02:00
title: Text(e.name),
leading: e.icon != null
? CachedNetworkImage(
height: 50,
width: 50,
imageUrl: e.icon,
errorWidget: (_, __, ___) =>
2021-01-03 19:43:39 +01:00
const SizedBox(width: 50, height: 50),
2020-09-08 18:57:48 +02:00
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.cover,
image: imageProvider,
),
),
))
2021-01-03 19:43:39 +01:00
: const SizedBox(width: 50),
2020-09-08 18:57:48 +02:00
))
else if (commSnap.hasError)
Padding(
2021-01-03 18:03:59 +01:00
padding: const EdgeInsets.all(8),
2020-09-08 18:57:48 +02:00
child: Text("Can't load communities, ${commSnap.error}"),
)
else
2021-01-03 19:43:39 +01:00
const Padding(
padding: EdgeInsets.symmetric(vertical: 10),
2020-09-08 18:57:48 +02:00
child: CircularProgressIndicator(),
),
ListTile(
2021-01-03 19:43:39 +01:00
title: const Center(child: Text('See all')),
2020-09-08 19:52:09 +02:00
onTap: goToCommunities,
2020-09-08 18:57:48 +02:00
),
2021-01-03 19:43:39 +01:00
const _Divider(),
2020-09-08 19:14:05 +02:00
ListTile(
title: Center(
child: Text(
'Admins:',
style: theme.textTheme.headline6.copyWith(fontSize: 18),
),
),
),
...site.admins.map((e) => ListTile(
2020-09-09 11:19:12 +02:00
title: Text((e.preferredUsername == null ||
e.preferredUsername.isEmpty)
? '@${e.name}'
: e.preferredUsername),
subtitle: e.bio != null
? MarkdownText(e.bio, instanceHost: instanceHost)
: null,
2020-09-08 19:52:09 +02:00
onTap: () => goToUser(e.id),
2020-09-08 19:14:05 +02:00
leading: e.avatar != null
? CachedNetworkImage(
height: 50,
width: 50,
imageUrl: e.avatar,
errorWidget: (_, __, ___) =>
2021-01-03 19:43:39 +01:00
const SizedBox(width: 50, height: 50),
2020-09-08 19:14:05 +02:00
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.cover, image: imageProvider),
),
))
2021-01-03 19:43:39 +01:00
: const SizedBox(width: 50),
2020-09-08 19:14:05 +02:00
)),
2021-01-03 19:43:39 +01:00
const _Divider(),
ListTile(
2021-01-03 19:43:39 +01:00
title: const Center(child: Text('Banned users')),
onTap: () => goToBannedUsers(context),
),
ListTile(
2021-01-03 19:43:39 +01:00
title: const Center(child: Text('Modlog')),
2020-09-08 19:52:09 +02:00
onTap: goToModLog,
),
2021-01-03 19:43:39 +01:00
const SizedBox(height: 20),
],
),
),
);
}
}
class _Badge extends StatelessWidget {
final String text;
2021-01-03 18:21:56 +01:00
const _Badge(this.text);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return Padding(
padding: const EdgeInsets.only(left: 8),
child: Badge(
child: Text(
text,
style:
TextStyle(color: textColorBasedOnBackground(theme.accentColor)),
),
),
);
}
}
class _Divider extends StatelessWidget {
2021-01-03 19:43:39 +01:00
const _Divider();
@override
2021-01-03 19:43:39 +01:00
Widget build(BuildContext context) => const Padding(
padding: EdgeInsets.symmetric(horizontal: 15, vertical: 10),
2020-09-08 23:10:40 +02:00
child: Divider(),
);
}