import {useMemo} from 'react' import {useDeeplink} from '../navigation/useDeeplink' /** * Structured description of an intent, following the Shopify Intents API * URI format: `action:type,value` * * @see https://shopify.dev/docs/api/admin-extensions/latest/target-apis/utility-apis/intents-api */ export interface IntentQuery { /** Verb describing the operation (e.g., 'try_on', 'create', 'edit') */ action: string /** Resource type identifier (e.g., 'shopify/Product', 'shop/UserImage') */ type: string /** Resource GID (e.g., 'gid://shopify/Product/123') if applicable */ value: string | null } export interface UseIntentReturn { /** Parsed intent query, or null if no intent was passed */ query: IntentQuery | null /** Additional JSON data passed with the intent, or null */ data: {[key: string]: unknown} | null } /** * Parses an intent passed to this mini via deeplink. * * Follows the Shopify Intents API URI format (`action:type,value`) with * an optional JSON data payload passed separately. * * @see https://shopify.dev/docs/api/admin-extensions/latest/target-apis/utility-apis/intents-api * * Deeplink format: * ?intentQuery=action:type,value&intentData={"key":"value"} * * Examples: * ?intentQuery=try_on:shopify/Product,gid://shopify/Product/123 * ?intentQuery=create:shopify/Product * ?intentQuery=edit:shopify/Product,gid://shopify/Product/123&intentData={"variantId":"456"} * * Shorthand GID syntax (type inferred from GID): * ?intentQuery=edit:gid://shopify/Product/123 * ?intentQuery=create:gid://shopify/Product * * Use this hook to receive intents from the host app (Host → Mini direction). */ export function useIntent(): UseIntentReturn { const {queryParams} = useDeeplink() return useMemo(() => { const raw = queryParams?.intentQuery if (!raw) return {query: null, data: null} let decoded: string try { decoded = decodeURIComponent(raw) } catch { decoded = raw } const colonIdx = decoded.indexOf(':') if (colonIdx === -1) return {query: null, data: null} const action = decoded.slice(0, colonIdx) if (!action) return {query: null, data: null} const rest = decoded.slice(colonIdx + 1) const commaIdx = rest.indexOf(',') let type = commaIdx === -1 ? rest : rest.slice(0, commaIdx) let value = commaIdx === -1 ? null : rest.slice(commaIdx + 1) || null // Shorthand GID syntax: edit:gid://shopify/Product/123 // Infer type from GID and use full GID as value const gidMatch = type.match(/^gid:\/\/shopify\/(\w+)(?:\/(.+))?$/) if (gidMatch) { type = `shopify/${gidMatch[1]}` value = gidMatch[2] ? `gid://shopify/${gidMatch[1]}/${gidMatch[2]}` : null } if (!type) return {query: null, data: null} let data: {[key: string]: unknown} | null = null const rawData = queryParams?.intentData if (rawData) { let parsed: unknown try { parsed = JSON.parse(rawData) } catch { try { parsed = JSON.parse(decodeURIComponent(rawData)) } catch { // malformed JSON — ignore } } if ( parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ) { data = parsed as {[key: string]: unknown} } } return { query: {action, type, value}, data, } }, [queryParams]) } /** * Parses an intent passed to the Mini via deeplink, following the [Shopify Intents API](https://shopify.dev/docs/api/admin-extensions/latest/target-apis/utility-apis/intents-api) URI format (`action:type,value`). Returns the parsed `query` (`action`, `type`, `value`) and optional `data` payload. Use this hook to receive intents from the host app (Host → Mini direction), for example when the Shop app launches a Mini to handle a virtual try-on for a specific product. * @publicDocs */ export type UseIntentGeneratedType = () => UseIntentReturn