From 95cbe25ae7dcc5e09f34371690d3c355d6c4dd54 Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Fri, 19 May 2023 01:18:57 -0300 Subject: [PATCH] remove all the redux stuff and do the same as file upload --- app/actions/actionsTypes.ts | 1 - app/actions/mediaDownload.ts | 50 ----------- app/containers/message/Video.tsx | 80 ++++++++--------- app/definitions/redux/index.ts | 6 +- app/lib/methods/handleMediaDownload.ts | 88 ++++++++++++++----- .../methods/helpers/formatAttachmentUrl.ts | 4 +- app/reducers/index.js | 4 +- app/reducers/mediaDownload.test.ts | 41 --------- app/reducers/mediaDownload.ts | 26 ------ app/selectors/mediaDownload.ts | 26 ------ 10 files changed, 107 insertions(+), 219 deletions(-) delete mode 100644 app/actions/mediaDownload.ts delete mode 100644 app/reducers/mediaDownload.test.ts delete mode 100644 app/reducers/mediaDownload.ts delete mode 100644 app/selectors/mediaDownload.ts diff --git a/app/actions/actionsTypes.ts b/app/actions/actionsTypes.ts index 5e0b267e1..75f42588c 100644 --- a/app/actions/actionsTypes.ts +++ b/app/actions/actionsTypes.ts @@ -84,4 +84,3 @@ export const ENCRYPTION = createRequestTypes('ENCRYPTION', ['INIT', 'STOP', 'DEC export const PERMISSIONS = createRequestTypes('PERMISSIONS', ['SET', 'UPDATE']); export const ROLES = createRequestTypes('ROLES', ['SET', 'UPDATE', 'REMOVE']); -export const MEDIA_DOWNLOAD = createRequestTypes('MEDIA_DOWNLOAD', ['IN_PROGRESS', 'REMOVE']); diff --git a/app/actions/mediaDownload.ts b/app/actions/mediaDownload.ts deleted file mode 100644 index a8759d408..000000000 --- a/app/actions/mediaDownload.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Action } from 'redux'; -import { DownloadResumable } from 'expo-file-system'; - -import { MEDIA_DOWNLOAD } from './actionsTypes'; -import { MediaTypes, mediaDownloadKey } from '../lib/methods/handleMediaDownload'; - -interface IMediaDownloadInProgressAction extends Action { - key: string; - downloadResumable: DownloadResumable; -} - -interface IMediaDownloadRemoveAction extends Action { - key: string; -} - -export type TActionMediaDownload = IMediaDownloadInProgressAction & IMediaDownloadRemoveAction; - -interface IMediaDownloadInprogress { - mediaType: MediaTypes; - messageId: string; - downloadResumable: DownloadResumable; -} - -interface IMediaDownloadRemove { - mediaType: MediaTypes; - messageId: string; -} - -export const mediaDownloadInProgress = ({ - mediaType, - messageId, - downloadResumable -}: IMediaDownloadInprogress): IMediaDownloadInProgressAction => { - const key = mediaDownloadKey(mediaType, messageId); - - return { - type: MEDIA_DOWNLOAD.IN_PROGRESS, - key, - downloadResumable - }; -}; - -export const mediaDownloadRemove = ({ mediaType, messageId }: IMediaDownloadRemove): IMediaDownloadRemoveAction => { - const key = mediaDownloadKey(mediaType, messageId); - - return { - type: MEDIA_DOWNLOAD.REMOVE, - key - }; -}; diff --git a/app/containers/message/Video.tsx b/app/containers/message/Video.tsx index b7e8d796c..3b99ace62 100644 --- a/app/containers/message/Video.tsx +++ b/app/containers/message/Video.tsx @@ -1,7 +1,6 @@ -import React, { useContext, useLayoutEffect, useRef, useState } from 'react'; +import React, { useContext, useEffect, useRef, useState } from 'react'; import { StyleProp, StyleSheet, TextStyle, View, Text } from 'react-native'; import { dequal } from 'dequal'; -import * as FileSystem from 'expo-file-system'; import Touchable from './Touchable'; import Markdown from '../markdown'; @@ -18,10 +17,16 @@ import RCActivityIndicator from '../ActivityIndicator'; import { TGetCustomEmoji } from '../../definitions/IEmoji'; import { useTheme } from '../../theme'; import { formatAttachmentUrl } from '../../lib/methods/helpers/formatAttachmentUrl'; -import { MediaTypes, downloadMediaFile, searchMediaFileAsync } from '../../lib/methods/handleMediaDownload'; +import { + LOCAL_DOCUMENT_PATH, + MediaTypes, + cancelDownload, + downloadMediaFile, + isDownloadActive, + searchMediaFileAsync +} from '../../lib/methods/handleMediaDownload'; import { isAutoDownloadEnabled } from './helpers/mediaDownload/autoDownloadPreference'; import sharedStyles from '../../views/Styles'; -import userPreferences from '../../lib/methods/userPreferences'; const SUPPORTED_TYPES = ['video/quicktime', 'video/mp4', ...(isIOS ? [] : ['video/3gp', 'video/mkv'])]; const isTypeSupported = (type: string) => SUPPORTED_TYPES.indexOf(type) !== -1; @@ -70,18 +75,16 @@ const DownloadIndicator = ({ handleCancelDownload }: { handleCancelDownload(): v ); }; -const downloadResumableKey = (video: string) => `DownloadResumable${video}`; - const Video = React.memo( ({ file, showAttachment, getCustomEmoji, style, isReply, messageId }: IMessageVideo) => { + const [newFile, setNewFile] = useState(file); const [loading, setLoading] = useState(false); const { baseUrl, user } = useContext(MessageContext); const { theme } = useTheme(); const filePath = useRef(''); - const downloadResumable = useRef(null); const video = formatAttachmentUrl(file.video_url, user.id, user.token, baseUrl); - useLayoutEffect(() => { + useEffect(() => { const handleAutoDownload = async () => { if (video) { const searchVideoCached = await searchMediaFileAsync({ @@ -89,16 +92,25 @@ const Video = React.memo( mimeType: file.video_type, messageId }); + console.log('🚀 ~ file: Video.tsx:100 ~ handleAutoDownload ~ searchVideoCached:', searchVideoCached); filePath.current = searchVideoCached.filePath; - handleDownloadResumableSnapshot(); + const downloadActive = isDownloadActive(MediaTypes.video, messageId); if (searchVideoCached.file?.exists) { - file.video_url = searchVideoCached.file.uri; + setNewFile(prev => ({ + ...prev, + video_url: searchVideoCached.file?.uri + })); + if (downloadActive) { + cancelDownload(MediaTypes.video, messageId); + } return; } + if (downloadActive) return setLoading(true); + // We don't pass the author to avoid auto-download what the user sent const autoDownload = await isAutoDownloadEnabled('imagesPreferenceDownload', { user }); - if (autoDownload && !downloadResumable.current) { + if (autoDownload) { await handleDownload(); } } @@ -110,44 +122,31 @@ const Video = React.memo( return null; } - const handleDownloadResumableSnapshot = () => { - if (video) { - const result = userPreferences.getString(downloadResumableKey(video)); - if (result) { - const snapshot = JSON.parse(result); - downloadResumable.current = new FileSystem.DownloadResumable( - video, - filePath.current, - {}, - () => {}, - snapshot.resumeData - ); - setLoading(true); - } - } - }; - const handleDownload = async () => { setLoading(true); - downloadResumable.current = FileSystem.createDownloadResumable(video, filePath.current); - userPreferences.setString(downloadResumableKey(video), JSON.stringify(downloadResumable.current.savable())); const videoUri = await downloadMediaFile({ - url: video, - filePath: filePath.current, - downloadResumable: downloadResumable.current + downloadUrl: video, + mediaType: MediaTypes.video, + messageId, + path: filePath.current }); - userPreferences.removeItem(downloadResumableKey(video)); + console.log('🚀 ~ file: Video.tsx:137 ~ handleDownload ~ videoUri:', videoUri); if (videoUri) { - file.video_url = videoUri; + setNewFile(prev => ({ + ...prev, + video_url: videoUri + })); } setLoading(false); }; const onPress = async () => { if (file.video_type && isTypeSupported(file.video_type) && showAttachment) { - // Keep the video downloading while showing the video buffering - handleDownload(); - return showAttachment(file); + if (!newFile.video_url?.startsWith(LOCAL_DOCUMENT_PATH) && !loading) { + // Keep the video downloading while showing the video buffering + handleDownload(); + } + return showAttachment(newFile); } if (!isIOS && file.video_url) { @@ -158,9 +157,8 @@ const Video = React.memo( }; const handleCancelDownload = () => { - if (loading && downloadResumable.current) { - downloadResumable.current.cancelAsync(); - userPreferences.removeItem(downloadResumableKey(video)); + if (loading) { + cancelDownload(MediaTypes.video, messageId); return setLoading(false); } }; diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index 476d62613..71c103c16 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -16,7 +16,6 @@ import { TActionSortPreferences } from '../../actions/sortPreferences'; import { TActionUserTyping } from '../../actions/usersTyping'; import { TActionPermissions } from '../../actions/permissions'; import { TActionEnterpriseModules } from '../../actions/enterpriseModules'; -import { TActionMediaDownload } from '../../actions/mediaDownload'; // REDUCERS import { IActiveUsers } from '../../reducers/activeUsers'; import { IApp } from '../../reducers/app'; @@ -35,7 +34,6 @@ import { IShare } from '../../reducers/share'; import { IInquiry } from '../../ee/omnichannel/reducers/inquiry'; import { IPermissionsState } from '../../reducers/permissions'; import { IEnterpriseModules } from '../../reducers/enterpriseModules'; -import { IDownloads } from '../../reducers/mediaDownload'; export interface IApplicationState { settings: TSettingsState; @@ -59,7 +57,6 @@ export interface IApplicationState { encryption: IEncryption; permissions: IPermissionsState; roles: IRoles; - mediaDownload: IDownloads; } export type TApplicationActions = TActionActiveUsers & @@ -78,5 +75,4 @@ export type TApplicationActions = TActionActiveUsers & TActionApp & TActionInquiry & TActionPermissions & - TActionEnterpriseModules & - TActionMediaDownload; + TActionEnterpriseModules; diff --git a/app/lib/methods/handleMediaDownload.ts b/app/lib/methods/handleMediaDownload.ts index de58e7b37..39f8e06e4 100644 --- a/app/lib/methods/handleMediaDownload.ts +++ b/app/lib/methods/handleMediaDownload.ts @@ -1,5 +1,7 @@ import * as FileSystem from 'expo-file-system'; import * as mime from 'react-native-mime-types'; +import { isEmpty } from 'lodash'; +import RNFetchBlob, { FetchBlobResponse, StatefulPromise } from 'rn-fetch-blob'; import { sanitizeLikeString } from '../database/utils'; import { store } from '../store/auxStore'; @@ -22,8 +24,70 @@ const defaultType = { [MediaTypes.video]: 'mp4' }; +const downloadQueue: { [index: string]: StatefulPromise } = {}; + export const mediaDownloadKey = (mediaType: MediaTypes, messageId: string) => `${mediaType}-${messageId}`; +export function isDownloadActive(mediaType: MediaTypes, messageId: string): boolean { + return !!downloadQueue[mediaDownloadKey(mediaType, messageId)]; +} + +export async function cancelDownload(mediaType: MediaTypes, messageId: string): Promise { + const downloadKey = mediaDownloadKey(mediaType, messageId); + if (!isEmpty(downloadQueue[downloadKey])) { + console.log('🚀 ~ file: handleMediaDownload.ts:38 ~ cancelDownload ~ downloadQueue:', downloadQueue); + try { + await downloadQueue[downloadKey].cancel(); + } catch { + // Do nothing + } + delete downloadQueue[downloadKey]; + console.log('🚀 ~ file: handleMediaDownload.ts:47 ~ cancelDownload ~ downloadQueue:', downloadQueue); + } +} + +export function downloadMediaFile({ + mediaType, + messageId, + downloadUrl, + path +}: { + mediaType: MediaTypes; + messageId: string; + downloadUrl: string; + path: string; +}): Promise { + return new Promise((resolve, reject) => { + const downloadKey = mediaDownloadKey(mediaType, messageId); + const options = { + timeout: 10000, + indicator: true, + overwrite: true, + path: path.replace('file://', '') + }; + downloadQueue[downloadKey] = RNFetchBlob.config(options).fetch('GET', downloadUrl); + downloadQueue[downloadKey].then(response => { + if (response.respInfo.status >= 200 && response.respInfo.status < 400) { + // If response is all good... + try { + console.log('🚀 ~ file: handleMediaDownload.ts:71 ~ returnnewPromise ~ response:', response, response.path()); + resolve(response.data); + delete downloadQueue[downloadKey]; + } catch (e) { + log(e); + } + } else { + reject(null); + } + }); + downloadQueue[downloadKey].catch(error => { + console.log('🚀 ~ file: handleMediaDownload.ts:82 ~ returnnewPromise ~ error:', error); + delete downloadQueue[downloadKey]; + reject(null); + }); + }); +} + export const LOCAL_DOCUMENT_PATH = `${FileSystem.documentDirectory}`; const sanitizeString = (value: string) => sanitizeLikeString(value.substring(value.lastIndexOf('/') + 1)); @@ -72,30 +136,6 @@ export const searchMediaFileAsync = async ({ return { file, filePath }; }; -export const downloadMediaFile = async ({ - url, - filePath, - downloadResumable -}: { - url: string; - filePath: string; - downloadResumable?: FileSystem.DownloadResumable; -}) => { - let uri = ''; - try { - if (downloadResumable) { - const downloadFile = await downloadResumable.downloadAsync(); - uri = downloadFile?.uri || ''; - } else { - const downloadedFile = await FileSystem.downloadAsync(url, filePath); - uri = downloadedFile.uri; - } - } catch (error) { - log(error); - } - return uri; -}; - export const deleteAllSpecificMediaFiles = async (type: MediaTypes, serverUrl: string): Promise => { try { const serverUrlParsed = sanitizeString(serverUrl); diff --git a/app/lib/methods/helpers/formatAttachmentUrl.ts b/app/lib/methods/helpers/formatAttachmentUrl.ts index e67ef7869..b9102b8c6 100644 --- a/app/lib/methods/helpers/formatAttachmentUrl.ts +++ b/app/lib/methods/helpers/formatAttachmentUrl.ts @@ -1,7 +1,7 @@ -import * as FileSystem from 'expo-file-system'; +import { LOCAL_DOCUMENT_PATH } from '../handleMediaDownload'; export const formatAttachmentUrl = (attachmentUrl: string | undefined, userId: string, token: string, server: string): string => { - if (attachmentUrl?.startsWith(`${FileSystem.documentDirectory}`)) { + if (attachmentUrl?.startsWith(LOCAL_DOCUMENT_PATH)) { return attachmentUrl; } if (attachmentUrl && attachmentUrl.startsWith('http')) { diff --git a/app/reducers/index.js b/app/reducers/index.js index e2210b447..7aad41aa0 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -21,7 +21,6 @@ import enterpriseModules from './enterpriseModules'; import encryption from './encryption'; import permissions from './permissions'; import roles from './roles'; -import mediaDownload from './mediaDownload'; export default combineReducers({ settings, @@ -44,6 +43,5 @@ export default combineReducers({ enterpriseModules, encryption, permissions, - roles, - mediaDownload + roles }); diff --git a/app/reducers/mediaDownload.test.ts b/app/reducers/mediaDownload.test.ts deleted file mode 100644 index 62e8c8048..000000000 --- a/app/reducers/mediaDownload.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { DownloadResumable } from 'expo-file-system'; - -import { mediaDownloadInProgress, mediaDownloadRemove } from '../actions/mediaDownload'; -import { IDownloads, initialState } from './mediaDownload'; -import { mockedStore } from './mockedStore'; -import { MediaTypes } from '../lib/methods/handleMediaDownload'; - -describe('test reducer', () => { - const downloadResumable = 'downloadResumable' as unknown as DownloadResumable; - const downloadResumableTwo = 'downloadResumableTwo' as unknown as DownloadResumable; - - it('should return initial state', () => { - const state = mockedStore.getState().mediaDownload; - expect(state).toEqual(initialState); - }); - it('should return modified store after action', () => { - const expectState: IDownloads = { [`${MediaTypes.video}-id`]: downloadResumable }; - mockedStore.dispatch(mediaDownloadInProgress({ mediaType: MediaTypes.video, messageId: 'id', downloadResumable })); - const state = mockedStore.getState().mediaDownload; - expect(state).toEqual({ ...expectState }); - }); - it('should return the state correct after add second download', () => { - mockedStore.dispatch( - mediaDownloadInProgress({ mediaType: MediaTypes.audio, messageId: 'id', downloadResumable: downloadResumableTwo }) - ); - const expectState = { - [`${MediaTypes.video}-id`]: downloadResumable, - [`${MediaTypes.audio}-id`]: downloadResumableTwo - }; - const state = mockedStore.getState().mediaDownload; - expect(state).toEqual({ ...expectState }); - }); - it('should remove one download', () => { - mockedStore.dispatch(mediaDownloadRemove({ mediaType: MediaTypes.video, messageId: 'id' })); - const expectState = { - [`${MediaTypes.audio}-id`]: downloadResumableTwo - }; - const state = mockedStore.getState().mediaDownload; - expect(state).toEqual({ ...expectState }); - }); -}); diff --git a/app/reducers/mediaDownload.ts b/app/reducers/mediaDownload.ts deleted file mode 100644 index 3b59d69d2..000000000 --- a/app/reducers/mediaDownload.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { DownloadResumable } from 'expo-file-system'; - -import { MEDIA_DOWNLOAD } from '../actions/actionsTypes'; -import { TApplicationActions } from '../definitions'; - -export interface IDownloads { - [key: string]: DownloadResumable; -} - -export const initialState: IDownloads = {}; - -export default function mediaDownload(state = initialState, action: TApplicationActions): IDownloads { - switch (action.type) { - case MEDIA_DOWNLOAD.IN_PROGRESS: - return { - ...state, - [action.key]: action.downloadResumable - }; - case MEDIA_DOWNLOAD.REMOVE: - const newState = { ...state }; - delete newState[action.key]; - return newState; - default: - return state; - } -} diff --git a/app/selectors/mediaDownload.ts b/app/selectors/mediaDownload.ts deleted file mode 100644 index 0331abfdc..000000000 --- a/app/selectors/mediaDownload.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { createSelector } from 'reselect'; - -import { IApplicationState } from '../definitions'; -import { MediaTypes, mediaDownloadKey } from '../lib/methods/handleMediaDownload'; -import { IDownloads } from '../reducers/mediaDownload'; - -const selectMediaDownload = (state: IApplicationState) => state.mediaDownload; - -const getMediaDownload = (mediaDownload: IDownloads, { mediaType, messageId }: { mediaType: MediaTypes; messageId: string }) => { - console.log('🚀 ~ file: mediaDownload.ts:10 ~ getMediaDownload ~ { mediaType, messageId }:', { mediaType, messageId }); - console.log('🚀 ~ file: mediaDownload.ts:10 ~ getMediaDownload ~ mediaDownload:', mediaDownload); - const key = mediaDownloadKey(mediaType, messageId); - if (mediaDownload[key]) return mediaDownload[key]; - return null; -}; - -export const getDownloadResumable = createSelector( - [ - selectMediaDownload, - (_state: IApplicationState, { mediaType, messageId }: { mediaType: MediaTypes; messageId: string }) => ({ - mediaType, - messageId - }) - ], - getMediaDownload -);