From 4e2860871402a4e0cfa6c076c6260b75780ff8d6 Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Sat, 26 Jan 2019 22:10:18 +0800 Subject: [PATCH] feat: style of home and notification screen --- lib/ios/home.dart | 110 +++++++---- lib/ios/notification.dart | 95 +++++----- lib/ios/user.dart | 6 +- lib/main.dart | 5 +- lib/providers/event.dart | 28 +-- lib/providers/notification.dart | 30 +-- lib/providers/search.dart | 2 +- lib/providers/user.dart | 4 +- lib/screens/screens.dart | 3 + lib/utils.dart | 43 ----- lib/utils/github.dart | 72 +++++++ lib/{ => utils}/octicons.dart | 0 lib/utils/timeago.dart | 25 +++ lib/utils/utils.dart | 3 + lib/widgets/event.dart | 325 +++++++++++++++----------------- lib/widgets/user.dart | 2 +- pubspec.yaml | 2 + 17 files changed, 389 insertions(+), 366 deletions(-) create mode 100644 lib/screens/screens.dart delete mode 100644 lib/utils.dart create mode 100644 lib/utils/github.dart rename lib/{ => utils}/octicons.dart (100%) create mode 100644 lib/utils/timeago.dart create mode 100644 lib/utils/utils.dart diff --git a/lib/ios/home.dart b/lib/ios/home.dart index 8d10ec8..ab82cc0 100644 --- a/lib/ios/home.dart +++ b/lib/ios/home.dart @@ -1,8 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import '../widgets/event.dart'; -import '../providers/event.dart'; -import '../models/event.dart'; +import 'package:git_flux/utils/utils.dart'; class HomeScreen extends StatefulWidget { @override @@ -14,6 +13,10 @@ class HomeScreen extends StatefulWidget { class HomeScreenState extends State { // final GlobalKey _refreshIndicatorKey = // GlobalKey(); + int page = 1; + bool loading = false; + List events = []; + ScrollController _controller = ScrollController(); @override void initState() { @@ -22,50 +25,79 @@ class HomeScreenState extends State { // Future.delayed(Duration(seconds: 0)).then((_) { // EventProvider.of(context).update.add(true); // }); + _refresh(); + _controller.addListener(() { + if (_controller.offset + 100 > _controller.position.maxScrollExtent && + !_controller.position.outOfRange && + !loading) { + _loadMore(); + } + }); + } + + Future _refresh() async { + setState(() { + loading = true; + }); + page = 1; + var items = await fetchEvents(page); + setState(() { + loading = false; + events = items; + }); + } + + Future _loadMore() async { + print('more'); + setState(() { + loading = true; + }); + page = page + 1; + var items = await fetchEvents(page); + setState(() { + loading = false; + events.addAll(items); + }); + } + + Widget _buildBottomIndicator() { + return Padding( + padding: EdgeInsets.symmetric(vertical: 15), + child: CupertinoActivityIndicator(radius: 12)); } @override Widget build(context) { // Navigator.of(context).pushNamed('/user'); - final eventBloc = EventProvider.of(context); + // final eventBloc = EventProvider.of(context); return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar( - middle: Text("GitFlux"), - ), - child: StreamBuilder>( - stream: eventBloc.events, - builder: (context, snapshot) { - // print(snapshot.data); - Widget widget; - if (snapshot.hasData) { - // List events = snapshot.data; - // ScrollController(debugLabel: 'aaa', ) - - // widget = CupertinoRefreshControl( - // // key: _refreshIndicatorKey, - // onRefresh: () { - // print(1); - // loadFirst(); - // }, - // ); - widget = ListView.builder(itemBuilder: (context, index) { - // print(index); - try { - return EventItem(snapshot.data[index]); - } catch (err) { - return Text(err.toString()); -// return null; - } - }); - } else if (snapshot.hasError) { - widget = Text("${snapshot.error}"); - } else { - widget = CupertinoActivityIndicator(); - } - - return widget; - }, + child: CustomScrollView( + controller: _controller, + slivers: [ + CupertinoSliverNavigationBar( + largeTitle: const Text('News'), + trailing: Icon(Octicons.settings), + ), + CupertinoSliverRefreshControl( + onRefresh: _refresh, + ), + SliverSafeArea( + top: false, + sliver: SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (index == events.length) { + return _buildBottomIndicator(); + } else { + return EventItem(events[index]); + } + }, + childCount: events.length + 1, + ), + ), + ), + ], ), ); } diff --git a/lib/ios/notification.dart b/lib/ios/notification.dart index c6abb83..884a947 100644 --- a/lib/ios/notification.dart +++ b/lib/ios/notification.dart @@ -1,9 +1,9 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/cupertino.dart'; -import 'package:git_flux/models/notification.dart'; +import 'dart:core'; +import 'package:flutter/material.dart' hide Notification; +import 'package:flutter/cupertino.dart' hide Notification; import 'package:git_flux/providers/notification.dart'; -import 'package:git_flux/screens/issue.dart'; -import 'package:git_flux/octicons.dart'; +import 'package:git_flux/screens/screens.dart'; +import 'package:git_flux/utils/utils.dart'; class NotificationScreen extends StatefulWidget { @override @@ -11,11 +11,6 @@ class NotificationScreen extends StatefulWidget { } class NotificationScreenState extends State { - initState() { - super.initState(); - // initFetch(); - } - Widget _getRouteWidget(String type) { switch (type) { case 'Issue': @@ -39,7 +34,7 @@ class NotificationScreenState extends State { } } - Widget _buildItem(BuildContext context, NotificationItem item) { + Widget _buildItem(BuildContext context, Notification item) { return Material( child: InkWell( splashColor: Colors.transparent, @@ -59,31 +54,36 @@ class NotificationScreenState extends State { ), Expanded( child: Container( - padding: EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( border: Border(bottom: BorderSide(color: Colors.grey))), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Row( children: [ - Text(item.subject.title, style: TextStyle(height: 1)), - Container( - padding: EdgeInsets.only(top: 4), - child: Text( - item.updatedAt, - style: TextStyle(fontSize: 12), + Expanded( + child: Container( + padding: EdgeInsets.symmetric(vertical: 8), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item.subject.title, + style: TextStyle(height: 1)), + Padding(padding: EdgeInsets.only(top: 4)), + Text(TimeAgo.format(item.updatedAt), + style: TextStyle(fontSize: 12)) + ], + ), ), - ) + ), + Container( + padding: EdgeInsets.symmetric(horizontal: 8), + child: Icon( + CupertinoIcons.right_chevron, + color: CupertinoColors.inactiveGray, + ), + ), ], ), ), ), - Container( - padding: EdgeInsets.symmetric(horizontal: 8), - child: Icon( - CupertinoIcons.right_chevron, - color: CupertinoColors.inactiveGray, - ), - ), ], ), ), @@ -147,31 +147,22 @@ class NotificationScreenState extends State { ), child: Column( children: [ - StreamBuilder( - stream: bloc.loading, - builder: (context, snapshot) { - return Flexible( - child: snapshot.data == null || snapshot.data - ? CupertinoActivityIndicator() - : StreamBuilder>( - stream: bloc.items, - builder: (context, snapshot) { - if (snapshot.data == null) { - return Text('loading...'); - } + Container( + child: StreamBuilder>( + stream: bloc.items, + builder: (context, snapshot) { + if (snapshot.data == null) { + return Text('loading...'); + } - List groups = snapshot.data; - - return ListView( - children: groups - .map((group) => - _buildGroupItem(context, group)) - .toList()); - }, - ), - ); - }, - ), + return ListView( + shrinkWrap: true, + children: snapshot.data + .map((group) => _buildGroupItem(context, group)) + .toList()); + }, + ), + ) ], ), ); diff --git a/lib/ios/user.dart b/lib/ios/user.dart index 5656c54..89ce263 100644 --- a/lib/ios/user.dart +++ b/lib/ios/user.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import '../models/user.dart'; -import '../utils.dart'; +import 'package:git_flux/utils/utils.dart'; class IosUserPage extends StatelessWidget { String login; @@ -22,11 +22,11 @@ class IosUserPage extends StatelessWidget { middle: Text(login), ), child: FutureBuilder( - future: fetchUser(login), + // future: fetchUser(login), builder: (context, snapshot) { Widget widget; if (snapshot.hasData) { - User user = snapshot.data; + // User user = snapshot.data; return Text(''); } else if (snapshot.hasError) { widget = Text("${snapshot.error}"); diff --git a/lib/main.dart b/lib/main.dart index ab99aa3..20c23d0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,9 @@ -// import 'package:uri/uri.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; import 'package:git_flux/providers/event.dart'; import 'package:git_flux/providers/notification.dart'; import 'package:git_flux/providers/search.dart'; import 'package:git_flux/ios/main.dart'; -// import 'package:git_flux/screens/user.dart'; -// import 'package:git_flux/screens/repo.dart'; class App extends StatelessWidget { final isIos = true; @@ -26,7 +23,7 @@ class App extends StatelessWidget { bloc: eventBloc, child: MaterialApp( home: DefaultTextStyle( - style: TextStyle(color: CupertinoColors.black), + style: TextStyle(color: Color(0xff24292e)), child: IosHomePage(title: 'GitFlux'), ), // theme: ThemeData( diff --git a/lib/providers/event.dart b/lib/providers/event.dart index 8730cfb..1fa7471 100644 --- a/lib/providers/event.dart +++ b/lib/providers/event.dart @@ -3,16 +3,6 @@ import 'dart:async'; import 'package:rxdart/subjects.dart'; import '../models/event.dart'; import 'package:rxdart/rxdart.dart'; -import '../utils.dart'; - -Future> fetchEvents([int page = 1]) async { - List data = await getWithCredentials( - '/users/pd4d10/received_events/public?page=$page', - ); - return data.map((item) { - return Event.fromJson(item); - }).toList(); -} class EventBloc { final _items = BehaviorSubject>(seedValue: []); @@ -24,15 +14,15 @@ class EventBloc { Sink get update => _update.sink; EventBloc() { - _update.stream.listen((bool isRefresh) async { - if (isRefresh) { - _items.add(await fetchEvents()); - _page.add(1); - } else { - _items.add(await fetchEvents()); - _page.add(_page.value + 1); - } - }); + // _update.stream.listen((bool isRefresh) async { + // if (isRefresh) { + // _items.add(await fetchEvents(1)); + // _page.add(1); + // } else { + // _items.add(await fetchEvents(1)); + // _page.add(_page.value + 1); + // } + // }); } void dispose() { diff --git a/lib/providers/notification.dart b/lib/providers/notification.dart index 6ce4830..ddee2ba 100644 --- a/lib/providers/notification.dart +++ b/lib/providers/notification.dart @@ -1,35 +1,7 @@ import 'package:flutter/widgets.dart'; import 'package:rxdart/subjects.dart'; import 'package:rxdart/rxdart.dart'; -import '../utils.dart'; -import '../models/notification.dart'; - -class NotificationGroup { - String fullName; - List items = []; - - NotificationGroup(this.fullName); -} - -Future> fetchNotifications([int index = 0]) async { - String search = ''; - if (index == 1) { - search = '?paticipating=true'; - } else if (index == 2) { - search = '?all=true'; - } - List data = await getWithCredentials('/notifications$search'); - Map groupMap = {}; - data.forEach((item) { - String repo = item['repository']['full_name']; - if (groupMap[repo] == null) { - groupMap[repo] = NotificationGroup(repo); - } - - groupMap[repo].items.add(NotificationItem.fromJson(item)); - }); - return groupMap.values.toList(); -} +import 'package:git_flux/utils/utils.dart'; class NotificationBloc { final _groups = BehaviorSubject>(seedValue: []); diff --git a/lib/providers/search.dart b/lib/providers/search.dart index 87d8f8b..85900de 100644 --- a/lib/providers/search.dart +++ b/lib/providers/search.dart @@ -2,7 +2,7 @@ import 'package:flutter/widgets.dart'; import 'dart:async'; import 'package:rxdart/subjects.dart'; import 'package:rxdart/rxdart.dart'; -import '../utils.dart'; +import 'package:git_flux/utils/utils.dart'; Future search(String keyword, String type) async { var data = await query(''' diff --git a/lib/providers/user.dart b/lib/providers/user.dart index 9b7c59c..04b5e24 100644 --- a/lib/providers/user.dart +++ b/lib/providers/user.dart @@ -1,8 +1,8 @@ -import 'package:flutter/widgets.dart'; import 'dart:async'; +import 'package:flutter/widgets.dart'; import 'package:rxdart/subjects.dart'; import 'package:rxdart/rxdart.dart'; -import '../utils.dart'; +import 'package:git_flux/utils/utils.dart'; Future queryUser(String login) async { var data = await query(''' diff --git a/lib/screens/screens.dart b/lib/screens/screens.dart new file mode 100644 index 0000000..7a93a9c --- /dev/null +++ b/lib/screens/screens.dart @@ -0,0 +1,3 @@ +export 'repo.dart'; +export 'user.dart'; +export 'issue.dart'; diff --git a/lib/utils.dart b/lib/utils.dart deleted file mode 100644 index 69c5540..0000000 --- a/lib/utils.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:convert'; -import 'dart:async'; -import 'dart:io'; -import 'package:http/http.dart' as http; -import 'token.dart'; -import 'models/user.dart'; - -final prefix = 'https://api.github.com'; -final endpoint = '/graphql'; - -Future getWithCredentials(String url) async { - final res = await http.get( - prefix + url, - headers: {HttpHeaders.authorizationHeader: 'token $token'}, - ); - final data = json.decode(res.body); - return data; -} - -Future postWithCredentials(String url, String body) async { - final res = await http.post( - prefix + url, - headers: {HttpHeaders.authorizationHeader: 'token $token'}, - body: body, - ); - final data = json.decode(res.body); - return data; -} - -Future query(String query) async { - final data = - await postWithCredentials('/graphql', json.encode({'query': query})); - if (data['errors'] != null) { - throw new Exception(data['errors'].toString()); - } - print(data); - return data['data']; -} - -Future fetchUser(String login) async { - Map data = await getWithCredentials('/users/$login'); - return User.fromJson(data); -} diff --git a/lib/utils/github.dart b/lib/utils/github.dart new file mode 100644 index 0000000..3a95869 --- /dev/null +++ b/lib/utils/github.dart @@ -0,0 +1,72 @@ +import 'dart:convert'; +import 'dart:async'; +import 'dart:io'; +import 'package:http/http.dart' as http; +import 'package:github/server.dart'; +export 'package:github/server.dart'; +import '../token.dart'; + +var ghClient = createGitHubClient(auth: Authentication.withToken(token)); + +final prefix = 'https://api.github.com'; +final endpoint = '/graphql'; + +Future getWithCredentials(String url) async { + final res = await http.get( + prefix + url, + headers: {HttpHeaders.authorizationHeader: 'token $token'}, + ); + final data = json.decode(res.body); + return data; +} + +Future postWithCredentials(String url, String body) async { + final res = await http.post( + prefix + url, + headers: {HttpHeaders.authorizationHeader: 'token $token'}, + body: body, + ); + final data = json.decode(res.body); + return data; +} + +Future query(String query) async { + final data = + await postWithCredentials('/graphql', json.encode({'query': query})); + if (data['errors'] != null) { + throw new Exception(data['errors'].toString()); + } + print(data); + return data['data']; +} + +Future> fetchEvents(int page) async { + List data = await getWithCredentials( + '/users/pd4d10/received_events/public?page=$page', + ); + return data.map((item) => Event.fromJSON(item)).toList(); +} + +class NotificationGroup { + String fullName; + List items = []; + + NotificationGroup(this.fullName); +} + +Future> fetchNotifications([int index = 0]) async { + var data = await ghClient.activity + .listNotifications(all: index == 2, participating: index == 1) + .toList(); + + Map groupMap = {}; + data.forEach((item) { + String repo = item.repository.fullName; + if (groupMap[repo] == null) { + groupMap[repo] = NotificationGroup(repo); + } + + groupMap[repo].items.add(item); + }); + return groupMap.values.toList(); +} diff --git a/lib/octicons.dart b/lib/utils/octicons.dart similarity index 100% rename from lib/octicons.dart rename to lib/utils/octicons.dart diff --git a/lib/utils/timeago.dart b/lib/utils/timeago.dart new file mode 100644 index 0000000..b5f9a08 --- /dev/null +++ b/lib/utils/timeago.dart @@ -0,0 +1,25 @@ +import 'dart:core'; + +class TimeAgo { + static String _ceil(double n) => n.ceil().toString(); + + static String _pluralize(double time, String unit) { + if (time == 1) { + return '${_ceil(time)} $unit ago'; + } + return '${_ceil(time)} ${unit}s ago'; + } + + static String format(DateTime time) { + double diff = + (DateTime.now().millisecondsSinceEpoch - time.millisecondsSinceEpoch) / + 1000; + if (diff < 3600) { + return _pluralize(diff / 60, 'minute'); + } else if (diff < 86400) { + return _pluralize(diff / 3600, 'hour'); + } else { + return _pluralize(diff / 86400, 'day'); + } + } +} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart new file mode 100644 index 0000000..1a7015b --- /dev/null +++ b/lib/utils/utils.dart @@ -0,0 +1,3 @@ +export 'github.dart'; +export 'octicons.dart'; +export 'timeago.dart'; diff --git a/lib/widgets/event.dart b/lib/widgets/event.dart index d169c0b..007bb41 100644 --- a/lib/widgets/event.dart +++ b/lib/widgets/event.dart @@ -1,201 +1,180 @@ -import '../utils.dart'; -import '../models/event.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter/gestures.dart'; -// import '../issue.dart'; -// import '../user.dart'; - -// class Strong extends StatelessWidget { -// final String text; - -// @override -// build(context) { -// return TextSpan( -// text: text, -// style: TextStyle( -// fontWeight: FontWeight.bold, -// color: Color(0xff24292e), -// ), -// // recognizer: recognizer, -// ); -// } -// } - -class _Avatar extends StatelessWidget { - final String url; - _Avatar(this.url); - - @override - build(context) { - return CircleAvatar( - backgroundImage: NetworkImage(url), - radius: 24.0, - ); - } -} +import 'package:git_flux/screens/screens.dart'; +import 'package:git_flux/utils/utils.dart'; +/// Events types: +/// +/// https://developer.github.com/v3/activity/events/types/#event-types--payloads class EventItem extends StatelessWidget { final Event event; EventItem(this.event); - Widget getEventItemByType(BuildContext context) { + TextSpan _buildEvent(BuildContext context) { switch (event.type) { case 'IssuesEvent': - 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'], - ) - ], - ), - ); + return TextSpan(children: [ + TextSpan(text: ' ${event.payload['action']} issue '), + _buildIssue(context), + TextSpan(text: ' at '), + _buildRepo(context), + ]); case 'PushEvent': - 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: '') - ], + return TextSpan(children: [ + TextSpan(text: ' pushed to '), + TextSpan( + text: event.payload['ref'], + style: TextStyle(color: CupertinoColors.activeBlue), ), - ); + TextSpan(text: ' at '), + _buildRepo(context), + TextSpan(text: '') + ]); case 'PullRequestEvent': - 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']) - ], - ), - ); + return TextSpan(children: [ + TextSpan(text: ' ${event.payload['action']} pull request '), + _buildPullRequest(context), + TextSpan(text: ' at '), + _buildRepo(context), + ]); + case 'PullRequestReviewCommentEvent': + return TextSpan(children: [ + TextSpan(text: ' reviewed pull request '), + _buildPullRequest(context), + TextSpan(text: ' at '), + _buildRepo(context), + ]); case 'WatchEvent': - return RichText( - text: TextSpan( - style: TextStyle(color: CupertinoColors.black), - children: [ - _user(event, context), - TextSpan(text: ' ${event.payload['action']} '), - _strong(event.repo.name), - ], - ), - ); + return TextSpan(children: [ + TextSpan(text: ' ${event.payload['action']} '), + _buildRepo(context) + ]); + case 'IssueCommentEvent': + return TextSpan(children: [ + TextSpan(text: ' commented on issue '), + _buildIssue(context), + TextSpan(text: ' at '), + _buildRepo(context), + // TextSpan(text: event.payload['comment']['body']) + ]); default: - return Text( - 'Not implement yet', + return TextSpan( + text: 'Type ${event.type} Not implement yet', style: TextStyle(color: CupertinoColors.destructiveRed), ); } } + String _buildOriginalComment() { + switch (event.type) { + case 'IssueCommentEvent': + return event.payload['comment']['body']; + case 'IssuesEvent': + return event.payload['issue']['title']; + case 'PullRequestEvent': + return event.payload['pull_request']['title']; + case 'PullRequestReviewCommentEvent': + return event.payload['comment']['body']; + default: + return ''; + } + } + + String _buildComment() { + return _buildOriginalComment(); + } + + TextSpan _buildLink(BuildContext context, String text, Function handle) { + return TextSpan( + text: text, + style: TextStyle(color: Color(0xff0366d6)), + recognizer: TapGestureRecognizer() + ..onTap = () { + Navigator.of(context).push( + CupertinoPageRoute( + builder: (context) { + return handle(); + }, + ), + ); + }, + ); + } + + TextSpan _buildRepo(BuildContext context) { + return _buildLink(context, event.repo.name, () => RepoScreen()); + } + + TextSpan _buildIssue(BuildContext context) { + return _buildLink(context, + '#' + event.payload['issue']['number'].toString(), () => UserScreen()); + } + + TextSpan _buildPullRequest(BuildContext context) { + return _buildLink( + context, + '#' + event.payload['pull_request']['number'].toString(), + () => UserScreen()); + } + + IconData _buildIconData(BuildContext context) { + switch (event.type) { + case 'IssueCommentEvent': + return Octicons.comment_discussion; + case 'IssuesEvent': + return Octicons.issue_opened; + case 'PullRequestEvent': + return Octicons.git_pull_request; + case 'PushEvent': + return Octicons.repo_push; + case 'WatchEvent': + return Octicons.star; + default: + return Octicons.octoface; + } + } + @override build(context) { return Container( - padding: EdgeInsets.only(top: 16.0), + padding: EdgeInsets.all(10), 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)), - ], - ), - ); - } -} - -TextSpan _strong(String text, [GestureRecognizer recognizer]) { - return TextSpan( - text: text, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Color(0xff24292e), - ), - recognizer: recognizer, - ); -} - -TextSpan _user(Event event, context) { - return _strong( - event.actor.login, - // TapGestureRecognizer() - // ..onTap = () { - // Navigator.of(context).push( - // CupertinoPageRoute( - // builder: (context) { - // return IosUserPage(event.actor, event.avatar); - // }, - // ), - // ); - // }, - ); -} - -class IssuesEvent extends StatelessWidget { - final Event event; - IssuesEvent(this.event); - - @override - build(context) { - 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(), + border: Border( + bottom: BorderSide(color: CupertinoColors.lightBackgroundGray))), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + CircleAvatar( + backgroundColor: Colors.transparent, + backgroundImage: NetworkImage(event.actor.avatarUrl), + radius: 16, + ), + Padding(padding: EdgeInsets.only(left: 10)), + Expanded( + child: RichText( + text: TextSpan( + style: TextStyle(color: Color(0xff24292e), height: 1.2), + children: [ + _buildLink( + context, event.actor.login, () => UserScreen()), + _buildEvent(context), + ], + ), + ), + ), + Padding(padding: EdgeInsets.only(left: 10)), + Icon(_buildIconData(context), + color: CupertinoColors.inactiveGray), + ], ), - TextSpan( - text: event.payload['issue']['title'], - ) - ], - ), - ); - } -} - -class IssueCommentEvent extends StatelessWidget { - final Event event; - IssueCommentEvent(this.event); - - @override - build(context) { - return RichText( - text: TextSpan( - style: TextStyle(color: CupertinoColors.black), - children: [ - _user(event, context), - TextSpan(text: ' commented on issue '), - _strong(event.repo.name), - TextSpan(text: '#' + event.payload['issue']['number'].toString()), - TextSpan(text: event.payload['comment']['body']) + Container( + padding: EdgeInsets.only(left: 42, top: 8), + child: Text(_buildComment(), + overflow: TextOverflow.ellipsis, + style: TextStyle(color: Color(0xffaaaaaa)))) ], ), ); diff --git a/lib/widgets/user.dart b/lib/widgets/user.dart index de96c22..8bcb156 100644 --- a/lib/widgets/user.dart +++ b/lib/widgets/user.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; -import 'package:git_flux/utils.dart'; +import 'package:git_flux/utils/utils.dart'; Future queryUser(String login) async { var data = await query(''' diff --git a/pubspec.yaml b/pubspec.yaml index 3c7d944..3b69b67 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,8 @@ dependencies: http: ^0.11.3 rxdart: ^0.20.0 uri: ^0.11.3 + github: ^4.1.0 + intl: ^0.15.7 dev_dependencies: flutter_test: