From 7cb3fb189181d1dd3ba1374d001b93a2417014f6 Mon Sep 17 00:00:00 2001 From: LuisMend12 <128239393+LuisMend12@users.noreply.github.com> Date: Sat, 22 Aug 2026 12:18:39 -0400 Subject: [PATCH 1/3] Fix: notification navigator doesn't recognize ChannelActivity notifications Fixes #1665. Channel activity ("N messages from M people") notifications carry the same PlanetId/ChannelId/SourceId/ClickUrl shape as planet mention notifications, but NotificationSource.ChannelActivity wasn't in NotificationNavigator's switch, so clicking "View" always fell through to the default case and showed "This notification doesn't have a destination." instead of opening the channel at the triggering message. The planet-channel-route case grouping is pulled into a single IsPlanetChannelRouteSource predicate (used by the switch itself) so it can be exercised directly in NotificationNavigatorTests without needing a live client/JS runtime for the rest of NavigateTo. --- .../Client/Utility/NotificationNavigator.cs | 27 ++++++++++--- .../Client/NotificationNavigatorTests.cs | 38 +++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 Valour/Tests/Client/NotificationNavigatorTests.cs diff --git a/Valour/Client/Utility/NotificationNavigator.cs b/Valour/Client/Utility/NotificationNavigator.cs index a39f70e6b..27bcf7c5c 100644 --- a/Valour/Client/Utility/NotificationNavigator.cs +++ b/Valour/Client/Utility/NotificationNavigator.cs @@ -57,6 +57,27 @@ private static (long PlanetId, long ThreadId)? TryParseThreadRoute(string clickU return null; } + /// + /// Notification sources that resolve to a planet channel message and are + /// routed by fetching the planet/channel and jumping to SourceId. + /// The single source of truth for that routing decision (used by + /// NavigateTo's switch and exercised directly by + /// NotificationNavigatorTests, since the rest of NavigateTo needs a live + /// client/JS runtime to run end-to-end). + /// + private static readonly HashSet PlanetChannelRouteSources = new() + { + NotificationSource.PlanetMemberMention, + NotificationSource.PlanetRoleMention, + NotificationSource.PlanetMemberReply, + NotificationSource.PlanetHereMention, + NotificationSource.PlanetEveryoneMention, + NotificationSource.ChannelActivity, + }; + + internal static bool IsPlanetChannelRouteSource(NotificationSource source) => + PlanetChannelRouteSources.Contains(source); + public static async Task NavigateTo(Notification notification) { if (notification?.Client is null) @@ -71,11 +92,7 @@ public static async Task NavigateTo(Notification notification) { switch (notification.Source) { - case NotificationSource.PlanetMemberMention: - case NotificationSource.PlanetRoleMention: - case NotificationSource.PlanetMemberReply: - case NotificationSource.PlanetHereMention: - case NotificationSource.PlanetEveryoneMention: + case var source when IsPlanetChannelRouteSource(source): { var planetId = notification.PlanetId; var channelId = notification.ChannelId; diff --git a/Valour/Tests/Client/NotificationNavigatorTests.cs b/Valour/Tests/Client/NotificationNavigatorTests.cs new file mode 100644 index 000000000..c5195d727 --- /dev/null +++ b/Valour/Tests/Client/NotificationNavigatorTests.cs @@ -0,0 +1,38 @@ +using Valour.Client.Utility; +using Valour.Shared.Models; + +namespace Valour.Tests.Client; + +public class NotificationNavigatorTests +{ + // #1665: "xx messages from xx people" activity notifications used Source + // = ChannelActivity, which NavigateTo's switch didn't recognize, so + // clicking "View" always hit the default case ("This notification + // doesn't have a destination.") instead of opening the channel. + [Theory] + [InlineData(NotificationSource.ChannelActivity)] + [InlineData(NotificationSource.PlanetMemberMention)] + [InlineData(NotificationSource.PlanetRoleMention)] + [InlineData(NotificationSource.PlanetMemberReply)] + [InlineData(NotificationSource.PlanetHereMention)] + [InlineData(NotificationSource.PlanetEveryoneMention)] + public void IsPlanetChannelRouteSource_ForPlanetChannelSources_ReturnsTrue(NotificationSource source) + { + Assert.True(NotificationNavigator.IsPlanetChannelRouteSource(source)); + } + + [Theory] + [InlineData(NotificationSource.DirectMention)] + [InlineData(NotificationSource.DirectReply)] + [InlineData(NotificationSource.DirectMessage)] + [InlineData(NotificationSource.ThreadComment)] + [InlineData(NotificationSource.ThreadReply)] + [InlineData(NotificationSource.FriendRequest)] + [InlineData(NotificationSource.FriendRequestAccepted)] + [InlineData(NotificationSource.EventReminder)] + [InlineData(NotificationSource.Platform)] + public void IsPlanetChannelRouteSource_ForNonPlanetChannelSources_ReturnsFalse(NotificationSource source) + { + Assert.False(NotificationNavigator.IsPlanetChannelRouteSource(source)); + } +} From fdd659f50820d5e26ebc93895355cc69c70dc413 Mon Sep 17 00:00:00 2001 From: LuisM23 <128239393+LuisMend12@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:15:22 -0400 Subject: [PATCH 2/3] Update Valour/Client/Utility/NotificationNavigator.cs Co-authored-by: Vitaly Orekhov --- Valour/Client/Utility/NotificationNavigator.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Valour/Client/Utility/NotificationNavigator.cs b/Valour/Client/Utility/NotificationNavigator.cs index 27bcf7c5c..04e72c4ca 100644 --- a/Valour/Client/Utility/NotificationNavigator.cs +++ b/Valour/Client/Utility/NotificationNavigator.cs @@ -57,14 +57,6 @@ private static (long PlanetId, long ThreadId)? TryParseThreadRoute(string clickU return null; } - /// - /// Notification sources that resolve to a planet channel message and are - /// routed by fetching the planet/channel and jumping to SourceId. - /// The single source of truth for that routing decision (used by - /// NavigateTo's switch and exercised directly by - /// NotificationNavigatorTests, since the rest of NavigateTo needs a live - /// client/JS runtime to run end-to-end). - /// private static readonly HashSet PlanetChannelRouteSources = new() { NotificationSource.PlanetMemberMention, From d3cd8367e83390721c15f28b3e091891c0401b74 Mon Sep 17 00:00:00 2001 From: LuisM23 <128239393+LuisMend12@users.noreply.github.com> Date: Sat, 22 Aug 2026 13:15:29 -0400 Subject: [PATCH 3/3] Update Valour/Tests/Client/NotificationNavigatorTests.cs Co-authored-by: Vitaly Orekhov --- Valour/Tests/Client/NotificationNavigatorTests.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Valour/Tests/Client/NotificationNavigatorTests.cs b/Valour/Tests/Client/NotificationNavigatorTests.cs index c5195d727..106c78786 100644 --- a/Valour/Tests/Client/NotificationNavigatorTests.cs +++ b/Valour/Tests/Client/NotificationNavigatorTests.cs @@ -5,10 +5,6 @@ namespace Valour.Tests.Client; public class NotificationNavigatorTests { - // #1665: "xx messages from xx people" activity notifications used Source - // = ChannelActivity, which NavigateTo's switch didn't recognize, so - // clicking "View" always hit the default case ("This notification - // doesn't have a destination.") instead of opening the channel. [Theory] [InlineData(NotificationSource.ChannelActivity)] [InlineData(NotificationSource.PlanetMemberMention)]