rcgit

/ karakeep

Commit 219e16a0

SHA 219e16a0bd8e0aaa7cbba24fc5e758e9f719907c
Author MohamedBassem <me at mbassem dot com>
Author Date 2025-01-18 21:59 +0000
Committer MohamedBassem <me at mbassem dot com>
Commit Date 2025-01-18 21:59 +0000
Parent(s) bbbf33536c6c (diff)
Tree f6851a97baf2

patch snapshot

fix(mobile): Use external blob encoder to fix mobile image uploads. Fixes #800
File + - Graph
M apps/mobile/lib/upload.ts +17 -15
M apps/mobile/package.json +1 -0
M pnpm-lock.yaml +24 -1
3 file(s) changed, 42 insertions(+), 16 deletions(-)

apps/mobile/lib/upload.ts

diff --git a/apps/mobile/lib/upload.ts b/apps/mobile/lib/upload.ts
index b31faa90..715119b1 100644
--- a/apps/mobile/lib/upload.ts
+++ b/apps/mobile/lib/upload.ts
@@ -1,3 +1,4 @@
+import ReactNativeBlobUtil from "react-native-blob-util";
 import { useMutation } from "@tanstack/react-query";
 
 import { BookmarkTypes, ZBookmark } from "@hoarder/shared/types/bookmarks";
@@ -36,23 +37,24 @@ export function useUploadAsset(
 
   const { mutate: uploadAsset, isPending: isUploading } = useMutation({
     mutationFn: async (file: { type: string; name: string; uri: string }) => {
-      const formData = new FormData();
-      // @ts-expect-error This is a valid api in react native
-      formData.append("file", {
-        uri: file.uri,
-        name: file.name,
-        type: file.type,
-      });
-      const resp = await fetch(`${settings.address}/api/assets`, {
-        method: "POST",
-        body: formData,
-        headers: {
+      // There's a bug in the native FormData implementation (https://github.com/facebook/react-native/issues/44737)
+      // that will only get fixed in react native 0.77. Using the BlobUtil implementation for now.
+      const resp = await ReactNativeBlobUtil.fetch(
+        "POST",
+        `${settings.address}/api/assets`,
+        {
           Authorization: `Bearer ${settings.apiKey}`,
+          "Content-Type": "multipart/form-data",
         },
-      });
-      if (!resp.ok) {
-        throw new Error(await resp.text());
-      }
+        [
+          {
+            name: "file",
+            filename: file.name,
+            type: file.type,
+            data: ReactNativeBlobUtil.wrap(file.uri.replace("file://", "")),
+          },
+        ],
+      );
       return zUploadResponseSchema.parse(await resp.json());
     },
     onSuccess: (resp) => {

apps/mobile/package.json

diff --git a/apps/mobile/package.json b/apps/mobile/package.json
index 2634bdd7..016531f1 100644
--- a/apps/mobile/package.json
+++ b/apps/mobile/package.json
@@ -44,6 +44,7 @@
     "react": "^18.3.1",
     "react-native": "0.76.3",
     "react-native-awesome-slider": "^2.5.3",
+    "react-native-blob-util": "^0.21.2",
     "react-native-gesture-handler": "~2.21.2",
     "react-native-image-viewing": "^0.2.2",
     "react-native-markdown-display": "^7.0.2",

pnpm-lock.yaml

diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a677c7d6..7fdfac1d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -378,6 +378,9 @@ importers:
       react-native-awesome-slider:
         specifier: ^2.5.3
         version: 2.5.3(react-native-gesture-handler@2.21.2(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native-reanimated@3.16.2(@babel/core@7.26.0)(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
+      react-native-blob-util:
+        specifier: ^0.21.2
+        version: 0.21.2(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
       react-native-gesture-handler:
         specifier: ~2.21.2
         version: 2.21.2(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
@@ -5677,6 +5680,9 @@ packages:
   bare-path@2.1.0:
     resolution: {integrity: sha512-DIIg7ts8bdRKwJRJrUMy/PICEaQZaPGZ26lsSx9MJSwIhSrcdHn7/C8W+XmnG/rKi6BaRcz+JO00CjZteybDtw==}
 
+  base-64@0.1.0:
+    resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==}
+
   base64-js@1.5.1:
     resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
 
@@ -11398,6 +11404,12 @@ packages:
       react-native-gesture-handler: '>=2.0.0'
       react-native-reanimated: '>=3.0.0'
 
+  react-native-blob-util@0.21.2:
+    resolution: {integrity: sha512-4DsF+zzBEJmLww12PsUjwqjSaUrz7gdL5MeduSRn9fv5M8GLRIk5WHcgc7n+fzorGhbbL9QtB/QLTL6dMKjYUw==}
+    peerDependencies:
+      react: '*'
+      react-native: '*'
+
   react-native-css-interop@0.1.22:
     resolution: {integrity: sha512-Mu01e+H9G+fxSWvwtgWlF5MJBJC4VszTCBXopIpeR171lbeBInHb8aHqoqRPxmJpi3xIHryzqKFOJYAdk7PBxg==}
     engines: {node: '>=18'}
@@ -20199,6 +20211,9 @@ snapshots:
     dev: false
     optional: true
 
+  base-64@0.1.0:
+    dev: false
+
   base64-js@1.5.1:
     dev: false
 
@@ -26918,7 +26933,7 @@ snapshots:
   path-scurry@1.10.1:
     dependencies:
       lru-cache: 10.2.0
-      minipass: 7.0.4
+      minipass: 7.1.2
     dev: false
 
   path-scurry@1.11.1:
@@ -27928,6 +27943,14 @@ snapshots:
       react-native-reanimated: 3.16.2(@babel/core@7.26.0)(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)
     dev: false
 
+  react-native-blob-util@0.21.2(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1):
+    dependencies:
+      base-64: 0.1.0
+      glob: 10.4.5
+      react: 18.3.1
+      react-native: 0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1)
+    dev: false
+
   react-native-css-interop@0.1.22(react-native-reanimated@3.16.2(@babel/core@7.26.0)(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.12.0(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native-svg@15.8.0(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1))(react-native@0.76.3(@babel/core@7.26.0)(@babel/preset-env@7.24.0(@babel/core@7.26.0))(@types/react@18.3.12)(react@18.3.1))(react@18.3.1)(tailwindcss@3.4.1):
     dependencies:
       '@babel/helper-module-imports': 7.25.9