Solian/lib/screens/notification.dart

185 lines
5.8 KiB
Dart
Raw Normal View History

2024-04-24 15:19:26 +00:00
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:solian/providers/auth.dart';
import 'package:solian/providers/notify.dart';
import 'package:solian/utils/service_url.dart';
import 'package:solian/widgets/indent_wrapper.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:url_launcher/url_launcher_string.dart';
import 'package:solian/models/notification.dart' as model;
class NotificationScreen extends StatefulWidget {
const NotificationScreen({super.key});
@override
State<NotificationScreen> createState() => _NotificationScreenState();
}
class _NotificationScreenState extends State<NotificationScreen> {
@override
Widget build(BuildContext context) {
final auth = context.read<AuthProvider>();
final nty = context.watch<NotifyProvider>();
2024-04-29 12:22:06 +00:00
WidgetsBinding.instance.addPostFrameCallback((_) {
nty.allRead();
});
2024-04-24 15:19:26 +00:00
return IndentWrapper(
noSafeArea: true,
2024-04-25 13:33:53 +00:00
hideDrawer: true,
2024-04-24 15:19:26 +00:00
title: AppLocalizations.of(context)!.notification,
child: RefreshIndicator(
onRefresh: () => nty.fetch(auth),
child: CustomScrollView(
slivers: [
nty.notifications.isEmpty
? SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10),
color: Theme.of(context).colorScheme.surfaceVariant,
child: ListTile(
leading: const Icon(Icons.check),
title: Text(AppLocalizations.of(context)!.notifyDone),
2024-05-01 09:37:34 +00:00
subtitle: Text(
AppLocalizations.of(context)!.notifyDoneCaption),
2024-04-24 15:19:26 +00:00
),
),
)
: SliverList.builder(
itemCount: nty.notifications.length,
itemBuilder: (BuildContext context, int index) {
var element = nty.notifications[index];
return NotificationItem(
index: index,
item: element,
2024-04-29 12:22:06 +00:00
onDismiss: () => nty.clearAt(index),
2024-04-24 15:19:26 +00:00
);
},
),
SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.only(top: 12),
child: Text(
AppLocalizations.of(context)!.notifyListHint,
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodySmall,
),
),
),
],
),
),
);
}
}
class NotificationItem extends StatelessWidget {
final int index;
final model.Notification item;
final void Function()? onDismiss;
2024-05-01 09:37:34 +00:00
const NotificationItem(
{super.key, required this.index, required this.item, this.onDismiss});
2024-04-24 15:19:26 +00:00
bool hasLinks() => item.links != null && item.links!.isNotEmpty;
void showLinks(BuildContext context) {
if (!hasLinks()) return;
showModalBottomSheet<void>(
context: context,
builder: (BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
2024-05-01 09:37:34 +00:00
padding: const EdgeInsets.only(
left: 16, right: 16, top: 34, bottom: 12),
2024-04-24 15:19:26 +00:00
child: Text(
"Links",
style: Theme.of(context).textTheme.headlineSmall,
),
),
Expanded(
child: ListView.builder(
itemCount: item.links!.length,
itemBuilder: (BuildContext context, int index) {
var element = item.links![index];
return ListTile(
title: Text(element.label),
onTap: () async {
await launchUrlString(element.url);
if (Navigator.canPop(context)) {
Navigator.pop(context);
}
},
);
},
),
),
],
);
},
);
}
2024-05-01 09:37:34 +00:00
Future<void> markAsRead(
model.Notification element, BuildContext context) async {
2024-04-24 15:19:26 +00:00
if (element.isRealtime) return;
final auth = context.read<AuthProvider>();
if (!await auth.isAuthorized()) return;
var id = element.id;
var uri = getRequestUri('passport', '/api/notifications/$id/read');
await auth.client!.put(uri);
}
@override
Widget build(BuildContext context) {
return Dismissible(
2024-04-29 12:22:06 +00:00
key: Key((DateTime.now().millisecondsSinceEpoch << 10).toString()),
2024-04-24 15:19:26 +00:00
onDismissed: (direction) {
markAsRead(item, context).then((value) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: RichText(
text: TextSpan(
children: [
TextSpan(
text: item.subject,
style: const TextStyle(fontWeight: FontWeight.bold),
),
const TextSpan(text: " is marked as read")
],
),
),
),
);
});
if (onDismiss != null) {
onDismiss!();
}
},
background: Container(
color: Colors.lightBlue,
),
child: Container(
padding: const EdgeInsets.only(left: 10),
child: ListTile(
title: Text(item.subject),
subtitle: Text(item.content),
trailing: hasLinks()
? TextButton(
onPressed: () => showLinks(context),
style: TextButton.styleFrom(shape: const CircleBorder()),
child: const Icon(Icons.more_vert),
)
: null,
),
),
);
}
}