1
0
mirror of https://github.com/git-touch/git-touch synced 2025-02-08 23:58:46 +01:00

feat(gitee): updates (#148)

* feat: gitee pull screen (with posting comment)

* feat: GeFilesScreen, remove 'changes' prop

* feat: commitScreen, commits in pullScreen

* fix: commit text formatting
This commit is contained in:
Shreyas Thirumalai 2021-01-08 07:55:22 +05:30 committed by GitHub
parent fde136b974
commit 92bcdaa4b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 546 additions and 21 deletions

View File

@ -79,6 +79,7 @@ class GiteeCommit {
GiteeCommitDetail commit;
String sha;
String htmlUrl;
List<GiteeCommitFile> files;
GiteeCommit();
factory GiteeCommit.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitFromJson(json);
@ -152,6 +153,7 @@ class GiteePull {
String body;
String bodyHtml;
String title;
String state;
GiteeRepoOwner user;
int number;
int id;
@ -169,3 +171,42 @@ class GiteeComment {
factory GiteeComment.fromJson(Map<String, dynamic> json) =>
_$GiteeCommentFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteePatch {
String diff;
GiteePatch();
factory GiteePatch.fromJson(Map<String, dynamic> json) =>
_$GiteePatchFromJson(json);
}
// Two different classes because of variable type mismatch
// for additions, deletions, patch
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteePullFile {
String additions;
String deletions;
String blobUrl;
String filename;
String sha;
String status;
GiteePatch patch;
GiteePullFile();
factory GiteePullFile.fromJson(Map<String, dynamic> json) =>
_$GiteePullFileFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GiteeCommitFile {
int additions;
int deletions;
int changes;
String blobUrl;
String filename;
String sha;
String status;
String patch;
GiteeCommitFile();
factory GiteeCommitFile.fromJson(Map<String, dynamic> json) =>
_$GiteeCommitFileFromJson(json);
}

View File

@ -132,7 +132,12 @@ GiteeCommit _$GiteeCommitFromJson(Map<String, dynamic> json) {
? null
: GiteeCommitDetail.fromJson(json['commit'] as Map<String, dynamic>)
..sha = json['sha'] as String
..htmlUrl = json['html_url'] as String;
..htmlUrl = json['html_url'] as String
..files = (json['files'] as List)
?.map((e) => e == null
? null
: GiteeCommitFile.fromJson(e as Map<String, dynamic>))
?.toList();
}
Map<String, dynamic> _$GiteeCommitToJson(GiteeCommit instance) =>
@ -141,6 +146,7 @@ Map<String, dynamic> _$GiteeCommitToJson(GiteeCommit instance) =>
'commit': instance.commit,
'sha': instance.sha,
'html_url': instance.htmlUrl,
'files': instance.files,
};
GiteeCommitDetail _$GiteeCommitDetailFromJson(Map<String, dynamic> json) {
@ -247,6 +253,7 @@ GiteePull _$GiteePullFromJson(Map<String, dynamic> json) {
..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>)
@ -262,6 +269,7 @@ Map<String, dynamic> _$GiteePullToJson(GiteePull instance) => <String, dynamic>{
'body': instance.body,
'body_html': instance.bodyHtml,
'title': instance.title,
'state': instance.state,
'user': instance.user,
'number': instance.number,
'id': instance.id,
@ -282,3 +290,60 @@ Map<String, dynamic> _$GiteeCommentToJson(GiteeComment instance) =>
'created_at': instance.createdAt,
'user': instance.user,
};
GiteePatch _$GiteePatchFromJson(Map<String, dynamic> json) {
return GiteePatch()..diff = json['diff'] as String;
}
Map<String, dynamic> _$GiteePatchToJson(GiteePatch instance) =>
<String, dynamic>{
'diff': instance.diff,
};
GiteePullFile _$GiteePullFileFromJson(Map<String, dynamic> json) {
return GiteePullFile()
..additions = json['additions'] as String
..deletions = json['deletions'] as String
..blobUrl = json['blob_url'] as String
..filename = json['filename'] as String
..sha = json['sha'] as String
..status = json['status'] as String
..patch = json['patch'] == null
? null
: GiteePatch.fromJson(json['patch'] as Map<String, dynamic>);
}
Map<String, dynamic> _$GiteePullFileToJson(GiteePullFile instance) =>
<String, dynamic>{
'additions': instance.additions,
'deletions': instance.deletions,
'blob_url': instance.blobUrl,
'filename': instance.filename,
'sha': instance.sha,
'status': instance.status,
'patch': instance.patch,
};
GiteeCommitFile _$GiteeCommitFileFromJson(Map<String, dynamic> json) {
return GiteeCommitFile()
..additions = json['additions'] as int
..deletions = json['deletions'] as int
..changes = json['changes'] as int
..blobUrl = json['blob_url'] as String
..filename = json['filename'] as String
..sha = json['sha'] as String
..status = json['status'] as String
..patch = json['patch'] as String;
}
Map<String, dynamic> _$GiteeCommitFileToJson(GiteeCommitFile instance) =>
<String, dynamic>{
'additions': instance.additions,
'deletions': instance.deletions,
'changes': instance.changes,
'blob_url': instance.blobUrl,
'filename': instance.filename,
'sha': instance.sha,
'status': instance.status,
'patch': instance.patch,
};

View File

@ -10,11 +10,14 @@ import 'package:git_touch/screens/bb_pulls.dart';
import 'package:git_touch/screens/bb_user.dart';
import 'package:git_touch/screens/code_theme.dart';
import 'package:git_touch/screens/ge_blob.dart';
import 'package:git_touch/screens/ge_commit.dart';
import 'package:git_touch/screens/ge_commits.dart';
import 'package:git_touch/screens/ge_files.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_pull.dart';
import 'package:git_touch/screens/ge_pulls.dart';
import 'package:git_touch/screens/ge_repo.dart';
import 'package:git_touch/screens/ge_repos.dart';
@ -414,6 +417,10 @@ class GiteeRouter {
GiteeRouter.issueAdd, // issueAdd should be above issue
GiteeRouter.issue, // Due to similarity of uris
GiteeRouter.issueComment,
GiteeRouter.pull,
GiteeRouter.pullComment,
GiteeRouter.files,
GiteeRouter.commit,
];
static final search = RouterScreen('/search', (context, parameters) {
return GeSearchScreen();
@ -499,6 +506,31 @@ class GiteeRouter {
static final issueComment = RouterScreen(
'/:owner/:name/issues/:number/comment', (context, parameters) {
return GeIssueCommentScreen(parameters['owner'].first,
parameters['name'].first, parameters['number'].first);
parameters['name'].first, parameters['number'].first,
isPr: false);
});
static final pull = RouterScreen(
'/:owner/:name/pulls/:number',
(context, parameters) {
return GePullScreen(parameters['owner'].first, parameters['name'].first,
parameters['number'].first,
isPr: true);
},
);
static final pullComment = RouterScreen('/:owner/:name/pulls/:number/comment',
(context, parameters) {
return GeIssueCommentScreen(parameters['owner'].first,
parameters['name'].first, parameters['number'].first,
isPr: true);
});
static final files =
RouterScreen('/:owner/:name/pulls/:number/files', (context, parameters) {
return GeFilesScreen(parameters['owner'].first, parameters['name'].first,
parameters['number'].first);
});
static final commit = RouterScreen(
'/:owner/:name/commits/:sha',
(context, parameters) => GeCommitScreen(parameters['owner'].first,
parameters['name'].first, parameters['sha'].first),
);
}

View File

@ -0,0 +1,91 @@
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_button.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/files_item.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/theme.dart';
class GeCommitScreen extends StatelessWidget {
final String owner;
final String name;
final String sha;
GeCommitScreen(this.owner, this.name, this.sha);
@override
Widget build(BuildContext context) {
final theme = context.watch<ThemeModel>();
return RefreshStatefulScaffold<GiteeCommit>(
title: Text("Commit: ${sha.substring(0, 7)}"),
fetch: () async {
final auth = context.read<AuthModel>();
final items = await auth.fetchGitee('/repos/$owner/$name/commits/$sha');
print(GiteeCommit.fromJson(items));
return GiteeCommit.fromJson(items);
},
actionBuilder: (data, _) => ActionButton(
title: 'Actions', items: [...ActionItem.getUrlActions(data.htmlUrl)]),
bodyBuilder: (data, _) {
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: data.author.avatarUrl,
size: AvatarSize.extraSmall,
),
SizedBox(width: 4),
Text(
'$owner / $name',
style: TextStyle(
fontSize: 17,
color: theme.palette.secondaryText,
),
),
SizedBox(width: 4),
Text(
'${sha.substring(0, 7)}',
style: TextStyle(
fontSize: 17,
color: theme.palette.tertiaryText,
),
),
],
),
),
SizedBox(height: 8),
Text(
data.commit.message,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
],
)),
Wrap(
children: data.files
.map<Widget>((file) => FilesItem(
filename: file.filename,
additions: file.additions,
deletions: file.deletions,
status: file.status,
patch: file.patch,
))
.toList(),
)
]);
},
);
}
}

