"use client" import { cn } from "@lib/utils" import { dmSans125ClassName } from "@/lib/fonts" import { AppleShortcutsIcon } from "@/components/integration-icons" import { authClient } from "@lib/auth" import { useAuth } from "@lib/auth-context" import { generateId } from "@lib/generate-id" import { ADD_MEMORY_SHORTCUT_URL, SEARCH_MEMORY_SHORTCUT_URL, } from "@lib/constants" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogPortal, } from "@ui/components/dialog" import { useMutation } from "@tanstack/react-query" import { Check, Copy, Loader, Plus, Search } from "lucide-react" import Image from "next/image" import { useId, useState } from "react" import { toast } from "sonner" type ShortcutType = "add" | "search" function PillButton({ children, onClick, disabled, className, }: { children: React.ReactNode onClick?: (e: React.MouseEvent) => void disabled?: boolean className?: string }) { return ( {children} ) } export function useShortcutsConnect() { const { org } = useAuth() const [showSetupDialog, setShowSetupDialog] = useState(false) const [apiKey, setApiKey] = useState("") const [copied, setCopied] = useState(false) const [shortcutType, setShortcutType] = useState(null) const copyApiKey = async (key: string) => { try { await navigator.clipboard.writeText(key) setCopied(true) setTimeout(() => setCopied(false), 2000) toast.success("API key copied to clipboard!") } catch { toast.error("Failed to copy API key") } } const createKeyMutation = useMutation({ mutationFn: async () => { if (!org?.id) throw new Error("Organization ID is required") const res = await authClient.apiKey.create({ metadata: { organizationId: org.id, type: "ios-shortcut" }, name: `ios-${generateId().slice(0, 8)}`, prefix: `sm_${org.id}_`, }) if (res.error) throw new Error(res.error.message ?? "Failed to create API key") if (!res.data?.key) throw new Error("API key missing from response") return res.data.key }, onSuccess: (key) => { setApiKey(key) setShowSetupDialog(true) setCopied(false) copyApiKey(key) }, onError: (error) => { toast.error("Failed to create API key", { description: error instanceof Error ? error.message : "Unknown error", }) }, }) const connect = (type: ShortcutType) => { if (createKeyMutation.isPending) return setShortcutType(type) createKeyMutation.mutate() } const openShortcut = () => { if (shortcutType === "add") { window.open(ADD_MEMORY_SHORTCUT_URL, "_blank") } else if (shortcutType === "search") { window.open(SEARCH_MEMORY_SHORTCUT_URL, "_blank") } } const dialog = ( copyApiKey(apiKey)} onOpenChange={(open) => { setShowSetupDialog(open) if (!open) { setShortcutType(null) setApiKey("") setCopied(false) } }} onOpenShortcut={openShortcut} open={showSetupDialog} shortcutType={shortcutType} /> ) return { connect, dialog, isPending: createKeyMutation.isPending, pendingType: createKeyMutation.isPending ? shortcutType : null, } } export type ShortcutsConnectController = ReturnType export function ShortcutsConnectButtons({ controller, }: { controller: ShortcutsConnectController }) { const { connect, isPending, pendingType } = controller return ( { e.stopPropagation() connect("add") }} disabled={isPending} > {pendingType === "add" ? ( ) : ( )} {pendingType === "add" ? "Creating..." : "Add memory shortcut"} { e.stopPropagation() connect("search") }} disabled={isPending} > {pendingType === "search" ? ( ) : ( )} {pendingType === "search" ? "Creating..." : "Search memory shortcut"} ) } function ShortcutsSetupDialog({ apiKey, copied, onCopy, onOpenChange, onOpenShortcut, open, shortcutType, }: { apiKey: string copied: boolean onCopy: () => void onOpenChange: (open: boolean) => void onOpenShortcut: () => void open: boolean shortcutType: ShortcutType | null }) { const apiKeyId = useId() return ( Setup Apple Shortcut Your API Key {copied ? ( ) : ( )} Follow these steps: {[ 'Click "Add to Shortcuts" below to open the shortcut', "Paste your API key when prompted", "Start using your shortcut!", ].map((text, i) => ( {i + 1} {text} ))} Add to Shortcuts ) } export function ShortcutsDetail() { const controller = useShortcutsConnect() const { connect, dialog, isPending, pendingType } = controller return ( <> Apple Shortcuts Add memories directly from iPhone, iPad or Mac connect("add")} disabled={isPending}> {pendingType === "add" ? ( ) : ( )} {pendingType === "add" ? "Creating..." : "Add memory shortcut"} connect("search")} disabled={isPending}> {pendingType === "search" ? ( ) : ( )} {pendingType === "search" ? "Creating..." : "Search memory shortcut"} {dialog} > ) }
{text}
Apple Shortcuts
Add memories directly from iPhone, iPad or Mac