diff --git a/build.yaml b/build.yaml new file mode 100644 index 0000000..f122e36 --- /dev/null +++ b/build.yaml @@ -0,0 +1,23 @@ +targets: + $default: + builders: + json_serializable: + options: + # Options configure how source code is generated for every + # `@JsonSerializable`-annotated class in the package. + # + # The default value for each is listed. + # + # For usage information, reference the corresponding field in + # `JsonSerializableGenerator`. + # any_map: false + # checked: false + # create_factory: true + # create_to_json: true + # disallow_unrecognized_keys: false + # explicit_to_json: false + field_rename: snake + # generate_to_json_function: true + # include_if_null: true + # nullable: true + # use_wrappers: false diff --git a/lib/android/home.dart b/lib/android/home.dart index ac3974a..6a14eaa 100644 --- a/lib/android/home.dart +++ b/lib/android/home.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import '../common/event.dart'; +import '../components/event.dart'; import '../utils.dart'; import 'dart:async'; diff --git a/lib/common/event.dart b/lib/components/event.dart similarity index 50% rename from lib/common/event.dart rename to lib/components/event.dart index 20c1905..d169c0b 100644 --- a/lib/common/event.dart +++ b/lib/components/event.dart @@ -1,4 +1,5 @@ import '../utils.dart'; +import '../models/event.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/gestures.dart'; @@ -38,30 +39,92 @@ class EventItem extends StatelessWidget { final Event event; EventItem(this.event); - getEventItemByType() { + Widget getEventItemByType(BuildContext context) { switch (event.type) { case 'IssuesEvent': - return IssuesEvent(event); + return RichText( + text: TextSpan( + style: TextStyle(color: CupertinoColors.black), + children: [ + _user(event, context), + TextSpan(text: ' ${event.payload['action']} issue '), + _strong(event.repo.name), + TextSpan( + text: '#' + event.payload['issue']['number'].toString(), + ), + TextSpan( + text: event.payload['issue']['title'], + ) + ], + ), + ); case 'PushEvent': - return PushEvent(event); + return RichText( + text: TextSpan( + style: TextStyle(color: CupertinoColors.black), + children: [ + _user(event, context), + TextSpan(text: ' pushed to '), + TextSpan( + text: event.payload['ref'], + style: TextStyle(color: CupertinoColors.activeBlue), + ), + TextSpan(text: ' in '), + _strong(event.repo.name), + TextSpan(text: '') + ], + ), + ); case 'PullRequestEvent': - return PullRequestEvent(event); + return RichText( + text: TextSpan( + style: TextStyle(color: CupertinoColors.black), + children: [ + _user(event, context), + TextSpan(text: ' ${event.payload['action']} pull request '), + _strong(event.repo.name), + TextSpan(text: '#' + event.payload['number'].toString()), + TextSpan(text: event.payload['pull_request']['title']) + ], + ), + ); case 'WatchEvent': - return WatchEvent(event); + return RichText( + text: TextSpan( + style: TextStyle(color: CupertinoColors.black), + children: [ + _user(event, context), + TextSpan(text: ' ${event.payload['action']} '), + _strong(event.repo.name), + ], + ), + ); default: - return Text('Not implement yet'); + return Text( + 'Not implement yet', + style: TextStyle(color: CupertinoColors.destructiveRed), + ); } } @override build(context) { - // Padding(padding: EdgeInsets.only(top: 16.0)), - - return Row( - children: [ - _Avatar(event.avatar), - Expanded(child: getEventItemByType()), - ], + return Container( + padding: EdgeInsets.only(top: 16.0), + decoration: BoxDecoration( + border: Border( + top: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)), + left: BorderSide(width: 1.0, color: Color(0xFFFFFFFFFF)), + right: BorderSide(width: 1.0, color: Color(0xFFFF000000)), + bottom: BorderSide(width: 1.0, color: Color(0xFFFF000000)), + ), + ), + child: Row( + children: [ + _Avatar(event.actor.avatarUrl), + Expanded(child: getEventItemByType(context)), + ], + ), ); } } @@ -79,7 +142,7 @@ TextSpan _strong(String text, [GestureRecognizer recognizer]) { TextSpan _user(Event event, context) { return _strong( - event.actor, + event.actor.login, // TapGestureRecognizer() // ..onTap = () { // Navigator.of(context).push( @@ -93,31 +156,6 @@ TextSpan _user(Event event, context) { ); } -class PushEvent extends StatelessWidget { - final Event event; - PushEvent(this.event); - - @override - build(context) { - return RichText( - text: TextSpan( - style: TextStyle(color: CupertinoColors.black), - children: [ - _user(event, context), - TextSpan(text: ' pushed to '), - TextSpan( - text: event.payload['ref'], - style: TextStyle(color: CupertinoColors.activeBlue), - ), - TextSpan(text: ' in '), - _strong(event.repo), - TextSpan(text: '') - ], - ), - ); - } -} - class IssuesEvent extends StatelessWidget { final Event event; IssuesEvent(this.event); @@ -130,7 +168,7 @@ class IssuesEvent extends StatelessWidget { children: [ _user(event, context), TextSpan(text: ' ${event.payload['action']} issue '), - _strong(event.repo), + _strong(event.repo.name), TextSpan( text: '#' + event.payload['issue']['number'].toString(), ), @@ -143,27 +181,6 @@ class IssuesEvent extends StatelessWidget { } } -class PullRequestEvent extends StatelessWidget { - final Event event; - PullRequestEvent(this.event); - - @override - build(context) { - return RichText( - text: TextSpan( - style: TextStyle(color: CupertinoColors.black), - children: [ - _user(event, context), - TextSpan(text: ' ${event.payload['action']} pull request '), - _strong(event.repo), - TextSpan(text: '#' + event.payload['number'].toString()), - TextSpan(text: event.payload['pull_request']['title']) - ], - ), - ); - } -} - class IssueCommentEvent extends StatelessWidget { final Event event; IssueCommentEvent(this.event); @@ -176,7 +193,7 @@ class IssueCommentEvent extends StatelessWidget { children: [ _user(event, context), TextSpan(text: ' commented on issue '), - _strong(event.repo), + _strong(event.repo.name), TextSpan(text: '#' + event.payload['issue']['number'].toString()), TextSpan(text: event.payload['comment']['body']) ], @@ -184,22 +201,3 @@ class IssueCommentEvent extends StatelessWidget { ); } } - -class WatchEvent extends StatelessWidget { - final Event event; - WatchEvent(this.event); - - @override - build(context) { - return RichText( - text: TextSpan( - style: TextStyle(color: CupertinoColors.black), - children: [ - _user(event, context), - TextSpan(text: ' ${event.payload['action']} '), - _strong(event.repo), - ], - ), - ); - } -} diff --git a/lib/ios/home.dart b/lib/ios/home.dart index 46f081d..c3f918c 100644 --- a/lib/ios/home.dart +++ b/lib/ios/home.dart @@ -2,10 +2,12 @@ // import 'dart:convert'; import '../utils.dart'; import 'package:flutter/cupertino.dart'; -import 'package:flutter/gestures.dart'; +// import 'package:graphql_flutter/graphql_flutter.dart'; +// import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import '../common/event.dart'; -import 'user.dart'; +import '../components/event.dart'; +// import 'user.dart'; +import '../models/event.dart'; class IosHomeTab extends StatefulWidget { @override @@ -32,7 +34,7 @@ class IosHomeTabState extends State { } final GlobalKey _refreshIndicatorKey = - GlobalKey(); + GlobalKey(); @override Widget build(context) { @@ -60,7 +62,7 @@ class IosHomeTabState extends State { try { return EventItem(events[index]); } catch (err) { - return Text(err.toString()); + return Text(err.toString()); // return null; } }); diff --git a/lib/ios/user.dart b/lib/ios/user.dart index 6de402b..5656c54 100644 --- a/lib/ios/user.dart +++ b/lib/ios/user.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; +import '../models/user.dart'; import '../utils.dart'; class IosUserPage extends StatelessWidget { @@ -12,7 +13,7 @@ class IosUserPage extends StatelessWidget { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( leading: CupertinoButton( - child: Text('Cancel'), + child: Text('Cancel'), padding: EdgeInsets.zero, onPressed: () { Navigator.of(context).pop(false); diff --git a/lib/main.dart b/lib/main.dart index fee704d..fb62432 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,22 @@ import 'package:flutter/material.dart'; +// import 'dart:io'; +// import 'package:graphql_flutter/graphql_flutter.dart'; import 'android/main.dart'; import 'ios/main.dart'; +// import 'token.dart'; class App extends StatelessWidget { - final isIos = false; + final isIos = true; + + // final ValueNotifier client = ValueNotifier( + // GraphQLClient( + // cache: InMemoryCache(), + // link: HttpLink( + // uri: 'https://api.github.com/graphql', + // headers: {HttpHeaders.authorizationHeader: 'token $token'}, + // ), + // ), + // ); @override build(context) { diff --git a/lib/models/event.dart b/lib/models/event.dart new file mode 100644 index 0000000..7f8ae4b --- /dev/null +++ b/lib/models/event.dart @@ -0,0 +1,38 @@ +import 'package:json_annotation/json_annotation.dart'; + +part 'event.g.dart'; + +@JsonSerializable() +class Actor { + Actor(this.login, this.avatarUrl); + + String login; + String avatarUrl; + + factory Actor.fromJson(Map json) => _$ActorFromJson(json); + Map toJson() => _$ActorToJson(this); +} + +@JsonSerializable() +class Repo { + Repo(this.name); + + String name; + + factory Repo.fromJson(Map json) => _$RepoFromJson(json); + Map toJson() => _$RepoToJson(this); +} + +@JsonSerializable() +class Event { + Event(this.id, this.type, this.actor, this.repo); + + String id; + String type; + Actor actor; + Repo repo; + Map payload; + + factory Event.fromJson(Map json) => _$EventFromJson(json); + Map toJson() => _$EventToJson(this); +} diff --git a/lib/models/event.g.dart b/lib/models/event.g.dart new file mode 100644 index 0000000..00a819d --- /dev/null +++ b/lib/models/event.g.dart @@ -0,0 +1,44 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'event.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +Actor _$ActorFromJson(Map json) { + return Actor(json['login'] as String, json['avatar_url'] as String); +} + +Map _$ActorToJson(Actor instance) => { + 'login': instance.login, + 'avatar_url': instance.avatarUrl + }; + +Repo _$RepoFromJson(Map json) { + return Repo(json['name'] as String); +} + +Map _$RepoToJson(Repo instance) => + {'name': instance.name}; + +Event _$EventFromJson(Map json) { + return Event( + json['id'] as String, + json['type'] as String, + json['actor'] == null + ? null + : Actor.fromJson(json['actor'] as Map), + json['repo'] == null + ? null + : Repo.fromJson(json['repo'] as Map)) + ..payload = json['payload'] as Map; +} + +Map _$EventToJson(Event instance) => { + 'id': instance.id, + 'type': instance.type, + 'actor': instance.actor, + 'repo': instance.repo, + 'payload': instance.payload + }; diff --git a/lib/models/user.dart b/lib/models/user.dart new file mode 100644 index 0000000..840fb63 --- /dev/null +++ b/lib/models/user.dart @@ -0,0 +1,31 @@ +import 'package:json_annotation/json_annotation.dart'; + +/// This allows the `User` class to access private members in +/// the generated file. The value for this is *.g.dart, where +/// the star denotes the source file name. +part 'user.g.dart'; + +/// An annotation for the code generator to know that this class needs the +/// JSON serialization logic to be generated. +@JsonSerializable() +class User { + User(this.login, this.avatarUrl, this.name, this.publicRepos, this.followers, + this.following); + + String login; + String avatarUrl; + String name; + int publicRepos; + int followers; + int following; + + /// A necessary factory constructor for creating a new User instance + /// from a map. Pass the map to the generated `_$UserFromJson()` constructor. + /// The constructor is named after the source class, in this case User. + factory User.fromJson(Map json) => _$UserFromJson(json); + + /// `toJson` is the convention for a class to declare support for serialization + /// to JSON. The implementation simply calls the private, generated + /// helper method `_$UserToJson`. + Map toJson() => _$UserToJson(this); +} diff --git a/lib/models/user.g.dart b/lib/models/user.g.dart new file mode 100644 index 0000000..e4a411c --- /dev/null +++ b/lib/models/user.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'user.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +User _$UserFromJson(Map json) { + return User( + json['login'] as String, + json['avatar_url'] as String, + json['name'] as String, + json['public_repos'] as int, + json['followers'] as int, + json['following'] as int); +} + +Map _$UserToJson(User instance) => { + 'login': instance.login, + 'avatar_url': instance.avatarUrl, + 'name': instance.name, + 'public_repos': instance.publicRepos, + 'followers': instance.followers, + 'following': instance.following + }; diff --git a/lib/utils.dart b/lib/utils.dart index e2fd1bd..0f05675 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -3,50 +3,18 @@ import 'dart:async'; import 'dart:io'; import 'package:http/http.dart' as http; import 'token.dart'; +import 'models/event.dart'; +import 'models/user.dart'; final prefix = 'https://api.github.com'; -class Event { - String type; - String actor; - String id; - String avatar; - String repo; - Map payload; - - Event.fromJson(data) { - id = data['id']; - type = data['type']; - actor = data['actor']['login']; - avatar = data['actor']['avatar_url']; - repo = data['repo']['name']; - payload = data['payload']; - } -} - -class User { - String login; - String avatar; - String name; - int repos; - int followers; - int following; - - User.fromJson(data) { - login = data['login']; - avatar = data['avatar_url']; - name = data['name']; - repos = data['public_repos']; - followers = data['followers']; - following = data['following']; - } -} - Future> fetchEvents([int page = 1]) async { final res = await http.get( prefix + '/users/pd4d10/received_events/public?page=$page', - headers: {HttpHeaders.AUTHORIZATION: 'token $token'}, + headers: {HttpHeaders.authorizationHeader: 'token $token'}, ); + + print(res.body); List data = json.decode(res.body); return data.map((item) { @@ -57,7 +25,7 @@ Future> fetchEvents([int page = 1]) async { Future fetchUser(String login) async { final res = await http.get( prefix + '/users/$login', - headers: {HttpHeaders.AUTHORIZATION: 'token $token'}, + headers: {HttpHeaders.authorizationHeader: 'token $token'}, ); Map data = json.decode(res.body); diff --git a/pubspec.yaml b/pubspec.yaml index 26bfb9d..fecef9c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,11 +20,15 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^0.1.2 http: ^0.11.3 + graphql_flutter: ^1.0.0-alpha dev_dependencies: flutter_test: sdk: flutter + build_runner: ^1.1.3 + json_serializable: ^2.0.1 + # For information on the generic Dart part of this file, see the # following page: https://www.dartlang.org/tools/pub/pubspec