1
0
mirror of https://github.com/git-touch/git-touch synced 2025-03-07 20:57:44 +01:00

refactor: me screen

This commit is contained in:
Rongjian Zhang 2020-01-01 16:35:50 +08:00
parent 36e772c85f
commit 7496129973
6 changed files with 262 additions and 185 deletions

View File

@ -15,8 +15,10 @@ class GithubUser with EquatableMixin {
GithubUserRepositoryOwner repositoryOwner;
GithubUserUser viewer;
@override
List<Object> get props => [repositoryOwner];
List<Object> get props => [repositoryOwner, viewer];
Map<String, dynamic> toJson() => _$GithubUserToJson(this);
}
@ -694,15 +696,17 @@ class GithubUserMemberStatusable with EquatableMixin {
@JsonSerializable(explicitToJson: true)
class GithubUserArguments extends JsonSerializable with EquatableMixin {
GithubUserArguments({this.login});
GithubUserArguments({this.login, this.isViewer});
factory GithubUserArguments.fromJson(Map<String, dynamic> json) =>
_$GithubUserArgumentsFromJson(json);
final String login;
final bool isViewer;
@override
List<Object> get props => [login];
List<Object> get props => [login, isViewer];
Map<String, dynamic> toJson() => _$GithubUserArgumentsToJson(this);
}
@ -720,6 +724,12 @@ class GithubUserQuery extends GraphQLQuery<GithubUser, GithubUserArguments> {
type: NamedTypeNode(
name: NameNode(value: 'String'), isNonNull: true),
defaultValue: DefaultValueNode(value: null),
directives: []),
VariableDefinitionNode(
variable: VariableNode(name: NameNode(value: 'isViewer')),
type: NamedTypeNode(
name: NameNode(value: 'Boolean'), isNonNull: true),
defaultValue: DefaultValueNode(value: null),
directives: [])
],
directives: [],
@ -732,7 +742,13 @@ class GithubUserQuery extends GraphQLQuery<GithubUser, GithubUserArguments> {
name: NameNode(value: 'login'),
value: VariableNode(name: NameNode(value: 'login')))
],
directives: [],
directives: [
DirectiveNode(name: NameNode(value: 'skip'), arguments: [
ArgumentNode(
name: NameNode(value: 'if'),
value: VariableNode(name: NameNode(value: 'isViewer')))
])
],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: '__typename'),
@ -1502,6 +1518,166 @@ class GithubUserQuery extends GraphQLQuery<GithubUser, GithubUserArguments> {
selectionSet: null)
]))
]))
])),
FieldNode(
name: NameNode(value: 'viewer'),
alias: null,
arguments: [],
directives: [
DirectiveNode(name: NameNode(value: 'include'), arguments: [
ArgumentNode(
name: NameNode(value: 'if'),
value: VariableNode(name: NameNode(value: 'isViewer')))
])
],
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: 'url'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'name'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'bio'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'company'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'location'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'email'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'websiteUrl'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'createdAt'),
alias: null,
arguments: [],
directives: [],
selectionSet: null),
FieldNode(
name: NameNode(value: 'starredRepositories'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'followers'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
])),
FieldNode(
name: NameNode(value: 'following'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'totalCount'),
alias: null,
arguments: [],
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,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'contributionCalendar'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'weeks'),
alias: null,
arguments: [],
directives: [],
selectionSet: SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'contributionDays'),
alias: null,
arguments: [],
directives: [],
selectionSet:
SelectionSetNode(selections: [
FieldNode(
name: NameNode(value: 'color'),
alias: null,
arguments: [],
directives: [],
selectionSet: null)
]))
]))
]))
]))
]))
]))
]);

View File

