diff --git a/lib/providers/settings.dart b/lib/providers/settings.dart index b88e39e..68dde63 100644 --- a/lib/providers/settings.dart +++ b/lib/providers/settings.dart @@ -10,6 +10,7 @@ import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; // import '../utils/utils.dart'; import '../utils/constants.dart'; +import '../utils/utils.dart'; import '../models/account.dart'; class PlatformType { @@ -17,12 +18,6 @@ class PlatformType { static const gitlab = 'gitlab'; } -class StorageKeys { - static const account = 'account'; - static const github = 'github'; - static const theme = 'theme'; -} - // abstract class Model { // Future query(BuildContext context) { // var settings = SettingsProvider.of(context); diff --git a/lib/scaffolds/list.dart b/lib/scaffolds/list.dart index 24cc9cb..19c2b17 100644 --- a/lib/scaffolds/list.dart +++ b/lib/scaffolds/list.dart @@ -17,23 +17,21 @@ class ListPayload { // This is a scaffold for infinite scroll screens class ListScaffold extends StatefulWidget { final Widget title; - // final IconData trailingIconData; - // final Function trailingOnTap; + final Widget Function({Function({bool force}) refresh}) trailingBuiler; final Widget Function(T payload) itemBuilder; final Future> Function() onRefresh; final Future> Function(K cursor) onLoadMore; ListScaffold({ @required this.title, - // this.trailingIconData, - // this.trailingOnTap, @required this.itemBuilder, @required this.onRefresh, @required this.onLoadMore, + this.trailingBuiler, }); @override - _ListScaffoldState createState() => _ListScaffoldState(); + _ListScaffoldState createState() => _ListScaffoldState(); } class _ListScaffoldState extends State> { @@ -62,11 +60,20 @@ class _ListScaffoldState extends State> { }); } - Future _refresh() async { - // print('list scaffold refresh'); + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + Future _refresh({bool force = false}) async { + print('list scaffold refresh'); setState(() { error = ''; loading = true; + if (force) { + items = []; + } }); try { var _payload = await widget.onRefresh(); @@ -96,7 +103,8 @@ class _ListScaffoldState extends State> { cursor = _payload.cursor; hasMore = _payload.hasMore; } catch (err) { - print(err); + error = err.toString(); + throw err; } finally { if (mounted) { setState(() { @@ -154,6 +162,7 @@ class _ListScaffoldState extends State> { return EmptyWidget(); } else { return ListView.builder( + // shrinkWrap: true, controller: _controller, itemCount: 2 * items.length + 1, itemBuilder: _buildItem, @@ -176,15 +185,9 @@ class _ListScaffoldState extends State> { return CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: widget.title, - // trailing: Link( - // child: Icon( - // widget.trailingIconData, - // size: 24, - // color: Colors.blueAccent, - // ), - // beforeRedirect: widget.trailingOnTap, - // bgColor: Colors.transparent, - // ), + trailing: widget.trailingBuiler == null + ? null + : widget.trailingBuiler(refresh: _refresh), ), child: SafeArea( child: CustomScrollView( @@ -197,14 +200,9 @@ class _ListScaffoldState extends State> { return Scaffold( appBar: AppBar( title: widget.title, - // actions: widget.trailingIconData == null - // ? [] - // : [ - // IconButton( - // icon: Icon(widget.trailingIconData), - // onPressed: widget.trailingOnTap, - // ) - // ], + actions: widget.trailingBuiler == null + ? null + : [widget.trailingBuiler(refresh: _refresh)], ), body: RefreshIndicator( onRefresh: _refresh, diff --git a/lib/screens/news.dart b/lib/screens/news.dart index 13a5e08..957f70c 100644 --- a/lib/screens/news.dart +++ b/lib/screens/news.dart @@ -4,6 +4,16 @@ import '../scaffolds/list.dart'; import '../widgets/event_item.dart'; import '../providers/settings.dart'; import '../utils/utils.dart'; +import '../widgets/action.dart'; + +class NewsFilter { + static const all = 'all'; + + /// The same as GitHub dashboard + /// + /// Exclude issue and pull request events + static const github = 'github'; +} class NewsScreen extends StatefulWidget { @override @@ -11,34 +21,69 @@ class NewsScreen extends StatefulWidget { } class NewsScreenState extends State { - Future> fetchEvents(int page) async { + String filter = NewsFilter.github; + + Future> fetchEvents([int page = 1]) async { var settings = SettingsProvider.of(context); var login = settings.activeLogin; List data = await settings.getWithCredentials( '/users/$login/received_events?page=$page&per_page=$pageSize'); - // print(data); - return data + print(data.length); + var hasMore = data.length == pageSize; + var events = data .map((item) => EventPayload.fromJson(item)) + .where(testEvents) .toList(); + + return ListPayload( + cursor: page + 1, + hasMore: hasMore, + items: events, + ); + } + + bool testEvents(EventPayload event) { + switch (filter) { + case NewsFilter.github: + return ![ + 'IssueCommentEvent', + 'IssuesEvent', + 'PullRequestEvent', + 'PullRequestReviewEvent', + 'PullRequestReviewCommentEvent', + ].contains(event.type); + default: + return true; + } } @override Widget build(context) { - // FIXME: can't add generic type here. Don't know why - // type '(Event) => EventItem' is not a subtype of type '(dynamic) => Widget' - return ListScaffold( + return ListScaffold( title: Text('News'), itemBuilder: (payload) => EventItem(payload), - onRefresh: () async { - var page = 1; - var items = await fetchEvents(page); - return ListPayload( - cursor: page + 1, hasMore: items.length == pageSize, items: items); - }, - onLoadMore: (page) async { - var items = await fetchEvents(page); - return ListPayload( - cursor: page + 1, hasMore: items.length == pageSize, items: items); + onRefresh: fetchEvents, + onLoadMore: (page) => fetchEvents(page), + trailingBuiler: ({refresh}) { + return ActionButton( + title: 'Filter', + actions: [ + Action( + text: 'Show all items', + onPress: () { + filter = NewsFilter.all; + refresh(force: true); + }, + ), + Action( + text: 'Only GitHub items', + onPress: () { + filter = NewsFilter.github; + refresh(force: true); + }, + ), + ], + ); }, ); } diff --git a/lib/screens/notifications.dart b/lib/screens/notifications.dart index b8b4155..c5001ae 100644 --- a/lib/screens/notifications.dart +++ b/lib/screens/notifications.dart @@ -218,7 +218,9 @@ $key: pullRequest(number: ${item.number}) { title: _buildTitle(), bottom: TabBar( onTap: _onSwitchTab, - tabs: textMap.entries.map((entry) => Tab(text: entry.value)).toList(), + tabs: textMap.entries + .map((entry) => Tab(text: entry.value.toUpperCase())) + .toList(), ), // trailing: GestureDetector( // child: Icon(Icons.more_vert, size: 20), diff --git a/lib/screens/repos.dart b/lib/screens/repos.dart index ef7ec3c..8d36493 100644 --- a/lib/screens/repos.dart +++ b/lib/screens/repos.dart @@ -54,7 +54,7 @@ class _ReposScreenState extends State { title: Text('$login\'s $title'), onRefresh: () => _queryRepos(), onLoadMore: (cursor) => _queryRepos(cursor), - itemBuilder: (payload) => RepoItem(payload), + itemBuilder: (payload) => RepoItem(payload, showOwner: false), ); } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 86ef90f..c7ef996 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -6,6 +6,13 @@ import '../screens/repo.dart'; export 'package:flutter_vector_icons/flutter_vector_icons.dart'; export 'timeago.dart'; +class StorageKeys { + static const account = 'account'; + static const github = 'github'; + static const theme = 'theme'; + static const newsFilter = 'news.filter'; +} + Color convertColor(String cssHex) { if (cssHex.startsWith('#')) { cssHex = cssHex.substring(1);