improvement: settings screen

This commit is contained in:
Rongjian Zhang 2020-01-27 14:43:10 +08:00
parent 4ea5a03431
commit 612a7a417a
6 changed files with 485 additions and 102 deletions

View File

@ -6309,6 +6309,12 @@ class GhUserQuery extends GraphQLQuery<GhUser, GhUserArguments> {
])
],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'id'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'login'),
alias: null,
@ -6358,13 +6364,13 @@ class GhUserQuery extends GraphQLQuery<GhUser, GhUserArguments> {
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'websiteUrl'),
name: NameNode(value: 'createdAt'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'createdAt'),
name: NameNode(value: 'websiteUrl'),
alias: null,
arguments: [],
directives: [],
@ -6408,19 +6414,6 @@ class GhUserQuery extends GraphQLQuery<GhUser, GhUserArguments> {
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'repositories'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'contributionsCollection'),
alias: null,
@ -6455,7 +6448,276 @@ class GhUserQuery extends GraphQLQuery<GhUser, GhUserArguments> {
]))
]))
]))
]))
])),
FieldNode(
name: NameNode(value: 'repositories'),
alias: null,
arguments: [
ArgumentNode(
name: NameNode(value: 'first'),
value: IntValueNode(value: '6')),
ArgumentNode(
name: NameNode(value: 'ownerAffiliations'),
value: EnumValueNode(name: NameNode(value: 'OWNER'))),
ArgumentNode(
name: NameNode(value: 'orderBy'),
value: ObjectValueNode(fields: [
ObjectFieldNode(
name: NameNode(value: 'field'),
value: EnumValueNode(
name: NameNode(value: 'STARGAZERS'))),
ObjectFieldNode(
name: NameNode(value: 'direction'),
value: EnumValueNode(
name: NameNode(value: 'DESC')))
]))
],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'nodes'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'owner'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'login'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'avatarUrl'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'name'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'description'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'isPrivate'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'isFork'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'stargazers'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'forks'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'primaryLanguage'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'color'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'name'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
]))
]))
])),
FieldNode(
name: NameNode(value: 'pinnedItems'),
alias: null,
arguments: [
ArgumentNode(
name: NameNode(value: 'first'),
value: IntValueNode(value: '6'))
],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'nodes'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: '__typename'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
InlineFragmentNode(
typeCondition: TypeConditionNode(
on: NamedTypeNode(
name: NameNode(value: 'Repository'),
isNonNull: false)),
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'owner'),
alias: null,
arguments: [],
directives: [],
selectionSet:
SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'login'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'avatarUrl'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'name'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'description'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'isPrivate'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'isFork'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'stargazers'),
alias: null,
arguments: [],
directives: [],
selectionSet:
SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'forks'),
alias: null,
arguments: [],
directives: [],
selectionSet:
SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'primaryLanguage'),
alias: null,
arguments: [],
directives: [],
selectionSet:
SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'color'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'name'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
]))
]))
]))
])),
FieldNode(
name: NameNode(value: 'viewerCanFollow'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'viewerIsFollowing'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
]))
]))
]);

View File

