diff --git a/e2e/specs/community.spec.ts b/e2e/specs/community.spec.ts index 3ab9d7ca..2124c2ad 100644 --- a/e2e/specs/community.spec.ts +++ b/e2e/specs/community.spec.ts @@ -687,6 +687,10 @@ test("US-053 browse and search the library", async ({seed, as}) => { await expect(alice.getByText("The Handbook")).toBeVisible() + await alice.getByRole("button", {name: /Recipes/}).click() + + await expect(alice.getByText("This shelf doesn't have any links yet.")).toBeVisible() + // The library is the whole space's, so an ordinary member is offered the same controls. await expect(alice.getByRole("button", {name: "Create Shelf"})).toBeVisible() await expect(alice.getByRole("button", {name: "Add a link"})).toBeVisible() diff --git a/e2e/specs/notifications.spec.ts b/e2e/specs/notifications.spec.ts index 557cf5ec..80d01324 100644 --- a/e2e/specs/notifications.spec.ts +++ b/e2e/specs/notifications.spec.ts @@ -24,8 +24,9 @@ import { } from "../harness" import type {SeededEvent, SeededSpace, TestUser} from "../harness" -// A small primary-colored dot in the corner. RelaySummary's member check is the same shape at h-5 w-5. -const unreadDot = (scope: Locator) => scope.locator("div.h-2.w-2.rounded-full.bg-primary") +// The primary nav draws its dot in utilities and the space menu's rooms carry secondary-nav__dot. +const unreadDot = (scope: Locator) => + scope.locator("div.h-2.w-2.rounded-full.bg-primary, div.secondary-nav__dot") // An icon is a css mask built from a data url, so a room only renders this one when it is muted. const mutedRoomBell = (room: Locator) => room.locator("div.ml-auto.opacity-50") diff --git a/src/app/feeds.ts b/src/app/feeds.ts index d1c56716..3f2dee8e 100644 --- a/src/app/feeds.ts +++ b/src/app/feeds.ts @@ -10,6 +10,7 @@ import { range, removeUndefined, sleep, + spec, uniqBy, DAY, MONTH, @@ -704,17 +705,24 @@ export const makeCalendarFeed = ({ const compareByStart = (a: TrustedEvent, b: TrustedEvent) => getStart(a) - getStart(b) || compareEventsAsc(a, b) + // The filters take in comments too, for the feed context, and a TimeEvent reader throws on them. const isValid = (event: TrustedEvent) => { - const timeEvent = reader(TimeEvent)(event) + if (event.kind === EVENT_TIME) { + const timeEvent = reader(TimeEvent)(event) - return timeEvent.start() && timeEvent.end() && !seen.has(event.id) + return timeEvent.start() && timeEvent.end() && !seen.has(event.id) + } + + return false } const events = writable( uniqBy( e => e.id, relays.flatMap(url => app.get().use(Events).forUrl(url, filters).get()), - ).sort(compareByStart), + ) + .filter(spec({kind: EVENT_TIME})) + .sort(compareByStart), ) const insertEvents = (newEvents: TrustedEvent[]) => { diff --git a/src/lib/components/EmojiButton.svelte b/src/lib/components/EmojiButton.svelte index 752c3e3c..a063dcac 100644 --- a/src/lib/components/EmojiButton.svelte +++ b/src/lib/components/EmojiButton.svelte @@ -42,7 +42,15 @@ component={EmojiPicker} strayMargin={allowKaomoji ? undefined : 100} props={{onClick, allowKaomoji}} - params={{trigger: "manual", interactive: true, ...tippyParams}}> + params={{ + trigger: "manual", + interactive: true, + // Popper only flips to a side the picker fits on whole, and it's taller than half a laptop screen. + popperOptions: { + modifiers: [{name: "flip", options: {fallbackPlacements: ["bottom", "left", "right"]}}], + }, + ...tippyParams, + }}> {@render children?.()}