@shopify/shop-minis-cli 0.0.161 → 0.0.163
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/build/commands/app/link.js +0 -2
- package/build/commands/app/link.js.map +1 -1
- package/build/commands/codemod/releases/index.js +2 -0
- package/build/commands/codemod/releases/index.js.map +1 -1
- package/build/commands/codemod/releases/v0-0-163.d.ts +3 -0
- package/build/commands/codemod/releases/v0-0-163.js +26 -0
- package/build/commands/codemod/releases/v0-0-163.js.map +1 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/.eslintignore +1 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/.eslintrc.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/.graphqlrc.js +1 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/.prettierrc.json +8 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/README.md +30 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/assets.d.ts +14 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/babel.config.js +5 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/index.tsx +5 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/metro.config.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/package.json +33 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/src/App.tsx +152 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/src/index.tsx +9 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/src/manifest.json +5 -0
- package/build/commands/create-mini/standalone/examples/with-camera-access/tsconfig.json +41 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/.eslintignore +1 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/.eslintrc.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/.graphqlrc.js +1 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/.prettierrc.json +8 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/README.md +46 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/assets.d.ts +14 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/babel.config.js +5 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/index.tsx +5 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/metro.config.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/package.json +33 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/src/App.tsx +5 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/src/index.tsx +9 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/src/manifest.json +21 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/src/screens/HomeScreen.tsx +142 -0
- package/build/commands/create-mini/standalone/examples/with-fal-ai/tsconfig.json +41 -0
- package/build/commands/create-mini/standalone/examples/with-getting-started/README.md +18 -0
- package/build/commands/create-mini/standalone/examples/with-getting-started/package.json +3 -3
- package/build/commands/create-mini/standalone/examples/with-getting-started/src/manifest.json +0 -2
- package/build/commands/create-mini/standalone/examples/with-search/.eslintignore +1 -0
- package/build/commands/create-mini/standalone/examples/with-search/.eslintrc.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-search/.graphqlrc.js +1 -0
- package/build/commands/create-mini/standalone/examples/with-search/.prettierrc.json +8 -0
- package/build/commands/create-mini/standalone/examples/with-search/README.md +25 -0
- package/build/commands/create-mini/standalone/examples/with-search/assets.d.ts +14 -0
- package/build/commands/create-mini/standalone/examples/with-search/babel.config.js +5 -0
- package/build/commands/create-mini/standalone/examples/with-search/index.tsx +5 -0
- package/build/commands/create-mini/standalone/examples/with-search/metro.config.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-search/package.json +33 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/App.tsx +23 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/components/HeaderAction.tsx +32 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/components/ShopCard.tsx +92 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/index.tsx +9 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/manifest.json +7 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/screens/MerchantSearchScreen.tsx +126 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/screens/ProductSearchScreen.tsx +115 -0
- package/build/commands/create-mini/standalone/examples/with-search/src/types/screens.ts +6 -0
- package/build/commands/create-mini/standalone/examples/with-search/tsconfig.json +41 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/.eslintignore +1 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/.eslintrc.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/.graphqlrc.js +1 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/.prettierrc.json +8 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/README.md +26 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/assets.d.ts +14 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/babel.config.js +5 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/index.tsx +5 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/metro.config.js +3 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/package.json +33 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/App.tsx +5 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ProductSection.tsx +39 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/components/SectionStyles.ts +9 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ShopCard.tsx +42 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ShopSection.tsx +40 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/index.tsx +9 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/manifest.json +10 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/src/screens/HomeScreen.tsx +67 -0
- package/build/commands/create-mini/standalone/examples/with-user-data/tsconfig.json +41 -0
- package/build/commands/create-mini/standalone/index.js +11 -4
- package/build/commands/create-mini/standalone/index.js.map +1 -1
- package/build/commands/create-mini/utils/examples.d.ts +3 -3
- package/build/commands/create-mini/utils/examples.js +15 -7
- package/build/commands/create-mini/utils/examples.js.map +1 -1
- package/build/commands/create-mini/utils/index.js.map +1 -1
- package/build/commands/dev/utils/start-app/start-app-with-options.js +1 -5
- package/build/commands/dev/utils/start-app/start-app-with-options.js.map +1 -1
- package/build/data/types/autogenerated/shop-minis-admin-api/graphql.d.ts +1 -1
- package/build/data/types/autogenerated/shop-minis-admin-api/graphql.js +1 -1
- package/build/data/types/autogenerated/shop-minis-admin-api/graphql.js.map +1 -1
- package/build/schemas/manifest.schema.json +0 -17
- package/build/utils/package-manager.d.ts +7 -0
- package/build/utils/package-manager.js +15 -0
- package/build/utils/package-manager.js.map +1 -1
- package/build/utils/sync-manifest.js +0 -10
- package/build/utils/sync-manifest.js.map +1 -1
- package/package.json +1 -1
- package/scripts/test-create-mini-standalone.ts +24 -7
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import {useState} from 'react'
|
|
2
|
+
import {ScrollView, View, Text, Button, StyleSheet} from 'react-native'
|
|
3
|
+
import {
|
|
4
|
+
SafeAreaView,
|
|
5
|
+
ProductCard,
|
|
6
|
+
useRecentProducts,
|
|
7
|
+
Product,
|
|
8
|
+
Image,
|
|
9
|
+
useFalAi,
|
|
10
|
+
} from '@shopify/shop-minis-sdk'
|
|
11
|
+
|
|
12
|
+
function generatePrompt(product: Product): string {
|
|
13
|
+
return `
|
|
14
|
+
A realistic Product image photo in a studio setting of a product named ${product.title} from the store: ${product.shop.name}
|
|
15
|
+
`
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function EmptyState() {
|
|
19
|
+
return (
|
|
20
|
+
<Text>
|
|
21
|
+
No recently viewed products found. Close the Mini and visit any product.
|
|
22
|
+
</Text>
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function ProductSection({
|
|
27
|
+
product,
|
|
28
|
+
isLoading,
|
|
29
|
+
onGenerateImage,
|
|
30
|
+
}: {
|
|
31
|
+
product: Product
|
|
32
|
+
isLoading: boolean
|
|
33
|
+
onGenerateImage: () => void
|
|
34
|
+
}) {
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
<Text style={styles.title}>Generate an AI image from this product</Text>
|
|
38
|
+
<Text>
|
|
39
|
+
Based on the product title, shop and description, this mini generates a
|
|
40
|
+
realistic image of the product.
|
|
41
|
+
</Text>
|
|
42
|
+
<View style={styles.productContainer}>
|
|
43
|
+
<ProductCard product={product} />
|
|
44
|
+
</View>
|
|
45
|
+
<Button
|
|
46
|
+
disabled={isLoading}
|
|
47
|
+
onPress={onGenerateImage}
|
|
48
|
+
title={isLoading ? 'Loading...' : 'Generate an AI image'}
|
|
49
|
+
/>
|
|
50
|
+
</>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function HomeScreen() {
|
|
55
|
+
const {products} = useRecentProducts({first: 1})
|
|
56
|
+
const fal = useFalAi()
|
|
57
|
+
const [imageUrl, setImageUrl] = useState('')
|
|
58
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
59
|
+
|
|
60
|
+
const generateImage = async () => {
|
|
61
|
+
const product = products?.[0]
|
|
62
|
+
if (!product) return
|
|
63
|
+
|
|
64
|
+
setIsLoading(true)
|
|
65
|
+
setImageUrl('')
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const prompt = generatePrompt(product)
|
|
69
|
+
const response = await fal.subscribe('fal-ai/flux/schnell', {
|
|
70
|
+
input: {
|
|
71
|
+
prompt,
|
|
72
|
+
},
|
|
73
|
+
mode: 'polling',
|
|
74
|
+
pollInterval: 1000,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
if (response.data.images?.[0]?.url) {
|
|
78
|
+
setImageUrl(response.data.images[0].url)
|
|
79
|
+
}
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('Failed to generateImage:', error)
|
|
82
|
+
} finally {
|
|
83
|
+
setIsLoading(false)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<SafeAreaView>
|
|
89
|
+
<ScrollView>
|
|
90
|
+
<View style={styles.container}>
|
|
91
|
+
<View style={styles.main}>
|
|
92
|
+
{(() => {
|
|
93
|
+
if (products === undefined) return null
|
|
94
|
+
if (products.length === 0) return <EmptyState />
|
|
95
|
+
return (
|
|
96
|
+
<ProductSection
|
|
97
|
+
product={products[0]}
|
|
98
|
+
isLoading={isLoading}
|
|
99
|
+
onGenerateImage={generateImage}
|
|
100
|
+
/>
|
|
101
|
+
)
|
|
102
|
+
})()}
|
|
103
|
+
|
|
104
|
+
{imageUrl ? (
|
|
105
|
+
<Image source={{uri: imageUrl}} style={styles.generatedImage} />
|
|
106
|
+
) : null}
|
|
107
|
+
</View>
|
|
108
|
+
</View>
|
|
109
|
+
</ScrollView>
|
|
110
|
+
</SafeAreaView>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const styles = StyleSheet.create({
|
|
115
|
+
container: {
|
|
116
|
+
flex: 1,
|
|
117
|
+
alignItems: 'stretch',
|
|
118
|
+
justifyContent: 'center',
|
|
119
|
+
marginHorizontal: 'auto',
|
|
120
|
+
},
|
|
121
|
+
main: {
|
|
122
|
+
gap: 8,
|
|
123
|
+
justifyContent: 'center',
|
|
124
|
+
alignItems: 'stretch',
|
|
125
|
+
paddingHorizontal: 24,
|
|
126
|
+
},
|
|
127
|
+
title: {
|
|
128
|
+
marginVertical: 10,
|
|
129
|
+
fontSize: 24,
|
|
130
|
+
fontWeight: 'bold',
|
|
131
|
+
},
|
|
132
|
+
productContainer: {
|
|
133
|
+
marginHorizontal: 50,
|
|
134
|
+
},
|
|
135
|
+
generatedImage: {
|
|
136
|
+
width: '100%',
|
|
137
|
+
height: 300,
|
|
138
|
+
marginTop: 12,
|
|
139
|
+
marginBottom: 20,
|
|
140
|
+
borderRadius: 8,
|
|
141
|
+
},
|
|
142
|
+
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"baseUrl": ".",
|
|
4
|
+
"paths": {
|
|
5
|
+
"*": [
|
|
6
|
+
"*",
|
|
7
|
+
"*.ios",
|
|
8
|
+
"*.android",
|
|
9
|
+
"./node_modules/@shopify/shop-minis-sdk/node_modules/*"
|
|
10
|
+
]
|
|
11
|
+
},
|
|
12
|
+
"allowJs": true,
|
|
13
|
+
"resolveJsonModule": true,
|
|
14
|
+
"allowSyntheticDefaultImports": true,
|
|
15
|
+
"esModuleInterop": true,
|
|
16
|
+
"isolatedModules": false,
|
|
17
|
+
"jsx": "preserve",
|
|
18
|
+
"lib": ["es2019"],
|
|
19
|
+
"noUnusedParameters": true,
|
|
20
|
+
"noUnusedLocals": true,
|
|
21
|
+
"moduleResolution": "node",
|
|
22
|
+
"noEmit": true,
|
|
23
|
+
"incremental": true,
|
|
24
|
+
"strict": true,
|
|
25
|
+
"target": "esnext",
|
|
26
|
+
"skipLibCheck": true,
|
|
27
|
+
"noImplicitReturns": false,
|
|
28
|
+
"noFallthroughCasesInSwitch": true,
|
|
29
|
+
"allowUnreachableCode": false,
|
|
30
|
+
"allowUnusedLabels": false,
|
|
31
|
+
"noImplicitAny": true,
|
|
32
|
+
"strictNullChecks": true,
|
|
33
|
+
"strictFunctionTypes": true,
|
|
34
|
+
"strictBindCallApply": true,
|
|
35
|
+
"strictPropertyInitialization": true,
|
|
36
|
+
"noImplicitThis": true,
|
|
37
|
+
"alwaysStrict": true,
|
|
38
|
+
"useUnknownInCatchVariables": false
|
|
39
|
+
},
|
|
40
|
+
"exclude": ["node_modules"]
|
|
41
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Getting Started Mini
|
|
2
|
+
|
|
3
|
+
A Shop Mini that demonstrates the basics of how to get started with Shop Minis.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
1. Run the development server:
|
|
8
|
+
```bash
|
|
9
|
+
npm start
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Development
|
|
13
|
+
|
|
14
|
+
This mini is built using:
|
|
15
|
+
|
|
16
|
+
- React Native
|
|
17
|
+
- TypeScript
|
|
18
|
+
- Shop Minis SDK
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "getting-started",
|
|
2
|
+
"name": "with-getting-started",
|
|
3
3
|
"version": "0.0.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
"lint": "eslint ."
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@shopify/shop-minis-sdk": "0.0.
|
|
11
|
+
"@shopify/shop-minis-sdk": "0.0.16"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
|
-
"@shopify/shop-minis-cli": "0.0.
|
|
14
|
+
"@shopify/shop-minis-cli": "0.0.161",
|
|
15
15
|
"@types/jest": "^29.2.6",
|
|
16
16
|
"babel-jest": "^26.6.3",
|
|
17
17
|
"eslint": "^8.32.0",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.graphql.d.ts
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('@shopify/shop-minis-sdk/graphqlrc')
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Show Buyers's data
|
|
2
|
+
|
|
3
|
+
A Shop Mini that searches forShop products and merchants
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Fetches products and merchants using the Shop Minis Platform SDK
|
|
8
|
+
- Displays vertical scrollable lists of items
|
|
9
|
+
- Shows product cards with images and details
|
|
10
|
+
- Shows shop cards with logos and names, and navigation to the shop
|
|
11
|
+
|
|
12
|
+
## Setup
|
|
13
|
+
|
|
14
|
+
1. Run the development server:
|
|
15
|
+
```bash
|
|
16
|
+
npm start
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Development
|
|
20
|
+
|
|
21
|
+
This mini is built using:
|
|
22
|
+
|
|
23
|
+
- React Native
|
|
24
|
+
- TypeScript
|
|
25
|
+
- Shop Minis SDK
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module '*.svg' {
|
|
2
|
+
const content: import('react').FC<import('react-native-svg').SvgProps>
|
|
3
|
+
export default content
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare module '*.png' {
|
|
7
|
+
const content: import('react-native').ImageRequireSource
|
|
8
|
+
export default content
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module '*.jpg' {
|
|
12
|
+
const content: import('react-native').ImageRequireSource
|
|
13
|
+
export default content
|
|
14
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
// Changes to babel config are not supported by the mini platform.
|
|
3
|
+
// Custom configuration can introduce unexpected behaviour in your mini and could be a cause of rejection during the submission process.
|
|
4
|
+
presets: ['@shopify/shop-minis-sdk/babel-preset'],
|
|
5
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "with-search",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"start": "shop-minis dev",
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"lint": "eslint ."
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@shopify/shop-minis-sdk": "0.0.16"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@shopify/shop-minis-cli": "0.0.161",
|
|
15
|
+
"@types/jest": "^29.2.6",
|
|
16
|
+
"babel-jest": "^26.6.3",
|
|
17
|
+
"eslint": "^8.32.0",
|
|
18
|
+
"jest": "^29.3.1",
|
|
19
|
+
"prettier": "^2.8.2"
|
|
20
|
+
},
|
|
21
|
+
"jest": {
|
|
22
|
+
"preset": "react-native"
|
|
23
|
+
},
|
|
24
|
+
"overrides": {
|
|
25
|
+
"graphql": "15.8.0",
|
|
26
|
+
"react": "18.2.0",
|
|
27
|
+
"react-native": "0.73.2"
|
|
28
|
+
},
|
|
29
|
+
"resolutions": {
|
|
30
|
+
"@apollo/federation": "0.38.1",
|
|
31
|
+
"graphql": "15.8.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import {createNativeStackNavigator} from '@react-navigation/native-stack'
|
|
2
|
+
|
|
3
|
+
import {ProductSearchScreen} from './screens/ProductSearchScreen'
|
|
4
|
+
import {MerchantSearchScreen} from './screens/MerchantSearchScreen'
|
|
5
|
+
|
|
6
|
+
const Stack = createNativeStackNavigator()
|
|
7
|
+
|
|
8
|
+
export function App() {
|
|
9
|
+
return (
|
|
10
|
+
<Stack.Navigator>
|
|
11
|
+
<Stack.Screen
|
|
12
|
+
name="ProductSearch"
|
|
13
|
+
component={ProductSearchScreen}
|
|
14
|
+
options={{headerShown: false}}
|
|
15
|
+
/>
|
|
16
|
+
<Stack.Screen
|
|
17
|
+
name="MerchantSearch"
|
|
18
|
+
component={MerchantSearchScreen}
|
|
19
|
+
options={{headerShown: false}}
|
|
20
|
+
/>
|
|
21
|
+
</Stack.Navigator>
|
|
22
|
+
)
|
|
23
|
+
}
|
package/build/commands/create-mini/standalone/examples/with-search/src/components/HeaderAction.tsx
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import {useNavigation, useRoute} from '@react-navigation/native'
|
|
2
|
+
import {NativeStackNavigationProp} from '@react-navigation/native-stack'
|
|
3
|
+
import {TouchableOpacity, Text} from 'react-native'
|
|
4
|
+
|
|
5
|
+
import {RootStackParamList} from '../types/screens'
|
|
6
|
+
|
|
7
|
+
export function HeaderAction() {
|
|
8
|
+
const navigation =
|
|
9
|
+
useNavigation<NativeStackNavigationProp<RootStackParamList>>()
|
|
10
|
+
const route = useRoute()
|
|
11
|
+
|
|
12
|
+
const isProductSearch = route.name === 'ProductSearch'
|
|
13
|
+
|
|
14
|
+
const navigationTarget = isProductSearch ? 'MerchantSearch' : 'ProductSearch'
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<TouchableOpacity
|
|
18
|
+
onPress={() => navigation.navigate(navigationTarget)}
|
|
19
|
+
style={{
|
|
20
|
+
backgroundColor: 'black',
|
|
21
|
+
paddingVertical: 6,
|
|
22
|
+
paddingHorizontal: 12,
|
|
23
|
+
borderRadius: 8,
|
|
24
|
+
alignItems: 'center',
|
|
25
|
+
}}
|
|
26
|
+
>
|
|
27
|
+
<Text style={{color: 'white', fontWeight: '600'}}>
|
|
28
|
+
{isProductSearch ? 'Search Merchants' : 'Search Products'}
|
|
29
|
+
</Text>
|
|
30
|
+
</TouchableOpacity>
|
|
31
|
+
)
|
|
32
|
+
}
|
package/build/commands/create-mini/standalone/examples/with-search/src/components/ShopCard.tsx
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {View, Text} from 'react-native'
|
|
2
|
+
import {Icon, IconButton, ImageBox, Shop} from '@shopify/shop-minis-sdk'
|
|
3
|
+
|
|
4
|
+
interface ShopCardProps {
|
|
5
|
+
shop: Shop
|
|
6
|
+
onFavoriteToggled?: () => void
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const IMAGE_ASPECT_RATIO = 1
|
|
10
|
+
|
|
11
|
+
export const ShopCard: React.FC<ShopCardProps> = ({
|
|
12
|
+
shop,
|
|
13
|
+
onFavoriteToggled,
|
|
14
|
+
}) => {
|
|
15
|
+
const {logoImage, name, isFollowing, reviewAnalytics} = shop
|
|
16
|
+
|
|
17
|
+
const averageRating = reviewAnalytics?.averageRating
|
|
18
|
+
|
|
19
|
+
const heartButton = isFollowing ? (
|
|
20
|
+
<IconButton
|
|
21
|
+
accessibilityLabel="Favorite"
|
|
22
|
+
onPress={() => onFavoriteToggled?.()}
|
|
23
|
+
name="heart"
|
|
24
|
+
variant="filled"
|
|
25
|
+
backgroundColor="primary-button-background"
|
|
26
|
+
size="xs"
|
|
27
|
+
/>
|
|
28
|
+
) : (
|
|
29
|
+
<IconButton
|
|
30
|
+
accessibilityLabel="Favorite"
|
|
31
|
+
onPress={() => onFavoriteToggled?.()}
|
|
32
|
+
name="favorites-heart"
|
|
33
|
+
variant="overlay"
|
|
34
|
+
size="xs"
|
|
35
|
+
/>
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<View>
|
|
40
|
+
<View
|
|
41
|
+
style={{
|
|
42
|
+
borderRadius: 16,
|
|
43
|
+
borderColor: '#E4E5E7',
|
|
44
|
+
borderWidth: 0.5,
|
|
45
|
+
borderRightWidth: 1,
|
|
46
|
+
}}
|
|
47
|
+
>
|
|
48
|
+
<View
|
|
49
|
+
style={{
|
|
50
|
+
minWidth: 134,
|
|
51
|
+
width: '100%',
|
|
52
|
+
aspectRatio: IMAGE_ASPECT_RATIO,
|
|
53
|
+
}}
|
|
54
|
+
>
|
|
55
|
+
<ImageBox
|
|
56
|
+
borderRadius="m"
|
|
57
|
+
style={{
|
|
58
|
+
width: '100%',
|
|
59
|
+
height: '100%',
|
|
60
|
+
minHeight: 134,
|
|
61
|
+
}}
|
|
62
|
+
resizeMode="cover"
|
|
63
|
+
source={{
|
|
64
|
+
uri: logoImage?.url,
|
|
65
|
+
}}
|
|
66
|
+
/>
|
|
67
|
+
</View>
|
|
68
|
+
|
|
69
|
+
{onFavoriteToggled ? (
|
|
70
|
+
<View style={{position: 'absolute', right: 12, bottom: 12}}>
|
|
71
|
+
{heartButton}
|
|
72
|
+
</View>
|
|
73
|
+
) : null}
|
|
74
|
+
</View>
|
|
75
|
+
|
|
76
|
+
<View
|
|
77
|
+
style={{flexDirection: 'column', alignItems: 'center', marginTop: 4}}
|
|
78
|
+
>
|
|
79
|
+
<Text style={{fontSize: 16, fontWeight: '400'}}>{name}</Text>
|
|
80
|
+
|
|
81
|
+
{averageRating ? (
|
|
82
|
+
<View
|
|
83
|
+
style={{flexDirection: 'row', alignItems: 'center', marginLeft: 8}}
|
|
84
|
+
>
|
|
85
|
+
<Text>{averageRating?.toFixed(1)}</Text>
|
|
86
|
+
<Icon name="star-filled" size="xs" />
|
|
87
|
+
</View>
|
|
88
|
+
) : null}
|
|
89
|
+
</View>
|
|
90
|
+
</View>
|
|
91
|
+
)
|
|
92
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import {useMemo, useState, useCallback} from 'react'
|
|
2
|
+
import {TouchableOpacity, TextInput, Text, View} from 'react-native'
|
|
3
|
+
import {
|
|
4
|
+
useShopSearch,
|
|
5
|
+
useUpdateFollowedShops,
|
|
6
|
+
useTheme,
|
|
7
|
+
useShopNavigation,
|
|
8
|
+
SafeAreaView,
|
|
9
|
+
Grid,
|
|
10
|
+
Shop,
|
|
11
|
+
} from '@shopify/shop-minis-sdk'
|
|
12
|
+
import debounce from 'lodash/debounce'
|
|
13
|
+
|
|
14
|
+
import {ShopCard} from '../components/ShopCard'
|
|
15
|
+
import {HeaderAction} from '../components/HeaderAction'
|
|
16
|
+
|
|
17
|
+
export function MerchantSearchScreen() {
|
|
18
|
+
const theme = useTheme()
|
|
19
|
+
|
|
20
|
+
const [inputValue, setInputValue] = useState('')
|
|
21
|
+
const [query, setQuery] = useState('')
|
|
22
|
+
|
|
23
|
+
const {followShop, unfollowShop} = useUpdateFollowedShops()
|
|
24
|
+
const {navigateToShop} = useShopNavigation()
|
|
25
|
+
const debouncedSetQuery = useMemo(
|
|
26
|
+
() =>
|
|
27
|
+
debounce((text: string) => {
|
|
28
|
+
setQuery(text)
|
|
29
|
+
}, 300),
|
|
30
|
+
[]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
const {shops, refetch} = useShopSearch({
|
|
34
|
+
query,
|
|
35
|
+
first: 10,
|
|
36
|
+
skip: !query,
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const onShopCardPressedHandler = useCallback(
|
|
40
|
+
(shop: Shop) => {
|
|
41
|
+
navigateToShop({
|
|
42
|
+
shopId: shop.id,
|
|
43
|
+
shopName: shop.name,
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
[navigateToShop]
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const onFavoriteToggledHandler = useCallback(
|
|
50
|
+
async (shop: Shop) => {
|
|
51
|
+
if (shop.isFollowing) {
|
|
52
|
+
unfollowShop({shopId: shop.id})
|
|
53
|
+
} else {
|
|
54
|
+
followShop({shopId: shop.id})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await refetch()
|
|
58
|
+
},
|
|
59
|
+
[followShop, unfollowShop, refetch]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<SafeAreaView style={{flex: 1}}>
|
|
64
|
+
<View
|
|
65
|
+
style={{
|
|
66
|
+
flex: 1,
|
|
67
|
+
paddingHorizontal: 16,
|
|
68
|
+
marginBottom: 8,
|
|
69
|
+
backgroundColor: 'white',
|
|
70
|
+
}}
|
|
71
|
+
>
|
|
72
|
+
<View
|
|
73
|
+
style={{
|
|
74
|
+
flexDirection: 'row',
|
|
75
|
+
justifyContent: 'space-between',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
marginBottom: 8,
|
|
78
|
+
marginTop: 4,
|
|
79
|
+
}}
|
|
80
|
+
>
|
|
81
|
+
<Text style={{fontSize: 20, fontWeight: 'bold'}}>
|
|
82
|
+
Search Merchants
|
|
83
|
+
</Text>
|
|
84
|
+
|
|
85
|
+
<HeaderAction />
|
|
86
|
+
</View>
|
|
87
|
+
|
|
88
|
+
<View>
|
|
89
|
+
<TextInput
|
|
90
|
+
placeholder="Search for..."
|
|
91
|
+
value={inputValue}
|
|
92
|
+
onChangeText={text => {
|
|
93
|
+
setInputValue(text)
|
|
94
|
+
debouncedSetQuery(text)
|
|
95
|
+
}}
|
|
96
|
+
style={{
|
|
97
|
+
borderWidth: 1,
|
|
98
|
+
borderColor: 'gray',
|
|
99
|
+
borderRadius: 5,
|
|
100
|
+
padding: 10,
|
|
101
|
+
}}
|
|
102
|
+
/>
|
|
103
|
+
</View>
|
|
104
|
+
|
|
105
|
+
{shops && shops.length > 0 ? (
|
|
106
|
+
<View style={{flex: 1, marginTop: theme.spacing.m}}>
|
|
107
|
+
<Grid
|
|
108
|
+
data={shops}
|
|
109
|
+
renderItem={({item}) => (
|
|
110
|
+
<TouchableOpacity
|
|
111
|
+
onPress={() => onShopCardPressedHandler(item)}
|
|
112
|
+
>
|
|
113
|
+
<ShopCard
|
|
114
|
+
shop={item}
|
|
115
|
+
onFavoriteToggled={() => onFavoriteToggledHandler(item)}
|
|
116
|
+
/>
|
|
117
|
+
</TouchableOpacity>
|
|
118
|
+
)}
|
|
119
|
+
initialNumToRender={10}
|
|
120
|
+
/>
|
|
121
|
+
</View>
|
|
122
|
+
) : null}
|
|
123
|
+
</View>
|
|
124
|
+
</SafeAreaView>
|
|
125
|
+
)
|
|
126
|
+
}
|