@ -11,12 +11,16 @@ GithubUser _$GithubUserFromJson(Map<String, dynamic> json) {
..repositoryOwner = json['repositoryOwner'] == null
? null
: GithubUserRepositoryOwner.fromJson(
json['repositoryOwner'] as Map<String, dynamic>);
json['repositoryOwner'] as Map<String, dynamic>)
..viewer = json['viewer'] == null
? null
: GithubUserUser.fromJson(json['viewer'] as Map<String, dynamic>);
}
Map<String, dynamic> _$GithubUserToJson(GithubUser instance) =>
<String, dynamic>{
'repositoryOwner': instance.repositoryOwner?.toJson(),
'viewer': instance.viewer?.toJson(),
};
GithubUserRepositoryOwner _$GithubUserRepositoryOwnerFromJson(
@ -546,6 +550,7 @@ Map<String, dynamic> _$GithubUserMemberStatusableToJson(
GithubUserArguments _$GithubUserArgumentsFromJson(Map<String, dynamic> json) {
return GithubUserArguments(
login: json['login'] as String,
isViewer: json['isViewer'] as bool,
);
}
@ -553,4 +558,5 @@ Map<String, dynamic> _$GithubUserArgumentsToJson(
GithubUserArguments instance) =>
<String, dynamic>{
'login': instance.login,
'isViewer': instance.isViewer,
};

View File

@ -1,5 +1,5 @@
query($login: String!) {
repositoryOwner(login: $login) {
query($login: String!, $isViewer: Boolean!) {
repositoryOwner(login: $login) @skip(if: $isViewer) {
__typename
login
avatarUrl
@ -149,4 +149,37 @@ query($login: String!) {
}
}
}
viewer @include(if: $isViewer) {
login
avatarUrl
url
name
bio
company
location
email
websiteUrl
createdAt
starredRepositories {
totalCount
}
followers {
totalCount
}
following {
totalCount
}
repositories {
totalCount
}
contributionsCollection {
contributionCalendar {
weeks {
contributionDays {
color
}
}
}
}
}
}

View File

@ -13,7 +13,6 @@ import 'package:git_touch/screens/gitlab_tree.dart';
import 'package:git_touch/screens/gitlab_user.dart';
import 'package:git_touch/screens/issue_form.dart';
import 'package:git_touch/screens/issues.dart';
import 'package:git_touch/screens/me.dart';
import 'package:git_touch/screens/notification.dart';
import 'package:git_touch/screens/object.dart';
import 'package:git_touch/screens/repository.dart';
@ -129,7 +128,7 @@ class _HomeState extends State<Home> {
case 3:
return SearchScreen();
case 4:
return MeScreen();
return UserScreen('');
}
break;
case PlatformType.gitlab:

View File

@ -1,158 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:git_touch/graphql/github_me.dart';
import 'package:git_touch/models/theme.dart';
import 'package:git_touch/scaffolds/refresh_stateful.dart';
import 'package:git_touch/utils/utils.dart';
import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:git_touch/widgets/entry_item.dart';
import 'package:git_touch/widgets/table_view.dart';
import 'package:git_touch/widgets/text_contains_organization.dart';
import 'package:git_touch/widgets/user_item.dart';
import 'package:git_touch/models/auth.dart';
import 'package:provider/provider.dart';
class MeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RefreshStatefulScaffold<GithubMeUser>(
fetchData: () async {
final data = await Provider.of<AuthModel>(context)
.gqlClient
.execute(GithubMeQuery());
return data.data.viewer;
},
title: AppBarTitle('Me'),
bodyBuilder: (user, _) {
final theme = Provider.of<ThemeModel>(context);
final login = user.login;
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
UserItem(
login: user.login,
name: user.name,
avatarUrl: user.avatarUrl,
bio: Text(user.bio ?? ''),
inUserScreen: true,
),
CommonStyle.border,
Row(children: [
EntryItem(
count: user.repositories.totalCount,
text: 'Repositories',
url: '/$login?tab=repositories',
),
EntryItem(
count: user.starredRepositories.totalCount,
text: 'Stars',
url: '/$login?tab=stars',
),
EntryItem(
count: user.followers.totalCount,
text: 'Followers',
url: '/$login?tab=followers',
),
EntryItem(
count: user.following.totalCount,
text: 'Following',
url: '/$login?tab=following',
),
]),
CommonStyle.verticalGap,
Container(
color: theme.palette.background,
padding: CommonStyle.padding,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
reverse: true,
child: Wrap(
spacing: 3,
children: user
.contributionsCollection.contributionCalendar.weeks
.map((week) {
return Wrap(
direction: Axis.vertical,
spacing: 3,
children: week.contributionDays.map((day) {
var color = convertColor(day.color);
if (theme.brightness == Brightness.dark) {
color = Color.fromRGBO(0xff - color.red,
0xff - color.green, 0xff - color.blue, 1);
}
return SizedBox(
width: 10,
height: 10,
child: DecoratedBox(
decoration: BoxDecoration(color: color),
),
);
}).toList(),
);
}).toList(),
),
),
),
CommonStyle.verticalGap,
TableView(
hasIcon: true,
items: [
if (isNotNullOrEmpty(user.company))
TableViewItem(
leftIconData: Octicons.organization,
text: TextContainsOrganization(
user.company,
style: TextStyle(fontSize: 16, color: theme.palette.text),
oneLine: true,
),
),
if (isNotNullOrEmpty(user.location))
TableViewItem(
leftIconData: Octicons.location,
text: Text(user.location),
onTap: () {
launchUrl('https://www.google.com/maps/place/' +
user.location.replaceAll(RegExp(r'\s+'), ''));
},
),
if (isNotNullOrEmpty(user.email))
TableViewItem(
leftIconData: Octicons.mail,
text: Text(user.email),
onTap: () {
launchUrl('mailto:' + user.email);
},
),
if (isNotNullOrEmpty(user.websiteUrl))
TableViewItem(
leftIconData: Octicons.link,
text: Text(user.websiteUrl),
onTap: () {
var url = user.websiteUrl;
if (!url.startsWith('http')) {
url = 'http://$url';
}
launchUrl(url);
},
),
],
),
CommonStyle.verticalGap,
TableView(
hasIcon: true,
items: [
TableViewItem(
leftIconData: Octicons.settings,
text: Text('Settings'),
url: '/settings',
),
],
),
CommonStyle.verticalGap,
],
);
},
);
}
}

