From f7eb0e6f6269d2011deb58c033adb6d7bda194d9 Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Wed, 5 Oct 2022 01:13:15 +0800 Subject: [PATCH] feat(github): organization sponsors --- lib/screens/gh_user.dart | 143 ++++++++++++++------------- lib/widgets/avatar.dart | 6 +- packages/gql_github/lib/user.graphql | 48 +++++---- 3 files changed, 102 insertions(+), 95 deletions(-) diff --git a/lib/screens/gh_user.dart b/lib/screens/gh_user.dart index 275a13e..1df7530 100644 --- a/lib/screens/gh_user.dart +++ b/lib/screens/gh_user.dart @@ -18,9 +18,51 @@ import 'package:git_touch/widgets/user_header.dart'; import 'package:go_router/go_router.dart'; import 'package:gql_github/user.data.gql.dart'; import 'package:gql_github/user.req.gql.dart'; +import 'package:intl/intl.dart'; import 'package:maps_launcher/maps_launcher.dart'; import 'package:provider/provider.dart'; +class _SponsorItem extends StatelessWidget { + const _SponsorItem({ + required this.count, + required this.login, + required this.nodes, + required this.sponsoring, + }); + final int count; + final String login; + final Iterable nodes; + final bool sponsoring; + + @override + Widget build(BuildContext context) { + return AntListItem( + prefix: Icon(sponsoring ? Octicons.heart : Octicons.heart_fill), + extra: Text(count.toString()), + onClick: () { + context.pushUrl(sponsoring + ? 'https://github.com/$login?tab=sponsoring' + : 'https://github.com/sponsors/$login#sponsors'); + }, + child: Row( + children: [ + Text(toBeginningOfSentenceCase( + sponsoring ? 'sponsoring' : 'sponsors')!), + const Spacer(), + for (final sponsor in nodes) ...[ + const SizedBox(width: 6), + Avatar( + square: sponsor.G__typename != 'User', + url: (sponsor as dynamic).avatarUrl, // TODO: same key here + size: AvatarSize.small, + ), + ], + ], + ), + ); + } +} + class _Repos extends StatelessWidget { _Repos(final Iterable pinned, final Iterable? repos) : title = @@ -153,58 +195,18 @@ class _User extends StatelessWidget { CommonStyle.verticalGap, AntList(children: [ if (p.sponsors.totalCount > 0) - AntListItem( - prefix: const Icon(Octicons.heart_fill), - extra: Text(p.sponsors.totalCount.toString()), - child: Row( - children: [ - const Text('Sponsors'), - const Spacer(), - for (final sponsor in p.sponsors.nodes!) ...[ - const SizedBox(width: 6), - Avatar( - isOrg: sponsor.G__typename != 'User', - url: sponsor.G__typename == 'User' - ? (sponsor as GUserData_user_sponsors_nodes__asUser) - .avatarUrl - : (sponsor - as GUserParts_sponsors_nodes__asOrganization) - .avatarUrl, - size: AvatarSize.small, - ), - ], - ], - ), - onClick: () { - context.goNamed('/github/${p.login}?tab=sponsors'); - }, + _SponsorItem( + count: p.sponsors.totalCount, + login: p.login, + nodes: p.sponsors.nodes!, + sponsoring: false, ), if (p.sponsoring.totalCount > 0) - AntListItem( - prefix: const Icon(Octicons.heart), - extra: Text(p.sponsoring.totalCount.toString()), - child: Row( - children: [ - const Text('Sponsoring'), - const Spacer(), - for (final sponsor in p.sponsoring.nodes!) ...[ - const SizedBox(width: 6), - Avatar( - isOrg: sponsor.G__typename != 'User', - url: sponsor.G__typename == 'User' - ? (sponsor as GUserData_user_sponsoring_nodes__asUser) - .avatarUrl - : (sponsor - as GUserParts_sponsoring_nodes__asOrganization) - .avatarUrl, - size: AvatarSize.small, - ), - ], - ], - ), - onClick: () { - context.goNamed('/github/${p.login}?tab=sponsoring'); - }, + _SponsorItem( + count: p.sponsoring.totalCount, + login: p.login, + nodes: p.sponsoring.nodes!, + sponsoring: true, ), if (p.organizations.totalCount > 0) AntListItem( @@ -217,7 +219,7 @@ class _User extends StatelessWidget { for (final org in p.organizations.nodes!) ...[ const SizedBox(width: 6), Avatar( - isOrg: true, + square: true, url: org.avatarUrl, size: AvatarSize.small, ), @@ -349,7 +351,9 @@ class GhUserScreen extends StatelessWidget { ) ], ); - } else if (data?.organization != null) { + } + + if (data?.organization != null) { final p = data!.organization!; return Column( crossAxisAlignment: CrossAxisAlignment.stretch, @@ -380,21 +384,6 @@ class GhUserScreen extends StatelessWidget { ), ], ), - CommonStyle.border, - Row( - children: [ - EntryItem( - count: p.sponsors.totalCount, - text: 'Sponsors', - url: 'https://github.com/sponsors/${p.login}', - ), - EntryItem( - count: p.membersWithRole.totalCount, - text: AppLocalizations.of(context)!.members, - url: '/github/${p.login}?tab=people', - ), - ], - ), AntList( children: [ if (p.location != null) @@ -438,6 +427,25 @@ class GhUserScreen extends StatelessWidget { ], ), CommonStyle.verticalGap, + AntList( + children: [ + if (p.sponsors.totalCount > 0) + _SponsorItem( + count: p.sponsors.totalCount, + login: p.login, + nodes: p.sponsors.nodes!, + sponsoring: false, + ), + if (p.sponsoring.totalCount > 0) + _SponsorItem( + count: p.sponsoring.totalCount, + login: p.login, + nodes: p.sponsoring.nodes!, + sponsoring: true, + ), + ], + ), + CommonStyle.verticalGap, AntList( children: [ AntListItem( @@ -466,6 +474,7 @@ class GhUserScreen extends StatelessWidget { ], ); } + return null; // TODO: not found }, ); diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index 81dd62f..0cdd1d6 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -18,12 +18,12 @@ class Avatar extends StatelessWidget { required this.url, this.size = AvatarSize.medium, this.linkUrl, - this.isOrg = false, + this.square = false, }); final String? url; final double size; final String? linkUrl; - final bool isOrg; + final bool square; @override Widget build(BuildContext context) { @@ -35,7 +35,7 @@ class Avatar extends StatelessWidget { final fallbackWidget = Image.asset(fallback, width: size, height: size); final widget = ClipRRect( - borderRadius: BorderRadius.circular(isOrg ? 4 : size), + borderRadius: BorderRadius.circular(square ? 4 : size), child: url == null ? fallbackWidget : FadeInImage.assetNetwork( diff --git a/packages/gql_github/lib/user.graphql b/packages/gql_github/lib/user.graphql index fd3824f..c64d44e 100644 --- a/packages/gql_github/lib/user.graphql +++ b/packages/gql_github/lib/user.graphql @@ -19,6 +19,19 @@ fragment RepoParts on Repository { } } +fragment SponsorConnectionParts on SponsorConnection { + totalCount + nodes { + __typename + ... on User { + avatarUrl + } + ... on Organization { + avatarUrl + } + } +} + fragment UserParts on User { login name @@ -48,29 +61,11 @@ fragment UserParts on User { } } } - sponsors(first: 3) { - totalCount - nodes { - __typename - ... on User { - avatarUrl - } - ... on Organization { - avatarUrl - } - } - } sponsoring(first: 3) { - totalCount - nodes { - __typename - ... on User { - avatarUrl - } - ... on Organization { - avatarUrl - } - } + ...SponsorConnectionParts + } + sponsors(first: 3) { + ...SponsorConnectionParts } organizations(first: 3) { totalCount @@ -125,12 +120,15 @@ query User($login: String!) { twitterUsername createdAt viewerIsFollowing - sponsors { - totalCount - } membersWithRole { totalCount } + sponsoring(first: 3) { + ...SponsorConnectionParts + } + sponsors(first: 3) { + ...SponsorConnectionParts + } pinnedItems(first: 6) { nodes { ... on Repository {