1
0
mirror of https://github.com/git-touch/git-touch synced 2025-02-22 06:17:40 +01:00

feat: contributor screen (#64)

closes #53
This commit is contained in:
Shreyas Thirumalai 2020-04-25 16:01:32 +05:30 committed by GitHub
parent 0864af7855
commit be30bcb7d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 178 additions and 3 deletions

View File

@ -301,3 +301,15 @@ class GithubContentReferenceItem {
factory GithubContentReferenceItem.fromJson(Map<String, dynamic> json) => factory GithubContentReferenceItem.fromJson(Map<String, dynamic> json) =>
_$GithubContentReferenceItemFromJson(json); _$GithubContentReferenceItemFromJson(json);
} }
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubContributorItem {
int id;
String login;
String avatarUrl;
String htmlUrl;
int contributions;
GithubContributorItem();
factory GithubContributorItem.fromJson(Map<String, dynamic> json) =>
_$GithubContributorItemFromJson(json);
}

View File

@ -108,7 +108,11 @@ GithubEventPayload _$GithubEventPayloadFromJson(Map<String, dynamic> json) {
..checkSuite = json['check_suite'] == null ..checkSuite = json['check_suite'] == null
? null ? null
: GithubCheckSuiteItem.fromJson( : GithubCheckSuiteItem.fromJson(
json['check_suite'] as Map<String, dynamic>); json['check_suite'] as Map<String, dynamic>)
..contentReference = json['content_reference'] == null
? null
: GithubContentReferenceItem.fromJson(
json['content_reference'] as Map<String, dynamic>);
} }
Map<String, dynamic> _$GithubEventPayloadToJson(GithubEventPayload instance) => Map<String, dynamic> _$GithubEventPayloadToJson(GithubEventPayload instance) =>
@ -132,6 +136,7 @@ Map<String, dynamic> _$GithubEventPayloadToJson(GithubEventPayload instance) =>
'installation': instance.installation, 'installation': instance.installation,
'check_run': instance.checkRun, 'check_run': instance.checkRun,
'check_suite': instance.checkSuite, 'check_suite': instance.checkSuite,
'content_reference': instance.contentReference,
}; };
GithubEventIssue _$GithubEventIssueFromJson(Map<String, dynamic> json) { GithubEventIssue _$GithubEventIssueFromJson(Map<String, dynamic> json) {
@ -415,3 +420,23 @@ Map<String, dynamic> _$GithubContentReferenceItemToJson(
'id': instance.id, 'id': instance.id,
'reference': instance.reference, 'reference': instance.reference,
}; };
GithubContributorItem _$GithubContributorItemFromJson(
Map<String, dynamic> json) {
return GithubContributorItem()
..id = json['id'] as int
..login = json['login'] as String
..avatarUrl = json['avatar_url'] as String
..htmlUrl = json['html_url'] as String
..contributions = json['contributions'] as int;
}
Map<String, dynamic> _$GithubContributorItemToJson(
GithubContributorItem instance) =>
<String, dynamic>{
'id': instance.id,
'login': instance.login,
'avatar_url': instance.avatarUrl,
'html_url': instance.htmlUrl,
'contributions': instance.contributions,
};

View File

@ -5,6 +5,7 @@ import 'package:git_touch/screens/bb_repo.dart';
import 'package:git_touch/screens/bb_user.dart'; 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/gh_commits.dart'; import 'package:git_touch/screens/gh_commits.dart';
import 'package:git_touch/screens/gh_contributors.dart';
import 'package:git_touch/screens/gh_org_repos.dart'; import 'package:git_touch/screens/gh_org_repos.dart';
import 'package:git_touch/screens/gl_commit.dart'; import 'package:git_touch/screens/gl_commit.dart';
import 'package:git_touch/screens/gl_starrers.dart'; import 'package:git_touch/screens/gl_starrers.dart';
@ -71,6 +72,7 @@ class GithubRouter {
GithubRouter.object, GithubRouter.object,
GithubRouter.stargazers, GithubRouter.stargazers,
GithubRouter.watchers, GithubRouter.watchers,
GithubRouter.contributors,
]; ];
static final user = RouterScreen('/:login', (_, p) { static final user = RouterScreen('/:login', (_, p) {
final login = p['login'].first; final login = p['login'].first;
@ -135,6 +137,10 @@ class GithubRouter {
return GhUsersScreen(p['owner'].first, UsersScreenType.watch, return GhUsersScreen(p['owner'].first, UsersScreenType.watch,
repoName: p['name'].first); repoName: p['name'].first);
}); });
static final contributors =
RouterScreen('/:owner/:name/contributors', (_, p) {
return GhContributorsScreen(p['owner'].first, p['name'].first);
});
} }
class GitlabRouter { class GitlabRouter {

View File

@ -0,0 +1,49 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/github.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/scaffolds/list_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/contributor_item.dart';
import 'package:github/github.dart';
import 'package:provider/provider.dart';
import 'package:git_touch/widgets/event_item.dart';
import 'package:git_touch/models/auth.dart';
class GhContributorsScreen extends StatelessWidget {
final String owner;
final String name;
GhContributorsScreen(this.owner, this.name);
Future<ListPayload<GithubContributorItem, int>> _query(BuildContext context,
[int page = 1]) async {
final auth = Provider.of<AuthModel>(context);
final res = await auth.ghClient.getJSON<List, List<GithubContributorItem>>(
'/repos/$owner/$name/contributors?page=$page',
convert: (vs) => [for (var v in vs) GithubContributorItem.fromJson(v)],
);
return ListPayload(
cursor: page + 1,
items: res,
hasMore: res.isNotEmpty,
);
}
Widget build(BuildContext context) {
return ListStatefulScaffold<GithubContributorItem, int>(
title: AppBarTitle('Contributors'),
onRefresh: () => _query(context),
onLoadMore: (cursor) => _query(context, cursor),
itemBuilder: (v) {
final String login = v.login;
return ContributorItem(
avatarUrl: v.avatarUrl,
commits: v.contributions,
login: v.login,
url: '/$login?tab=contributors',
);
},
);
}
}

View File

@ -51,6 +51,14 @@ class GhRepoScreen extends StatelessWidget {
} }
} }
Future<String> _fetchContributors(BuildContext context) async {
final auth = Provider.of<AuthModel>(context);
final res = await auth.ghClient.getJSON(
'/repos/$owner/$name/stats/contributors',
);
return res.length.toString();
}
Future<String> _fetchReadme(BuildContext context) async { Future<String> _fetchReadme(BuildContext context) async {
try { try {
final auth = Provider.of<AuthModel>(context); final auth = Provider.of<AuthModel>(context);
@ -67,14 +75,16 @@ class GhRepoScreen extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context); final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context); final auth = Provider.of<AuthModel>(context);
return RefreshStatefulScaffold<Tuple2<GhRepoRepository, String>>( return RefreshStatefulScaffold<Tuple3<GhRepoRepository, String, String>>(
title: AppBarTitle('Repository'), title: AppBarTitle('Repository'),
fetchData: () async { fetchData: () async {
final rs = await Future.wait([ final rs = await Future.wait([
_query(context), _query(context),
_fetchReadme(context), _fetchReadme(context),
_fetchContributors(context),
]); ]);
return Tuple2(rs[0] as GhRepoRepository, rs[1] as String);
return Tuple3(rs[0] as GhRepoRepository, rs[1] as String, rs[2]);
}, },
actionBuilder: (data, setState) { actionBuilder: (data, setState) {
final repo = data.item1; final repo = data.item1;
@ -96,6 +106,7 @@ class GhRepoScreen extends StatelessWidget {
bodyBuilder: (data, setState) { bodyBuilder: (data, setState) {
final repo = data.item1; final repo = data.item1;
final readme = data.item2; final readme = data.item2;
final contributorsCount = data.item3;
final ref = branch == null ? repo.defaultBranchRef : repo.ref; final ref = branch == null ? repo.defaultBranchRef : repo.ref;
final license = repo.licenseInfo?.spdxId ?? repo.licenseInfo?.name; final license = repo.licenseInfo?.spdxId ?? repo.licenseInfo?.name;
@ -321,6 +332,12 @@ class GhRepoScreen extends StatelessWidget {
}, },
), ),
], ],
TableViewItem(
leftIconData: Octicons.organization,
text: Text('Contributors'),
rightWidget: Text(contributorsCount),
url: '/$owner/$name/contributors',
)
], ],
), ),
if (readme != null) if (readme != null)

View File

@ -0,0 +1,66 @@
import 'package:flutter/material.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/avatar.dart';
import 'package:git_touch/widgets/link.dart';
import 'package:provider/provider.dart';
class ContributorItem extends StatelessWidget {
final String login;
final String avatarUrl;
final int commits;
final String url;
ContributorItem({
@required this.login,
@required this.avatarUrl,
@required this.commits,
@required this.url,
});
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
return Link(
url: url,
child: Container(
padding: CommonStyle.padding,
child: Row(
children: <Widget>[
Avatar(url: avatarUrl, size: AvatarSize.large),
SizedBox(width: 10),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
Text(
login,
style: TextStyle(
color: theme.palette.primary,
fontSize: 18,
fontWeight: FontWeight.w600,
),
),
],
),
SizedBox(height: 6),
if (commits != null)
DefaultTextStyle(
style: TextStyle(
color: theme.palette.secondaryText,
fontSize: 16,
),
child: Text("Commits: " + commits.toString()),
),
],
),
)
],
),
),
);
}
}