Skip to content

Commit

Permalink
Merge branch 'main' into fix-expo-v7
Browse files Browse the repository at this point in the history
  • Loading branch information
juliusmarminge committed Nov 5, 2024
2 parents 057c55a + 0550da6 commit 0f13859
Show file tree
Hide file tree
Showing 115 changed files with 1,929 additions and 475 deletions.
10 changes: 0 additions & 10 deletions .changeset/breezy-windows-admire.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/chatty-bananas-destroy.md

This file was deleted.

6 changes: 6 additions & 0 deletions .changeset/little-boxes-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"uploadthing": patch
"@uploadthing/nuxt": patch
---

fix: dynamically add either stylesheet or tailwind plugin based on whether `@nuxtjs/tailwindcss´ is installed
5 changes: 0 additions & 5 deletions .changeset/odd-cheetahs-kneel.md

This file was deleted.

5 changes: 0 additions & 5 deletions .changeset/sharp-needles-collect.md

This file was deleted.

14 changes: 14 additions & 0 deletions docs/src/app/(docs)/api-reference/client/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,20 @@ const response = await uploadFiles("routeEndpoint", {
The first parameter is the route endpoint to upload to, and the second parameter
is an options object:

<Note>

The endpoint arg may be a string literal or a callback function:

```ts
await uploadFiles((routeRegistry) => routeRegistry.routeEndpoint, { ... })
```

Using a callback function allows `Go to Defintion` on `routeEndpoint` to take
you straight to your backend file route definition, which is not possible when
using a string literal parameter.

</Note>

<Properties>
<Property name="files" type="File[]" required since="5.0">
An array of files to upload.
Expand Down
48 changes: 46 additions & 2 deletions docs/src/app/(docs)/api-reference/react/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,23 @@ export const OurUploadButton = () => (
### Props

<Properties>
<Property name="endpoint" type="keyof FileRouter" required>
<Property name="endpoint" type="EndpointArg<FileRouter>" required>
The name/slug of the [route](/file-routes) you want to upload to

<Note>

The endpoint arg may be a string literal or a callback function:

```ts
await uploadFiles((routeRegistry) => routeRegistry.routeEndpoint, { ... })
```

Using a callback function allows `Go to Defintion` on `routeEndpoint` to take
you straight to your backend file route definition, which is not possible when
using a string literal parameter.

</Note>

</Property>
<Property name="input" type="TInput" since="5.0">
Input JSON data matching your validator set on the [FileRoute](/file-routes#input)
Expand Down Expand Up @@ -353,8 +368,23 @@ export const OurUploadDropzone = () => (
### Props

<Properties>
<Property name="endpoint" type="keyof FileRouter" required>
<Property name="endpoint" type="EndpointArg<FileRouter>" required>
The name/slug of the [route](/file-routes) you want to upload to upload to

<Note>

The endpoint arg may be a string literal or a callback function:

```ts
await uploadFiles((routeRegistry) => routeRegistry.routeEndpoint, { ... })
```

Using a callback function allows `Go to Defintion` on `routeEndpoint` to take
you straight to your backend file route definition, which is not possible when
using a string literal parameter.

</Note>

</Property>
<Property name="input" type="TInput" since="5.0">
Input JSON data matching your validator set on the [FileRoute](/file-routes#input)
Expand Down Expand Up @@ -470,6 +500,20 @@ counts are allowed by the endpoint.
The first parameter is the route endpoint to upload to, and the second parameter
is an options object:
<Note>
The endpoint arg may be a string literal or a callback function:
```ts
useUploadThing((routeRegistry) => routeRegistry.routeEndpoint, { ... })
```
Using a callback function allows `Go to Defintion` on `routeEndpoint` to take
you straight to your backend file route definition, which is not possible when
using a string literal parameter.
</Note>
<Properties>
<Property name="files" type="File[]" required since="5.0">
An array of files to upload.
Expand Down
2 changes: 2 additions & 0 deletions docs/src/app/(docs)/concepts/theming/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ type ButtonCallbackArguments = {
isUploading: boolean;
uploadProgress: number;
fileTypes: string[];
files: File[];
};

type DropzoneCallbackArguments = {
Expand All @@ -143,6 +144,7 @@ type DropzoneCallbackArguments = {
uploadProgress: number;
fileTypes: string[];
isDragActive: boolean;
files: File[];
};
```

