@shopify/shop-minis-react 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/atoms/content-wrapper.js.map +1 -1
- package/dist/hooks/content/useCreateImageContent.js +28 -18
- package/dist/hooks/content/useCreateImageContent.js.map +1 -1
- package/dist/hooks/navigation/useCloseMini.js +8 -5
- package/dist/hooks/navigation/useCloseMini.js.map +1 -1
- package/dist/hooks/util/useSafeArea.js.map +1 -1
- package/package.json +1 -1
- package/src/components/atoms/content-wrapper.tsx +3 -0
- package/src/hooks/content/useCreateImageContent.test.ts +33 -0
- package/src/hooks/content/useCreateImageContent.ts +42 -1
- package/src/hooks/navigation/useCloseMini.ts +6 -1
- package/src/hooks/util/useSafeArea.ts +3 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content-wrapper.js","sources":["../../../src/components/atoms/content-wrapper.tsx"],"sourcesContent":["import {useContent} from '../../hooks/content/useContent'\nimport {Content} from '../../types'\n\nimport {ContentMonitor} from './content-monitor'\n\ninterface BaseContentWrapperProps {\n children: ({\n content,\n loading,\n }: {\n content?: Content\n loading: boolean\n }) => React.JSX.Element | null\n}\n\ninterface PublicIdContentWrapperProps extends BaseContentWrapperProps {\n publicId: string\n externalId?: never\n}\n\ninterface ExternalIdContentWrapperProps extends BaseContentWrapperProps {\n externalId: string\n publicId?: never\n}\n\ntype ContentWrapperProps =\n | PublicIdContentWrapperProps\n | ExternalIdContentWrapperProps\n\n// It's too messy in the docs to show the complete types here so we show a simplified version\nexport interface ContentWrapperPropsForDocs extends BaseContentWrapperProps {\n publicId?: string\n externalId?: string\n}\n\nexport function ContentWrapper({\n publicId,\n externalId,\n children,\n}: ContentWrapperProps) {\n const {content, loading} = useContent({\n identifiers: [{publicId, externalId}],\n skip: !publicId && !externalId,\n })\n\n const contentItem = content?.[0]\n\n if (loading || !contentItem) {\n return children({loading})\n }\n\n return (\n <ContentMonitor publicId={contentItem.publicId}>\n {children({content: contentItem, loading})}\n </ContentMonitor>\n )\n}\n"],"names":["ContentWrapper","publicId","externalId","children","content","loading","useContent","contentItem","jsx","ContentMonitor"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"content-wrapper.js","sources":["../../../src/components/atoms/content-wrapper.tsx"],"sourcesContent":["import {useContent} from '../../hooks/content/useContent'\nimport {Content} from '../../types'\n\nimport {ContentMonitor} from './content-monitor'\n\ninterface BaseContentWrapperProps {\n /** A render function that receives the content data and loading state, and returns a React element. */\n children: ({\n content,\n loading,\n }: {\n content?: Content\n loading: boolean\n }) => React.JSX.Element | null\n}\n\ninterface PublicIdContentWrapperProps extends BaseContentWrapperProps {\n publicId: string\n externalId?: never\n}\n\ninterface ExternalIdContentWrapperProps extends BaseContentWrapperProps {\n externalId: string\n publicId?: never\n}\n\ntype ContentWrapperProps =\n | PublicIdContentWrapperProps\n | ExternalIdContentWrapperProps\n\n// It's too messy in the docs to show the complete types here so we show a simplified version\nexport interface ContentWrapperPropsForDocs extends BaseContentWrapperProps {\n /** The public ID of the content item (use this OR externalId). */\n publicId?: string\n /** The external ID of the content item (use this OR publicId). */\n externalId?: string\n}\n\nexport function ContentWrapper({\n publicId,\n externalId,\n children,\n}: ContentWrapperProps) {\n const {content, loading} = useContent({\n identifiers: [{publicId, externalId}],\n skip: !publicId && !externalId,\n })\n\n const contentItem = content?.[0]\n\n if (loading || !contentItem) {\n return children({loading})\n }\n\n return (\n <ContentMonitor publicId={contentItem.publicId}>\n {children({content: contentItem, loading})}\n </ContentMonitor>\n )\n}\n"],"names":["ContentWrapper","publicId","externalId","children","content","loading","useContent","contentItem","jsx","ContentMonitor"],"mappings":";;;AAsCO,SAASA,EAAe;AAAA,EAC7B,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AACF,GAAwB;AACtB,QAAM,EAAC,SAAAC,GAAS,SAAAC,EAAO,IAAIC,EAAW;AAAA,IACpC,aAAa,CAAC,EAAC,UAAAL,GAAU,YAAAC,GAAW;AAAA,IACpC,MAAM,CAACD,KAAY,CAACC;AAAA,EAAA,CACrB,GAEKK,IAAcH,IAAU,CAAC;AAE3B,SAAAC,KAAW,CAACE,IACPJ,EAAS,EAAC,SAAAE,GAAQ,IAIzB,gBAAAG,EAACC,GAAe,EAAA,UAAUF,EAAY,UACnC,UAASJ,EAAA,EAAC,SAASI,GAAa,SAAAF,EAAO,CAAC,EAC3C,CAAA;AAEJ;"}
|
|
@@ -1,34 +1,44 @@
|
|
|
1
|
-
import { useState as
|
|
2
|
-
import { useHandleAction as
|
|
3
|
-
import { useShopActions as
|
|
4
|
-
import { useImageUpload as
|
|
5
|
-
const
|
|
6
|
-
const { createContent: t } =
|
|
1
|
+
import { useState as f, useCallback as I } from "react";
|
|
2
|
+
import { useHandleAction as w } from "../../internal/useHandleAction.js";
|
|
3
|
+
import { useShopActions as C } from "../../internal/useShopActions.js";
|
|
4
|
+
import { useImageUpload as y } from "../storage/useImageUpload.js";
|
|
5
|
+
const v = () => {
|
|
6
|
+
const { createContent: t } = C(), { uploadImage: o } = y(), [n, a] = f(!1), i = I(
|
|
7
7
|
async (s) => {
|
|
8
|
-
|
|
9
|
-
const {
|
|
8
|
+
a(!0);
|
|
9
|
+
const {
|
|
10
|
+
image: e,
|
|
11
|
+
contentTitle: l,
|
|
12
|
+
visibility: m,
|
|
13
|
+
externalId: c,
|
|
14
|
+
description: p,
|
|
15
|
+
productIds: u
|
|
16
|
+
} = s;
|
|
10
17
|
if (!e.type)
|
|
11
18
|
throw new Error("Unable to determine file type");
|
|
12
19
|
if (!e.type.startsWith("image/"))
|
|
13
20
|
throw new Error("Invalid file type: must be an image");
|
|
14
|
-
const [
|
|
15
|
-
if (!
|
|
21
|
+
const [d] = await o(e), r = d.imageUrl;
|
|
22
|
+
if (!r)
|
|
16
23
|
throw new Error("Image upload failed");
|
|
17
|
-
const
|
|
24
|
+
const g = await t({
|
|
18
25
|
title: l,
|
|
19
|
-
imageUrl:
|
|
20
|
-
visibility: m
|
|
26
|
+
imageUrl: r,
|
|
27
|
+
visibility: m,
|
|
28
|
+
externalId: c,
|
|
29
|
+
description: p,
|
|
30
|
+
productIds: u
|
|
21
31
|
});
|
|
22
|
-
return
|
|
32
|
+
return a(!1), g;
|
|
23
33
|
},
|
|
24
|
-
[t,
|
|
34
|
+
[t, o]
|
|
25
35
|
);
|
|
26
36
|
return {
|
|
27
|
-
createImageContent:
|
|
28
|
-
loading:
|
|
37
|
+
createImageContent: w(i),
|
|
38
|
+
loading: n
|
|
29
39
|
};
|
|
30
40
|
};
|
|
31
41
|
export {
|
|
32
|
-
|
|
42
|
+
v as useCreateImageContent
|
|
33
43
|
};
|
|
34
44
|
//# sourceMappingURL=useCreateImageContent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCreateImageContent.js","sources":["../../../src/hooks/content/useCreateImageContent.ts"],"sourcesContent":["import {useCallback, useState} from 'react'\n\nimport {\n ContentVisibility,\n Content,\n ContentCreateUserErrors,\n} from '@shopify/shop-minis-platform'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\nimport {useImageUpload} from '../storage/useImageUpload'\n\ninterface CreateImageContentParams {\n image: File\n contentTitle: string\n visibility?: ContentVisibility[] | null\n}\n\ninterface UseCreateImageContentReturns {\n /**\n * Upload an image and create content.\n */\n createImageContent: (\n params: CreateImageContentParams\n ) => Promise<{data: Content; userErrors?: ContentCreateUserErrors[]}>\n /**\n * Whether the content is being created.\n */\n loading: boolean\n}\n\nexport const useCreateImageContent = (): UseCreateImageContentReturns => {\n const {createContent} = useShopActions()\n const {uploadImage} = useImageUpload()\n const [loading, setLoading] = useState(false)\n\n const createImageContent = useCallback(\n async (params: CreateImageContentParams) => {\n setLoading(true)\n\n const {image
|
|
1
|
+
{"version":3,"file":"useCreateImageContent.js","sources":["../../../src/hooks/content/useCreateImageContent.ts"],"sourcesContent":["import {useCallback, useState} from 'react'\n\nimport {\n ContentVisibility,\n Content,\n ContentCreateUserErrors,\n} from '@shopify/shop-minis-platform'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\nimport {useImageUpload} from '../storage/useImageUpload'\n\ninterface CreateImageContentParams {\n /**\n * The image file to upload.\n */\n image: File\n /**\n * The title for the content entry.\n */\n contentTitle: string\n /**\n * Visibility options for the content. Use `['DISCOVERABLE']` to appear in\n * recommendations, `['LINKABLE']` to enable shareable URLs, or both. Pass\n * `null` or `[]` to keep content private within your Mini.\n */\n visibility?: ContentVisibility[] | null\n /**\n * A unique identifier from your own system that lets you look up content\n * later via `ContentWrapper` using an ID you already know.\n * If not provided, the content can only be retrieved by its `publicId`.\n * Each `externalId` must be unique per Mini — creating content with a\n * duplicate returns a `DUPLICATE_EXTERNAL_ID` error.\n */\n externalId?: string\n /**\n * A text description for the content. Displayed alongside the image in Shop\n * surfaces such as feeds and content detail views. Use this to provide\n * context about the image, such as a caption or review text.\n */\n description?: string\n /**\n * An array of Shopify product GIDs (e.g. `'gid://shopify/Product/123'`) to\n * associate with the content. Maximum 20 products. Associated products\n * appear alongside the content, enabling shoppable content experiences.\n */\n productIds?: string[]\n}\n\ninterface UseCreateImageContentReturns {\n /**\n * Upload an image and create content.\n */\n createImageContent: (\n params: CreateImageContentParams\n ) => Promise<{data: Content; userErrors?: ContentCreateUserErrors[]}>\n /**\n * Whether the content is being created.\n */\n loading: boolean\n}\n\nexport const useCreateImageContent = (): UseCreateImageContentReturns => {\n const {createContent} = useShopActions()\n const {uploadImage} = useImageUpload()\n const [loading, setLoading] = useState(false)\n\n const createImageContent = useCallback(\n async (params: CreateImageContentParams) => {\n setLoading(true)\n\n const {\n image,\n contentTitle,\n visibility,\n externalId,\n description,\n productIds,\n } = params\n\n if (!image.type) {\n throw new Error('Unable to determine file type')\n }\n if (!image.type.startsWith('image/')) {\n throw new Error('Invalid file type: must be an image')\n }\n\n const [uploadImageResult] = await uploadImage(image)\n const uploadImageUrl = uploadImageResult.imageUrl\n\n if (!uploadImageUrl) {\n throw new Error('Image upload failed')\n }\n\n const createContentResult = await createContent({\n title: contentTitle,\n imageUrl: uploadImageUrl,\n visibility,\n externalId,\n description,\n productIds,\n })\n\n setLoading(false)\n\n return createContentResult\n },\n [createContent, uploadImage]\n )\n\n return {\n createImageContent: useHandleAction(createImageContent),\n loading,\n }\n}\n"],"names":["useCreateImageContent","createContent","useShopActions","uploadImage","useImageUpload","loading","setLoading","useState","createImageContent","useCallback","params","image","contentTitle","visibility","externalId","description","productIds","uploadImageResult","uploadImageUrl","createContentResult","useHandleAction"],"mappings":";;;;AA8DO,MAAMA,IAAwB,MAAoC;AACjE,QAAA,EAAC,eAAAC,EAAa,IAAIC,EAAe,GACjC,EAAC,aAAAC,EAAW,IAAIC,EAAe,GAC/B,CAACC,GAASC,CAAU,IAAIC,EAAS,EAAK,GAEtCC,IAAqBC;AAAA,IACzB,OAAOC,MAAqC;AAC1C,MAAAJ,EAAW,EAAI;AAET,YAAA;AAAA,QACJ,OAAAK;AAAA,QACA,cAAAC;AAAA,QACA,YAAAC;AAAA,QACA,YAAAC;AAAA,QACA,aAAAC;AAAA,QACA,YAAAC;AAAA,MAAA,IACEN;AAEA,UAAA,CAACC,EAAM;AACH,cAAA,IAAI,MAAM,+BAA+B;AAEjD,UAAI,CAACA,EAAM,KAAK,WAAW,QAAQ;AAC3B,cAAA,IAAI,MAAM,qCAAqC;AAGvD,YAAM,CAACM,CAAiB,IAAI,MAAMd,EAAYQ,CAAK,GAC7CO,IAAiBD,EAAkB;AAEzC,UAAI,CAACC;AACG,cAAA,IAAI,MAAM,qBAAqB;AAGjC,YAAAC,IAAsB,MAAMlB,EAAc;AAAA,QAC9C,OAAOW;AAAA,QACP,UAAUM;AAAA,QACV,YAAAL;AAAA,QACA,YAAAC;AAAA,QACA,aAAAC;AAAA,QACA,YAAAC;AAAA,MAAA,CACD;AAED,aAAAV,EAAW,EAAK,GAETa;AAAA,IACT;AAAA,IACA,CAAClB,GAAeE,CAAW;AAAA,EAC7B;AAEO,SAAA;AAAA,IACL,oBAAoBiB,EAAgBZ,CAAkB;AAAA,IACtD,SAAAH;AAAA,EACF;AACF;"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import { useCallback as s } from "react";
|
|
1
2
|
import { useHandleAction as i } from "../../internal/useHandleAction.js";
|
|
2
|
-
import { useShopActions as
|
|
3
|
-
const
|
|
4
|
-
const { closeMini:
|
|
3
|
+
import { useShopActions as n } from "../../internal/useShopActions.js";
|
|
4
|
+
const l = () => {
|
|
5
|
+
const { closeMini: e } = n(), o = i(e);
|
|
5
6
|
return {
|
|
6
|
-
|
|
7
|
+
// Wrapper prevents React SyntheticEvent args from leaking through
|
|
8
|
+
// when used directly as an event handler (e.g. onClick={closeMini})
|
|
9
|
+
closeMini: s(() => o(), [o])
|
|
7
10
|
};
|
|
8
11
|
};
|
|
9
12
|
export {
|
|
10
|
-
|
|
13
|
+
l as useCloseMini
|
|
11
14
|
};
|
|
12
15
|
//# sourceMappingURL=useCloseMini.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCloseMini.js","sources":["../../../src/hooks/navigation/useCloseMini.ts"],"sourcesContent":["import {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\n\ninterface UseCloseMiniReturns {\n /**\n * Closes the Mini viewer.\n */\n closeMini: () => void\n}\n\nexport const useCloseMini = (): UseCloseMiniReturns => {\n const {closeMini} = useShopActions()\n\n return {\n closeMini:
|
|
1
|
+
{"version":3,"file":"useCloseMini.js","sources":["../../../src/hooks/navigation/useCloseMini.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {useHandleAction} from '../../internal/useHandleAction'\nimport {useShopActions} from '../../internal/useShopActions'\n\ninterface UseCloseMiniReturns {\n /**\n * Closes the Mini viewer.\n */\n closeMini: () => void\n}\n\nexport const useCloseMini = (): UseCloseMiniReturns => {\n const {closeMini} = useShopActions()\n const handleClose = useHandleAction(closeMini)\n\n return {\n // Wrapper prevents React SyntheticEvent args from leaking through\n // when used directly as an event handler (e.g. onClick={closeMini})\n closeMini: useCallback(() => handleClose(), [handleClose]),\n }\n}\n"],"names":["useCloseMini","closeMini","useShopActions","handleClose","useHandleAction","useCallback"],"mappings":";;;AAYO,MAAMA,IAAe,MAA2B;AAC/C,QAAA,EAAC,WAAAC,EAAS,IAAIC,EAAe,GAC7BC,IAAcC,EAAgBH,CAAS;AAEtC,SAAA;AAAA;AAAA;AAAA,IAGL,WAAWI,EAAY,MAAMF,EAAe,GAAA,CAACA,CAAW,CAAC;AAAA,EAC3D;AACF;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSafeArea.js","sources":["../../../src/hooks/util/useSafeArea.ts"],"sourcesContent":["import type {SafeAreaInsets} from '@shopify/shop-minis-platform'\n\nconst DEFAULT_INSETS: SafeAreaInsets = {top: 0, right: 0, bottom: 0, left: 0}\n\n/**\n * Returns the safe area insets for the current device.\n *\n * These values represent the areas of the screen that are obscured by\n * system UI (home indicator, navigation bar, etc). Use them to ensure\n * content isn't hidden behind system chrome.\n *\n * The values are also available as CSS custom properties:\n * `--safe-area-inset-top`, `--safe-area-inset-right`,\n * `--safe-area-inset-bottom`, `--safe-area-inset-left`\n *\n * @example\n * ```tsx\n * const {bottom} = useSafeArea()\n * return <div style={{paddingBottom: bottom}}>Content</div>\n * ```\n */\nexport function useSafeArea(): SafeAreaInsets {\n return window.minisParams?.safeAreaInsets ?? DEFAULT_INSETS\n}\n"],"names":["DEFAULT_INSETS","useSafeArea"],"mappings":"
|
|
1
|
+
{"version":3,"file":"useSafeArea.js","sources":["../../../src/hooks/util/useSafeArea.ts"],"sourcesContent":["import type {SafeAreaInsets} from '@shopify/shop-minis-platform'\n\n/** @docs Return type for the useSafeArea hook */\nexport type UseSafeAreaGeneratedType = SafeAreaInsets\n\nconst DEFAULT_INSETS: SafeAreaInsets = {top: 0, right: 0, bottom: 0, left: 0}\n\n/**\n * Returns the safe area insets for the current device.\n *\n * These values represent the areas of the screen that are obscured by\n * system UI (home indicator, navigation bar, etc). Use them to ensure\n * content isn't hidden behind system chrome.\n *\n * The values are also available as CSS custom properties:\n * `--safe-area-inset-top`, `--safe-area-inset-right`,\n * `--safe-area-inset-bottom`, `--safe-area-inset-left`\n *\n * @example\n * ```tsx\n * const {bottom} = useSafeArea()\n * return <div style={{paddingBottom: bottom}}>Content</div>\n * ```\n */\nexport function useSafeArea(): SafeAreaInsets {\n return window.minisParams?.safeAreaInsets ?? DEFAULT_INSETS\n}\n"],"names":["DEFAULT_INSETS","useSafeArea"],"mappings":"AAKA,MAAMA,IAAiC,EAAC,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,EAAC;AAmBrE,SAASC,IAA8B;AACrC,SAAA,OAAO,aAAa,kBAAkBD;AAC/C;"}
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@ import {Content} from '../../types'
|
|
|
4
4
|
import {ContentMonitor} from './content-monitor'
|
|
5
5
|
|
|
6
6
|
interface BaseContentWrapperProps {
|
|
7
|
+
/** A render function that receives the content data and loading state, and returns a React element. */
|
|
7
8
|
children: ({
|
|
8
9
|
content,
|
|
9
10
|
loading,
|
|
@@ -29,7 +30,9 @@ type ContentWrapperProps =
|
|
|
29
30
|
|
|
30
31
|
// It's too messy in the docs to show the complete types here so we show a simplified version
|
|
31
32
|
export interface ContentWrapperPropsForDocs extends BaseContentWrapperProps {
|
|
33
|
+
/** The public ID of the content item (use this OR externalId). */
|
|
32
34
|
publicId?: string
|
|
35
|
+
/** The external ID of the content item (use this OR publicId). */
|
|
33
36
|
externalId?: string
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -120,6 +120,9 @@ describe('useCreateImageContent', () => {
|
|
|
120
120
|
title: 'Test Content',
|
|
121
121
|
imageUrl: 'https://example.com/uploaded-image.jpg',
|
|
122
122
|
visibility: ['DISCOVERABLE'],
|
|
123
|
+
externalId: undefined,
|
|
124
|
+
description: undefined,
|
|
125
|
+
productIds: undefined,
|
|
123
126
|
})
|
|
124
127
|
expect(mockCreateContent).toHaveBeenCalledTimes(1)
|
|
125
128
|
})
|
|
@@ -253,6 +256,36 @@ describe('useCreateImageContent', () => {
|
|
|
253
256
|
title: 'Test Content',
|
|
254
257
|
imageUrl: 'https://example.com/uploaded-image.jpg',
|
|
255
258
|
visibility: null,
|
|
259
|
+
externalId: undefined,
|
|
260
|
+
description: undefined,
|
|
261
|
+
productIds: undefined,
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
it('passes externalId, description, and productIds to createContent', async () => {
|
|
266
|
+
const {result} = renderHook(() => useCreateImageContent())
|
|
267
|
+
|
|
268
|
+
const imageFile = new File(['test'], 'test.jpg', {type: 'image/jpeg'})
|
|
269
|
+
const params = {
|
|
270
|
+
image: imageFile,
|
|
271
|
+
contentTitle: 'Test Content',
|
|
272
|
+
visibility: ['DISCOVERABLE'] as any,
|
|
273
|
+
externalId: 'ext-123',
|
|
274
|
+
description: 'A test description',
|
|
275
|
+
productIds: ['gid://shopify/Product/1', 'gid://shopify/Product/2'],
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
await act(async () => {
|
|
279
|
+
await result.current.createImageContent(params)
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
expect(mockCreateContent).toHaveBeenCalledWith({
|
|
283
|
+
title: 'Test Content',
|
|
284
|
+
imageUrl: 'https://example.com/uploaded-image.jpg',
|
|
285
|
+
visibility: ['DISCOVERABLE'],
|
|
286
|
+
externalId: 'ext-123',
|
|
287
|
+
description: 'A test description',
|
|
288
|
+
productIds: ['gid://shopify/Product/1', 'gid://shopify/Product/2'],
|
|
256
289
|
})
|
|
257
290
|
})
|
|
258
291
|
|
|
@@ -11,9 +11,40 @@ import {useShopActions} from '../../internal/useShopActions'
|
|
|
11
11
|
import {useImageUpload} from '../storage/useImageUpload'
|
|
12
12
|
|
|
13
13
|
interface CreateImageContentParams {
|
|
14
|
+
/**
|
|
15
|
+
* The image file to upload.
|
|
16
|
+
*/
|
|
14
17
|
image: File
|
|
18
|
+
/**
|
|
19
|
+
* The title for the content entry.
|
|
20
|
+
*/
|
|
15
21
|
contentTitle: string
|
|
22
|
+
/**
|
|
23
|
+
* Visibility options for the content. Use `['DISCOVERABLE']` to appear in
|
|
24
|
+
* recommendations, `['LINKABLE']` to enable shareable URLs, or both. Pass
|
|
25
|
+
* `null` or `[]` to keep content private within your Mini.
|
|
26
|
+
*/
|
|
16
27
|
visibility?: ContentVisibility[] | null
|
|
28
|
+
/**
|
|
29
|
+
* A unique identifier from your own system that lets you look up content
|
|
30
|
+
* later via `ContentWrapper` using an ID you already know.
|
|
31
|
+
* If not provided, the content can only be retrieved by its `publicId`.
|
|
32
|
+
* Each `externalId` must be unique per Mini — creating content with a
|
|
33
|
+
* duplicate returns a `DUPLICATE_EXTERNAL_ID` error.
|
|
34
|
+
*/
|
|
35
|
+
externalId?: string
|
|
36
|
+
/**
|
|
37
|
+
* A text description for the content. Displayed alongside the image in Shop
|
|
38
|
+
* surfaces such as feeds and content detail views. Use this to provide
|
|
39
|
+
* context about the image, such as a caption or review text.
|
|
40
|
+
*/
|
|
41
|
+
description?: string
|
|
42
|
+
/**
|
|
43
|
+
* An array of Shopify product GIDs (e.g. `'gid://shopify/Product/123'`) to
|
|
44
|
+
* associate with the content. Maximum 20 products. Associated products
|
|
45
|
+
* appear alongside the content, enabling shoppable content experiences.
|
|
46
|
+
*/
|
|
47
|
+
productIds?: string[]
|
|
17
48
|
}
|
|
18
49
|
|
|
19
50
|
interface UseCreateImageContentReturns {
|
|
@@ -38,7 +69,14 @@ export const useCreateImageContent = (): UseCreateImageContentReturns => {
|
|
|
38
69
|
async (params: CreateImageContentParams) => {
|
|
39
70
|
setLoading(true)
|
|
40
71
|
|
|
41
|
-
const {
|
|
72
|
+
const {
|
|
73
|
+
image,
|
|
74
|
+
contentTitle,
|
|
75
|
+
visibility,
|
|
76
|
+
externalId,
|
|
77
|
+
description,
|
|
78
|
+
productIds,
|
|
79
|
+
} = params
|
|
42
80
|
|
|
43
81
|
if (!image.type) {
|
|
44
82
|
throw new Error('Unable to determine file type')
|
|
@@ -58,6 +96,9 @@ export const useCreateImageContent = (): UseCreateImageContentReturns => {
|
|
|
58
96
|
title: contentTitle,
|
|
59
97
|
imageUrl: uploadImageUrl,
|
|
60
98
|
visibility,
|
|
99
|
+
externalId,
|
|
100
|
+
description,
|
|
101
|
+
productIds,
|
|
61
102
|
})
|
|
62
103
|
|
|
63
104
|
setLoading(false)
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import {useCallback} from 'react'
|
|
2
|
+
|
|
1
3
|
import {useHandleAction} from '../../internal/useHandleAction'
|
|
2
4
|
import {useShopActions} from '../../internal/useShopActions'
|
|
3
5
|
|
|
@@ -10,8 +12,11 @@ interface UseCloseMiniReturns {
|
|
|
10
12
|
|
|
11
13
|
export const useCloseMini = (): UseCloseMiniReturns => {
|
|
12
14
|
const {closeMini} = useShopActions()
|
|
15
|
+
const handleClose = useHandleAction(closeMini)
|
|
13
16
|
|
|
14
17
|
return {
|
|
15
|
-
|
|
18
|
+
// Wrapper prevents React SyntheticEvent args from leaking through
|
|
19
|
+
// when used directly as an event handler (e.g. onClick={closeMini})
|
|
20
|
+
closeMini: useCallback(() => handleClose(), [handleClose]),
|
|
16
21
|
}
|
|
17
22
|
}
|