mirror of
https://github.com/git-touch/git-touch
synced 2024-12-17 02:38:39 +01:00
refactor: refresh scaffold
This commit is contained in:
parent
0fce330d70
commit
e7b01a0c23
@ -2,17 +2,28 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:git_touch/scaffolds/utils.dart';
|
||||
|
||||
class RefreshStatefulScaffoldPayload<T> {
|
||||
bool loading;
|
||||
String error;
|
||||
T data;
|
||||
void Function() refresh;
|
||||
|
||||
RefreshStatefulScaffoldPayload(
|
||||
this.loading, this.error, this.data, this.refresh);
|
||||
}
|
||||
|
||||
class RefreshStatefulScaffold<T> extends StatefulWidget {
|
||||
final Widget title;
|
||||
final Widget Function(T payload) bodyBuilder;
|
||||
final Future<T> Function() onRefresh;
|
||||
final Widget Function(T payload) trailingBuilder;
|
||||
final Widget Function(RefreshStatefulScaffoldPayload<T> payload) bodyBuilder;
|
||||
final Future<T> Function() fetchData;
|
||||
final Widget Function(RefreshStatefulScaffoldPayload<T> payload)
|
||||
actionBuilder;
|
||||
|
||||
RefreshStatefulScaffold({
|
||||
@required this.title,
|
||||
@required this.bodyBuilder,
|
||||
@required this.onRefresh,
|
||||
this.trailingBuilder,
|
||||
@required this.fetchData,
|
||||
this.actionBuilder,
|
||||
});
|
||||
|
||||
@override
|
||||
@ -23,9 +34,12 @@ class RefreshStatefulScaffold<T> extends StatefulWidget {
|
||||
class _RefreshStatefulScaffoldState<T>
|
||||
extends State<RefreshStatefulScaffold<T>> {
|
||||
bool _loading;
|
||||
T _payload;
|
||||
T _data;
|
||||
String _error = '';
|
||||
|
||||
RefreshStatefulScaffoldPayload get _payload =>
|
||||
RefreshStatefulScaffoldPayload(_loading, _error, _data, _refresh);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@ -38,7 +52,7 @@ class _RefreshStatefulScaffoldState<T>
|
||||
_error = '';
|
||||
_loading = true;
|
||||
});
|
||||
_payload = await widget.onRefresh();
|
||||
_data = await widget.fetchData();
|
||||
} catch (err) {
|
||||
_error = err.toString();
|
||||
throw err;
|
||||
@ -52,8 +66,8 @@ class _RefreshStatefulScaffoldState<T>
|
||||
}
|
||||
|
||||
Widget get _trailing {
|
||||
if (widget.trailingBuilder == null) return null;
|
||||
return widget.trailingBuilder(_payload);
|
||||
if (widget.actionBuilder == null) return null;
|
||||
return widget.actionBuilder(_payload);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -65,7 +79,7 @@ class _RefreshStatefulScaffoldState<T>
|
||||
body: ErrorLoadingWrapper(
|
||||
bodyBuilder: () => widget.bodyBuilder(_payload),
|
||||
error: _error,
|
||||
loading: _payload == null,
|
||||
loading: _data == null,
|
||||
reload: _refresh,
|
||||
),
|
||||
),
|
||||
|
@ -146,7 +146,7 @@ class ObjectScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshStatefulScaffold(
|
||||
title: AppBarTitle(paths.join('/')),
|
||||
onRefresh: () async {
|
||||
fetchData: () async {
|
||||
var data = await Provider.of<AuthModel>(context).query('''{
|
||||
repository(owner: "$owner", name: "$name") {
|
||||
object(expression: "$_expression") {
|
||||
@ -170,15 +170,15 @@ class ObjectScreen extends StatelessWidget {
|
||||
|
||||
return data['repository']['object'];
|
||||
},
|
||||
trailingBuilder: (payload) {
|
||||
actionBuilder: (payload) {
|
||||
switch (type) {
|
||||
case 'blob':
|
||||
return ActionEntry(
|
||||
iconData: Octicons.settings,
|
||||
onTap: () {
|
||||
if (payload != null) {
|
||||
if (payload.data != null) {
|
||||
Provider.of<ThemeModel>(context).pushRoute(context,
|
||||
(_) => CodeThemeScreen(payload['text'], _language));
|
||||
(_) => CodeThemeScreen(payload.data['text'], _language));
|
||||
}
|
||||
},
|
||||
);
|
||||
|
@ -53,7 +53,7 @@ class OrganizationScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshStatefulScaffold(
|
||||
onRefresh: () async {
|
||||
fetchData: () async {
|
||||
// Use pinnableItems instead of organization here due to token permission
|
||||
var data = await Provider.of<AuthModel>(context).query('''
|
||||
{
|
||||
@ -89,37 +89,39 @@ class OrganizationScreen extends StatelessWidget {
|
||||
return data['organization'];
|
||||
},
|
||||
title: AppBarTitle('Organization'),
|
||||
trailingBuilder: (payload) {
|
||||
actionBuilder: (payload) {
|
||||
return ActionButton(
|
||||
title: 'Organization Actions',
|
||||
items: [
|
||||
if (payload != null) ...[
|
||||
ActionItem.share(payload['url']),
|
||||
ActionItem.launch(payload['url']),
|
||||
if (payload.data != null) ...[
|
||||
ActionItem.share(payload.data['url']),
|
||||
ActionItem.launch(payload.data['url']),
|
||||
],
|
||||
],
|
||||
);
|
||||
},
|
||||
bodyBuilder: (payload) {
|
||||
var data = payload.data;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
UserItem(
|
||||
login: login,
|
||||
name: payload['name'],
|
||||
avatarUrl: payload['avatarUrl'],
|
||||
bio: payload['description'],
|
||||
name: data['name'],
|
||||
avatarUrl: data['avatarUrl'],
|
||||
bio: data['description'],
|
||||
),
|
||||
borderView,
|
||||
Row(children: <Widget>[
|
||||
EntryItem(
|
||||
count: payload['pinnableItems']['totalCount'],
|
||||
count: data['pinnableItems']['totalCount'],
|
||||
text: 'Repositories',
|
||||
screenBuilder: (context) =>
|
||||
RepositoriesScreen.ofOrganization(login),
|
||||
),
|
||||
EntryItem(
|
||||
count: payload['membersWithRole']['totalCount'],
|
||||
count: data['membersWithRole']['totalCount'],
|
||||
text: 'Members',
|
||||
screenBuilder: (context) => UsersScreen.members(login),
|
||||
),
|
||||
@ -128,30 +130,30 @@ class OrganizationScreen extends StatelessWidget {
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
if (isNotNullOrEmpty(payload['location']))
|
||||
if (isNotNullOrEmpty(data['location']))
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.location,
|
||||
text: Text(payload['location']),
|
||||
text: Text(data['location']),
|
||||
onTap: () {
|
||||
launchUrl('https://www.google.com/maps/place/' +
|
||||
(payload['location'] as String)
|
||||
(data['location'] as String)
|
||||
.replaceAll(RegExp(r'\s+'), ''));
|
||||
},
|
||||
),
|
||||
if (isNotNullOrEmpty(payload['email']))
|
||||
if (isNotNullOrEmpty(data['email']))
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.mail,
|
||||
text: Text(payload['email']),
|
||||
text: Text(data['email']),
|
||||
onTap: () {
|
||||
launchUrl('mailto:' + payload['email']);
|
||||
launchUrl('mailto:' + data['email']);
|
||||
},
|
||||
),
|
||||
if (isNotNullOrEmpty(payload['websiteUrl']))
|
||||
if (isNotNullOrEmpty(data['websiteUrl']))
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.link,
|
||||
text: Text(payload['websiteUrl']),
|
||||
text: Text(data['websiteUrl']),
|
||||
onTap: () {
|
||||
var url = payload['websiteUrl'] as String;
|
||||
var url = data['websiteUrl'] as String;
|
||||
if (!url.startsWith('http')) {
|
||||
url = 'http://$url';
|
||||
}
|
||||
@ -160,7 +162,7 @@ class OrganizationScreen extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
..._buildRepos(payload),
|
||||
..._buildRepos(data),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -141,11 +141,12 @@ class RepositoryScreen extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshStatefulScaffold(
|
||||
title: AppBarTitle('Repository'),
|
||||
onRefresh: () => Future.wait([
|
||||
fetchData: () => Future.wait([
|
||||
queryRepo(context),
|
||||
fetchReadme(context),
|
||||
]),
|
||||
trailingBuilder: (data) {
|
||||
actionBuilder: (payload) {
|
||||
var data = payload.data;
|
||||
return ActionButton(
|
||||
title: 'Repository Actions',
|
||||
items: [
|
||||
@ -206,41 +207,41 @@ class RepositoryScreen extends StatelessWidget {
|
||||
],
|
||||
);
|
||||
},
|
||||
bodyBuilder: (data) {
|
||||
var payload = data[0];
|
||||
var readme = data[1] as String;
|
||||
bodyBuilder: (payload) {
|
||||
var data = payload.data[0];
|
||||
var readme = payload.data[1] as String;
|
||||
|
||||
final langWidth = MediaQuery.of(context).size.width -
|
||||
_languageBarPadding * 2 -
|
||||
(payload['languages']['edges'] as List).length +
|
||||
(data['languages']['edges'] as List).length +
|
||||
1;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
RepositoryItem(payload, inRepoScreen: true),
|
||||
RepositoryItem(data, inRepoScreen: true),
|
||||
borderView,
|
||||
Row(
|
||||
children: <Widget>[
|
||||
EntryItem(
|
||||
count: payload['watchers']['totalCount'],
|
||||
count: data['watchers']['totalCount'],
|
||||
text: 'Watchers',
|
||||
screenBuilder: (context) => UsersScreen.watchers(owner, name),
|
||||
),
|
||||
EntryItem(
|
||||
count: payload['stargazers']['totalCount'],
|
||||
count: data['stargazers']['totalCount'],
|
||||
text: 'Stars',
|
||||
screenBuilder: (context) => UsersScreen.stars(owner, name),
|
||||
),
|
||||
EntryItem(
|
||||
count: payload['forks']['totalCount'],
|
||||
count: data['forks']['totalCount'],
|
||||
text: 'Forks',
|
||||
// screenBuilder: (context) => UsersScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
borderView1,
|
||||
if ((payload['languages']['edges'] as List).isNotEmpty)
|
||||
if ((data['languages']['edges'] as List).isNotEmpty)
|
||||
Container(
|
||||
padding: const EdgeInsets.all(_languageBarPadding),
|
||||
child: ClipRRect(
|
||||
@ -250,12 +251,12 @@ class RepositoryScreen extends StatelessWidget {
|
||||
child: Row(
|
||||
children: join(
|
||||
SizedBox(width: 1),
|
||||
(payload['languages']['edges'] as List)
|
||||
(data['languages']['edges'] as List)
|
||||
.map((lang) => Container(
|
||||
color: convertColor(lang['node']['color']),
|
||||
width: langWidth *
|
||||
lang['size'] /
|
||||
payload['languages']['totalSize']))
|
||||
data['languages']['totalSize']))
|
||||
.toList())),
|
||||
),
|
||||
),
|
||||
@ -263,41 +264,41 @@ class RepositoryScreen extends StatelessWidget {
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
if (payload[branchInfoKey] != null)
|
||||
if (data[branchInfoKey] != null)
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.code,
|
||||
text: Text('Code'),
|
||||
rightWidget:
|
||||
Text(filesize((payload['diskUsage'] as int) * 1000)),
|
||||
Text(filesize((data['diskUsage'] as int) * 1000)),
|
||||
screenBuilder: (_) => ObjectScreen(
|
||||
owner: owner,
|
||||
name: name,
|
||||
branch: payload[branchInfoKey]['name'],
|
||||
branch: data[branchInfoKey]['name'],
|
||||
),
|
||||
),
|
||||
if (payload['hasIssuesEnabled'] as bool)
|
||||
if (data['hasIssuesEnabled'] as bool)
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.issue_opened,
|
||||
text: Text('Issues'),
|
||||
rightWidget: Text(
|
||||
numberFormat.format(payload['issues']['totalCount'])),
|
||||
rightWidget:
|
||||
Text(numberFormat.format(data['issues']['totalCount'])),
|
||||
screenBuilder: (_) =>
|
||||
IssuesScreen(owner: owner, name: name),
|
||||
),
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.git_pull_request,
|
||||
text: Text('Pull requests'),
|
||||
rightWidget: Text(numberFormat
|
||||
.format(payload['pullRequests']['totalCount'])),
|
||||
rightWidget: Text(
|
||||
numberFormat.format(data['pullRequests']['totalCount'])),
|
||||
screenBuilder: (_) => IssuesScreen(
|
||||
owner: owner, name: name, isPullRequest: true),
|
||||
),
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.project,
|
||||
text: Text('Projects'),
|
||||
rightWidget: Text(
|
||||
numberFormat.format(payload['projects']['totalCount'])),
|
||||
url: 'https://github.com' + payload['projectsResourcePath'],
|
||||
rightWidget:
|
||||
Text(numberFormat.format(data['projects']['totalCount'])),
|
||||
url: 'https://github.com' + data['projectsResourcePath'],
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -305,30 +306,30 @@ class RepositoryScreen extends StatelessWidget {
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
if (payload[branchInfoKey] != null) ...[
|
||||
if (data[branchInfoKey] != null) ...[
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.history,
|
||||
text: Text('Commits'),
|
||||
rightWidget: Text(numberFormat.format(payload[branchInfoKey]
|
||||
rightWidget: Text(numberFormat.format(data[branchInfoKey]
|
||||
['target']['history']['totalCount'])),
|
||||
screenBuilder: (_) =>
|
||||
CommitsScreen(owner, name, branch: branch),
|
||||
),
|
||||
if (payload['refs'] != null)
|
||||
if (data['refs'] != null)
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.git_branch,
|
||||
text: Text('Branches'),
|
||||
rightWidget: Text(payload[branchInfoKey]['name'] +
|
||||
rightWidget: Text(data[branchInfoKey]['name'] +
|
||||
' • ' +
|
||||
numberFormat.format(payload['refs']['totalCount'])),
|
||||
numberFormat.format(data['refs']['totalCount'])),
|
||||
onTap: () async {
|
||||
var refs = payload['refs']['nodes'] as List;
|
||||
var refs = data['refs']['nodes'] as List;
|
||||
if (refs.length < 2) return;
|
||||
|
||||
await Provider.of<ThemeModel>(context).showPicker(
|
||||
context,
|
||||
PickerGroupItem(
|
||||
value: payload[branchInfoKey]['name'],
|
||||
value: data[branchInfoKey]['name'],
|
||||
items: refs
|
||||
.map((b) => PickerItem(b['name'] as String,
|
||||
text: (b['name'] as String)))
|
||||
@ -349,17 +350,17 @@ class RepositoryScreen extends StatelessWidget {
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.tag,
|
||||
text: Text('Releases'),
|
||||
rightWidget: Text(
|
||||
(payload['releases']['totalCount'] as int).toString()),
|
||||
url: payload['url'] + '/releases',
|
||||
rightWidget:
|
||||
Text((data['releases']['totalCount'] as int).toString()),
|
||||
url: data['url'] + '/releases',
|
||||
),
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.law,
|
||||
text: Text('License'),
|
||||
rightWidget: Text(payload['licenseInfo'] == null
|
||||
rightWidget: Text(data['licenseInfo'] == null
|
||||
? 'Unknown'
|
||||
: (payload['licenseInfo']['spdxId'] ??
|
||||
payload['licenseInfo']['name'])),
|
||||
: (data['licenseInfo']['spdxId'] ??
|
||||
data['licenseInfo']['name'])),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -141,13 +141,14 @@ class UserScreen extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return RefreshStatefulScaffold(
|
||||
onRefresh: () {
|
||||
fetchData: () {
|
||||
return Future.wait(
|
||||
[query(context), getContributions(login)],
|
||||
);
|
||||
},
|
||||
title: AppBarTitle('User'),
|
||||
trailingBuilder: (data) {
|
||||
actionBuilder: (payload) {
|
||||
var data = payload.data;
|
||||
if (isMe) {
|
||||
return ActionEntry(
|
||||
iconData: Icons.settings,
|
||||
@ -184,33 +185,33 @@ class UserScreen extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
},
|
||||
bodyBuilder: (data) {
|
||||
var payload = data[0];
|
||||
var contributions = data[1] as List<ContributionsInfo>;
|
||||
bodyBuilder: (payload) {
|
||||
var data = payload.data[0];
|
||||
var contributions = payload.data[1] as List<ContributionsInfo>;
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
UserItem.fromData(payload, inUserScreen: true),
|
||||
UserItem.fromData(data, inUserScreen: true),
|
||||
borderView,
|
||||
Row(children: <Widget>[
|
||||
EntryItem(
|
||||
count: payload['repositories']['totalCount'],
|
||||
count: data['repositories']['totalCount'],
|
||||
text: 'Repositories',
|
||||
screenBuilder: (context) => RepositoriesScreen(login),
|
||||
),
|
||||
EntryItem(
|
||||
count: payload['starredRepositories']['totalCount'],
|
||||
count: data['starredRepositories']['totalCount'],
|
||||
text: 'Stars',
|
||||
screenBuilder: (context) => RepositoriesScreen.stars(login),
|
||||
),
|
||||
EntryItem(
|
||||
count: payload['followers']['totalCount'],
|
||||
count: data['followers']['totalCount'],
|
||||
text: 'Followers',
|
||||
screenBuilder: (context) => UsersScreen.followers(login),
|
||||
),
|
||||
EntryItem(
|
||||
count: payload['following']['totalCount'],
|
||||
count: data['following']['totalCount'],
|
||||
text: 'Following',
|
||||
screenBuilder: (context) => UsersScreen.following(login),
|
||||
),
|
||||
@ -224,38 +225,38 @@ class UserScreen extends StatelessWidget {
|
||||
TableView(
|
||||
hasIcon: true,
|
||||
items: [
|
||||
if (isNotNullOrEmpty(payload['company']))
|
||||
if (isNotNullOrEmpty(data['company']))
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.organization,
|
||||
text: TextContainsOrganization(payload['company'],
|
||||
text: TextContainsOrganization(data['company'],
|
||||
style: TextStyle(
|
||||
fontSize: 16, color: PrimerColors.gray900),
|
||||
overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
if (isNotNullOrEmpty(payload['location']))
|
||||
if (isNotNullOrEmpty(data['location']))
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.location,
|
||||
text: Text(payload['location']),
|
||||
text: Text(data['location']),
|
||||
onTap: () {
|
||||
launchUrl('https://www.google.com/maps/place/' +
|
||||
(payload['location'] as String)
|
||||
(data['location'] as String)
|
||||
.replaceAll(RegExp(r'\s+'), ''));
|
||||
},
|
||||
),
|
||||
if (isNotNullOrEmpty(payload['email']))
|
||||
if (isNotNullOrEmpty(data['email']))
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.mail,
|
||||
text: Text(payload['email']),
|
||||
text: Text(data['email']),
|
||||
onTap: () {
|
||||
launchUrl('mailto:' + payload['email']);
|
||||
launchUrl('mailto:' + data['email']);
|
||||
},
|
||||
),
|
||||
if (isNotNullOrEmpty(payload['websiteUrl']))
|
||||
if (isNotNullOrEmpty(data['websiteUrl']))
|
||||
TableViewItem(
|
||||
leftIconData: Octicons.link,
|
||||
text: Text(payload['websiteUrl']),
|
||||
text: Text(data['websiteUrl']),
|
||||
onTap: () {
|
||||
var url = payload['websiteUrl'] as String;
|
||||
var url = data['websiteUrl'] as String;
|
||||
if (!url.startsWith('http')) {
|
||||
url = 'http://$url';
|
||||
}
|
||||
@ -264,7 +265,7 @@ class UserScreen extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
..._buildRepos(payload),
|
||||
..._buildRepos(data),
|
||||
borderView1,
|
||||
],
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user