diff --git a/e2e/USER_STORIES.md b/e2e/USER_STORIES.md
index 6c6fb44e..e9848180 100644
--- a/e2e/USER_STORIES.md
+++ b/e2e/USER_STORIES.md
@@ -1551,11 +1551,26 @@ never staring at a blank screen.
Acceptance:
- On a build with a configured platform space, `/home` opens that space.
-- With none configured, it shows a welcome screen offering "Add a space" and
- "Start a conversation".
+- With none configured, it shows the dashboard, whose empty inbox offers "Add a
+ space" and "Start a conversation".
- Those options navigate to the spaces directory and the chat view respectively.
- On a phone, the bottom bar carries a link home from wherever she is.
+### US-116 — Read the home dashboard
+
+As alice, I want the home page to tell me what happened while I was away, so
+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.
+- 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.
+- Relay health checks are listed alongside the inbox, with the recommendation
+ each one applies.
+
### US-106 — Share text into the app
As alice, I want to hand text to Flotilla and choose where it lands, so that I
diff --git a/e2e/specs/notifications.spec.ts b/e2e/specs/notifications.spec.ts
index 5bff4ca8..2f745f9c 100644
--- a/e2e/specs/notifications.spec.ts
+++ b/e2e/specs/notifications.spec.ts
@@ -327,7 +327,7 @@ test("US-105 land on the home page", async ({seed, as}) => {
await expect(platform).toHaveURL(pattern(spacePath(space.url)))
- // With none configured, /home is a welcome screen with two ways out
+ // With none configured, /home is the dashboard, whose empty inbox offers two ways out
const page = await as(users.alice, "/home")
const addSpace = page.getByRole("link", {name: "Add a space"})
@@ -358,6 +358,39 @@ test("US-105 land on the home page", async ({seed, as}) => {
await expect(phone).toHaveURL(/\/home$/)
})
+test("US-116 read the home dashboard", async ({seed, as}) => {
+ const scenario = await seed(({relay, user, at}) => {
+ const space = relay("space")
+
+ space.room("general", {name: "General"})
+ space.join(user.alice, "general")
+ space.join(user.bob, "general")
+ space.message(user.bob, "general", "the server is on fire", at(1, HOUR))
+ })
+
+ const space = scenario.space("space")
+ const page = await as(users.alice, "/home")
+
+ // Each conversation is one link, headed by its room and carrying the latest message underneath
+ const conversation = page.getByRole("link").filter({hasText: "the server is on fire"})
+
+ await expect(conversation).toBeVisible()
+ await expect(conversation).toContainText("General")
+ await expect(unreadDot(conversation)).toBeVisible()
+
+ // Relay health checks had no mount point at all before the dashboard
+ await expect(page.getByText("Health checks")).toBeVisible()
+
+ await page.getByRole("button", {name: "Mark all read"}).click()
+
+ await expect(unreadDot(conversation)).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()
+
+ await expect(page).toHaveURL(pattern(roomPath(space.url, "general")))
+})
+
test("US-106 share text into the app", async ({seed, as}) => {
const shared = "the offsite is moving to the 14th"
diff --git a/src/app/components/RelaySettingsHealthCheck.svelte b/src/app/components/HealthCheckItem.svelte
similarity index 100%
rename from src/app/components/RelaySettingsHealthCheck.svelte
rename to src/app/components/HealthCheckItem.svelte
diff --git a/src/app/components/HomeHealthChecks.svelte b/src/app/components/HomeHealthChecks.svelte
new file mode 100644
index 00000000..51096bf1
--- /dev/null
+++ b/src/app/components/HomeHealthChecks.svelte
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+ Health checks
+
+ {#if $pending.length > 0}
+ {$pending.length} open
+ {/if}
+
+ {#if $pending.length === 0}
+
+
+ Your connection to the network looks healthy.
+
+ {#each relays as relay (relay.id)}
+
+ {/each}
+
+ {#if relays.length > 0}
+ Spin up another hosted space — rooms, calendar and moderation included.
+ {:else}
+ Don't wait for an invite. Flotilla Hosting sets up a space with rooms, calendar and
+ moderation in about a minute.
+ {/if}
+
- {PLATFORM_NAME} actively checks your connection to the network in the background to discover relays
- that are offline, that you don't have access to, or are otherwise causing trouble.
-