-
Notifications
You must be signed in to change notification settings - Fork 328
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
Merged
Merged
Changes from all commits
Commits
Show all changes
163 commits
Select commit
Hold shift + click to select a range
f28790c
first attempt
juliusmarminge f2ad01b
bunchee powers
juliusmarminge ee9d04f
add transistive dep explicitely
juliusmarminge bf745e9
fix import
juliusmarminge b3dd46f
working uploads
juliusmarminge c2e6a2f
lint fix
juliusmarminge 7cfea6c
make sure types are the same
juliusmarminge 871a3cd
add require path
juliusmarminge 1515d50
add back express but with ts-ignore
juliusmarminge dc2fae0
more fix
juliusmarminge 716cfe0
restack expo 1/2
juliusmarminge c2f9bbf
sync lock
juliusmarminge c2bf1c9
revert exports of types
juliusmarminge 5f38c4a
first attempt
juliusmarminge c7229fa
bunchee powers
juliusmarminge ae43f53
add transistive dep explicitely
juliusmarminge c4e3b04
fix import
juliusmarminge 0a5ee48
working uploads
juliusmarminge eae63d2
lint fix
juliusmarminge 6a62a7a
fmt
juliusmarminge 48e94ba
restack expo 1/2
juliusmarminge cb324dc
sync lock
juliusmarminge b17da5b
revert exports of types
juliusmarminge ee7fd87
re-init expo app
juliusmarminge cfcdb2d
sync lock, rename exmaple path
juliusmarminge 94de51e
nit
juliusmarminge 201c086
up router
juliusmarminge 3a4303e
fmt
juliusmarminge eda0a4e
fix express
juliusmarminge 51b7cc7
module
juliusmarminge 7f5fb1f
rev
juliusmarminge 9cb71a6
resolve types from src during dev
juliusmarminge 23db2fb
single * per pattern...
juliusmarminge abbc59c
vitest workspaces
juliusmarminge b6834a7
rm aliases for now
juliusmarminge ab60c73
fmt
juliusmarminge 2f50476
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 7acc1ec
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 6d91300
rm splash
juliusmarminge 7a3a1bc
up deps
juliusmarminge b86e07d
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 1017fb8
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 208674e
expo package
juliusmarminge b3e679a
nice
juliusmarminge 50e5600
nicee
juliusmarminge fe78f85
document uploader [NOT WORKING]
juliusmarminge 702744e
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 88c7b8c
sync
juliusmarminge 9a67d91
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 2e496e8
fix uploads
juliusmarminge 48f02d6
nice
juliusmarminge b22a9a1
fix lint
juliusmarminge 2a0e0f8
fix turbo cache deps
juliusmarminge ce52acb
update config to support json
juliusmarminge af507cb
cs
juliusmarminge 19dc987
eas
juliusmarminge d6766df
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 5fcc604
sync
juliusmarminge aa01d0e
format
juliusmarminge a2b04c9
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge f00006d
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 2f46e27
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge d174b37
fixynit
juliusmarminge 94d9253
fmt
juliusmarminge b46058a
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 4478703
sync lock
juliusmarminge 1aaf82b
nice
juliusmarminge d9b3892
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 2373a24
niceer
juliusmarminge 24e91c5
pnpm i
markflorkowski 6dc58bd
Merge branch 'main' into 01-29-re-init_expo_app
markflorkowski 416cabf
bum bunchee for expo package
markflorkowski d88ce8c
lint
markflorkowski b44195b
helps to save
markflorkowski 11231b7
pnpm i
markflorkowski 4e137e9
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge a2915d2
sync versions
juliusmarminge 3972aff
sync lock
juliusmarminge 883635d
node_linker hoisted, expo file store
juliusmarminge 32e3cc4
manypkg fix
juliusmarminge 75013f5
forward isUploading
juliusmarminge b87959a
wording
juliusmarminge e90ff26
improve example
juliusmarminge 6323979
comment, add clean script to minimal-expo
markflorkowski 707f647
short modal styling, empty state
markflorkowski 62d0066
align deps
juliusmarminge 273075f
hide button on scroll
juliusmarminge 5ac269d
check permissions
juliusmarminge 820a5cb
simplify
juliusmarminge cea2bbe
fix logic
juliusmarminge 474f3dd
permission alert
juliusmarminge 857cf80
3rd button
juliusmarminge 56687fd
nit
juliusmarminge a92e232
do some splitting, nicer drawer
juliusmarminge 31c547f
only dark mode
juliusmarminge 5041b4a
apparently RN.FormData isn't spec compliant...
juliusmarminge da88346
fix manypkg
juliusmarminge f64640f
allow multiple
juliusmarminge 6fec3c7
onUploadError
juliusmarminge 0b85f17
psuh test
juliusmarminge 1e0b533
don't hardcode ip for testing
markflorkowski b810e64
correct url format
markflorkowski 843cb4e
docs
juliusmarminge ad38368
tell'em to do auth
juliusmarminge c2ddb78
createContext
juliusmarminge 3e0b005
refactor a bit
juliusmarminge 699473c
swap rnsvg to expo vector icons
juliusmarminge 81cd953
active class on more-button
juliusmarminge 0f9b9e1
rm
juliusmarminge d473c2f
swipe to delete
juliusmarminge 3377ee3
typo
juliusmarminge 108c03b
classname
juliusmarminge 84f50a6
filter deleted
juliusmarminge 7073a58
make dates descending
juliusmarminge 1fb7fd7
revert to deterministic to avoid flickering
juliusmarminge 0a7cd8b
fix generateMediaTypes
juliusmarminge 27e8284
memoize
juliusmarminge 135cb03
memoize
juliusmarminge fdb56a1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 337600a
fix effect expo
juliusmarminge 857958e
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 2536547
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge c064929
bump to sdk51
juliusmarminge 58647ca
rm TextEncoder polyfill
juliusmarminge a096a78
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 36526df
fix: mismatch in response schema for `utapi.listFiles` (#798)
juliusmarminge 8620002
chore: set `sideEffects: false` (#806)
juliusmarminge 6b239b6
add missing changeset
juliusmarminge fa6035e
chore(release): 📦 version packages (#803)
t3dotgg 3b8ceba
docs: fix typo on react api reference (#811)
NicklessOne 96c691e
fix: better mime type support for generic types (#810)
juliusmarminge d244186
feat: added onDrop prop for UploadDropzone component (#809)
growupanand e96200a
fix: Node 20.13 compat (#813)
juliusmarminge 5d7351f
feat: add onDrop to remaining libs (#814)
juliusmarminge 8aa19e4
feat: add with-novel example (#800)
juliusmarminge ba37b13
refactor: UploadButton (#801)
juliusmarminge 831a869
chore: inline returns
juliusmarminge 9188a8a
fix: make `@uploadthing/shared` treeshakeable (#808)
juliusmarminge 5f18384
fix lint
juliusmarminge f157ee6
merge
juliusmarminge 0f12de1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge a2fa533
rename imporrt
juliusmarminge a9c2162
update expo
juliusmarminge 4bf669e
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 7a5e12b
better rnw support
juliusmarminge d14fa0d
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 257b18a
fixx
juliusmarminge c8b8410
maybe now?
juliusmarminge 61db742
man
juliusmarminge 559e783
fmt
juliusmarminge 327afa1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge 9be7aac
kewl
juliusmarminge 4a267b9
swap
juliusmarminge 1a40575
fix manypkg lint
juliusmarminge b83d5c5
fix
juliusmarminge d4034e8
mayb?
juliusmarminge 9004814
mby?
juliusmarminge d4ecf70
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge d2bee79
Update docs/src/pages/getting-started/expo.mdx
juliusmarminge aee2dc1
Merge branch 'main' into 01-29-re-init_expo_app
juliusmarminge cdd06ba
tasks*
juliusmarminge 1e3b465
fmt
juliusmarminge 0690df8
lint-fix
juliusmarminge File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
--- | ||
"@uploadthing/react": minor | ||
"@uploadthing/expo": minor | ||
--- | ||
|
||
feat: support expo |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import { Callout } from "nextra-theme-docs"; | ||
|
||
## `generateReactNativeHelpers` | ||
|
||
The `generateReactNativeHelpers` function is used to generate the | ||
useImageUploader and useDocumentUploader hooks you use to interact with | ||
UploadThing in your app. | ||
|
||
```tsx copy filename="utils/uploadthing.tsx" | ||
import { generateReactNativeHelpers } from "@uploadthing/expo"; | ||
|
||
import type { UploadRouter } from "~/app/api/uploadthing+api"; | ||
|
||
export const { useImageUploader, useDocumentUploader } = | ||
generateReactNativeHelpers<UploadRouter>({ | ||
/** | ||
* Your server url. | ||
* @default process.env.EXPO_PUBLIC_SERVER_URL | ||
* @remarks In dev we will also try to use Expo.debuggerHost | ||
*/ | ||
url: "https://my-server.com", | ||
}); | ||
``` | ||
|
||
## `useImageUploader` | ||
|
||
A hook wrapping the native `expo-image-picker` module that allows access to the | ||
camera and photo library and uploads files to your server. The first time the | ||
user triggers the picker they will be prompted to grant your app permission to | ||
access the camera or photo library. | ||
|
||
```tsx copy filename="app/example-uploader.tsx" | ||
import { Alert, Pressable, Text, View } from "react-native"; | ||
|
||
import { useImageUploader } from "@uploadthing/expo"; | ||
|
||
export function MultiUploader() { | ||
const { openImagePicker, isUploading } = useImageUploader("imageUploader", { | ||
/** | ||
* Any props here are forwarded to the underlying `useUploadThing` hook. | ||
* Refer to the React API reference for more info. | ||
*/ | ||
onClientUploadComplete: () => Alert.alert("Upload Completed"), | ||
onUploadError: (error) => Alert.alert("Upload Error", error.message), | ||
}); | ||
|
||
return ( | ||
<View> | ||
<Pressable | ||
onPress={() => { | ||
openImagePicker({ | ||
input, // Matches the input schema from the FileRouter endpoint | ||
source: "library", // or "camera" | ||
onInsufficientPermissions: () => { | ||
Alert.alert( | ||
"No Permissions", | ||
"You need to grant permission to your Photos to use this", | ||
[ | ||
{ text: "Dismiss" }, | ||
{ text: "Open Settings", onPress: openSettings }, | ||
], | ||
); | ||
}, | ||
}); | ||
}} | ||
> | ||
<Text>Select Image</Text> | ||
</Pressable> | ||
</View> | ||
); | ||
} | ||
``` | ||
|
||
### Configuration | ||
|
||
| Prop | Type | Required | Notes | Description | | ||
| :------- | :-------------------- | :------- | :----------------------------------------------- | --------------------------------------------------------------------------------- | | ||
| endpoint | `keyof UploadRouter` | Yes | | The name of the [route](./server#FileRouter) you want this button to upload to | | ||
| opts | `UseUploadThingProps` | No | [See docs](/api-reference/react#configuration-3) | Props forwarded to the underlying `useUploadThing` hook from `@uploadthing/react` | | ||
|
||
### Returns | ||
|
||
| Prop | Type | Description | | ||
| :-------------- | :------------------------------------------------ | :------------------------------------------------------------------------------- | | ||
| openImagePicker | `(opts: OpenImagePickerOptions) => Promise<void>` | Function to open the native image picker and start uploading the selected files. | | ||
| isUploading | boolean | Flag whether file(s) are currently uploading | | ||
|
||
```ts | ||
type OpenImagePickerOptions = { | ||
input: TInput; // Matches the input schema from the FileRouter endpoint | ||
source: "library" | "camera"; | ||
/** | ||
* Callback to run if the user cancels the picker. | ||
*/ | ||
onCancel?: () => void; | ||
/** | ||
* Callback to run if the user hasn't granted your app permission to the camera or photo library. | ||
*/ | ||
onInsufficientPermissions: () => void; | ||
}; | ||
``` | ||
|
||
## `useDocumentUploader` | ||
|
||
A hook wrapping the native `expo-document-picker` module that allows access to | ||
the native file system and uploads files to your server. | ||
|
||
```tsx copy filename="app/example-uploader.tsx" | ||
import { Alert, Pressable, Text, View } from "react-native"; | ||
|
||
import { useDocumentUploader } from "@uploadthing/expo"; | ||
|
||
export function MultiUploader() { | ||
const { openDocumentPicker, isUploading } = useDocumentUploader("document", { | ||
/** | ||
* Any props here are forwarded to the underlying `useUploadThing` hook. | ||
* Refer to the React API reference for more info. | ||
*/ | ||
onClientUploadComplete: () => Alert.alert("Upload Completed"), | ||
onUploadError: (error) => Alert.alert("Upload Error", error.message), | ||
}); | ||
|
||
return ( | ||
<View> | ||
<Pressable | ||
onPress={() => { | ||
openDocumentPicker({ | ||
input, // Matches the input schema from the FileRouter endpoint | ||
onInsufficientPermissions: () => { | ||
Alert.alert( | ||
"No Permissions", | ||
"You need to grant permission to your Photos to use this", | ||
[ | ||
{ text: "Dismiss" }, | ||
{ text: "Open Settings", onPress: openSettings }, | ||
], | ||
); | ||
}, | ||
}); | ||
}} | ||
> | ||
<Text>Select Document</Text> | ||
</Pressable> | ||
</View> | ||
); | ||
} | ||
``` | ||
|
||
### Configuration | ||
|
||
| Prop | Type | Required | Notes | Description | | ||
| :------- | :-------------------- | :------- | :----------------------------------------------- | --------------------------------------------------------------------------------- | | ||
| endpoint | `keyof UploadRouter` | Yes | | The name of the [route](./server#FileRouter) you want this button to upload to | | ||
| opts | `UseUploadThingProps` | No | [See docs](/api-reference/react#configuration-3) | Props forwarded to the underlying `useUploadThing` hook from `@uploadthing/react` | | ||
|
||
### Returns | ||
|
||
| Prop | Type | Description | | ||
| :-------------- | :------------------------------------------------ | :------------------------------------------------------------------------------- | | ||
| openImagePicker | `(opts: OpenImagePickerOptions) => Promise<void>` | Function to open the native image picker and start uploading the selected files. | | ||
| isUploading | boolean | Flag whether file(s) are currently uploading | | ||
|
||
```ts | ||
type OpenImagePickerOptions = { | ||
input: TInput; // Matches the input schema from the FileRouter endpoint | ||
/** | ||
* Callback to run if the user cancels the picker. | ||
*/ | ||
onCancel?: () => void; | ||
}; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import { Callout, Steps } from "nextra-theme-docs"; | ||
|
||
# Expo Setup | ||
|
||
UploadThing is the easiest way to add file uploads to your native mobile | ||
applications powered by export. | ||
|
||
<Callout type="info"> | ||
Check out a full example | ||
[here](https://github.com/pingdotgg/uploadthing/tree/main/examples/minimal-expo) | ||
</Callout> | ||
|
||
## Package Setup | ||
|
||
<Steps> | ||
|
||
### Install the packages | ||
|
||
```bash copy npm2yarn | ||
npm install uploadthing @uploadthing/expo expo-image-picker expo-document-picker | ||
``` | ||
|
||
### Add env variables | ||
|
||
<Callout> | ||
If you don't already have a uploadthing secret key, [sign | ||
up](https://uploadthing.com/sign-in) and create one from the | ||
[dashboard!](https://uploadthing.com/dashboard) | ||
</Callout> | ||
|
||
```bash copy | ||
UPLOADTHING_SECRET=... # A secret key for your app (starts with sk_live_) | ||
UPLOADTHING_APP_ID=... # Your app id | ||
EXPO_PUBLIC_SERVER_URL=... # Absolute URL to your server | ||
``` | ||
|
||
</Steps> | ||
|
||
## Set Up A FileRouter | ||
|
||
<Callout type="info"> | ||
Steps 1 and 2 below assumes you're using [Expo API | ||
routes](https://docs.expo.dev/router/reference/api-routes/) which are | ||
currently experimental. You can also choose to use a standalone server using | ||
some of our [backend adapters, e.g. Fetch](/backend-adapters/fetch) | ||
</Callout> | ||
|
||
<Steps> | ||
### Creating your first FileRoute | ||
|
||
<Callout> | ||
For more details on how to create a file router, see the | ||
[router](/api-reference/server#file-routes) docs | ||
</Callout> | ||
|
||
All files uploaded to uploadthing are associated with a FileRoute. The following | ||
is a very minimalistic example, with a single FileRoute "imageUploader". Think | ||
of a FileRoute similar to an endpoint, it has: | ||
|
||
- Permitted types ["image", "video", etc] | ||
- Max file size | ||
- (Optional) `middleware` to authenticate and tag requests | ||
- `onUploadComplete` callback for when uploads are completed | ||
|
||
To get full insight into what you can do with the FileRoutes, please refer to | ||
the [File Router API](/api-reference/server#file-routes). | ||
|
||
```ts copy filename="src/app/api/uploadthing+api.ts" | ||
import { createUploadthing, UploadThingError } from "uploadthing/server"; | ||
import type { FileRouter } from "uploadthing/server"; | ||
|
||
const f = createUploadthing(); | ||
|
||
const auth = (req: Request) => ({ id: "fakeId" }); // Fake auth function | ||
|
||
const uploadRouter = { | ||
profileImage: f({ | ||
image: { | ||
maxFileSize: "4MB", | ||
}, | ||
}) | ||
.middleware(async ({ req }) => { | ||
// This code runs on your server before upload | ||
const user = await auth(req); | ||
|
||
// If you throw, the user will not be able to upload | ||
if (!user) throw new UploadThingError("Unauthorized"); | ||
|
||
// Whatever is returned here is accessible in onUploadComplete as `metadata` | ||
return { userId: user.id }; | ||
}) | ||
.onUploadComplete(({ file, metadata }) => { | ||
// This code RUNS ON YOUR SERVER after upload | ||
console.log("Upload complete for userId:", metadata.userId); | ||
|
||
console.log("file url", file.url); | ||
|
||
// !!! Whatever is returned here is sent to the clientside `onClientUploadComplete` callback | ||
return { uploadedBy: metadata.userId }; | ||
}), | ||
} satisfies FileRouter; | ||
export type UploadRouter = typeof uploadRouter; | ||
``` | ||
|
||
### Create an API route using the FileRouter | ||
|
||
```ts copy filename="src/app/api/uploadthing+api.ts" | ||
import { createRouteHandler } from "uploadthing/server"; | ||
|
||
const uploadRouter = { ... } satisfies FileRouter; | ||
export type UploadRouter = typeof uploadRouter; | ||
|
||
export const { GET, POST } = createRouteHandler({ | ||
router: uploadRouter, | ||
|
||
// Apply an (optional) custom config: | ||
// config: { ... }, | ||
}); | ||
``` | ||
|
||
> See configuration options in | ||
> [server API reference](/api-reference/server#createroutehandler) | ||
|
||
### Generate typed hooks | ||
|
||
Unlike the other UploadThing packages, the Expo package does not include any | ||
prebuilt components. Instead, we provide some helper hooks to help interact with | ||
the native file pickers. | ||
|
||
```tsx copy filename="src/utils/uploadthing.ts" | ||
import { generateReactNativeHelpers } from "@uploadthing/expo"; | ||
|
||
import type { UploadRouter } from "~/app/api/uploadthing+api"; | ||
|
||
export const { useImageUploader, useDocumentUploader } = | ||
generateReactNativeHelpers<UploadRouter>({ | ||
/** | ||
* Your server url. | ||
* @default process.env.EXPO_PUBLIC_SERVER_URL | ||
* @remarks In dev we will also try to use Expo.debuggerHost | ||
*/ | ||
url: "https://my-server.com", | ||
}); | ||
``` | ||
|
||
### Use the FileRouter in your app | ||
|
||
```tsx copy filename="src/routes/index.tsx" | ||
import { openSettings } from "expo-linking"; | ||
import { Alert, Pressable, StyleSheet, Text, View } from "react-native"; | ||
|
||
import { useImageUploader } from "~/utils/uploadthing"; | ||
|
||
export default function Home() { | ||
const { openImagePicker, isUploading } = useImageUploader({ | ||
/** | ||
* Any props here are forwarded to the underlying `useUploadThing` hook. | ||
* Refer to the React API reference for more info. | ||
*/ | ||
onClientUploadComplete: () => Alert.alert("Upload Completed"), | ||
onUploadError: (error) => Alert.alert("Upload Error", error.message), | ||
}); | ||
|
||
return ( | ||
<View> | ||
<Pressable | ||
style={styles.button} | ||
onPress={() => { | ||
openImagePicker({ | ||
input, // Matches the input schema from the FileRouter endpoint | ||
source: "library", // or "camera" | ||
onInsufficientPermissions: () => { | ||
Alert.alert( | ||
"No Permissions", | ||
"You need to grant permission to your Photos to use this", | ||
[ | ||
{ text: "Dismiss" }, | ||
{ text: "Open Settings", onPress: openSettings }, | ||
], | ||
); | ||
}, | ||
}) | ||
}} | ||
> | ||
<Text>Select Image</Text> | ||
</Pressable> | ||
</View> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
button: { ... }, | ||
}); | ||
``` | ||
|
||
</Steps> | ||
|
||
## Deployment | ||
|
||
Follow the | ||
[Expo Router API Routes Deployment Guide](https://docs.expo.dev/router/reference/api-routes/#deployment) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
UPLOADTHING_SECRET="sk_live_xxx" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
Is there any way to "hook" into this variable @EvanBacon?