diff --git a/lib/controllers/settings_sections.dart b/lib/controllers/settings_sections.dart index 2cd804c..5ca7231 100644 --- a/lib/controllers/settings_sections.dart +++ b/lib/controllers/settings_sections.dart @@ -19,6 +19,7 @@ import "package:nexus/controllers/unified_push_distributors.dart"; import "package:nexus/helpers/extensions/distributor_name.dart"; import "package:nexus/models/account_data.dart"; import "package:nexus/models/client_state.dart"; +import "package:nexus/models/name_color.dart"; import "package:nexus/models/room_order.dart"; import "package:nexus/models/settings.dart"; import "package:nexus/models/settings_category.dart"; @@ -83,6 +84,26 @@ class SettingsSectionsController .onError(showError), ), ), + .new( + title: "Name Colors", + description: "Color names by power level, so moderators and admins stand out, or by user, so everyone has their own.", + icon: Icons.format_color_text, + builder: (title, description, icon) => DialogListTile( + icon: Icon(icon), + title: title, + subtitle: Text(description), + value: settings.nameColor, + options: NameColor.values, + getName: (option) => switch (option) { + .powerLevel => "By power level", + .user => "By user", + }, + onChanged: (value) => ref + .read(SettingsController.provider.notifier) + .set(settings.copyWith(nameColor: value)) + .onError(showError), + ), + ), .new( title: "Use Dynamic Theme", icon: Icons.palette, diff --git a/lib/helpers/extensions/displayname_color.dart b/lib/helpers/extensions/displayname_color.dart index 6eab735..5b431e6 100644 --- a/lib/helpers/extensions/displayname_color.dart +++ b/lib/helpers/extensions/displayname_color.dart @@ -1,13 +1,29 @@ import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:material_ui/material_ui.dart"; import "package:nexus/controllers/room_power_levels.dart"; +import "package:nexus/controllers/settings.dart"; import "package:nexus/helpers/extensions/string_to_color.dart"; extension DisplaynameColor on WidgetRef { - /// The color of [userId]'s name in [roomId], hashed from their power level, - /// so everyone with the same level shares one. - Color displaynameColor(String roomId, String userId) => watch( - RoomPowerLevelsController.provider(roomId) - .select((levels) => levels.levelOf(userId)), - ).toString().colorHash; + /// The color of [userId]'s name in [roomId], from their power level + /// (so everyone with the same level shares one) or their user ID, depending + /// on the user's settings. + Color displaynameColor(String roomId, String userId) => switch (watch( + SettingsController.provider.select((settings) => settings.value?.nameColor), + )) { + .user => userId.colorHash, + .powerLevel || null => powerLevelColor( + watch( + RoomPowerLevelsController.provider(roomId) + .select((levels) => levels.levelOf(userId)), + ), + ), + }; + + static Color powerLevelColor(double level) => HSLColor.fromAHSL( + 1, + level.isInfinite ? 30 : (210 + level / 50 * 137.50776) % 360, + 0.7, + 0.5, + ).toColor(); } diff --git a/lib/models/name_color.dart b/lib/models/name_color.dart new file mode 100644 index 0000000..f67429e --- /dev/null +++ b/lib/models/name_color.dart @@ -0,0 +1,2 @@ +/// What users' name colors are hashed from. +enum NameColor { powerLevel, user } diff --git a/lib/models/settings.dart b/lib/models/settings.dart index 7ea61d9..9b26f14 100644 --- a/lib/models/settings.dart +++ b/lib/models/settings.dart @@ -1,5 +1,6 @@ import "package:material_ui/material_ui.dart"; import "package:freezed_annotation/freezed_annotation.dart"; +import "package:nexus/models/name_color.dart"; import "package:nexus/models/room_order.dart"; import "package:nexus/models/show_and_send.dart"; @@ -17,6 +18,7 @@ class const Settings({ final bool swipeToReply = false, final bool hideMembershipEvents = false, final RoomOrder roomOrder = .unreadFirst, + final NameColor nameColor = .powerLevel, final ShowAndSend typingIndicators = .showAndSend, /// When not sending, read receipts are still sent privately, so that the