From c06ef90618b3a58100f747b4475bcb88f386238b Mon Sep 17 00:00:00 2001 From: Rongjian Zhang Date: Sat, 2 Mar 2019 18:17:46 +0800 Subject: [PATCH] feat: add trending screen --- lib/main.dart | 10 +++- lib/screens/trending.dart | 97 ++++++++++++++++++++++++++++++++++++++ lib/widgets/repo_item.dart | 25 +++++----- pubspec.yaml | 1 + 4 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 lib/screens/trending.dart diff --git a/lib/main.dart b/lib/main.dart index e5e8f93..eaf580a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -9,6 +9,7 @@ import 'screens/me.dart'; import 'screens/login.dart'; import 'screens/issue.dart'; import 'screens/repos.dart'; +import 'screens/trending.dart'; class Home extends StatefulWidget { @override @@ -49,6 +50,10 @@ class _HomeState extends State { icon: _buildNotificationIcon(context), title: Text('Notification'), ), + BottomNavigationBarItem( + icon: Icon(Icons.trending_up), + title: Text('Trending'), + ), BottomNavigationBarItem( icon: Icon(Icons.search), title: Text('Search'), @@ -63,14 +68,17 @@ class _HomeState extends State { _buildScreen(int index) { // return IssueScreen(number: 29, owner: 'reactjs', name: 'rfcs'); // return ReposScreen('pd4d10'); + // return TrendingScreen(); switch (index) { case 0: return NewsScreen(); case 1: return NotificationScreen(); case 2: - return SearchScreen(); + return TrendingScreen(); case 3: + return SearchScreen(); + case 4: return MeScreen(); } } diff --git a/lib/screens/trending.dart b/lib/screens/trending.dart new file mode 100644 index 0000000..41d7e40 --- /dev/null +++ b/lib/screens/trending.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:html/parser.dart' show parse; +import '../scaffolds/refresh.dart'; +import '../widgets/repo_item.dart'; + +// class TrendingRepo { +// String owner; +// String name; +// String description; +// String languageName; +// String languageColor; +// int starCount; +// int forkCount; +// int stars; +// } + +class TrendingScreen extends StatefulWidget { + @override + _TrendingScreenState createState() => _TrendingScreenState(); +} + +class _TrendingScreenState extends State { + Future> _fetchTrendingRepos() async { + var res = await http.get('https://github.com/trending'); + // print(res.body); + var document = parse(res.body); + var items = document.querySelectorAll('.repo-list>li'); + + return items.map((item) { + Map lang; + var colorNode = item.querySelector('.repo-language-color'); + if (colorNode != null) { + lang = { + 'name': colorNode.nextElementSibling.innerHtml.trim(), + 'color': RegExp(r'(#\w{6})') + .firstMatch(colorNode.attributes['style']) + .group(0), + }; + } + + var payload = { + 'owner': { + 'login': item + .querySelector('h3>a>span') + ?.innerHtml + ?.replaceFirst('/', '') + ?.trim() + }, + 'name': item + .querySelector('h3>a') + ?.innerHtml + ?.replaceFirst(RegExp(r'^[\s\S]*span>'), '') + ?.trim(), + 'description': item.children[2].querySelector('p')?.innerHtml?.trim(), + 'stargazers': { + 'totalCount': item.children[3] + .querySelectorAll('a')[0] + ?.innerHtml + ?.replaceFirst(RegExp(r'^[\s\S]*svg>'), '') + ?.trim() + }, + 'forks': { + 'totalCount': item.children[3] + .querySelectorAll('a')[1] + ?.innerHtml + ?.replaceFirst(RegExp(r'^[\s\S]*svg>'), '') + ?.trim(), + }, + 'primaryLanguage': lang, + 'isPrivate': false, + 'isFork': false // TODO: + }; + // print(payload); + return payload; + }).toList(); + } + + @override + Widget build(BuildContext context) { + return RefreshScaffold( + title: Text('Trending'), + onRefresh: _fetchTrendingRepos, + bodyBuilder: (payload) { + return Column( + children: payload.map((repo) { + return Container( + decoration: BoxDecoration( + border: Border(bottom: BorderSide(color: Colors.black12))), + child: RepoItem(repo), + ); + }).toList(), + ); + }, + ); + } +} diff --git a/lib/widgets/repo_item.dart b/lib/widgets/repo_item.dart index 6541d2d..9a67aa7 100644 --- a/lib/widgets/repo_item.dart +++ b/lib/widgets/repo_item.dart @@ -5,15 +5,15 @@ import '../screens/repo.dart'; import 'link.dart'; class RepoItem extends StatelessWidget { - final Map item; + final Map payload; - RepoItem(this.item); + RepoItem(this.payload); IconData _buildIconData() { - if (item['isPrivate']) { + if (payload['isPrivate']) { return Octicons.lock; } - if (item['isFork']) { + if (payload['isFork']) { return Octicons.repo_forked; } return Octicons.repo; @@ -22,7 +22,8 @@ class RepoItem extends StatelessWidget { @override Widget build(BuildContext context) { return Link( - screenBuilder: (_) => RepoScreen(item['owner']['login'], item['name']), + screenBuilder: (_) => + RepoScreen(payload['owner']['login'], payload['name']), child: Padding( padding: EdgeInsets.all(10), child: Row( @@ -33,24 +34,24 @@ class RepoItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - item['owner']['login'] + '/' + item['name'], + payload['owner']['login'] + '/' + payload['name'], style: TextStyle(fontWeight: FontWeight.w600, fontSize: 15), ), Padding(padding: EdgeInsets.only(top: 6)), - Text(item['description'] ?? 'No description provided yet'), + Text(payload['description'] ?? 'No description provided yet'), Padding(padding: EdgeInsets.only(top: 6)), DefaultTextStyle( style: TextStyle(color: Colors.black54, fontSize: 13), child: Row( children: [ Icon(Octicons.star, size: 14, color: Colors.black54), - Text(item['stargazers']['totalCount'].toString()), + Text(payload['stargazers']['totalCount'].toString()), Padding(padding: EdgeInsets.only(left: 16)), Icon(Octicons.repo_forked, size: 14, color: Colors.black54), - Text(item['forks']['totalCount'].toString()), + Text(payload['forks']['totalCount'].toString()), Padding(padding: EdgeInsets.only(left: 16)), - item['primaryLanguage'] == null + payload['primaryLanguage'] == null ? Container() : Row(children: [ Container( @@ -58,12 +59,12 @@ class RepoItem extends StatelessWidget { height: 10, decoration: new BoxDecoration( color: convertColor( - item['primaryLanguage']['color']), + payload['primaryLanguage']['color']), shape: BoxShape.circle, ), ), Padding(padding: EdgeInsets.only(left: 4)), - Text(item['primaryLanguage']['name']), + Text(payload['primaryLanguage']['name']), ]), ], ), diff --git a/pubspec.yaml b/pubspec.yaml index 32c07cb..7195402 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: nanoid: ^0.0.6 share: ^0.6.0 flutter_vector_icons: ^0.0.2 + html: ^0.13.3 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons.