@shopify/shop-minis-cli 0.0.160 → 0.0.162

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 (84) hide show
  1. package/build/commands/create-mini/legacy/index.js +2 -6
  2. package/build/commands/create-mini/legacy/index.js.map +1 -1
  3. package/build/commands/create-mini/standalone/examples/with-camera-access/.eslintignore +1 -0
  4. package/build/commands/create-mini/standalone/examples/with-camera-access/.eslintrc.js +3 -0
  5. package/build/commands/create-mini/standalone/examples/with-camera-access/.graphqlrc.js +1 -0
  6. package/build/commands/create-mini/standalone/examples/with-camera-access/.prettierrc.json +8 -0
  7. package/build/commands/create-mini/standalone/examples/with-camera-access/README.md +30 -0
  8. package/build/commands/create-mini/standalone/examples/with-camera-access/assets.d.ts +14 -0
  9. package/build/commands/create-mini/standalone/examples/with-camera-access/babel.config.js +5 -0
  10. package/build/commands/create-mini/standalone/examples/with-camera-access/index.tsx +5 -0
  11. package/build/commands/create-mini/standalone/examples/with-camera-access/metro.config.js +3 -0
  12. package/build/commands/create-mini/standalone/examples/with-camera-access/package.json +33 -0
  13. package/build/commands/create-mini/standalone/examples/with-camera-access/src/App.tsx +152 -0
  14. package/build/commands/create-mini/standalone/examples/with-camera-access/src/index.tsx +9 -0
  15. package/build/commands/create-mini/standalone/examples/with-camera-access/src/manifest.json +5 -0
  16. package/build/commands/create-mini/standalone/examples/with-camera-access/tsconfig.json +41 -0
  17. package/build/commands/create-mini/standalone/examples/with-fal-ai/.eslintignore +1 -0
  18. package/build/commands/create-mini/standalone/examples/with-fal-ai/.eslintrc.js +3 -0
  19. package/build/commands/create-mini/standalone/examples/with-fal-ai/.graphqlrc.js +1 -0
  20. package/build/commands/create-mini/standalone/examples/with-fal-ai/.prettierrc.json +8 -0
  21. package/build/commands/create-mini/standalone/examples/with-fal-ai/README.md +46 -0
  22. package/build/commands/create-mini/standalone/examples/with-fal-ai/assets.d.ts +14 -0
  23. package/build/commands/create-mini/standalone/examples/with-fal-ai/babel.config.js +5 -0
  24. package/build/commands/create-mini/standalone/examples/with-fal-ai/index.tsx +5 -0
  25. package/build/commands/create-mini/standalone/examples/with-fal-ai/metro.config.js +3 -0
  26. package/build/commands/create-mini/standalone/examples/with-fal-ai/package.json +33 -0
  27. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/App.tsx +5 -0
  28. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/index.tsx +9 -0
  29. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/manifest.json +21 -0
  30. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/screens/HomeScreen.tsx +142 -0
  31. package/build/commands/create-mini/standalone/examples/with-fal-ai/tsconfig.json +41 -0
  32. package/build/commands/create-mini/standalone/examples/with-getting-started/README.md +18 -0
  33. package/build/commands/create-mini/standalone/examples/with-getting-started/package.json +3 -3
  34. package/build/commands/create-mini/standalone/examples/with-search/.eslintignore +1 -0
  35. package/build/commands/create-mini/standalone/examples/with-search/.eslintrc.js +3 -0
  36. package/build/commands/create-mini/standalone/examples/with-search/.graphqlrc.js +1 -0
  37. package/build/commands/create-mini/standalone/examples/with-search/.prettierrc.json +8 -0
  38. package/build/commands/create-mini/standalone/examples/with-search/README.md +25 -0
  39. package/build/commands/create-mini/standalone/examples/with-search/assets.d.ts +14 -0
  40. package/build/commands/create-mini/standalone/examples/with-search/babel.config.js +5 -0
  41. package/build/commands/create-mini/standalone/examples/with-search/index.tsx +5 -0
  42. package/build/commands/create-mini/standalone/examples/with-search/metro.config.js +3 -0
  43. package/build/commands/create-mini/standalone/examples/with-search/package.json +33 -0
  44. package/build/commands/create-mini/standalone/examples/with-search/src/App.tsx +23 -0
  45. package/build/commands/create-mini/standalone/examples/with-search/src/components/HeaderAction.tsx +32 -0
  46. package/build/commands/create-mini/standalone/examples/with-search/src/components/ShopCard.tsx +92 -0
  47. package/build/commands/create-mini/standalone/examples/with-search/src/index.tsx +9 -0
  48. package/build/commands/create-mini/standalone/examples/with-search/src/manifest.json +7 -0
  49. package/build/commands/create-mini/standalone/examples/with-search/src/screens/MerchantSearchScreen.tsx +126 -0
  50. package/build/commands/create-mini/standalone/examples/with-search/src/screens/ProductSearchScreen.tsx +115 -0
  51. package/build/commands/create-mini/standalone/examples/with-search/src/types/screens.ts +6 -0
  52. package/build/commands/create-mini/standalone/examples/with-search/tsconfig.json +41 -0
  53. package/build/commands/create-mini/standalone/examples/with-user-data/.eslintignore +1 -0
  54. package/build/commands/create-mini/standalone/examples/with-user-data/.eslintrc.js +3 -0
  55. package/build/commands/create-mini/standalone/examples/with-user-data/.graphqlrc.js +1 -0
  56. package/build/commands/create-mini/standalone/examples/with-user-data/.prettierrc.json +8 -0
  57. package/build/commands/create-mini/standalone/examples/with-user-data/README.md +26 -0
  58. package/build/commands/create-mini/standalone/examples/with-user-data/assets.d.ts +14 -0
  59. package/build/commands/create-mini/standalone/examples/with-user-data/babel.config.js +5 -0
  60. package/build/commands/create-mini/standalone/examples/with-user-data/index.tsx +5 -0
  61. package/build/commands/create-mini/standalone/examples/with-user-data/metro.config.js +3 -0
  62. package/build/commands/create-mini/standalone/examples/with-user-data/package.json +33 -0
  63. package/build/commands/create-mini/standalone/examples/with-user-data/src/App.tsx +5 -0
  64. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ProductSection.tsx +39 -0
  65. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/SectionStyles.ts +9 -0
  66. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ShopCard.tsx +42 -0
  67. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ShopSection.tsx +40 -0
  68. package/build/commands/create-mini/standalone/examples/with-user-data/src/index.tsx +9 -0
  69. package/build/commands/create-mini/standalone/examples/with-user-data/src/manifest.json +10 -0
  70. package/build/commands/create-mini/standalone/examples/with-user-data/src/screens/HomeScreen.tsx +67 -0
  71. package/build/commands/create-mini/standalone/examples/with-user-data/tsconfig.json +41 -0
  72. package/build/commands/create-mini/standalone/index.js +13 -6
  73. package/build/commands/create-mini/standalone/index.js.map +1 -1
  74. package/build/commands/create-mini/utils/examples.d.ts +3 -3
  75. package/build/commands/create-mini/utils/examples.js +16 -7
  76. package/build/commands/create-mini/utils/examples.js.map +1 -1
  77. package/build/commands/create-mini/utils/index.d.ts +1 -0
  78. package/build/commands/create-mini/utils/index.js +9 -0
  79. package/build/commands/create-mini/utils/index.js.map +1 -1
  80. package/build/utils/package-manager.d.ts +7 -0
  81. package/build/utils/package-manager.js +15 -0
  82. package/build/utils/package-manager.js.map +1 -1
  83. package/package.json +1 -1
  84. 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"