Expand Down
31 changes: 31 additions & 0 deletions docs/src/app/(docs)/getting-started/appdir/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,37 @@ export default function RootLayout({
}
```

<Warning>

If using the experimental `ppr` or `dynamicIO` features in Next.js 15, wrap the
`<NextSSRPlugin />` in suspense and call `await connection()` before rendering
the component.

```tsx {{title: "app/layout.tsx" }}
import { connection } from "next/server"; // [!code ++]
import { Suspense } from "react"; // [!code ++]

async function UTSSR() {
await connection(); // [!code ++]

return <NextSSRPlugin routerConfig={extractRouterConfig(ourFileRouter)} />
}

export default function RootLayout(props) {
return (
<html>
{...}
<Suspense> // [!code ++]
<UTSSR />
</Suspense> // [!code ++]
{...}
</html>
)
}
```

</Warning>

---

### 🎉 You're Done!
Expand Down
170 changes: 165 additions & 5 deletions docs/src/app/(docs)/uploading-files/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,20 @@ The file seed can be anything you want, but it should be unique for each file,
as well as url safe. In this example, we include a base64 encoding to ensure the
file seed is url safe, but you can do this however you want.

Although we currently only offer a JavaScript SDK, here are some reference
implementations you can use to generate valid file keys. We also plan on making
this process easier in the future.

<Note>
If you struggle to implement it for your language, you can also request one
from the [`/v7/prepareUpload` REST
endpoint](/api-reference/openapi-spec#tag/default/POST/v7/prepareUpload). Keep
in mind that this adds extra latency to your uploads.
</Note>

<Tabs tabs={["JavaScript", "Python", "PHP", "Go"]}>
<Tab>

```ts
import * as Hash from "effect/Hash";
import SQIds, { defaultOptions } from "sqids";
Expand All @@ -123,10 +137,7 @@ function shuffle(str: string, seed: string) {
return chars.join("");
}

function generateKey(
appId: string,
fileSeed: string,
) =>
function generateKey(appId: string, fileSeed: string) {
// Hash and Encode the parts and apiKey as sqids
const alphabet = shuffle(defaultOptions.alphabet, appId);

Expand All @@ -142,6 +153,155 @@ function generateKey(
}
```

</Tab>
<Tab>

```py
import math
import base64
from sqids import Sqids
from sqids.constants import DEFAULT_ALPHABET

def hash_string(s: str) -> int:
h = 5381
for char in reversed(s):
h = (h * 33) ^ ord(char)
# 32-bit integer overflow
h &= 0xFFFFFFFF
h = (h & 0xBFFFFFFF) | ((h >> 1) & 0x40000000)

# Convert to signed 32-bit integer
if h >= 0x80000000:
h -= 0x100000000

return h


def shuffle(string: str, seed: str) -> str:
chars = list(string)
seed_num = hash_string(seed)

for i in range(len(chars)):
j = int(math.fmod(math.fmod(seed_num, i + 1) + i, len(chars)))
chars[i], chars[j] = chars[j], chars[i]

return "".join(chars)


def generate_key(file_seed: str, app_id: str) -> str:
alphabet = shuffle(DEFAULT_ALPHABET, app_id)

encoded_app_id = Sqids(alphabet, min_length=12).encode(
[abs(hash_string(app_id))]
)

return encoded_app_id + file_seed
```

</Tab>
<Tab>

```php
use Sqids\Sqids;

function hash_string(string $string): int {
$h = 5381;
for ($i = strlen($string) - 1; $i >= 0; $i--) {
$char = $string[$i];
$h = ($h * 33) ^ ord($char);
// 32-bit integer overflow
$h &= 0xFFFFFFFF;
}
$h = ($h & 0xBFFFFFFF) | (($h >> 1) & 0x40000000);

// Convert to signed 32-bit integer
if ($h >= 0x80000000) {
$h -= 0x100000000;
}

return $h;
}

function shuffle_string(string $string, string $seed): string {
$chars = str_split($string);
$seed_num = hash_string($seed);

for ($i = 0; $i < count($chars); $i++) {
$j = (($seed_num % ($i + 1)) + $i) % count($chars);
[$chars[$i], $chars[$j]] = [$chars[$j], $chars[$i]];
}

return implode('', $chars);
}

function generate_key(string $file_seed, string $appId): string {
$alphabet = shuffle_string(Sqids::DEFAULT_ALPHABET, $appId);
$sqids = new Sqids($alphabet, 12);

$encodedAppId = $sqids->encode(
[abs(hash_string($appId))]
);

return $encodedAppId . base64_encode($file_seed);
}
```

</Tab>
<Tab>

```go
import (
"math"
"github.com/sqids/sqids-go"
)

func hashString(s string) int32 {
h := int64(5381)
for i := len(s) - 1; i >= 0; i-- {
h = (h * 33) ^ int64(s[i])
// 32-bit integer overflow
h &= 0xFFFFFFFF
}
h = (h & 0xBFFFFFFF) | ((h >> 1) & 0x40000000)

// Convert to signed 32-bit integer
if h >= 0x80000000 {
h -= 0x100000000
}

return int32(h)
}

func shuffle(input string, seed string) string {
chars := []rune(input)
seedNum := hashString(seed)

for i := 0; i < len(chars); i++ {
j := (int(seedNum)%(i+1) + i) % len(chars)
chars[i], chars[j] = chars[j], chars[i]
}

return string(chars)
}

func generateKey(fileSeed string, appId string) string {
alphabet := shuffle("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", appId)
s, _ := sqids.New(sqids.Options{
MinLength: 12,
Alphabet: alphabet,
})

encodedAppId, _ := s.Encode(
[]uint64{uint64(math.Abs(float64(hashString(appId))))},
)

return encodedAppId + fileSeed
}
```

</Tab>
</Tabs>

The URL, to which you will upload the file, will depend on your app's region.
You can find the list of regions in the
[regions documentation](/concepts/regions-acl#regions). The upload URL can then
Expand All @@ -156,7 +316,7 @@ the URL:
```ts
const searchParams = new URLSearchParams({
// Required
expires: Math.floor(Date.now() / 1000) + 3600, // 1 hour from now (you choose)
expires: Date.now() + 60 * 60 * 1000, // 1 hour from now (you choose)
"x-ut-identifier": "MY_APP_ID",
"x-ut-file-name": "my-file.png",
"x-ut-file-size": 131072,
Expand Down
2 changes: 2 additions & 0 deletions docs/src/mdx/rehype.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ function rehypeShiki() {
"css",
"python",
"diff",
"go",
"php",
],
});

Expand Down
Loading

0 comments on commit 0f13859

Please sign in to comment.