@shopify/shop-minis-react 0.0.33 → 0.0.35

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.
Files changed (86) hide show
  1. package/dist/_virtual/index10.js +2 -2
  2. package/dist/_virtual/index2.js +4 -4
  3. package/dist/_virtual/index3.js +4 -4
  4. package/dist/_virtual/index8.js +2 -2
  5. package/dist/_virtual/index9.js +2 -2
  6. package/dist/components/atoms/alert-dialog.js.map +1 -1
  7. package/dist/components/atoms/icon-button.js +12 -12
  8. package/dist/components/atoms/icon-button.js.map +1 -1
  9. package/dist/components/atoms/image.js +52 -0
  10. package/dist/components/atoms/image.js.map +1 -0
  11. package/dist/components/atoms/text-input.js +22 -0
  12. package/dist/components/atoms/text-input.js.map +1 -0
  13. package/dist/components/commerce/merchant-card.js +2 -1
  14. package/dist/components/commerce/merchant-card.js.map +1 -1
  15. package/dist/components/commerce/product-card.js +11 -11
  16. package/dist/components/commerce/product-card.js.map +1 -1
  17. package/dist/components/content/image-content-wrapper.js +29 -22
  18. package/dist/components/content/image-content-wrapper.js.map +1 -1
  19. package/dist/components/ui/input.js +15 -9
  20. package/dist/components/ui/input.js.map +1 -1
  21. package/dist/hooks/content/useCreateImageContent.js +16 -22
  22. package/dist/hooks/content/useCreateImageContent.js.map +1 -1
  23. package/dist/hooks/storage/useImageUpload.js +36 -37
  24. package/dist/hooks/storage/useImageUpload.js.map +1 -1
  25. package/dist/hooks/util/useKeyboardAvoidingView.js +23 -0
  26. package/dist/hooks/util/useKeyboardAvoidingView.js.map +1 -0
  27. package/dist/index.js +218 -212
  28. package/dist/index.js.map +1 -1
  29. package/dist/mocks.js +4 -1
  30. package/dist/mocks.js.map +1 -1
  31. package/dist/shop-minis-platform/src/types/content.js.map +1 -1
  32. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +1 -1
  33. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js +1 -1
  34. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
  35. package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +1 -1
  36. package/dist/utils/colors.js +1 -1
  37. package/dist/utils/image.js +46 -9
  38. package/dist/utils/image.js.map +1 -1
  39. package/package.json +21 -4
  40. package/src/components/atoms/alert-dialog.test.tsx +67 -0
  41. package/src/components/atoms/alert-dialog.tsx +13 -11
  42. package/src/components/atoms/favorite-button.test.tsx +56 -0
  43. package/src/components/atoms/icon-button.tsx +1 -1
  44. package/src/components/atoms/image.test.tsx +108 -0
  45. package/src/components/atoms/{thumbhash-image.tsx → image.tsx} +14 -14
  46. package/src/components/atoms/product-variant-price.test.tsx +128 -0
  47. package/src/components/atoms/text-input.test.tsx +104 -0
  48. package/src/components/atoms/text-input.tsx +31 -0
  49. package/src/components/commerce/merchant-card.test.tsx +261 -0
  50. package/src/components/commerce/merchant-card.tsx +4 -2
  51. package/src/components/commerce/product-card.test.tsx +364 -0
  52. package/src/components/commerce/product-card.tsx +2 -2
  53. package/src/components/commerce/product-link.test.tsx +483 -0
  54. package/src/components/commerce/quantity-selector.test.tsx +382 -0
  55. package/src/components/commerce/search.test.tsx +487 -0
  56. package/src/components/content/image-content-wrapper.test.tsx +92 -0
  57. package/src/components/content/image-content-wrapper.tsx +9 -2
  58. package/src/components/index.ts +2 -1
  59. package/src/components/navigation/transition-link.test.tsx +155 -0
  60. package/src/components/ui/input.test.tsx +21 -0
  61. package/src/components/ui/input.tsx +10 -1
  62. package/src/hooks/content/useCreateImageContent.test.ts +352 -0
  63. package/src/hooks/content/useCreateImageContent.ts +1 -7
  64. package/src/hooks/index.ts +1 -0
  65. package/src/hooks/navigation/useNavigateWithTransition.test.ts +371 -0
  66. package/src/hooks/navigation/useViewTransitions.test.ts +469 -0
  67. package/src/hooks/product/useProductSearch.test.ts +470 -0
  68. package/src/hooks/storage/useAsyncStorage.test.ts +225 -0
  69. package/src/hooks/storage/useImageUpload.test.ts +322 -0
  70. package/src/hooks/storage/useImageUpload.ts +22 -20
  71. package/src/hooks/util/useKeyboardAvoidingView.ts +37 -0
  72. package/src/internal/useHandleAction.test.ts +265 -0
  73. package/src/internal/useShopActionsDataFetching.test.ts +465 -0
  74. package/src/mocks.ts +3 -1
  75. package/src/providers/ImagePickerProvider.test.tsx +467 -0
  76. package/src/stories/ProductCard.stories.tsx +2 -2
  77. package/src/stories/TextInput.stories.tsx +26 -0
  78. package/src/test-setup.ts +34 -0
  79. package/src/test-utils.tsx +167 -0
  80. package/src/utils/image.ts +73 -0
  81. package/src/utils/index.ts +1 -1
  82. package/dist/components/atoms/thumbhash-image.js +0 -54
  83. package/dist/components/atoms/thumbhash-image.js.map +0 -1
  84. package/dist/utils/imageToDataUri.js +0 -10
  85. package/dist/utils/imageToDataUri.js.map +0 -1
  86. package/src/utils/imageToDataUri.ts +0 -8
