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:
parent
cc8ea1d39a
commit
2bdd9216c3
@ -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(() {
|
||||
|
@ -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(() {
|
||||
|
@ -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 {
|
||||
|
@ -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'];
|
||||
|
@ -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),
|
||||
|
@ -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;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -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 =
|
||||
|
@ -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)),
|
||||
),
|
||||
),
|
||||
|
@ -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':
|
||||
|
@ -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
|
||||
|
@ -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':
|
||||
|
Loading…
x
Reference in New Issue
Block a user