From 78a0a27a72e5e6c3d349abe2cb28ce6cd1e64d83 Mon Sep 17 00:00:00 2001 From: Maze Winther Date: Tue, 14 Apr 2026 21:45:11 +0200 Subject: feat: editor feedback --- apps/web/src/lib/stickers/index.ts | 634 ++++++++++++++++++------------------- 1 file changed, 317 insertions(+), 317 deletions(-) (limited to 'apps/web/src/lib/stickers') diff --git a/apps/web/src/lib/stickers/index.ts b/apps/web/src/lib/stickers/index.ts index d3e14ed1..2d71d097 100644 --- a/apps/web/src/lib/stickers/index.ts +++ b/apps/web/src/lib/stickers/index.ts @@ -1,317 +1,317 @@ -import { - STICKER_CATEGORIES, -} from "@/lib/stickers/categories"; -import { STICKER_INTRINSIC_SIZE_FALLBACK } from "@/lib/stickers/intrinsic-size"; -import type { StickerCategory } from "@/lib/stickers/types"; -import { stickersRegistry } from "./registry"; -import { resolveStickerId } from "./resolver"; -import { registerDefaultStickerProviders } from "./providers"; -import { parseStickerId } from "./sticker-id"; -import type { - StickerBrowseResult, - StickerItem, - StickerProvider, - StickerSearchResult, -} from "./types"; - -const DEFAULT_BROWSE_LIMIT = 12; -const DEFAULT_SEARCH_LIMIT = 100; - -function mergeSearchResults({ - results, -}: { - results: StickerSearchResult[]; -}): StickerSearchResult { - const deduplicatedItems = new Map< - string, - StickerSearchResult["items"][number] - >(); - let total = 0; - let hasMore = false; - - for (const result of results) { - total += result.total; - hasMore = hasMore || result.hasMore; - for (const item of result.items) { - if (!deduplicatedItems.has(item.id)) { - deduplicatedItems.set(item.id, item); - } - } - } - - return { - items: Array.from(deduplicatedItems.values()), - total, - hasMore, - }; -} - -function getProviderByCategory({ - category, -}: { - category: StickerCategory; -}): StickerProvider | null { - if (category === "all") { - return null; - } - - try { - return stickersRegistry.get(category); - } catch { - return null; - } -} - -function getEmptyBrowseResult(): StickerBrowseResult { - return { - sections: [], - }; -} - -function getStickerNameFromId({ stickerId }: { stickerId: string }): string { - const stickerIdParts = stickerId.split(":"); - if (stickerIdParts.length <= 1) { - return stickerId; - } - return ( - stickerIdParts.slice(1).join(":").split(":").pop()?.replaceAll("-", " ") ?? - stickerId - ); -} - -function toRecentStickerItem({ - stickerId, -}: { - stickerId: string; -}): StickerItem | null { - try { - const { providerId } = parseStickerId({ stickerId }); - return { - id: stickerId, - provider: providerId, - name: getStickerNameFromId({ stickerId }), - previewUrl: resolveStickerId({ - stickerId, - options: { width: 64, height: 64 }, - }), - metadata: {}, - }; - } catch { - return null; - } -} - -export async function searchStickers({ - query, - category, - limit = DEFAULT_SEARCH_LIMIT, -}: { - query: string; - category: StickerCategory; - limit?: number; -}): Promise { - registerDefaultStickerProviders({}); - - const effectiveCategory = category in STICKER_CATEGORIES ? category : "all"; - if (effectiveCategory !== "all") { - const provider = getProviderByCategory({ category: effectiveCategory }); - if (!provider) { - return { - items: [], - total: 0, - hasMore: false, - }; - } - return provider.search({ - query, - options: { limit }, - }); - } - - const providers = stickersRegistry.getAll(); - if (providers.length === 0) { - return { - items: [], - total: 0, - hasMore: false, - }; - } - - const perProviderLimit = Math.max(1, Math.ceil(limit / providers.length)); - const settledResults = await Promise.allSettled( - providers.map((provider) => - provider.search({ - query, - options: { limit: perProviderLimit }, - }), - ), - ); - - const fulfilledResults = settledResults - .filter( - (result): result is PromiseFulfilledResult => - result.status === "fulfilled", - ) - .map((result) => result.value); - - return mergeSearchResults({ - results: fulfilledResults, - }); -} - -export async function searchAll({ - query, - limit = DEFAULT_SEARCH_LIMIT, -}: { - query: string; - limit?: number; -}): Promise { - registerDefaultStickerProviders({}); - - const providers = stickersRegistry.getAll(); - if (providers.length === 0) { - return { sections: [] }; - } - - const perProviderLimit = Math.max(1, Math.ceil(limit / providers.length)); - const settledResults = await Promise.allSettled( - providers.map(async (provider) => { - const result = await provider.search({ - query, - options: { limit: perProviderLimit }, - }); - return { provider, result }; - }), - ); - - const sections: StickerBrowseResult["sections"] = []; - for (const settled of settledResults) { - if (settled.status !== "fulfilled") { - continue; - } - const { provider, result } = settled.value; - if (result.items.length === 0) { - continue; - } - const category = provider.id as StickerCategory; - sections.push({ - id: category, - title: STICKER_CATEGORIES[category] ?? provider.id, - items: result.items, - hasMore: result.hasMore, - layout: "grid", - }); - } - - return { sections }; -} - -export async function browseCategory({ - category, -}: { - category: StickerCategory; -}): Promise { - registerDefaultStickerProviders({}); - - const effectiveCategory = category in STICKER_CATEGORIES ? category : "all"; - if (effectiveCategory === "all") { - return getEmptyBrowseResult(); - } - - const provider = getProviderByCategory({ category: effectiveCategory }); - if (!provider) { - return getEmptyBrowseResult(); - } - - return provider.browse({ options: {} }); -} - -export async function browseAll({ - recentStickers, - limit = DEFAULT_BROWSE_LIMIT, -}: { - recentStickers: string[]; - limit?: number; -}): Promise { - registerDefaultStickerProviders({}); - - const sections: StickerBrowseResult["sections"] = []; - const recentItems = recentStickers - .map((stickerId) => toRecentStickerItem({ stickerId })) - .filter((item): item is StickerItem => item !== null); - - if (recentItems.length > 0) { - sections.push({ - id: "recent", - title: "Recently used", - items: recentItems.slice(0, limit), - hasMore: recentItems.length > limit, - layout: "row", - }); - } - - const settledResults = await Promise.allSettled( - stickersRegistry.getAll().map(async (provider) => { - const browseResult = await provider.browse({ - options: { limit }, - }); - const firstSection = browseResult.sections[0]; - - if (!firstSection || firstSection.items.length === 0) { - return null; - } - - const category = provider.id as StickerCategory; - return { - ...firstSection, - id: category, - title: STICKER_CATEGORIES[category] ?? firstSection.title, - layout: "row" as const, - action: { - type: "see-all" as const, - category, - sectionId: firstSection.id, - }, - }; - }), - ); - - for (const result of settledResults) { - if (result.status === "fulfilled" && result.value) { - sections.push(result.value); - } - } - - return { sections }; -} - -export async function resolveStickerIntrinsicSize({ - stickerId, -}: { - stickerId: string; -}): Promise<{ width: number; height: number }> { - const url = resolveStickerId({ stickerId }); - return new Promise((resolve) => { - const img = new Image(); - img.onload = () => - resolve({ width: img.naturalWidth, height: img.naturalHeight }); - img.onerror = () => - resolve({ - width: STICKER_INTRINSIC_SIZE_FALLBACK, - height: STICKER_INTRINSIC_SIZE_FALLBACK, - }); - img.src = url; - }); -} - -export { resolveStickerId }; -export { resolveQueryToRegions, getRegionLabel } from "./providers/flags"; -export type { - StickerBrowseResult, - StickerBrowseSection, - StickerCategory, - StickerItem, - StickerProvider, - StickerResolveOptions, - StickerSearchResult, -} from "./types"; +import { + STICKER_CATEGORIES, +} from "@/lib/stickers/categories"; +import { STICKER_INTRINSIC_SIZE_FALLBACK } from "@/lib/stickers/intrinsic-size"; +import type { StickerCategory } from "@/lib/stickers/types"; +import { stickersRegistry } from "./registry"; +import { resolveStickerId } from "./resolver"; +import { registerDefaultStickerProviders } from "./providers"; +import { parseStickerId } from "./sticker-id"; +import type { + StickerBrowseResult, + StickerItem, + StickerProvider, + StickerSearchResult, +} from "./types"; + +const DEFAULT_BROWSE_LIMIT = 12; +const DEFAULT_SEARCH_LIMIT = 100; + +function mergeSearchResults({ + results, +}: { + results: StickerSearchResult[]; +}): StickerSearchResult { + const deduplicatedItems = new Map< + string, + StickerSearchResult["items"][number] + >(); + let total = 0; + let hasMore = false; + + for (const result of results) { + total += result.total; + hasMore = hasMore || result.hasMore; + for (const item of result.items) { + if (!deduplicatedItems.has(item.id)) { + deduplicatedItems.set(item.id, item); + } + } + } + + return { + items: Array.from(deduplicatedItems.values()), + total, + hasMore, + }; +} + +function getProviderByCategory({ + category, +}: { + category: StickerCategory; +}): StickerProvider | null { + if (category === "all") { + return null; + } + + try { + return stickersRegistry.get(category); + } catch { + return null; + } +} + +function getEmptyBrowseResult(): StickerBrowseResult { + return { + sections: [], + }; +} + +function getStickerNameFromId({ stickerId }: { stickerId: string }): string { + const stickerIdParts = stickerId.split(":"); + if (stickerIdParts.length <= 1) { + return stickerId; + } + return ( + stickerIdParts.slice(1).join(":").split(":").pop()?.replaceAll("-", " ") ?? + stickerId + ); +} + +function toRecentStickerItem({ + stickerId, +}: { + stickerId: string; +}): StickerItem | null { + try { + const { providerId } = parseStickerId({ stickerId }); + return { + id: stickerId, + provider: providerId, + name: getStickerNameFromId({ stickerId }), + previewUrl: resolveStickerId({ + stickerId, + options: { width: 64, height: 64 }, + }), + metadata: {}, + }; + } catch { + return null; + } +} + +export async function searchStickers({ + query, + category, + limit = DEFAULT_SEARCH_LIMIT, +}: { + query: string; + category: StickerCategory; + limit?: number; +}): Promise { + registerDefaultStickerProviders({}); + + const effectiveCategory = category in STICKER_CATEGORIES ? category : "all"; + if (effectiveCategory !== "all") { + const provider = getProviderByCategory({ category: effectiveCategory }); + if (!provider) { + return { + items: [], + total: 0, + hasMore: false, + }; + } + return provider.search({ + query, + options: { limit }, + }); + } + + const providers = stickersRegistry.getAll(); + if (providers.length === 0) { + return { + items: [], + total: 0, + hasMore: false, + }; + } + + const perProviderLimit = Math.max(1, Math.ceil(limit / providers.length)); + const settledResults = await Promise.allSettled( + providers.map((provider) => + provider.search({ + query, + options: { limit: perProviderLimit }, + }), + ), + ); + + const fulfilledResults = settledResults + .filter( + (result): result is PromiseFulfilledResult => + result.status === "fulfilled", + ) + .map((result) => result.value); + + return mergeSearchResults({ + results: fulfilledResults, + }); +} + +export async function searchAll({ + query, + limit = DEFAULT_SEARCH_LIMIT, +}: { + query: string; + limit?: number; +}): Promise { + registerDefaultStickerProviders({}); + + const providers = stickersRegistry.getAll(); + if (providers.length === 0) { + return { sections: [] }; + } + + const perProviderLimit = Math.max(1, Math.ceil(limit / providers.length)); + const settledResults = await Promise.allSettled( + providers.map(async (provider) => { + const result = await provider.search({ + query, + options: { limit: perProviderLimit }, + }); + return { provider, result }; + }), + ); + + const sections: StickerBrowseResult["sections"] = []; + for (const settled of settledResults) { + if (settled.status !== "fulfilled") { + continue; + } + const { provider, result } = settled.value; + if (result.items.length === 0) { + continue; + } + const category = provider.id as StickerCategory; + sections.push({ + id: category, + title: STICKER_CATEGORIES[category] ?? provider.id, + items: result.items, + hasMore: result.hasMore, + layout: "grid", + }); + } + + return { sections }; +} + +export async function browseCategory({ + category, +}: { + category: StickerCategory; +}): Promise { + registerDefaultStickerProviders({}); + + const effectiveCategory = category in STICKER_CATEGORIES ? category : "all"; + if (effectiveCategory === "all") { + return getEmptyBrowseResult(); + } + + const provider = getProviderByCategory({ category: effectiveCategory }); + if (!provider) { + return getEmptyBrowseResult(); + } + + return provider.browse({ options: {} }); +} + +export async function browseAll({ + recentStickers, + limit = DEFAULT_BROWSE_LIMIT, +}: { + recentStickers: string[]; + limit?: number; +}): Promise { + registerDefaultStickerProviders({}); + + const sections: StickerBrowseResult["sections"] = []; + const recentItems = recentStickers + .map((stickerId) => toRecentStickerItem({ stickerId })) + .filter((item): item is StickerItem => item !== null); + + if (recentItems.length > 0) { + sections.push({ + id: "recent", + title: "Recently used", + items: recentItems.slice(0, limit), + hasMore: recentItems.length > limit, + layout: "row", + }); + } + + const settledResults = await Promise.allSettled( + stickersRegistry.getAll().map(async (provider) => { + const browseResult = await provider.browse({ + options: { limit }, + }); + const firstSection = browseResult.sections[0]; + + if (!firstSection || firstSection.items.length === 0) { + return null; + } + + const category = provider.id as StickerCategory; + return { + ...firstSection, + id: category, + title: STICKER_CATEGORIES[category] ?? firstSection.title, + layout: "row" as const, + action: { + type: "see-all" as const, + category, + sectionId: firstSection.id, + }, + }; + }), + ); + + for (const result of settledResults) { + if (result.status === "fulfilled" && result.value) { + sections.push(result.value); + } + } + + return { sections }; +} + +export async function resolveStickerIntrinsicSize({ + stickerId, +}: { + stickerId: string; +}): Promise<{ width: number; height: number }> { + const url = resolveStickerId({ stickerId }); + return new Promise((resolve) => { + const img = new Image(); + img.onload = () => + resolve({ width: img.naturalWidth, height: img.naturalHeight }); + img.onerror = () => + resolve({ + width: STICKER_INTRINSIC_SIZE_FALLBACK, + height: STICKER_INTRINSIC_SIZE_FALLBACK, + }); + img.src = url; + }); +} + +export { resolveStickerId }; +export { resolveQueryToRegions, getRegionLabel } from "./providers/flags"; +export type { + StickerBrowseResult, + StickerBrowseSection, + StickerCategory, + StickerItem, + StickerProvider, + StickerResolveOptions, + StickerSearchResult, +} from "./types"; -- cgit v1.3.1