From 4099baaadb9771d6065eab650bf389535901ab5a Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Thu, 26 Dec 2019 18:00:36 +0800 Subject: [PATCH] refactor: use hash code as key --- lib/models/github.dart | 67 ++++++++++++++++++++++++++++++ lib/models/github.g.dart | 57 +++++++++++++++++++++++++ lib/models/notification.dart | 67 ++++++++++-------------------- lib/screens/notification.dart | 26 ++++++------ lib/widgets/notification_item.dart | 27 ++++++------ 5 files changed, 173 insertions(+), 71 deletions(-) diff --git a/lib/models/github.dart b/lib/models/github.dart index d8858d0..2c339e4 100644 --- a/lib/models/github.dart +++ b/lib/models/github.dart @@ -127,6 +127,73 @@ class GithubEventRelease { _$GithubEventReleaseFromJson(json); } +// Notification +@JsonSerializable(fieldRename: FieldRename.snake) +class GithubNotificationItem { + String id; + String htmlUrl; + GithubNotificationItemSubject subject; + DateTime updatedAt; + GithubNotificationItemRepo repository; + bool unread; + + @JsonKey(ignore: true) + String state; + + String get key => '_$hashCode'; + + GithubNotificationItem(); + + factory GithubNotificationItem.fromJson(Map json) => + _$GithubNotificationItemFromJson(json); +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class GithubNotificationItemSubject { + String title; + String type; + String url; + + int _number; + int get number { + if (_number == null) { + _number = int.parse(url?.split('/')?.last ?? '0'); + } + return _number; + } + + GithubNotificationItemSubject(); + + factory GithubNotificationItemSubject.fromJson(Map json) => + _$GithubNotificationItemSubjectFromJson(json); +} + +@JsonSerializable(fieldRename: FieldRename.snake) +class GithubNotificationItemRepo { + String fullName; + + Tuple2 _repo; + String get owner { + if (_repo == null) { + _repo = parseRepositoryFullName(fullName); + } + return _repo.item1; + } + + String get name { + if (_repo == null) { + _repo = parseRepositoryFullName(fullName); + } + return _repo.item2; + } + + GithubNotificationItemRepo(); + + factory GithubNotificationItemRepo.fromJson(Map json) => + _$GithubNotificationItemRepoFromJson(json); +} + +// Trending @JsonSerializable() class GithubTrendingItem { String author; diff --git a/lib/models/github.g.dart b/lib/models/github.g.dart index 080fc9f..64dc393 100644 --- a/lib/models/github.g.dart +++ b/lib/models/github.g.dart @@ -164,6 +164,63 @@ Map _$GithubEventReleaseToJson(GithubEventRelease instance) => 'tag_name': instance.tagName, }; +GithubNotificationItem _$GithubNotificationItemFromJson( + Map json) { + return GithubNotificationItem() + ..id = json['id'] as String + ..htmlUrl = json['html_url'] as String + ..subject = json['subject'] == null + ? null + : GithubNotificationItemSubject.fromJson( + json['subject'] as Map) + ..updatedAt = json['updated_at'] == null + ? null + : DateTime.parse(json['updated_at'] as String) + ..repository = json['repository'] == null + ? null + : GithubNotificationItemRepo.fromJson( + json['repository'] as Map) + ..unread = json['unread'] as bool; +} + +Map _$GithubNotificationItemToJson( + GithubNotificationItem instance) => + { + 'id': instance.id, + 'html_url': instance.htmlUrl, + 'subject': instance.subject, + 'updated_at': instance.updatedAt?.toIso8601String(), + 'repository': instance.repository, + 'unread': instance.unread, + }; + +GithubNotificationItemSubject _$GithubNotificationItemSubjectFromJson( + Map json) { + return GithubNotificationItemSubject() + ..title = json['title'] as String + ..type = json['type'] as String + ..url = json['url'] as String; +} + +Map _$GithubNotificationItemSubjectToJson( + GithubNotificationItemSubject instance) => + { + 'title': instance.title, + 'type': instance.type, + 'url': instance.url, + }; + +GithubNotificationItemRepo _$GithubNotificationItemRepoFromJson( + Map json) { + return GithubNotificationItemRepo()..fullName = json['full_name'] as String; +} + +Map _$GithubNotificationItemRepoToJson( + GithubNotificationItemRepo instance) => + { + 'full_name': instance.fullName, + }; + GithubTrendingItem _$GithubTrendingItemFromJson(Map json) { return GithubTrendingItem() ..author = json['author'] as String diff --git a/lib/models/notification.dart b/lib/models/notification.dart index ef454bb..4764602 100644 --- a/lib/models/notification.dart +++ b/lib/models/notification.dart @@ -1,54 +1,31 @@ import 'package:flutter/material.dart'; -import 'package:timeago/timeago.dart' as timeago; +import 'package:git_touch/models/github.dart'; +import 'package:tuple/tuple.dart'; -class NotificationPayload { - String id; - String type; - String owner; - String name; - int number; - String title; - String updateAt; - bool unread; - - String state; - - String get key => '_' + number.toString(); - - NotificationPayload.fromJson(input) { - id = input['id']; - type = input['subject']['type']; - name = input['repository']['name']; - owner = input['repository']['owner']['login']; - - String url = input['subject']['url']; - - if (type == 'Issue' || type == 'PullRequest') { - String numberStr = url.split('/').lastWhere((_) => true); - number = int.parse(numberStr); - } else { - // Fimber.d(input); - } - - title = input['subject']['title']; - updateAt = timeago.format(DateTime.parse(input['updated_at'])); - unread = input['unread']; - } -} +import '../utils/utils.dart'; class NotificationGroup { - String owner; - String name; - get repo => owner + '/' + name; - List items = []; + String fullName; + List items = []; - // Add heading _ to fix number case - // - => __ - // . => ___ - String get key => - ('_' + owner + '_' + name).replaceAll('-', '__').replaceAll('.', '___'); + Tuple2 _repo; + String get owner { + if (_repo == null) { + _repo = parseRepositoryFullName(fullName); + } + return _repo.item1; + } - NotificationGroup(this.owner, this.name); + String get name { + if (_repo == null) { + _repo = parseRepositoryFullName(fullName); + } + return _repo.item2; + } + + String get key => '_$hashCode'; + + NotificationGroup(this.fullName); } class NotificationModel with ChangeNotifier { diff --git a/lib/screens/notification.dart b/lib/screens/notification.dart index e1f49c7..4ed3a71 100644 --- a/lib/screens/notification.dart +++ b/lib/screens/notification.dart @@ -7,6 +7,7 @@ import 'package:git_touch/widgets/app_bar_title.dart'; import 'package:provider/provider.dart'; import 'package:git_touch/models/notification.dart'; import 'package:git_touch/models/auth.dart'; +import 'package:git_touch/models/github.dart'; import '../widgets/notification_item.dart'; import '../widgets/list_group.dart'; import '../widgets/empty.dart'; @@ -21,7 +22,8 @@ class NotificationScreenState extends State { Future> fetchNotifications(int index) async { List items = await Provider.of(context).getWithCredentials( '/notifications?all=${index == 2}&participating=${index == 1}'); - var ns = items.map((item) => NotificationPayload.fromJson(item)).toList(); + final ns = + items.map((item) => GithubNotificationItem.fromJson(item)).toList(); if (index == 0) { Provider.of(context).setCount(ns.length); @@ -30,9 +32,9 @@ class NotificationScreenState extends State { Map _groupMap = {}; ns.forEach((item) { - String repo = item.owner + '/' + item.name; + final repo = item.repository.fullName; if (_groupMap[repo] == null) { - _groupMap[repo] = NotificationGroup(item.owner, item.name); + _groupMap[repo] = NotificationGroup(repo); } _groupMap[repo].items.add(item); @@ -44,7 +46,8 @@ class NotificationScreenState extends State { _groupMap.forEach((repo, group) { // Check if issue and pull request exist if (group.items.where((item) { - return item.type == 'Issue' || item.type == 'PullRequest'; + return item.subject.type == 'Issue' || + item.subject.type == 'PullRequest'; }).isEmpty) { return; } @@ -53,19 +56,17 @@ class NotificationScreenState extends State { '${group.key}: repository(owner: "${group.owner}", name: "${group.name}") {'; group.items.forEach((item) { - var key = item.key; - - switch (item.type) { + switch (item.subject.type) { case 'Issue': schema += ''' -$key: issue(number: ${item.number}) { +${item.key}: issue(number: ${item.subject.number}) { state } '''; break; case 'PullRequest': schema += ''' -$key: pullRequest(number: ${item.number}) { +${item.key}: pullRequest(number: ${item.subject.number}) { state } '''; @@ -102,20 +103,19 @@ $key: pullRequest(number: ${item.number}) { BuildContext context, MapEntry entry, Map groupMap) { - var group = entry.value; - var repo = group.repo; + final group = entry.value; return ListGroup( title: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - repo, + group.fullName, style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), ), GestureDetector( onTap: () async { await Provider.of(context) - .putWithCredentials('/repos/$repo/notifications'); + .putWithCredentials('/repos/${group.fullName}/notifications'); // await _onSwitchTab(); // TODO: }, child: Icon( diff --git a/lib/widgets/notification_item.dart b/lib/widgets/notification_item.dart index bb9169d..8952ad8 100644 --- a/lib/widgets/notification_item.dart +++ b/lib/widgets/notification_item.dart @@ -1,15 +1,15 @@ import 'package:fimber/fimber.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:git_touch/models/notification.dart'; import 'package:git_touch/widgets/issue_icon.dart'; -import '../utils/utils.dart'; +import 'package:git_touch/models/github.dart'; +import 'package:git_touch/utils/utils.dart'; import 'package:git_touch/models/auth.dart'; import 'package:provider/provider.dart'; -import 'link.dart'; +import 'package:git_touch/widgets/link.dart'; class NotificationItem extends StatefulWidget { - final NotificationPayload payload; + final GithubNotificationItem payload; final Function markAsRead; NotificationItem({ @@ -23,7 +23,7 @@ class NotificationItem extends StatefulWidget { } class _NotificationItemState extends State { - NotificationPayload get payload => widget.payload; + GithubNotificationItem get payload => widget.payload; bool loading = false; Widget _buildIcon(IconData data, [Color color = Colors.black54]) { @@ -31,7 +31,7 @@ class _NotificationItemState extends State { } Widget _buildIconData() { - switch (payload.type) { + switch (payload.subject.type) { case 'Issue': switch (payload.state) { case 'OPEN': @@ -60,7 +60,7 @@ class _NotificationItemState extends State { case 'Commit': return _buildIcon(Octicons.git_commit); default: - Fimber.d('Unhandled notification type: ${payload.type}'); + Fimber.d('Unhandled notification type: ${payload.subject.type}'); return _buildIcon(Octicons.octoface); } } @@ -80,7 +80,7 @@ class _NotificationItemState extends State { }); try { await Provider.of(context) - .patchWithCredentials('/notifications/threads/' + payload.id); + .patchWithCredentials('/notifications/threads/${payload.id}'); widget.markAsRead(); } finally { if (mounted) { @@ -93,13 +93,14 @@ class _NotificationItemState extends State { } String get _url { - switch (payload.type) { + switch (payload.subject.type) { case 'Issue': + return '/${payload.repository.owner}/${payload.repository.name}/issues/${payload.subject.number}'; case 'PullRequest': - final resource = payload.type == 'PullRequest' ? 'pulls' : 'issues'; - return '/${payload.owner}/${payload.name}/$resource/${payload.number}'; + return '/${payload.repository.owner}/${payload.repository.name}/pulls/${payload.subject.number}'; case 'Release': - return 'https://github.com/${payload.owner}/${payload.name}/releases/tag/${payload.title}'; + // TODO: title + // return 'https://github.com/${payload.repository.owner}/${payload.repository.name}/releases/tag/${payload.title}'; case 'Commit': return ''; default: @@ -124,7 +125,7 @@ class _NotificationItemState extends State { ), Expanded( child: Text( - payload.title, + payload.subject.title, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 15), ),