From a021f755f343f4bae823a254c98f19bfa8c0435a Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Tue, 28 Jan 2020 20:50:38 +0800 Subject: [PATCH] feat(gitlab): project badges --- lib/models/gitlab.dart | 19 ++--- lib/models/gitlab.g.dart | 9 ++ lib/screens/gitlab_project.dart | 146 ++++++++++++++++++-------------- 3 files changed, 99 insertions(+), 75 deletions(-) diff --git a/lib/models/gitlab.dart b/lib/models/gitlab.dart index b28618c..febcd00 100644 --- a/lib/models/gitlab.dart +++ b/lib/models/gitlab.dart @@ -81,6 +81,14 @@ class GitlabIssueNote { _$GitlabIssueNoteFromJson(json); } +@JsonSerializable(fieldRename: FieldRename.snake) +class GitlabLabel { + String renderedImageUrl; + GitlabLabel(); + factory GitlabLabel.fromJson(Map json) => + _$GitlabLabelFromJson(json); +} + @JsonSerializable(fieldRename: FieldRename.snake) class GitlabProject { int id; @@ -96,18 +104,7 @@ class GitlabProject { bool issuesEnabled; int openIssuesCount; bool mergeRequestsEnabled; - - @JsonKey(ignore: true) - String readme; - - @JsonKey(ignore: true) - Map languages; - - @JsonKey(ignore: true) - String primaryLanguage; - GitlabProject(); - factory GitlabProject.fromJson(Map json) => _$GitlabProjectFromJson(json); } diff --git a/lib/models/gitlab.g.dart b/lib/models/gitlab.g.dart index e902be4..490936b 100644 --- a/lib/models/gitlab.g.dart +++ b/lib/models/gitlab.g.dart @@ -116,6 +116,15 @@ Map _$GitlabIssueNoteToJson(GitlabIssueNote instance) => 'body': instance.body, }; +GitlabLabel _$GitlabLabelFromJson(Map json) { + return GitlabLabel()..renderedImageUrl = json['rendered_image_url'] as String; +} + +Map _$GitlabLabelToJson(GitlabLabel instance) => + { + 'rendered_image_url': instance.renderedImageUrl, + }; + GitlabProject _$GitlabProjectFromJson(Map json) { return GitlabProject() ..id = json['id'] as int diff --git a/lib/screens/gitlab_project.dart b/lib/screens/gitlab_project.dart index e9b4791..dac3f36 100644 --- a/lib/screens/gitlab_project.dart +++ b/lib/screens/gitlab_project.dart @@ -1,17 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:git_touch/models/auth.dart'; import 'package:git_touch/models/gitlab.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/avatar.dart'; import 'package:git_touch/widgets/entry_item.dart'; import 'package:git_touch/widgets/markdown_view.dart'; import 'package:git_touch/widgets/table_view.dart'; import 'package:provider/provider.dart'; import 'package:git_touch/models/theme.dart'; -import 'package:git_touch/widgets/repository_item.dart'; import 'package:git_touch/widgets/action_button.dart'; +import 'package:tuple/tuple.dart'; final gitlabProjectRouter = RouterScreen('/gitlab/projects/:id', (context, params) => GitlabProjectScreen(int.parse(['id'].first))); @@ -23,116 +25,132 @@ class GitlabProjectScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return RefreshStatefulScaffold( + return RefreshStatefulScaffold< + Tuple4, List, String>>( title: AppBarTitle('Project'), fetchData: () async { final auth = Provider.of(context); - final json = await auth.fetchGitlab('/projects/$id'); - final project = GitlabProject.fromJson(json); - if (project.readmeUrl != null) { - project.readme = await auth.fetchWithGitlabToken( - project.readmeUrl.replaceFirst(r'/blob/', '/raw/')); + final res = await Future.wait([ + auth.fetchGitlab('/projects/$id'), + auth.fetchGitlab('/projects/$id/languages'), + auth.fetchGitlab('/projects/$id/badges'), + ]); + final p = GitlabProject.fromJson(res[0]); + String readme; + if (p.readmeUrl != null) { + readme = await auth.fetchWithGitlabToken( + p.readmeUrl.replaceFirst(r'/blob/', '/raw/')); } - final l = await auth.fetchGitlab('/projects/$id/languages'); - project.languages = Map.from(l); - return project; + return Tuple4( + p, + Map.from(res[1]), + (res[2] as List).map((v) => GitlabLabel.fromJson(v)).toList(), + readme, + ); }, - actionBuilder: (data, setState) { + actionBuilder: (t, setState) { return ActionButton( title: 'Project Actions', items: [ - ...ActionItem.getUrlActions(data.webUrl), + ...ActionItem.getUrlActions(t.item1.webUrl), ], ); }, - bodyBuilder: (data, _) { - final langWidth = MediaQuery.of(context).size.width - - CommonStyle.padding.left - - CommonStyle.padding.right - - data.languages.length + - 1; - + bodyBuilder: (t, _) { + // final langWidth = MediaQuery.of(context).size.width - + // CommonStyle.padding.left - + // CommonStyle.padding.right - + // data.languages.length + + // 1; + final p = t.item1; + final langs = t.item2; + final badges = t.item3; final theme = Provider.of(context); - return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - RepositoryItem( - data.namespace.name, - data.avatarUrl, - data.name, - data.description, - data.starCount, - data.forksCount, - data.languages.keys.first, - null, - null, + Container( + padding: CommonStyle.padding, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: join(SizedBox(height: 12), [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Avatar( + url: p.avatarUrl, + size: AvatarSize.small, + linkUrl: '/${p.namespace.name}', + ), + SizedBox(width: 8), + Text( + '${p.namespace.name} / ${p.name}', + style: TextStyle( + fontSize: 20, + color: theme.palette.primary, + ), + ), + ], + ), + if (p.description != null && p.description.isNotEmpty) + Text( + p.description, + style: TextStyle( + color: theme.palette.secondaryText, + fontSize: 17, + ), + ), + if (badges.isNotEmpty) + Wrap(spacing: 4, runSpacing: 4, children: [ + for (var label in badges) + SvgPicture.network(label.renderedImageUrl, height: 20) + ]) + ]), + ), ), CommonStyle.border, Row( children: [ EntryItem( - count: data.starCount, + count: p.starCount, text: 'Stars', ), EntryItem( - count: data.forksCount, + count: p.forksCount, text: 'Forks', // TODO: ), ], ), - CommonStyle.verticalGap, - if (data.languages.isNotEmpty) - Container( - color: theme.palette.background, - padding: CommonStyle.padding.copyWith(top: 8, bottom: 8), - child: ClipRRect( - borderRadius: BorderRadius.circular(2), - child: SizedBox( - height: 10, - child: Row( - children: join( - SizedBox(width: 1), - data.languages.entries - .map((e) => Container( - color: convertColor('#ff0'), - width: langWidth * - e.value / - data.languages.length)) - .toList(), - ), - ), - ), - ), - ), + CommonStyle.border, TableView( hasIcon: true, items: [ TableViewItem( leftIconData: Octicons.code, - text: Text('Code'), - url: '/projects/$id/tree', + text: Text(langs.keys.isEmpty ? 'Code' : langs.keys.first), + url: '/gitlab/projects/$id/tree', ), - if (data.issuesEnabled) + if (p.issuesEnabled) TableViewItem( leftIconData: Octicons.issue_opened, text: Text('Issues'), - rightWidget: - Text(numberFormat.format(data.openIssuesCount)), + rightWidget: Text(numberFormat.format(p.openIssuesCount)), + url: '/gitlab/projects/$id/issues', ), - if (data.mergeRequestsEnabled) + if (p.mergeRequestsEnabled) TableViewItem( leftIconData: Octicons.git_pull_request, text: Text('Merge requests'), + url: '/gitlab/projects/$id/merge_requests', ), ], ), CommonStyle.verticalGap, - if (data.readme != null) + if (t.item4 != null) Container( padding: CommonStyle.padding, color: theme.palette.background, - child: MarkdownView(data.readme), + child: MarkdownView(t.item4), ), CommonStyle.verticalGap, ],