diff --git a/app/containers/message/Audio.tsx b/app/containers/message/Audio.tsx index eded0bdd0..2e2921036 100644 --- a/app/containers/message/Audio.tsx +++ b/app/containers/message/Audio.tsx @@ -121,7 +121,6 @@ Button.displayName = 'MessageAudioButton'; class MessageAudio extends React.Component { static contextType = MessageContext; private sound: Sound; - private filePath?: string; constructor(props: IMessageAudioProps) { super(props); @@ -149,9 +148,8 @@ class MessageAudio extends React.Component { + const { file } = this.props; // @ts-ignore can't use declare to type this const { user } = this.context; this.setState({ loading: true }); try { const url = this.getUrl(); - if (url && this.filePath) { + if (url) { const audio = await downloadMediaFile({ downloadUrl: `${url}?rc_uid=${user.id}&rc_token=${user.token}`, - path: this.filePath + type: 'audio', + mimeType: file.audio_type }); - await this.sound.loadAsync({ uri: audio }); this.setState({ loading: false, cached: true }); } diff --git a/app/containers/message/Image.tsx b/app/containers/message/Image.tsx index f6b6c31f1..ad8a32a17 100644 --- a/app/containers/message/Image.tsx +++ b/app/containers/message/Image.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useLayoutEffect, useRef, useState } from 'react'; +import React, { useContext, useLayoutEffect, useState } from 'react'; import { StyleProp, TextStyle, View } from 'react-native'; import FastImage from 'react-native-fast-image'; import { dequal } from 'dequal'; @@ -68,7 +68,6 @@ const ImageContainer = React.memo( const [loading, setLoading] = useState(true); const { theme } = useTheme(); const { baseUrl, user } = useContext(MessageContext); - const filePath = useRef(''); const getUrl = (link?: string) => imageUrl || formatAttachmentUrl(link, user.id, user.token, baseUrl); const img = getUrl(file.image_url); // The param file.title_link is the one that point to image with best quality, however we still need to test the imageUrl @@ -83,11 +82,10 @@ const ImageContainer = React.memo( mimeType: imageCached.image_type, urlToCache: imgUrlToCache }); - filePath.current = cachedImageResult.filePath; - if (cachedImageResult.file?.exists) { + if (cachedImageResult?.exists) { setImageCached(prev => ({ ...prev, - title_link: cachedImageResult.file?.uri + title_link: cachedImageResult?.uri })); setLoading(false); setCached(true); @@ -122,7 +120,8 @@ const ImageContainer = React.memo( try { const imageUri = await downloadMediaFile({ downloadUrl: imgUrlToCache, - path: filePath.current + type: 'image', + mimeType: imageCached.image_type }); setImageCached(prev => ({ ...prev, diff --git a/app/containers/message/Video.tsx b/app/containers/message/Video.tsx index ca0833e61..9a45315fa 100644 --- a/app/containers/message/Video.tsx +++ b/app/containers/message/Video.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useRef, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { StyleProp, StyleSheet, TextStyle, View, Text } from 'react-native'; import { dequal } from 'dequal'; import * as VideoThumbnails from 'expo-video-thumbnails'; @@ -106,7 +106,6 @@ const Video = React.memo( const [cached, setCached] = useState(false); const { baseUrl, user } = useContext(MessageContext); const { theme } = useTheme(); - const filePath = useRef(''); const video = formatAttachmentUrl(file.video_url, user.id, user.token, baseUrl); useEffect(() => { @@ -117,12 +116,11 @@ const Video = React.memo( mimeType: file.video_type, urlToCache: video }); - filePath.current = cachedVideoResult.filePath; const downloadActive = isDownloadActive(video); - if (cachedVideoResult.file?.exists) { + if (cachedVideoResult?.exists) { setVideoCached(prev => ({ ...prev, - video_url: cachedVideoResult.file?.uri + video_url: cachedVideoResult?.uri })); setLoading(false); setCached(true); @@ -157,7 +155,8 @@ const Video = React.memo( try { const videoUri = await downloadMediaFile({ downloadUrl: video, - path: filePath.current + type: 'video', + mimeType: file.video_type }); setVideoCached(prev => ({ ...prev, diff --git a/app/lib/methods/handleMediaDownload.ts b/app/lib/methods/handleMediaDownload.ts index 243170398..40f05e36c 100644 --- a/app/lib/methods/handleMediaDownload.ts +++ b/app/lib/methods/handleMediaDownload.ts @@ -8,54 +8,12 @@ import log from './helpers/log'; export type MediaTypes = 'audio' | 'image' | 'video'; -const typeString = { - audio: 'audios/', - image: 'images/', - video: 'videos/' -}; - const defaultType = { audio: 'mp3', image: 'jpg', video: 'mp4' }; -const downloadQueue: { [index: string]: FileSystem.DownloadResumable } = {}; - -export const mediaDownloadKey = (messageUrl: string) => `${sanitizeString(messageUrl)}`; - -export function isDownloadActive(messageUrl: string): boolean { - return !!downloadQueue[mediaDownloadKey(messageUrl)]; -} - -export async function cancelDownload(messageUrl: string): Promise { - const downloadKey = mediaDownloadKey(messageUrl); - if (!isEmpty(downloadQueue[downloadKey])) { - try { - await downloadQueue[downloadKey].cancelAsync(); - } catch { - // Do nothing - } - delete downloadQueue[downloadKey]; - } -} - -export function downloadMediaFile({ downloadUrl, path }: { downloadUrl: string; path: string }): Promise { - return new Promise(async (resolve, reject) => { - try { - const downloadKey = mediaDownloadKey(downloadUrl); - downloadQueue[downloadKey] = FileSystem.createDownloadResumable(downloadUrl, path); - const result = await downloadQueue[downloadKey].downloadAsync(); - if (result?.uri) { - return resolve(result.uri); - } - reject(); - } catch { - reject(); - } - }); -} - export const LOCAL_DOCUMENT_DIRECTORY = FileSystem.documentDirectory; const sanitizeString = (value: string) => { @@ -93,6 +51,24 @@ const ensureDirAsync = async (dir: string, intermediates = true): Promise return ensureDirAsync(dir, intermediates); }; +const getFilePath = ({ type, mimeType, urlToCache }: { type: MediaTypes; mimeType?: string; urlToCache?: string }) => { + if (!urlToCache) { + return; + } + const folderPath = getFolderPath(); + const fileUrlSanitized = sanitizeString(urlToCache); + const filename = `${fileUrlSanitized}.${getExtension(type, mimeType)}`; + const filePath = `${folderPath}${filename}`; + return filePath; +}; + +const getFolderPath = () => { + const serverUrl = store.getState().server.server; + const serverUrlParsed = serverUrlParsedAsPath(serverUrl); + const folderPath = `${LOCAL_DOCUMENT_DIRECTORY}${serverUrlParsed}`; + return folderPath; +}; + export const getMediaCache = async ({ type, mimeType, @@ -103,21 +79,20 @@ export const getMediaCache = async ({ urlToCache?: string; }) => { if (!urlToCache) { - return { file: null, filePath: '' }; + return null; } try { - const serverUrl = store.getState().server.server; - const serverUrlParsed = serverUrlParsedAsPath(serverUrl); - const folderPath = `${LOCAL_DOCUMENT_DIRECTORY}${serverUrlParsed}${typeString[type]}`; - const fileUrlSanitized = sanitizeString(urlToCache); - const filename = `${fileUrlSanitized}.${getExtension(type, mimeType)}`; - const filePath = `${folderPath}${filename}`; + const folderPath = getFolderPath(); + const filePath = getFilePath({ type, mimeType, urlToCache }); + if (!filePath) { + return null; + } await ensureDirAsync(folderPath); const file = await FileSystem.getInfoAsync(filePath); - return { file, filePath }; + return file; } catch (error) { log(error); - return { file: null, filePath: '' }; + return null; } }; @@ -130,3 +105,52 @@ export const deleteMediaFiles = async (serverUrl: string): Promise => { log(error); } }; + +const downloadQueue: { [index: string]: FileSystem.DownloadResumable } = {}; + +export const mediaDownloadKey = (messageUrl: string) => `${sanitizeString(messageUrl)}`; + +export function isDownloadActive(messageUrl: string): boolean { + return !!downloadQueue[mediaDownloadKey(messageUrl)]; +} + +export async function cancelDownload(messageUrl: string): Promise { + const downloadKey = mediaDownloadKey(messageUrl); + if (!isEmpty(downloadQueue[downloadKey])) { + try { + await downloadQueue[downloadKey].cancelAsync(); + } catch { + // Do nothing + } + delete downloadQueue[downloadKey]; + } +} + +export function downloadMediaFile({ + type, + mimeType, + downloadUrl +}: { + type: MediaTypes; + mimeType?: string; + downloadUrl: string; +}): Promise { + return new Promise(async (resolve, reject) => { + try { + const path = getFilePath({ type, mimeType, urlToCache: downloadUrl }); + if (!path) { + reject(); + return; + } + const downloadKey = mediaDownloadKey(downloadUrl); + downloadQueue[downloadKey] = FileSystem.createDownloadResumable(downloadUrl, path); + const result = await downloadQueue[downloadKey].downloadAsync(); + if (result?.uri) { + return resolve(result.uri); + } + reject(); + } catch { + reject(); + } + }); +}