-
Notifications
You must be signed in to change notification settings - Fork 326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: @uploadthing/expo
#583
Changes from 82 commits
f28790c
f2ad01b
ee9d04f
bf745e9
b3dd46f
c2e6a2f
7cfea6c
871a3cd
1515d50
dc2fae0
716cfe0
c2f9bbf
c2bf1c9
5f38c4a
c7229fa
ae43f53
c4e3b04
0a5ee48
eae63d2
6a62a7a
48e94ba
cb324dc
b17da5b
ee7fd87
cfcdb2d
94de51e
201c086
3a4303e
eda0a4e
51b7cc7
7f5fb1f
9cb71a6
23db2fb
abbc59c
b6834a7
ab60c73
2f50476
7acc1ec
6d91300
7a3a1bc
b86e07d
1017fb8
208674e
b3e679a
50e5600
fe78f85
702744e
88c7b8c
9a67d91
2e496e8
48f02d6
b22a9a1
2a0e0f8
ce52acb
af507cb
19dc987
d6766df
5fcc604
aa01d0e
a2b04c9
f00006d
2f46e27
d174b37
94d9253
b46058a
4478703
1aaf82b
d9b3892
2373a24
24e91c5
6dc58bd
416cabf
d88ce8c
b44195b
11231b7
4e137e9
a2915d2
3972aff
883635d
32e3cc4
75013f5
b87959a
e90ff26
6323979
707f647
62d0066
273075f
5ac269d
820a5cb
cea2bbe
474f3dd
857cf80
56687fd
a92e232
31c547f
5041b4a
da88346
f64640f
6fec3c7
0b85f17
1e0b533
b810e64
843cb4e
ad38368
c2ddb78
3e0b005
699473c
81cd953
0f9b9e1
d473c2f
3377ee3
108c03b
84f50a6
7073a58
1fb7fd7
0a7cd8b
27e8284
135cb03
fdb56a1
337600a
857958e
2536547
c064929
58647ca
a096a78
36526df
8620002
6b239b6
fa6035e
3b8ceba
96c691e
d244186
e96200a
5d7351f
8aa19e4
ba37b13
831a869
9188a8a
5f18384
f157ee6
0f12de1
a2fa533
a9c2162
4bf669e
7a5e12b
d14fa0d
257b18a
c8b8410
61db742
559e783
327afa1
9be7aac
4a267b9
1a40575
b83d5c5
d4034e8
9004814
d4ecf70
d2bee79
aee2dc1
cdd06ba
1e3b465
0690df8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@uploadthing/react": minor | ||
"@uploadthing/expo": minor | ||
--- | ||
|
||
feat: support expo |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,5 @@ save-workspace-protocol=false | |
prefer-workspace-packages=true | ||
|
||
engine-strict=true | ||
|
||
node-linker=hoisted |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
UPLOADTHING_SECRET="sk_live_xxx" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files | ||
|
||
# dependencies | ||
node_modules/ | ||
|
||
# Expo | ||
.expo/ | ||
dist/ | ||
web-build/ | ||
|
||
# Native | ||
*.orig.* | ||
*.jks | ||
*.p8 | ||
*.p12 | ||
*.key | ||
*.mobileprovision | ||
|
||
# Metro | ||
.metro-health-check* | ||
|
||
# debug | ||
npm-debug.* | ||
yarn-debug.* | ||
yarn-error.* | ||
|
||
# macOS | ||
.DS_Store | ||
*.pem | ||
|
||
# local env files | ||
.env*.local | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
|
||
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb | ||
# The following patterns were generated by expo-cli | ||
|
||
expo-env.d.ts | ||
# @end expo-cli |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import type { ConfigContext, ExpoConfig } from "expo/config"; | ||
|
||
export default ({ config }: ConfigContext): ExpoConfig => ({ | ||
...config, | ||
name: "Minimal Expo x UploadThing", | ||
slug: "minimal-expo", | ||
version: "1.0.0", | ||
orientation: "portrait", | ||
icon: "./assets/icon.png", | ||
scheme: "myapp", | ||
userInterfaceStyle: "automatic", | ||
splash: { | ||
backgroundColor: "#ffffff", | ||
}, | ||
assetBundlePatterns: ["**/*"], | ||
ios: { | ||
bundleIdentifier: "your.bundle.identifier", | ||
supportsTablet: true, | ||
}, | ||
android: { | ||
adaptiveIcon: { | ||
foregroundImage: "./assets/icon.png", | ||
backgroundColor: "#ffffff", | ||
}, | ||
}, | ||
web: { | ||
bundler: "metro", | ||
output: "server", | ||
}, | ||
plugins: ["expo-router"], | ||
experiments: { | ||
typedRoutes: true, | ||
}, | ||
// extra: { | ||
// eas: { | ||
// projectId: "", | ||
// }, | ||
// }, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { Stack } from "expo-router"; | ||
import { StatusBar } from "expo-status-bar"; | ||
|
||
export default function RootLayout() { | ||
return ( | ||
<> | ||
<Stack | ||
screenOptions={{ | ||
headerStyle: { | ||
backgroundColor: "#D73328", | ||
}, | ||
}} | ||
/> | ||
<StatusBar /> | ||
</> | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { z } from "zod"; | ||
|
||
import { createRouteHandler, createUploadthing } from "uploadthing/server"; | ||
import type { FileRouter } from "uploadthing/server"; | ||
|
||
const f = createUploadthing({ | ||
/** | ||
* Log out more information about the error, but don't return it to the client | ||
* @see https://docs.uploadthing.com/errors#error-formatting | ||
*/ | ||
errorFormatter: (err) => { | ||
console.log("Error uploading file", err.message); | ||
console.log(" - Above error caused by:", err.cause); | ||
|
||
return { message: err.message }; | ||
}, | ||
}); | ||
|
||
/** | ||
* This is your Uploadthing file router. For more information: | ||
* @see https://docs.uploadthing.com/api-reference/server#file-routes | ||
*/ | ||
export const uploadRouter = { | ||
videoAndImage: f({ | ||
image: { | ||
maxFileSize: "4MB", | ||
maxFileCount: 4, | ||
}, | ||
}).onUploadComplete((data) => { | ||
console.log("upload completed", data); | ||
}), | ||
document: f({ | ||
blob: { | ||
maxFileSize: "16MB", | ||
maxFileCount: 1, | ||
contentDisposition: "inline", | ||
}, | ||
}) | ||
.input(z.object({ foo: z.string() })) | ||
.middleware(({ input }) => { | ||
return { input }; | ||
}) | ||
.onUploadComplete((data) => { | ||
console.log("upload completed", data); | ||
}), | ||
} satisfies FileRouter; | ||
|
||
export type UploadRouter = typeof uploadRouter; | ||
|
||
export const { GET, POST } = createRouteHandler({ | ||
router: uploadRouter, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { Stack } from "expo-router"; | ||
import { Button, StyleSheet, Text, View } from "react-native"; | ||
|
||
import { generateReactNativeHelpers } from "@uploadthing/expo"; | ||
|
||
import type { UploadRouter } from "./api/uploadthing+api"; | ||
|
||
const { useImageUploader, useDocumentUploader } = | ||
generateReactNativeHelpers<UploadRouter>(); | ||
|
||
export default function ModalScreen() { | ||
const { openImagePicker, isUploading: isUploadingImage } = | ||
useImageUploader("videoAndImage"); | ||
const { openDocumentPicker, isUploading: isUploadingDocument } = | ||
useDocumentUploader("document"); | ||
|
||
return ( | ||
<> | ||
<Stack.Screen options={{ title: "Home Page" }} /> | ||
<View style={styles.container}> | ||
<Text style={styles.title}>Modal</Text> | ||
<Button | ||
title="Open Gallery" | ||
onPress={() => openImagePicker({})} | ||
disabled={isUploadingImage} | ||
/> | ||
<Button | ||
title="Select File" | ||
onPress={() => { | ||
return openDocumentPicker({ | ||
input: { foo: "bar" }, | ||
}); | ||
}} | ||
disabled={isUploadingDocument} | ||
/> | ||
<View style={styles.separator} /> | ||
</View> | ||
</> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
container: { | ||
flex: 1, | ||
alignItems: "center", | ||
justifyContent: "center", | ||
}, | ||
title: { | ||
fontSize: 20, | ||
fontWeight: "bold", | ||
}, | ||
separator: { | ||
marginVertical: 30, | ||
height: 1, | ||
width: "80%", | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/** @type {import("@babel/core").ConfigFunction} */ | ||
module.exports = function (api) { | ||
api.cache(true); | ||
return { | ||
presets: ["babel-preset-expo"], | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"cli": { | ||
"version": ">= 7.3.0" | ||
}, | ||
"build": { | ||
"base": { | ||
"ios": { | ||
"resourceClass": "m-medium" | ||
} | ||
}, | ||
"development": { | ||
"extends": "base", | ||
"developmentClient": true, | ||
"distribution": "internal" | ||
}, | ||
"preview": { | ||
"extends": "base", | ||
"distribution": "internal", | ||
"ios": { | ||
"simulator": true | ||
} | ||
}, | ||
"production": { | ||
"extends": "base" | ||
} | ||
}, | ||
"submit": { | ||
"production": {} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
const { getDefaultConfig } = require("expo/metro-config"); | ||
const { FileStore } = require("@expo/metro-config/file-store"); | ||
const path = require("path"); | ||
|
||
module.exports = withTurborepoManagedCache( | ||
withMonorepoPaths(getDefaultConfig(__dirname)), | ||
); | ||
|
||
/** | ||
* Add the monorepo paths to the Metro config. | ||
* This allows Metro to resolve modules from the monorepo. | ||
* | ||
* @see https://docs.expo.dev/guides/monorepos/#modify-the-metro-config | ||
* @param {import('expo/metro-config').MetroConfig} config | ||
* @returns {import('expo/metro-config').MetroConfig} | ||
*/ | ||
function withMonorepoPaths(config) { | ||
const projectRoot = __dirname; | ||
const workspaceRoot = path.resolve(projectRoot, "../.."); | ||
|
||
// #1 - Watch all files in the monorepo | ||
config.watchFolders = [workspaceRoot]; | ||
|
||
// #2 - Resolve modules within the project's `node_modules` first, then all monorepo modules | ||
config.resolver.nodeModulesPaths = [ | ||
path.resolve(projectRoot, "node_modules"), | ||
path.resolve(workspaceRoot, "node_modules"), | ||
]; | ||
Comment on lines
+24
to
+34
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. expo/metro-config does this automatically. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. even for monorepos? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's from the docs: https://docs.expo.dev/guides/monorepos/#modify-the-metro-config There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Setting |
||
|
||
return config; | ||
} | ||
|
||
/** | ||
* Move the Metro cache to the `node_modules/.cache/metro` folder. | ||
* This repository configured Turborepo to use this cache location as well. | ||
* If you have any environment variables, you can configure Turborepo to invalidate it when needed. | ||
* | ||
* @see https://turbo.build/repo/docs/reference/configuration#env | ||
* @param {import('expo/metro-config').MetroConfig} config | ||
* @returns {import('expo/metro-config').MetroConfig} | ||
*/ | ||
function withTurborepoManagedCache(config) { | ||
config.cacheStores = [ | ||
new FileStore({ root: path.join(__dirname, "node_modules/.cache/metro") }), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use Expo's FileStore to ensure CSS works. https://docs.expo.dev/versions/latest/config/metro/#tailwind-troubleshooting const { FileStore } = require('@expo/metro-config/file-store'); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah needed to hoist the deps... classic pnpm x expo limitation |
||
]; | ||
return config; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "@example/minimal-expo", | ||
"main": "expo-router/entry", | ||
"scripts": { | ||
"dev": "expo start --ios", | ||
"start": "expo start", | ||
"android": "expo start --android", | ||
"ios": "expo start --ios" | ||
}, | ||
"dependencies": { | ||
"@uploadthing/expo": "6.3.0", | ||
"expo": "~50.0.15", | ||
"expo-image-picker": "^14.7.1", | ||
"expo-linking": "~6.2.2", | ||
"expo-router": "~3.4.8", | ||
"expo-splash-screen": "~0.26.4", | ||
"expo-status-bar": "~1.11.1", | ||
"react": "18.2.0", | ||
"react-dom": "18.2.0", | ||
"react-native": "^0.73.6", | ||
"react-native-safe-area-context": "4.8.2", | ||
"react-native-screens": "~3.29.0", | ||
"uploadthing": "6.9.0", | ||
"zod": "^3.22.4" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.24.4", | ||
"@babel/runtime": "^7.24.4", | ||
"@types/react": "18.2.78", | ||
"typescript": "^5.4.5" | ||
}, | ||
"private": true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"extends": "expo/tsconfig.base", | ||
"compilerOptions": { | ||
"strict": true, | ||
"paths": { | ||
"~/*": ["./*"] | ||
} | ||
}, | ||
"include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure you add
config.resolver.unstable_enablePackageExports = true;
to enable package exports. They're a bit buggy in Metro, but @robhogan has been looking into them a bit recently so it may improve.