import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:git_touch/graphql/gh.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/action_entry.dart'; import 'package:git_touch/widgets/app_bar_title.dart'; import 'package:git_touch/widgets/contribution.dart'; import 'package:git_touch/widgets/mutation_button.dart'; import 'package:git_touch/widgets/entry_item.dart'; import 'package:git_touch/widgets/repository_item.dart'; import 'package:git_touch/widgets/table_view.dart'; import 'package:git_touch/widgets/text_with_at.dart'; import 'package:git_touch/models/auth.dart'; import 'package:git_touch/widgets/user_header.dart'; import 'package:provider/provider.dart'; import 'package:git_touch/widgets/action_button.dart'; class GhUserScreen extends StatelessWidget { final String login; GhUserScreen(this.login); bool get isViewer => login == null; Iterable _buildPinnedItems(Iterable pinnedItems, Iterable repositories) { String title; Iterable items = []; if (pinnedItems.isNotEmpty) { title = 'pinned repositories'; items = pinnedItems; } else if (repositories.isNotEmpty) { title = 'popular repositories'; items = repositories; } if (items.isEmpty) return []; return [ if (title != null) TableViewHeader(title), ...join( CommonStyle.border, items.map((v) { return RepositoryItem.gh( owner: v.owner.login, avatarUrl: v.owner.avatarUrl, name: v.name, description: v.description, starCount: v.stargazers.totalCount, forkCount: v.forks.totalCount, primaryLanguageName: v.primaryLanguage?.name, primaryLanguageColor: v.primaryLanguage?.color, isPrivate: v.isPrivate, isFork: v.isFork, ); }).toList(), ), ]; } Widget _buildUser(BuildContext context, GhUserUser p, void Function(void Function()) setState) { final theme = Provider.of(context); final auth = Provider.of(context); final login = p.login; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ UserHeader( avatarUrl: p.avatarUrl, name: p.name, login: p.login, createdAt: p.createdAt, bio: p.bio, isViewer: isViewer, rightWidgets: [ if (p.viewerCanFollow) MutationButton( active: p.viewerIsFollowing, text: p.viewerIsFollowing ? 'Unfollow' : 'Follow', onTap: () async { if (p.viewerIsFollowing) { await auth.ghClient.users.unfollowUser(p.login); } else { await auth.ghClient.users.followUser(p.login); } setState(() { p.viewerIsFollowing = !p.viewerIsFollowing; }); }, ) ], ), CommonStyle.border, Row(children: [ EntryItem( count: p.repositories.totalCount, text: 'Repositories', url: '/github/$login?tab=repositories', ), EntryItem( count: p.starredRepositories.totalCount, text: 'Stars', url: '/github/$login?tab=stars', ), EntryItem( count: p.followers.totalCount, text: 'Followers', url: '/github/$login?tab=followers', ), EntryItem( count: p.following.totalCount, text: 'Following', url: '/github/$login?tab=following', ), ]), CommonStyle.border, ContributionWidget( weeks: [ for (final week in p.contributionsCollection.contributionCalendar.weeks) [ // https://github.com/git-touch/git-touch/issues/122 for (final day in week.contributionDays) if (day.color.startsWith('#')) ContributionDay(hexColor: day.color) else if (day.color.contains('L1')) ContributionDay(hexColor: contributionColors[0]) else if (day.color.contains('L2')) ContributionDay(hexColor: contributionColors[1]) else if (day.color.contains('L3')) ContributionDay(hexColor: contributionColors[2]) else if (day.color.contains('L4')) ContributionDay(hexColor: contributionColors[3]) else ContributionDay(hexColor: contributionEmptyColor) ] ], ), CommonStyle.border, TableView( hasIcon: true, items: [ TableViewItem( leftIconData: Icons.rss_feed, text: Text('Events'), url: '/github/$login?tab=events', ), TableViewItem( leftIconData: Octicons.book, text: Text('Gists'), url: '/github/$login?tab=gists', ), TableViewItem( leftIconData: Octicons.home, text: Text('Organizations'), url: '/github/$login?tab=organizations', ), if (isNotNullOrEmpty(p.company)) TableViewItem( leftIconData: Octicons.organization, text: TextWithAt( text: p.company, linkFactory: (text) => '/github/' + text.substring(1), style: TextStyle(fontSize: 17, color: theme.palette.text), oneLine: true, ), ), if (isNotNullOrEmpty(p.location)) TableViewItem( leftIconData: Octicons.location, text: Text(p.location), onTap: () { launchUrl('https://www.google.com/maps/place/' + p.location.replaceAll(RegExp(r'\s+'), '')); }, ), if (isNotNullOrEmpty(p.email)) TableViewItem( leftIconData: Octicons.mail, text: Text(p.email), onTap: () { launchUrl('mailto:' + p.email); }, ), if (isNotNullOrEmpty(p.websiteUrl)) TableViewItem( leftIconData: Octicons.link, text: Text(p.websiteUrl), onTap: () { var url = p.websiteUrl; if (!url.startsWith('http')) { url = 'http://$url'; } launchUrl(url); }, ), ], ), 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(), p.repositories.nodes), CommonStyle.verticalGap, ], ); } Widget _buildOrganization(BuildContext context, GhUserOrganization p) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ UserHeader( avatarUrl: p.avatarUrl, name: p.name, login: p.login, createdAt: p.createdAt, bio: p.description, ), CommonStyle.border, Row(children: [ EntryItem( count: p.pinnableItems.totalCount, text: 'Repositories', url: '/github/${p.login}?tab=orgrepo', ), EntryItem( count: p.membersWithRole.totalCount, text: 'Members', url: '/github/${p.login}?tab=people', ), ]), TableView( hasIcon: true, items: [ TableViewItem( leftIconData: Icons.rss_feed, text: Text('Events'), url: '/github/$login?tab=events', ), if (isNotNullOrEmpty(p.location)) TableViewItem( leftIconData: Octicons.location, text: Text(p.location), onTap: () { launchUrl('https://www.google.com/maps/place/' + p.location.replaceAll(RegExp(r'\s+'), '')); }, ), if (isNotNullOrEmpty(p.email)) TableViewItem( leftIconData: Octicons.mail, text: Text(p.email), onTap: () { launchUrl('mailto:' + p.email); }, ), if (isNotNullOrEmpty(p.websiteUrl)) TableViewItem( leftIconData: Octicons.link, text: Text(p.websiteUrl), onTap: () { var url = p.websiteUrl; if (!url.startsWith('http')) { url = 'http://$url'; } launchUrl(url); }, ), ], ), CommonStyle.verticalGap, ..._buildPinnedItems( p.pinnedItems.nodes .where((n) => n is GhUserRepository) .cast(), p.pinnableItems.nodes .where((n) => n is GhUserRepository) .cast(), ), CommonStyle.verticalGap, ], ); } @override Widget build(BuildContext context) { final auth = Provider.of(context); return RefreshStatefulScaffold( fetch: () async { final data = await auth.gqlClient.execute(GhUserQuery( variables: GhUserArguments(login: login ?? '', isViewer: isViewer))); return isViewer ? data.data.viewer : data.data.repositoryOwner; }, title: AppBarTitle(isViewer ? 'Me' : login), action: isViewer ? ActionEntry( iconData: Icons.settings, url: '/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); } switch (payload.resolveType) { case 'User': return _buildUser(context, payload as GhUserUser, setState); case 'Organization': return _buildOrganization(context, payload as GhUserOrganization); default: return null; } }, ); } }