remove all the redux stuff and do the same as file upload
This commit is contained in:
parent
f136dfb03c
commit
95cbe25ae7
|
@ -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']);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
};
|
|
@ -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) {
|
||||
if (!newFile.video_url?.startsWith(LOCAL_DOCUMENT_PATH) && !loading) {
|
||||
// Keep the video downloading while showing the video buffering
|
||||
handleDownload();
|
||||
return showAttachment(file);
|
||||
}
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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')) {
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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 });
|
||||
});
|
||||
});
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
Loading…
Reference in New Issue