Skip to content

Commit

Permalink
feat: add and remove reactions
Browse files Browse the repository at this point in the history
  • Loading branch information
pd4d10 committed Apr 5, 2019
1 parent af79d33 commit 3bc1e7e
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 44 deletions.
11 changes: 11 additions & 0 deletions lib/providers/settings.dart
Expand Up @@ -346,6 +346,17 @@ class SettingsProviderState extends State<SettingsProvider> {
return true;
}

Future<dynamic> postWithCredentials(String url,
{String contentType, String body}) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
final res = await http
.post(prefix + url, headers: headers, body: body ?? {})
.timeout(_timeoutDuration);

// print(res.body);
return true;
}

Future<dynamic> deleteWithCredentials(String url) async {
var headers = {HttpHeaders.authorizationHeader: 'token $token'};
final res = await http
Expand Down
30 changes: 28 additions & 2 deletions lib/screens/issue.dart
Expand Up @@ -53,6 +53,7 @@ class _IssueScreenState extends State<IssueScreen> {

String get issueChunk {
var base = '''
id
title
createdAt
body
Expand Down Expand Up @@ -82,6 +83,7 @@ commits {
var base = '''
__typename
... on IssueComment {
id
createdAt
body
author {
Expand Down Expand Up @@ -344,6 +346,26 @@ __typename
);
}

_handleReaction(payload) {
return (String emojiKey, bool isRemove) async {
if (emojiKey == null) return;

var id = payload['id'] as String;
var operation = isRemove ? 'remove' : 'add';
await SettingsProvider.of(context).query('''
mutation {
${operation}Reaction(input: {subjectId: "$id", content: $emojiKey}) {
clientMutationId
}
}
''');
setState(() {
payload[emojiKey]['totalCount'] += isRemove ? -1 : 1;
payload[emojiKey]['viewerHasReacted'] = !isRemove;
});
};
}

@override
Widget build(BuildContext context) {
return LongListScaffold(
Expand Down Expand Up @@ -394,13 +416,17 @@ __typename
],
),
Padding(padding: EdgeInsets.only(bottom: 16)),
CommentItem(payload),
CommentItem(
payload,
onReaction: _handleReaction(payload),
),
],
),
)
]);
},
itemBuilder: (itemPayload) => TimelineItem(itemPayload),
itemBuilder: (itemPayload) =>
TimelineItem(itemPayload, onReaction: _handleReaction(itemPayload)),
onRefresh: () async {
var res = await _queryIssue();
int totalCount = res['timeline']['totalCount'];
Expand Down
64 changes: 43 additions & 21 deletions lib/utils/utils.dart
Expand Up @@ -91,37 +91,58 @@ class DialogOption<T> {
DialogOption({this.value, this.widget});
}

Future<T> showOptions<T>(BuildContext context, List<DialogOption<T>> options) {
var builder = (BuildContext context) {
return CupertinoAlertDialog(
actions: options.map((option) {
return CupertinoDialogAction(
child: option.widget,
onPressed: () {
Navigator.pop(context, option.value);
},
);
}).toList(),
);
};
Future<T> showDialogOptions<T>(
BuildContext context, List<DialogOption<T>> options) {
var title = Text('Pick your reaction');
var cancelWidget = Text('Cancel');

switch (SettingsProvider.of(context).theme) {
case ThemeMap.cupertino:
return showCupertinoDialog<T>(
context: context,
builder: builder,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: title,
actions: options.map((option) {
return CupertinoDialogAction(
child: option.widget,
onPressed: () {
Navigator.pop(context, option.value);
},
);
}).toList()
..add(
CupertinoDialogAction(
child: cancelWidget,
isDestructiveAction: true,
onPressed: () {
Navigator.pop(context, null);
},
),
),
);
},
);
default:
return showDialog(
context: context,
builder: (BuildContext context) {
return Dialog(
child: Column(
children: <Widget>[
PopupMenuItem(child: Text('a')),
PopupMenuItem(child: Text('b')),
],
),
return SimpleDialog(
title: title,
children: options.map<Widget>((option) {
return SimpleDialogOption(
child: option.widget,
onPressed: () {
Navigator.pop(context, option.value);
},
);
}).toList()
..add(SimpleDialogOption(
child: cancelWidget,
onPressed: () {
Navigator.pop(context, null);
},
)),
);
},
);
Expand Down Expand Up @@ -162,6 +183,7 @@ class Palette {
static const link = Color(0xff0366d6);
static const branchName = Palette.link;
static const branchBackground = Color(0xffeaf5ff);
static const emojiBackground = Color(0xfff1f8ff);
}

// final pageSize = 5;
Expand Down
85 changes: 67 additions & 18 deletions lib/widgets/comment_item.dart
Expand Up @@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import '../utils/utils.dart';
import 'avatar.dart';
import 'link.dart';
import 'user_name.dart';

final emojiMap = {
Expand All @@ -17,8 +18,21 @@ final emojiMap = {

class CommentItem extends StatelessWidget {
final Map<String, dynamic> payload;
final Function(String emojiKey, bool isRemove) onReaction;

CommentItem(this.payload);
CommentItem(this.payload, {@required this.onReaction});

bool _hasReacted(String emojiKey) {
if (payload[emojiKey] == null) return false;
return payload[emojiKey]['viewerHasReacted'] as bool;
}

Decoration _getDecorationByKey(String emojiKey) {
return BoxDecoration(
color: _hasReacted(emojiKey)
? Palette.emojiBackground
: Colors.transparent);
}

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -55,28 +69,63 @@ class CommentItem extends StatelessWidget {
),
Wrap(
children: emojiMap.entries
.where((entry) => payload[entry.key]['totalCount'] != 0)
.map((entry) {
.where((entry) => payload[entry.key]['totalCount'] as int != 0)
.map<Widget>((entry) {
var emojiKey = entry.key;
var emoji = entry.value;
int count = payload[entry.key]['totalCount'];
var count = payload[entry.key]['totalCount'] as int;

return Container(
padding: EdgeInsets.all(6),
child: RichText(
text: TextSpan(
style: TextStyle(fontSize: 16),
children: [
TextSpan(text: emoji),
TextSpan(text: ' '),
TextSpan(
text: count.toString(),
style: TextStyle(color: Palette.link),
),
],
return Link(
onTap: () {
onReaction(emojiKey, _hasReacted(emojiKey));
},
child: Container(
padding: EdgeInsets.all(6),
decoration: _getDecorationByKey(emojiKey),
child: RichText(
text: TextSpan(
style: TextStyle(fontSize: 16),
children: [
TextSpan(text: emoji),
TextSpan(text: ' '),
TextSpan(
text: count.toString(),
style: TextStyle(color: Palette.link),
),
],
),
),
),
);
}).toList(),
}).toList()
..add(
Link(
onTap: () async {
var result = await showDialogOptions(
context,
emojiMap.entries.map((entry) {
var emojiKey = entry.key;
return DialogOption(
value: emojiKey,
widget: Container(
decoration: _getDecorationByKey(emojiKey),
child: Text(emojiKey + ' ' + entry.value),
),
);
}).toList(),
);
onReaction(result, _hasReacted(result));
},
child: Container(
padding: EdgeInsets.all(12),
child: Icon(
Octicons.smiley,
color: Palette.link,
size: 16,
),
),
),
),
),
],
);
Expand Down
2 changes: 2 additions & 0 deletions lib/widgets/loading.dart
Expand Up @@ -8,6 +8,8 @@ class Loading extends StatelessWidget {
Loading({this.more = false});

Widget _buildIndicator(BuildContext context) {
// return Image.asset('images/loading.webp');

switch (SettingsProvider.of(context).theme) {
case ThemeMap.cupertino:
return CupertinoActivityIndicator(radius: 12);
Expand Down
7 changes: 4 additions & 3 deletions lib/widgets/timeline_item.dart
Expand Up @@ -7,8 +7,9 @@ import 'user_name.dart';

class TimelineItem extends StatelessWidget {
final Map<String, dynamic> payload;
final Function(String emojiKey, bool isRemove) onReaction;

TimelineItem(this.payload);
TimelineItem(this.payload, {@required this.onReaction});

TextSpan _buildReviewText(BuildContext context, item) {
switch (item['state']) {
Expand Down Expand Up @@ -95,7 +96,7 @@ class TimelineItem extends StatelessWidget {
item: payload,
);
case 'IssueComment':
return CommentItem(payload);
return CommentItem(payload, onReaction: onReaction);
case 'CrossReferencedEvent':
return _buildItem(
actor: payload['actor']['login'],
Expand Down Expand Up @@ -295,7 +296,7 @@ class TimelineItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(10),
padding: EdgeInsets.all(12),
child: _buildByType(context),
);
}
Expand Down

0 comments on commit 3bc1e7e

Please sign in to comment.