mirror of
https://github.com/git-touch/git-touch
synced 2025-02-23 14:57:42 +01:00
feat(gitee): issues, issue, pullRequests screens (#144)
* feat: gitee pulls, issue screens * add action button - issue screen * remove unnecessary comment * feat: gitee create issue in repo * feat: commenting in issue screen * fix: prepend '#' to subtitle props
This commit is contained in:
parent
4679ca10ee
commit
1ac27f4853
@ -276,9 +276,25 @@ class AuthModel with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future fetchGitee(String p) async {
|
Future fetchGitee(
|
||||||
final res = await http.get('${activeAccount.domain}/api/v5$p',
|
String p, {
|
||||||
headers: {'Authorization': 'token $token'});
|
isPost = false,
|
||||||
|
Map<String, dynamic> body = const {},
|
||||||
|
}) async {
|
||||||
|
http.Response res;
|
||||||
|
if (isPost) {
|
||||||
|
res = await http.post(
|
||||||
|
'${activeAccount.domain}/api/v5$p',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'token $token',
|
||||||
|
HttpHeaders.contentTypeHeader: 'application/json'
|
||||||
|
},
|
||||||
|
body: jsonEncode(body),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
res = await http.get('${activeAccount.domain}/api/v5$p',
|
||||||
|
headers: {'Authorization': 'token $token'});
|
||||||
|
}
|
||||||
final info = json.decode(utf8.decode(res.bodyBytes));
|
final info = json.decode(utf8.decode(res.bodyBytes));
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
@ -122,3 +122,49 @@ class GiteeBlob {
|
|||||||
factory GiteeBlob.fromJson(Map<String, dynamic> json) =>
|
factory GiteeBlob.fromJson(Map<String, dynamic> json) =>
|
||||||
_$GiteeBlobFromJson(json);
|
_$GiteeBlobFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||||
|
class GiteeIssue {
|
||||||
|
int comments;
|
||||||
|
String commentsUrl;
|
||||||
|
String createdAt;
|
||||||
|
String htmlUrl;
|
||||||
|
String updatedAt;
|
||||||
|
String body;
|
||||||
|
String bodyHtml;
|
||||||
|
String title;
|
||||||
|
String state;
|
||||||
|
GiteeRepoOwner user;
|
||||||
|
String number;
|
||||||
|
int id;
|
||||||
|
GiteeIssue();
|
||||||
|
factory GiteeIssue.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GiteeIssueFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||||
|
class GiteePull {
|
||||||
|
String commentsUrl;
|
||||||
|
String createdAt;
|
||||||
|
String htmlUrl;
|
||||||
|
String updatedAt;
|
||||||
|
String body;
|
||||||
|
String bodyHtml;
|
||||||
|
String title;
|
||||||
|
GiteeRepoOwner user;
|
||||||
|
int number;
|
||||||
|
int id;
|
||||||
|
GiteePull();
|
||||||
|
factory GiteePull.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GiteePullFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||||
|
class GiteeComment {
|
||||||
|
String body;
|
||||||
|
String createdAt;
|
||||||
|
GiteeRepoOwner user;
|
||||||
|
GiteeComment();
|
||||||
|
factory GiteeComment.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$GiteeCommentFromJson(json);
|
||||||
|
}
|
||||||
|
@ -199,3 +199,82 @@ GiteeBlob _$GiteeBlobFromJson(Map<String, dynamic> json) {
|
|||||||
Map<String, dynamic> _$GiteeBlobToJson(GiteeBlob instance) => <String, dynamic>{
|
Map<String, dynamic> _$GiteeBlobToJson(GiteeBlob instance) => <String, dynamic>{
|
||||||
'content': instance.content,
|
'content': instance.content,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GiteeIssue _$GiteeIssueFromJson(Map<String, dynamic> json) {
|
||||||
|
return GiteeIssue()
|
||||||
|
..comments = json['comments'] as int
|
||||||
|
..commentsUrl = json['comments_url'] as String
|
||||||
|
..createdAt = json['created_at'] as String
|
||||||
|
..htmlUrl = json['html_url'] as String
|
||||||
|
..updatedAt = json['updated_at'] as String
|
||||||
|
..body = json['body'] as String
|
||||||
|
..bodyHtml = json['body_html'] as String
|
||||||
|
..title = json['title'] as String
|
||||||
|
..state = json['state'] as String
|
||||||
|
..user = json['user'] == null
|
||||||
|
? null
|
||||||
|
: GiteeRepoOwner.fromJson(json['user'] as Map<String, dynamic>)
|
||||||
|
..number = json['number'] as String
|
||||||
|
..id = json['id'] as int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _$GiteeIssueToJson(GiteeIssue instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'comments': instance.comments,
|
||||||
|
'comments_url': instance.commentsUrl,
|
||||||
|
'created_at': instance.createdAt,
|
||||||
|
'html_url': instance.htmlUrl,
|
||||||
|
'updated_at': instance.updatedAt,
|
||||||
|
'body': instance.body,
|
||||||
|
'body_html': instance.bodyHtml,
|
||||||
|
'title': instance.title,
|
||||||
|
'state': instance.state,
|
||||||
|
'user': instance.user,
|
||||||
|
'number': instance.number,
|
||||||
|
'id': instance.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
GiteePull _$GiteePullFromJson(Map<String, dynamic> json) {
|
||||||
|
return GiteePull()
|
||||||
|
..commentsUrl = json['comments_url'] as String
|
||||||
|
..createdAt = json['created_at'] as String
|
||||||
|
..htmlUrl = json['html_url'] as String
|
||||||
|
..updatedAt = json['updated_at'] as String
|
||||||
|
..body = json['body'] as String
|
||||||
|
..bodyHtml = json['body_html'] as String
|
||||||
|
..title = json['title'] as String
|
||||||
|
..user = json['user'] == null
|
||||||
|
? null
|
||||||
|
: GiteeRepoOwner.fromJson(json['user'] as Map<String, dynamic>)
|
||||||
|
..number = json['number'] as int
|
||||||
|
..id = json['id'] as int;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _$GiteePullToJson(GiteePull instance) => <String, dynamic>{
|
||||||
|
'comments_url': instance.commentsUrl,
|
||||||
|
'created_at': instance.createdAt,
|
||||||
|
'html_url': instance.htmlUrl,
|
||||||
|
'updated_at': instance.updatedAt,
|
||||||
|
'body': instance.body,
|
||||||
|
'body_html': instance.bodyHtml,
|
||||||
|
'title': instance.title,
|
||||||
|
'user': instance.user,
|
||||||
|
'number': instance.number,
|
||||||
|
'id': instance.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
GiteeComment _$GiteeCommentFromJson(Map<String, dynamic> json) {
|
||||||
|
return GiteeComment()
|
||||||
|
..body = json['body'] as String
|
||||||
|
..createdAt = json['created_at'] as String
|
||||||
|
..user = json['user'] == null
|
||||||
|
? null
|
||||||
|
: GiteeRepoOwner.fromJson(json['user'] as Map<String, dynamic>);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> _$GiteeCommentToJson(GiteeComment instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'body': instance.body,
|
||||||
|
'created_at': instance.createdAt,
|
||||||
|
'user': instance.user,
|
||||||
|
};
|
||||||
|
@ -8,6 +8,11 @@ import 'package:git_touch/screens/bb_user.dart';
|
|||||||
import 'package:git_touch/screens/code_theme.dart';
|
import 'package:git_touch/screens/code_theme.dart';
|
||||||
import 'package:git_touch/screens/ge_blob.dart';
|
import 'package:git_touch/screens/ge_blob.dart';
|
||||||
import 'package:git_touch/screens/ge_commits.dart';
|
import 'package:git_touch/screens/ge_commits.dart';
|
||||||
|
import 'package:git_touch/screens/ge_issue.dart';
|
||||||
|
import 'package:git_touch/screens/ge_issue_comment.dart';
|
||||||
|
import 'package:git_touch/screens/ge_issue_form.dart';
|
||||||
|
import 'package:git_touch/screens/ge_issues.dart';
|
||||||
|
import 'package:git_touch/screens/ge_pulls.dart';
|
||||||
import 'package:git_touch/screens/ge_repo.dart';
|
import 'package:git_touch/screens/ge_repo.dart';
|
||||||
import 'package:git_touch/screens/ge_repos.dart';
|
import 'package:git_touch/screens/ge_repos.dart';
|
||||||
import 'package:git_touch/screens/ge_search.dart';
|
import 'package:git_touch/screens/ge_search.dart';
|
||||||
@ -392,6 +397,11 @@ class GiteeRouter {
|
|||||||
GiteeRouter.commits,
|
GiteeRouter.commits,
|
||||||
GiteeRouter.tree,
|
GiteeRouter.tree,
|
||||||
GiteeRouter.blob,
|
GiteeRouter.blob,
|
||||||
|
GiteeRouter.issues,
|
||||||
|
GiteeRouter.pulls,
|
||||||
|
GiteeRouter.issueAdd, // issueAdd should be above issue
|
||||||
|
GiteeRouter.issue, // Due to similarity of uris
|
||||||
|
GiteeRouter.issueComment,
|
||||||
];
|
];
|
||||||
static final search = RouterScreen('/search', (context, parameters) {
|
static final search = RouterScreen('/search', (context, parameters) {
|
||||||
return GeSearchScreen();
|
return GeSearchScreen();
|
||||||
@ -449,4 +459,34 @@ class GiteeRouter {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
static final issues = RouterScreen(
|
||||||
|
'/:owner/:name/issues',
|
||||||
|
(context, parameters) {
|
||||||
|
return GeIssuesScreen(parameters['owner'].first, parameters['name'].first,
|
||||||
|
isPr: false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
static final issue =
|
||||||
|
RouterScreen('/:owner/:name/issues/:number', (context, parameters) {
|
||||||
|
return GeIssueScreen(parameters['owner'].first, parameters['name'].first,
|
||||||
|
parameters['number'].first,
|
||||||
|
isPr: false);
|
||||||
|
});
|
||||||
|
static final pulls = RouterScreen(
|
||||||
|
'/:owner/:name/pulls',
|
||||||
|
(context, parameters) {
|
||||||
|
return GePullsScreen(parameters['owner'].first, parameters['name'].first,
|
||||||
|
isPr: true);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
static final issueAdd =
|
||||||
|
RouterScreen('/:owner/:name/issues/new', (context, parameters) {
|
||||||
|
return GeIssueFormScreen(
|
||||||
|
parameters['owner'].first, parameters['name'].first);
|
||||||
|
});
|
||||||
|
static final issueComment = RouterScreen(
|
||||||
|
'/:owner/:name/issues/:number/comment', (context, parameters) {
|
||||||
|
return GeIssueCommentScreen(parameters['owner'].first,
|
||||||
|
parameters['name'].first, parameters['number'].first);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
@ -37,7 +37,7 @@ class BbIssuesScreen extends StatelessWidget {
|
|||||||
avatarUrl: v.reporter.avatarUrl,
|
avatarUrl: v.reporter.avatarUrl,
|
||||||
author: v.reporter.displayName,
|
author: v.reporter.displayName,
|
||||||
title: v.title,
|
title: v.title,
|
||||||
number: issueNumber,
|
subtitle: '#' + issueNumber.toString(),
|
||||||
commentCount: 0,
|
commentCount: 0,
|
||||||
updatedAt: v.createdOn,
|
updatedAt: v.createdOn,
|
||||||
url: '${auth.activeAccount.domain}/$owner/$name/issues/$issueNumber',
|
url: '${auth.activeAccount.domain}/$owner/$name/issues/$issueNumber',
|
||||||
|
@ -36,7 +36,7 @@ class BbPullsScreen extends StatelessWidget {
|
|||||||
avatarUrl: v.author.avatarUrl,
|
avatarUrl: v.author.avatarUrl,
|
||||||
author: v.author.displayName,
|
author: v.author.displayName,
|
||||||
title: v.title,
|
title: v.title,
|
||||||
number: pullNumber,
|
subtitle: '#' + pullNumber.toString(),
|
||||||
commentCount: 0,
|
commentCount: 0,
|
||||||
updatedAt: v.createdOn,
|
updatedAt: v.createdOn,
|
||||||
url: '${auth.activeAccount.domain}/$owner/$name/issues/$pullNumber',
|
url: '${auth.activeAccount.domain}/$owner/$name/issues/$pullNumber',
|
||||||
|
116
lib/screens/ge_issue.dart
Normal file
116
lib/screens/ge_issue.dart
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:git_touch/models/gitee.dart';
|
||||||
|
import 'package:git_touch/scaffolds/refresh_stateful.dart';
|
||||||
|
import 'package:git_touch/utils/utils.dart';
|
||||||
|
import 'package:git_touch/widgets/action_entry.dart';
|
||||||
|
import 'package:git_touch/widgets/avatar.dart';
|
||||||
|
import 'package:git_touch/widgets/link.dart';
|
||||||
|
import 'package:git_touch/widgets/comment_item.dart';
|
||||||
|
import 'package:primer/primer.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:git_touch/models/auth.dart';
|
||||||
|
import 'package:git_touch/models/theme.dart';
|
||||||
|
import 'package:tuple/tuple.dart';
|
||||||
|
|
||||||
|
class GeIssueScreen extends StatelessWidget {
|
||||||
|
final String owner;
|
||||||
|
final String name;
|
||||||
|
final String number;
|
||||||
|
final bool isPr;
|
||||||
|
|
||||||
|
GeIssueScreen(this.owner, this.name, this.number, {this.isPr: false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return RefreshStatefulScaffold<Tuple2<GiteeIssue, List<GiteeComment>>>(
|
||||||
|
title: Text("Issue: #$number"),
|
||||||
|
fetch: () async {
|
||||||
|
final auth = context.read<AuthModel>();
|
||||||
|
final items = await Future.wait([
|
||||||
|
auth.fetchGitee('/repos/$owner/$name/issues/$number'),
|
||||||
|
auth.fetchGitee('/repos/$owner/$name/issues/$number/comments')
|
||||||
|
]);
|
||||||
|
return Tuple2(GiteeIssue.fromJson(items[0]),
|
||||||
|
[for (var v in items[1]) GiteeComment.fromJson(v)]);
|
||||||
|
},
|
||||||
|
actionBuilder: (data, _) => ActionEntry(
|
||||||
|
iconData: Octicons.plus,
|
||||||
|
url: '/gitee/$owner/$name/issues/$number/comment',
|
||||||
|
),
|
||||||
|
bodyBuilder: (data, _) {
|
||||||
|
final issue = data.item1;
|
||||||
|
final comments = data.item2;
|
||||||
|
final theme = context.read<ThemeModel>();
|
||||||
|
return Column(children: <Widget>[
|
||||||
|
Container(
|
||||||
|
padding: CommonStyle.padding,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: <Widget>[
|
||||||
|
Link(
|
||||||
|
url: '/gitee/$owner/$name',
|
||||||
|
child: Row(
|
||||||
|
children: <Widget>[
|
||||||
|
Avatar(
|
||||||
|
url: issue.user.avatarUrl,
|
||||||
|
size: AvatarSize.extraSmall,
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'$owner / $name',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 17,
|
||||||
|
color: theme.palette.secondaryText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(width: 4),
|
||||||
|
Text(
|
||||||
|
'#$number',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 17,
|
||||||
|
color: theme.palette.tertiaryText,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
issue.title,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: 8),
|
||||||
|
StateLabel(
|
||||||
|
issue.state == 'open'
|
||||||
|
? StateLabelStatus.issueOpened
|
||||||
|
: StateLabelStatus.issueClosed,
|
||||||
|
small: true),
|
||||||
|
SizedBox(height: 16),
|
||||||
|
CommonStyle.border,
|
||||||
|
],
|
||||||
|
)),
|
||||||
|
Column(children: [
|
||||||
|
for (var comment in comments) ...[
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(left: 10),
|
||||||
|
child: CommentItem(
|
||||||
|
avatar: Avatar(
|
||||||
|
url: comment.user.avatarUrl,
|
||||||
|
linkUrl: '/gitee/${comment.user.login}',
|
||||||
|
),
|
||||||
|
createdAt: DateTime.parse(comment.createdAt),
|
||||||
|
body: comment.body,
|
||||||
|
login: comment.user.login,
|
||||||
|
prefix: 'gitee')),
|
||||||
|
CommonStyle.border,
|
||||||
|
SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
]),
|
||||||
|
]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
65
lib/screens/ge_issue_comment.dart
Normal file
65
lib/screens/ge_issue_comment.dart
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:git_touch/models/auth.dart';
|
||||||
|
import 'package:git_touch/models/gitee.dart';
|
||||||
|
import 'package:git_touch/models/theme.dart';
|
||||||
|
import 'package:git_touch/scaffolds/common.dart';
|
||||||
|
import 'package:git_touch/utils/utils.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class GeIssueCommentScreen extends StatefulWidget {
|
||||||
|
final String owner;
|
||||||
|
final String name;
|
||||||
|
final String number;
|
||||||
|
GeIssueCommentScreen(this.owner, this.name, this.number);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_GeIssueCommentScreenState createState() => _GeIssueCommentScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GeIssueCommentScreenState extends State<GeIssueCommentScreen> {
|
||||||
|
var _body = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Provider.of<ThemeModel>(context);
|
||||||
|
final auth = Provider.of<AuthModel>(context);
|
||||||
|
return CommonScaffold(
|
||||||
|
title: Text('New Comment'),
|
||||||
|
body: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: CommonStyle.padding,
|
||||||
|
child: CupertinoTextField(
|
||||||
|
style: TextStyle(color: theme.palette.text),
|
||||||
|
placeholder: 'Body',
|
||||||
|
onChanged: (v) {
|
||||||
|
setState(() {
|
||||||
|
_body = v;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
maxLines: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CupertinoButton.filled(
|
||||||
|
child: Text('Comment'),
|
||||||
|
onPressed: () async {
|
||||||
|
final res = await auth.fetchGitee(
|
||||||
|
'/repos/${widget.owner}/${widget.name}/issues/${widget.number}/comments',
|
||||||
|
isPost: true,
|
||||||
|
body: {'body': _body, 'repo': widget.name},
|
||||||
|
).then((v) {
|
||||||
|
return GiteeIssue.fromJson(v);
|
||||||
|
});
|
||||||
|
await theme.push(
|
||||||
|
context,
|
||||||
|
'/gitee/${widget.owner}/${widget.name}/issues/${widget.number}',
|
||||||
|
replace: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
77
lib/screens/ge_issue_form.dart
Normal file
77
lib/screens/ge_issue_form.dart
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import 'package:flutter/cupertino.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:git_touch/models/auth.dart';
|
||||||
|
import 'package:git_touch/models/gitee.dart';
|
||||||
|
import 'package:git_touch/models/theme.dart';
|
||||||
|
import 'package:git_touch/scaffolds/common.dart';
|
||||||
|
import 'package:git_touch/utils/utils.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class GeIssueFormScreen extends StatefulWidget {
|
||||||
|
final String owner;
|
||||||
|
final String name;
|
||||||
|
GeIssueFormScreen(this.owner, this.name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_GeIssueFormScreenState createState() => _GeIssueFormScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _GeIssueFormScreenState extends State<GeIssueFormScreen> {
|
||||||
|
var _title = '';
|
||||||
|
var _body = '';
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final theme = Provider.of<ThemeModel>(context);
|
||||||
|
final auth = Provider.of<AuthModel>(context);
|
||||||
|
return CommonScaffold(
|
||||||
|
title: Text('Submit an issue'),
|
||||||
|
body: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: CommonStyle.padding,
|
||||||
|
child: CupertinoTextField(
|
||||||
|
style: TextStyle(color: theme.palette.text),
|
||||||
|
placeholder: 'Title',
|
||||||
|
onChanged: (v) {
|
||||||
|
setState(() {
|
||||||
|
_title = v;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: CommonStyle.padding,
|
||||||
|
child: CupertinoTextField(
|
||||||
|
style: TextStyle(color: theme.palette.text),
|
||||||
|
placeholder: 'Body',
|
||||||
|
onChanged: (v) {
|
||||||
|
setState(() {
|
||||||
|
_body = v;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
maxLines: 10,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
CupertinoButton.filled(
|
||||||
|
child: Text('Submit'),
|
||||||
|
onPressed: () async {
|
||||||
|
final res = await auth.fetchGitee(
|
||||||
|
'/repos/${widget.owner}/issues',
|
||||||
|
isPost: true,
|
||||||
|
body: {'body': _body, 'title': _title, 'repo': widget.name},
|
||||||
|
).then((v) {
|
||||||
|
return GiteeIssue.fromJson(v);
|
||||||
|
});
|
||||||
|
await theme.push(
|
||||||
|
context,
|
||||||
|
'/gitee/${widget.owner}/${widget.name}/issues/${res.number}',
|
||||||
|
replace: true,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
46
lib/screens/ge_issues.dart
Normal file
46
lib/screens/ge_issues.dart
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:git_touch/models/auth.dart';
|
||||||
|
import 'package:git_touch/models/gitee.dart';
|
||||||
|
import 'package:git_touch/scaffolds/list_stateful.dart';
|
||||||
|
import 'package:git_touch/utils/utils.dart';
|
||||||
|
import 'package:git_touch/widgets/action_entry.dart';
|
||||||
|
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||||
|
import 'package:git_touch/widgets/issue_item.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class GeIssuesScreen extends StatelessWidget {
|
||||||
|
final String owner;
|
||||||
|
final String name;
|
||||||
|
final bool isPr;
|
||||||
|
GeIssuesScreen(this.owner, this.name, {this.isPr = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListStatefulScaffold<GiteeIssue, int>(
|
||||||
|
title: AppBarTitle(isPr ? 'Pull Requests' : 'Issues'),
|
||||||
|
fetch: (page) async {
|
||||||
|
final res = await context
|
||||||
|
.read<AuthModel>()
|
||||||
|
.fetchGiteeWithPage('/repos/$owner/$name/issues', page: page);
|
||||||
|
return ListPayload(
|
||||||
|
cursor: res.cursor,
|
||||||
|
hasMore: res.hasMore,
|
||||||
|
items: [for (var v in res.data) GiteeIssue.fromJson(v)],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
actionBuilder: () => ActionEntry(
|
||||||
|
iconData: Octicons.plus,
|
||||||
|
url: '/gitee/$owner/$name/issues/new',
|
||||||
|
),
|
||||||
|
itemBuilder: (p) => IssueItem(
|
||||||
|
author: p.user.login,
|
||||||
|
avatarUrl: p.user.avatarUrl,
|
||||||
|
commentCount: p.comments,
|
||||||
|
subtitle: '#' + p.number,
|
||||||
|
title: p.title,
|
||||||
|
updatedAt: DateTime.parse(p.updatedAt),
|
||||||
|
url: '/gitee/$owner/$name/issues/${p.number}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
40
lib/screens/ge_pulls.dart
Normal file
40
lib/screens/ge_pulls.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:git_touch/models/auth.dart';
|
||||||
|
import 'package:git_touch/models/gitee.dart';
|
||||||
|
import 'package:git_touch/scaffolds/list_stateful.dart';
|
||||||
|
import 'package:git_touch/widgets/app_bar_title.dart';
|
||||||
|
import 'package:git_touch/widgets/issue_item.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
|
class GePullsScreen extends StatelessWidget {
|
||||||
|
final String owner;
|
||||||
|
final String name;
|
||||||
|
final bool isPr;
|
||||||
|
GePullsScreen(this.owner, this.name, {this.isPr = false});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ListStatefulScaffold<GiteePull, int>(
|
||||||
|
title: AppBarTitle(isPr ? 'Pull Requests' : 'Issues'),
|
||||||
|
fetch: (page) async {
|
||||||
|
final res = await context
|
||||||
|
.read<AuthModel>()
|
||||||
|
.fetchGiteeWithPage('/repos/$owner/$name/pulls', page: page);
|
||||||
|
return ListPayload(
|
||||||
|
cursor: res.cursor,
|
||||||
|
hasMore: res.hasMore,
|
||||||
|
items: [for (var v in res.data) GiteePull.fromJson(v)],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
itemBuilder: (p) => IssueItem(
|
||||||
|
author: p.user.login,
|
||||||
|
avatarUrl: p.user.avatarUrl,
|
||||||
|
commentCount: 0, // fix this
|
||||||
|
subtitle: '#' + p.number.toString(),
|
||||||
|
title: p.title,
|
||||||
|
updatedAt: DateTime.parse(p.updatedAt),
|
||||||
|
url: p.htmlUrl,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -92,13 +92,13 @@ class GeRepoScreen extends StatelessWidget {
|
|||||||
leftIconData: Octicons.issue_opened,
|
leftIconData: Octicons.issue_opened,
|
||||||
text: Text('Issues'),
|
text: Text('Issues'),
|
||||||
rightWidget: Text(numberFormat.format(p.openIssuesCount)),
|
rightWidget: Text(numberFormat.format(p.openIssuesCount)),
|
||||||
url: 'https://gitee.com/$owner/$name/issues', // TODO:
|
url: '/gitee/$owner/$name/issues',
|
||||||
),
|
),
|
||||||
if (p.pullRequestsEnabled)
|
if (p.pullRequestsEnabled)
|
||||||
TableViewItem(
|
TableViewItem(
|
||||||
leftIconData: Octicons.git_pull_request,
|
leftIconData: Octicons.git_pull_request,
|
||||||
text: Text('Pull requests'),
|
text: Text('Pull requests'),
|
||||||
url: 'https://gitee.com/$owner/$name/pulls', // TODO:
|
url: '/gitee/$owner/$name/pulls',
|
||||||
),
|
),
|
||||||
TableViewItem(
|
TableViewItem(
|
||||||
leftIconData: Octicons.history,
|
leftIconData: Octicons.history,
|
||||||
|
@ -42,7 +42,7 @@ class GhIssuesScreen extends StatelessWidget {
|
|||||||
author: p.author?.login,
|
author: p.author?.login,
|
||||||
avatarUrl: p.author?.avatarUrl,
|
avatarUrl: p.author?.avatarUrl,
|
||||||
commentCount: p.comments.totalCount,
|
commentCount: p.comments.totalCount,
|
||||||
number: p.number,
|
subtitle: '#' + p.number.toString(),
|
||||||
title: p.title,
|
title: p.title,
|
||||||
updatedAt: p.updatedAt,
|
updatedAt: p.updatedAt,
|
||||||
labels: p.labels.nodes.isEmpty
|
labels: p.labels.nodes.isEmpty
|
||||||
|
@ -37,7 +37,7 @@ class GhPullsScreen extends StatelessWidget {
|
|||||||
author: p.author?.login,
|
author: p.author?.login,
|
||||||
avatarUrl: p.author?.avatarUrl,
|
avatarUrl: p.author?.avatarUrl,
|
||||||
commentCount: p.comments.totalCount,
|
commentCount: p.comments.totalCount,
|
||||||
number: p.number,
|
subtitle: '#' + p.number.toString(),
|
||||||
title: p.title,
|
title: p.title,
|
||||||
updatedAt: p.updatedAt,
|
updatedAt: p.updatedAt,
|
||||||
labels: p.labels.nodes.isEmpty
|
labels: p.labels.nodes.isEmpty
|
||||||
|
@ -198,7 +198,7 @@ class _GhSearchScreenState extends State<GhSearchScreen> {
|
|||||||
author: p['author']['login'],
|
author: p['author']['login'],
|
||||||
avatarUrl: p['author']['avatarUrl'],
|
avatarUrl: p['author']['avatarUrl'],
|
||||||
commentCount: p['comments']['totalCount'],
|
commentCount: p['comments']['totalCount'],
|
||||||
number: p['number'],
|
subtitle: '#' + p['number'].toString(),
|
||||||
title: p['title'],
|
title: p['title'],
|
||||||
updatedAt: DateTime.parse(p['updatedAt']),
|
updatedAt: DateTime.parse(p['updatedAt']),
|
||||||
url: '/github' + Uri.parse(p['url']).path,
|
url: '/github' + Uri.parse(p['url']).path,
|
||||||
|
@ -52,6 +52,7 @@ class GlIssueScreen extends StatelessWidget {
|
|||||||
createdAt: issue.createdAt,
|
createdAt: issue.createdAt,
|
||||||
body: issue.description,
|
body: issue.description,
|
||||||
login: issue.author.username,
|
login: issue.author.username,
|
||||||
|
prefix: 'gitlab',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
CommonStyle.border,
|
CommonStyle.border,
|
||||||
@ -80,6 +81,7 @@ class GlIssueScreen extends StatelessWidget {
|
|||||||
createdAt: note.createdAt,
|
createdAt: note.createdAt,
|
||||||
body: note.body,
|
body: note.body,
|
||||||
login: note.author.username,
|
login: note.author.username,
|
||||||
|
prefix: 'gitlab',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -34,7 +34,7 @@ class GlIssuesScreen extends StatelessWidget {
|
|||||||
author: p.author.username,
|
author: p.author.username,
|
||||||
avatarUrl: p.author.avatarUrl,
|
avatarUrl: p.author.avatarUrl,
|
||||||
commentCount: p.userNotesCount,
|
commentCount: p.userNotesCount,
|
||||||
number: p.iid,
|
subtitle: '#' + p.iid.toString(),
|
||||||
title: p.title,
|
title: p.title,
|
||||||
updatedAt: p.updatedAt,
|
updatedAt: p.updatedAt,
|
||||||
labels: p.labels.isEmpty
|
labels: p.labels.isEmpty
|
||||||
|
@ -33,7 +33,7 @@ class GlMergeRequestsScreen extends StatelessWidget {
|
|||||||
author: p.author.username,
|
author: p.author.username,
|
||||||
avatarUrl: p.author.avatarUrl,
|
avatarUrl: p.author.avatarUrl,
|
||||||
commentCount: p.userNotesCount,
|
commentCount: p.userNotesCount,
|
||||||
number: p.iid,
|
subtitle: '#' + p.iid.toString(),
|
||||||
title: p.title,
|
title: p.title,
|
||||||
updatedAt: p.updatedAt,
|
updatedAt: p.updatedAt,
|
||||||
labels: p.labels.isEmpty
|
labels: p.labels.isEmpty
|
||||||
|
@ -34,7 +34,7 @@ class GtIssuesScreen extends StatelessWidget {
|
|||||||
author: p.user.login,
|
author: p.user.login,
|
||||||
avatarUrl: p.user.avatarUrl,
|
avatarUrl: p.user.avatarUrl,
|
||||||
commentCount: p.comments,
|
commentCount: p.comments,
|
||||||
number: p.number,
|
subtitle: '#' + p.number.toString(),
|
||||||
title: p.title,
|
title: p.title,
|
||||||
updatedAt: p.updatedAt,
|
updatedAt: p.updatedAt,
|
||||||
url: p.htmlUrl,
|
url: p.htmlUrl,
|
||||||
|
@ -137,6 +137,7 @@ class CommentItem extends StatelessWidget {
|
|||||||
final String login;
|
final String login;
|
||||||
final DateTime createdAt;
|
final DateTime createdAt;
|
||||||
final String body;
|
final String body;
|
||||||
|
final String prefix;
|
||||||
final List<Widget> widgets;
|
final List<Widget> widgets;
|
||||||
|
|
||||||
CommentItem.gh(Map<String, dynamic> payload)
|
CommentItem.gh(Map<String, dynamic> payload)
|
||||||
@ -147,13 +148,15 @@ class CommentItem extends StatelessWidget {
|
|||||||
login = payload['author']['login'],
|
login = payload['author']['login'],
|
||||||
createdAt = DateTime.parse(payload['createdAt']),
|
createdAt = DateTime.parse(payload['createdAt']),
|
||||||
body = payload['body'],
|
body = payload['body'],
|
||||||
widgets = [GhEmojiAction(payload)];
|
widgets = [GhEmojiAction(payload)],
|
||||||
|
prefix = 'github';
|
||||||
|
|
||||||
CommentItem({
|
CommentItem({
|
||||||
@required this.avatar,
|
@required this.avatar,
|
||||||
@required this.login,
|
@required this.login,
|
||||||
@required this.createdAt,
|
@required this.createdAt,
|
||||||
@required this.body,
|
@required this.body,
|
||||||
|
@required this.prefix,
|
||||||
this.widgets,
|
this.widgets,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -170,7 +173,7 @@ class CommentItem extends StatelessWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
UserName(login),
|
UserName(login, prefix),
|
||||||
SizedBox(height: 2),
|
SizedBox(height: 2),
|
||||||
Text(
|
Text(
|
||||||
timeago.format(createdAt),
|
timeago.format(createdAt),
|
||||||
|
@ -34,7 +34,7 @@ comments {
|
|||||||
|
|
||||||
class IssueItem extends StatelessWidget {
|
class IssueItem extends StatelessWidget {
|
||||||
final String url;
|
final String url;
|
||||||
final int number;
|
final String subtitle;
|
||||||
final String title;
|
final String title;
|
||||||
final int commentCount;
|
final int commentCount;
|
||||||
final DateTime updatedAt;
|
final DateTime updatedAt;
|
||||||
@ -45,7 +45,7 @@ class IssueItem extends StatelessWidget {
|
|||||||
|
|
||||||
IssueItem({
|
IssueItem({
|
||||||
@required this.url,
|
@required this.url,
|
||||||
@required this.number,
|
@required this.subtitle,
|
||||||
@required this.title,
|
@required this.title,
|
||||||
@required this.commentCount,
|
@required this.commentCount,
|
||||||
@required this.updatedAt,
|
@required this.updatedAt,
|
||||||
@ -83,7 +83,7 @@ class IssueItem extends StatelessWidget {
|
|||||||
children: [
|
children: [
|
||||||
TextSpan(text: '$title '),
|
TextSpan(text: '$title '),
|
||||||
TextSpan(
|
TextSpan(
|
||||||
text: '#$number',
|
text: '$subtitle',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: theme.palette.tertiaryText,
|
color: theme.palette.tertiaryText,
|
||||||
fontWeight: FontWeight.normal,
|
fontWeight: FontWeight.normal,
|
||||||
|
@ -5,14 +5,15 @@ import 'package:provider/provider.dart';
|
|||||||
|
|
||||||
class UserName extends StatelessWidget {
|
class UserName extends StatelessWidget {
|
||||||
final String login;
|
final String login;
|
||||||
|
final String prefix;
|
||||||
|
|
||||||
UserName(this.login);
|
UserName(this.login, this.prefix);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final theme = Provider.of<ThemeModel>(context);
|
final theme = Provider.of<ThemeModel>(context);
|
||||||
return Link(
|
return Link(
|
||||||
url: '/github/$login',
|
url: '/$prefix/$login',
|
||||||
child: Container(
|
child: Container(
|
||||||
// padding: EdgeInsets.all(2),
|
// padding: EdgeInsets.all(2),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user