1
0
mirror of https://github.com/git-touch/git-touch synced 2025-02-20 21:40:44 +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) =>
_$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
? null
: 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) =>
@ -132,6 +136,7 @@ Map<String, dynamic> _$GithubEventPayloadToJson(GithubEventPayload instance) =>
'installation': instance.installation,
'check_run': instance.checkRun,
'check_suite': instance.checkSuite,
'content_reference': instance.contentReference,
};
GithubEventIssue _$GithubEventIssueFromJson(Map<String, dynamic> json) {
@ -415,3 +420,23 @@ Map<String, dynamic> _$GithubContentReferenceItemToJson(
'id': instance.id,
'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/code_theme.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/gl_commit.dart';
import 'package:git_touch/screens/gl_starrers.dart';
@ -71,6 +72,7 @@ class GithubRouter {
GithubRouter.object,
GithubRouter.stargazers,
GithubRouter.watchers,
GithubRouter.contributors,
];
static final user = RouterScreen('/:login', (_, p) {
final login = p['login'].first;
@ -135,6 +137,10 @@ class GithubRouter {
return GhUsersScreen(p['owner'].first, UsersScreenType.watch,
repoName: p['name'].first);
});
static final contributors =
RouterScreen('/:owner/:name/contributors', (_, p) {
return GhContributorsScreen(p['owner'].first, p['name'].first);
});
}
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 {
try {
final auth = Provider.of<AuthModel>(context);
@ -67,14 +75,16 @@ class GhRepoScreen extends StatelessWidget {
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context);
return RefreshStatefulScaffold<Tuple2<GhRepoRepository, String>>(
return RefreshStatefulScaffold<Tuple3<GhRepoRepository, String, String>>(
title: AppBarTitle('Repository'),
fetchData: () async {
final rs = await Future.wait([
_query(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) {
final repo = data.item1;
@ -96,6 +106,7 @@ class GhRepoScreen extends StatelessWidget {
bodyBuilder: (data, setState) {
final repo = data.item1;
final readme = data.item2;
final contributorsCount = data.item3;
final ref = branch == null ? repo.defaultBranchRef : repo.ref;
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)

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()),
),
],
),
)
],
),
),
);
}
}