diff --git a/e2e/USER_STORIES.md b/e2e/USER_STORIES.md index 596d69f9..2af80352 100644 --- a/e2e/USER_STORIES.md +++ b/e2e/USER_STORIES.md @@ -235,21 +235,7 @@ Acceptance: appear when they apply. - A members summary listing admins and newest members links through to the full directory. -- Content admin has featured renders at the top for every visitor; with none, a - recent-activity summary appears instead. - -### US-016 — Catch up on a space's recent activity - -As bob, I want one feed of what's new across a space, so that I don't have to -open every room. - -Acceptance: - -- "Recent Activity" lists the latest message from each visible room alongside - recent posts and threads, newest first. -- A new message in a previously quiet room moves that room's entry to the top. -- Scrolling to the bottom loads older items, and a space with nothing in it - shows "No recent activity found". +- Content admin has featured renders at the top for every visitor. ### US-017 — Search across a space diff --git a/e2e/specs/spaces.spec.ts b/e2e/specs/spaces.spec.ts index aecac9e9..7307f331 100644 --- a/e2e/specs/spaces.spec.ts +++ b/e2e/specs/spaces.spec.ts @@ -1,5 +1,5 @@ -import {DAY, HOUR, MINUTE, WEEK, sortBy} from "@welshman/lib" -import {ROOMS, THREAD, makeEvent} from "@welshman/util" +import {DAY, HOUR, WEEK, sortBy} from "@welshman/lib" +import {ROOMS} from "@welshman/util" import type {Page} from "@playwright/test" import {expect, readCachedEvents, roomPath, spacePath, test, users} from "../harness" @@ -405,7 +405,7 @@ test("US-014 leave a space", async ({seed, as}) => { }) test("US-015 view a space's details", async ({seed, as}) => { - const scenario = await seed(({relay, user, at}) => { + const scenario = await seed(({relay, user}) => { const space = relay("space") space.room("general", {name: "General"}) @@ -413,7 +413,6 @@ test("US-015 view a space's details", async ({seed, as}) => { space.join(user.carol, "general") space.join(user.alice, "general") space.profile(user.alice, {name: "Alice Anchor"}) - space.message(user.alice, "general", "the tide is high", at(1, HOUR)) }) const space = scenario.space("space") @@ -451,10 +450,6 @@ test("US-015 view a space's details", async ({seed, as}) => { await expect(carol.getByText("Admins")).toBeVisible() await expect(carol.getByText("New members")).toBeVisible() - // Nothing is featured yet, so recent activity stands in for it - await expect(carol.getByRole("heading", {name: "Recent Activity"})).toBeVisible() - await expect(carol.getByText("the tide is high")).toBeVisible() - const admin = await as(users.admin, spacePath(space.url) + "/about", {relayInfo}) const featuredHeader = admin.getByRole("heading", {name: "Featured"}).locator("xpath=..") @@ -465,7 +460,7 @@ test("US-015 view a space's details", async ({seed, as}) => { await expect(admin.getByText("Featured content updated!")).toBeVisible() - // What admin featured reaches every visitor, and takes the top slot from recent activity + // What admin featured reaches every visitor await expect(carol.getByRole("heading", {name: "Featured"})).toBeVisible() await expect(carol.getByText("Start with the harbor rules")).toBeVisible() @@ -474,84 +469,6 @@ test("US-015 view a space's details", async ({seed, as}) => { await expect(carol).toHaveURL(/\/spaces\/space\.test\/directory/) }) -test("US-016 catch up on a space's recent activity", async ({seed, as}) => { - const scenario = await seed(({relay, user, at}) => { - const space = relay("space") - const other = relay("other") - - space.room("general", {name: "General"}) - space.room("quiet", {name: "Quiet Corner"}) - space.join(user.alice, "general", "quiet") - space.join(user.bob, "general", "quiet") - space.message(user.alice, "general", "the tide is high", at(30, MINUTE)) - space.message(user.alice, "quiet", "anyone still here?", at(3, HOUR)) - space.event( - user.alice, - makeEvent(THREAD, { - created_at: at(2, HOUR), - content: "Where should we sail next?", - tags: [ - ["h", "general"], - ["title", "Next voyage"], - ], - }), - ) - - // More items than the page renders at once, so the oldest is only reachable by scrolling - for (let day = 1; day <= 20; day++) { - space.event( - user.alice, - makeEvent(THREAD, { - created_at: at(day, DAY), - content: `Log entry ${day}`, - tags: [ - ["h", "general"], - ["title", `Old voyage ${day}`], - ], - }), - ) - } - - // A space with a member and nothing else - other.join(user.bob) - }) - - const space = scenario.space("space") - const other = scenario.space("other") - const bob = await as(users.bob, spacePath(space.url) + "/recent") - const items = bob.locator(".cv") - - await expect(bob.getByText("the tide is high")).toBeVisible() - await expect(bob.getByText("anyone still here?")).toBeVisible() - await expect(bob.getByText("Next voyage")).toBeVisible() - - // Newest first: the latest message in each room alongside the thread, in one feed - await expect(items.first()).toContainText("the tide is high") - - await bob.mouse.move(640, 400) - await bob.mouse.wheel(0, 8000) - - await expect(bob.getByText("Old voyage 20")).toBeVisible() - - // A message in the room that had gone quiet pulls it back to the top - const alice = await as(users.alice, roomPath(space.url, "quiet")) - - await alice.locator(".chat-editor [contenteditable=true]").pressSequentially("still here!") - await alice.locator(".chat-editor [contenteditable=true]").press("Enter") - - await expect(alice.getByText("still here!")).toBeVisible() - - // The feed is assembled when the page loads rather than kept up to date behind the reader, so - // this is the order bob finds when he comes back to it. - await bob.reload() - - await expect(items.first()).toContainText("Quiet Corner") - - await bob.goto(spacePath(other.url) + "/recent") - - await expect(bob.getByText("No recent activity found!")).toBeVisible() -}) - test("US-017 search across a space", async ({seed, as}) => { const scenario = await seed(({relay, user, at}) => { const space = relay("space") diff --git a/src/app/components/RecentConversation.svelte b/src/app/components/RecentConversation.svelte deleted file mode 100644 index 6268ce79..00000000 --- a/src/app/components/RecentConversation.svelte +++ /dev/null @@ -1,59 +0,0 @@ - - - -
-
- {#if h} - - {:else} - - Chat - {/if} - - {formatTimestamp(event.created_at)} - -
-
- -
- -
-
-
- - {count} - recent messages{count === 1 ? "" : "s"} - - -
-
-
diff --git a/src/app/components/RecentItem.svelte b/src/app/components/RecentItem.svelte deleted file mode 100644 index 6a25599c..00000000 --- a/src/app/components/RecentItem.svelte +++ /dev/null @@ -1,39 +0,0 @@ - - -{#if item.type === "message"} - -{:else if item.event.kind === THREAD} - -{:else if item.event.kind === CLASSIFIED} - -{:else if item.event.kind === LONG_FORM} - -{:else if item.event.kind === ZAP_GOAL} - -{:else if item.event.kind === EVENT_TIME} - -{:else if item.event.kind === POLL} - -{:else} - -{/if} diff --git a/src/app/components/SpaceFeaturedContent.svelte b/src/app/components/SpaceFeaturedContent.svelte index 9e9b603c..e37b5eda 100644 --- a/src/app/components/SpaceFeaturedContent.svelte +++ b/src/app/components/SpaceFeaturedContent.svelte @@ -6,7 +6,6 @@ import Card from "@lib/components/Card.svelte" import Content from "@app/components/Content.svelte" import EditFeaturedContent from "@app/components/EditFeaturedContent.svelte" - import SpaceRecentSummary from "@app/components/SpaceRecentSummary.svelte" import {deriveFeaturedContent} from "@app/featured" import {deriveUserIsSpaceAdmin} from "@app/management" import {pushModal} from "@app/modal" @@ -46,6 +45,4 @@ {/if} -{:else} - {/if} diff --git a/src/app/components/SpaceMenuNavItems.svelte b/src/app/components/SpaceMenuNavItems.svelte index 82694e1d..ef443dea 100644 --- a/src/app/components/SpaceMenuNavItems.svelte +++ b/src/app/components/SpaceMenuNavItems.svelte @@ -7,7 +7,6 @@ import Home from "@assets/icons/home.svg?dataurl" import GalleryWide from "@assets/icons/gallery-wide.svg?dataurl" import ChatRound from "@assets/icons/chat-round.svg?dataurl" - import History from "@assets/icons/history.svg?dataurl" import StarFallMinimalistic from "@assets/icons/star-fall-minimalistic-2.svg?dataurl" import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl" import CalendarMinimalistic from "@assets/icons/calendar-minimalistic.svg?dataurl" @@ -63,11 +62,7 @@ Space Details -{#if hasNip29} - - Recent Activity - -{:else} +{#if !hasNip29} Chat diff --git a/src/app/components/SpaceRecentSummary.svelte b/src/app/components/SpaceRecentSummary.svelte deleted file mode 100644 index 71a94b76..00000000 --- a/src/app/components/SpaceRecentSummary.svelte +++ /dev/null @@ -1,43 +0,0 @@ - - -
-

- - Recent Activity -

- {#if $recentActivity.length === 0} -

No recent activity yet.

- {:else} -
- {#each $recentActivity.slice(0, 3) as item (item.event.id)} - - {/each} -
- {/if} - - View all recent activity - - -
diff --git a/src/app/recent.ts b/src/app/recent.ts deleted file mode 100644 index 071a0227..00000000 --- a/src/app/recent.ts +++ /dev/null @@ -1,67 +0,0 @@ -import {derived} from "svelte/store" -import {groupBy, first, sortBy, uniqBy, ago, MONTH} from "@welshman/lib" -import {MESSAGE, COMMENT, getIdAndAddress, tagSpec, tagValue, tagValues} from "@welshman/util" -import type {TrustedEvent} from "@welshman/util" -import {app} from "@app/core" -import {deriveEventsForUrl} from "@app/repository" -import {CONTENT_KINDS} from "@app/content" - -export type RecentActivityItem = { - type: "message" | "content" - event: TrustedEvent - count: number - timestamp: number -} - -// Recent activity for a space: latest message per room plus content with the -// most recent activity (post or comment), sorted newest first. -export const deriveRecentActivity = (url: string) => { - const since = ago(3, MONTH) - const messages = deriveEventsForUrl(url, [{kinds: [MESSAGE], since}]) - const content = deriveEventsForUrl(url, [{kinds: CONTENT_KINDS, since}]) - const comments = deriveEventsForUrl(url, [{kinds: [COMMENT], since}]) - - return derived([messages, content, comments], ([$messages, $content, $comments]) => { - const activity: RecentActivityItem[] = [] - - const byRoom = groupBy(e => tagValue(tagSpec("h"), e.tags), $messages) - for (const roomMessages of byRoom.values()) { - const latest = first(roomMessages) - if (latest) { - activity.push({ - type: "message", - event: latest, - count: roomMessages.length, - timestamp: latest.created_at, - }) - } - } - - const latestActivityByKey = new Map() - - for (const event of $content) { - for (const k of getIdAndAddress(event)) { - latestActivityByKey.set(k, Math.max(latestActivityByKey.get(k) || 0, event.created_at)) - } - } - - for (const event of $comments) { - for (const k of tagValues(tagSpec(["E", "A"]), event.tags)) { - latestActivityByKey.set(k, Math.max(latestActivityByKey.get(k) || 0, event.created_at)) - } - } - - for (const [address, timestamp] of latestActivityByKey.entries()) { - const event = app.get().repository.getEvent(address) - - if (event) { - activity.push({type: "content", event, timestamp, count: 1}) - } - } - - return sortBy( - a => -a.timestamp, - uniqBy(a => a.event.id, activity), - ) - }) -} diff --git a/src/app/title.ts b/src/app/title.ts index 170cffff..90b54b28 100644 --- a/src/app/title.ts +++ b/src/app/title.ts @@ -16,7 +16,6 @@ const staticTitles = new Map([ ["/spaces/create", "Create a Space"], ["/spaces/[relay]", "Space"], ["/spaces/[relay]/chat", "Space Chat"], - ["/spaces/[relay]/recent", "Recent Activity"], ["/spaces/[relay]/threads", "Threads"], ["/spaces/[relay]/classifieds", "Classifieds"], ["/spaces/[relay]/articles", "Articles"], diff --git a/src/routes/spaces/[relay]/recent/+page.svelte b/src/routes/spaces/[relay]/recent/+page.svelte deleted file mode 100644 index 9d3578ac..00000000 --- a/src/routes/spaces/[relay]/recent/+page.svelte +++ /dev/null @@ -1,67 +0,0 @@ - - - - {#snippet leading()} - - {/snippet} - {#snippet title()} - Recent Activity - {/snippet} - {#snippet action()} - - {/snippet} - - - - {#if $recentActivity.length === 0} -

No recent activity found!

- {:else} - {#each $recentActivity.slice(0, limit) as item (item.event.id)} - - {/each} - {/if} -