@@ -1 +1 @@
1
- {"version":3,"file":"input.js","sources":["../../../src/components/ui/input.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {cn} from '../../lib/utils'\n\nfunction Input({className, type, ...props}: React.ComponentProps<'input'>) {\n return (\n <input\n type={type}\n data-slot=\"input\"\n className={cn(\n 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n 'focus:outline-none focus:ring-0 focus-visible:ring-0 focus-visible:outline-none',\n 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n className\n )}\n {...props}\n />\n )\n}\n\nexport {Input}\n"],"names":["Input","className","type","props","jsx","cn"],"mappings":";;AAIA,SAASA,EAAM,EAAC,WAAAC,GAAW,MAAAC,GAAM,GAAGC,KAAuC;AAEvE,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,MAAAF;AAAA,MACA,aAAU;AAAA,MACV,WAAWG;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACAJ;AAAA,MACF;AAAA,MACC,GAAGE;AAAA,IAAA;AAAA,EACN;AAEJ;"}
1
+ {"version":3,"file":"input.js","sources":["../../../src/components/ui/input.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {cn} from '../../lib/utils'\n\n// using the default ref doesn't seem to set the parent's ref object when mounted\n// Since this is a shadCN component, we need to make sure to add back the innerRef prop,\n// whenever the component is updated.\nfunction Input({\n innerRef,\n className,\n type,\n ...props\n}: React.ComponentProps<'input'> & {innerRef?: React.Ref<HTMLInputElement>}) {\n return (\n <input\n ref={innerRef}\n type={type}\n data-slot=\"input\"\n className={cn(\n 'file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm',\n 'focus:outline-none focus:ring-0 focus-visible:ring-0 focus-visible:outline-none',\n 'aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive',\n className\n )}\n {...props}\n />\n )\n}\n\nexport {Input}\n"],"names":["Input","innerRef","className","type","props","jsx","cn"],"mappings":";;AAOA,SAASA,EAAM;AAAA,EACb,UAAAC;AAAA,EACA,WAAAC;AAAA,EACA,MAAAC;AAAA,EACA,GAAGC;AACL,GAA6E;AAEzE,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAKJ;AAAA,MACL,MAAAE;AAAA,MACA,aAAU;AAAA,MACV,WAAWG;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACAJ;AAAA,MACF;AAAA,MACC,GAAGE;AAAA,IAAA;AAAA,EACN;AAEJ;"}
@@ -1,40 +1,34 @@
1
- import { useState as u, useCallback as g } from "react";
1
+ import { useState as g, useCallback as p } from "react";
2
2
  import { useHandleAction as f } from "../../internal/useHandleAction.js";
3
3
  import { useShopActions as d } from "../../internal/useShopActions.js";
