From ee027bb0d20a6e9e9f899f3ef3967498f648b786 Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Wed, 4 Dec 2019 22:00:39 +0800 Subject: [PATCH] refactor: gitlab types --- lib/models/gitlab.dart | 81 ++++++++++++++++++++++ lib/models/gitlab.g.dart | 115 +++++++++++++++++++++++++++++++ lib/screens/gitlab/issue.dart | 43 +++++++----- lib/screens/gitlab/todos.dart | 28 ++++---- lib/screens/gitlab/user.dart | 41 ++++++----- lib/widgets/repository_item.dart | 19 ++--- 6 files changed, 268 insertions(+), 59 deletions(-) create mode 100644 lib/models/gitlab.dart create mode 100644 lib/models/gitlab.g.dart diff --git a/lib/models/gitlab.dart b/lib/models/gitlab.dart new file mode 100644 index 0000000..abb95bc --- /dev/null +++ b/lib/models/gitlab.dart @@ -0,0 +1,81 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'gitlab.g.dart'; + +@JsonSerializable(fieldRename: FieldRename.snake) +class GitlabUser { + int id; + String username; + String name; + String avatarUrl; + + GitlabUser(); + + factory GitlabUser.fromJson(Map json) => + _$GitlabUserFromJson(json); +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class GitlabRepository { + int id; + GitlabUser owner; + String name; + String description; + int starCount; + int forksCount; + + GitlabRepository(); + + factory GitlabRepository.fromJson(Map json) => + _$GitlabRepositoryFromJson(json); +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class GitlabTodoProject { + String pathWithNamespace; + + GitlabTodoProject(); + + factory GitlabTodoProject.fromJson(Map json) => + _$GitlabTodoProjectFromJson(json); +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class GitlabTodo { + GitlabUser author; + GitlabTodoProject project; + String actionName; + String targetType; + GitlabIssue target; + + GitlabTodo(); + + factory GitlabTodo.fromJson(Map json) => + _$GitlabTodoFromJson(json); +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class GitlabIssue { + int iid; + int projectId; + String title; + GitlabUser author; + String description; + String createdAt; + + GitlabIssue(); + + factory GitlabIssue.fromJson(Map json) => + _$GitlabIssueFromJson(json); +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class GitlabIssueNote { + GitlabUser author; + String body; + + GitlabIssueNote(); + + factory GitlabIssueNote.fromJson(Map json) => + _$GitlabIssueNoteFromJson(json); +} diff --git a/lib/models/gitlab.g.dart b/lib/models/gitlab.g.dart new file mode 100644 index 0000000..8de8c72 --- /dev/null +++ b/lib/models/gitlab.g.dart @@ -0,0 +1,115 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'gitlab.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +GitlabUser _$GitlabUserFromJson(Map json) { + return GitlabUser() + ..id = json['id'] as int + ..username = json['username'] as String + ..name = json['name'] as String + ..avatarUrl = json['avatar_url'] as String; +} + +Map _$GitlabUserToJson(GitlabUser instance) => + { + 'id': instance.id, + 'username': instance.username, + 'name': instance.name, + 'avatar_url': instance.avatarUrl, + }; + +GitlabRepository _$GitlabRepositoryFromJson(Map json) { + return GitlabRepository() + ..id = json['id'] as int + ..owner = json['owner'] == null + ? null + : GitlabUser.fromJson(json['owner'] as Map) + ..name = json['name'] as String + ..description = json['description'] as String + ..starCount = json['star_count'] as int + ..forksCount = json['forks_count'] as int; +} + +Map _$GitlabRepositoryToJson(GitlabRepository instance) => + { + 'id': instance.id, + 'owner': instance.owner, + 'name': instance.name, + 'description': instance.description, + 'star_count': instance.starCount, + 'forks_count': instance.forksCount, + }; + +GitlabTodoProject _$GitlabTodoProjectFromJson(Map json) { + return GitlabTodoProject() + ..pathWithNamespace = json['path_with_namespace'] as String; +} + +Map _$GitlabTodoProjectToJson(GitlabTodoProject instance) => + { + 'path_with_namespace': instance.pathWithNamespace, + }; + +GitlabTodo _$GitlabTodoFromJson(Map json) { + return GitlabTodo() + ..author = json['author'] == null + ? null + : GitlabUser.fromJson(json['author'] as Map) + ..project = json['project'] == null + ? null + : GitlabTodoProject.fromJson(json['project'] as Map) + ..actionName = json['action_name'] as String + ..targetType = json['target_type'] as String + ..target = json['target'] == null + ? null + : GitlabIssue.fromJson(json['target'] as Map); +} + +Map _$GitlabTodoToJson(GitlabTodo instance) => + { + 'author': instance.author, + 'project': instance.project, + 'action_name': instance.actionName, + 'target_type': instance.targetType, + 'target': instance.target, + }; + +GitlabIssue _$GitlabIssueFromJson(Map json) { + return GitlabIssue() + ..iid = json['iid'] as int + ..projectId = json['project_id'] as int + ..title = json['title'] as String + ..author = json['author'] == null + ? null + : GitlabUser.fromJson(json['author'] as Map) + ..description = json['description'] as String + ..createdAt = json['created_at'] as String; +} + +Map _$GitlabIssueToJson(GitlabIssue instance) => + { + 'iid': instance.iid, + 'project_id': instance.projectId, + 'title': instance.title, + 'author': instance.author, + 'description': instance.description, + 'created_at': instance.createdAt, + }; + +GitlabIssueNote _$GitlabIssueNoteFromJson(Map json) { + return GitlabIssueNote() + ..author = json['author'] == null + ? null + : GitlabUser.fromJson(json['author'] as Map) + ..body = json['body'] as String; +} + +Map _$GitlabIssueNoteToJson(GitlabIssueNote instance) => + { + 'author': instance.author, + 'body': instance.body, + }; diff --git a/lib/screens/gitlab/issue.dart b/lib/screens/gitlab/issue.dart index 41905b6..503b0b9 100644 --- a/lib/screens/gitlab/issue.dart +++ b/lib/screens/gitlab/issue.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.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/avatar.dart'; @@ -6,34 +7,40 @@ import 'package:git_touch/widgets/markdown_view.dart'; import 'package:provider/provider.dart'; import 'package:git_touch/models/auth.dart'; import 'package:timeago/timeago.dart' as timeago; +import 'package:tuple/tuple.dart'; class GitlabIssueScreen extends StatelessWidget { final int projectId; - final int issueIid; + final int iid; final bool isMr; - GitlabIssueScreen(this.projectId, this.issueIid, {this.isMr}); + GitlabIssueScreen(this.projectId, this.iid, {this.isMr}); @override Widget build(BuildContext context) { - return RefreshStatefulScaffold( - title: Text('Issue #$issueIid'), + return RefreshStatefulScaffold< + Tuple3, List>>( + title: Text('Issue #$iid'), fetchData: () async { final type = isMr ? 'merge_requests' : 'issues'; final items = await Future.wait([ Provider.of(context) - .fetchGitlab('/projects/$projectId/$type/$issueIid'), + .fetchGitlab('/projects/$projectId/$type/$iid'), Provider.of(context) - .fetchGitlab('/projects/$projectId/$type/$issueIid/notes'), + .fetchGitlab('/projects/$projectId/$type/$iid/notes'), Provider.of(context) - .fetchGitlab('/projects/$projectId/$type/$issueIid/award_emoji'), + .fetchGitlab('/projects/$projectId/$type/$iid/award_emoji'), ]); - return items; + return Tuple3( + GitlabIssue.fromJson(items[0]), + (items[1] as List).map((v) => GitlabIssueNote.fromJson(v)), + items[2] as List, + ); }, bodyBuilder: (data, _) { - final issue = data[0]; - final notes = data[1] as List; - final emoji = data[2]; + final issue = data.item1; + final notes = data.item2; + final emoji = data.item3; return Column( children: [ @@ -41,16 +48,16 @@ class GitlabIssueScreen extends StatelessWidget { padding: CommonStyle.padding, child: Column( children: [ - Text(issue['title']), + Text(issue.title), Row( children: [ - Avatar.medium(url: issue['author']['avatar_url']), + Avatar.medium(url: issue.author.avatarUrl), Expanded( - child: Text(issue['description']), + child: Text(issue.description), ), ], ), - Text(timeago.format(DateTime.parse(issue['created_at']))) + Text(timeago.format(DateTime.parse(issue.createdAt))) ], ), ), @@ -63,15 +70,15 @@ class GitlabIssueScreen extends StatelessWidget { children: [ Row( children: [ - Avatar.medium(url: note['author']['avatar_url']), + Avatar.medium(url: note.author.avatarUrl), Expanded( child: Column( - children: [Text(note['author']['name'])], + children: [Text(note.author.name)], ), ) ], ), - MarkdownView(note['body']), + MarkdownView(note.body), ], ), ); diff --git a/lib/screens/gitlab/todos.dart b/lib/screens/gitlab/todos.dart index 517b5a4..d133dbe 100644 --- a/lib/screens/gitlab/todos.dart +++ b/lib/screens/gitlab/todos.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:git_touch/models/auth.dart'; +import 'package:git_touch/models/gitlab.dart'; import 'package:git_touch/models/theme.dart'; import 'package:git_touch/scaffolds/refresh_stateful.dart'; import 'package:git_touch/screens/gitlab/issue.dart'; @@ -13,33 +14,32 @@ class GitlabTodosScreen extends StatelessWidget { Widget build(BuildContext context) { final theme = Provider.of(context); - return RefreshStatefulScaffold( + return RefreshStatefulScaffold>( title: Text('Todos'), - fetchData: () { - return Provider.of(context).fetchGitlab('/todos'); + fetchData: () async { + final vs = await Provider.of(context).fetchGitlab('/todos'); + return (vs as List).map((v) => GitlabTodo.fromJson(v)); }, bodyBuilder: (data, _) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, - children: (data as List).map((item) { + children: data.map((item) { return Link( screenBuilder: (_) => GitlabIssueScreen( - item['target']['project_id'], item['target']['iid'], - isMr: item['target_type'] == 'MergeRequest'), + item.target.projectId, item.target.iid, + isMr: item.targetType == 'MergeRequest'), child: Container( padding: CommonStyle.padding, child: Row( children: [ - Avatar.medium( - url: item['author']['avatar_url'], - ), + Avatar.medium(url: item.author.avatarUrl), SizedBox(width: 12), Expanded( child: Text.rich( TextSpan( children: [ TextSpan( - text: item['author']['name'], + text: item.author.name, style: TextStyle( color: theme.palette.primary, fontWeight: FontWeight.w500, @@ -47,13 +47,13 @@ class GitlabTodosScreen extends StatelessWidget { ), TextSpan( text: ' ' + - item['action_name'] + + item.actionName + ' you ' + - item['target_type'] + + item.targetType + ' ' + - item['project']['path_with_namespace'] + + item.project.pathWithNamespace + ' ' + - item['target']['iid'].toString(), + item.target.iid.toString(), ), ], ), diff --git a/lib/screens/gitlab/user.dart b/lib/screens/gitlab/user.dart index d9e2f6a..8b9f226 100644 --- a/lib/screens/gitlab/user.dart +++ b/lib/screens/gitlab/user.dart @@ -1,10 +1,12 @@ import 'package:flutter/widgets.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/widgets/border_view.dart'; import 'package:git_touch/widgets/repository_item.dart'; import 'package:git_touch/widgets/user_item.dart'; import 'package:provider/provider.dart'; +import 'package:tuple/tuple.dart'; class GitlabUserScreen extends StatelessWidget { final String username; @@ -13,32 +15,35 @@ class GitlabUserScreen extends StatelessWidget { @override Widget build(BuildContext context) { - return RefreshStatefulScaffold( + return RefreshStatefulScaffold< + Tuple3, + List>>>( title: Text('User'), fetchData: () async { - final items = await Provider.of(context) - .fetchGitlab('/users?username=$username'); - final user = items[0]; - final projects = await Provider.of(context) - .fetchGitlab('/users/${user['id']}/projects') as List; - final langs = await Future.wait(projects.map((p) => - Provider.of(context) - .fetchGitlab('/projects/${p['id']}/languages'))); - for (var i = 0; i < projects.length; i++) { - projects[i]['language'] = langs[i]; - } - return [user, projects]; + final auth = Provider.of(context); + + final v0 = await auth.fetchGitlab('/users?username=$username'); + final user = GitlabUser.fromJson(v0[0]); + + final v1 = await auth.fetchGitlab('/users/${user.id}/projects'); + final projects = (v1 as List).map((v) => GitlabRepository.fromJson(v)); + + final languages = await Future.wait(projects + .map((p) => auth.fetchGitlab('/projects/${p.id}/languages'))); + + return Tuple3(user, projects, languages.cast>()); }, bodyBuilder: (data, _) { - final user = data[0]; - final projects = data[1] as List; + final user = data.item1; + final projects = data.item2; + final languages = data.item3; return Column( children: [ UserItem( - login: user['username'], - avatarUrl: user['avatar_url'], - name: user['name'], + login: user.username, + avatarUrl: user.avatarUrl, + name: user.name, ), BorderView(height: 10), Column( diff --git a/lib/widgets/repository_item.dart b/lib/widgets/repository_item.dart index cf182cf..0b84ba1 100644 --- a/lib/widgets/repository_item.dart +++ b/lib/widgets/repository_item.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import 'package:git_touch/models/gitlab.dart'; import 'package:git_touch/models/theme.dart'; import 'package:git_touch/screens/repository.dart'; import 'package:git_touch/widgets/action_button.dart'; @@ -65,18 +66,18 @@ class RepositoryItem extends StatelessWidget { ? [] : payload['repositoryTopics']['nodes']; - RepositoryItem.gitlab(payload, {this.inRepoScreen = false}) - : this.owner = payload['namespace']['name'], - this.avatarUrl = payload['owner']['avatar_url'], - this.name = payload['name'], - this.description = payload['description'], + RepositoryItem.gitlab(GitlabRepository payload, {this.inRepoScreen = false}) + : this.owner = payload.owner.name, + this.avatarUrl = payload.owner.avatarUrl, + this.name = payload.name, + this.description = payload.description, this.iconData = Octicons.repo, - this.starCount = payload['star_count'], - this.forkCount = payload['forks_count'], + this.starCount = payload.starCount, + this.forkCount = payload.forksCount, this.primaryLanguageName = null, this.primaryLanguageColor = null, - this.screenBuilder = ((_) => - RepositoryScreen(payload['owner']['login'], payload['name'])), + this.screenBuilder = + ((_) => RepositoryScreen(payload.owner.username, payload.name)), this.topics = []; static IconData _buildIconData(payload) {