forked from Nexus/nexus
parent
a5711d2ded
commit
8aada9a895
4 changed files with 137 additions and 3 deletions
|
|
@ -8,6 +8,7 @@ import "package:nexus/controllers/client.dart";
|
|||
import "package:nexus/models/account_data.dart";
|
||||
import "package:nexus/models/push_rules.dart";
|
||||
import "package:nexus/models/requests/update_push_rule.dart";
|
||||
import "package:nexus/models/room_notification_level.dart";
|
||||
import "package:nexus/models/sync_data.dart";
|
||||
|
||||
class AccountDataController extends Notifier<AccountData> {
|
||||
|
|
@ -98,6 +99,27 @@ class AccountDataController extends Notifier<AccountData> {
|
|||
(rules) => rules.find(kind, ruleId) == null,
|
||||
);
|
||||
|
||||
/// Replaces the push rules for the room with [roomId] with those [level]
|
||||
/// needs, deleting them first so disabled ones don't stay disabled.
|
||||
Future<void> setRoomNotificationLevel(
|
||||
String roomId,
|
||||
RoomNotificationLevel level,
|
||||
) async {
|
||||
final rules = state.pushRules.global;
|
||||
if (RoomNotificationLevel.of(rules, roomId) == level) return;
|
||||
|
||||
return _updatePushRules(
|
||||
.new([
|
||||
...[PushRuleKind.override, PushRuleKind.room]
|
||||
.where((kind) => rules.find(kind, roomId) != null)
|
||||
.map((kind) => .new(kind: kind, ruleId: roomId, action: .delete)),
|
||||
if (level.ruleFor(roomId) case (:final kind, :final rule))
|
||||
.new(kind: kind, ruleId: roomId, action: .put, newContent: rule),
|
||||
]),
|
||||
(rules) => RoomNotificationLevel.of(rules, roomId) == level,
|
||||
);
|
||||
}
|
||||
|
||||
/// Sends [requests] in order, then waits until [arrived] is true for the
|
||||
/// synced push rules.
|
||||
Future<void> _updatePushRules(
|
||||
|
|
|
|||
|
|
@ -15,18 +15,20 @@ class const PushRules({final PushRuleset global = const .new()})
|
|||
_$PushRulesFromJson(json);
|
||||
}
|
||||
|
||||
/// Push rules by kind, apart from room and sender rules, which Nexus doesn't
|
||||
/// use. The first rule that matches decides what happens.
|
||||
/// Push rules by kind, apart from sender rules, which Nexus doesn't use. The
|
||||
/// first rule that matches decides what happens.
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const PushRuleset({
|
||||
final IList<PushRule> override = const .empty(),
|
||||
final IList<PushRule> content = const .empty(),
|
||||
final IList<PushRule> room = const .empty(),
|
||||
final IList<PushRule> underride = const .empty(),
|
||||
}) with _$PushRuleset {
|
||||
IList<PushRule> ofKind(PushRuleKind kind) => switch (kind) {
|
||||
.override => override,
|
||||
.content => content,
|
||||
.room => room,
|
||||
.underride => underride,
|
||||
};
|
||||
|
||||
|
|
@ -46,7 +48,7 @@ class const PushRuleset({
|
|||
_$PushRulesetFromJson(json);
|
||||
}
|
||||
|
||||
enum PushRuleKind { override, content, underride }
|
||||
enum PushRuleKind { override, content, room, underride }
|
||||
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
|
|
@ -60,6 +62,9 @@ class const PushRule({
|
|||
|
||||
/// What content rules match in messages' bodies.
|
||||
final String? pattern,
|
||||
|
||||
/// What override and underride rules match.
|
||||
final IList<PushCondition> conditions = const .empty(),
|
||||
}) with _$PushRule {
|
||||
Map<String, Object?> toJson() => _$PushRuleToJson(this);
|
||||
|
||||
|
|
@ -67,6 +72,24 @@ class const PushRule({
|
|||
_$PushRuleFromJson(json);
|
||||
}
|
||||
|
||||
/// Only the fields of event_match conditions are kept.
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
@JsonSerializable()
|
||||
class const PushCondition({
|
||||
required final String kind,
|
||||
final String? key,
|
||||
final String? pattern,
|
||||
}) with _$PushCondition {
|
||||
/// Matches events in the room with [roomId].
|
||||
factory PushCondition.inRoom(String roomId) =>
|
||||
.new(kind: "event_match", key: "room_id", pattern: roomId);
|
||||
|
||||
Map<String, Object?> toJson() => _$PushConditionToJson(this);
|
||||
|
||||
factory PushCondition.fromJson(Map<String, Object?> json) =>
|
||||
_$PushConditionFromJson(json);
|
||||
}
|
||||
|
||||
/// What happens when a push rule matches. Actions Nexus doesn't support are
|
||||
/// dropped, so they're replaced when these are changed.
|
||||
@Freezed(toJson: false, fromJson: false)
|
||||
|
|
|
|||
49
lib/models/room_notification_level.dart
Normal file
49
lib/models/room_notification_level.dart
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import "package:material_ui/material_ui.dart";
|
||||
import "package:nexus/models/push_rules.dart";
|
||||
|
||||
enum RoomNotificationLevel(
|
||||
final String title,
|
||||
final String description,
|
||||
final IconData icon,
|
||||
) {
|
||||
global(
|
||||
"Default",
|
||||
"Follows your global notification rules.",
|
||||
Icons.notifications,
|
||||
),
|
||||
allMessages(
|
||||
"All Messages",
|
||||
"Every message notifies you.",
|
||||
Icons.notifications_active,
|
||||
),
|
||||
mentionsAndKeywords(
|
||||
"Mentions & Keywords",
|
||||
"Only messages that mention you or contain your keywords notify you.",
|
||||
Icons.alternate_email,
|
||||
),
|
||||
mute("Mute", "Nothing in this room notifies you.", Icons.notifications_off);
|
||||
|
||||
static RoomNotificationLevel of(PushRuleset ruleset, String roomId) =>
|
||||
switch ((ruleset.find(.override, roomId), ruleset.find(.room, roomId))) {
|
||||
(PushRule(enabled: true, actions: PushActions(notify: false)), _) =>
|
||||
.mute,
|
||||
(_, PushRule(enabled: true, actions: PushActions(notify: true))) =>
|
||||
.allMessages,
|
||||
(_, PushRule(enabled: true)) => .mentionsAndKeywords,
|
||||
_ => .global,
|
||||
};
|
||||
|
||||
({PushRuleKind kind, PushRule rule})? ruleFor(String roomId) =>
|
||||
switch (this) {
|
||||
.global => null,
|
||||
.allMessages => (
|
||||
kind: .room,
|
||||
rule: .new(ruleId: roomId, actions: .notifyWithSound),
|
||||
),
|
||||
.mentionsAndKeywords => (kind: .room, rule: .new(ruleId: roomId)),
|
||||
.mute => (
|
||||
kind: .override,
|
||||
rule: .new(ruleId: roomId, conditions: .new([.inRoom(roomId)])),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
|
@ -5,7 +5,9 @@ import "package:nexus/helpers/extensions/get_localpart.dart";
|
|||
import "package:nexus/helpers/extensions/show_leave_room_sheet.dart";
|
||||
import "package:flutter/services.dart";
|
||||
import "package:flutter_riverpod/flutter_riverpod.dart";
|
||||
import "package:nexus/controllers/account_data.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/models/room_notification_level.dart";
|
||||
import "package:nexus/controllers/room_settings_sections.dart";
|
||||
import "package:nexus/widgets/settings/settings_layout.dart";
|
||||
import "package:nexus/controllers/via.dart";
|
||||
|
|
@ -44,6 +46,44 @@ final class const RoomMenuButton(
|
|||
),
|
||||
),
|
||||
if (room != null) ...[
|
||||
PopupMenuItem(
|
||||
onTap: () async {
|
||||
final roomId = room!.metadata!.id;
|
||||
final accountData = ref.read(
|
||||
AccountDataController.provider.notifier,
|
||||
);
|
||||
final current = RoomNotificationLevel.of(
|
||||
ref.read(AccountDataController.provider).pushRules.global,
|
||||
roomId,
|
||||
);
|
||||
|
||||
final level = await context.showAdaptiveMenu(
|
||||
RoomNotificationLevel.values
|
||||
.map(
|
||||
(level) => PopupMenuItem(
|
||||
value: level,
|
||||
child: ListTile(
|
||||
selected: level == current,
|
||||
leading: Icon(level.icon),
|
||||
title: Text(level.title),
|
||||
subtitle: Text(level.description),
|
||||
trailing: level == current ? Icon(Icons.check) : null,
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
if (level == null) return;
|
||||
await accountData
|
||||
.setRoomNotificationLevel(roomId, level)
|
||||
.onError(showError);
|
||||
},
|
||||
child: ListTile(
|
||||
leading: Icon(Icons.notifications),
|
||||
title: Text("Notifications"),
|
||||
trailing: Icon(Icons.expand_more),
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () async {
|
||||
final vias = ref.read(ViaController.provider(room!)).viaQuery;
|
||||
|
|
|
|||
Loading…
Reference in a new issue