diff --git a/e2e/USER_STORIES.md b/e2e/USER_STORIES.md
index 0012d48c..9a6263a3 100644
--- a/e2e/USER_STORIES.md
+++ b/e2e/USER_STORIES.md
@@ -1564,7 +1564,11 @@ that I don't have to walk every space to find out.
Acceptance:
- The inbox lists each room and conversation with unread activity, naming the
- room and space and showing the latest message, newest first.
+ room and space and showing the latest message, newest first. It holds messages
+ only.
+- Activity is a row of one card per space, counting what that space has waiting
+ that isn't a message - threads, events, classifieds and the rest. A card
+ disappears once its space is read.
- A conversation with unread activity carries an unread dot, and "Mark all read"
clears every dot on the page.
- Selecting a conversation opens it, and it stays in the inbox once read.
diff --git a/e2e/specs/notifications.spec.ts b/e2e/specs/notifications.spec.ts
index 9dfa4929..5903f869 100644
--- a/e2e/specs/notifications.spec.ts
+++ b/e2e/specs/notifications.spec.ts
@@ -366,6 +366,19 @@ test("US-116 read the home dashboard", async ({seed, as}) => {
space.join(user.alice, "general")
space.join(user.bob, "general")
space.message(user.bob, "general", "the server is on fire", at(1, HOUR))
+
+ space.event(
+ user.bob,
+ () =>
+ space
+ .kind(Thread)
+ .writer()
+ .setRoom(space.url, "general")
+ .setTitle("Bed rotation")
+ .setContent("How often do you move things around?")
+ .renderTemplate(),
+ at(2, HOUR),
+ )
})
const space = scenario.space("space")
@@ -378,6 +391,14 @@ test("US-116 read the home dashboard", async ({seed, as}) => {
await expect(conversation).toContainText("General")
await expect(unreadDot(conversation)).toBeVisible()
+ // A space's threads, events and classifieds are counted per space in Activity rather than listed
+ // as conversations, so the inbox stays a list of messages
+ const activity = page.getByRole("link").filter({hasText: "1 thread"})
+
+ await expect(page.getByRole("heading", {name: "Activity"})).toBeVisible()
+ await expect(activity).toBeVisible()
+ await expect(conversation).not.toContainText("1 thread")
+
// Relay health checks had no mount point at all before the dashboard
await expect(page.getByText("Health checks")).toBeVisible()
@@ -388,6 +409,9 @@ test("US-116 read the home dashboard", async ({seed, as}) => {
await expect(unreadDot(conversation)).toHaveCount(0)
+ // Unlike a conversation, a space's activity card is a count of what's new, so reading it empties
+ await expect(activity).toHaveCount(0)
+
// A conversation stays in the inbox once it's read - it's a list of where things are, not a queue
await conversation.click()
diff --git a/src/app/components/HomeActivity.svelte b/src/app/components/HomeActivity.svelte
new file mode 100644
index 00000000..29082424
--- /dev/null
+++ b/src/app/components/HomeActivity.svelte
@@ -0,0 +1,34 @@
+
+
+{#if $inboxSpaceContent.length > 0}
+
+
- Rooms and spaces you belong to report their activity here — messages, threads, classifieds,
- events and polls.
+ The rooms, space chats and direct messages you belong to show their latest message here.