View File

@ -43,6 +43,8 @@ class UserScreen extends StatelessWidget {
UserScreen(this.login);
bool get isViewer => login.isEmpty;
Iterable<Widget> _buildPinnedItems(Iterable<GithubUserRepository> pinnedItems,
Iterable<GithubUserRepository> repositories) {
String title;
@ -58,7 +60,6 @@ class UserScreen extends StatelessWidget {
if (items.isEmpty) return [];
return [
CommonStyle.verticalGap,
if (title != null) TableViewHeader(title),
...join(
CommonStyle.border,
@ -70,7 +71,7 @@ class UserScreen extends StatelessWidget {
}
Widget _buildHeader(BuildContext context, String avatarUrl, String name,
DateTime createdAt, String bio) {
String login, DateTime createdAt, String bio) {
final theme = Provider.of<ThemeModel>(context);
return Container(
@ -153,8 +154,8 @@ class UserScreen extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_buildHeader(
context, user.avatarUrl, user.name, user.createdAt, user.bio),
_buildHeader(context, user.avatarUrl, user.name, user.login,
user.createdAt, user.bio),
CommonStyle.border,
Row(children: [
EntryItem(
@ -255,11 +256,24 @@ class UserScreen extends StatelessWidget {
),
],
),
..._buildPinnedItems(
user.pinnedItems.nodes
.where((n) => n is GithubUserRepository)
.cast<GithubUserRepository>(),
user.repositories.nodes),
CommonStyle.verticalGap,
if (isViewer)
TableView(
hasIcon: true,
items: [
TableViewItem(
leftIconData: Octicons.settings,
text: Text('Settings'),
url: '/settings',
),
],
)
else
..._buildPinnedItems(
user.pinnedItems.nodes
.where((n) => n is GithubUserRepository)
.cast<GithubUserRepository>(),
user.repositories.nodes),
CommonStyle.verticalGap,
],
);
@ -270,19 +284,19 @@ class UserScreen extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
_buildHeader(context, payload.avatarUrl, payload.name,
_buildHeader(context, payload.avatarUrl, payload.name, payload.login,
payload.createdAt, payload.description),
CommonStyle.border,
Row(children: [
EntryItem(
count: payload.pinnableItems.totalCount,
text: 'Repositories',
url: '/$login?tab=repositories',
url: '/${payload.login}?tab=repositories',
),
EntryItem(
count: payload.membersWithRole.totalCount,
text: 'Members',
url: '/$login?tab=people',
url: '/${payload.login}?tab=people',
),
]),
CommonStyle.verticalGap,
@ -320,6 +334,7 @@ class UserScreen extends StatelessWidget {
),
],
),
CommonStyle.verticalGap,
..._buildPinnedItems(
payload.pinnedItems.nodes
.where((n) => n is GithubUserRepository)
@ -338,10 +353,12 @@ class UserScreen extends StatelessWidget {
return RefreshStatefulScaffold<GithubUserRepositoryOwner>(
fetchData: () async {
final data = await Provider.of<AuthModel>(context).gqlClient.execute(
GithubUserQuery(variables: GithubUserArguments(login: login)));
return data.data.repositoryOwner;
GithubUserQuery(
variables:
GithubUserArguments(login: login, isViewer: isViewer)));
return isViewer ? data.data.viewer : data.data.repositoryOwner;
},
title: AppBarTitle('User'), // TODO:
title: AppBarTitle(isViewer ? 'Me' : 'User'), // TODO:
actionBuilder: (payload, _) {
if (payload == null)
return ActionButton(
@ -361,11 +378,12 @@ class UserScreen extends StatelessWidget {
onPress: (_) async {
if (user.viewerIsFollowing) {
await Provider.of<AuthModel>(context)
.deleteWithCredentials('/user/following/$login');
.deleteWithCredentials(
'/user/following/${user.login}');
user.viewerIsFollowing = false;
} else {
Provider.of<AuthModel>(context)
.putWithCredentials('/user/following/$login');
Provider.of<AuthModel>(context).putWithCredentials(
'/user/following/${user.login}');
user.viewerIsFollowing = true;
}
},
@ -392,6 +410,9 @@ class UserScreen extends StatelessWidget {
}
},
bodyBuilder: (payload, _) {
if (isViewer) {
return _buildUser(context, payload as GithubUserUser);
}
switch (payload.resolveType) {
case 'User':
return _buildUser(context, payload as GithubUserUser);