1
0
mirror of https://github.com/git-touch/git-touch synced 2025-03-23 22:50:05 +01:00

refactor: use hash code as key

This commit is contained in:
Rongjian Zhang 2019-12-26 18:00:36 +08:00
parent 785c4368b6
commit 4099baaadb
5 changed files with 173 additions and 71 deletions

View File

@ -127,6 +127,73 @@ class GithubEventRelease {
_$GithubEventReleaseFromJson(json); _$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<String, dynamic> 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<String, dynamic> json) =>
_$GithubNotificationItemSubjectFromJson(json);
}
@JsonSerializable(fieldRename: FieldRename.snake)
class GithubNotificationItemRepo {
String fullName;
Tuple2<String, String> _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<String, dynamic> json) =>
_$GithubNotificationItemRepoFromJson(json);
}
// Trending
@JsonSerializable() @JsonSerializable()
class GithubTrendingItem { class GithubTrendingItem {
String author; String author;

View File

@ -164,6 +164,63 @@ Map<String, dynamic> _$GithubEventReleaseToJson(GithubEventRelease instance) =>
'tag_name': instance.tagName, 'tag_name': instance.tagName,
}; };
GithubNotificationItem _$GithubNotificationItemFromJson(
Map<String, dynamic> 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<String, dynamic>)
..updatedAt = json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String)
..repository = json['repository'] == null
? null
: GithubNotificationItemRepo.fromJson(
json['repository'] as Map<String, dynamic>)
..unread = json['unread'] as bool;
}
Map<String, dynamic> _$GithubNotificationItemToJson(
GithubNotificationItem instance) =>
<String, dynamic>{
'id': instance.id,
'html_url': instance.htmlUrl,
'subject': instance.subject,
'updated_at': instance.updatedAt?.toIso8601String(),
'repository': instance.repository,
'unread': instance.unread,
};
GithubNotificationItemSubject _$GithubNotificationItemSubjectFromJson(
Map<String, dynamic> json) {
return GithubNotificationItemSubject()
..title = json['title'] as String
..type = json['type'] as String
..url = json['url'] as String;
}
Map<String, dynamic> _$GithubNotificationItemSubjectToJson(
GithubNotificationItemSubject instance) =>
<String, dynamic>{
'title': instance.title,
'type': instance.type,
'url': instance.url,
};
GithubNotificationItemRepo _$GithubNotificationItemRepoFromJson(
Map<String, dynamic> json) {
return GithubNotificationItemRepo()..fullName = json['full_name'] as String;
}
Map<String, dynamic> _$GithubNotificationItemRepoToJson(
GithubNotificationItemRepo instance) =>
<String, dynamic>{
'full_name': instance.fullName,
};
GithubTrendingItem _$GithubTrendingItemFromJson(Map<String, dynamic> json) { GithubTrendingItem _$GithubTrendingItemFromJson(Map<String, dynamic> json) {
return GithubTrendingItem() return GithubTrendingItem()
..author = json['author'] as String ..author = json['author'] as String

View File

@ -1,54 +1,31 @@
import 'package:flutter/material.dart'; 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 { import '../utils/utils.dart';
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'];
}
}
class NotificationGroup { class NotificationGroup {
String owner; String fullName;
String name; List<GithubNotificationItem> items = [];
get repo => owner + '/' + name;
List<NotificationPayload> items = [];
// Add heading _ to fix number case Tuple2<String, String> _repo;
// - => __ String get owner {
// . => ___ if (_repo == null) {
String get key => _repo = parseRepositoryFullName(fullName);
('_' + owner + '_' + name).replaceAll('-', '__').replaceAll('.', '___'); }
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 { class NotificationModel with ChangeNotifier {

View File

@ -7,6 +7,7 @@ import 'package:git_touch/widgets/app_bar_title.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:git_touch/models/notification.dart'; import 'package:git_touch/models/notification.dart';
import 'package:git_touch/models/auth.dart'; import 'package:git_touch/models/auth.dart';
import 'package:git_touch/models/github.dart';
import '../widgets/notification_item.dart'; import '../widgets/notification_item.dart';
import '../widgets/list_group.dart'; import '../widgets/list_group.dart';
import '../widgets/empty.dart'; import '../widgets/empty.dart';
@ -21,7 +22,8 @@ class NotificationScreenState extends State<NotificationScreen> {
Future<Map<String, NotificationGroup>> fetchNotifications(int index) async { Future<Map<String, NotificationGroup>> fetchNotifications(int index) async {
List items = await Provider.of<AuthModel>(context).getWithCredentials( List items = await Provider.of<AuthModel>(context).getWithCredentials(
'/notifications?all=${index == 2}&participating=${index == 1}'); '/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) { if (index == 0) {
Provider.of<NotificationModel>(context).setCount(ns.length); Provider.of<NotificationModel>(context).setCount(ns.length);
@ -30,9 +32,9 @@ class NotificationScreenState extends State<NotificationScreen> {
Map<String, NotificationGroup> _groupMap = {}; Map<String, NotificationGroup> _groupMap = {};
ns.forEach((item) { ns.forEach((item) {
String repo = item.owner + '/' + item.name; final repo = item.repository.fullName;
if (_groupMap[repo] == null) { if (_groupMap[repo] == null) {
_groupMap[repo] = NotificationGroup(item.owner, item.name); _groupMap[repo] = NotificationGroup(repo);
} }
_groupMap[repo].items.add(item); _groupMap[repo].items.add(item);
@ -44,7 +46,8 @@ class NotificationScreenState extends State<NotificationScreen> {
_groupMap.forEach((repo, group) { _groupMap.forEach((repo, group) {
// Check if issue and pull request exist // Check if issue and pull request exist
if (group.items.where((item) { if (group.items.where((item) {
return item.type == 'Issue' || item.type == 'PullRequest'; return item.subject.type == 'Issue' ||
item.subject.type == 'PullRequest';
}).isEmpty) { }).isEmpty) {
return; return;
} }
@ -53,19 +56,17 @@ class NotificationScreenState extends State<NotificationScreen> {
'${group.key}: repository(owner: "${group.owner}", name: "${group.name}") {'; '${group.key}: repository(owner: "${group.owner}", name: "${group.name}") {';
group.items.forEach((item) { group.items.forEach((item) {
var key = item.key; switch (item.subject.type) {
switch (item.type) {
case 'Issue': case 'Issue':
schema += ''' schema += '''
$key: issue(number: ${item.number}) { ${item.key}: issue(number: ${item.subject.number}) {
state state
} }
'''; ''';
break; break;
case 'PullRequest': case 'PullRequest':
schema += ''' schema += '''
$key: pullRequest(number: ${item.number}) { ${item.key}: pullRequest(number: ${item.subject.number}) {
state state
} }
'''; ''';
@ -102,20 +103,19 @@ $key: pullRequest(number: ${item.number}) {
BuildContext context, BuildContext context,
MapEntry<String, NotificationGroup> entry, MapEntry<String, NotificationGroup> entry,
Map<String, NotificationGroup> groupMap) { Map<String, NotificationGroup> groupMap) {
var group = entry.value; final group = entry.value;
var repo = group.repo;
return ListGroup( return ListGroup(
title: Row( title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[ children: <Widget>[
Text( Text(
repo, group.fullName,
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600), style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
), ),
GestureDetector( GestureDetector(
onTap: () async { onTap: () async {
await Provider.of<AuthModel>(context) await Provider.of<AuthModel>(context)
.putWithCredentials('/repos/$repo/notifications'); .putWithCredentials('/repos/${group.fullName}/notifications');
// await _onSwitchTab(); // TODO: // await _onSwitchTab(); // TODO:
}, },
child: Icon( child: Icon(

View File

@ -1,15 +1,15 @@
import 'package:fimber/fimber.dart'; import 'package:fimber/fimber.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:git_touch/models/notification.dart';
import 'package:git_touch/widgets/issue_icon.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:git_touch/models/auth.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'link.dart'; import 'package:git_touch/widgets/link.dart';
class NotificationItem extends StatefulWidget { class NotificationItem extends StatefulWidget {
final NotificationPayload payload; final GithubNotificationItem payload;
final Function markAsRead; final Function markAsRead;
NotificationItem({ NotificationItem({
@ -23,7 +23,7 @@ class NotificationItem extends StatefulWidget {
} }
class _NotificationItemState extends State<NotificationItem> { class _NotificationItemState extends State<NotificationItem> {
NotificationPayload get payload => widget.payload; GithubNotificationItem get payload => widget.payload;
bool loading = false; bool loading = false;
Widget _buildIcon(IconData data, [Color color = Colors.black54]) { Widget _buildIcon(IconData data, [Color color = Colors.black54]) {
@ -31,7 +31,7 @@ class _NotificationItemState extends State<NotificationItem> {
} }
Widget _buildIconData() { Widget _buildIconData() {
switch (payload.type) { switch (payload.subject.type) {
case 'Issue': case 'Issue':
switch (payload.state) { switch (payload.state) {
case 'OPEN': case 'OPEN':
@ -60,7 +60,7 @@ class _NotificationItemState extends State<NotificationItem> {
case 'Commit': case 'Commit':
return _buildIcon(Octicons.git_commit); return _buildIcon(Octicons.git_commit);
default: default:
Fimber.d('Unhandled notification type: ${payload.type}'); Fimber.d('Unhandled notification type: ${payload.subject.type}');
return _buildIcon(Octicons.octoface); return _buildIcon(Octicons.octoface);
} }
} }
@ -80,7 +80,7 @@ class _NotificationItemState extends State<NotificationItem> {
}); });
try { try {
await Provider.of<AuthModel>(context) await Provider.of<AuthModel>(context)
.patchWithCredentials('/notifications/threads/' + payload.id); .patchWithCredentials('/notifications/threads/${payload.id}');
widget.markAsRead(); widget.markAsRead();
} finally { } finally {
if (mounted) { if (mounted) {
@ -93,13 +93,14 @@ class _NotificationItemState extends State<NotificationItem> {
} }
String get _url { String get _url {
switch (payload.type) { switch (payload.subject.type) {
case 'Issue': case 'Issue':
return '/${payload.repository.owner}/${payload.repository.name}/issues/${payload.subject.number}';
case 'PullRequest': case 'PullRequest':
final resource = payload.type == 'PullRequest' ? 'pulls' : 'issues'; return '/${payload.repository.owner}/${payload.repository.name}/pulls/${payload.subject.number}';
return '/${payload.owner}/${payload.name}/$resource/${payload.number}';
case 'Release': 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': case 'Commit':
return ''; return '';
default: default:
@ -124,7 +125,7 @@ class _NotificationItemState extends State<NotificationItem> {
), ),
Expanded( Expanded(
child: Text( child: Text(
payload.title, payload.subject.title,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15), style: TextStyle(fontSize: 15),
), ),