add power level groups

This commit is contained in:
Henry Hiles 2026-10-23 12:24:22 -04:00
commit 6336764da0
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
2 changed files with 216 additions and 11 deletions

View file

@ -58,6 +58,7 @@ enum EventType(
avatar("m.room.avatar", AvatarContent.fromJson),
topic("m.room.topic", TopicContent.fromJson),
name("m.room.name", NameContent.fromJson),
tombstone("m.room.tombstone", Content.fromJson),
reaction("m.reaction", ReactionContent.fromJson),
pinnedEvents("m.room.pinned_events", PinnedEventsContent.fromJson),
message("m.room.message", MessageContent.fromJson),

View file

@ -1,3 +1,5 @@
import "dart:math";
import "package:fast_immutable_collections/fast_immutable_collections.dart";
import "package:material_ui/material_ui.dart";
import "package:nexus/models/content/content.dart";
@ -113,14 +115,133 @@ enum PowerLevelEvent(
"Pin Messages",
"The power level needed to pin messages.",
Icons.push_pin,
),
name(
.name,
"Change Name",
"The power level needed to change the room's name.",
Icons.title,
),
topic(
.topic,
"Change Topic",
"The power level needed to change the room's topic.",
Icons.description,
),
avatar(
.avatar,
"Change Avatar",
"The power level needed to change the room's avatar.",
Icons.image,
),
canonicalAlias(
.canonicalAlias,
"Change Aliases",
"The power level needed to change the room's canonical alias and published aliases.",
Icons.numbers,
),
powerLevels(
.powerLevels,
"Change Power Levels",
"The power level needed to change power levels, including these.",
Icons.admin_panel_settings,
),
joinRules(
.joinRules,
"Change Join Rule",
"The power level needed to change who can join the room.",
Icons.login,
),
historyVisibility(
.historyVisibility,
"Change History Visibility",
"The power level needed to change who can read the room's history.",
Icons.history,
),
serverACL(
.serverACL,
"Change Server ACL",
"The power level needed to change which servers can take part in the room.",
Icons.dns,
),
encryption(
.encryption,
"Enable Encryption",
"The power level needed to enable encryption, which can't be turned off.",
Icons.lock,
),
tombstone(
.tombstone,
"Upgrade Room",
"The power level needed to replace the room with an upgraded one.",
Icons.upgrade,
);
static IList<PowerLevelEvent> shownIn({required bool encrypted}) => .new([
if (encrypted) .encrypted else ...[.message, .sticker],
.reaction,
.pinnedEvents,
]);
static PowerLevelEvent? of(String type) =>
values.where((event) => event.type.type == type).firstOrNull;
}
/// Power levels set together, like everything moderators need, to save
/// setting each one. Each can still be set on its own, by expanding it.
enum PowerLevelGroup(final String title, final IconData icon) {
moderation("Moderation", Icons.shield),
roomDetails("Room Details", Icons.edit),
administration("Administration", Icons.admin_panel_settings);
IList<PowerLevelPermission> get permissions => .new(switch (this) {
.moderation => [.kick, .ban, .redact, .notifyRoom],
.roomDetails || .administration => [],
});
IList<PowerLevelEvent> get events => .new(switch (this) {
.moderation => [],
.roomDetails => [.name, .topic, .avatar, .canonicalAlias, .pinnedEvents],
.administration => [
.powerLevels,
.joinRules,
.historyVisibility,
.serverACL,
.encryption,
.tombstone,
],
});
String get description => [
...permissions.map((permission) => permission.title),
...events.map((event) => event.title),
].join(", ");
int? levelIn(PowerLevelsContent content) {
final levels = {
...permissions.map((permission) => permission.levelIn(content)),
...events.map(
(event) => content.events[event.type.type] ?? content.stateDefault,
),
};
return levels.length == 1 ? levels.single : null;
}
int highestIn(PowerLevelsContent content) => [
...permissions.map((permission) => permission.levelIn(content)),
...events.map(
(event) => content.events[event.type.type] ?? content.stateDefault,
),
].reduce(max);
PowerLevelsContent withLevel(PowerLevelsContent content, int level) =>
permissions
.fold(
content,
(content, permission) => permission.withLevel(content, level),
)
.copyWith(
events: content.events.addEntries(
events.map((event) => MapEntry(event.type.type, level)),
),
);
}