@shopify/shop-minis-cli 0.0.161 → 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 (80) hide show
  1. package/build/commands/create-mini/standalone/examples/with-camera-access/.eslintignore +1 -0
  2. package/build/commands/create-mini/standalone/examples/with-camera-access/.eslintrc.js +3 -0
  3. package/build/commands/create-mini/standalone/examples/with-camera-access/.graphqlrc.js +1 -0
  4. package/build/commands/create-mini/standalone/examples/with-camera-access/.prettierrc.json +8 -0
  5. package/build/commands/create-mini/standalone/examples/with-camera-access/README.md +30 -0
  6. package/build/commands/create-mini/standalone/examples/with-camera-access/assets.d.ts +14 -0
  7. package/build/commands/create-mini/standalone/examples/with-camera-access/babel.config.js +5 -0
  8. package/build/commands/create-mini/standalone/examples/with-camera-access/index.tsx +5 -0
  9. package/build/commands/create-mini/standalone/examples/with-camera-access/metro.config.js +3 -0
  10. package/build/commands/create-mini/standalone/examples/with-camera-access/package.json +33 -0
  11. package/build/commands/create-mini/standalone/examples/with-camera-access/src/App.tsx +152 -0
  12. package/build/commands/create-mini/standalone/examples/with-camera-access/src/index.tsx +9 -0
  13. package/build/commands/create-mini/standalone/examples/with-camera-access/src/manifest.json +5 -0
  14. package/build/commands/create-mini/standalone/examples/with-camera-access/tsconfig.json +41 -0
  15. package/build/commands/create-mini/standalone/examples/with-fal-ai/.eslintignore +1 -0
  16. package/build/commands/create-mini/standalone/examples/with-fal-ai/.eslintrc.js +3 -0
  17. package/build/commands/create-mini/standalone/examples/with-fal-ai/.graphqlrc.js +1 -0
  18. package/build/commands/create-mini/standalone/examples/with-fal-ai/.prettierrc.json +8 -0
  19. package/build/commands/create-mini/standalone/examples/with-fal-ai/README.md +46 -0
  20. package/build/commands/create-mini/standalone/examples/with-fal-ai/assets.d.ts +14 -0
  21. package/build/commands/create-mini/standalone/examples/with-fal-ai/babel.config.js +5 -0
  22. package/build/commands/create-mini/standalone/examples/with-fal-ai/index.tsx +5 -0
  23. package/build/commands/create-mini/standalone/examples/with-fal-ai/metro.config.js +3 -0
  24. package/build/commands/create-mini/standalone/examples/with-fal-ai/package.json +33 -0
  25. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/App.tsx +5 -0
  26. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/index.tsx +9 -0
  27. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/manifest.json +21 -0
  28. package/build/commands/create-mini/standalone/examples/with-fal-ai/src/screens/HomeScreen.tsx +142 -0
  29. package/build/commands/create-mini/standalone/examples/with-fal-ai/tsconfig.json +41 -0
  30. package/build/commands/create-mini/standalone/examples/with-getting-started/README.md +18 -0
  31. package/build/commands/create-mini/standalone/examples/with-getting-started/package.json +3 -3
  32. package/build/commands/create-mini/standalone/examples/with-search/.eslintignore +1 -0
  33. package/build/commands/create-mini/standalone/examples/with-search/.eslintrc.js +3 -0
  34. package/build/commands/create-mini/standalone/examples/with-search/.graphqlrc.js +1 -0
  35. package/build/commands/create-mini/standalone/examples/with-search/.prettierrc.json +8 -0
  36. package/build/commands/create-mini/standalone/examples/with-search/README.md +25 -0
  37. package/build/commands/create-mini/standalone/examples/with-search/assets.d.ts +14 -0
  38. package/build/commands/create-mini/standalone/examples/with-search/babel.config.js +5 -0
  39. package/build/commands/create-mini/standalone/examples/with-search/index.tsx +5 -0
  40. package/build/commands/create-mini/standalone/examples/with-search/metro.config.js +3 -0
  41. package/build/commands/create-mini/standalone/examples/with-search/package.json +33 -0
  42. package/build/commands/create-mini/standalone/examples/with-search/src/App.tsx +23 -0
  43. package/build/commands/create-mini/standalone/examples/with-search/src/components/HeaderAction.tsx +32 -0
  44. package/build/commands/create-mini/standalone/examples/with-search/src/components/ShopCard.tsx +92 -0
  45. package/build/commands/create-mini/standalone/examples/with-search/src/index.tsx +9 -0
  46. package/build/commands/create-mini/standalone/examples/with-search/src/manifest.json +7 -0
  47. package/build/commands/create-mini/standalone/examples/with-search/src/screens/MerchantSearchScreen.tsx +126 -0
  48. package/build/commands/create-mini/standalone/examples/with-search/src/screens/ProductSearchScreen.tsx +115 -0
  49. package/build/commands/create-mini/standalone/examples/with-search/src/types/screens.ts +6 -0
  50. package/build/commands/create-mini/standalone/examples/with-search/tsconfig.json +41 -0
  51. package/build/commands/create-mini/standalone/examples/with-user-data/.eslintignore +1 -0
  52. package/build/commands/create-mini/standalone/examples/with-user-data/.eslintrc.js +3 -0
  53. package/build/commands/create-mini/standalone/examples/with-user-data/.graphqlrc.js +1 -0
  54. package/build/commands/create-mini/standalone/examples/with-user-data/.prettierrc.json +8 -0
  55. package/build/commands/create-mini/standalone/examples/with-user-data/README.md +26 -0
  56. package/build/commands/create-mini/standalone/examples/with-user-data/assets.d.ts +14 -0
  57. package/build/commands/create-mini/standalone/examples/with-user-data/babel.config.js +5 -0
  58. package/build/commands/create-mini/standalone/examples/with-user-data/index.tsx +5 -0
  59. package/build/commands/create-mini/standalone/examples/with-user-data/metro.config.js +3 -0
  60. package/build/commands/create-mini/standalone/examples/with-user-data/package.json +33 -0
  61. package/build/commands/create-mini/standalone/examples/with-user-data/src/App.tsx +5 -0
  62. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ProductSection.tsx +39 -0
  63. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/SectionStyles.ts +9 -0
  64. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ShopCard.tsx +42 -0
  65. package/build/commands/create-mini/standalone/examples/with-user-data/src/components/ShopSection.tsx +40 -0
  66. package/build/commands/create-mini/standalone/examples/with-user-data/src/index.tsx +9 -0
  67. package/build/commands/create-mini/standalone/examples/with-user-data/src/manifest.json +10 -0
  68. package/build/commands/create-mini/standalone/examples/with-user-data/src/screens/HomeScreen.tsx +67 -0
  69. package/build/commands/create-mini/standalone/examples/with-user-data/tsconfig.json +41 -0
  70. package/build/commands/create-mini/standalone/index.js +11 -4
  71. package/build/commands/create-mini/standalone/index.js.map +1 -1
  72. package/build/commands/create-mini/utils/examples.d.ts +3 -3
  73. package/build/commands/create-mini/utils/examples.js +16 -7
  74. package/build/commands/create-mini/utils/examples.js.map +1 -1
  75. package/build/commands/create-mini/utils/index.js.map +1 -1
  76. package/build/utils/package-manager.d.ts +7 -0
  77. package/build/utils/package-manager.js +15 -0
  78. package/build/utils/package-manager.js.map +1 -1
  79. package/package.json +1 -1
  80. package/scripts/test-create-mini-standalone.ts +24 -7
