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)]