View File

@ -34,7 +34,7 @@ class GeCommitsScreen extends StatelessWidget {
avatarLink: '/gitee/${c.author.login}',
createdAt: c.commit.author.date,
message: c.commit.message,
url: c.htmlUrl,
url: '/gitee/$owner/$name/commits/${c.sha}',
);
},
);

57
lib/screens/ge_files.dart Normal file
View File

@ -0,0 +1,57 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/gitee.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/files_item.dart';
import 'package:git_touch/models/auth.dart';
import '../generated/l10n.dart';
class GeFilesScreen extends StatelessWidget {
final String owner;
final String name;
final String pullNumber;
GeFilesScreen(this.owner, this.name, this.pullNumber);
Widget build(BuildContext context) {
return ListStatefulScaffold<GiteePullFile, int>(
title: AppBarTitle(S.of(context).files),
actionBuilder: () {
return ActionButton(
title: 'Actions',
items: [
...ActionItem.getUrlActions(
'https://gitee.com/$owner/$name/pulls/$pullNumber/files'),
],
);
},
fetch: (page) async {
page = page ?? 1;
final res = await context
.read<AuthModel>()
.fetchGiteeWithPage(
'/repos/$owner/$name/pulls/$pullNumber/files?page=$page',
)
.then((v) {
return [for (var file in v.data) GiteePullFile.fromJson(file)];
});
return ListPayload(
cursor: page + 1,
items: res,
hasMore: res.isNotEmpty,
);
},
itemBuilder: (v) {
return FilesItem(
filename: v.filename,
additions: int.parse(v.additions),
deletions: int.parse(v.deletions),
status: v.status,
patch: v.patch.diff,
);
},
);
}
}

