feat: notification filters

This commit is contained in:
Rongjian Zhang 2019-02-06 21:35:52 +08:00
parent fb9fd4cb3a
commit e16d6c2468
7 changed files with 140 additions and 121 deletions

View File

@ -20,32 +20,16 @@ class _HomeState extends State<Home> {
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: <Widget>[
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: <Widget>[
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),
)
]);
}

View File

@ -76,9 +76,11 @@ class _IssueScreenState extends State<IssueScreen> {
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: () => ,
);

View File

@ -16,11 +16,15 @@ String getItemKey(NotificationPayload item) {
}
Future<Map<String, NotificationGroup>> 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<String, NotificationGroup> _groupMap = {};
ns.forEach((item) {
@ -97,7 +101,14 @@ class NotificationScreenState extends State<NotificationScreen> {
bool loading = false;
Map<String, NotificationGroup> groupMap = {};
Widget _buildGroupItem(String key, NotificationGroup group) {
@override
void initState() {
super.initState();
_refresh();
}
Widget _buildGroupItem(MapEntry<String, NotificationGroup> entry) {
var group = entry.value;
var repo = group.repo;
return ListGroup(
title: Row(
@ -126,7 +137,7 @@ class NotificationScreenState extends State<NotificationScreen> {
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<NotificationScreen> {
}
Future<void> _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<void> _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<Widget> 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: <Widget>[
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());
},
);
}

View File

@ -147,9 +147,11 @@ class _PullRequestScreenState extends State<PullRequestScreen> {
onRefresh: () async {
var _payload =
await queryPullRequest(widget.id, widget.owner, widget.name);
setState(() {
payload = _payload;
});
if (mounted) {
setState(() {
payload = _payload;
});
}
},
// onLoadMore: () => ,
);

View File

@ -61,9 +61,11 @@ class _ListScaffoldState extends State<ListScaffold> {
} catch (err) {
print(err);
} finally {
setState(() {
loading = false;
});
if (mounted) {
setState(() {
loading = false;
});
}
}
}

View File

@ -109,10 +109,29 @@ class _NotificationItemState extends State<NotificationItem> {
);
}
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<NotificationItem> {
),
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,
),
],
),

View File

@ -7,51 +7,28 @@ import 'loading.dart';
typedef RefreshCallback = Future<void> 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<Widget> 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<RefreshScaffold> {
bool loading = false;
@override
void initState() {
super.initState();
_refresh();
}
Future<void> _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<RefreshScaffold> {
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: <Widget>[
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()),
),
);
}