git-touch-android-ios-app/lib/widgets/event_item.dart

394 lines
13 KiB
Dart
Raw Normal View History

2018-07-05 16:08:19 +02:00
import 'package:flutter/material.dart';
2019-02-02 17:28:51 +01:00
import 'package:flutter/cupertino.dart';
2019-11-03 16:33:24 +01:00
import 'package:git_touch/models/theme.dart';
2019-09-23 12:28:33 +02:00
import 'package:git_touch/screens/repository.dart';
2019-11-02 16:36:49 +01:00
import 'package:git_touch/widgets/action_button.dart';
2019-11-03 16:33:24 +01:00
import 'package:provider/provider.dart';
2019-08-31 15:37:29 +02:00
import 'package:timeago/timeago.dart' as timeago;
2019-05-12 09:14:28 +02:00
import 'package:primer/primer.dart';
2019-02-07 07:35:19 +01:00
import '../screens/issue.dart';
import '../screens/user.dart';
import 'avatar.dart';
import '../widgets/link.dart';
import '../utils/utils.dart';
2018-07-05 16:08:19 +02:00
2019-02-10 05:54:48 +01:00
class EventPayload {
String actorLogin;
String actorAvatarUrl;
String type;
2019-11-02 16:56:10 +01:00
String repoOwner;
String repoName;
2019-02-10 05:54:48 +01:00
Map<String, dynamic> payload;
2019-08-31 15:37:29 +02:00
DateTime createdAt;
2019-02-10 05:54:48 +01:00
EventPayload.fromJson(input) {
actorLogin = input['actor']['login'];
actorAvatarUrl = input['actor']['avatar_url'];
type = input['type'];
payload = input['payload'];
2019-08-31 15:37:29 +02:00
createdAt = DateTime.parse(input['created_at']);
2019-11-02 16:56:10 +01:00
final repoFullName = input['repo']['name'] as String;
if (repoFullName != null) {
final ls = parseRepositoryFullName(repoFullName);
repoOwner = ls.item1;
repoName = ls.item2;
}
2019-02-10 05:54:48 +01:00
}
}
2018-07-05 16:08:19 +02:00
class EventItem extends StatelessWidget {
2019-02-10 05:54:48 +01:00
final EventPayload event;
2018-07-05 16:08:19 +02:00
EventItem(this.event);
2019-11-02 16:36:49 +01:00
static const linkStyle = TextStyle(
color: PrimerColors.blue500,
fontWeight: FontWeight.w600,
);
2019-11-02 16:56:10 +01:00
TextSpan _buildRepo() =>
TextSpan(text: '${event.repoOwner}/${event.repoName}', style: linkStyle);
2018-07-05 16:08:19 +02:00
2019-11-02 16:56:10 +01:00
TextSpan _buildIssue(int number) =>
TextSpan(text: '#$number', style: linkStyle);
2019-11-03 16:23:05 +01:00
Iterable<ActionItem> _getUserActions(List<String> users) {
// Remove duplicates
return users.toSet().map((user) {
return ActionItem.user(user);
});
}
2019-02-07 10:19:08 +01:00
Widget _buildItem({
@required BuildContext context,
2019-09-25 07:34:13 +02:00
@required List<InlineSpan> spans,
2019-02-07 10:19:08 +01:00
String detail,
Widget detailWidget,
2019-02-07 10:19:08 +01:00
IconData iconData = Octicons.octoface,
2019-02-10 12:32:30 +01:00
WidgetBuilder screenBuilder,
2019-09-08 14:33:04 +02:00
String url,
2019-11-02 16:36:49 +01:00
List<ActionItem> actionItems,
2019-02-07 10:19:08 +01:00
}) {
2019-09-07 11:08:24 +02:00
if (detailWidget == null && detail != null) {
detailWidget =
Text(detail.trim(), overflow: TextOverflow.ellipsis, maxLines: 5);
2019-08-31 15:37:29 +02:00
}
return Link(
screenBuilder: screenBuilder,
2019-09-08 14:33:04 +02:00
url: url,
2019-11-02 16:36:49 +01:00
onLongPress: () async {
if (actionItems == null) return;
2019-11-03 16:33:24 +01:00
await Provider.of<ThemeModel>(context)
.showActions(context, actionItems);
2019-11-02 16:36:49 +01:00
},
2019-08-31 15:37:29 +02:00
child: Container(
2019-10-02 10:09:54 +02:00
padding: CommonStyle.padding,
2019-08-31 15:37:29 +02:00
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
2019-10-03 04:12:22 +02:00
Link(
child: Avatar.medium(url: event.actorAvatarUrl),
screenBuilder: (_) => UserScreen(event.actorLogin),
2019-09-07 11:08:24 +02:00
),
SizedBox(width: 10),
2019-08-31 15:37:29 +02:00
Expanded(
2019-09-07 11:08:24 +02:00
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
2019-09-08 14:33:04 +02:00
children: join(SizedBox(height: 8), [
2019-09-07 11:08:24 +02:00
RichText(
text: TextSpan(
style: TextStyle(
2019-09-13 09:55:58 +02:00
fontSize: 16,
color: PrimerColors.gray900,
fontWeight: FontWeight.w500,
),
2019-09-07 11:08:24 +02:00
children: [
2019-11-02 16:36:49 +01:00
TextSpan(text: event.actorLogin, style: linkStyle),
2019-09-07 11:08:24 +02:00
...spans,
],
2019-08-31 15:37:29 +02:00
),
2019-09-07 11:08:24 +02:00
),
2019-09-12 10:30:35 +02:00
if (detailWidget != null)
DefaultTextStyle(
style: TextStyle(
2019-09-13 09:55:58 +02:00
color: PrimerColors.gray900, fontSize: 14),
2019-09-12 10:30:35 +02:00
child: detailWidget,
),
2019-09-07 11:08:24 +02:00
Row(
2019-09-13 09:55:58 +02:00
crossAxisAlignment: CrossAxisAlignment.start,
2019-09-07 11:08:24 +02:00
children: <Widget>[
2019-09-13 09:55:58 +02:00
Icon(iconData, color: PrimerColors.gray500, size: 14),
2019-09-07 11:08:24 +02:00
SizedBox(width: 4),
Text(timeago.format(event.createdAt),
style: TextStyle(
2019-09-13 09:55:58 +02:00
fontSize: 13,
color: PrimerColors.gray500,
))
2019-09-07 11:08:24 +02:00
],
),
]),
),
2019-01-31 07:37:25 +01:00
),
2019-08-31 15:37:29 +02:00
],
),
],
),
2018-07-05 16:08:19 +02:00
),
);
}
2019-02-07 10:19:08 +01:00
@override
build(BuildContext context) {
var defaultItem = _buildItem(
context: context,
2019-02-10 05:54:48 +01:00
spans: [
TextSpan(
text: ' ' + event.type,
2019-09-13 09:55:58 +02:00
style: TextStyle(color: PrimerColors.blue500),
2019-02-10 05:54:48 +01:00
)
],
iconData: Octicons.octoface,
detail: 'Woops, ${event.type} not implemented yet',
);
// all events types here:
// https://developer.github.com/v3/activity/events/types/#event-types--payloads
2019-02-07 10:19:08 +01:00
switch (event.type) {
case 'CheckRunEvent':
case 'CheckSuiteEvent':
case 'CommitCommentEvent':
case 'ContentReferenceEvent':
case 'CreateEvent':
case 'DeleteEvent':
case 'DeploymentEvent':
case 'DeploymentStatusEvent':
case 'DownloadEvent':
case 'FollowEvent':
// TODO:
return defaultItem;
case 'ForkEvent':
2019-11-02 16:36:49 +01:00
final forkeeOwner = event.payload['forkee']['owner']['login'] as String;
final forkeeName = event.payload['forkee']['name'] as String;
2019-02-07 10:19:08 +01:00
return _buildItem(
context: context,
spans: [
TextSpan(text: ' forked '),
2019-11-02 16:36:49 +01:00
TextSpan(text: '$forkeeOwner/$forkeeName', style: linkStyle),
TextSpan(text: ' from '),
2019-11-02 16:56:10 +01:00
_buildRepo(),
],
iconData: Octicons.repo_forked,
2019-11-02 16:36:49 +01:00
screenBuilder: (_) => RepositoryScreen(forkeeOwner, forkeeName),
actionItems: [
2019-11-03 16:23:05 +01:00
..._getUserActions([event.actorLogin, forkeeOwner]),
2019-11-02 16:36:49 +01:00
ActionItem.repository(forkeeOwner, forkeeName),
2019-11-03 16:23:05 +01:00
ActionItem.repository(event.repoOwner, event.repoName),
2019-11-02 16:36:49 +01:00
],
);
case 'ForkApplyEvent':
case 'GitHubAppAuthorizationEvent':
case 'GistEvent':
case 'GollumEvent':
case 'InstallationEvent':
case 'InstallationRepositoriesEvent':
// TODO:
return defaultItem;
case 'IssueCommentEvent':
2019-11-02 16:36:49 +01:00
final isPullRequest = event.payload['issue']['pull_request'] != null;
final resource = isPullRequest ? 'pull request' : 'issue';
final number = event.payload['issue']['number'] as int;
return _buildItem(
context: context,
spans: [
TextSpan(text: ' commented on $resource '),
2019-11-02 16:56:10 +01:00
_buildIssue(number),
2019-02-07 10:19:08 +01:00
TextSpan(text: ' at '),
2019-11-02 16:56:10 +01:00
_buildRepo(),
// TextSpan(text: event.payload['comment']['body'])
2019-02-07 10:19:08 +01:00
],
detail: event.payload['comment']['body'],
iconData: Octicons.comment_discussion,
2019-11-02 16:36:49 +01:00
screenBuilder: (_) => IssueScreen(
2019-11-02 17:02:41 +01:00
event.repoOwner,
event.repoName,
number,
2019-07-28 15:59:18 +02:00
isPullRequest: isPullRequest,
),
2019-11-02 16:36:49 +01:00
actionItems: [
2019-11-03 16:23:05 +01:00
..._getUserActions([event.actorLogin, event.repoOwner]),
2019-11-03 16:13:37 +01:00
ActionItem.issue(event.repoOwner, event.repoName, number,
isPullRequest: isPullRequest),
2019-11-02 16:36:49 +01:00
],
2019-02-07 10:19:08 +01:00
);
case 'IssuesEvent':
2019-11-02 16:36:49 +01:00
final action = event.payload['action'];
final number = event.payload['issue']['number'] as int;
2019-02-07 10:19:08 +01:00
return _buildItem(
context: context,
spans: [
2019-11-02 16:36:49 +01:00
TextSpan(text: ' $action issue '),
2019-11-02 16:56:10 +01:00
_buildIssue(number),
2019-02-07 10:19:08 +01:00
TextSpan(text: ' at '),
2019-11-02 16:56:10 +01:00
_buildRepo(),
2019-02-07 10:19:08 +01:00
],
iconData: Octicons.issue_opened,
detail: event.payload['issue']['title'],
2019-11-02 17:02:41 +01:00
screenBuilder: (_) =>
IssueScreen(event.repoOwner, event.repoName, number),
2019-11-02 16:36:49 +01:00
actionItems: [
2019-11-03 16:23:05 +01:00
..._getUserActions([event.actorLogin, event.repoOwner]),
2019-11-02 16:56:10 +01:00
ActionItem.repository(event.repoOwner, event.repoName),
ActionItem.issue(event.repoOwner, event.repoName, number),
2019-11-02 16:36:49 +01:00
],
2019-02-07 10:19:08 +01:00
);
case 'LabelEvent':
case 'MarketplacePurchaseEvent':
case 'MemberEvent':
case 'MembershipEvent':
case 'MilestoneEvent':
case 'OrganizationEvent':
case 'OrgBlockEvent':
case 'PageBuildEvent':
case 'ProjectCardEvent':
case 'ProjectColumnEvent':
case 'ProjectEvent':
case 'PublicEvent':
// TODO:
return defaultItem;
2019-02-07 10:19:08 +01:00
case 'PullRequestEvent':
2019-11-02 16:36:49 +01:00
final action = event.payload['action'];
final number = event.payload['pull_request']['number'] as int;
2019-02-07 10:19:08 +01:00
return _buildItem(
context: context,
spans: [
2019-11-02 16:36:49 +01:00
TextSpan(text: ' $action pull request '),
2019-11-02 16:56:10 +01:00
_buildIssue(number),
2019-02-07 10:19:08 +01:00
TextSpan(text: ' at '),
2019-11-02 16:56:10 +01:00
_buildRepo(),
2019-02-07 10:19:08 +01:00
],
iconData: Octicons.git_pull_request,
detail: event.payload['pull_request']['title'],
2019-11-02 16:36:49 +01:00
screenBuilder: (_) => IssueScreen(
2019-11-02 17:02:41 +01:00
event.repoOwner,
event.repoName,
number,
2019-07-28 15:59:18 +02:00
isPullRequest: true,
),
2019-11-02 16:36:49 +01:00
actionItems: [
2019-11-03 16:23:05 +01:00
..._getUserActions([event.actorLogin, event.repoOwner]),
2019-11-02 16:56:10 +01:00
ActionItem.repository(event.repoOwner, event.repoName),
2019-11-03 16:13:37 +01:00
ActionItem.issue(event.repoOwner, event.repoName, number,
isPullRequest: true),
2019-11-02 16:36:49 +01:00
],
2019-02-07 10:19:08 +01:00
);
case 'PullRequestReviewEvent':
// TODO:
return defaultItem;
2019-02-07 10:19:08 +01:00
case 'PullRequestReviewCommentEvent':
2019-11-02 16:36:49 +01:00
final number = event.payload['pull_request']['number'] as int;
2019-02-07 10:19:08 +01:00
return _buildItem(
context: context,
spans: [
TextSpan(text: ' reviewed pull request '),
2019-11-02 16:56:10 +01:00
_buildIssue(number),
2019-02-07 10:19:08 +01:00
TextSpan(text: ' at '),
2019-11-02 16:56:10 +01:00
_buildRepo(),
2019-02-07 10:19:08 +01:00
],
detail: event.payload['comment']['body'],
2019-11-02 16:36:49 +01:00
screenBuilder: (_) => IssueScreen(
2019-11-02 17:02:41 +01:00
event.repoOwner,
event.repoName,
number,
2019-07-28 15:59:18 +02:00
isPullRequest: true,
),
2019-11-02 16:36:49 +01:00
actionItems: [
2019-11-03 16:23:05 +01:00
..._getUserActions([event.actorLogin, event.repoOwner]),
2019-11-02 16:56:10 +01:00
ActionItem.repository(event.repoOwner, event.repoName),
2019-11-03 16:13:37 +01:00
ActionItem.issue(event.repoOwner, event.repoName, number,
isPullRequest: true),
2019-11-02 16:36:49 +01:00
],
2019-02-07 10:19:08 +01:00
);
case 'PushEvent':
2019-11-02 16:36:49 +01:00
final ref = event.payload['ref'] as String;
final commits = event.payload['commits'] as List;
2019-02-07 10:19:08 +01:00
return _buildItem(
context: context,
spans: [
2019-08-31 15:37:29 +02:00
TextSpan(text: ' pushed to '),
2019-09-25 07:34:13 +02:00
WidgetSpan(
child: PrimerBranchName(ref.replaceFirst('refs/heads/', '')),
),
2019-02-07 10:19:08 +01:00
TextSpan(text: ' at '),
2019-11-02 16:56:10 +01:00
_buildRepo()
2019-02-07 10:19:08 +01:00
],
iconData: Octicons.repo_push,
2019-09-08 14:33:04 +02:00
detailWidget: Column(
children: commits.map((commit) {
2019-09-13 09:55:58 +02:00
return Row(
children: <Widget>[
Text(
(commit['sha'] as String).substring(0, 7),
style: TextStyle(
color: PrimerColors.blue500,
fontSize: 13,
2019-10-02 10:09:54 +02:00
fontFamily: CommonStyle.monospace,
2019-09-13 09:55:58 +02:00
),
2019-08-31 15:37:29 +02:00
),
2019-09-13 09:55:58 +02:00
SizedBox(width: 6),
Expanded(
child: Text(
commit['message'],
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
)
],
);
2019-09-08 14:33:04 +02:00
}).toList(),
),
2019-11-02 16:56:10 +01:00
url:
'https://github.com/${event.repoOwner}/${event.repoName}/compare/${event.payload['before']}...${event.payload['head']}',
2019-11-02 16:36:49 +01:00
actionItems: [
2019-11-03 16:23:05 +01:00
..._getUserActions([event.actorLogin, event.repoOwner]),
2019-11-02 16:56:10 +01:00
ActionItem.repository(event.repoOwner, event.repoName),
2019-11-02 16:36:49 +01:00
],
2019-02-07 10:19:08 +01:00
);
case 'ReleaseEvent':
case 'RepositoryEvent':
case 'RepositoryImportEvent':
case 'RepositoryVulnerabilityAlertEvent':
case 'SecurityAdvisoryEvent':
case 'StatusEvent':
case 'TeamEvent':
case 'TeamAddEvent':
// TODO:
return defaultItem;
case 'WatchEvent':
2019-02-07 10:19:08 +01:00
return _buildItem(
context: context,
2019-11-02 16:56:10 +01:00
spans: [TextSpan(text: ' starred '), _buildRepo()],
iconData: Octicons.star,
2019-11-02 16:56:10 +01:00
screenBuilder: (_) =>
RepositoryScreen(event.repoOwner, event.repoName),
2019-11-02 16:36:49 +01:00
actionItems: [
2019-11-03 16:23:05 +01:00
..._getUserActions([event.actorLogin, event.repoOwner]),
2019-11-02 16:56:10 +01:00
ActionItem.repository(event.repoOwner, event.repoName),
2019-11-02 16:36:49 +01:00
],
2019-02-07 10:19:08 +01:00
);
default:
return defaultItem;
2019-02-07 10:19:08 +01:00
}
}
2018-07-05 16:08:19 +02:00
}