1
0
mirror of https://github.com/git-touch/git-touch synced 2025-02-21 14:01:02 +01:00

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: [ selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'id'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode( FieldNode(
name: NameNode(value: 'login'), name: NameNode(value: 'login'),
alias: null, alias: null,
@ -6358,13 +6364,13 @@ class GhUserQuery extends GraphQLQuery<GhUser, GhUserArguments> {
directives: [], directives: [],
selectionSet: null), selectionSet: null),
FieldNode( FieldNode(
name: NameNode(value: 'websiteUrl'), name: NameNode(value: 'createdAt'),
alias: null, alias: null,
arguments: [], arguments: [],
directives: [], directives: [],
selectionSet: null), selectionSet: null),
FieldNode( FieldNode(
name: NameNode(value: 'createdAt'), name: NameNode(value: 'websiteUrl'),
alias: null, alias: null,
arguments: [], arguments: [],
directives: [], directives: [],
@ -6408,19 +6414,6 @@ class GhUserQuery extends GraphQLQuery<GhUser, GhUserArguments> {
directives: [], directives: [],
selectionSet: null) 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( FieldNode(
name: NameNode(value: 'contributionsCollection'), name: NameNode(value: 'contributionsCollection'),
alias: null, 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) { viewer @include(if: $isViewer) {
id
login login
avatarUrl avatarUrl
url url
@ -159,8 +160,8 @@ query GhUser($login: String!, $isViewer: Boolean!) {
company company
location location
email email
websiteUrl
createdAt createdAt
websiteUrl
starredRepositories { starredRepositories {
totalCount totalCount
} }
@ -170,9 +171,6 @@ query GhUser($login: String!, $isViewer: Boolean!) {
following { following {
totalCount totalCount
} }
repositories {
totalCount
}
contributionsCollection { contributionsCollection {
contributionCalendar { contributionCalendar {
weeks { 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 Future<T> Function() fetchData;
final Widget Function(T data, void Function(VoidCallback fn) setState) final Widget Function(T data, void Function(VoidCallback fn) setState)
actionBuilder; actionBuilder;
final Widget action;
final canRefresh; final canRefresh;
RefreshStatefulScaffold({ RefreshStatefulScaffold({
@ -17,8 +18,9 @@ class RefreshStatefulScaffold<T> extends StatefulWidget {
@required this.bodyBuilder, @required this.bodyBuilder,
@required this.fetchData, @required this.fetchData,
this.actionBuilder, this.actionBuilder,
this.action,
this.canRefresh = true, this.canRefresh = true,
}); }) : assert(actionBuilder == null || action == null);
@override @override
_RefreshStatefulScaffoldState<T> createState() => _RefreshStatefulScaffoldState<T> createState() =>
@ -45,9 +47,9 @@ class _RefreshStatefulScaffoldState<T>
_loading = true; _loading = true;
}); });
_data = await widget.fetchData(); _data = await widget.fetchData();
} catch (err) { // } catch (err) {
_error = err.toString(); // _error = err.toString();
throw err; // throw err;
} finally { } finally {
if (mounted) { if (mounted) {
setState(() { setState(() {
@ -58,6 +60,7 @@ class _RefreshStatefulScaffoldState<T>
} }
Widget get _action { Widget get _action {
if (widget.action != null) return widget.action;
if (widget.actionBuilder == null || _data == null) return null; if (widget.actionBuilder == null || _data == null) return null;
return widget.actionBuilder(_data, setState); return widget.actionBuilder(_data, setState);
} }

View File

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

View File

@ -1,11 +1,15 @@
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.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/models/theme.dart';
import 'package:git_touch/scaffolds/single.dart'; import 'package:git_touch/scaffolds/single.dart';
import 'package:git_touch/utils/utils.dart'; import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/action_button.dart'; import 'package:git_touch/widgets/action_button.dart';
import 'package:git_touch/widgets/app_bar_title.dart'; import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/table_view.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:provider/provider.dart';
import 'package:tuple/tuple.dart'; import 'package:tuple/tuple.dart';
@ -14,7 +18,24 @@ final settingsRouter = RouterScreen(
(context, parameters) => SettingsScreen(), (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) { Widget _buildRightWidget(BuildContext context, bool checked) {
final theme = Provider.of<ThemeModel>(context); final theme = Provider.of<ThemeModel>(context);
if (!checked) return null; if (!checked) return null;
@ -24,44 +45,22 @@ class SettingsScreen extends StatelessWidget {
@override @override
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 code = Provider.of<CodeModel>(context);
return SingleScaffold( return SingleScaffold(
title: AppBarTitle('Settings'), title: AppBarTitle('Settings'),
body: Column( body: Column(
children: <Widget>[ children: <Widget>[
CommonStyle.verticalGap, CommonStyle.verticalGap,
TableView( TableView(headerText: 'accounts', items: [
headerText: 'ACCOUNTS',
items: [
TableViewItem(
text: Text('Switch to another account'),
url: '/login',
),
],
),
CommonStyle.verticalGap,
TableView(headerText: 'THEME', items: [
TableViewItem( TableViewItem(
text: Text('Scaffold'), text: Text('Switch Accounts'),
rightWidget: Text(theme.theme == AppThemeType.cupertino url: '/login',
? 'Cupertino' rightWidget: Text(auth.activeAccount.login),
: '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);
}
},
)
]);
},
), ),
]),
CommonStyle.verticalGap,
TableView(headerText: 'theme', items: [
TableViewItem( TableViewItem(
text: Text('Brightness'), text: Text('Brightness'),
rightWidget: Text(theme.brighnessValue == AppBrightnessType.light 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( TableViewItem(
text: Text('Code Theme'), text: Text('Code Theme'),
url: '/choose-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/scaffolds/refresh_stateful.dart';
import 'package:git_touch/screens/users.dart'; import 'package:git_touch/screens/users.dart';
import 'package:git_touch/utils/utils.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/widgets/app_bar_title.dart';
import 'package:git_touch/screens/repositories.dart'; import 'package:git_touch/screens/repositories.dart';
import 'package:git_touch/widgets/avatar.dart'; import 'package:git_touch/widgets/avatar.dart';
@ -292,28 +293,28 @@ class UserScreen extends StatelessWidget {
], ],
), ),
CommonStyle.verticalGap, CommonStyle.verticalGap,
if (isViewer) // if (isViewer)
TableView( // TableView(
hasIcon: true, // hasIcon: true,
items: [ // items: [
TableViewItem( // TableViewItem(
leftIconData: Icons.settings, // leftIconData: Icons.settings,
text: Text('Settings'), // text: Text('Settings'),
url: '/settings', // url: '/settings',
), // ),
TableViewItem( // TableViewItem(
leftIconData: Icons.info_outline, // leftIconData: Icons.info_outline,
text: Text('About'), // text: Text('About'),
url: '/about', // url: '/about',
), // ),
], // ],
) // )
else // else
..._buildPinnedItems( ..._buildPinnedItems(
p.pinnedItems.nodes p.pinnedItems.nodes
.where((n) => n is GhUserRepository) .where((n) => n is GhUserRepository)
.cast<GhUserRepository>(), .cast<GhUserRepository>(),
p.repositories.nodes), p.repositories.nodes),
CommonStyle.verticalGap, CommonStyle.verticalGap,
], ],
); );
@ -389,6 +390,7 @@ class UserScreen extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final auth = Provider.of<AuthModel>(context); final auth = Provider.of<AuthModel>(context);
final theme = Provider.of<ThemeModel>(context);
return RefreshStatefulScaffold<GhUserRepositoryOwner>( return RefreshStatefulScaffold<GhUserRepositoryOwner>(
fetchData: () async { fetchData: () async {
final data = await auth.gqlClient.execute(GhUserQuery( final data = await auth.gqlClient.execute(GhUserQuery(
@ -396,26 +398,36 @@ class UserScreen extends StatelessWidget {
return isViewer ? data.data.viewer : data.data.repositoryOwner; return isViewer ? data.data.viewer : data.data.repositoryOwner;
}, },
title: AppBarTitle(isViewer ? 'Me' : login), title: AppBarTitle(isViewer ? 'Me' : login),
actionBuilder: (payload, setState) { action: isViewer
switch (payload.resolveType) { ? ActionEntry(
case 'User': iconData: Icons.settings,
final user = payload as GhUserUser; onTap: () {
return ActionButton( theme.push(context, '/settings');
title: 'User Actions', },
items: [...ActionItem.getUrlActions(user.url)], )
); : null,
case 'Organization': actionBuilder: isViewer
final organization = payload as GhUserOrganization; ? null
return ActionButton( : (payload, setState) {
title: 'Organization Actions', switch (payload.resolveType) {
items: [ case 'User':
...ActionItem.getUrlActions(organization.url), final user = payload as GhUserUser;
], return ActionButton(
); title: 'User Actions',
default: items: [...ActionItem.getUrlActions(user.url)],
return null; );
} case 'Organization':
}, final organization = payload as GhUserOrganization;
return ActionButton(
title: 'Organization Actions',
items: [
...ActionItem.getUrlActions(organization.url),
],
);
default:
return null;
}
},
bodyBuilder: (payload, setState) { bodyBuilder: (payload, setState) {
if (isViewer) { if (isViewer) {
return _buildUser(context, payload as GhUserUser, setState); return _buildUser(context, payload as GhUserUser, setState);