Files
mattermost-mobile/app/utils/image_cache_manager.js
Miguel Alatzar ee4b85edcf [MM-23520] Port mattermost-redux (#4088)
* Remove mattermost-redux

* Move mm-redux files into app/redux

* Add @redux path to tsconfig.json

* Fix imports

* Install missing dependencies

* Fix tsc errors

* Fix i18n_utils test

* Fix more imports

* Remove redux websocket

* Fix tests

* Rename @redux

* Apply changes from mattermost-redux PR 1103

* Remove mattermost-redux mention in template

* Add missing imports

* Rename app/redux/ to app/mm-redux/

* Remove test file

* Fix fetching Sidebar GM profiles

Co-authored-by: Elias Nahum <nahumhbl@gmail.com>
2020-03-31 11:09:26 -07:00

177 lines
5.6 KiB
JavaScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// Based on the work done by https://github.com/wcandillon/react-native-expo-image-cache/
import {Platform} from 'react-native';
import RNFetchBlob from 'rn-fetch-blob';
import Url from 'url-parse';
import {Client4} from '@mm-redux/client';
import {DeviceTypes} from 'app/constants';
import {
getExtensionFromMime,
getExtensionFromContentDisposition,
} from 'app/utils/file';
import mattermostBucket from 'app/mattermost_bucket';
const {IMAGES_PATH} = DeviceTypes;
const DEFAULT_MIME_TYPE = 'image/png';
let siteUrl;
export default class ImageCacheManager {
static listeners = {};
static cache = async (filename, fileUri, listener) => {
if (!listener) {
console.warn('Unable to cache image when no listener is provided'); // eslint-disable-line no-console
}
const uri = parseUri(fileUri);
const {path, exists} = await getCacheFile(filename, uri);
const prefix = Platform.OS === 'android' ? 'file://' : '';
let pathWithPrefix = `${prefix}${path}`;
if (exports.isDownloading(uri)) {
addListener(uri, listener);
} else if (exists) {
listener(pathWithPrefix);
} else {
addListener(uri, listener);
if (uri.startsWith('http')) {
try {
const certificate = await mattermostBucket.getPreference('cert');
const options = {
session: uri,
timeout: 10000,
indicator: true,
overwrite: true,
path,
certificate,
};
const headers = {};
if (uri.includes(Client4.getUrl()) || uri.includes(siteUrl)) {
headers.Authorization = `Bearer ${Client4.getToken()}`;
headers['X-Requested-With'] = 'XMLHttpRequest';
}
this.downloadTask = await RNFetchBlob.config(options).fetch('GET', uri, headers);
if (this.downloadTask.respInfo.respType === 'text') {
throw new Error();
}
const contentDisposition = this.downloadTask.respInfo.headers['Content-Disposition'];
const mimeType = this.downloadTask.respInfo.headers['Content-Type'];
const ext = `.${
getExtensionFromContentDisposition(contentDisposition) ||
getExtensionFromMime(mimeType) ||
getExtensionFromMime(DEFAULT_MIME_TYPE)
}`;
if (!path.endsWith(ext)) {
const oldExt = path.substring(path.lastIndexOf('.'));
const newPath = path.replace(oldExt, ext);
await RNFetchBlob.fs.mv(path, newPath);
pathWithPrefix = `${prefix}${newPath}`;
}
notifyAll(uri, pathWithPrefix);
} catch (e) {
RNFetchBlob.fs.unlink(pathWithPrefix);
notifyAll(uri, null);
unsubscribe(uri);
return null;
}
} else {
// In case the uri we are trying to cache is already a local file just notify and return
notifyAll(uri, uri);
}
unsubscribe(uri);
}
return pathWithPrefix;
};
}
const parseUri = (uri) => {
const url = new Url(uri);
return url.href;
};
export const getCacheFile = async (name, uri) => {
const filename = name || uri.substring(uri.lastIndexOf('/'), uri.indexOf('?') === -1 ? uri.length : uri.indexOf('?'));
const defaultExt = `.${getExtensionFromMime(DEFAULT_MIME_TYPE)}`;
const ext = filename.indexOf('.') === -1 ? defaultExt : filename.substring(filename.lastIndexOf('.'));
let path = `${IMAGES_PATH}/${Math.abs(hashCode(uri))}${ext}`;
try {
const isDir = await RNFetchBlob.fs.isDir(IMAGES_PATH);
if (!isDir) {
await RNFetchBlob.fs.mkdir(IMAGES_PATH);
}
} catch (error) {
// do nothing
}
let exists = await RNFetchBlob.fs.exists(path);
if (!exists) {
const pathWithDiffExt = await RNFetchBlob.fs.existsWithDiffExt(path);
if (pathWithDiffExt) {
exists = true;
path = pathWithDiffExt;
}
}
return {exists, path};
};
export const getSiteUrl = () => {
return siteUrl;
};
export const setSiteUrl = (url) => {
siteUrl = url;
};
export const isDownloading = (uri) => Boolean(ImageCacheManager.listeners[uri]);
const addListener = (uri, listener) => {
if (!ImageCacheManager.listeners[uri]) {
ImageCacheManager.listeners[uri] = [];
}
ImageCacheManager.listeners[uri].push(listener);
};
const unsubscribe = (uri) => Reflect.deleteProperty(ImageCacheManager.listeners, uri);
const notifyAll = (uri, path) => {
ImageCacheManager.listeners[uri].forEach((listener) => {
if (typeof listener === 'function') {
listener(path);
}
});
};
// taken from https://stackoverflow.com/questions/7616461/generate-a-hash-from-string-in-javascript-jquery
export const hashCode = (str) => {
let hash = 0;
let i;
let chr;
if (!str || str.length === 0) {
return hash;
}
for (i = 0; i < str.length; i++) {
chr = str.charCodeAt(i);
hash = ((hash << 5) - hash) + chr;
hash |= 0; // Convert to 32bit integer
}
return hash;
};