Reduce the home inbox to unread conversations and restyle the dashboard
This commit is contained in:
parent
7825477155
commit
b50d35e5fa
7 changed files with 32 additions and 39 deletions
|
|
@ -1563,15 +1563,14 @@ 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. It holds messages
|
||||
only.
|
||||
- The inbox lists each room and conversation with unread activity as a card,
|
||||
naming the room and space and showing the latest message, newest first. It
|
||||
holds messages only, and only while they are unread.
|
||||
- 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.
|
||||
- A conversation carries an unread dot, and "Mark all read" empties the inbox.
|
||||
- Selecting a conversation opens it.
|
||||
- Relay health checks are listed alongside the inbox, with the recommendation
|
||||
each one applies.
|
||||
- Hosting is offered whether or not she hosts a space: a shortcut to the hosting
|
||||
|
|
|
|||
|
|
@ -405,17 +405,14 @@ test("US-116 read the home dashboard", async ({seed, as}) => {
|
|||
// Hosting shows even though alice hosts nothing, as an invitation to start a space
|
||||
await expect(page.getByRole("button", {name: "Start a space"})).toBeVisible()
|
||||
|
||||
await expect(conversation).toHaveAttribute("href", roomPath(space.url, "general"))
|
||||
|
||||
// The inbox is the badges, so a conversation leaves the list once it has nothing unread
|
||||
await page.getByRole("button", {name: "Mark all read"}).click()
|
||||
|
||||
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(conversation).toHaveCount(0)
|
||||
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()
|
||||
|
||||
await expect(page).toHaveURL(pattern(roomPath(space.url, "general")))
|
||||
await expect(page.getByText("You're all caught up")).toBeVisible()
|
||||
})
|
||||
|
||||
test("US-106 share text into the app", async ({seed, as}) => {
|
||||
|
|
|
|||
|
|
@ -24,11 +24,17 @@
|
|||
<Button class="button button-neutral button-xs" onclick={markAllRead}>Mark all read</Button>
|
||||
{/if}
|
||||
{/snippet}
|
||||
{#if conversations.length === 0}
|
||||
{#if conversations.length > 0}
|
||||
<div class="flex flex-col gap-3 px-4 pb-4">
|
||||
{#each conversations as conversation (conversation.path)}
|
||||
<HomeInboxItem {conversation} />
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col items-center gap-3 px-4 pb-8 text-center">
|
||||
<p class="font-medium">Nothing in your inbox yet</p>
|
||||
<p class="font-medium">You're all caught up</p>
|
||||
<p class="max-w-md text-sm opacity-75">
|
||||
The rooms, space chats and direct messages you belong to show their latest message here.
|
||||
The rooms, space chats and direct messages you belong to show what's unread here.
|
||||
</p>
|
||||
<div class="flex flex-wrap justify-center gap-2">
|
||||
<Link href="/spaces" class="button button-primary button-sm">
|
||||
|
|
@ -41,11 +47,5 @@
|
|||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col divide-y divide-line border-t border-line">
|
||||
{#each conversations as conversation (conversation.path)}
|
||||
<HomeInboxItem {conversation} />
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</HomeSection>
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@
|
|||
import NoteContentMinimal from "@app/components/NoteContentMinimal.svelte"
|
||||
import UnreadDot from "@app/components/UnreadDot.svelte"
|
||||
import {user} from "@app/core"
|
||||
import type {InboxConversation} from "@app/inbox"
|
||||
import type {Activity} from "@app/notifications"
|
||||
|
||||
type Props = {
|
||||
conversation: InboxConversation
|
||||
conversation: Activity
|
||||
}
|
||||
|
||||
const {conversation}: Props = $props()
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
const others = $derived(uniq(remove($user.pubkey, conversation.pubkeys ?? [])))
|
||||
</script>
|
||||
|
||||
<Link href={path} class="flex items-center gap-3 px-4 py-3 hover:bg-surface-more">
|
||||
<Link href={path} class="card card-sm card-interactive flex items-center gap-3">
|
||||
{#if url}
|
||||
<RelayIcon {url} size={9} class="shrink-0" />
|
||||
{:else}
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
|
||||
<section class="flex min-w-0 flex-col border-b border-line last:border-b-0">
|
||||
<div class="flex items-center justify-between gap-3 px-4 pt-4 pb-3">
|
||||
<h2 class="flex items-center gap-2 text-xs font-bold tracking-widest uppercase opacity-75">
|
||||
<Icon {icon} size={4} />
|
||||
<h2 class="flex items-center gap-2 text-base font-bold tracking-wide uppercase">
|
||||
<Icon {icon} size={5} />
|
||||
{title}
|
||||
</h2>
|
||||
{@render action?.()}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
import {derived} from "svelte/store"
|
||||
import {sortBy} from "@welshman/lib"
|
||||
import type {Activity} from "@app/notifications"
|
||||
import {allNotifications, latestActivityByPath} from "@app/notifications"
|
||||
|
||||
export type InboxConversation = Activity & {unread: boolean}
|
||||
|
||||
// Every room, space chat and direct message the user can see, newest first, whether or not it
|
||||
// has anything unread - an inbox that hid what has been read would be empty for anyone caught up.
|
||||
// Every room, space chat and direct message with something unread, newest first. The inbox shows
|
||||
// exactly what the badges show, so reading a conversation is what takes it out of the list.
|
||||
export const inboxConversations = derived(
|
||||
[latestActivityByPath, allNotifications],
|
||||
([$latestActivityByPath, $allNotifications]) =>
|
||||
sortBy(
|
||||
conversation => -conversation.event.created_at,
|
||||
[...$latestActivityByPath.values()]
|
||||
.filter(activity => !activity.contentKind)
|
||||
.map(activity => ({...activity, unread: $allNotifications.has(activity.path)})),
|
||||
activity => -activity.event.created_at,
|
||||
[...$latestActivityByPath.values()].filter(
|
||||
activity => !activity.contentKind && $allNotifications.has(activity.path),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@
|
|||
<span class="hidden text-sm opacity-75 sm:inline">{today}</span>
|
||||
</div>
|
||||
</PageBar>
|
||||
<PageContent noPad class="flex flex-col">
|
||||
<PageContent noPad class="flex flex-col bg-surface">
|
||||
<div class="flex min-w-0 flex-col lg:flex-row lg:items-stretch">
|
||||
<div class="flex min-w-0 flex-col bg-surface lg:flex-[2] lg:border-r lg:border-line">
|
||||
<div class="flex min-w-0 flex-col lg:flex-[2] lg:border-r lg:border-line">
|
||||
<HomeInbox />
|
||||
<HomeActivity />
|
||||
<HomeNetwork />
|
||||
|
|
|
|||
Loading…
Reference in a new issue