lemmur-app-android/lib/widgets/post.dart

367 lines
11 KiB
Dart
Raw Normal View History

2020-08-27 23:27:27 +02:00
import 'package:cached_network_image/cached_network_image.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
2020-08-29 21:01:01 +02:00
import 'package:intl/intl.dart';
import 'package:lemmy_api_client/lemmy_api_client.dart';
2020-08-27 23:27:27 +02:00
import 'package:timeago/timeago.dart' as timeago;
import 'markdown_text.dart';
2020-08-29 21:01:01 +02:00
enum MediaType {
image,
gallery,
video,
other,
}
MediaType whatType(String url) {
if (url == null) return MediaType.other;
2020-08-30 19:29:12 +02:00
// TODO: make detection more nuanced
2020-08-29 21:01:01 +02:00
if (url.endsWith('.jpg') || url.endsWith('.png') || url.endsWith('.gif')) {
return MediaType.image;
}
return MediaType.other;
}
2020-08-27 23:27:27 +02:00
class PostWidget extends StatelessWidget {
final PostView post;
final String hostUrl;
/// nullable
final String linkPostDomain;
ThemeData _theme;
BuildContext _context;
2020-08-27 23:27:27 +02:00
PostWidget(this.post)
: hostUrl = post.communityActorId.split('/')[2],
linkPostDomain = post.url != null ? post.url.split('/')[2] : null;
// == ACTIONS ==
void _openLink() {
print('OPEN LINK');
}
void _goToUser() {
print('GO TO USER');
}
void _goToPost() {
print('GO OT POST');
}
void _goToCommunity() {
print('GO TO COMMUNITY');
}
void _goToInstance() {
print('GO TO INSTANCE');
}
2020-08-29 21:01:01 +02:00
void _openFullImage() {
print('OPEN FULL IMAGE');
}
// POST ACTIONS
void _savePost() {
print('SAVE POST');
}
void _upvotePost() {
print('UPVOTE POST');
}
void _downvotePost() {
print('DOWNVOTE POST');
}
void _showMoreMenu() {
print('SHOW MORE MENU');
}
// == UI ==
2020-08-27 23:27:27 +02:00
@override
Widget build(BuildContext context) {
_theme = Theme.of(context);
_context = context;
2020-08-27 23:27:27 +02:00
return Container(
decoration: BoxDecoration(
boxShadow: [BoxShadow(blurRadius: 10, color: Colors.black54)],
color: _theme.colorScheme.surface,
borderRadius: BorderRadius.all(Radius.circular(20)),
),
child: InkWell(
2020-08-29 21:01:01 +02:00
onTap: _goToPost,
2020-08-27 23:27:27 +02:00
child: Column(
children: [
_info(),
_title(),
2020-08-29 21:01:01 +02:00
if (whatType(post.url) != MediaType.other)
_postImage()
else if (post.url != null)
_linkPreview(),
if (post.body != null) _textBody(),
2020-08-27 23:27:27 +02:00
_actions(),
],
),
),
);
}
Widget _info() {
return Column(children: [
Padding(
padding: const EdgeInsets.all(10),
child: Row(children: [
Column(
mainAxisSize: MainAxisSize.min,
children: [
if (post.communityIcon != null)
Padding(
padding: const EdgeInsets.only(right: 10),
child: InkWell(
onTap: _goToCommunity,
2020-08-27 23:27:27 +02:00
child: SizedBox(
height: 40,
width: 40,
child: CachedNetworkImage(
imageBuilder: (context, imageProvider) => Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
image: DecorationImage(
fit: BoxFit.cover,
image: imageProvider,
),
),
),
imageUrl: post.communityIcon,
errorWidget: (context, url, error) =>
Text(error.toString()),
),
),
),
),
],
),
Column(
children: [
Row(children: [
RichText(
2020-08-30 19:29:12 +02:00
overflow: TextOverflow.ellipsis, // TODO: fix overflowing
2020-08-27 23:27:27 +02:00
text: TextSpan(
style: TextStyle(
fontSize: 15, color: _theme.textTheme.bodyText1.color),
children: [
TextSpan(
text: '!',
style: TextStyle(fontWeight: FontWeight.w300)),
TextSpan(
text: post.communityName,
style: TextStyle(fontWeight: FontWeight.w600),
recognizer: TapGestureRecognizer()
..onTap = _goToCommunity),
2020-08-27 23:27:27 +02:00
TextSpan(
text: '@',
style: TextStyle(fontWeight: FontWeight.w300)),
TextSpan(
text: hostUrl,
style: TextStyle(fontWeight: FontWeight.w600),
recognizer: TapGestureRecognizer()
..onTap = _goToInstance),
2020-08-27 23:27:27 +02:00
],
),
)
]),
Row(children: [
RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 13,
color: _theme.textTheme.bodyText1.color),
children: [
TextSpan(
text: 'by',
style: TextStyle(fontWeight: FontWeight.w300)),
TextSpan(
text:
''' ${post.creatorPreferredUsername ?? post.creatorName}''',
style: TextStyle(fontWeight: FontWeight.w600),
recognizer: TapGestureRecognizer()..onTap = _goToUser,
2020-08-27 23:27:27 +02:00
),
TextSpan(
text:
''' · ${timeago.format(post.published, locale: 'en_short')}'''),
if (linkPostDomain != null)
TextSpan(text: ' · $linkPostDomain'),
if (post.locked) TextSpan(text: ' · 🔒'),
],
))
]),
],
crossAxisAlignment: CrossAxisAlignment.start,
),
Spacer(),
Column(
children: [
IconButton(
onPressed: _showMoreMenu,
2020-08-27 23:27:27 +02:00
icon: Icon(Icons.more_vert),
)
],
)
]),
),
]);
}
Widget _title() {
return Padding(
padding: const EdgeInsets.only(left: 10, right: 10, bottom: 10),
child: Row(
children: [
Flexible(
child: Text(
'${post.name}',
textAlign: TextAlign.left,
softWrap: true,
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w600),
),
),
2020-08-29 21:01:01 +02:00
if (post.url != null &&
!(whatType(post.url) != MediaType.other) &&
post.thumbnailUrl != null)
Spacer(),
if (post.url != null &&
!(whatType(post.url) != MediaType.other) &&
post.thumbnailUrl != null)
2020-08-27 23:27:27 +02:00
InkWell(
onTap: _openLink,
2020-08-27 23:27:27 +02:00
child: Stack(children: [
ClipRRect(
borderRadius: BorderRadius.circular(20),
child: CachedNetworkImage(
imageUrl: post.thumbnailUrl,
width: 70,
height: 70,
fit: BoxFit.cover,
errorWidget: (context, url, error) =>
Text(error.toString()),
)),
Positioned(
top: 8,
right: 8,
child: Icon(
Icons.launch,
size: 20,
),
)
]),
)
],
),
);
}
2020-08-29 21:01:01 +02:00
Widget _postImage() {
assert(post.url != null);
return InkWell(
onTap: _openFullImage,
child: CachedNetworkImage(
2020-08-27 23:27:27 +02:00
imageUrl: post.url,
progressIndicatorBuilder: (context, url, progress) =>
CircularProgressIndicator(value: progress.progress),
2020-08-29 21:01:01 +02:00
),
);
2020-08-27 23:27:27 +02:00
}
Widget _linkPreview() {
assert(post.url != null);
var url = post.url.split('/')[2];
if (url.startsWith('www.')) {
url = url.substring(4);
}
return Padding(
padding: const EdgeInsets.all(10),
child: InkWell(
onTap: _openLink,
2020-08-27 23:27:27 +02:00
child: Container(
decoration: BoxDecoration(
border: Border.all(width: 1),
borderRadius: BorderRadius.circular(5)),
child: Padding(
padding: const EdgeInsets.all(10),
child: Column(
children: [
Row(children: [
Spacer(),
Text('$url ',
style: _theme.textTheme.caption
.apply(fontStyle: FontStyle.italic)),
Icon(Icons.launch, size: 12),
]),
Row(children: [
Flexible(
child: Text(post.embedTitle,
style: _theme.textTheme.subtitle1
.apply(fontWeightDelta: 2)))
]),
Row(children: [Flexible(child: Text(post.embedDescription))]),
],
),
),
),
),
);
}
2020-08-29 21:01:01 +02:00
Widget _textBody() {
return Padding(
padding: const EdgeInsets.all(10),
child: MarkdownText(post.body, _context),
2020-08-29 21:01:01 +02:00
);
}
2020-08-27 23:27:27 +02:00
Widget _actions() {
return Padding(
padding: const EdgeInsets.fromLTRB(10, 5, 10, 5),
child: Row(
children: [
Icon(Icons.comment),
Expanded(
flex: 999,
child: Text(
''' ${NumberFormat.compact().format(post.numberOfComments)} comment${post.numberOfComments == 1 ? '' : 's'}''',
overflow: TextOverflow.fade,
softWrap: false,
2020-08-29 20:31:53 +02:00
),
),
2020-08-27 23:27:27 +02:00
Spacer(),
IconButton(
icon: Icon(Icons.share),
onPressed: () => Share.text('Share post url', post.apId,
2020-08-30 19:29:12 +02:00
'text/plain')), // TODO: find a way to mark it as url
2020-08-27 23:27:27 +02:00
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)),
2020-08-27 23:27:27 +02:00
IconButton(
icon: Icon(Icons.arrow_downward), onPressed: _downvotePost),
2020-08-27 23:27:27 +02:00
],
),
);
}
}