@shopify/shop-minis-react 0.18.0 → 0.20.0

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/eslint/config.cjs CHANGED
@@ -8,6 +8,7 @@
8
8
 
9
9
  // Import the plugin directly so consumers don't need to install it separately
10
10
  const compatPlugin = require('eslint-plugin-compat')
11
+ const importXPlugin = require('eslint-plugin-import-x')
11
12
  const reactPlugin = require('eslint-plugin-react')
12
13
 
13
14
  const shopMinisPlugin = require('./index.cjs')
@@ -29,6 +30,7 @@ module.exports = {
29
30
  'shop-minis': shopMinisPlugin,
30
31
  react: reactPlugin,
31
32
  compat: compatPlugin,
33
+ 'import-x': importXPlugin,
32
34
  },
33
35
  rules: {
34
36
  // Console usage
@@ -37,6 +39,24 @@ module.exports = {
37
39
  // React security
38
40
  'react/no-danger': 'error',
39
41
 
42
+ // Block phantom-dep imports: every imported package must be declared in
43
+ // package.json. Hoisting can make undeclared imports resolve locally but
44
+ // they break under strict installs (pnpm) in the submission pipeline.
45
+ 'import-x/no-extraneous-dependencies': [
46
+ 'error',
47
+ {
48
+ devDependencies: [
49
+ '**/*.test.{js,jsx,ts,tsx}',
50
+ '**/*.spec.{js,jsx,ts,tsx}',
51
+ '**/test-setup.{js,ts}',
52
+ '**/test-utils.{js,jsx,ts,tsx}',
53
+ '**/setup-tests.{js,ts}',
54
+ '**/*.config.{js,cjs,mjs,ts}',
55
+ ],
56
+ includeTypes: true,
57
+ },
58
+ ],
59
+
40
60
  // Shop Minis custom rules
41
61
  'shop-minis/no-dynamic-asset-paths': 'warn',
42
62
  'shop-minis/no-env-without-fallback': 'error',
@@ -15,6 +15,9 @@
15
15
  "useCheckPermissions": [
16
16
  "CHECK_PERMISSION"
17
17
  ],
18
+ "useCheckScopesConsent": [
19
+ "CHECK_SCOPES_CONSENT"
20
+ ],
18
21
  "useCloseMini": [
19
22
  "CLOSE_MINI"
20
23
  ],
@@ -104,6 +107,9 @@
104
107
  "useRequestPermissions": [
105
108
  "REQUEST_PERMISSION"
106
109
  ],
110
+ "useRequestScopesConsent": [
111
+ "REQUEST_SCOPES_CONSENT"
112
+ ],
107
113
  "useSavedProducts": [
108
114
  "GET_SAVED_PRODUCTS"
109
115
  ],
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shopify/shop-minis-react",
3
3
  "license": "SEE LICENSE IN LICENSE.txt",
4
- "version": "0.18.0",
4
+ "version": "0.20.0",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "engines": {
@@ -63,7 +63,7 @@
63
63
  "typescript": ">=5.0.0"
64
64
  },
65
65
  "dependencies": {
66
- "@shopify/shop-minis-platform": "0.16.0",
66
+ "@shopify/shop-minis-platform": "0.17.0",
67
67
  "@tailwindcss/vite": "4.1.8",
68
68
  "@tanstack/react-query": "5.86.0",
69
69
  "@types/lodash": "4.17.20",
@@ -77,6 +77,7 @@
77
77
  "embla-carousel-react": "8.6.0",
78
78
  "eslint": "8.57.0",
79
79
  "eslint-plugin-compat": "6.0.2",
80
+ "eslint-plugin-import-x": "4.16.2",
80
81
  "eslint-plugin-no-secrets": "2.2.1",
81
82
  "eslint-plugin-react": "7.37.5",
82
83
  "js-base64": "3.7.7",
@@ -51,6 +51,8 @@ export * from './util/useImagePicker'
51
51
  export * from './util/useKeyboardAvoidingView'
52
52
  export * from './util/useRequestPermissions'
53
53
  export * from './util/useCheckPermissions'
54
+ export * from './util/useCheckScopesConsent'
55
+ export * from './util/useRequestScopesConsent'
54
56
 
55
57
  // - Intent Hooks
56
58
  export * from './intents/useIntent'
@@ -0,0 +1,61 @@
1
+ import {CheckScopesConsentResponse} from '@shopify/shop-minis-platform/actions'
2
+
3
+ import {useShopActionQuery} from '../../internal/reactQuery'
4
+ import {useShopActions} from '../../internal/useShopActions'
5
+
6
+ export interface UseCheckScopesConsentReturns {
7
+ /**
8
+ * Required scopes declared by the mini, fetched fresh from the API.
9
+ */
10
+ requiredScopes: string[] | undefined
11
+ /**
12
+ * Scopes already granted by the user.
13
+ */
14
+ grantedScopes: string[] | undefined
15
+ /**
16
+ * Consent status derived from comparing required vs granted scopes.
17
+ *
18
+ * - `'granted'` — all required scopes are granted, or none declared
19
+ * - `'partially_granted'` — at least one required scope is granted but not all
20
+ * - `'not_granted'` — no required scopes are granted
21
+ */
22
+ status: CheckScopesConsentResponse['status'] | undefined
23
+ loading: boolean
24
+ error: Error | null
25
+ /**
26
+ * Re-fetch scope consent status. Call this after `requestScopesConsent()` resolves
27
+ * to get the updated status.
28
+ */
29
+ refetch: () => Promise<void>
30
+ }
31
+
32
+ export const useCheckScopesConsent = (): UseCheckScopesConsentReturns => {
33
+ const {checkScopesConsent} = useShopActions()
34
+ const {data, loading, error, refetch} = useShopActionQuery(
35
+ ['scopesConsent'],
36
+ checkScopesConsent,
37
+ {}
38
+ )
39
+ return {
40
+ requiredScopes: data?.requiredScopes,
41
+ grantedScopes: data?.grantedScopes,
42
+ status: data?.status,
43
+ loading,
44
+ error,
45
+ refetch,
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Returns the current scope consent status for this mini, fetched fresh from the API on mount.
51
+ *
52
+ * Use `status` to branch UI on whether the user has granted all required scopes:
53
+ * - `'granted'` — all required scopes are granted (or none declared)
54
+ * - `'partially_granted'` — at least one required scope is granted but not all
55
+ * - `'not_granted'` — no required scopes are granted
56
+ *
57
+ * Call `refetch()` after `requestScopesConsent()` resolves to get the updated status.
58
+ * @publicDocs
59
+ */
60
+ export type UseCheckScopesConsentGeneratedType =
61
+ () => UseCheckScopesConsentReturns
@@ -26,6 +26,7 @@ export const useRequestPermissions = (): UseRequestPermissionsReturns => {
26
26
  /**
27
27
  * The `useRequestPermissions` hook provides a function to request native device permissions from the user. It handles both app-level and system-level permission requests, showing appropriate dialogs and managing permission state. Supported permissions include camera, microphone, and device motion access.
28
28
  *
29
+ * `errorMessage` is normally a string describing the failure. The value `'request_blocked'` is a reserved sentinel — it means the request couldn't be served because another consent or permission sheet is already showing, or the Mini's scope state hasn't finished loading yet. Don't surface this string to users.
29
30
  *
30
31
  * > Note: Before using this hook, add the required permissions to your Mini's manifest file: `"permissions": ["CAMERA"]`.
31
32
  * @publicDocs
@@ -0,0 +1,38 @@
1
+ import {RequestScopesConsentResponse} from '@shopify/shop-minis-platform/actions'
2
+
3
+ import {useHandleAction} from '../../internal/useHandleAction'
4
+ import {useShopActions} from '../../internal/useShopActions'
5
+
6
+ export interface UseRequestScopesConsentReturns {
7
+ /**
8
+ * Programmatically show the consent sheet for the Mini's required scopes.
9
+ *
10
+ * The sheet will show even if the user previously rejected it in the same
11
+ * session. Use this to build retry UX (e.g., a "Connect account" button).
12
+ *
13
+ * Resolves with `{granted: boolean}`. Rejects with `'request_blocked'`
14
+ * if the request can't be served right now — either another consent or
15
+ * permission sheet is already showing, or the Mini's scope state hasn't
16
+ * finished loading yet.
17
+ *
18
+ * Call `refetch()` on `useCheckScopesConsent` after resolution to get the
19
+ * updated scope status.
20
+ */
21
+ requestScopesConsent: () => Promise<RequestScopesConsentResponse>
22
+ }
23
+
24
+ export const useRequestScopesConsent = (): UseRequestScopesConsentReturns => {
25
+ const {requestScopesConsent} = useShopActions()
26
+ return {
27
+ requestScopesConsent: useHandleAction(requestScopesConsent),
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Returns a function to programmatically show the consent sheet. Use this to
33
+ * build re-consent UX after the user has previously rejected, or when partial
34
+ * scope grants are detected via `useCheckScopesConsent`.
35
+ * @publicDocs
36
+ */
37
+ export type UseRequestScopesConsentGeneratedType =
38
+ () => UseRequestScopesConsentReturns
package/src/mocks.ts CHANGED
@@ -578,6 +578,14 @@ export function makeMockActions(): ShopActions {
578
578
  checkPermission: {
579
579
  status: 'granted',
580
580
  },
581
+ checkScopesConsent: {
582
+ data: {
583
+ requiredScopes: ['profile'],
584
+ grantedScopes: ['profile'],
585
+ status: 'granted',
586
+ },
587
+ },
588
+ requestScopesConsent: {granted: true},
581
589
  reportError: undefined,
582
590
  reportFetch: undefined,
583
591
  invokeIntent: {code: 'closed' as const},