diff --git a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecryptedLocalAttachment.kt b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecryptedLocalAttachment.kt index ab6f53979..770e7dac6 100644 --- a/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecryptedLocalAttachment.kt +++ b/android/src/main/java/expo/modules/xmtpreactnativesdk/wrappers/DecryptedLocalAttachment.kt @@ -15,7 +15,7 @@ class DecryptedLocalAttachment( companion object { fun fromJsonObject(obj: JsonObject) = DecryptedLocalAttachment( obj.get("fileUri").asString, - obj.get("mimeType").asString, + obj.get("mimeType")?.asString ?: "", obj.get("filename")?.asString ?: "", ) diff --git a/example/android/app/src/main/java/expo/modules/xmtpreactnativesdk/example/MainApplication.java b/example/android/app/src/main/java/expo/modules/xmtpreactnativesdk/example/MainApplication.java index b96917926..08d7246c6 100644 --- a/example/android/app/src/main/java/expo/modules/xmtpreactnativesdk/example/MainApplication.java +++ b/example/android/app/src/main/java/expo/modules/xmtpreactnativesdk/example/MainApplication.java @@ -15,8 +15,13 @@ import expo.modules.ApplicationLifecycleDispatcher; import expo.modules.ReactNativeHostWrapper; +import com.ReactNativeBlobUtil.ReactNativeBlobUtilUtils; + +import java.security.cert.CertificateException; import java.util.List; +import javax.net.ssl.X509TrustManager; + public class MainApplication extends Application implements ReactApplication { private final ReactNativeHost mReactNativeHost = @@ -64,6 +69,21 @@ public void onCreate() { // If you opted-in for the New Architecture, we load the native entry point for this app. DefaultNewArchitectureEntryPoint.load(); } + ReactNativeBlobUtilUtils.sharedTrustManager = new X509TrustManager() { + @Override + public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException { + } + + @Override + public java.security.cert.X509Certificate[] getAcceptedIssuers() { + return new java.security.cert.X509Certificate[]{}; + } + }; + ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); ApplicationLifecycleDispatcher.onApplicationCreate(this); } diff --git a/example/dev/local/docker-compose.yml b/example/dev/local/docker-compose.yml index aeddca0ed..0a09c8d48 100644 --- a/example/dev/local/docker-compose.yml +++ b/example/dev/local/docker-compose.yml @@ -32,4 +32,18 @@ services: depends_on: wakunode: condition: service_healthy - build: ./test \ No newline at end of file + build: ./test + + upload-service: + build: ./upload-service + ports: + - 4567:4567 + + caddy: + image: caddy:latest + ports: + - "443:443" + volumes: + - ./upload-service/Caddyfile:/etc/caddy/Caddyfile + - ./upload-service/data/data:/data + - ./upload-service/data/config:/config \ No newline at end of file diff --git a/example/src/ConversationScreen.tsx b/example/src/ConversationScreen.tsx index 5b12e2b4b..93ce3f442 100644 --- a/example/src/ConversationScreen.tsx +++ b/example/src/ConversationScreen.tsx @@ -132,9 +132,11 @@ export default function ConversationScreen({ + onAttachedImageFromCamera={(image) => { console.log('from camera', image) - } + setAttachment({ image }) + setShowingAttachmentModal(false) + }} onAttachedImageFromLibrary={(image) => { setAttachment({ image }) setShowingAttachmentModal(false) diff --git a/example/src/hooks.tsx b/example/src/hooks.tsx index dc64123b2..3e474459b 100644 --- a/example/src/hooks.tsx +++ b/example/src/hooks.tsx @@ -1,5 +1,6 @@ +import { useCallback, useEffect, useState } from 'react' import EncryptedStorage from 'react-native-encrypted-storage' -import { useQuery, UseQueryResult } from 'react-query' +import { useMutation, useQuery, UseQueryResult } from 'react-query' import { Conversation, DecodedMessage, @@ -273,38 +274,71 @@ export function usePrepareRemoteAttachment({ }): { remoteAttachment: RemoteAttachmentContent | undefined } { + const [remoteAttachment, setRemoteAttachment] = useState< + RemoteAttachmentContent | undefined + >(undefined) const { client } = useXmtp() - const { data: encrypted } = useQuery( - ['xmtp', 'remoteAttachment', 'local', fileUri, mimeType ?? ''], - () => + const { mutateAsync: encryptAttachment } = useMutation< + EncryptedLocalAttachment, + unknown, + { fileUri?: string; mimeType?: string } + >( + ['xmtp', 'remoteAttachment', 'local'], + ({ fileUri, mimeType }) => client!.encryptAttachment({ fileUri: fileUri!, mimeType, }), - { - enabled: !!client && !!fileUri, - } + {} ) - const { data: url } = useQuery( - ['xmtp', 'remoteAttachment', 'upload', encrypted?.metadata?.contentDigest], - () => + const { mutateAsync: uploadAttachment } = useMutation< + string, + unknown, + EncryptedLocalAttachment + >( + ['xmtp', 'remoteAttachment', 'upload'], + (attachement: EncryptedLocalAttachment) => uploadFile( - encrypted!.encryptedLocalFileUri, - encrypted?.metadata?.contentDigest - ), - { - enabled: !!encrypted, - } + attachement!.encryptedLocalFileUri, + attachement?.metadata?.contentDigest + ) ) - return { - remoteAttachment: url - ? { - url, + + const callback = useCallback( + async ({ fileUri, mimeType }: { fileUri: string; mimeType?: string }) => { + const encrypted = await encryptAttachment({ + fileUri, + mimeType, + }) + const url = await uploadAttachment(encrypted) + return { + url, + metadata: encrypted.metadata, + } + }, + [encryptAttachment, uploadAttachment] + ) + + useEffect(() => { + console.log('Preparing remote attachment', { fileUri, mimeType }) + if (!fileUri) { + setRemoteAttachment(undefined) + return + } + callback({ fileUri, mimeType }) + .then((res) => { + setRemoteAttachment({ + url: res.url, scheme: 'https://', - ...encrypted!.metadata, - } - : undefined, - } + ...res.metadata, + }) + }) + .catch((err) => { + console.log('Error preparing remote attachment', err) + }) + }, [fileUri, mimeType, callback]) + + return { remoteAttachment } } /** diff --git a/example/src/storage.ts b/example/src/storage.ts index 385f8468a..3801702b2 100644 --- a/example/src/storage.ts +++ b/example/src/storage.ts @@ -5,7 +5,10 @@ import ReactNativeBlobUtil from 'react-native-blob-util' // It is not intended for production use, but is useful for testing and development. // See `dev/local/upload-service` -const storageUrl = process.env.REACT_APP_STORAGE_URL || 'https://localhost' +const useLocalServer = !process.env.REACT_APP_USE_LOCAL_SERVER +const storageUrl = useLocalServer + ? 'https://localhost' + : process.env.REACT_APP_STORAGE_URL const headers = { 'Content-Type': 'application/octet-stream', } @@ -16,20 +19,24 @@ export async function uploadFile( ): Promise { const url = `${storageUrl}/${fileId}` console.log('uploading to', url) - await ReactNativeBlobUtil.config({ fileCache: true }).fetch( + await ReactNativeBlobUtil.config({ + fileCache: true, + trusty: useLocalServer, + }).fetch( 'POST', url, headers, ReactNativeBlobUtil.wrap(localFileUri.slice('file://'.length)) ) + return url } export async function downloadFile(url: string): Promise { console.log('downloading from', url) - const res = await ReactNativeBlobUtil.config({ fileCache: true }).fetch( - 'GET', - url - ) + const res = await ReactNativeBlobUtil.config({ + fileCache: true, + trusty: useLocalServer, + }).fetch('GET', url) return `file://${res.path()}` }