remove all the redux stuff and do the same as file upload

This commit is contained in:
Reinaldo Neto 2023-05-19 01:18:57 -03:00
parent f136dfb03c
commit 95cbe25ae7
10 changed files with 107 additions and 219 deletions

View File

@ -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']);

View File

@ -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
};
};

View File

@ -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<FileSystem.DownloadResumable | null>(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);
}
};

View File

@ -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;

View File

@ -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<FetchBlobResponse> } = {};
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<void> {
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<string | null> {
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<void> => {
try {
const serverUrlParsed = sanitizeString(serverUrl);

View File

@ -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')) {

View File

@ -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
});

View File

@ -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 });
});
});

View File

@ -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;
}
}

View File

@ -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
);