diff --git a/lib/main.dart b/lib/main.dart index 2ef6be7..1ec4add 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,32 +20,16 @@ class _HomeState extends State { return Icon(Icons.notifications); } - String text = count > 99 ? '99+' : count.toString(); + // String text = count > 99 ? '99+' : count.toString(); - // https://stackoverflow.com/a/54094844 - return Stack(children: [ - Icon(Icons.notifications), - Positioned( - right: 0, - child: new Container( - padding: EdgeInsets.all(1), - decoration: new BoxDecoration( - color: Colors.red, - borderRadius: BorderRadius.circular(6), - ), - constraints: BoxConstraints( - minWidth: 12, - minHeight: 12, - ), - child: new Text( - '$text', - style: new TextStyle( - color: Colors.white, - fontSize: 8, - ), - textAlign: TextAlign.center, - ), - ), + // https://stackoverflow.com/a/45434404 + return new Stack(children: [ + new Icon(Icons.notifications), + new Positioned( + // draw a red marble + top: 0.0, + right: 0.0, + child: new Icon(Icons.brightness_1, size: 8.0, color: Colors.redAccent), ) ]); } diff --git a/lib/screens/issue.dart b/lib/screens/issue.dart index bdaf95d..b858a9e 100644 --- a/lib/screens/issue.dart +++ b/lib/screens/issue.dart @@ -76,9 +76,11 @@ class _IssueScreenState extends State { itemBuilder: (context, index) => TimelineItem(_items[index], payload), onRefresh: () async { var _payload = await queryIssue(widget.id, widget.owner, widget.name); - setState(() { - payload = _payload; - }); + if (mounted) { + setState(() { + payload = _payload; + }); + } }, // onLoadMore: () => , ); diff --git a/lib/screens/notifications.dart b/lib/screens/notifications.dart index 6f03f1f..08096d8 100644 --- a/lib/screens/notifications.dart +++ b/lib/screens/notifications.dart @@ -16,11 +16,15 @@ String getItemKey(NotificationPayload item) { } Future> fetchNotifications( - [int page = 1]) async { + int index, BuildContext context) async { List items = await getWithCredentials( - '/notifications?page=$page&all=true&per_page=100'); + '/notifications?all=${index == 2}&participating=${index == 1}'); var ns = items.map((item) => NotificationPayload.fromJson(item)).toList(); + if (index == 0) { + NotificationProvider.of(context).setCount(ns.length); + } + Map _groupMap = {}; ns.forEach((item) { @@ -97,7 +101,14 @@ class NotificationScreenState extends State { bool loading = false; Map groupMap = {}; - Widget _buildGroupItem(String key, NotificationGroup group) { + @override + void initState() { + super.initState(); + _refresh(); + } + + Widget _buildGroupItem(MapEntry entry) { + var group = entry.value; var repo = group.repo; return ListGroup( title: Row( @@ -126,7 +137,7 @@ class NotificationScreenState extends State { payload: item, markAsRead: () { setState(() { - groupMap[key].items[index].unread = false; + groupMap[entry.key].items[index].unread = false; }); }, ); @@ -134,46 +145,79 @@ class NotificationScreenState extends State { } Future _onSwitchTab(BuildContext context, int index) async { - // setState(() { - // active = index; - // loading = true; - // }); - - var _groupMap = await fetchNotifications(); - - // NotificationProvider.of(context).setCount(ns.length); - setState(() { - groupMap = _groupMap; - // loading = false; + active = index; + loading = true; }); + + var _groupMap = await fetchNotifications(active, context); + + if (mounted) { + setState(() { + groupMap = _groupMap; + loading = false; + }); + } } Future _refresh() async { - print('onrefresh'); await _onSwitchTab(context, active); } + var textMap = { + 0: 'Unread', + 1: 'Paticipating', + 2: 'All', + }; + + // var iconMap = { + // 0: Icon(Icons.inbox), + // 1: Icon(Icons.group), + // 2: Icon(Icons.mail), + // }; + @override Widget build(context) { return RefreshScaffold( - title: Text('Notifications'), - onRefresh: _refresh, - bodyBuilder: () { - List children = []; - children.add(CupertinoSegmentedControl( - groupValue: active, - onValueChanged: (index) => _onSwitchTab(context, index), - children: { - 0: Text('Unread'), - 1: Text('Paticipating'), - 2: Text('All') + title: Text(textMap[active]), + trailing: GestureDetector( + child: Icon(Icons.more_vert, size: 20), + onTap: () async { + int value = await showCupertinoDialog( + context: context, + builder: (context) { + return CupertinoAlertDialog( + title: Text('Select filter'), + actions: textMap.entries.map((entry) { + return CupertinoDialogAction( + child: Text(entry.value), + onPressed: () { + Navigator.pop(context, entry.key); + }, + ); + }).toList(), + ); + }, + ); + _onSwitchTab(context, value); + }, + ), + actions: [ + PopupMenuButton( + onSelected: (value) { + _onSwitchTab(context, value); }, - )); - children.addAll(groupMap.entries - .map((entry) => _buildGroupItem(entry.key, entry.value))); - - return Column(children: children); + itemBuilder: (context) { + return textMap.entries.map((entry) { + return PopupMenuItem(value: entry.key, child: Text(entry.value)); + }).toList(); + }, + ) + ], + onRefresh: _refresh, + loading: loading, + bodyBuilder: () { + return Column(children: groupMap.entries.map(_buildGroupItem).toList()); }, ); } diff --git a/lib/screens/pull_request.dart b/lib/screens/pull_request.dart index b3af3af..f06db1c 100644 --- a/lib/screens/pull_request.dart +++ b/lib/screens/pull_request.dart @@ -147,9 +147,11 @@ class _PullRequestScreenState extends State { onRefresh: () async { var _payload = await queryPullRequest(widget.id, widget.owner, widget.name); - setState(() { - payload = _payload; - }); + if (mounted) { + setState(() { + payload = _payload; + }); + } }, // onLoadMore: () => , ); diff --git a/lib/widgets/list_scaffold.dart b/lib/widgets/list_scaffold.dart index 9dbc0ef..4ac9f54 100644 --- a/lib/widgets/list_scaffold.dart +++ b/lib/widgets/list_scaffold.dart @@ -61,9 +61,11 @@ class _ListScaffoldState extends State { } catch (err) { print(err); } finally { - setState(() { - loading = false; - }); + if (mounted) { + setState(() { + loading = false; + }); + } } } diff --git a/lib/widgets/notification_item.dart b/lib/widgets/notification_item.dart index c259d1d..dd06cbd 100644 --- a/lib/widgets/notification_item.dart +++ b/lib/widgets/notification_item.dart @@ -109,10 +109,29 @@ class _NotificationItemState extends State { ); } + void _markAsRead() async { + if (payload.unread && !loading) { + setState(() { + loading = true; + }); + try { + await patchWithCredentials('/notifications/threads/' + payload.id); + widget.markAsRead(); + } finally { + if (mounted) { + setState(() { + loading = false; + }); + } + } + } + } + @override Widget build(BuildContext context) { return Link( onTap: () { + _markAsRead(); Navigator.of(context).push( CupertinoPageRoute(builder: (context) => _buildRoute()), ); @@ -135,22 +154,7 @@ class _NotificationItemState extends State { ), Link( child: _buildCheckIcon(), - onTap: () async { - if (payload.unread && !loading) { - setState(() { - loading = true; - }); - try { - await patchWithCredentials( - '/notifications/threads/' + payload.id); - widget.markAsRead(); - } finally { - setState(() { - loading = false; - }); - } - } - }, + onTap: _markAsRead, ), ], ), diff --git a/lib/widgets/refresh_scaffold.dart b/lib/widgets/refresh_scaffold.dart index ad06227..36579a1 100644 --- a/lib/widgets/refresh_scaffold.dart +++ b/lib/widgets/refresh_scaffold.dart @@ -7,51 +7,28 @@ import 'loading.dart'; typedef RefreshCallback = Future Function(); typedef WidgetBuilder = Widget Function(); -class RefreshScaffold extends StatefulWidget { +class RefreshScaffold extends StatelessWidget { final Widget title; final WidgetBuilder bodyBuilder; final RefreshCallback onRefresh; + final bool loading; + final Widget trailing; + final List actions; RefreshScaffold({ @required this.title, @required this.bodyBuilder, @required this.onRefresh, + @required this.loading, + this.trailing, + this.actions, }); - @override - _RefreshScaffoldState createState() => _RefreshScaffoldState(); -} - -class _RefreshScaffoldState extends State { - bool loading = false; - - @override - void initState() { - super.initState(); - _refresh(); - } - - Future _refresh() async { - print('refresh'); - setState(() { - loading = true; - }); - // try { - await widget.onRefresh(); - // } catch (err) { - // print(err); - // } finally { - setState(() { - loading = false; - }); - // } - } - - Widget _buildBody(BuildContext context) { + Widget _buildBody() { if (loading) { - return Loading(more: false); + return Loading(more: true); } else { - return widget.bodyBuilder(); + return bodyBuilder(); } } @@ -60,22 +37,26 @@ class _RefreshScaffoldState extends State { switch (SettingsProvider.of(context).layout) { case LayoutMap.cupertino: return CupertinoPageScaffold( - navigationBar: CupertinoNavigationBar(middle: widget.title), + navigationBar: + CupertinoNavigationBar(middle: title, trailing: trailing), child: SafeArea( child: CustomScrollView( slivers: [ - CupertinoSliverRefreshControl(onRefresh: _refresh), - SliverToBoxAdapter(child: _buildBody(context)) + CupertinoSliverRefreshControl(onRefresh: onRefresh), + SliverToBoxAdapter(child: _buildBody()) ], ), ), ); default: return Scaffold( - appBar: AppBar(title: widget.title), + appBar: AppBar( + title: title, + actions: actions, + ), body: RefreshIndicator( - onRefresh: _refresh, - child: SingleChildScrollView(child: _buildBody(context)), + onRefresh: onRefresh, + child: SingleChildScrollView(child: _buildBody()), ), ); }