View File

@ -11,7 +11,8 @@ class GeIssueCommentScreen extends StatefulWidget {
final String owner;
final String name;
final String number;
GeIssueCommentScreen(this.owner, this.name, this.number);
final bool isPr;
GeIssueCommentScreen(this.owner, this.name, this.number, {this.isPr: false});
@override
_GeIssueCommentScreenState createState() => _GeIssueCommentScreenState();
@ -44,18 +45,35 @@ class _GeIssueCommentScreenState extends State<GeIssueCommentScreen> {
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,
);
if (!widget.isPr) {
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);
});
Navigator.pop(context, '');
await theme.push(
context,
'/gitee/${widget.owner}/${widget.name}/issues/${widget.number}',
replace: true,
);
} else {
final res = await auth.fetchGitee(
'/repos/${widget.owner}/${widget.name}/pulls/${widget.number}/comments',
isPost: true,
body: {'body': _body, 'repo': widget.name},
).then((v) {
return GiteePull.fromJson(v);
});
Navigator.pop(context, '');
await theme.push(
context,
'/gitee/${widget.owner}/${widget.name}/pulls/${widget.number}',
replace: true,
);
}
},
),
],

225
lib/screens/ge_pull.dart Normal file
View File

@ -0,0 +1,225 @@
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 GePullScreen extends StatelessWidget {
final String owner;
final String name;
final String number;
final bool isPr;
GePullScreen(this.owner, this.name, this.number, {this.isPr: false});
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<
Tuple4<GiteePull, List<GiteeComment>, List<GiteePullFile>,
List<GiteeCommit>>>(
title: Text("Pull Request: #$number"),
fetch: () async {
final auth = context.read<AuthModel>();
final items = await Future.wait([
auth.fetchGitee('/repos/$owner/$name/pulls/$number'),
auth.fetchGitee('/repos/$owner/$name/pulls/$number/comments'),
auth.fetchGitee('/repos/$owner/$name/pulls/$number/files'),
auth.fetchGitee('/repos/$owner/$name/pulls/$number/commits'),
]);
return Tuple4(
GiteePull.fromJson(items[0]),
[for (var v in items[1]) GiteeComment.fromJson(v)],
[for (var v in items[2]) GiteePullFile.fromJson(v)],
[for (var v in items[3]) GiteeCommit.fromJson(v)]);
},
actionBuilder: (data, _) => ActionEntry(
iconData: Octicons.plus,
url: '/gitee/$owner/$name/pulls/$number/comment',
),
bodyBuilder: (data, _) {
final pull = data.item1;
final comments = data.item2;
final files = data.item3;
final commits = data.item4;
final theme = context.read<ThemeModel>();
var additions = 0;
var deletions = 0;
for (var file in files) {
additions += int.parse(file.additions);
deletions += int.parse(file.deletions);
}
return Column(children: <Widget>[
Container(
padding: CommonStyle.padding,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Link(
child: Container(
padding: EdgeInsets.symmetric(vertical: 8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Link(
url: '/gitee/$owner/$name',
child: Row(
children: <Widget>[
Avatar(
url: pull.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(
pull.title,
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 8),
StateLabel(
pull.state == 'open'
? StateLabelStatus.pullOpened
: StateLabelStatus.pullClosed,
small: true),
SizedBox(height: 16),
CommonStyle.border,
CommonStyle.border,
Link(
url: '/gitee/$owner/$name/pulls/$number/files',
child: Container(
padding: EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'${files.length} files changed',
style: TextStyle(
color: theme.palette.secondaryText,
fontSize: 17,
),
),
Row(
children: <Widget>[
Text(
'+$additions',
style: TextStyle(
color: Colors.green,
fontSize: 15,
),
),
SizedBox(width: 2),
Text(
'-$deletions',
style: TextStyle(
color: Colors.red,
fontSize: 15,
),
),
Icon(
Icons.chevron_right,
color: theme.palette.border,
),
],
)
],
),
),
),
CommonStyle.border,
ListTileTheme(
contentPadding: EdgeInsets.zero,
child: ExpansionTile(
title: Text(
'Commits',
style: TextStyle(
color: theme.palette.primary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
children: [
for (var commit in commits) ...[
Link(
url:
'/gitee/$owner/$name/commits/${commit.sha}',
child: Container(
padding:
EdgeInsets.symmetric(vertical: 8),
child: Row(
mainAxisAlignment:
MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'${commit.sha.substring(0, 7)}',
style: TextStyle(
color: theme.palette.primary,
fontSize: 17,
fontFamily:
CommonStyle.monospace,
),
),
],
),
),
)
]
],
)),
]),
),
url: '/gitee/$owner/$name',
),
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),
],
]),
]);
},
);
}
}

View File

@ -33,7 +33,7 @@ class GePullsScreen extends StatelessWidget {
subtitle: '#' + p.number.toString(),
title: p.title,
updatedAt: DateTime.parse(p.updatedAt),
url: p.htmlUrl,
url: '/gitee/$owner/$name/pulls/${p.number}',
),
);
}

View File

@ -42,7 +42,6 @@ class GhComparisonScreen extends StatelessWidget {
additions: vs.additions,
deletions: vs.deletions,
status: vs.status,
changes: vs.changes,
patch: vs.patch,
))
.toList(),

View File

@ -48,7 +48,6 @@ class GhFilesScreen extends StatelessWidget {
additions: v.additions,
deletions: v.deletions,
status: v.status,
changes: v.changes,
patch: v.patch,
);
},

View File

@ -9,7 +9,6 @@ import 'package:flutter_highlight/theme_map.dart';
class FilesItem extends StatelessWidget {
final String filename;
final String status;
final int changes;
final int additions;
final int deletions;
final String patch;
@ -17,7 +16,6 @@ class FilesItem extends StatelessWidget {
FilesItem({
@required this.filename,
@required this.status,
@required this.changes,
@required this.deletions,
@required this.additions,
@required this.patch,