From 154deb62acb0c77520de6390bafb07971948876e Mon Sep 17 00:00:00 2001 From: Reinaldo Neto Date: Thu, 18 May 2023 18:36:58 -0300 Subject: [PATCH] introducing the reducer to keep the downloads in progress --- app/actions/actionsTypes.ts | 1 + app/actions/mediaDownload.ts | 52 ++++++++++++++++++++++++++ app/definitions/redux/index.ts | 6 ++- app/lib/methods/handleMediaDownload.ts | 6 ++- app/reducers/index.js | 4 +- app/reducers/mediaDownload.test.ts | 41 ++++++++++++++++++++ app/reducers/mediaDownload.ts | 26 +++++++++++++ 7 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 app/actions/mediaDownload.ts create mode 100644 app/reducers/mediaDownload.test.ts create mode 100644 app/reducers/mediaDownload.ts diff --git a/app/actions/actionsTypes.ts b/app/actions/actionsTypes.ts index 75f42588c..5e0b267e1 100644 --- a/app/actions/actionsTypes.ts +++ b/app/actions/actionsTypes.ts @@ -84,3 +84,4 @@ 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 new file mode 100644 index 000000000..342010339 --- /dev/null +++ b/app/actions/mediaDownload.ts @@ -0,0 +1,52 @@ +import { Action } from 'redux'; +import { DownloadResumable } from 'expo-file-system'; + +import { MEDIA_DOWNLOAD } from './actionsTypes'; +import { MediaTypes } 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; +} + +const downloadKey = (mediaType: MediaTypes, messageId: string) => `${mediaType}-${messageId}`; + +export const mediaDownloadInProgress = ({ + mediaType, + messageId, + downloadResumable +}: IMediaDownloadInprogress): IMediaDownloadInProgressAction => { + const key = downloadKey(mediaType, messageId); + + return { + type: MEDIA_DOWNLOAD.IN_PROGRESS, + key, + downloadResumable + }; +}; + +export const mediaDownloadRemove = ({ mediaType, messageId }: IMediaDownloadRemove): IMediaDownloadRemoveAction => { + const key = downloadKey(mediaType, messageId); + + return { + type: MEDIA_DOWNLOAD.REMOVE, + key + }; +}; diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index 71c103c16..476d62613 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -16,6 +16,7 @@ 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'; @@ -34,6 +35,7 @@ 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; @@ -57,6 +59,7 @@ export interface IApplicationState { encryption: IEncryption; permissions: IPermissionsState; roles: IRoles; + mediaDownload: IDownloads; } export type TApplicationActions = TActionActiveUsers & @@ -75,4 +78,5 @@ export type TApplicationActions = TActionActiveUsers & TActionApp & TActionInquiry & TActionPermissions & - TActionEnterpriseModules; + TActionEnterpriseModules & + TActionMediaDownload; diff --git a/app/lib/methods/handleMediaDownload.ts b/app/lib/methods/handleMediaDownload.ts index 0bef792af..17cbeb315 100644 --- a/app/lib/methods/handleMediaDownload.ts +++ b/app/lib/methods/handleMediaDownload.ts @@ -22,6 +22,8 @@ const defaultType = { [MediaTypes.video]: 'mp4' }; +export const LOCAL_DOCUMENT_PATH = `${FileSystem.documentDirectory}`; + const sanitizeString = (value: string) => sanitizeLikeString(value.substring(value.lastIndexOf('/') + 1)); const getExtension = (type: MediaTypes, mimeType?: string) => { @@ -57,7 +59,7 @@ export const searchMediaFileAsync = async ({ try { const serverUrl = store.getState().server.server; const serverUrlParsed = sanitizeString(serverUrl); - const folderPath = `${FileSystem.documentDirectory}${typeString[type]}${serverUrlParsed}`; + const folderPath = `${LOCAL_DOCUMENT_PATH}${typeString[type]}${serverUrlParsed}`; const filename = `${messageId}.${getExtension(type, mimeType)}`; filePath = `${folderPath}/${filename}`; await ensureDirAsync(folderPath); @@ -95,7 +97,7 @@ export const downloadMediaFile = async ({ export const deleteAllSpecificMediaFiles = async (type: MediaTypes, serverUrl: string): Promise => { try { const serverUrlParsed = sanitizeString(serverUrl); - const path = `${FileSystem.documentDirectory}${typeString[type]}${serverUrlParsed}`; + const path = `${LOCAL_DOCUMENT_PATH}${typeString[type]}${serverUrlParsed}`; await FileSystem.deleteAsync(path, { idempotent: true }); } catch (error) { log(error); diff --git a/app/reducers/index.js b/app/reducers/index.js index 7aad41aa0..e2210b447 100644 --- a/app/reducers/index.js +++ b/app/reducers/index.js @@ -21,6 +21,7 @@ import enterpriseModules from './enterpriseModules'; import encryption from './encryption'; import permissions from './permissions'; import roles from './roles'; +import mediaDownload from './mediaDownload'; export default combineReducers({ settings, @@ -43,5 +44,6 @@ export default combineReducers({ enterpriseModules, encryption, permissions, - roles + roles, + mediaDownload }); diff --git a/app/reducers/mediaDownload.test.ts b/app/reducers/mediaDownload.test.ts new file mode 100644 index 000000000..62e8c8048 --- /dev/null +++ b/app/reducers/mediaDownload.test.ts @@ -0,0 +1,41 @@ +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 new file mode 100644 index 000000000..3b59d69d2 --- /dev/null +++ b/app/reducers/mediaDownload.ts @@ -0,0 +1,26 @@ +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; + } +}