1
0
mirror of https://github.com/git-touch/git-touch synced 2025-01-27 14:19:24 +01:00

refactor: merge issue and pull request screen

This commit is contained in:
Rongjian Zhang 2019-02-19 15:25:15 +08:00
parent cc8ea1d39a
commit 2bdd9216c3
11 changed files with 376 additions and 658 deletions

View File

@ -74,8 +74,8 @@ class _ListScaffoldState<T, K> extends State<ListScaffold<T, K>> {
cursor = _payload.cursor;
hasMore = _payload.hasMore;
} catch (err) {
// print(err);
error = err.toString();
throw err;
} finally {
if (mounted) {
setState(() {

View File

@ -71,8 +71,8 @@ class _LongListScaffoldState<T, K> extends State<LongListScaffold<T, K>> {
try {
payload = await widget.onRefresh();
} catch (err) {
// print(err);
error = err.toString();
throw err;
} finally {
if (mounted) {
setState(() {

View File

@ -3,7 +3,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/widgets.dart';
import '../providers/settings.dart';
typedef RefreshCallback = Future<void> Function();
typedef WidgetBuilder = Widget Function();
class SimpleScaffold extends StatelessWidget {

View File

@ -9,106 +9,306 @@ import '../widgets/comment_item.dart';
import '../providers/settings.dart';
import '../widgets/link.dart';
/// Screen for issue and pull request
class IssueScreen extends StatefulWidget {
final int number;
final String owner;
final String name;
final bool isPullRequest;
IssueScreen({
@required this.number,
@required this.owner,
@required this.name,
this.isPullRequest = false,
});
IssueScreen.fromFullName({@required this.number, @required String fullName})
: this.owner = fullName.split('/')[0],
this.name = fullName.split('/')[1];
IssueScreen.fromFullName({
@required this.number,
@required String fullName,
this.isPullRequest = false,
}) : owner = fullName.split('/')[0],
name = fullName.split('/')[1];
@override
_IssueScreenState createState() => _IssueScreenState();
}
class _IssueScreenState extends State<IssueScreen> {
get _fullName => widget.owner + '/' + widget.name;
get owner => widget.owner;
get id => widget.number;
get name => widget.name;
String get owner => widget.owner;
String get name => widget.name;
int get number => widget.number;
bool get isPullRequest => widget.isPullRequest;
String get resource => isPullRequest ? 'pullRequest' : 'issue';
String get issueChunk {
var base = '''
title
createdAt
body
author {
login
avatarUrl
}
closed
url
''';
if (isPullRequest) {
base += '''
merged
additions
deletions
commits {
totalCount
}
''';
}
return base;
}
String get timelineChunk {
var base = '''
__typename
... on IssueComment {
createdAt
body
author {
login
avatarUrl
}
}
... on Commit {
committedDate
oid
author {
user {
login
}
}
}
... on ReferencedEvent {
createdAt
isCrossRepository
actor {
login
}
commit {
oid
url
}
commitRepository {
owner {
login
}
name
}
}
... on RenamedTitleEvent {
createdAt
previousTitle
currentTitle
actor {
login
}
}
... on ClosedEvent {
createdAt
actor {
login
}
}
... on ReopenedEvent {
createdAt
actor {
login
}
}
... on CrossReferencedEvent {
createdAt
actor {
login
}
source {
__typename
... on Issue {
number
repository {
owner {
login
}
name
}
}
... on PullRequest {
number
repository {
owner {
login
}
name
}
}
}
}
... on LabeledEvent {
createdAt
actor {
login
}
label {
name
color
}
}
... on UnlabeledEvent {
createdAt
actor {
login
}
label {
name
color
}
}
... on MilestonedEvent {
createdAt
actor {
login
}
milestoneTitle
}
... on LockedEvent {
createdAt
actor {
login
}
lockReason
}
... on UnlockedEvent {
createdAt
actor {
login
}
}
... on AssignedEvent {
createdAt
actor {
login
}
user {
login
}
}
''';
if (isPullRequest) {
base += '''
... on ReviewRequestedEvent {
createdAt
actor {
login
}
requestedReviewer {
... on User {
login
}
}
}
... on PullRequestReview {
createdAt
state
author {
login
}
}
... on MergedEvent {
createdAt
mergeRefName
actor {
login
}
commit {
oid
url
}
}
... on HeadRefDeletedEvent {
createdAt
actor {
login
}
headRefName
}
''';
}
return base;
}
Future _queryIssue({String cursor, bool trailing = false}) async {
String timelineParams;
if (trailing) {
timelineParams = 'last: $pageSize';
} else {
timelineParams = 'first: $pageSize';
if (cursor != null) {
timelineParams += ', after: $cursor';
}
}
Future queryIssue() async {
var data = await SettingsProvider.of(context).query('''
{
repository(owner: "$owner", name: "$name") {
issue(number: $id) {
$graphqlChunk1
timeline(first: $pageSize) {
$resource(number: $number) {
$issueChunk
timeline($timelineParams) {
totalCount
pageInfo {
hasNextPage
endCursor
}
nodes {
$graghqlChunk
$timelineChunk
}
}
}
}
}
''');
return data['repository']['issue'];
return data['repository'][resource];
}
Future queryMore(String cursor) async {
var data = await SettingsProvider.of(context).query('''
{
repository(owner: "$owner", name: "$name") {
issue(number: $id) {
timeline(first: $pageSize, after: $cursor) {
totalCount
pageInfo {
endCursor
}
nodes {
$graghqlChunk
}
}
}
}
}
''');
return data['repository']['issue'];
}
Future<List> queryTrailing() async {
var data = await SettingsProvider.of(context).query('''
{
repository(owner: "$owner", name: "$name") {
issue(number: $id) {
timeline(last: $pageSize) {
nodes {
$graghqlChunk
}
}
}
}
}
''');
return data['repository']['issue']['timeline']['nodes'];
}
// TODO: extract as widget, this is copied from pull request
Widget _buildBadge(payload) {
Color bgColor;
IconData iconData;
String text;
if (payload['closed']) {
bgColor = Palette.red;
iconData = Octicons.issue_closed;
text = 'Closed';
if (isPullRequest) {
if (payload['merged']) {
bgColor = Palette.purple;
iconData = Octicons.git_merge;
text = 'Merged';
} else if (payload['closed']) {
bgColor = Palette.red;
iconData = Octicons.git_pull_request;
text = 'Closed';
} else {
bgColor = Palette.green;
iconData = Octicons.git_pull_request;
text = 'Open';
}
} else {
bgColor = Palette.green;
iconData = Octicons.issue_opened;
text = 'Open';
if (payload['closed']) {
bgColor = Palette.red;
iconData = Octicons.issue_closed;
text = 'Closed';
} else {
bgColor = Palette.green;
iconData = Octicons.issue_opened;
text = 'Open';
}
}
return Container(
decoration: BoxDecoration(
@ -136,17 +336,15 @@ class _IssueScreenState extends State<IssueScreen> {
Future<void> _openActions(payload) async {
if (payload == null) return;
var _actionMap = {
2: 'Share',
3: 'Open in Browser',
};
var value = await showCupertinoModalPopup<int>(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Issue Actions'),
actions: _actionMap.entries.map((entry) {
title: Text((isPullRequest ? 'Pull Request' : 'Issue') + ' Actions'),
actions: {
2: 'Share',
3: 'Open in Browser',
}.entries.map((entry) {
return CupertinoActionSheetAction(
child: Text(entry.value),
onPressed: () {
@ -179,7 +377,7 @@ class _IssueScreenState extends State<IssueScreen> {
@override
Widget build(BuildContext context) {
return LongListScaffold(
title: Text(_fullName + ' #' + widget.number.toString()),
title: Text('$owner/$name #$number'),
trailingBuilder: (payload) {
return Link(
child: Icon(Icons.more_vert, size: 24),
@ -230,7 +428,7 @@ class _IssueScreenState extends State<IssueScreen> {
},
itemBuilder: (itemPayload) => TimelineItem(itemPayload),
onRefresh: () async {
var res = await queryIssue();
var res = await _queryIssue();
int totalCount = res['timeline']['totalCount'];
String cursor = res['timeline']['pageInfo']['endCursor'];
List leadingItems = res['timeline']['nodes'];
@ -244,13 +442,14 @@ class _IssueScreenState extends State<IssueScreen> {
);
if (totalCount > 2 * pageSize) {
payload.trailingItems = await queryTrailing();
var res = await _queryIssue(trailing: true);
payload.trailingItems = res['timeline']['nodes'];
}
return payload;
},
onLoadMore: (String _cursor) async {
var res = await queryMore(_cursor);
var res = await _queryIssue(cursor: _cursor);
int totalCount = res['timeline']['totalCount'];
String cursor = res['timeline']['pageInfo']['endCursor'];
List leadingItems = res['timeline']['nodes'];

View File

@ -3,7 +3,6 @@ import '../scaffolds/list.dart';
import '../providers/settings.dart';
import '../utils/utils.dart';
import '../widgets/link.dart';
import '../screens/pull_request.dart';
import '../screens/issue.dart';
class IssuesScreen extends StatefulWidget {
@ -22,9 +21,9 @@ class IssuesScreen extends StatefulWidget {
}
class _IssuesScreenState extends State<IssuesScreen> {
get owner => widget.owner;
get name => widget.name;
get isPullRequest => widget.isPullRequest;
String get owner => widget.owner;
String get name => widget.name;
bool get isPullRequest => widget.isPullRequest;
Future<ListPayload> _query([String cursor]) async {
var cursorChunk = cursor == null ? '' : ', after: "$cursor"';
@ -66,19 +65,12 @@ class _IssuesScreenState extends State<IssuesScreen> {
Widget _buildItem(payload) {
return Link(
screenBuilder: (context) {
if (widget.isPullRequest) {
return PullRequestScreen(
number: payload['number'],
owner: owner,
name: name,
);
} else {
return IssueScreen(
number: payload['number'],
owner: owner,
name: name,
);
}
return IssueScreen(
number: payload['number'],
owner: owner,
name: name,
isPullRequest: isPullRequest,
);
},
child: Container(
padding: EdgeInsets.all(8),

View File

@ -1,319 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:share/share.dart';
import 'package:url_launcher/url_launcher.dart';
import '../providers/settings.dart';
import '../utils/utils.dart';
import '../scaffolds/long_list.dart';
import '../widgets/timeline_item.dart';
import '../widgets/comment_item.dart';
import '../widgets/link.dart';
class PullRequestScreen extends StatefulWidget {
final int number;
final String owner;
final String name;
PullRequestScreen({
@required this.number,
@required this.owner,
@required this.name,
});
PullRequestScreen.fromFullName(
{@required this.number, @required String fullName})
: this.owner = fullName.split('/')[0],
this.name = fullName.split('/')[1];
@override
_PullRequestScreenState createState() => _PullRequestScreenState();
}
var commonChunk = '''
$graghqlChunk
... on ReviewRequestedEvent {
createdAt
actor {
login
}
requestedReviewer {
... on User {
login
}
}
}
... on PullRequestReview {
createdAt
state
author {
login
}
}
... on MergedEvent {
createdAt
mergeRefName
actor {
login
}
commit {
oid
url
}
}
... on HeadRefDeletedEvent {
createdAt
actor {
login
}
headRefName
}
''';
class _PullRequestScreenState extends State<PullRequestScreen> {
get owner => widget.owner;
get id => widget.number;
get name => widget.name;
Future queryPullRequest() async {
var data = await SettingsProvider.of(context).query('''
{
repository(owner: "$owner", name: "$name") {
pullRequest(number: $id) {
$graphqlChunk1
merged
additions
deletions
commits {
totalCount
}
timeline(first: $pageSize) {
totalCount
pageInfo {
endCursor
}
nodes {
$commonChunk
}
}
}
}
}
''');
return data['repository']['pullRequest'];
}
Future queryMore(String cursor) async {
var data = await SettingsProvider.of(context).query('''
{
repository(owner: "$owner", name: "$name") {
pullRequest(number: $id) {
timeline(first: $pageSize, after: $cursor) {
totalCount
pageInfo {
endCursor
}
nodes {
$commonChunk
}
}
}
}
}
''');
return data['repository']['pullRequest'];
}
Future<List> queryTrailing() async {
var data = await SettingsProvider.of(context).query('''
{
repository(owner: "$owner", name: "$name") {
pullRequest(number: $id) {
timeline(last: $pageSize) {
nodes {
$commonChunk
}
}
}
}
}
''');
return data['repository']['pullRequest']['timeline']['nodes'];
}
Widget _buildBadge(payload) {
Color bgColor;
IconData iconData;
String text;
if (payload['merged']) {
bgColor = Palette.purple;
iconData = Octicons.git_merge;
text = 'Merged';
} else if (payload['closed']) {
bgColor = Palette.red;
iconData = Octicons.git_pull_request;
text = 'Closed';
} else {
bgColor = Palette.green;
iconData = Octicons.git_pull_request;
text = 'Open';
}
return Container(
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.all(Radius.circular(4)),
),
padding: EdgeInsets.all(6),
child: Row(
children: <Widget>[
Icon(iconData, color: Colors.white, size: 15),
Padding(padding: EdgeInsets.only(left: 2)),
Text(
text,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
],
),
);
}
get _fullName => widget.owner + '/' + widget.name;
Future<void> _openActions(payload) async {
if (payload == null) return;
var _actionMap = {
2: 'Share',
3: 'Open in Browser',
};
var value = await showCupertinoModalPopup<int>(
context: context,
builder: (BuildContext context) {
return CupertinoActionSheet(
title: Text('Issue Actions'),
actions: _actionMap.entries.map((entry) {
return CupertinoActionSheetAction(
child: Text(entry.value),
onPressed: () {
Navigator.pop(context, entry.key);
},
);
}).toList(),
cancelButton: CupertinoActionSheetAction(
child: const Text('Cancel'),
isDefaultAction: true,
onPressed: () {
Navigator.pop(context);
},
),
);
},
);
switch (value) {
case 2:
Share.share(payload['url']);
break;
case 3:
launch(payload['url']);
break;
default:
}
}
@override
Widget build(BuildContext context) {
return LongListScaffold(
title: Text(_fullName + ' #' + widget.number.toString()),
trailingBuilder: (payload) {
return Link(
child: Icon(Icons.more_vert, size: 24),
material: false,
beforeRedirect: () => _openActions(payload),
);
},
actionsBuilder: (payload) {
return [
Link(
iconButton: Icon(Icons.more_vert),
beforeRedirect: () => _openActions(payload),
),
];
},
headerBuilder: (payload) {
return Column(children: <Widget>[
Container(
padding: EdgeInsets.all(10),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.black12)),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Expanded(
child: Text(
payload['title'],
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
Padding(padding: EdgeInsets.only(right: 8)),
_buildBadge(payload),
],
),
Padding(padding: EdgeInsets.only(bottom: 16)),
CommentItem(payload),
],
),
)
]);
},
itemBuilder: (itemPayload) => TimelineItem(itemPayload),
onRefresh: () async {
var res = await queryPullRequest();
int totalCount = res['timeline']['totalCount'];
String cursor = res['timeline']['pageInfo']['endCursor'];
List leadingItems = res['timeline']['nodes'];
var payload = LongListPayload(
header: res,
totalCount: totalCount,
cursor: cursor,
leadingItems: leadingItems,
trailingItems: [],
);
if (totalCount > 2 * pageSize) {
payload.trailingItems = await queryTrailing();
}
return payload;
},
onLoadMore: (String _cursor) async {
var res = await queryMore(_cursor);
int totalCount = res['timeline']['totalCount'];
String cursor = res['timeline']['pageInfo']['endCursor'];
List leadingItems = res['timeline']['nodes'];
var payload = LongListPayload(
totalCount: totalCount,
cursor: cursor,
leadingItems: leadingItems,
);
return payload;
},
);
}
}

View File

@ -150,165 +150,9 @@ class Palette {
static const gray = Color(0xff959da5);
}
// final pageSize = 5;
final pageSize = 30;
final graphqlChunk1 = '''
title
createdAt
body
author {
login
avatarUrl
}
closed
url
''';
var graghqlChunk = '''
__typename
... on IssueComment {
createdAt
body
author {
login
avatarUrl
}
}
... on Commit {
committedDate
oid
author {
user {
login
}
}
}
... on ReferencedEvent {
createdAt
isCrossRepository
actor {
login
}
commit {
oid
url
}
commitRepository {
owner {
login
}
name
}
}
... on RenamedTitleEvent {
createdAt
previousTitle
currentTitle
actor {
login
}
}
... on ClosedEvent {
createdAt
actor {
login
}
}
... on ReopenedEvent {
createdAt
actor {
login
}
}
... on CrossReferencedEvent {
createdAt
actor {
login
}
source {
__typename
... on Issue {
number
repository {
owner {
login
}
name
}
}
... on PullRequest {
number
repository {
owner {
login
}
name
}
}
}
}
... on LabeledEvent {
createdAt
actor {
login
}
label {
name
color
}
}
... on UnlabeledEvent {
createdAt
actor {
login
}
label {
name
color
}
}
... on MilestonedEvent {
createdAt
actor {
login
}
milestoneTitle
}
... on LockedEvent {
createdAt
actor {
login
}
lockReason
}
... on UnlockedEvent {
createdAt
actor {
login
}
}
... on AssignedEvent {
createdAt
actor {
login
}
user {
login
}
}
''';
var createWarning =
(String text) => Text(text, style: TextStyle(color: Colors.redAccent));
var warningSpan =

View File

@ -5,17 +5,17 @@ import 'avatar.dart';
import 'user_name.dart';
class CommentItem extends StatelessWidget {
final Map<String, dynamic> item;
final Map<String, dynamic> payload;
CommentItem(this.item);
CommentItem(this.payload);
@override
Widget build(BuildContext context) {
return Column(children: <Widget>[
Row(children: <Widget>[
Avatar(
login: item['author']['login'],
url: item['author']['avatarUrl'],
login: payload['author']['login'],
url: payload['author']['avatarUrl'],
size: 16,
),
Padding(padding: EdgeInsets.only(left: 6)),
@ -23,10 +23,10 @@ class CommentItem extends StatelessWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
UserName(item['author']['login']),
UserName(payload['author']['login']),
Padding(padding: EdgeInsets.only(bottom: 2)),
Text(
TimeAgo.formatFromString(item['createdAt']),
TimeAgo.formatFromString(payload['createdAt']),
style: TextStyle(color: Colors.black54, fontSize: 13),
),
],
@ -36,7 +36,7 @@ class CommentItem extends StatelessWidget {
Padding(
padding: const EdgeInsets.only(left: 36, top: 8),
child: MarkdownBody(
data: item['body'],
data: payload['body'],
// styleSheet: MarkdownStyleSheet(code: TextStyle(fontSize: 14)),
),
),

View File

@ -1,11 +1,8 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../screens/issue.dart';
import '../screens/pull_request.dart';
import '../screens/user.dart';
import '../screens/repo.dart';
import 'avatar.dart';
import 'link.dart';
import '../utils/utils.dart';
class EventPayload {
@ -35,22 +32,18 @@ class EventItem extends StatelessWidget {
return createRepoLinkSpan(context, arr[0], arr[1]);
}
TextSpan _buildIssue(BuildContext context) {
int id = event.payload['issue']['number'];
return createLinkSpan(
context,
'#' + id.toString(),
() =>
IssueScreen.fromFullName(number: id, fullName: event.repoFullName));
}
TextSpan _buildIssue(BuildContext context,
{@required int number, bool isPullRequest = false}) {
// var resource = isPullRequest ? 'pull_request' : 'issue';
// int number = event.payload['issue']['number'];
TextSpan _buildPullRequest(BuildContext context, int number) {
return createLinkSpan(
context,
'#' + number.toString(),
() => PullRequestScreen.fromFullName(
number: number, fullName: event.repoFullName),
);
return createLinkSpan(context, '#' + number.toString(), () {
return IssueScreen.fromFullName(
number: number,
fullName: event.repoFullName,
isPullRequest: isPullRequest,
);
});
}
Widget _buildItem({
@ -161,17 +154,19 @@ class EventItem extends StatelessWidget {
// TODO:
return defaultItem;
case 'IssueCommentEvent':
bool isIssue = event.payload['issue']['pull_request'] == null;
String resource = isIssue ? 'issue' : 'pull request';
TextSpan link = isIssue
? _buildIssue(context)
: _buildPullRequest(context, event.payload['issue']['number']);
bool isPullRequest = event.payload['issue']['pull_request'] != null;
String resource = isPullRequest ? 'pull request' : 'issue';
int number = event.payload['issue']['number'];
return _buildItem(
context: context,
spans: [
TextSpan(text: ' commented on $resource '),
link,
_buildIssue(
context,
number: number,
isPullRequest: isPullRequest,
),
TextSpan(text: ' at '),
_buildRepo(context),
// TextSpan(text: event.payload['comment']['body'])
@ -179,23 +174,26 @@ class EventItem extends StatelessWidget {
detail: event.payload['comment']['body'],
iconData: Octicons.comment_discussion,
screenBuilder: (_) => IssueScreen.fromFullName(
number: event.payload['issue']['number'],
number: number,
fullName: event.repoFullName,
isPullRequest: isPullRequest,
),
);
case 'IssuesEvent':
int number = event.payload['issue']['number'];
return _buildItem(
context: context,
spans: [
TextSpan(text: ' ${event.payload['action']} issue '),
_buildIssue(context),
_buildIssue(context, number: number),
TextSpan(text: ' at '),
_buildRepo(context),
],
iconData: Octicons.issue_opened,
detail: event.payload['issue']['title'],
screenBuilder: (_) => IssueScreen.fromFullName(
number: event.payload['issue']['number'],
number: number,
fullName: event.repoFullName,
),
);
@ -218,15 +216,17 @@ class EventItem extends StatelessWidget {
context: context,
spans: [
TextSpan(text: ' ${event.payload['action']} pull request '),
_buildPullRequest(context, event.payload['pull_request']['number']),
_buildIssue(context,
number: event.payload['number'], isPullRequest: true),
TextSpan(text: ' at '),
_buildRepo(context),
],
iconData: Octicons.git_pull_request,
detail: event.payload['pull_request']['title'],
screenBuilder: (_) => PullRequestScreen.fromFullName(
screenBuilder: (_) => IssueScreen.fromFullName(
number: event.payload['pull_request']['number'],
fullName: event.repoFullName,
isPullRequest: true,
),
);
case 'PullRequestReviewEvent':
@ -237,14 +237,17 @@ class EventItem extends StatelessWidget {
context: context,
spans: [
TextSpan(text: ' reviewed pull request '),
_buildPullRequest(context, event.payload['pull_request']['number']),
_buildIssue(context,
number: event.payload['pull_request']['number'],
isPullRequest: true),
TextSpan(text: ' at '),
_buildRepo(context),
],
detail: event.payload['comment']['body'],
screenBuilder: (_) => PullRequestScreen.fromFullName(
screenBuilder: (_) => IssueScreen.fromFullName(
number: event.payload['pull_request']['number'],
fullName: event.repoFullName,
isPullRequest: true,
),
);
case 'PushEvent':

View File

@ -2,7 +2,6 @@ import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import '../utils/utils.dart';
import '../screens/issue.dart';
import '../screens/pull_request.dart';
import '../screens/not_found.dart';
import '../providers/settings.dart';
import 'link.dart';
@ -65,10 +64,11 @@ class _NotificationItemState extends State<NotificationItem> {
name: payload.name,
);
case 'PullRequest':
return PullRequestScreen(
return IssueScreen(
number: payload.number,
owner: payload.owner,
name: payload.name,
isPullRequest: true,
);
case 'Release':
// return

View File

@ -6,9 +6,9 @@ import 'comment_item.dart';
import 'user_name.dart';
class TimelineItem extends StatelessWidget {
final Map<String, dynamic> item;
final Map<String, dynamic> payload;
TimelineItem(this.item);
TimelineItem(this.payload);
TextSpan _buildReviewText(BuildContext context, item) {
switch (item['state']) {
@ -69,7 +69,7 @@ class TimelineItem extends StatelessWidget {
}
Widget _buildByType(BuildContext context) {
String type = item['__typename'];
String type = payload['__typename'];
var defaultItem = _buildItem(
actor: '',
@ -77,150 +77,150 @@ class TimelineItem extends StatelessWidget {
textSpan: TextSpan(children: [
TextSpan(text: 'Woops, $type type not implemented yet'),
]),
item: item,
item: payload,
);
switch (type) {
// common types
case 'Commit':
return _buildItem(
actor: item['author']['user'] == null
actor: payload['author']['user'] == null
? null
: item['author']['user']['login'],
: payload['author']['user']['login'],
iconData: Octicons.git_commit,
textSpan: TextSpan(children: [
TextSpan(text: ' added commit '),
TextSpan(text: item['oid'].substring(0, 8))
TextSpan(text: payload['oid'].substring(0, 8))
]),
item: item,
item: payload,
);
case 'IssueComment':
return CommentItem(item);
return CommentItem(payload);
case 'CrossReferencedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.primitive_dot,
iconColor: Palette.green,
textSpan: TextSpan(
text: ' referenced this on #' +
item['source']['number'].toString()),
item: item,
payload['source']['number'].toString()),
item: payload,
);
case 'ClosedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.circle_slash,
iconColor: Palette.red,
textSpan: TextSpan(text: ' closed this '),
item: item,
item: payload,
);
case 'ReopenedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.primitive_dot,
iconColor: Palette.green,
textSpan: TextSpan(text: ' reopened this '),
item: item,
item: payload,
);
case 'SubscribedEvent':
case 'UnsubscribedEvent':
return defaultItem; // TODO:
case 'ReferencedEvent':
// TODO: isCrossRepository
if (item['commit'] == null) {
if (payload['commit'] == null) {
return Container();
}
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.bookmark,
textSpan: TextSpan(children: [
TextSpan(text: ' referenced this pull request from commit '),
TextSpan(text: item['commit']['oid'].substring(0, 8)),
TextSpan(text: payload['commit']['oid'].substring(0, 8)),
]),
item: item,
item: payload,
);
case 'AssignedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.key,
textSpan: TextSpan(children: [
TextSpan(text: ' assigned this to '),
TextSpan(text: item['user']['login'])
TextSpan(text: payload['user']['login'])
]),
item: item,
item: payload,
);
case 'UnassignedEvent':
return defaultItem; // TODO:
case 'LabeledEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.tag,
textSpan: TextSpan(children: [
TextSpan(text: ' added '),
_buildLabel(item),
_buildLabel(payload),
TextSpan(text: ' label'),
]),
item: item,
item: payload,
);
case 'UnlabeledEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.tag,
textSpan: TextSpan(children: [
TextSpan(text: ' removed '),
_buildLabel(item),
_buildLabel(payload),
TextSpan(text: ' label'),
]),
item: item,
item: payload,
);
case 'MilestonedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.milestone,
textSpan: TextSpan(children: [
TextSpan(text: ' added this to '),
TextSpan(text: item['milestoneTitle']),
TextSpan(text: payload['milestoneTitle']),
TextSpan(text: ' milestone'),
]),
item: item,
item: payload,
);
case 'DemilestonedEvent':
return defaultItem; // TODO:
case 'RenamedTitleEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.pencil,
textSpan: TextSpan(children: [
TextSpan(text: ' changed the title '),
TextSpan(
text: item['previousTitle'],
text: payload['previousTitle'],
style: TextStyle(decoration: TextDecoration.lineThrough),
),
TextSpan(text: ' to '),
TextSpan(text: item['currentTitle'])
TextSpan(text: payload['currentTitle'])
]),
item: item,
item: payload,
);
case 'LockedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.lock,
textSpan: TextSpan(children: [
TextSpan(text: ' locked this conversation '),
]),
item: item,
item: payload,
);
case 'UnlockedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.key,
textSpan: TextSpan(children: [
TextSpan(text: ' unlocked this conversation '),
]),
item: item,
item: payload,
);
// issue only types
@ -232,41 +232,41 @@ class TimelineItem extends StatelessWidget {
return defaultItem; // TODO:
case 'PullRequestReview':
return _buildItem(
actor: item['author']['login'],
actor: payload['author']['login'],
iconColor: Color(0xff28a745),
iconData: Octicons.check,
textSpan: _buildReviewText(context, item),
item: item,
textSpan: _buildReviewText(context, payload),
item: payload,
);
case 'PullRequestReviewThread':
case 'PullRequestReviewComment':
return defaultItem; // TODO:
case 'MergedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.git_merge,
iconColor: Color(0xff6f42c1),
textSpan: TextSpan(children: [
TextSpan(text: ' merged commit '),
TextSpan(text: item['commit']['oid'].substring(0, 8)),
TextSpan(text: payload['commit']['oid'].substring(0, 8)),
TextSpan(text: ' into '),
TextSpan(text: item['mergeRefName']),
TextSpan(text: payload['mergeRefName']),
]),
item: item,
item: payload,
);
case 'DeployedEvent':
case 'DeploymentEnvironmentChangedEvent':
return defaultItem; // TODO:
case 'HeadRefDeletedEvent':
return _buildItem(
actor: item['actor']['login'],
actor: payload['actor']['login'],
iconData: Octicons.git_branch,
textSpan: TextSpan(children: [
TextSpan(text: ' deleted the '),
TextSpan(text: item['headRefName']),
TextSpan(text: payload['headRefName']),
TextSpan(text: ' branch'),
]),
item: item,
item: payload,
);
case 'HeadRefRestoredEvent':
case 'HeadRefForcePushedEvent':
@ -280,9 +280,9 @@ class TimelineItem extends StatelessWidget {
actor: 'test',
textSpan: TextSpan(children: [
TextSpan(text: ' requested a review from '),
createUserSpan(item['requestedReviewer']['login']),
createUserSpan(payload['requestedReviewer']['login']),
]),
item: item,
item: payload,
);
case 'ReviewRequestRemovedEvent':
case 'ReviewDismissedEvent':