@ -151,6 +151,7 @@ query GhUser($login: String!, $isViewer: Boolean!) {
}
}
viewer @include(if: $isViewer) {
id
login
avatarUrl
url
@ -159,8 +160,8 @@ query GhUser($login: String!, $isViewer: Boolean!) {
company
location
email
websiteUrl
createdAt
websiteUrl
starredRepositories {
totalCount
}
@ -170,9 +171,6 @@ query GhUser($login: String!, $isViewer: Boolean!) {
following {
totalCount
}
repositories {
totalCount
}
contributionsCollection {
contributionCalendar {
weeks {
@ -182,5 +180,60 @@ query GhUser($login: String!, $isViewer: Boolean!) {
}
}
}
repositories(
first: 6
ownerAffiliations: OWNER
orderBy: { field: STARGAZERS, direction: DESC }
) {
totalCount
nodes {
owner {
login
avatarUrl
}
name
description
isPrivate
isFork
stargazers {
totalCount
}
forks {
totalCount
}
primaryLanguage {
color
name
}
}
}
pinnedItems(first: 6) {
totalCount # TODO: Add this for correct generated code
nodes {
__typename
... on Repository {
owner {
login
avatarUrl
}
name
description
isPrivate
isFork
stargazers {
totalCount
}
forks {
totalCount
}
primaryLanguage {
color
name
}
}
}
}
viewerCanFollow
viewerIsFollowing
}
}

View File

@ -10,6 +10,7 @@ class RefreshStatefulScaffold<T> extends StatefulWidget {
final Future<T> Function() fetchData;
final Widget Function(T data, void Function(VoidCallback fn) setState)
actionBuilder;
final Widget action;
final canRefresh;
RefreshStatefulScaffold({
@ -17,8 +18,9 @@ class RefreshStatefulScaffold<T> extends StatefulWidget {
@required this.bodyBuilder,
@required this.fetchData,
this.actionBuilder,
this.action,
this.canRefresh = true,
});
}) : assert(actionBuilder == null || action == null);
@override
_RefreshStatefulScaffoldState<T> createState() =>
@ -45,9 +47,9 @@ class _RefreshStatefulScaffoldState<T>
_loading = true;
});
_data = await widget.fetchData();
} catch (err) {
_error = err.toString();
throw err;
// } catch (err) {
// _error = err.toString();
// throw err;
} finally {
if (mounted) {
setState(() {
@ -58,6 +60,7 @@ class _RefreshStatefulScaffoldState<T>
}
Widget get _action {
if (widget.action != null) return widget.action;
if (widget.actionBuilder == null || _data == null) return null;
return widget.actionBuilder(_data, setState);
}

View File

@ -169,10 +169,9 @@ class ObjectScreen extends StatelessWidget {
child: HighlightView(
text,
language: _language,
theme: themeMap[
theme.brightness == Brightness.dark
? codeProvider.themeDark
: codeProvider.theme],
theme: themeMap[theme.brightness == Brightness.dark
? codeProvider.themeDark
: codeProvider.theme],
padding: CommonStyle.padding,
textStyle: TextStyle(
fontSize: codeProvider.fontSize.toDouble(),

View File

@ -1,11 +1,15 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/code.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/single.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:launch_review/launch_review.dart';
import 'package:package_info/package_info.dart';
import 'package:provider/provider.dart';
import 'package:tuple/tuple.dart';
@ -14,7 +18,24 @@ final settingsRouter = RouterScreen(
(context, parameters) => SettingsScreen(),
);
class SettingsScreen extends StatelessWidget {
class SettingsScreen extends StatefulWidget {
@override
_SettingsScreenState createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
var _version = '';
@override
void initState() {
super.initState();
PackageInfo.fromPlatform().then((info) {
setState(() {
_version = info.version;
});
});
}
Widget _buildRightWidget(BuildContext context, bool checked) {
final theme = Provider.of<ThemeModel>(context);
if (!checked) return null;
@ -24,44 +45,22 @@ class SettingsScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = Provider.of<ThemeModel>(context);
final auth = Provider.of<AuthModel>(context);
final code = Provider.of<CodeModel>(context);
return SingleScaffold(
title: AppBarTitle('Settings'),
body: Column(
children: <Widget>[
CommonStyle.verticalGap,
TableView(
headerText: 'ACCOUNTS',
items: [
TableViewItem(
text: Text('Switch to another account'),
url: '/login',
),
],
),
CommonStyle.verticalGap,
TableView(headerText: 'THEME', items: [
TableView(headerText: 'accounts', items: [
TableViewItem(
text: Text('Scaffold'),
rightWidget: Text(theme.theme == AppThemeType.cupertino
? 'Cupertino'
: 'Material'),
onTap: () {
theme.showActions(context, [
for (var t in [
Tuple2('Material', AppThemeType.material),
Tuple2('Cupertino', AppThemeType.cupertino),
])
ActionItem(
text: t.item1,
onTap: (_) {
if (theme.theme != t.item2) {
theme.setTheme(t.item2);
}
},
)
]);
},
text: Text('Switch Accounts'),
url: '/login',
rightWidget: Text(auth.activeAccount.login),
),
]),
CommonStyle.verticalGap,
TableView(headerText: 'theme', items: [
TableViewItem(
text: Text('Brightness'),
rightWidget: Text(theme.brighnessValue == AppBrightnessType.light
@ -86,11 +85,66 @@ class SettingsScreen extends StatelessWidget {
]);
},
),
TableViewItem(
text: Text('Scaffold Theme'),
rightWidget: Text(theme.theme == AppThemeType.cupertino
? 'Cupertino'
: 'Material'),
onTap: () {
theme.showActions(context, [
for (var t in [
Tuple2('Material', AppThemeType.material),
Tuple2('Cupertino', AppThemeType.cupertino),
])
ActionItem(
text: t.item1,
onTap: (_) {
if (theme.theme != t.item2) {
theme.setTheme(t.item2);
}
},
)
]);
},
),
TableViewItem(
text: Text('Code Theme'),
url: '/choose-code-theme',
rightWidget: Text('${code.fontFamily}, ${code.fontSize}pt'),
),
]),
CommonStyle.verticalGap,
TableView(headerText: 'feedback', items: [
TableViewItem(
text: Text('Submit an issue'),
rightWidget: Text('pd4d10/git-touch'),
url: '/pd4d10/git-touch/issues/new',
),
TableViewItem(
text: Text('Rate This App'),
onTap: () {
LaunchReview.launch(
androidAppId: 'io.github.pd4d10.gittouch',
iOSAppId: '1452042346',
);
},
),
TableViewItem(
text: Text('Email'),
rightWidget: Text('pd4d10@gmail.com'),
hideRightChevron: true,
url: 'mailto:pd4d10@gmail.com',
),
]),
CommonStyle.verticalGap,
TableView(headerText: 'about', items: [
TableViewItem(text: Text('Version'), rightWidget: Text(_version)),
TableViewItem(
text: Text('Source Code'),
rightWidget: Text('pd4d10/git-touch'),
url: '/pd4d10/git-touch',
),
])
],
),
);

View File

@ -5,6 +5,7 @@ import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/screens/users.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_entry.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/screens/repositories.dart';
import 'package:git_touch/widgets/avatar.dart';
@ -292,28 +293,28 @@ class UserScreen extends StatelessWidget {
],
),
CommonStyle.verticalGap,
if (isViewer)
TableView(
hasIcon: true,
items: [
TableViewItem(
leftIconData: Icons.settings,
text: Text('Settings'),
url: '/settings',
),
TableViewItem(
leftIconData: Icons.info_outline,
text: Text('About'),
url: '/about',
),
],
)
else
..._buildPinnedItems(
p.pinnedItems.nodes
.where((n) => n is GhUserRepository)
.cast<GhUserRepository>(),
p.repositories.nodes),
// if (isViewer)
// TableView(
// hasIcon: true,
// items: [
// TableViewItem(
// leftIconData: Icons.settings,
// text: Text('Settings'),
// url: '/settings',
// ),
// TableViewItem(
// leftIconData: Icons.info_outline,
// text: Text('About'),
// url: '/about',
// ),
// ],
// )
// else
..._buildPinnedItems(
p.pinnedItems.nodes
.where((n) => n is GhUserRepository)
.cast<GhUserRepository>(),
p.repositories.nodes),
CommonStyle.verticalGap,
],
);
@ -389,6 +390,7 @@ class UserScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final auth = Provider.of<AuthModel>(context);
final theme = Provider.of<ThemeModel>(context);
return RefreshStatefulScaffold<GhUserRepositoryOwner>(
fetchData: () async {
final data = await auth.gqlClient.execute(GhUserQuery(
@ -396,26 +398,36 @@ class UserScreen extends StatelessWidget {
return isViewer ? data.data.viewer : data.data.repositoryOwner;
},
title: AppBarTitle(isViewer ? 'Me' : login),
actionBuilder: (payload, setState) {
switch (payload.resolveType) {
case 'User':
final user = payload as GhUserUser;
return ActionButton(
title: 'User Actions',
items: [...ActionItem.getUrlActions(user.url)],
);
case 'Organization':
final organization = payload as GhUserOrganization;
return ActionButton(
title: 'Organization Actions',
items: [
...ActionItem.getUrlActions(organization.url),
],
);
default:
return null;
}
},
action: isViewer
? ActionEntry(
iconData: Icons.settings,
onTap: () {
theme.push(context, '/settings');
},
)
: null,
actionBuilder: isViewer
? null
: (payload, setState) {
switch (payload.resolveType) {
case 'User':
final user = payload as GhUserUser;
return ActionButton(
title: 'User Actions',
items: [...ActionItem.getUrlActions(user.url)],
);
case 'Organization':
final organization = payload as GhUserOrganization;
return ActionButton(
title: 'Organization Actions',
items: [
...ActionItem.getUrlActions(organization.url),
],
);
default:
return null;
}
},
bodyBuilder: (payload, setState) {
if (isViewer) {
return _buildUser(context, payload as GhUserUser, setState);