added talk notification levels for chats

This commit is contained in:
2026-09-25 17:04:10 +02:00
parent e21560ed3d
commit ef7d17033d
9 changed files with 281 additions and 0 deletions
@@ -6,6 +6,7 @@ import '../../../../api/marianumcloud/talk/get_participants/get_participants_cac
import '../../../../api/marianumcloud/talk/get_participants/get_participants_response.dart';
import '../../../../api/marianumcloud/talk/get_shared_items/get_shared_items_overview_response.dart';
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../api/marianumcloud/talk/room/notification_level.dart';
import '../../../../state/app/modules/chat_list/bloc/chat_list_bloc.dart';
import '../../../../widget/app_progress_indicator.dart';
import '../../../../widget/async_action_button.dart';
@@ -14,6 +15,7 @@ import '../../../../widget/confirm_dialog.dart';
import '../../../../widget/large_profile_picture_view.dart';
import '../../../../widget/user_avatar.dart';
import '../talk_navigator.dart';
import '../widgets/notification_level_sheet.dart';
import 'participants_list_view.dart';
import 'shared_items_view.dart';
@@ -29,6 +31,7 @@ class _ChatInfoState extends State<ChatInfo> {
GetParticipantsResponse? participants;
GetSharedItemsOverviewResponse? _sharesOverview;
late bool _isFavorite;
late NotificationLevel _notificationLevel;
int _avatarVersion = 0;
bool _avatarBusy = false;
@@ -36,6 +39,7 @@ class _ChatInfoState extends State<ChatInfo> {
void initState() {
super.initState();
_isFavorite = widget.room.isFavorite;
_notificationLevel = widget.room.effectiveNotificationLevel;
GetParticipantsCache(
chatToken: widget.room.token,
onUpdate: (GetParticipantsResponse data) {
@@ -67,6 +71,16 @@ class _ChatInfoState extends State<ChatInfo> {
_refreshList();
}
Future<void> _editNotificationLevel() async {
final level = await showNotificationLevelSheet(
context,
widget.room,
current: _notificationLevel,
);
if (level == null || !mounted) return;
setState(() => _notificationLevel = level);
}
Future<void> _confirmLeave() async {
final closed = await showDialog<bool>(
context: context,
@@ -243,6 +257,11 @@ class _ChatInfoState extends State<ChatInfo> {
title: const Text('Zu Favoriten hinzufügen'),
onPressed: _toggleFavorite,
),
NotificationLevelTile(
level: _notificationLevel,
trailing: const Icon(Icons.arrow_right),
onTap: _editNotificationLevel,
),
const Divider(),
ListTile(
leading: Icon(
@@ -6,6 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../api/marianumcloud/talk/actions/talk_actions.dart';
import '../../../../api/marianumcloud/talk/chat/rich_object_string_processor.dart';
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../api/marianumcloud/talk/room/notification_level.dart';
import '../../../../api/marianumcloud/talk/set_read_marker/set_read_marker.dart';
import '../../../../extensions/date_time.dart';
import '../../../../model/account_data.dart';
@@ -22,6 +23,7 @@ import '../../../../widget/details_bottom_sheet.dart';
import '../../../../widget/user_avatar.dart';
import '../data/talk_markdown.dart';
import '../talk_navigator.dart';
import 'notification_level_sheet.dart';
class ChatTile extends StatefulWidget {
final GetRoomResponseObject data;
@@ -151,6 +153,15 @@ class _ChatTileState extends State<ChatTile> {
semanticLabel: A11yLabels.draft,
),
],
if (widget.data.isMuted) ...[
const SizedBox(width: 5),
Icon(
NotificationLevel.neverNotify.icon,
size: 15,
color: Theme.of(context).colorScheme.onSurfaceVariant,
semanticLabel: A11yLabels.muted,
),
],
],
),
subtitle: Text(
@@ -238,6 +249,17 @@ class _ChatTileState extends State<ChatTile> {
if (mounted) _refreshList();
},
),
NotificationLevelTile(
level: widget.data.effectiveNotificationLevel,
onTap: () {
Navigator.of(sheetCtx).pop();
showNotificationLevelSheet(
context,
widget.data,
current: widget.data.effectiveNotificationLevel,
);
},
),
ListTile(
leading: const Icon(Icons.delete_outline),
title: const Text('Talk-Chat verlassen'),
@@ -0,0 +1,95 @@
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../api/marianumcloud/talk/room/get_room_response.dart';
import '../../../../api/marianumcloud/talk/room/notification_level.dart';
import '../../../../state/app/modules/chat_list/bloc/chat_list_bloc.dart';
import '../../../../widget/async_action_button.dart';
import '../../../../widget/details_bottom_sheet.dart';
extension NotificationLevelPresentation on NotificationLevel {
// defaultLevel never reaches the UI (see effectiveNotificationLevel), but
// the switch has to stay exhaustive.
({String label, String description, IconData icon}) get _presentation =>
switch (this) {
NotificationLevel.alwaysNotify || NotificationLevel.defaultLevel => (
label: 'Alle Nachrichten',
description: 'Bei jeder neuen Nachricht',
icon: Icons.notifications_outlined,
),
NotificationLevel.notifyOnMention => (
label: 'Nur Erwähnungen',
description: 'Nur wenn du erwähnt wirst',
icon: Icons.alternate_email,
),
NotificationLevel.neverNotify => (
label: 'Aus',
description: 'Keine Benachrichtigungen',
icon: Icons.notifications_off_outlined,
),
};
String get label => _presentation.label;
String get description => _presentation.description;
IconData get icon => _presentation.icon;
}
/// Lets the user pick the room's Talk notification level. Nextcloud applies
/// it server-side, so no push for the room is sent at all when muted.
/// Returns the new level, or null when nothing changed.
Future<NotificationLevel?> showNotificationLevelSheet(
BuildContext context,
GetRoomResponseObject room, {
required NotificationLevel current,
}) async {
final bloc = context.read<ChatListBloc>();
NotificationLevel? selected;
await showDetailsBottomSheet(
context,
header: ListTile(
leading: const Icon(Icons.notifications_outlined),
title: const Text('Benachrichtigungen'),
subtitle: Text(room.displayName, overflow: TextOverflow.ellipsis),
),
children: (_) => [
for (final level in selectableNotificationLevels)
AsyncListTile(
leading: Icon(
level == current
? Icons.radio_button_checked
: Icons.radio_button_unchecked,
),
title: Text(level.label),
subtitle: Text(level.description),
onPressed: () async {
if (level == current) return;
await bloc.setNotificationLevel(room.token, level);
selected = level;
},
),
],
);
return selected;
}
class NotificationLevelTile extends StatelessWidget {
final NotificationLevel level;
final VoidCallback onTap;
final Widget? trailing;
const NotificationLevelTile({
required this.level,
required this.onTap,
this.trailing,
super.key,
});
@override
Widget build(BuildContext context) => ListTile(
leading: Icon(level.icon),
title: const Text('Benachrichtigungen'),
subtitle: Text(level.label),
trailing: trailing,
onTap: onTap,
);
}