@@ -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,30 @@
1
+ # Camera Access Mini
2
+
3
+ A Shop Mini that demonstrates device camera access, photo gallery selection, and photo sharing capabilities.
4
+
5
+ ## Features
6
+
7
+ - Access device camera to take photos
8
+ - Access photo gallery to select existing photos
9
+ - Display selected/captured photos
10
+ - Share photos using the device's native share sheet
11
+ - Built with React Native components and styling
12
+
13
+ ## Setup
14
+
15
+ 1. Run the development server:
16
+ ```bash
17
+ npm start
18
+ ```
19
+
20
+ ## Development
21
+
22
+ This mini is built using:
23
+
24
+ - React Native
25
+ - TypeScript
26
+ - Shop Minis SDK
27
+ - react-native-image-crop-picker - for camera and gallery access
28
+ - react-native-share - for native share sheet functionality
29
+
30
+ Note: Camera access requires running the app on a physical device.
@@ -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-camera-access",
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,152 @@
1
+ import {useState} from 'react'
2
+ import {View, Text, TouchableOpacity, StyleSheet} from 'react-native'
3
+ import {
4
+ SafeAreaView,
5
+ ImageBox,
6
+ useErrorToast,
7
+ useImagePicker,
8
+ useShare,
9
+ } from '@shopify/shop-minis-sdk'
10
+
11
+ function Button({
12
+ text,
13
+ onPress,
14
+ variant = 'primary',
15
+ disabled,
16
+ style,
17
+ }: {
18
+ text: string
19
+ onPress: () => void
20
+ variant?: 'primary' | 'secondary'
21
+ disabled?: boolean
22
+ style?: object
23
+ }) {
24
+ return (
25
+ <TouchableOpacity
26
+ disabled={disabled}
27
+ style={[
28
+ styles.button,
29
+ variant === 'primary' ? styles.primaryButton : styles.secondaryButton,
30
+ disabled && {opacity: 0.5},
31
+ style,
32
+ ]}
33
+ onPress={onPress}
34
+ >
35
+ <Text
36
+ style={[
37
+ styles.buttonText,
38
+ {color: variant === 'primary' ? '#FFFFFF' : '#000000'},
39
+ ]}
40
+ >
41
+ {text}
42
+ </Text>
43
+ </TouchableOpacity>
44
+ )
45
+ }
46
+
47
+ export function App() {
48
+ const [imageUrl, setImageUrl] = useState<string>()
49
+ const {showErrorToast} = useErrorToast()
50
+ const {openCamera, openPicker} = useImagePicker()
51
+ const {share} = useShare()
52
+
53
+ return (
54
+ <SafeAreaView style={{flex: 1}}>
55
+ <View
56
+ style={{flex: 1, paddingHorizontal: 16, backgroundColor: '#FFFFFF'}}
57
+ >
58
+ <Text style={styles.title}>Share a Photo</Text>
59
+ <Text style={styles.subtitle}>
60
+ Let&apos;s explore the native capabilities your Shop Mini can access.
61
+ </Text>
62
+ <View>
63
+ <Button
64
+ text="Camera access"
65
+ onPress={async () => {
66
+ await openCamera({})
67
+ .then(image => setImageUrl(image.path))
68
+ .catch(error => {
69
+ showErrorToast({
70
+ message: error.message,
71
+ })
72
+ })
73
+ }}
74
+ />
75
+ <Text style={{marginTop: 8}}>
76
+ Run this Mini on a real device to access the camera.
77
+ </Text>
78
+ </View>
79
+ <View style={{marginVertical: 16}}>
80
+ <Button
81
+ text="Gallery access"
82
+ variant="secondary"
83
+ onPress={async () => {
84
+ await openPicker({})
85
+ .then(image => setImageUrl(image.path))
86
+ .catch(console.log)
87
+ }}
88
+ />
89
+ </View>
90
+ {imageUrl ? (
91
+ <View style={{marginTop: 32, alignItems: 'center'}}>
92
+ <View style={styles.imageContainer}>
93
+ <ImageBox source={{uri: imageUrl}} style={styles.image} />
94
+ </View>
95
+ <Button
96
+ text="Share photo"
97
+ variant="secondary"
98
+ disabled={!imageUrl}
99
+ style={{width: 200, marginTop: 16}}
100
+ onPress={async () => {
101
+ if (imageUrl) {
102
+ await share({
103
+ url: imageUrl,
104
+ title: 'My Shared Image',
105
+ type: 'image/*',
106
+ }).catch(console.log)
107
+ }
108
+ }}
109
+ />
110
+ </View>
111
+ ) : null}
112
+ </View>
113
+ </SafeAreaView>
114
+ )
115
+ }
116
+
117
+ const styles = StyleSheet.create({
118
+ button: {
119
+ padding: 12,
120
+ borderRadius: 8,
121
+ alignItems: 'center',
122
+ },
123
+ primaryButton: {
124
+ backgroundColor: '#000000',
125
+ },
126
+ secondaryButton: {
127
+ backgroundColor: '#E6E6E6',
128
+ },
129
+ buttonText: {
130
+ fontSize: 16,
131
+ },
132
+ title: {
133
+ fontSize: 24,
134
+ fontWeight: 'bold',
135
+ marginBottom: 16,
136
+ marginTop: 8,
137
+ },
138
+ subtitle: {
139
+ fontSize: 16,
140
+ marginBottom: 16,
141
+ },
142
+ imageContainer: {
143
+ width: 200,
144
+ height: 200,
145
+ borderRadius: 16,
146
+ overflow: 'hidden',
147
+ },
148
+ image: {
149
+ width: 200,
150
+ height: 200,
151
+ },
152
+ })
@@ -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,5 @@
1
+ {
2
+ "name": "with-camera-access",
3
+ "features": ["FULL_BLEED_VIEWER", "STANDALONE"],
4
+ "permissions": ["GALLERY", "CAMERA"]
5
+ }
@@ -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,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,46 @@
1
+ # Product AI Image Generator Mini
2
+
3
+ A Shop Mini that generates AI-powered product images based on your recently viewed products. Using FAL AI's image generation capabilities, this mini creates realistic studio-quality product images from product titles, descriptions, and shop information.
4
+
5
+ ## Features
6
+
7
+ - Automatically detects your most recently viewed product
8
+ - Generates AI-powered product images using FAL AI
9
+ - Displays original product information alongside generated images
10
+ - Simple one-click image generation
11
+
12
+ ## Setup
13
+
14
+ 1. Clone this repository
15
+ 2. Install dependencies:
16
+
17
+ ```bash
18
+ npm install
19
+ ```
20
+
21
+ 3. Setup your mini and login with your partners account:
22
+
23
+ ```bash
24
+ npx shop-minis setup
25
+ ```
26
+
27
+ 4. Set up your FAL AI API key:
28
+
29
+ - Sign up for a FAL AI account at [FAL AI](https://fal.ai)
30
+ - Get your API key from the dashboard
31
+ - Run `npx shop-minis secrets set fal_ai_key <your-api-key-here>`
32
+ - Run `npx shop-minis proxies apply`
33
+
34
+ 5. Run the development server:
35
+ ```bash
36
+ npm start
37
+ ```
38
+
39
+ ## Development
40
+
41
+ This mini is built using:
42
+
43
+ - React Native
44
+ - TypeScript
45
+ - Shop Minis SDK
46
+ - FAL AI API
@@ -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-fal-ai",
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,5 @@
1
+ import {HomeScreen} from './screens/HomeScreen'
2
+
3
+ export function App() {
4
+ return <HomeScreen />
5
+ }
@@ -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,21 @@
1
+ {
2
+ "name": "with-fal-ai",
3
+ "features": ["FULL_BLEED_VIEWER", "STANDALONE"],
4
+ "trusted_domains": ["v3.fal.media"],
5
+ "proxies": [
6
+ {
7
+ "target_url_pattern": "https://queue.fal.run/fal-ai/flux/schnell*",
8
+ "allowed_methods": ["POST"],
9
+ "appended_headers": ["Authorization: Key {{ secrets.fal_ai_key }}"],
10
+ "user_rate_limit_requests": 10,
11
+ "user_rate_limit_interval": 3600
12
+ },
13
+ {
14
+ "target_url_pattern": "https://queue.fal.run/fal-ai/flux/requests*",
15
+ "allowed_methods": ["GET"],
16
+ "appended_headers": ["Authorization: Key {{ secrets.fal_ai_key }}"],
17
+ "user_rate_limit_requests": 50,
18
+ "user_rate_limit_interval": 300
19
+ }
20
+ ]
21
+ }
@@ -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
+ })