summaryrefslogtreecommitdiff
path: root/apps/web/src/stores/stickers-store.ts
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-03-25 16:47:22 +0100
committerMaze Winther <mazewinther@gmail.com>2026-03-29 15:48:22 +0200
commit8db3bead13441a5882d8689dfc6a9f09c82375d9 (patch)
treee9d0cafa93904ffcbec3188c37961bcc16c6422d /apps/web/src/stores/stickers-store.ts
parent39ea298a9ccba3104c549c0ca079c231981811f5 (diff)
downloadopencut-8db3bead13441a5882d8689dfc6a9f09c82375d9.tar.gz
feat: masks, properties refactor, shaders, storage migrations, and more
Diffstat (limited to 'apps/web/src/stores/stickers-store.ts')
-rw-r--r--apps/web/src/stores/stickers-store.ts177
1 files changed, 98 insertions, 79 deletions
diff --git a/apps/web/src/stores/stickers-store.ts b/apps/web/src/stores/stickers-store.ts
index 3f713b3d..7a678084 100644
--- a/apps/web/src/stores/stickers-store.ts
+++ b/apps/web/src/stores/stickers-store.ts
@@ -1,16 +1,20 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
-import { EditorCore } from "@/core";
-import { searchStickers as searchStickersFromProviders } from "@/lib/stickers";
-import type { StickerSearchResult } from "@/lib/stickers";
-import { buildStickerElement } from "@/lib/timeline/element-utils";
+import {
+ browseAll,
+ browseCategory,
+ searchAll,
+ searchStickers as searchStickersFromProviders,
+} from "@/lib/stickers";
+import type { StickerBrowseResult, StickerSearchResult } from "@/lib/stickers";
import { STICKER_CATEGORIES } from "@/constants/sticker-constants";
-import type { StickerCategory } from "@/types/stickers";
+import type { StickerCategory } from "@/lib/stickers/types";
import { registerDefaultStickerProviders } from "@/lib/stickers/providers";
-import { hasProvider } from "@/lib/stickers/registry";
+import { stickersRegistry } from "@/lib/stickers/registry";
import { parseStickerId } from "@/lib/stickers/sticker-id";
const MAX_RECENT_STICKERS = 50;
+let browseRequestVersion = 0;
function isValidStickerId(value: unknown): value is string {
if (typeof value !== "string") {
@@ -19,7 +23,7 @@ function isValidStickerId(value: unknown): value is string {
try {
const parsed = parseStickerId({ stickerId: value });
- return hasProvider({ providerId: parsed.providerId });
+ return stickersRegistry.has(parsed.providerId);
} catch {
return false;
}
@@ -60,20 +64,15 @@ interface StickersStore {
selectedCategory: StickerCategory;
viewMode: ViewMode;
searchResults: StickerSearchResult | null;
+ browseContent: StickerBrowseResult | null;
recentStickers: string[];
isSearching: boolean;
- addingSticker: string | null;
+ isBrowsing: boolean;
setSearchQuery: ({ query }: { query: string }) => void;
setSelectedCategory: ({ category }: { category: StickerCategory }) => void;
searchStickers: ({ query }: { query: string }) => Promise<void>;
- addStickerToTimeline: ({
- stickerId,
- name,
- }: {
- stickerId: string;
- name?: string;
- }) => void;
+ browseStickers: () => Promise<void>;
addToRecentStickers: ({ stickerId }: { stickerId: string }) => void;
clearRecentStickers: () => void;
}
@@ -86,22 +85,34 @@ export const useStickersStore = create<StickersStore>()(
viewMode: "browse",
searchResults: null,
+ browseContent: null,
recentStickers: [],
isSearching: false,
- addingSticker: null,
+ isBrowsing: false,
setSearchQuery: ({ query }) => set({ searchQuery: query }),
- setSelectedCategory: ({ category }) =>
- set({
- selectedCategory: category in STICKER_CATEGORIES ? category : "all",
- viewMode: "browse",
- }),
+ setSelectedCategory: ({ category }) => {
+ set({
+ selectedCategory: category in STICKER_CATEGORIES ? category : "all",
+ browseContent: null,
+ });
+
+ const query = get().searchQuery.trim();
+ if (query) {
+ void get().searchStickers({ query });
+ return;
+ }
+
+ void get().browseStickers();
+ },
searchStickers: async ({ query }: { query: string }) => {
- if (!query.trim()) {
+ const trimmedQuery = query.trim();
+ if (!trimmedQuery) {
set({ searchResults: null, viewMode: "browse" });
+ await get().browseStickers();
return;
}
@@ -111,12 +122,17 @@ export const useStickersStore = create<StickersStore>()(
set({ isSearching: true, viewMode: "search" });
try {
- const results = await searchStickersFromProviders({
- query,
- category: selectedCategory,
- limit: 100,
- });
- set({ searchResults: results });
+ if (selectedCategory === "all") {
+ const browseContent = await searchAll({ query: trimmedQuery });
+ set({ browseContent, searchResults: null });
+ } else {
+ const results = await searchStickersFromProviders({
+ query: trimmedQuery,
+ category: selectedCategory,
+ limit: 100,
+ });
+ set({ searchResults: results });
+ }
} catch (error) {
console.error("Search failed:", error);
set({ searchResults: null });
@@ -125,67 +141,70 @@ export const useStickersStore = create<StickersStore>()(
}
},
- addStickerToTimeline: ({
- stickerId,
- name,
- }: {
- stickerId: string;
- name?: string;
- }) => {
- set({ addingSticker: stickerId });
- try {
- const editor = EditorCore.getInstance();
- const currentTime = editor.playback.getCurrentTime();
- const tracks = editor.timeline.getTracks();
+ browseStickers: async () => {
+ const version = ++browseRequestVersion;
+ const category = get().selectedCategory;
+ const selectedCategory =
+ category in STICKER_CATEGORIES ? category : "all";
- const stickerTrack = tracks.find((t) => t.type === "sticker");
- let trackId: string;
+ set({ isBrowsing: true, viewMode: "browse" });
+ try {
+ const browseContent =
+ selectedCategory === "all"
+ ? await browseAll({
+ recentStickers: get().recentStickers,
+ })
+ : await browseCategory({
+ category: selectedCategory,
+ });
- if (stickerTrack) {
- trackId = stickerTrack.id;
- } else {
- trackId = editor.timeline.addTrack({ type: "sticker" });
- }
+ if (version !== browseRequestVersion) return;
+ set({ browseContent });
+ } catch (error) {
+ if (version !== browseRequestVersion) return;
+ console.error("Browse failed:", error);
+ set({ browseContent: null });
+ } finally {
+ if (version === browseRequestVersion) {
+ set({ isBrowsing: false });
+ }
+ }
+ },
- const element = buildStickerElement({
- stickerId,
- name,
- startTime: currentTime,
- });
- editor.timeline.insertElement({
- placement: { mode: "explicit", trackId },
- element,
- });
+ addToRecentStickers: ({ stickerId }: { stickerId: string }) => {
+ const sanitizedStickerIds = sanitizeRecentStickers({
+ recentStickers: [stickerId],
+ });
+ if (sanitizedStickerIds.length === 0) {
+ return;
+ }
- get().addToRecentStickers({ stickerId });
- } finally {
- set({ addingSticker: null });
- }
- },
+ set((state) => {
+ const recent = [
+ sanitizedStickerIds[0],
+ ...state.recentStickers.filter((s) => s !== sanitizedStickerIds[0]),
+ ];
+ return {
+ recentStickers: recent.slice(0, MAX_RECENT_STICKERS),
+ };
+ });
- addToRecentStickers: ({ stickerId }: { stickerId: string }) => {
- const sanitizedStickerIds = sanitizeRecentStickers({
- recentStickers: [stickerId],
- });
- if (sanitizedStickerIds.length === 0) {
- return;
- }
+ if (get().viewMode === "browse" && get().selectedCategory === "all") {
+ void get().browseStickers();
+ }
+ },
- set((state) => {
- const recent = [
- sanitizedStickerIds[0],
- ...state.recentStickers.filter((s) => s !== sanitizedStickerIds[0]),
- ];
- return {
- recentStickers: recent.slice(0, MAX_RECENT_STICKERS),
- };
- });
- },
+ clearRecentStickers: () => {
+ set({ recentStickers: [] });
- clearRecentStickers: () => set({ recentStickers: [] }),
+ if (get().viewMode === "browse" && get().selectedCategory === "all") {
+ void get().browseStickers();
+ }
+ },
}),
{
name: "stickers-settings",
+ version: 1,
migrate: (persistedState) => {
if (
typeof persistedState === "object" &&