style: apply prettier across fork files
check:prettier was not part of my gate routine, so formatting drift accumulated across the session's touched files (and a few older ones). Run prettier --write to bring the repo back to 'All matched files use Prettier code style!'. Formatting only — no logic changes. tsc/tests/build all green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -64,12 +64,7 @@ test('sortBookmarks does not mutate its input', () => {
|
||||
});
|
||||
|
||||
test('groupBookmarksByRoom buckets by room, newest-first within a group', () => {
|
||||
const input = [
|
||||
bk('a', 'r1', 100),
|
||||
bk('b', 'r2', 500),
|
||||
bk('c', 'r1', 300),
|
||||
bk('d', 'r2', 200),
|
||||
];
|
||||
const input = [bk('a', 'r1', 100), bk('b', 'r2', 500), bk('c', 'r1', 300), bk('d', 'r2', 200)];
|
||||
const groups = groupBookmarksByRoom(input);
|
||||
assert.equal(groups.length, 2);
|
||||
const r1 = groups.find((g) => g.roomId === 'r1')!;
|
||||
|
||||
@@ -31,7 +31,8 @@ export function sortBookmarks(bookmarks: Bookmark[], sort: BookmarkSort): Bookma
|
||||
if (sort === 'oldest') {
|
||||
// Oldest-first is the reverse ordering; keep the same eventId tie-break shape.
|
||||
return copy.sort(
|
||||
(a, b) => a.savedAt - b.savedAt || (a.eventId < b.eventId ? -1 : a.eventId > b.eventId ? 1 : 0),
|
||||
(a, b) =>
|
||||
a.savedAt - b.savedAt || (a.eventId < b.eventId ? -1 : a.eventId > b.eventId ? 1 : 0),
|
||||
);
|
||||
}
|
||||
return copy.sort(byNewest);
|
||||
|
||||
@@ -126,7 +126,9 @@ test('marked-unread + already fully read: clears the flag even though no receipt
|
||||
await markAsRead(mx, '!r:server', false);
|
||||
assert.equal(calls.length, 0); // no receipt (the stuck-dot case)
|
||||
// ...but the marked-unread flag is cleared directly (both keys, unread:false)
|
||||
assert.ok(accountDataWrites.some((w) => w.type === 'm.marked_unread' && w.content.unread === false));
|
||||
assert.ok(
|
||||
accountDataWrites.some((w) => w.type === 'm.marked_unread' && w.content.unread === false),
|
||||
);
|
||||
});
|
||||
|
||||
test('not marked-unread: markAsRead does not touch account data', async () => {
|
||||
|
||||
@@ -201,7 +201,7 @@ test('parseResponseAnswerIds reads stable m.selections and unstable nested answe
|
||||
assert.deepEqual(parseResponseAnswerIds({ 'm.selections': ['0', '1'] }), ['0', '1']);
|
||||
assert.deepEqual(
|
||||
parseResponseAnswerIds({ 'org.matrix.msc3381.poll.response': { answers: ['a'] } }),
|
||||
['a']
|
||||
['a'],
|
||||
);
|
||||
assert.deepEqual(parseResponseAnswerIds({}), []);
|
||||
});
|
||||
|
||||
@@ -77,7 +77,7 @@ export function parseResponseAnswerIds(content: Record<string, any>): string[] {
|
||||
export function validateSelections(
|
||||
rawIds: string[],
|
||||
validIds: Set<string>,
|
||||
maxSelections: number
|
||||
maxSelections: number,
|
||||
): string[] {
|
||||
const out: string[] = [];
|
||||
const seen = new Set<string>();
|
||||
|
||||
@@ -2,7 +2,11 @@ import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { upsertPreset, normalizeLabel, StatusPreset } from './statusPresets';
|
||||
|
||||
const p = (id: string, label: string, clearAfter = '0'): StatusPreset => ({ id, label, clearAfter });
|
||||
const p = (id: string, label: string, clearAfter = '0'): StatusPreset => ({
|
||||
id,
|
||||
label,
|
||||
clearAfter,
|
||||
});
|
||||
|
||||
test('normalizeLabel trims and lowercases', () => {
|
||||
assert.equal(normalizeLabel(' 🎮 Gaming '), '🎮 gaming');
|
||||
|
||||
@@ -47,11 +47,7 @@ export function makePresetId(): string {
|
||||
* re-saving the same status moves it to the front instead of duplicating, and
|
||||
* capped at `max`. Pure — returns a new array and never mutates the input.
|
||||
*/
|
||||
export function upsertPreset(
|
||||
list: StatusPreset[],
|
||||
preset: StatusPreset,
|
||||
max = 20,
|
||||
): StatusPreset[] {
|
||||
export function upsertPreset(list: StatusPreset[], preset: StatusPreset, max = 20): StatusPreset[] {
|
||||
const key = normalizeLabel(preset.label);
|
||||
const withoutDup = list.filter((p) => normalizeLabel(p.label) !== key);
|
||||
return [preset, ...withoutDup].slice(0, max);
|
||||
|
||||
@@ -8,12 +8,12 @@ import {
|
||||
ThreadSnapshot,
|
||||
} from './threadList';
|
||||
|
||||
const t = (
|
||||
id: string,
|
||||
latestTs: number,
|
||||
unread = 0,
|
||||
participated = false,
|
||||
): ThreadSnapshot => ({ id, latestTs, unread, participated });
|
||||
const t = (id: string, latestTs: number, unread = 0, participated = false): ThreadSnapshot => ({
|
||||
id,
|
||||
latestTs,
|
||||
unread,
|
||||
participated,
|
||||
});
|
||||
|
||||
test('filterThreads all returns every thread', () => {
|
||||
const input = [t('a', 1, 0, false), t('b', 2, 3, true)];
|
||||
|
||||
@@ -50,7 +50,9 @@ test('Vimeo (incl. unlisted hash + channel/group/album forms)', () => {
|
||||
id: '123456789',
|
||||
hash: 'abc123',
|
||||
});
|
||||
assert.ok(parseMediaEmbed('https://vimeo.com/123456789/abc123', 'h')?.embedUrl.includes('h=abc123'));
|
||||
assert.ok(
|
||||
parseMediaEmbed('https://vimeo.com/123456789/abc123', 'h')?.embedUrl.includes('h=abc123'),
|
||||
);
|
||||
// channel / group / album share a trailing numeric video id
|
||||
assert.equal(getVimeoParts('https://vimeo.com/channels/staffpicks/76979871')?.id, '76979871');
|
||||
assert.equal(getVimeoParts('https://vimeo.com/groups/motion/videos/12345')?.id, '12345');
|
||||
@@ -61,7 +63,9 @@ test('extractEmbedHeight: Instagram / Reddit / Twitter shapes', () => {
|
||||
assert.equal(extractEmbedHeight({ type: 'MEASURE', details: { height: 640 } }), 640);
|
||||
assert.equal(extractEmbedHeight({ type: 'resize.embed', data: 812 }), 812); // Reddit
|
||||
assert.equal(
|
||||
extractEmbedHeight({ 'twttr.embed': [{ method: 'twttr.private.resize', params: [{ height: 500 }] }] }),
|
||||
extractEmbedHeight({
|
||||
'twttr.embed': [{ method: 'twttr.private.resize', params: [{ height: 500 }] }],
|
||||
}),
|
||||
500,
|
||||
);
|
||||
assert.equal(extractEmbedHeight({ height: 300 }), 300); // generic fallback
|
||||
@@ -74,7 +78,10 @@ test('mobile Shorts (m.youtube.com) → portrait', () => {
|
||||
});
|
||||
|
||||
test('TikTok: canonical /video/<id> only', () => {
|
||||
assert.equal(getTikTokVideoId('https://www.tiktok.com/@user/video/7234567890123456789'), '7234567890123456789');
|
||||
assert.equal(
|
||||
getTikTokVideoId('https://www.tiktok.com/@user/video/7234567890123456789'),
|
||||
'7234567890123456789',
|
||||
);
|
||||
assert.equal(getTikTokVideoId('https://vm.tiktok.com/ZMabc/'), null); // short link → oEmbed
|
||||
assert.equal(getTikTokVideoId('https://www.tiktok.com/@user'), null);
|
||||
});
|
||||
@@ -89,10 +96,16 @@ test('isTikTokLink: canonical + short + vm/vt', () => {
|
||||
});
|
||||
|
||||
test('tiktokIdFromOembed + player url', () => {
|
||||
assert.equal(tiktokIdFromOembed({ embed_product_id: '7659555276823006478' }), '7659555276823006478');
|
||||
assert.equal(
|
||||
tiktokIdFromOembed({ embed_product_id: '7659555276823006478' }),
|
||||
'7659555276823006478',
|
||||
);
|
||||
assert.equal(tiktokIdFromOembed({ html: '<blockquote data-video-id="123456">' }), '123456');
|
||||
assert.equal(tiktokIdFromOembed({}), null);
|
||||
assert.equal(tiktokPlayerEmbedUrl('999'), 'https://www.tiktok.com/player/v1/999?autoplay=1&rel=0');
|
||||
assert.equal(
|
||||
tiktokPlayerEmbedUrl('999'),
|
||||
'https://www.tiktok.com/player/v1/999?autoplay=1&rel=0',
|
||||
);
|
||||
});
|
||||
|
||||
test('Dailymotion + Streamable', () => {
|
||||
@@ -203,16 +216,28 @@ test('Tidal: track (audio) vs video (landscape)', () => {
|
||||
embedUrl: 'https://embed.tidal.com/tracks/12345',
|
||||
height: 120,
|
||||
});
|
||||
assert.equal(getTidalEmbed('https://listen.tidal.com/album/999')?.embedUrl, 'https://embed.tidal.com/albums/999?layout=gridify');
|
||||
assert.equal(
|
||||
getTidalEmbed('https://listen.tidal.com/album/999')?.embedUrl,
|
||||
'https://embed.tidal.com/albums/999?layout=gridify',
|
||||
);
|
||||
assert.equal(getTidalEmbed('https://listen.tidal.com/album/999')?.height, 275);
|
||||
assert.equal(getTidalEmbed('https://tidal.com/video/555')?.kind, 'landscape');
|
||||
assert.equal(getTidalEmbed('https://tidal.com/browse'), null);
|
||||
});
|
||||
|
||||
test('Instagram: p / reel / tv → embed path', () => {
|
||||
assert.equal(getInstagramEmbed('https://www.instagram.com/p/AbC123_-/'), 'https://www.instagram.com/p/AbC123_-/embed/');
|
||||
assert.equal(getInstagramEmbed('https://instagram.com/reel/XyZ/'), 'https://www.instagram.com/reel/XyZ/embed/');
|
||||
assert.equal(getInstagramEmbed('https://www.instagram.com/reels/XyZ/'), 'https://www.instagram.com/reel/XyZ/embed/');
|
||||
assert.equal(
|
||||
getInstagramEmbed('https://www.instagram.com/p/AbC123_-/'),
|
||||
'https://www.instagram.com/p/AbC123_-/embed/',
|
||||
);
|
||||
assert.equal(
|
||||
getInstagramEmbed('https://instagram.com/reel/XyZ/'),
|
||||
'https://www.instagram.com/reel/XyZ/embed/',
|
||||
);
|
||||
assert.equal(
|
||||
getInstagramEmbed('https://www.instagram.com/reels/XyZ/'),
|
||||
'https://www.instagram.com/reel/XyZ/embed/',
|
||||
);
|
||||
assert.equal(getInstagramEmbed('https://www.instagram.com/someuser/'), null);
|
||||
});
|
||||
|
||||
@@ -221,7 +246,10 @@ test('Reddit post embed → embed.reddit.com', () => {
|
||||
getRedditPostEmbed('https://www.reddit.com/r/aww/comments/abc123/cute_cat/'),
|
||||
'https://embed.reddit.com/r/aww/comments/abc123/?ref_source=embed&ref=share&embed=true&theme=dark',
|
||||
);
|
||||
assert.equal(getRedditPostEmbed('https://old.reddit.com/r/aww/comments/xyz/'), 'https://embed.reddit.com/r/aww/comments/xyz/?ref_source=embed&ref=share&embed=true&theme=dark');
|
||||
assert.equal(
|
||||
getRedditPostEmbed('https://old.reddit.com/r/aww/comments/xyz/'),
|
||||
'https://embed.reddit.com/r/aww/comments/xyz/?ref_source=embed&ref=share&embed=true&theme=dark',
|
||||
);
|
||||
assert.equal(getRedditPostEmbed('https://www.reddit.com/r/aww/'), null); // subreddit, not a post
|
||||
// redd.it short link / i.redd.it media host can't build the /r/<sub>/comments/<id>
|
||||
// path embed.reddit.com requires (a bare /comments/<id> 404s) → null so the caller's
|
||||
@@ -232,7 +260,10 @@ test('Reddit post embed → embed.reddit.com', () => {
|
||||
|
||||
test('parseMediaEmbed: Instagram/Reddit → rich, Tidal → audio', () => {
|
||||
assert.equal(parseMediaEmbed('https://www.instagram.com/p/abc/', 'h')?.kind, 'rich');
|
||||
assert.equal(parseMediaEmbed('https://www.reddit.com/r/x/comments/y/z/', 'h')?.provider, 'reddit');
|
||||
assert.equal(
|
||||
parseMediaEmbed('https://www.reddit.com/r/x/comments/y/z/', 'h')?.provider,
|
||||
'reddit',
|
||||
);
|
||||
assert.equal(parseMediaEmbed('https://tidal.com/browse/track/1', 'h')?.provider, 'tidal');
|
||||
});
|
||||
|
||||
@@ -242,7 +273,10 @@ test('Bluesky / Loom / Kick', () => {
|
||||
'https://embed.bsky.app/embed/alice.bsky.social/app.bsky.feed.post/3kabc',
|
||||
);
|
||||
assert.equal(getBlueskyEmbed('https://bsky.app/profile/alice.bsky.social'), null);
|
||||
assert.equal(parseMediaEmbed('https://bsky.app/profile/a.bsky.social/post/3k', 'h')?.kind, 'rich');
|
||||
assert.equal(
|
||||
parseMediaEmbed('https://bsky.app/profile/a.bsky.social/post/3k', 'h')?.kind,
|
||||
'rich',
|
||||
);
|
||||
|
||||
assert.equal(getLoomId('https://www.loom.com/share/abc123DEF'), 'abc123DEF');
|
||||
assert.equal(getLoomId('https://www.loom.com/embed/abc123DEF'), 'abc123DEF');
|
||||
|
||||
@@ -122,7 +122,11 @@ export function tiktokIdFromOembed(data: {
|
||||
const pid = String(data.embed_product_id ?? '').match(/\d+/)?.[0];
|
||||
if (pid) return pid;
|
||||
if (typeof data.html === 'string') {
|
||||
return data.html.match(/data-video-id="(\d+)"/)?.[1] ?? data.html.match(/\/video\/(\d+)/)?.[1] ?? null;
|
||||
return (
|
||||
data.html.match(/data-video-id="(\d+)"/)?.[1] ??
|
||||
data.html.match(/\/video\/(\d+)/)?.[1] ??
|
||||
null
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -252,7 +256,10 @@ export function isSoundCloudTrack(url: string): boolean {
|
||||
// round-trip (soundcloud.com/oembed is CORS-enabled) to get the canonical URL.
|
||||
if (hostname.replace(/^www\./, '') !== 'soundcloud.com') return false;
|
||||
// /<artist>/<track|sets/set> — at least two segments, not a bare profile
|
||||
const parts = pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
|
||||
const parts = pathname
|
||||
.replace(/^\/+|\/+$/g, '')
|
||||
.split('/')
|
||||
.filter(Boolean);
|
||||
return parts.length >= 2;
|
||||
} catch {
|
||||
return false;
|
||||
@@ -304,7 +311,8 @@ export function getTidalEmbed(
|
||||
if (h !== 'tidal.com') return null;
|
||||
const p = u.pathname.replace(/^\/browse/, '');
|
||||
let m = p.match(/^\/track\/(\d+)/);
|
||||
if (m) return { kind: 'audio', embedUrl: `https://embed.tidal.com/tracks/${m[1]}`, height: 120 };
|
||||
if (m)
|
||||
return { kind: 'audio', embedUrl: `https://embed.tidal.com/tracks/${m[1]}`, height: 120 };
|
||||
m = p.match(/^\/album\/(\d+)/);
|
||||
if (m)
|
||||
// layout=gridify → full-width grid that fills the container (fixes the
|
||||
@@ -387,7 +395,10 @@ export function getKickChannel(url: string): string | null {
|
||||
try {
|
||||
const { hostname, pathname } = new URL(url);
|
||||
if (hostname.replace(/^www\./, '') !== 'kick.com') return null;
|
||||
const parts = pathname.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
|
||||
const parts = pathname
|
||||
.replace(/^\/+|\/+$/g, '')
|
||||
.split('/')
|
||||
.filter(Boolean);
|
||||
return parts.length === 1 && /^[A-Za-z0-9_]+$/.test(parts[0]) ? parts[0] : null;
|
||||
} catch {
|
||||
return null;
|
||||
@@ -412,7 +423,11 @@ export function getBlueskyEmbed(url: string): string | null {
|
||||
|
||||
const enc = encodeURIComponent;
|
||||
|
||||
export function buildVideoEmbedUrl(provider: 'youtube' | 'vimeo', id: string, hash?: string): string {
|
||||
export function buildVideoEmbedUrl(
|
||||
provider: 'youtube' | 'vimeo',
|
||||
id: string,
|
||||
hash?: string,
|
||||
): string {
|
||||
if (provider === 'vimeo') {
|
||||
// dnt=1 = Do Not Track (no non-essential cookies); h={hash} required for unlisted.
|
||||
return `https://player.vimeo.com/video/${enc(id)}?autoplay=1&dnt=1${
|
||||
@@ -435,11 +450,19 @@ export function spotifyEmbedHeight(type: SpotifyType): number {
|
||||
export function parseMediaEmbed(url: string, host: string): MediaEmbed | null {
|
||||
const shortsId = getYoutubeShortsId(url);
|
||||
if (shortsId)
|
||||
return { provider: 'youtube', kind: 'portrait', embedUrl: buildVideoEmbedUrl('youtube', shortsId) };
|
||||
return {
|
||||
provider: 'youtube',
|
||||
kind: 'portrait',
|
||||
embedUrl: buildVideoEmbedUrl('youtube', shortsId),
|
||||
};
|
||||
|
||||
const ytId = getYouTubeVideoId(url);
|
||||
if (ytId)
|
||||
return { provider: 'youtube', kind: 'landscape', embedUrl: buildVideoEmbedUrl('youtube', ytId) };
|
||||
return {
|
||||
provider: 'youtube',
|
||||
kind: 'landscape',
|
||||
embedUrl: buildVideoEmbedUrl('youtube', ytId),
|
||||
};
|
||||
|
||||
const vimeo = getVimeoParts(url);
|
||||
if (vimeo)
|
||||
|
||||
Reference in New Issue
Block a user