11
+ "@shopify/shop-minis-sdk": "0.0.16"
12
12
  },
13
13
  "devDependencies": {
14
- "@shopify/shop-minis-cli": "0.0.158",
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,3 @@
1
+ const {minisEslintConfig} = require('@shopify/shop-minis-sdk/eslint-config')
2
+
3
+ module.exports = minisEslintConfig
@@ -0,0 +1 @@
1
+ module.exports = require('@shopify/shop-minis-sdk/graphqlrc')
@@ -0,0 +1,8 @@
1
+ {
2
+ "semi": false,
3
+ "singleQuote": true,
4
+ "trailingComma": "es5",
5
+ "arrowParens": "avoid",
6
+ "bracketSpacing": false,
7
+ "printWidth": 80
8
+ }
@@ -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,5 @@
1
+ import {renderDevelopmentMini} from '@shopify/shop-minis-sdk/src/renderDevelopmentMini'
2
+
3
+ import MiniApp from './src'
4
+
5
+ renderDevelopmentMini(MiniApp)
@@ -0,0 +1,3 @@
1
+ const {getMinisMetroConfig} = require('@shopify/shop-minis-sdk/metro-config')
2
+
3
+ module.exports = getMinisMetroConfig(__dirname)
@@ -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
+ }
@@ -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
+ }
@@ -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,9 @@
1
+ import {MiniAppConfig} from '@shopify/shop-minis-sdk'
2
+
3
+ import {App} from './App'
4
+
5
+ const config: MiniAppConfig = {
6
+ ViewerRoot: App,
7
+ }
8
+
9
+ export default config
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "with-search-merchants",
3
+ "features": [
4
+ "FULL_BLEED_VIEWER",
5
+ "STANDALONE"
6
+ ]
7
+ }
@@ -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
+ }