4
- import { useImageUpload as w } from "../storage/useImageUpload.js";
5
- import { fileToDataUri as I } from "../../utils/imageToDataUri.js";
6
- const E = () => {
7
- const { createContent: t } = d(), { uploadImage: a } = w(), [i, o] = u(!1), n = g(
8
- async (m) => {
4
+ import { useImageUpload as I } from "../storage/useImageUpload.js";
5
+ const U = () => {
6
+ const { createContent: t } = d(), { uploadImage: a } = I(), [r, o] = g(!1), i = p(
7
+ async (s) => {
9
8
  o(!0);
10
- const { image: e, contentTitle: s, visibility: l } = m;
9
+ const { image: e, contentTitle: l, visibility: m } = s;
11
10
  if (!e.type)
12
11
  throw new Error("Unable to determine file type");
13
12
  if (!e.type.startsWith("image/"))
14
13
  throw new Error("Invalid file type: must be an image");
15
- const [c] = await a([
16
- {
17
- mimeType: e.type,
18
- uri: await I(e)
19
- }
20
- ]), r = c.imageUrl;
21
- if (!r)
14
+ const [c] = await a(e), n = c.imageUrl;
15
+ if (!n)
22
16
  throw new Error("Image upload failed");
23
- const p = await t({
24
- title: s,
25
- imageUrl: r,
26
- visibility: l
17
+ const u = await t({
18
+ title: l,
19
+ imageUrl: n,
20
+ visibility: m
27
21
  });
28
- return o(!1), p;
22
+ return o(!1), u;
29
23
  },
30
24
  [t, a]
31
25
  );
32
26
  return {
33
- createImageContent: f(n),
34
- loading: i
27
+ createImageContent: f(i),
28
+ loading: r
35
29
  };
36
30
  };
37
31
  export {
38
- E as useCreateImageContent
32
+ U as useCreateImageContent
39
33
  };
40
34
  //# 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 {fileToDataUri} from '../../utils'\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, contentTitle, visibility} = 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([\n {\n mimeType: image.type,\n uri: await fileToDataUri(image),\n },\n ])\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 })\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","uploadImageResult","fileToDataUri","uploadImageUrl","createContentResult","useHandleAction"],"mappings":";;;;;AAgCO,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;AAEf,YAAM,EAAC,OAAAK,GAAO,cAAAC,GAAc,YAAAC,EAAc,IAAAH;AAEtC,UAAA,CAACC,EAAM;AACH,cAAA,IAAI,MAAM,+BAA+B;AAEjD,UAAI,CAACA,EAAM,KAAK,WAAW,QAAQ;AAC3B,cAAA,IAAI,MAAM,qCAAqC;AAGvD,YAAM,CAACG,CAAiB,IAAI,MAAMX,EAAY;AAAA,QAC5C;AAAA,UACE,UAAUQ,EAAM;AAAA,UAChB,KAAK,MAAMI,EAAcJ,CAAK;AAAA,QAAA;AAAA,MAChC,CACD,GACKK,IAAiBF,EAAkB;AAEzC,UAAI,CAACE;AACG,cAAA,IAAI,MAAM,qBAAqB;AAGjC,YAAAC,IAAsB,MAAMhB,EAAc;AAAA,QAC9C,OAAOW;AAAA,QACP,UAAUI;AAAA,QACV,YAAAH;AAAA,MAAA,CACD;AAED,aAAAP,EAAW,EAAK,GAETW;AAAA,IACT;AAAA,IACA,CAAChB,GAAeE,CAAW;AAAA,EAC7B;AAEO,SAAA;AAAA,IACL,oBAAoBe,EAAgBV,CAAkB;AAAA,IACtD,SAAAH;AAAA,EACF;AACF;"}
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, contentTitle, visibility} = 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 })\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","uploadImageResult","uploadImageUrl","createContentResult","useHandleAction"],"mappings":";;;;AA+BO,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;AAEf,YAAM,EAAC,OAAAK,GAAO,cAAAC,GAAc,YAAAC,EAAc,IAAAH;AAEtC,UAAA,CAACC,EAAM;AACH,cAAA,IAAI,MAAM,+BAA+B;AAEjD,UAAI,CAACA,EAAM,KAAK,WAAW,QAAQ;AAC3B,cAAA,IAAI,MAAM,qCAAqC;AAGvD,YAAM,CAACG,CAAiB,IAAI,MAAMX,EAAYQ,CAAK,GAC7CI,IAAiBD,EAAkB;AAEzC,UAAI,CAACC;AACG,cAAA,IAAI,MAAM,qBAAqB;AAGjC,YAAAC,IAAsB,MAAMf,EAAc;AAAA,QAC9C,OAAOW;AAAA,QACP,UAAUG;AAAA,QACV,YAAAF;AAAA,MAAA,CACD;AAED,aAAAP,EAAW,EAAK,GAETU;AAAA,IACT;AAAA,IACA,CAACf,GAAeE,CAAW;AAAA,EAC7B;AAEO,SAAA;AAAA,IACL,oBAAoBc,EAAgBT,CAAkB;AAAA,IACtD,SAAAH;AAAA,EACF;AACF;"}
@@ -1,51 +1,50 @@
1
1
  import { useCallback as m } from "react";
2
- import { useShopActions as u } from "../../internal/useShopActions.js";
2
+ import { useShopActions as c } from "../../internal/useShopActions.js";
3
+ import { fileToDataUri as u } from "../../utils/image.js";
3
4
  const d = async (e) => {
4
- const r = await (await fetch(e.uri)).blob();
5
+ const r = await u(e), o = await (await fetch(r)).blob();
5
6
  return {
6
- ...e,
7
- fileSize: e.fileSize ?? r.size,
8
- fileBlob: r
7
+ mimeType: e.type,
8
+ fileSize: e.size ?? o.size,
9
+ fileBlob: o
9
10
  };
10
- }, f = async (e, o) => {
11
- const r = new FormData();
12
- o.parameters.forEach(({ name: l, value: t }) => {
13
- r.append(l, t);
14
- }), r.append("file", e.fileBlob);
15
- const a = await fetch(o.url, {
11
+ }, f = async (e, r) => {
12
+ const t = new FormData();
13
+ r.parameters.forEach(({ name: s, value: a }) => {
14
+ t.append(s, a);
15
+ }), t.append("file", e.fileBlob);
16
+ const o = await fetch(r.url, {
16
17
  method: "POST",
17
- body: r
18
+ body: t
18
19
  });
19
- return a.ok ? {} : (console.error("Failed to upload image", {
20
- response: await a.text()
20
+ return o.ok ? {} : (console.error("Failed to upload image", {
21
+ response: await o.text()
21
22
  }), { error: "Failed to upload image" });
22
- }, h = () => {
23
- const { createImageUploadLink: e, completeImageUpload: o } = u();
23
+ }, U = () => {
24
+ const { createImageUploadLink: e, completeImageUpload: r } = c();
24
25
  return {
25
26
  uploadImage: m(
26
- async (a) => {
27
- if (a.length > 1)
28
- throw new Error("Multiple image upload is not supported yet");
29
- const l = a[0], t = await d(l), s = await e({
27
+ async (o) => {
28
+ const s = await d(o), a = await e({
30
29
  input: [
31
30
  {
32
- mimeType: t.mimeType,
33
- fileSize: t.fileSize
31
+ mimeType: s.mimeType,
32
+ fileSize: s.fileSize
34
33
  }
35
34
  ]
36
35
  });
37
- if (!s.ok)
38
- throw new Error(s.error.message);
39
- const { error: p } = await f(
40
- t,
41
- s?.data?.targets?.[0]
36
+ if (!a.ok)
37
+ throw new Error(a.error.message);
38
+ const { error: n } = await f(
39
+ s,
40
+ a?.data?.targets?.[0]
42
41
  );
43
- if (p)
44
- throw new Error(p);
45
- let c = 0;
46
- for (; c < 30; ) {
47
- const i = await o({
48
- resourceUrls: s?.data?.targets?.map((n) => n.resourceUrl) || []
42
+ if (n)
43
+ throw new Error(n);
44
+ let p = 0;
45
+ for (; p < 30; ) {
46
+ const i = await r({
47
+ resourceUrls: a?.data?.targets?.map((l) => l.resourceUrl) || []
49
48
  });
50
49
  if (!i.ok)
51
50
  throw new Error(i.error.message);
@@ -54,18 +53,18 @@ const d = async (e) => {
54
53
  {
55
54
  id: i.data.files[0].id,
56
55
  imageUrl: i.data.files[0].image?.url,
57
- resourceUrl: s?.data?.targets?.[0]?.resourceUrl
56
+ resourceUrl: a?.data?.targets?.[0]?.resourceUrl
58
57
  }
59
58
  ];
60
- await new Promise((n) => setTimeout(n, 1e3)), c++;
59
+ await new Promise((l) => setTimeout(l, 1e3)), p++;
61
60
  }
62
61
  throw new Error("Image upload completion timed out");
63
62
  },
64
- [e, o]
63
+ [e, r]
65
64
  )
66
65
  };
67
66
  };
68
67
  export {
69
- h as useImageUpload
68
+ U as useImageUpload
70
69
  };
71
70
  //# sourceMappingURL=useImageUpload.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useImageUpload.js","sources":["../../../src/hooks/storage/useImageUpload.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\n\nimport type {UploadTarget} from '@shopify/shop-minis-platform/actions'\n\nexport interface UploadImageParams {\n /**\n * The MIME type of the image.\n */\n mimeType: string\n /**\n * The size of the image in bytes.\n */\n fileSize?: number\n /**\n * The URI of the image to upload.\n */\n uri: string\n}\n\nexport interface UploadedImage {\n /**\n * The ID of the uploaded image.\n */\n id: string\n /**\n * The URL of the uploaded image.\n */\n imageUrl?: string\n /**\n * The resource URL of the uploaded image.\n */\n resourceUrl?: string\n}\n\ninterface UseImageUploadReturns {\n /**\n * Upload an image attached to the current user.\n */\n uploadImage: (params: UploadImageParams[]) => Promise<UploadedImage[]>\n}\n\n// Fetch file data and detect file sizes if not provided\n// Works with file://, data:, and http(s):// URIs\nconst processFileData = async (image: UploadImageParams) => {\n const response = await fetch(image.uri)\n const blob = await response.blob()\n\n return {\n ...image,\n fileSize: image.fileSize ?? blob.size,\n fileBlob: blob,\n }\n}\n\nconst uploadFileToGCS = async (\n image: UploadImageParams & {fileSize: number; fileBlob: Blob},\n target: UploadTarget\n) => {\n const formData = new FormData()\n target.parameters.forEach(({name, value}: {name: string; value: string}) => {\n formData.append(name, value)\n })\n\n formData.append('file', image.fileBlob)\n\n const uploadResponse = await fetch(target.url, {\n method: 'POST',\n body: formData,\n })\n\n if (!uploadResponse.ok) {\n console.error('Failed to upload image', {\n response: await uploadResponse.text(),\n })\n return {error: 'Failed to upload image'}\n }\n\n return {}\n}\n\nexport const useImageUpload = (): UseImageUploadReturns => {\n const {createImageUploadLink, completeImageUpload} = useShopActions()\n\n const uploadImage = useCallback(\n async (params: UploadImageParams[]) => {\n if (params.length > 1) {\n throw new Error('Multiple image upload is not supported yet')\n }\n\n const imageParams = params[0]\n const processedImageParams = await processFileData(imageParams)\n\n const links = await createImageUploadLink({\n input: [\n {\n mimeType: processedImageParams.mimeType,\n fileSize: processedImageParams.fileSize,\n },\n ],\n })\n\n if (!links.ok) {\n throw new Error(links.error.message)\n }\n\n // Upload single file to GCS\n const {error: uploadError} = await uploadFileToGCS(\n processedImageParams,\n links?.data?.targets?.[0]!\n )\n\n if (uploadError) {\n throw new Error(uploadError)\n }\n\n // 10 second polling for image upload\n let count = 0\n while (count < 30) {\n const result = await completeImageUpload({\n resourceUrls:\n links?.data?.targets?.map(target => target.resourceUrl) || [],\n })\n\n if (!result.ok) {\n throw new Error(result.error.message)\n }\n\n if (result.data?.files?.[0]?.fileStatus === 'READY') {\n return [\n {\n id: result.data.files[0].id,\n imageUrl: result.data.files[0].image?.url,\n resourceUrl: links?.data?.targets?.[0]?.resourceUrl,\n },\n ]\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000))\n count++\n }\n\n throw new Error('Image upload completion timed out')\n },\n [createImageUploadLink, completeImageUpload]\n )\n\n return {\n uploadImage,\n }\n}\n"],"names":["processFileData","image","blob","uploadFileToGCS","target","formData","name","value","uploadResponse","useImageUpload","createImageUploadLink","completeImageUpload","useShopActions","useCallback","params","imageParams","processedImageParams","links","uploadError","count","result","resolve"],"mappings":";;AA6CA,MAAMA,IAAkB,OAAOC,MAA6B;AAEpD,QAAAC,IAAO,OADI,MAAM,MAAMD,EAAM,GAAG,GACV,KAAK;AAE1B,SAAA;AAAA,IACL,GAAGA;AAAA,IACH,UAAUA,EAAM,YAAYC,EAAK;AAAA,IACjC,UAAUA;AAAA,EACZ;AACF,GAEMC,IAAkB,OACtBF,GACAG,MACG;AACG,QAAAC,IAAW,IAAI,SAAS;AAC9B,EAAAD,EAAO,WAAW,QAAQ,CAAC,EAAC,MAAAE,GAAM,OAAAC,QAA0C;AACjE,IAAAF,EAAA,OAAOC,GAAMC,CAAK;AAAA,EAAA,CAC5B,GAEQF,EAAA,OAAO,QAAQJ,EAAM,QAAQ;AAEtC,QAAMO,IAAiB,MAAM,MAAMJ,EAAO,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,MAAMC;AAAA,EAAA,CACP;AAEG,SAACG,EAAe,KAOb,CAAC,KANN,QAAQ,MAAM,0BAA0B;AAAA,IACtC,UAAU,MAAMA,EAAe,KAAK;AAAA,EAAA,CACrC,GACM,EAAC,OAAO,yBAAwB;AAI3C,GAEaC,IAAiB,MAA6B;AACzD,QAAM,EAAC,uBAAAC,GAAuB,qBAAAC,EAAmB,IAAIC,EAAe;AAiE7D,SAAA;AAAA,IACL,aAhEkBC;AAAA,MAClB,OAAOC,MAAgC;AACjC,YAAAA,EAAO,SAAS;AACZ,gBAAA,IAAI,MAAM,4CAA4C;AAGxD,cAAAC,IAAcD,EAAO,CAAC,GACtBE,IAAuB,MAAMhB,EAAgBe,CAAW,GAExDE,IAAQ,MAAMP,EAAsB;AAAA,UACxC,OAAO;AAAA,YACL;AAAA,cACE,UAAUM,EAAqB;AAAA,cAC/B,UAAUA,EAAqB;AAAA,YAAA;AAAA,UACjC;AAAA,QACF,CACD;AAEG,YAAA,CAACC,EAAM;AACT,gBAAM,IAAI,MAAMA,EAAM,MAAM,OAAO;AAIrC,cAAM,EAAC,OAAOC,EAAW,IAAI,MAAMf;AAAA,UACjCa;AAAA,UACAC,GAAO,MAAM,UAAU,CAAC;AAAA,QAC1B;AAEA,YAAIC;AACI,gBAAA,IAAI,MAAMA,CAAW;AAI7B,YAAIC,IAAQ;AACZ,eAAOA,IAAQ,MAAI;AACX,gBAAAC,IAAS,MAAMT,EAAoB;AAAA,YACvC,cACEM,GAAO,MAAM,SAAS,IAAI,CAAUb,MAAAA,EAAO,WAAW,KAAK,CAAA;AAAA,UAAC,CAC/D;AAEG,cAAA,CAACgB,EAAO;AACV,kBAAM,IAAI,MAAMA,EAAO,MAAM,OAAO;AAGtC,cAAIA,EAAO,MAAM,QAAQ,CAAC,GAAG,eAAe;AACnC,mBAAA;AAAA,cACL;AAAA,gBACE,IAAIA,EAAO,KAAK,MAAM,CAAC,EAAE;AAAA,gBACzB,UAAUA,EAAO,KAAK,MAAM,CAAC,EAAE,OAAO;AAAA,gBACtC,aAAaH,GAAO,MAAM,UAAU,CAAC,GAAG;AAAA,cAAA;AAAA,YAE5C;AAGF,gBAAM,IAAI,QAAQ,CAAAI,MAAW,WAAWA,GAAS,GAAI,CAAC,GACtDF;AAAA,QAAA;AAGI,cAAA,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAAA,MACA,CAACT,GAAuBC,CAAmB;AAAA,IAC7C;AAAA,EAIA;AACF;"}
1
+ {"version":3,"file":"useImageUpload.js","sources":["../../../src/hooks/storage/useImageUpload.ts"],"sourcesContent":["import {useCallback} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\nimport {fileToDataUri} from '../../utils'\n\nimport type {UploadTarget} from '@shopify/shop-minis-platform/actions'\n\nexport interface UploadImageParams {\n /**\n * The file to upload.\n */\n image: File\n}\n\ninterface ProcessedImage {\n /**\n * The MIME type of the image.\n */\n mimeType: string\n /**\n * The size of the image in bytes.\n */\n fileSize: number\n /**\n * The file blob of the image.\n */\n fileBlob: Blob\n}\n\nexport interface UploadedImage {\n /**\n * The ID of the uploaded image.\n */\n id: string\n /**\n * The URL of the uploaded image.\n */\n imageUrl?: string\n /**\n * The resource URL of the uploaded image.\n */\n resourceUrl?: string\n}\n\ninterface UseImageUploadReturns {\n /**\n * Upload an image which will be attached to the current user.\n */\n uploadImage: (image: File) => Promise<UploadedImage[]>\n}\n\n// Fetch file data and detect file sizes if not provided\n// Works with file://, data:, and http(s):// URIs\nconst processFileData = async (image: File): Promise<ProcessedImage> => {\n const uri = await fileToDataUri(image)\n\n const response = await fetch(uri)\n const blob = await response.blob()\n\n return {\n mimeType: image.type,\n fileSize: image.size ?? blob.size,\n fileBlob: blob,\n }\n}\n\nconst uploadFileToGCS = async (image: ProcessedImage, target: UploadTarget) => {\n const formData = new FormData()\n target.parameters.forEach(({name, value}: {name: string; value: string}) => {\n formData.append(name, value)\n })\n\n formData.append('file', image.fileBlob)\n\n const uploadResponse = await fetch(target.url, {\n method: 'POST',\n body: formData,\n })\n\n if (!uploadResponse.ok) {\n console.error('Failed to upload image', {\n response: await uploadResponse.text(),\n })\n return {error: 'Failed to upload image'}\n }\n\n return {}\n}\n\nexport const useImageUpload = (): UseImageUploadReturns => {\n const {createImageUploadLink, completeImageUpload} = useShopActions()\n\n const uploadImage = useCallback(\n async (image: File) => {\n const processedImageParams = await processFileData(image)\n\n const links = await createImageUploadLink({\n input: [\n {\n mimeType: processedImageParams.mimeType,\n fileSize: processedImageParams.fileSize,\n },\n ],\n })\n\n if (!links.ok) {\n throw new Error(links.error.message)\n }\n\n // Upload single file to GCS\n const {error: uploadError} = await uploadFileToGCS(\n processedImageParams,\n links?.data?.targets?.[0]!\n )\n\n if (uploadError) {\n throw new Error(uploadError)\n }\n\n // 10 second polling for image upload\n let count = 0\n while (count < 30) {\n const result = await completeImageUpload({\n resourceUrls:\n links?.data?.targets?.map(target => target.resourceUrl) || [],\n })\n\n if (!result.ok) {\n throw new Error(result.error.message)\n }\n\n if (result.data?.files?.[0]?.fileStatus === 'READY') {\n return [\n {\n id: result.data.files[0].id,\n imageUrl: result.data.files[0].image?.url,\n resourceUrl: links?.data?.targets?.[0]?.resourceUrl,\n },\n ]\n }\n\n await new Promise(resolve => setTimeout(resolve, 1000))\n count++\n }\n\n throw new Error('Image upload completion timed out')\n },\n [createImageUploadLink, completeImageUpload]\n )\n\n return {\n uploadImage,\n }\n}\n"],"names":["processFileData","image","uri","fileToDataUri","blob","uploadFileToGCS","target","formData","name","value","uploadResponse","useImageUpload","createImageUploadLink","completeImageUpload","useShopActions","useCallback","processedImageParams","links","uploadError","count","result","resolve"],"mappings":";;;AAqDA,MAAMA,IAAkB,OAAOC,MAAyC;AAChE,QAAAC,IAAM,MAAMC,EAAcF,CAAK,GAG/BG,IAAO,OADI,MAAM,MAAMF,CAAG,GACJ,KAAK;AAE1B,SAAA;AAAA,IACL,UAAUD,EAAM;AAAA,IAChB,UAAUA,EAAM,QAAQG,EAAK;AAAA,IAC7B,UAAUA;AAAA,EACZ;AACF,GAEMC,IAAkB,OAAOJ,GAAuBK,MAAyB;AACvE,QAAAC,IAAW,IAAI,SAAS;AAC9B,EAAAD,EAAO,WAAW,QAAQ,CAAC,EAAC,MAAAE,GAAM,OAAAC,QAA0C;AACjE,IAAAF,EAAA,OAAOC,GAAMC,CAAK;AAAA,EAAA,CAC5B,GAEQF,EAAA,OAAO,QAAQN,EAAM,QAAQ;AAEtC,QAAMS,IAAiB,MAAM,MAAMJ,EAAO,KAAK;AAAA,IAC7C,QAAQ;AAAA,IACR,MAAMC;AAAA,EAAA,CACP;AAEG,SAACG,EAAe,KAOb,CAAC,KANN,QAAQ,MAAM,0BAA0B;AAAA,IACtC,UAAU,MAAMA,EAAe,KAAK;AAAA,EAAA,CACrC,GACM,EAAC,OAAO,yBAAwB;AAI3C,GAEaC,IAAiB,MAA6B;AACzD,QAAM,EAAC,uBAAAC,GAAuB,qBAAAC,EAAmB,IAAIC,EAAe;AA4D7D,SAAA;AAAA,IACL,aA3DkBC;AAAA,MAClB,OAAOd,MAAgB;AACf,cAAAe,IAAuB,MAAMhB,EAAgBC,CAAK,GAElDgB,IAAQ,MAAML,EAAsB;AAAA,UACxC,OAAO;AAAA,YACL;AAAA,cACE,UAAUI,EAAqB;AAAA,cAC/B,UAAUA,EAAqB;AAAA,YAAA;AAAA,UACjC;AAAA,QACF,CACD;AAEG,YAAA,CAACC,EAAM;AACT,gBAAM,IAAI,MAAMA,EAAM,MAAM,OAAO;AAIrC,cAAM,EAAC,OAAOC,EAAW,IAAI,MAAMb;AAAA,UACjCW;AAAA,UACAC,GAAO,MAAM,UAAU,CAAC;AAAA,QAC1B;AAEA,YAAIC;AACI,gBAAA,IAAI,MAAMA,CAAW;AAI7B,YAAIC,IAAQ;AACZ,eAAOA,IAAQ,MAAI;AACX,gBAAAC,IAAS,MAAMP,EAAoB;AAAA,YACvC,cACEI,GAAO,MAAM,SAAS,IAAI,CAAUX,MAAAA,EAAO,WAAW,KAAK,CAAA;AAAA,UAAC,CAC/D;AAEG,cAAA,CAACc,EAAO;AACV,kBAAM,IAAI,MAAMA,EAAO,MAAM,OAAO;AAGtC,cAAIA,EAAO,MAAM,QAAQ,CAAC,GAAG,eAAe;AACnC,mBAAA;AAAA,cACL;AAAA,gBACE,IAAIA,EAAO,KAAK,MAAM,CAAC,EAAE;AAAA,gBACzB,UAAUA,EAAO,KAAK,MAAM,CAAC,EAAE,OAAO;AAAA,gBACtC,aAAaH,GAAO,MAAM,UAAU,CAAC,GAAG;AAAA,cAAA;AAAA,YAE5C;AAGF,gBAAM,IAAI,QAAQ,CAAAI,MAAW,WAAWA,GAAS,GAAI,CAAC,GACtDF;AAAA,QAAA;AAGI,cAAA,IAAI,MAAM,mCAAmC;AAAA,MACrD;AAAA,MACA,CAACP,GAAuBC,CAAmB;AAAA,IAC7C;AAAA,EAIA;AACF;"}
@@ -0,0 +1,23 @@
1
+ import { useCallback as e } from "react";
2
+ import { useShopActions as i } from "../../internal/useShopActions.js";
3
+ const l = () => {
4
+ const { translateContentUp: t, translateContentDown: o } = i(), r = e(
5
+ (n) => {
6
+ if (n.current) {
7
+ const c = n.current.getBoundingClientRect();
8
+ t({ inputYPosition: c.bottom });
9
+ }
10
+ },
11
+ [t]
12
+ ), s = e(() => {
13
+ o();
14
+ }, [o]);
15
+ return {
16
+ onFocus: r,
17
+ onBlur: s
18
+ };
19
+ };
20
+ export {
21
+ l as useKeyboardAvoidingView
22
+ };
23
+ //# sourceMappingURL=useKeyboardAvoidingView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useKeyboardAvoidingView.js","sources":["../../../src/hooks/util/useKeyboardAvoidingView.ts"],"sourcesContent":["import {RefObject, useCallback} from 'react'\n\nimport {useShopActions} from '../../internal/useShopActions'\n\nexport interface UseKeyboardAvoidingViewReturns {\n /**\n * function to call when the input is focused\n */\n onFocus: (ref: RefObject<HTMLElement | null>) => void\n /**\n * function to call when the input is blurred\n */\n onBlur: () => void\n}\n\nexport const useKeyboardAvoidingView = (): UseKeyboardAvoidingViewReturns => {\n const {translateContentUp, translateContentDown} = useShopActions()\n\n const onFocus = useCallback(\n (ref: RefObject<HTMLElement | null>) => {\n if (ref.current) {\n const rect = ref.current.getBoundingClientRect()\n translateContentUp({inputYPosition: rect.bottom})\n }\n },\n [translateContentUp]\n )\n\n const onBlur = useCallback(() => {\n translateContentDown()\n }, [translateContentDown])\n\n return {\n onFocus,\n onBlur,\n }\n}\n"],"names":["useKeyboardAvoidingView","translateContentUp","translateContentDown","useShopActions","onFocus","useCallback","ref","rect","onBlur"],"mappings":";;AAeO,MAAMA,IAA0B,MAAsC;AAC3E,QAAM,EAAC,oBAAAC,GAAoB,sBAAAC,EAAoB,IAAIC,EAAe,GAE5DC,IAAUC;AAAA,IACd,CAACC,MAAuC;AACtC,UAAIA,EAAI,SAAS;AACT,cAAAC,IAAOD,EAAI,QAAQ,sBAAsB;AAC/C,QAAAL,EAAmB,EAAC,gBAAgBM,EAAK,OAAA,CAAO;AAAA,MAAA;AAAA,IAEpD;AAAA,IACA,CAACN,CAAkB;AAAA,EACrB,GAEMO,IAASH,EAAY,MAAM;AACV,IAAAH,EAAA;AAAA,EAAA,GACpB,CAACA,CAAoB,CAAC;AAElB,SAAA;AAAA,IACL,SAAAE;AAAA,IACA,QAAAI;AAAA,EACF;AACF;"}