1
0
mirror of https://github.com/git-touch/git-touch synced 2025-02-21 05:51:06 +01:00

improvement: add event card

This commit is contained in:
Rongjian Zhang 2019-12-20 20:52:49 +08:00
parent 1820929eb2
commit ddc0f69e95
3 changed files with 299 additions and 137 deletions

View File

@ -6,11 +6,11 @@ part 'github.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEvent {
GithubEventActor actor;
GithubEventUser actor;
String type;
GithubEventRepo repo;
String createdAt;
Map<String, dynamic> payload;
DateTime createdAt;
GithubEventPayload payload;
Tuple2<String, String> _repo;
String get repoOwner {
@ -34,14 +34,14 @@ class GithubEvent {
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventActor {
class GithubEventUser {
String login;
String avatarUrl;
GithubEventActor();
GithubEventUser();
factory GithubEventActor.fromJson(Map<String, dynamic> json) =>
_$GithubEventActorFromJson(json);
factory GithubEventUser.fromJson(Map<String, dynamic> json) =>
_$GithubEventUserFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
@ -54,6 +54,60 @@ class GithubEventRepo {
_$GithubEventRepoFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventPayload {
GithubEventIssue issue;
GithubEventIssue pullRequest;
GithubEventComment comment;
String action;
String ref;
String before;
String after;
List<GithubEventCommit> commits;
Map<String, dynamic> forkee;
GithubEventPayload();
factory GithubEventPayload.fromJson(Map<String, dynamic> json) =>
_$GithubEventPayloadFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubEventIssue {
String title;
GithubEventUser user;
int number;
String body;
dynamic pullRequest;
GithubEventIssue();
factory GithubEventIssue.fromJson(Map<String, dynamic> json) =>
_$GithubEventIssueFromJson(json);
}
@JsonSerializable()
class GithubEventComment {
String body;
GithubEventUser user;
GithubEventComment();
factory GithubEventComment.fromJson(Map<String, dynamic> json) =>
_$GithubEventCommentFromJson(json);
}
@JsonSerializable()
class GithubEventCommit {
String sha;
String message;
GithubEventCommit();
factory GithubEventCommit.fromJson(Map<String, dynamic> json) =>
_$GithubEventCommitFromJson(json);
}
@JsonSerializable()
class GithubTrendingItem {
String author;

View File

@ -10,13 +10,17 @@ GithubEvent _$GithubEventFromJson(Map<String, dynamic> json) {
return GithubEvent()
..actor = json['actor'] == null
? null
: GithubEventActor.fromJson(json['actor'] as Map<String, dynamic>)
: GithubEventUser.fromJson(json['actor'] as Map<String, dynamic>)
..type = json['type'] as String
..repo = json['repo'] == null
? null
: GithubEventRepo.fromJson(json['repo'] as Map<String, dynamic>)
..createdAt = json['created_at'] as String
..payload = json['payload'] as Map<String, dynamic>;
..createdAt = json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String)
..payload = json['payload'] == null
? null
: GithubEventPayload.fromJson(json['payload'] as Map<String, dynamic>);
}
Map<String, dynamic> _$GithubEventToJson(GithubEvent instance) =>
@ -24,17 +28,17 @@ Map<String, dynamic> _$GithubEventToJson(GithubEvent instance) =>
'actor': instance.actor,
'type': instance.type,
'repo': instance.repo,
'created_at': instance.createdAt,
'created_at': instance.createdAt?.toIso8601String(),
'payload': instance.payload,
};
GithubEventActor _$GithubEventActorFromJson(Map<String, dynamic> json) {
return GithubEventActor()
GithubEventUser _$GithubEventUserFromJson(Map<String, dynamic> json) {
return GithubEventUser()
..login = json['login'] as String
..avatarUrl = json['avatar_url'] as String;
}
Map<String, dynamic> _$GithubEventActorToJson(GithubEventActor instance) =>
Map<String, dynamic> _$GithubEventUserToJson(GithubEventUser instance) =>
<String, dynamic>{
'login': instance.login,
'avatar_url': instance.avatarUrl,
@ -49,6 +53,89 @@ Map<String, dynamic> _$GithubEventRepoToJson(GithubEventRepo instance) =>
'name': instance.name,
};
GithubEventPayload _$GithubEventPayloadFromJson(Map<String, dynamic> json) {
return GithubEventPayload()
..issue = json['issue'] == null
? null
: GithubEventIssue.fromJson(json['issue'] as Map<String, dynamic>)
..pullRequest = json['pull_request'] == null
? null
: GithubEventIssue.fromJson(
json['pull_request'] as Map<String, dynamic>)
..comment = json['comment'] == null
? null
: GithubEventComment.fromJson(json['comment'] as Map<String, dynamic>)
..action = json['action'] as String
..ref = json['ref'] as String
..before = json['before'] as String
..after = json['after'] as String
..commits = (json['commits'] as List)
?.map((e) => e == null
? null
: GithubEventCommit.fromJson(e as Map<String, dynamic>))
?.toList()
..forkee = json['forkee'] as Map<String, dynamic>;
}
Map<String, dynamic> _$GithubEventPayloadToJson(GithubEventPayload instance) =>
<String, dynamic>{
'issue': instance.issue,
'pull_request': instance.pullRequest,
'comment': instance.comment,
'action': instance.action,
'ref': instance.ref,
'before': instance.before,
'after': instance.after,
'commits': instance.commits,
'forkee': instance.forkee,
};
GithubEventIssue _$GithubEventIssueFromJson(Map<String, dynamic> json) {
return GithubEventIssue()
..title = json['title'] as String
..user = json['user'] == null
? null
: GithubEventUser.fromJson(json['user'] as Map<String, dynamic>)
..number = json['number'] as int
..body = json['body'] as String
..pullRequest = json['pull_request'];
}
Map<String, dynamic> _$GithubEventIssueToJson(GithubEventIssue instance) =>
<String, dynamic>{
'title': instance.title,
'user': instance.user,
'number': instance.number,
'body': instance.body,
'pull_request': instance.pullRequest,
};
GithubEventComment _$GithubEventCommentFromJson(Map<String, dynamic> json) {
return GithubEventComment()
..body = json['body'] as String
..user = json['user'] == null
? null
: GithubEventUser.fromJson(json['user'] as Map<String, dynamic>);
}
Map<String, dynamic> _$GithubEventCommentToJson(GithubEventComment instance) =>
<String, dynamic>{
'body': instance.body,
'user': instance.user,
};
GithubEventCommit _$GithubEventCommitFromJson(Map<String, dynamic> json) {
return GithubEventCommit()
..sha = json['sha'] as String
..message = json['message'] as String;
}
Map<String, dynamic> _$GithubEventCommitToJson(GithubEventCommit instance) =>
<String, dynamic>{
'sha': instance.sha,
'message': instance.message,
};
GithubTrendingItem _$GithubTrendingItemFromJson(Map<String, dynamic> json) {
return GithubTrendingItem()
..author = json['author'] as String

View File

@ -3,6 +3,7 @@ import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:primer/primer.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
import 'avatar.dart';
@ -19,7 +20,6 @@ class EventItem extends StatelessWidget {
text: text,
style: TextStyle(
color: theme.palette.primary,
fontWeight: FontWeight.w600,
),
);
}
@ -50,71 +50,73 @@ class EventItem extends StatelessWidget {
Text(detail.trim(), overflow: TextOverflow.ellipsis, maxLines: 5);
}
return Link(
url: url,
child: Container(
padding: CommonStyle.padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Link(
url: '/' + event.actor.login,
child: Avatar.medium(url: event.actor.avatarUrl),
),
SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: join(SizedBox(height: 8), [
RichText(
text: TextSpan(
style: TextStyle(
fontSize: 16,
color: theme.palette.text,
fontWeight: FontWeight.w500,
),
children: [
_buildLinkSpan(theme, event.actor.login),
...spans,
],
return Container(
padding: CommonStyle.padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Link(
url: '/' + event.actor.login,
child: Avatar.small(url: event.actor.avatarUrl),
),
SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: join(SizedBox(height: 8), [
RichText(
text: TextSpan(
style: TextStyle(
fontSize: 16,
color: theme.palette.text,
),
children: [
_buildLinkSpan(theme, event.actor.login),
...spans,
],
),
if (detailWidget != null)
DefaultTextStyle(
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Icon(iconData,
color: theme.palette.tertiaryText, size: 14),
SizedBox(width: 4),
Text(timeago.format(event.createdAt),
style: TextStyle(
fontSize: 13,
color: theme.palette.tertiaryText,
)),
// Expanded(child: Container()),
// GestureDetector(
// child: Icon(Icons.more_horiz),
// onTap: () {
// theme.showActions(context, actionItems);
// },
// ),
],
),
if (detailWidget != null)
Container(
padding: EdgeInsets.all(12),
decoration: BoxDecoration(
color: PrimerColors.gray100,
borderRadius: BorderRadius.all(Radius.circular(4))),
child: DefaultTextStyle(
style: TextStyle(
color: theme.palette.text, fontSize: 14),
child: detailWidget,
),
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Icon(iconData,
color: theme.palette.tertiaryText, size: 14),
SizedBox(width: 4),
Text(timeago.format(DateTime.parse(event.createdAt)),
style: TextStyle(
fontSize: 13,
color: theme.palette.tertiaryText,
)),
Expanded(child: Container()),
GestureDetector(
child: Icon(Icons.more_horiz),
onTap: () {
theme.showActions(context, actionItems);
},
),
],
),
]),
),
]),
),
],
),
],
),
),
],
),
],
),
);
}
@ -134,6 +136,22 @@ class EventItem extends StatelessWidget {
);
}
Widget _buildIssueCard(GithubEventIssue issue, String body) {
return Column(
children: <Widget>[
Row(
children: <Widget>[
Icon(Octicons.issue_opened),
Text('#' + issue.number.toString()),
Text(issue.title),
],
),
SizedBox(height: 4),
if (body != null) Text(body),
],
);
}
@override
build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
@ -154,8 +172,8 @@ class EventItem extends StatelessWidget {
// TODO:
return _buildDefaultItem(context);
case 'ForkEvent':
final forkeeOwner = event.payload['forkee']['owner']['login'] as String;
final forkeeName = event.payload['forkee']['name'] as String;
final forkeeOwner = event.payload.forkee['owner']['login'] as String;
final forkeeName = event.payload.forkee['name'] as String;
return _buildItem(
context: context,
spans: [
@ -181,47 +199,46 @@ class EventItem extends StatelessWidget {
// TODO:
return _buildDefaultItem(context);
case 'IssueCommentEvent':
final isPullRequest = event.payload['issue']['pull_request'] != null;
final resource = isPullRequest ? 'pull request' : 'issue';
final number = event.payload['issue']['number'] as int;
final isPullRequest = event.payload.issue.pullRequest != null;
return _buildItem(
context: context,
spans: [
TextSpan(text: ' commented on $resource '),
_buildLinkSpan(theme, '#$number'),
TextSpan(
text:
' commented on ${isPullRequest ? 'pull request' : 'issue'} '),
_buildLinkSpan(theme, '#${event.payload.issue.number}'),
TextSpan(text: ' at '),
_buildRepo(theme),
// TextSpan(text: event.payload['comment']['body'])
],
detail: event.payload['comment']['body'],
detailWidget:
_buildIssueCard(event.payload.issue, event.payload.comment.body),
iconData: Octicons.comment_discussion,
url:
'/${event.repoOwner}/${event.repoName}/${isPullRequest ? 'pulls' : 'issues'}/$number',
'/${event.repoOwner}/${event.repoName}/${isPullRequest ? 'pulls' : 'issues'}/${event.payload.issue.number}',
actionItems: [
..._getUserActions([event.actor.login, event.repoOwner]),
ActionItem.pullRequest(event.repoOwner, event.repoName, number),
ActionItem.pullRequest(
event.repoOwner, event.repoName, event.payload.issue.number),
],
);
case 'IssuesEvent':
final action = event.payload['action'];
final number = event.payload['issue']['number'] as int;
final issue = event.payload.issue;
return _buildItem(
context: context,
spans: [
TextSpan(text: ' $action issue '),
_buildLinkSpan(theme, '#$number'),
TextSpan(text: ' ${event.payload.action} issue '),
_buildLinkSpan(theme, '#${issue.number}'),
TextSpan(text: ' at '),
_buildRepo(theme),
],
iconData: Octicons.issue_opened,
detail: event.payload['issue']['title'],
url: '/${event.repoOwner}/${event.repoName}/issues/$number',
detailWidget: _buildIssueCard(issue, issue.body),
url: '/${event.repoOwner}/${event.repoName}/issues/${issue.number}',
actionItems: [
..._getUserActions([event.actor.login, event.repoOwner]),
ActionItem.repository(event.repoOwner, event.repoName),
ActionItem.issue(event.repoOwner, event.repoName, number),
ActionItem.issue(event.repoOwner, event.repoName, issue.number),
],
);
case 'LabelEvent':
@ -239,89 +256,93 @@ class EventItem extends StatelessWidget {
// TODO:
return _buildDefaultItem(context);
case 'PullRequestEvent':
final action = event.payload['action'];
final number = event.payload['pull_request']['number'] as int;
final pr = event.payload.pullRequest;
return _buildItem(
context: context,
spans: [
TextSpan(text: ' $action pull request '),
_buildLinkSpan(theme, '#$number'),
TextSpan(text: ' ${event.payload.action} pull request '),
_buildLinkSpan(theme, '#${pr.number}'),
TextSpan(text: ' at '),
_buildRepo(theme),
],
iconData: Octicons.git_pull_request,
detail: event.payload['pull_request']['title'],
url: '/${event.repoOwner}/${event.repoName}/pulls/$number',
detailWidget: _buildIssueCard(pr, pr.body),
url: '/${event.repoOwner}/${event.repoName}/pulls/${pr.number}',
actionItems: [
..._getUserActions([event.actor.login, event.repoOwner]),
ActionItem.repository(event.repoOwner, event.repoName),
ActionItem.pullRequest(event.repoOwner, event.repoName, number),
ActionItem.pullRequest(event.repoOwner, event.repoName, pr.number),
],
);
case 'PullRequestReviewEvent':
// TODO:
return _buildDefaultItem(context);
case 'PullRequestReviewCommentEvent':
final number = event.payload['pull_request']['number'] as int;
final pr = event.payload.pullRequest;
return _buildItem(
context: context,
spans: [
TextSpan(text: ' reviewed pull request '),
_buildLinkSpan(theme, '#$number'),
_buildLinkSpan(theme, '#${pr.number}'),
TextSpan(text: ' at '),
_buildRepo(theme),
],
detail: event.payload['comment']['body'],
url: '/${event.repoOwner}/${event.repoName}/pulls/$number',
detailWidget: _buildIssueCard(pr, pr.body),
url: '/${event.repoOwner}/${event.repoName}/pulls/${pr.number}',
actionItems: [
..._getUserActions([event.actor.login, event.repoOwner]),
ActionItem.repository(event.repoOwner, event.repoName),
ActionItem.pullRequest(event.repoOwner, event.repoName, number),
ActionItem.pullRequest(event.repoOwner, event.repoName, pr.number),
],
);
case 'PushEvent':
final ref = event.payload['ref'] as String;
final commits = event.payload['commits'] as List;
return _buildItem(
context: context,
spans: [
TextSpan(text: ' pushed to '),
WidgetSpan(
child: PrimerBranchName(ref.replaceFirst('refs/heads/', '')),
),
TextSpan(text: ' at '),
_buildRepo(theme)
],
spans: [TextSpan(text: ' pushed to '), _buildRepo(theme)],
iconData: Octicons.repo_push,
detailWidget: Column(
children: commits.map((commit) {
return Row(
children: <Widget>[
Text(
(commit['sha'] as String).substring(0, 7),
style: TextStyle(
color: theme.palette.primary,
fontSize: 13,
fontFamily: CommonStyle.monospace,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
style: TextStyle(color: theme.palette.primary),
children: [
TextSpan(
text: event.payload.commits.length.toString() +
' commits to '),
WidgetSpan(
child: PrimerBranchName(
event.payload.ref.replaceFirst('refs/heads/', '')),
),
),
SizedBox(width: 6),
Expanded(
child: Text(
commit['message'],
overflow: TextOverflow.ellipsis,
maxLines: 1,
],
),
),
...event.payload.commits.map((commit) {
return Row(
children: <Widget>[
Text(
commit.sha.substring(0, 7),
style: TextStyle(
color: theme.palette.primary,
fontSize: 13,
fontFamily: CommonStyle.monospace,
),
),
)
],
);
}).toList(),
SizedBox(width: 6),
Expanded(
child: Text(
commit.message,
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
)
],
);
}).toList()
],
),
url:
'https://github.com/${event.repoOwner}/${event.repoName}/compare/${event.payload['before']}...${event.payload['head']}',
'https://github.com/${event.repoOwner}/${event.repoName}/compare/${event.payload.before}...${event.payload.after}',
actionItems: [
..._getUserActions([event.actor.login, event.repoOwner]),
ActionItem.repository(event.repoOwner, event.repoName),