diff --git a/apps/web/src/components/editor/panels/assets/views/stickers.tsx b/apps/web/src/components/editor/panels/assets/views/stickers.tsx
index 68ba91d2..671effec 100644
--- a/apps/web/src/components/editor/panels/assets/views/stickers.tsx
+++ b/apps/web/src/components/editor/panels/assets/views/stickers.tsx
@@ -4,144 +4,104 @@ import Image from "next/image";
import type { CSSProperties } from "react";
import { useEffect, useMemo, useState } from "react";
import { toast } from "sonner";
+import { PanelView } from "@/components/editor/panels/assets/views/base-view";
import { DraggableItem } from "@/components/editor/panels/assets/draggable-item";
-import { PanelBaseView as BaseView } from "@/components/editor/panels/panel-base-view";
import { Button } from "@/components/ui/button";
-import { InputWithBack } from "@/components/ui/input-with-back";
-import { ScrollArea } from "@/components/ui/scroll-area";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
-import { STICKER_CATEGORIES } from "@/constants/stickers-constants";
-import { useInfiniteScroll } from "@/hooks/use-infinite-scroll";
import {
- buildIconSvgUrl,
- getIconSvgUrl,
- ICONIFY_HOSTS,
- POPULAR_COLLECTIONS,
-} from "@/lib/iconify-api";
+ resolveStickerId,
+ type StickerItem as StickerData,
+} from "@/lib/stickers";
import { useStickersStore } from "@/stores/stickers-store";
-import type { StickerCategory } from "@/types/stickers";
import { cn } from "@/utils/ui";
import {
- ArrowRightIcon,
HappyIcon,
ClockIcon,
- LayoutGridIcon,
MultiplicationSignIcon,
- SparklesIcon,
- HashtagIcon,
+ Search01Icon,
} from "@hugeicons/core-free-icons";
import { HugeiconsIcon } from "@hugeicons/react";
import { Spinner } from "@/components/ui/spinner";
-
-function isStickerCategory(value: string): value is StickerCategory {
- return STICKER_CATEGORIES.includes(value as StickerCategory);
-}
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { OcSlidersVerticalIcon } from "@opencut/ui/icons";
+import type { StickerCategory } from "@/types/stickers";
+import { STICKER_CATEGORIES } from "@/constants/sticker-constants";
+import { parseStickerId } from "@/lib/stickers/sticker-id";
export function StickersView() {
const { selectedCategory, setSelectedCategory } = useStickersStore();
return (
-
{
- if (isStickerCategory(v)) {
- setSelectedCategory({ category: v });
- }
- }}
- tabs={[
- {
- value: "all",
- label: "All",
- icon: ,
- content: ,
- },
- {
- value: "general",
- label: "Icons",
- icon: ,
- content: ,
- },
- {
- value: "brands",
- label: "Brands",
- icon: ,
- content: ,
- },
- {
- value: "emoji",
- label: "Emoji",
- icon: ,
- content: ,
- },
- ]}
- className="flex h-full flex-col overflow-hidden p-0"
- />
+
+
+
+
+
+
+
+ }
+ >
+
-
- {
- if (!expanded && isInCollection) {
- setSelectedCollection({ collection: null });
- }
- }}
- placeholder={
- category === "all"
- ? "Search all stickers"
- : category === "general"
- ? "Search icons"
- : category === "brands"
- ? "Search brands"
- : "Search Emojis"
- }
- value={localSearchQuery}
- onChange={setLocalSearchQuery}
- disableAnimation={true}
- />
-
-
-
-
-
- {recentStickers.length > 0 && viewMode === "browse" && (
-
-
-
-
Recent
-
-
-
-
-
-
- Clear recent stickers
-
-
-
-
-
-
- )}
-
- {viewMode === "collection" && selectedCollection && (
-
- {isLoadingCollection ? (
-
-
-
- ) : showCollectionItems ? (
-
- ) : (
-
-
-
- )}
-
- )}
-
- {viewMode === "search" && (
-
- {isSearching ? (
-
-
-
- ) : searchResults?.icons.length ? (
- <>
-
-
- {searchResults.total} results
-
-
-
+ {recentStickerItems.length > 0 && viewMode === "browse" && (
+
+
+
+
Recent
+
+
+
+
- )}
-
- {viewMode === "browse" && !selectedCollection && (
-
- {isLoadingCollections ? (
-
-
-
- ) : (
- <>
- {category !== "all" && (
-
-
- setSelectedCollection({ collection: prefix })
- }
- />
-
- )}
+
+
+
+ Clear recent stickers
+
+
+
+
+
+
+ )}
- {category === "all" && filteredCollections.length > 0 && (
-
-
- setSelectedCollection({ collection: prefix })
- }
- />
-
- )}
- >
- )}
+ {viewMode === "search" && (
+
+ {isSearching ? (
+
+
+
+ ) : searchResults?.items.length ? (
+
+
+
+ {searchResults.total} results
+
- )}
-
-
-
+
+
+ ) : searchQuery ? (
+
+ ) : null}
+
+ )}
);
}
-interface CollectionItemProps {
- title: string;
- subtitle: string;
- onClick: () => void;
-}
-
-function CollectionItem({ title, subtitle, onClick }: CollectionItemProps) {
- return (
-
-
-
-
- );
-}
-
interface StickerItemProps {
- iconName: string;
- onAdd: (iconName: string) => void;
- isAdding?: boolean;
- capSize?: boolean;
+ item: StickerData;
+ shouldCapSize?: boolean;
}
-function StickerItem({
- iconName,
- onAdd,
- isAdding,
- capSize = false,
-}: StickerItemProps) {
- const [imageError, setImageError] = useState(false);
- const [hostIndex, setHostIndex] = useState(0);
+function StickerItem({ item, shouldCapSize = false }: StickerItemProps) {
+ const { addingSticker, addStickerToTimeline } = useStickersStore();
+ const isAdding = addingSticker === item.id;
+ const [hasImageError, setHasImageError] = useState(false);
useEffect(() => {
- if (!iconName) {
+ if (!item.id) {
return;
}
- setImageError(false);
- setHostIndex(0);
- }, [iconName]);
+ setHasImageError(false);
+ }, [item.id]);
- const displayName = iconName.split(":")[1] || iconName;
- const collectionPrefix = iconName.split(":")[0];
+ const displayName = item.name;
+
+ const handleAdd = async () => {
+ try {
+ await addStickerToTimeline({
+ stickerId: item.id,
+ name: item.name,
+ });
+ } catch (error) {
+ console.error("Failed to add sticker:", error);
+ toast.error("Failed to add sticker to timeline");
+ }
+ };
- const preview = imageError ? (
+ const preview = hasImageError ? (
{displayName}
@@ -536,21 +249,13 @@ function StickerItem({
) : (
{
- const next = hostIndex + 1;
- if (next < ICONIFY_HOSTS.length) {
- setHostIndex(next);
- } else {
- setImageError(true);
- }
+ setHasImageError(true);
}}
loading="lazy"
unoptimized
@@ -572,44 +272,63 @@ function StickerItem({
);
return (
-
-
-
-
onAdd(iconName)}
- aspectRatio={1}
- shouldShowLabel={false}
- isRounded={true}
- variant="card"
- className=""
- containerClassName="w-full"
- />
- {isAdding && (
-
-
-
- )}
-
-
-
-
-
{displayName}
-
{collectionPrefix}
+
+
+ {isAdding && (
+
+
-
-
+ )}
+
+ );
+}
+
+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;
+}): StickerData | 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;
+ }
+}
diff --git a/apps/web/src/components/editor/panels/assets/views/text.tsx b/apps/web/src/components/editor/panels/assets/views/text.tsx
index 6360c363..3673acc3 100644
--- a/apps/web/src/components/editor/panels/assets/views/text.tsx
+++ b/apps/web/src/components/editor/panels/assets/views/text.tsx
@@ -1,5 +1,5 @@
import { DraggableItem } from "@/components/editor/panels/assets/draggable-item";
-import { PanelBaseView as BaseView } from "@/components/editor/panels/panel-base-view";
+import { PanelView } from "@/components/editor/panels/assets/views/base-view";
import { useEditor } from "@/hooks/use-editor";
import { DEFAULT_TEXT_ELEMENT } from "@/constants/text-constants";
import { buildTextElement } from "@/lib/timeline/element-utils";
@@ -23,7 +23,7 @@ export function TextView() {
};
return (
-
+
-
+
);
}
--
cgit v1.3.1