2020-02-08 06:46:15 +01:00
|
|
|
import 'package:flutter/cupertino.dart';
|
2019-01-28 14:32:01 +01:00
|
|
|
import 'package:flutter/material.dart';
|
2020-01-30 10:20:36 +01:00
|
|
|
import 'package:git_touch/models/auth.dart';
|
2019-09-02 15:52:32 +02:00
|
|
|
import 'package:git_touch/models/theme.dart';
|
2020-02-08 06:02:12 +01:00
|
|
|
import 'package:git_touch/widgets/action_button.dart';
|
2019-09-13 10:27:33 +02:00
|
|
|
import 'package:git_touch/widgets/markdown_view.dart';
|
2019-09-02 15:52:32 +02:00
|
|
|
import 'package:provider/provider.dart';
|
2019-08-31 14:44:59 +02:00
|
|
|
import 'package:timeago/timeago.dart' as timeago;
|
2019-05-12 09:14:28 +02:00
|
|
|
import 'package:primer/primer.dart';
|
2019-01-31 07:37:25 +01:00
|
|
|
import '../utils/utils.dart';
|
2019-02-07 07:35:19 +01:00
|
|
|
import 'avatar.dart';
|
2019-04-05 15:19:00 +02:00
|
|
|
import 'link.dart';
|
2019-02-07 07:35:19 +01:00
|
|
|
import 'user_name.dart';
|
2019-01-28 14:32:01 +01:00
|
|
|
|
2019-03-20 18:21:50 +01:00
|
|
|
final emojiMap = {
|
|
|
|
'THUMBS_UP': '👍',
|
|
|
|
'THUMBS_DOWN': '👎',
|
|
|
|
'LAUGH': '😄',
|
|
|
|
'HOORAY': '🎉',
|
|
|
|
'CONFUSED': '😕',
|
|
|
|
'HEART': '❤️',
|
|
|
|
'ROCKET': '🚀',
|
|
|
|
'EYES': '👀'
|
|
|
|
};
|
|
|
|
|
2020-01-30 10:20:36 +01:00
|
|
|
class GhEmojiAction extends StatefulWidget {
|
2019-02-19 08:25:15 +01:00
|
|
|
final Map<String, dynamic> payload;
|
2020-01-30 10:20:36 +01:00
|
|
|
GhEmojiAction(this.payload);
|
|
|
|
@override
|
|
|
|
_GhEmojiActionState createState() => _GhEmojiActionState();
|
|
|
|
}
|
2019-01-28 14:32:01 +01:00
|
|
|
|
2020-01-30 10:20:36 +01:00
|
|
|
class _GhEmojiActionState extends State<GhEmojiAction> {
|
|
|
|
get payload => widget.payload;
|
|
|
|
|
2020-02-08 06:02:12 +01:00
|
|
|
onReaction(String emojiKey) async {
|
2020-01-30 10:20:36 +01:00
|
|
|
if (emojiKey == null) return;
|
|
|
|
|
2020-02-08 06:02:12 +01:00
|
|
|
final isRemove = _hasReacted(emojiKey);
|
2020-01-30 10:20:36 +01:00
|
|
|
var id = payload['id'] as String;
|
|
|
|
var operation = isRemove ? 'remove' : 'add';
|
2020-02-08 06:46:15 +01:00
|
|
|
|
|
|
|
try {
|
|
|
|
await Provider.of<AuthModel>(context).query('''
|
2020-01-30 10:20:36 +01:00
|
|
|
mutation {
|
|
|
|
${operation}Reaction(input: {subjectId: "$id", content: $emojiKey}) {
|
|
|
|
clientMutationId
|
|
|
|
}
|
|
|
|
}
|
|
|
|
''');
|
2020-02-08 06:46:15 +01:00
|
|
|
setState(() {
|
|
|
|
payload[emojiKey]['totalCount'] += isRemove ? -1 : 1;
|
|
|
|
payload[emojiKey]['viewerHasReacted'] = !isRemove;
|
|
|
|
});
|
|
|
|
} catch (e) {
|
|
|
|
final theme = Provider.of<ThemeModel>(context);
|
|
|
|
theme.showWarning(context, e);
|
|
|
|
}
|
2020-01-30 10:20:36 +01:00
|
|
|
}
|
2019-04-05 15:19:00 +02:00
|
|
|
|
|
|
|
bool _hasReacted(String emojiKey) {
|
|
|
|
if (payload[emojiKey] == null) return false;
|
|
|
|
return payload[emojiKey]['viewerHasReacted'] as bool;
|
|
|
|
}
|
|
|
|
|
2020-01-30 10:20:36 +01:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
final theme = Provider.of<ThemeModel>(context);
|
|
|
|
return Wrap(
|
|
|
|
crossAxisAlignment: WrapCrossAlignment.center,
|
|
|
|
children: [
|
|
|
|
...emojiMap.entries
|
|
|
|
.where((entry) => payload[entry.key]['totalCount'] as int != 0)
|
|
|
|
.map<Widget>((entry) {
|
|
|
|
var emojiKey = entry.key;
|
|
|
|
var emoji = entry.value;
|
|
|
|
var count = payload[entry.key]['totalCount'] as int;
|
|
|
|
|
|
|
|
return Link(
|
|
|
|
onTap: () {
|
2020-02-08 06:02:12 +01:00
|
|
|
onReaction(emojiKey);
|
2020-01-30 10:20:36 +01:00
|
|
|
},
|
|
|
|
child: Container(
|
|
|
|
padding: EdgeInsets.all(4),
|
2020-02-08 06:09:53 +01:00
|
|
|
decoration: BoxDecoration(
|
|
|
|
color: _hasReacted(emojiKey)
|
|
|
|
? (theme.brightness == Brightness.dark
|
|
|
|
? PrimerColors.blue900
|
|
|
|
: PrimerColors.blue000)
|
|
|
|
: Colors.transparent,
|
|
|
|
),
|
2020-01-30 10:20:36 +01:00
|
|
|
child: Wrap(
|
|
|
|
crossAxisAlignment: WrapCrossAlignment.center,
|
|
|
|
children: <Widget>[
|
|
|
|
Text(emoji, style: TextStyle(fontSize: 18)),
|
|
|
|
SizedBox(width: 4),
|
|
|
|
Text(numberFormat.format(count),
|
|
|
|
style:
|
|
|
|
TextStyle(color: theme.palette.primary, fontSize: 14))
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}),
|
|
|
|
Link(
|
|
|
|
onTap: () async {
|
2020-02-08 06:02:12 +01:00
|
|
|
await theme.showActions(
|
2020-01-30 10:20:36 +01:00
|
|
|
context,
|
|
|
|
emojiMap.entries.map((entry) {
|
2020-02-08 06:02:12 +01:00
|
|
|
final emojiKey = entry.key;
|
|
|
|
return ActionItem(
|
|
|
|
text: emojiKey + ' ' + entry.value,
|
|
|
|
onTap: (_) {
|
|
|
|
onReaction(emojiKey);
|
|
|
|
},
|
2020-01-30 10:20:36 +01:00
|
|
|
);
|
|
|
|
}).toList(),
|
|
|
|
);
|
|
|
|
},
|
|
|
|
child: Container(
|
|
|
|
padding: EdgeInsets.all(4),
|
|
|
|
child: Wrap(
|
|
|
|
crossAxisAlignment: WrapCrossAlignment.center,
|
|
|
|
children: <Widget>[
|
|
|
|
Text('+', style: TextStyle(color: theme.palette.primary)),
|
|
|
|
Icon(Octicons.smiley, color: theme.palette.primary, size: 18),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
);
|
2019-04-05 15:19:00 +02:00
|
|
|
}
|
2020-01-30 10:20:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
class CommentItem extends StatelessWidget {
|
|
|
|
final Avatar avatar;
|
|
|
|
final String login;
|
|
|
|
final DateTime createdAt;
|
|
|
|
final String body;
|
|
|
|
final List<Widget> widgets;
|
|
|
|
|
|
|
|
CommentItem.gh(Map<String, dynamic> payload)
|
|
|
|
: avatar = Avatar(
|
|
|
|
url: payload['author']['avatarUrl'], // TODO: deleted user
|
|
|
|
linkUrl: '/' + payload['author']['login'],
|
|
|
|
),
|
|
|
|
login = payload['author']['login'],
|
|
|
|
createdAt = DateTime.parse(payload['createdAt']),
|
|
|
|
body = payload['body'],
|
|
|
|
widgets = [GhEmojiAction(payload)];
|
|
|
|
|
|
|
|
CommentItem({
|
|
|
|
@required this.avatar,
|
|
|
|
@required this.login,
|
|
|
|
@required this.createdAt,
|
|
|
|
@required this.body,
|
|
|
|
this.widgets,
|
|
|
|
});
|
2019-01-28 14:32:01 +01:00
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2019-11-08 11:29:08 +01:00
|
|
|
final theme = Provider.of<ThemeModel>(context);
|
2019-03-20 18:21:50 +01:00
|
|
|
return Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: <Widget>[
|
|
|
|
Row(children: <Widget>[
|
2020-01-30 10:20:36 +01:00
|
|
|
avatar,
|
2019-09-07 11:48:59 +02:00
|
|
|
SizedBox(width: 8),
|
2019-03-20 18:21:50 +01:00
|
|
|
Expanded(
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: <Widget>[
|
2020-01-30 10:20:36 +01:00
|
|
|
UserName(login),
|
2019-09-07 11:48:59 +02:00
|
|
|
SizedBox(height: 2),
|
2019-03-20 18:21:50 +01:00
|
|
|
Text(
|
2020-01-30 10:20:36 +01:00
|
|
|
timeago.format(createdAt),
|
2019-11-08 11:29:08 +01:00
|
|
|
style: TextStyle(
|
2020-01-27 08:11:51 +01:00
|
|
|
color: theme.palette.tertiaryText, fontSize: 13),
|
2019-03-20 18:21:50 +01:00
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
]),
|
2019-09-07 11:48:59 +02:00
|
|
|
SizedBox(height: 12),
|
2020-01-30 10:20:36 +01:00
|
|
|
MarkdownView(body), // TODO: link
|
2019-09-07 11:48:59 +02:00
|
|
|
SizedBox(height: 12),
|
2020-01-30 10:20:36 +01:00
|
|
|
if (widgets != null)
|
|
|
|
...widgets
|
2019-03-20 18:21:50 +01:00
|
|
|
],
|
|
|
|
);
|
2019-01-28 14:32:01 +01:00
|
|
|
}
|
|
|
|
}
|