refactor: refresh scaffold

This commit is contained in:
Rongjian Zhang 2019-11-02 20:54:23 +08:00
parent 0c315c354c
commit 79febd275b
7 changed files with 99 additions and 112 deletions

View File

@ -4,21 +4,12 @@ import 'package:git_touch/scaffolds/common.dart';
import 'package:git_touch/scaffolds/utils.dart';
import 'package:primer/primer.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(RefreshStatefulScaffoldPayload<T> payload) bodyBuilder;
final Widget Function(T data, void Function(VoidCallback fn) setState)
bodyBuilder;
final Future<T> Function() fetchData;
final Widget Function(RefreshStatefulScaffoldPayload<T> payload)
final Widget Function(T data, void Function(VoidCallback fn) setState)
actionBuilder;
RefreshStatefulScaffold({
@ -39,13 +30,11 @@ class _RefreshStatefulScaffoldState<T>
T _data;
String _error = '';
RefreshStatefulScaffoldPayload get _payload =>
RefreshStatefulScaffoldPayload(_loading, _error, _data, _refresh);
@override
void initState() {
super.initState();
_refresh();
setState(() {});
}
Future<void> _refresh() async {
@ -69,7 +58,7 @@ class _RefreshStatefulScaffoldState<T>
Widget get _action {
if (widget.actionBuilder == null) return null;
return widget.actionBuilder(_payload);
return widget.actionBuilder(_data, setState);
}
@override
@ -80,7 +69,7 @@ class _RefreshStatefulScaffoldState<T>
body: RefreshWrapper(
onRefresh: _refresh,
body: ErrorLoadingWrapper(
bodyBuilder: () => widget.bodyBuilder(_payload),
bodyBuilder: () => widget.bodyBuilder(_data, setState),
error: _error,
loading: _data == null,
reload: _refresh,

View File

@ -30,10 +30,10 @@ class GitlabIssueScreen extends StatelessWidget {
]);
return items;
},
bodyBuilder: (payload) {
final data = payload.data[0];
final notes = payload.data[1] as List;
final emoji = payload.data[2];
bodyBuilder: (data, _) {
final issue = data[0];
final notes = data[1] as List;
final emoji = data[2];
return Column(
children: <Widget>[
@ -41,16 +41,16 @@ class GitlabIssueScreen extends StatelessWidget {
padding: CommonStyle.padding,
child: Column(
children: <Widget>[
Text(data['title']),
Text(issue['title']),
Row(
children: <Widget>[
Avatar.medium(url: data['author']['avatar_url']),
Avatar.medium(url: issue['author']['avatar_url']),
Expanded(
child: Text(data['description']),
child: Text(issue['description']),
),
],
),
Text(timeago.format(DateTime.parse(data['created_at'])))
Text(timeago.format(DateTime.parse(issue['created_at'])))
],
),
),

View File

@ -16,10 +16,10 @@ class GitlabTodosScreen extends StatelessWidget {
fetchData: () {
return Provider.of<AuthModel>(context).fetchGitlab('/todos');
},
bodyBuilder: (payload) {
bodyBuilder: (data, _) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: (payload.data as List).map((item) {
children: (data as List).map((item) {
return Link(
screenBuilder: (_) => GitlabIssueScreen(
item['target']['project_id'], item['target']['iid'],

View File

@ -29,16 +29,16 @@ class GitlabUserScreen extends StatelessWidget {
}
return [user, projects];
},
bodyBuilder: (payload) {
final data = payload.data[0];
final projects = payload.data[1] as List;
bodyBuilder: (data, _) {
final user = data[0];
final projects = data[1] as List;
return Column(
children: <Widget>[
UserItem(
login: data['username'],
avatarUrl: data['avatar_url'],
name: data['name'],
login: user['username'],
avatarUrl: user['avatar_url'],
name: user['name'],
),
BorderView(height: 10),
Column(

View File

@ -173,15 +173,15 @@ class ObjectScreen extends StatelessWidget {
return data['repository']['object'];
},
actionBuilder: (payload) {
actionBuilder: (data, _) {
switch (type) {
case 'blob':
return ActionEntry(
iconData: Octicons.settings,
onTap: () {
if (payload.data != null) {
Provider.of<ThemeModel>(context).pushRoute(context,
(_) => CodeThemeScreen(payload.data['text'], _language));
if (data != null) {
Provider.of<ThemeModel>(context).pushRoute(
context, (_) => CodeThemeScreen(data['text'], _language));
}
},
);
@ -189,12 +189,12 @@ class ObjectScreen extends StatelessWidget {
return null;
}
},
bodyBuilder: (payload) {
bodyBuilder: (data, _) {
switch (type) {
case 'tree':
return _buildTree(payload.data);
return _buildTree(data);
case 'blob':
return _buildBlob(context, payload.data);
return _buildBlob(context, data);
default:
return null;
}

View File

@ -124,8 +124,7 @@ class RepositoryScreen extends StatelessWidget {
queryRepo(context),
fetchReadme(context),
]),
actionBuilder: (payload) {
var data = payload.data;
actionBuilder: (data, setState) {
return ActionButton(
title: 'Repository Actions',
items: [
@ -142,7 +141,7 @@ class RepositoryScreen extends StatelessWidget {
.putWithCredentials('/user/starred/$owner/$name');
data[0]['viewerHasStarred'] = true;
}
payload.refresh();
setState(() {});
},
),
ActionItem(
@ -159,7 +158,7 @@ class RepositoryScreen extends StatelessWidget {
.putWithCredentials('/repos/$owner/$name/subscription');
data[0]['viewerSubscription'] = 'SUBSCRIBED';
}
payload.refresh();
setState(() {});
},
),
],
@ -170,35 +169,35 @@ class RepositoryScreen extends StatelessWidget {
],
);
},
bodyBuilder: (payload) {
var data = payload.data[0];
var readme = payload.data[1] as String;
bodyBuilder: (data, _) {
var repo = data[0];
var readme = data[1] as String;
final langWidth = MediaQuery.of(context).size.width -
CommonStyle.padding.left -
CommonStyle.padding.right -
(data['languages']['edges'] as List).length +
(repo['languages']['edges'] as List).length +
1;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
RepositoryItem(data, inRepoScreen: true),
RepositoryItem(repo, inRepoScreen: true),
CommonStyle.border,
Row(
children: <Widget>[
EntryItem(
count: data['watchers']['totalCount'],
count: repo['watchers']['totalCount'],
text: 'Watchers',
screenBuilder: (context) => UsersScreen.watchers(owner, name),
),
EntryItem(
count: data['stargazers']['totalCount'],
count: repo['stargazers']['totalCount'],
text: 'Stars',
screenBuilder: (context) => UsersScreen.stars(owner, name),
),
EntryItem(
count: data['forks']['totalCount'],
count: repo['forks']['totalCount'],
text: 'Forks',
screenBuilder: (context) =>
RepositoriesScreen.forks(owner, name),
@ -206,7 +205,7 @@ class RepositoryScreen extends StatelessWidget {
],
),
CommonStyle.verticalGap,
if ((data['languages']['edges'] as List).isNotEmpty)
if ((repo['languages']['edges'] as List).isNotEmpty)
Container(
color: Colors.white,
padding: CommonStyle.padding.copyWith(top: 8, bottom: 8),
@ -217,12 +216,12 @@ class RepositoryScreen extends StatelessWidget {
child: Row(
children: join(
SizedBox(width: 1),
(data['languages']['edges'] as List)
(repo['languages']['edges'] as List)
.map((lang) => Container(
color: convertColor(lang['node']['color']),
width: langWidth *
lang['size'] /
data['languages']['totalSize']))
repo['languages']['totalSize']))
.toList())),
),
),
@ -230,24 +229,24 @@ class RepositoryScreen extends StatelessWidget {
TableView(
hasIcon: true,
items: [
if (data[branchInfoKey] != null)
if (repo[branchInfoKey] != null)
TableViewItem(
leftIconData: Octicons.code,
text: Text('Code'),
rightWidget:
Text(filesize((data['diskUsage'] as int) * 1000)),
Text(filesize((repo['diskUsage'] as int) * 1000)),
screenBuilder: (_) => ObjectScreen(
owner: owner,
name: name,
branch: data[branchInfoKey]['name'],
branch: repo[branchInfoKey]['name'],
),
),
if (data['hasIssuesEnabled'] as bool)
if (repo['hasIssuesEnabled'] as bool)
TableViewItem(
leftIconData: Octicons.issue_opened,
text: Text('Issues'),
rightWidget:
Text(numberFormat.format(data['issues']['totalCount'])),
Text(numberFormat.format(repo['issues']['totalCount'])),
screenBuilder: (_) =>
IssuesScreen(owner: owner, name: name),
),
@ -255,7 +254,7 @@ class RepositoryScreen extends StatelessWidget {
leftIconData: Octicons.git_pull_request,
text: Text('Pull requests'),
rightWidget: Text(
numberFormat.format(data['pullRequests']['totalCount'])),
numberFormat.format(repo['pullRequests']['totalCount'])),
screenBuilder: (_) => IssuesScreen(
owner: owner, name: name, isPullRequest: true),
),
@ -263,8 +262,8 @@ class RepositoryScreen extends StatelessWidget {
leftIconData: Octicons.project,
text: Text('Projects'),
rightWidget:
Text(numberFormat.format(data['projects']['totalCount'])),
url: 'https://github.com' + data['projectsResourcePath'],
Text(numberFormat.format(repo['projects']['totalCount'])),
url: 'https://github.com' + repo['projectsResourcePath'],
),
],
),
@ -272,30 +271,30 @@ class RepositoryScreen extends StatelessWidget {
TableView(
hasIcon: true,
items: [
if (data[branchInfoKey] != null) ...[
if (repo[branchInfoKey] != null) ...[
TableViewItem(
leftIconData: Octicons.history,
text: Text('Commits'),
rightWidget: Text(numberFormat.format(data[branchInfoKey]
rightWidget: Text(numberFormat.format(repo[branchInfoKey]
['target']['history']['totalCount'])),
screenBuilder: (_) =>
CommitsScreen(owner, name, branch: branch),
),
if (data['refs'] != null)
if (repo['refs'] != null)
TableViewItem(
leftIconData: Octicons.git_branch,
text: Text('Branches'),
rightWidget: Text(data[branchInfoKey]['name'] +
rightWidget: Text(repo[branchInfoKey]['name'] +
'' +
numberFormat.format(data['refs']['totalCount'])),
numberFormat.format(repo['refs']['totalCount'])),
onTap: () async {
var refs = data['refs']['nodes'] as List;
var refs = repo['refs']['nodes'] as List;
if (refs.length < 2) return;
await Provider.of<ThemeModel>(context).showPicker(
context,
PickerGroupItem(
value: data[branchInfoKey]['name'],
value: repo[branchInfoKey]['name'],
items: refs
.map((b) => PickerItem(b['name'] as String,
text: (b['name'] as String)))
@ -319,16 +318,16 @@ class RepositoryScreen extends StatelessWidget {
leftIconData: Octicons.tag,
text: Text('Releases'),
rightWidget:
Text((data['releases']['totalCount'] as int).toString()),
url: data['url'] + '/releases',
Text((repo['releases']['totalCount'] as int).toString()),
url: repo['url'] + '/releases',
),
TableViewItem(
leftIconData: Octicons.law,
text: Text('License'),
rightWidget: Text(data['licenseInfo'] == null
rightWidget: Text(repo['licenseInfo'] == null
? 'Unknown'
: (data['licenseInfo']['spdxId'] ??
data['licenseInfo']['name'])),
: (repo['licenseInfo']['spdxId'] ??
repo['licenseInfo']['name'])),
),
],
),

View File

@ -123,8 +123,7 @@ class UserScreen extends StatelessWidget {
]);
},
title: AppBarTitle('User'), // TODO:
actionBuilder: (payload) {
var data = payload.data;
actionBuilder: (data, _) {
if (data == null)
return ActionButton(
title: "Actions",
@ -175,8 +174,8 @@ class UserScreen extends StatelessWidget {
title: 'Organization Actions',
items: [
if (data != null) ...[
ActionItem.share(payload.data[0]['url']),
ActionItem.launch(payload.data[0]['url']),
ActionItem.share(data[0]['url']),
ActionItem.launch(data[0]['url']),
],
],
);
@ -184,59 +183,59 @@ class UserScreen extends StatelessWidget {
return null;
}
},
bodyBuilder: (payload) {
var data = payload.data[0];
var contributions = payload.data[1];
final isOrganization = data['__typename'] == 'Organization';
bodyBuilder: (data, _) {
var user = data[0];
var contributions = data[1];
final isOrganization = user['__typename'] == 'Organization';
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
UserItem(
login: data['login'],
name: data['name'],
avatarUrl: data['avatarUrl'],
bio: isOrganization ? data['description'] : data['bio'],
login: user['login'],
name: user['name'],
avatarUrl: user['avatarUrl'],
bio: isOrganization ? user['description'] : user['bio'],
inUserScreen: true,
),
CommonStyle.border,
Row(children: [
if (isOrganization) ...[
EntryItem(
count: data['pinnableItems']['totalCount'],
count: user['pinnableItems']['totalCount'],
text: 'Repositories',
screenBuilder: (context) =>
RepositoriesScreen.ofOrganization(data['login']),
RepositoriesScreen.ofOrganization(user['login']),
),
EntryItem(
count: data['membersWithRole']['totalCount'],
count: user['membersWithRole']['totalCount'],
text: 'Members',
screenBuilder: (context) =>
UsersScreen.members(data['login']),
UsersScreen.members(user['login']),
),
] else ...[
EntryItem(
count: data['repositories']['totalCount'],
count: user['repositories']['totalCount'],
text: 'Repositories',
screenBuilder: (context) => RepositoriesScreen(data['login']),
screenBuilder: (context) => RepositoriesScreen(user['login']),
),
EntryItem(
count: data['starredRepositories']['totalCount'],
count: user['starredRepositories']['totalCount'],
text: 'Stars',
screenBuilder: (context) =>
RepositoriesScreen.stars(data['login']),
RepositoriesScreen.stars(user['login']),
),
EntryItem(
count: data['followers']['totalCount'],
count: user['followers']['totalCount'],
text: 'Followers',
screenBuilder: (context) =>
UsersScreen.followers(data['login']),
UsersScreen.followers(user['login']),
),
EntryItem(
count: data['following']['totalCount'],
count: user['following']['totalCount'],
text: 'Following',
screenBuilder: (context) =>
UsersScreen.following(data['login']),
UsersScreen.following(user['login']),
),
]
]),
@ -248,38 +247,38 @@ class UserScreen extends StatelessWidget {
TableView(
hasIcon: true,
items: [
if (!isOrganization && isNotNullOrEmpty(data['company']))
if (!isOrganization && isNotNullOrEmpty(user['company']))
TableViewItem(
leftIconData: Octicons.organization,
text: TextContainsOrganization(data['company'],
text: TextContainsOrganization(user['company'],
style: TextStyle(
fontSize: 16, color: PrimerColors.gray900),
overflow: TextOverflow.ellipsis),
),
if (isNotNullOrEmpty(data['location']))
if (isNotNullOrEmpty(user['location']))
TableViewItem(
leftIconData: Octicons.location,
text: Text(data['location']),
text: Text(user['location']),
onTap: () {
launchUrl('https://www.google.com/maps/place/' +
(data['location'] as String)
(user['location'] as String)
.replaceAll(RegExp(r'\s+'), ''));
},
),
if (isNotNullOrEmpty(data['email']))
if (isNotNullOrEmpty(user['email']))
TableViewItem(
leftIconData: Octicons.mail,
text: Text(data['email']),
text: Text(user['email']),
onTap: () {
launchUrl('mailto:' + data['email']);
launchUrl('mailto:' + user['email']);
},
),
if (isNotNullOrEmpty(data['websiteUrl']))
if (isNotNullOrEmpty(user['websiteUrl']))
TableViewItem(
leftIconData: Octicons.link,
text: Text(data['websiteUrl']),
text: Text(user['websiteUrl']),
onTap: () {
var url = data['websiteUrl'] as String;
var url = user['websiteUrl'] as String;
if (!url.startsWith('http')) {
url = 'http://$url';
}
@ -289,8 +288,8 @@ class UserScreen extends StatelessWidget {
],
),
...buildPinnedItems(
data['pinnedItems']['nodes'],
data[isOrganization ? 'pinnableItems' : 'repositories']
user['pinnedItems']['nodes'],
user[isOrganization ? 'pinnableItems' : 'repositories']
['nodes']),
CommonStyle.verticalGap,
],