Last active
July 11, 2019 21:36
-
-
Save dereknelson/cfd249dea35014fef540f4203b96b6e3 to your computer and use it in GitHub Desktop.
Entirety of image caching
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import produce from 'immer' | |
import moment from 'moment' | |
import { DOWNLOADING, DOWNLOAD_IMAGE_SUCCESS, PURGE_IMAGE_CACHE } from './NotDumbImageActions'; | |
const initialState = { | |
loaded: {}, | |
downloading: [], | |
purgedCache: false | |
} | |
export const imgDownloadIsOld = (startedDownloading) => moment().diff(moment(startedDownloading), 'seconds') > 15 | |
export default (state = initialState, action) => { | |
switch (action.type){ | |
case DOWNLOADING: { | |
const downloading = produce(state.downloading, newList => { | |
newList.push(action.payload) | |
newList = newList.filter(img => !imgDownloadIsOld(img.startedDownloading)) | |
}) | |
return { ...state, downloading } | |
} | |
case DOWNLOAD_IMAGE_SUCCESS: { | |
let downloading = state.downloading.filter((img) => img.source != action.source) | |
let loaded = { ...state.loaded, [action.source]: action.local } | |
return { ...state, downloading, loaded } | |
} | |
case PURGE_IMAGE_CACHE: | |
return { ...initialState, purgedCache: true } | |
default: return state | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component, PureComponent } from 'react'; | |
import { connect } from 'react-redux'; | |
import { View, ActivityIndicator, Image, Platform } from 'react-native'; | |
import { FileSystem } from 'expo' | |
import md5 from 'js-md5'; | |
const ios = Platform.OS == 'ios' | |
import { downloadImage, removeFromDownloading } from './NotDumbImageActions'; | |
import { imgDownloadIsOld } from './cacheRedux'; | |
export const documentFolder = `${FileSystem.documentDirectory}yourDocumentFolder`; //documentDirectory ends in a / | |
/* in my root component App.js I have the following lines: | |
async componentWillMount(){ | |
let folderInfo = await FileSystem.getInfoAsync(documentFolder) | |
if (!folderInfo.exists) await FileSystem.makeDirectoryAsync(documentFolder) | |
} | |
*/ | |
const findDownloading = (downloading, uri) => downloading.filter(img => img.source == uri).length > 0 | |
@connect((state, props) => { | |
const { downloading, loaded } = state.cache, isDownloading = findDownloading(downloading, props.source.uri) | |
return { | |
isDownloading, | |
downloadingList: downloading, | |
local: loaded[props.source.uri], | |
downloadingThisMany: downloading.length | |
} | |
}, { downloadImage, removeFromDownloading }) | |
export default class NotDumbImage extends PureComponent { | |
constructor(props){ | |
super(props) | |
this.fetchImage = this.fetchImage.bind(this), this.cacheTimer = this.cacheTimer.bind(this), this.setFetched = this.setFetched.bind(this) | |
this.state = { fetched: false } | |
} | |
fetchImage() { | |
const { local, isDownloading, dontCache, downloadImage, source, downloadingList: dlList } = this.props | |
let index = dlList.findIndex(img => img.source == source.uri), shouldDownloadAnyway = false | |
if (index != -1) shouldDownloadAnyway = imgDownloadIsOld(dlList[index].startedDownloading) && dlList.filter(img => !imgDownloadIsOld(img.startedDownloading)).length == 0 | |
if ((!local && !isDownloading && !dontCache) || (!local && shouldDownloadAnyway)) downloadImage(source) | |
} | |
componentDidUpdate(prevProps) { | |
if (prevProps.source.uri != this.props.source.uri) this.fetchImage() | |
} | |
async componentDidMount(){ | |
this.fetchImage() | |
} | |
cacheTimer() { | |
this.timer = setTimeout(() => this.fetchImage(), 16000) | |
} | |
setFetched = () => this.setState({ fetched: true }) | |
render() { | |
const { local, style, source, resizeMode, list, dontCache } = this.props, { fetched } = this.state | |
const hash = md5(source.uri), split = source.uri.split('.'), type = split[split.length - 1], | |
file = dontCache ? source.uri || '' : `${documentFolder}/${hash}.${type}`; | |
if (!local || source && (typeof source.uri != 'string' || source.uri == '')) { | |
this.cacheTimer() | |
return ( | |
<View style={[style, { backgroundColor: 'gray', justifyContent: 'center' }]} > | |
<ActivityIndicator animating={true} size="small" /> | |
</View> | |
) | |
} | |
clearTimeout(this.timer) | |
//default source prevents flickering on setstate but fucks up the performance of flatlists for some reason | |
if (Platform.OS == 'ios' && !__DEV__ && !list) return <Image style={style} onLoadEnd={this.setFetched} resizeMode={resizeMode} defaultSource={{ uri: file }} /> | |
else return <Image style={style || defaultStyle} onLoad={this.setFetched} source={{ uri: file }} resizeMode={resizeMode} /> | |
} | |
} | |
const defaultStyle = { height: 50, width: 50, borderRadius: 25 } | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import md5 from 'js-md5'; | |
import { FileSystem } from 'expo' | |
import moment from 'moment' | |
export const DOWNLOADING = 'DOWNLOADING' | |
export const DOWNLOAD_IMAGE_SUCCESS = 'DOWNLOAD_IMAGE_SUCCESS' | |
export const PURGE_IMAGE_CACHE = 'PURGE_IMAGE_CACHE' | |
import { documentFolder } from './NotDumbImage'; | |
export function downloadImage(theSource){ | |
return async (dispatch, getState) => { | |
let source = theSource.uri | |
if (!source || source == '') return | |
dispatch(downloading(source)) | |
const hash = md5(source), split = source.split('.'), type = split[split.length - 1] | |
const output = `${documentFolder}/${hash}.${type}`; | |
const downloaded = await FileSystem.downloadAsync(source, output) | |
return dispatch(downloadImageSuccess(downloaded, source)) | |
} | |
} | |
function downloading(source){ | |
return { type: DOWNLOADING, payload: { source, startedDownloading: moment() } } | |
} | |
function downloadImageSuccess(downloaded, source){ | |
return { type: DOWNLOAD_IMAGE_SUCCESS, local: downloaded.uri, source } | |
} | |
export function purgeCache(){ | |
return { type: PURGE_IMAGE_CACHE } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment