mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-11-22 23:28:56 +03:00
add progressive thumbnail loading
This commit is contained in:
parent
245e6bd9ca
commit
6066c58d43
@ -75,6 +75,7 @@
|
||||
"react-native-gesture-handler": "^1.4.1",
|
||||
"react-native-iap": "3.4.15",
|
||||
"react-native-image-crop-picker": "^0.26.1",
|
||||
"react-native-image-size": "^1.1.3",
|
||||
"react-native-image-zoom-viewer": "^2.2.27",
|
||||
"react-native-keyboard-aware-scroll-view": "^0.9.1",
|
||||
"react-native-linear-gradient": "^2.4.2",
|
||||
|
@ -33,6 +33,7 @@ import { PostForm } from './postForm';
|
||||
import { PostHeaderDescription, PostBody, Tags } from './postElements';
|
||||
import { PostListItem } from './postListItem';
|
||||
import { ProfileSummary } from './profileSummary';
|
||||
import { ProgressiveImage } from './progressiveImage';
|
||||
|
||||
import { SearchInput } from './searchInput';
|
||||
import { SearchModal } from './searchModal';
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { Component, useState, useEffect } from 'react';
|
||||
import get from 'lodash/get';
|
||||
import { TouchableOpacity, Text, View, Dimensions } from 'react-native';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import ImageSize from 'react-native-image-size';
|
||||
|
||||
// Utils
|
||||
import { getTimeFromNow } from '../../../utils/time';
|
||||
@ -19,10 +19,13 @@ import { Upvote } from '../../upvote';
|
||||
import styles from './postCardStyles';
|
||||
|
||||
// Defaults
|
||||
import DEFAULT_IMAGE from '../../../assets/no_image.png';
|
||||
import NSFW_IMAGE from '../../../assets/nsfw.png';
|
||||
import ProgressiveImage from '../../progressiveImage';
|
||||
|
||||
const { width, height } = Dimensions.get('window');
|
||||
const dim = Dimensions.get('window');
|
||||
const DEFAULT_IMAGE =
|
||||
'https://images.ecency.com/DQmT8R33geccEjJfzZEdsRHpP3VE8pu3peRCnQa1qukU4KR/no_image_3x.png';
|
||||
const NSFW_IMAGE =
|
||||
'https://images.ecency.com/DQmZ1jW4p7o5GyoqWyCib1fSLE2ftbewsMCt2GvbmT9kmoY/nsfw_3x.png';
|
||||
|
||||
const PostCardView = ({
|
||||
handleOnUserPress,
|
||||
@ -39,7 +42,7 @@ const PostCardView = ({
|
||||
}) => {
|
||||
const [rebloggedBy, setRebloggedBy] = useState(get(content, 'reblogged_by[0]', null));
|
||||
const [activeVot, setActiveVot] = useState(activeVotes);
|
||||
const [calcImgHeight, setCalcImgHeight] = useState(0);
|
||||
const [calcImgHeight, setCalcImgHeight] = useState(300);
|
||||
//console.log(activeVotes);
|
||||
// Component Functions
|
||||
|
||||
@ -66,11 +69,16 @@ const PostCardView = ({
|
||||
const _getPostImage = (content, isNsfwPost) => {
|
||||
if (content && content.image) {
|
||||
if (isNsfwPost && content.nsfw) {
|
||||
return NSFW_IMAGE;
|
||||
return { image: NSFW_IMAGE, thumbnail: NSFW_IMAGE };
|
||||
}
|
||||
return { uri: content.image, priority: FastImage.priority.high };
|
||||
//console.log(content)
|
||||
ImageSize.getSize(content.image).then((size) => {
|
||||
setCalcImgHeight((size.height / size.width) * dim.width);
|
||||
});
|
||||
return { image: content.image, thumbnail: content.thumbnail };
|
||||
} else {
|
||||
return { image: DEFAULT_IMAGE, thumbnail: DEFAULT_IMAGE };
|
||||
}
|
||||
return DEFAULT_IMAGE;
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
@ -106,17 +114,13 @@ const PostCardView = ({
|
||||
<View style={styles.postBodyWrapper}>
|
||||
<TouchableOpacity style={styles.hiddenImages} onPress={_handleOnContentPress}>
|
||||
{!isHideImage && (
|
||||
<FastImage
|
||||
<ProgressiveImage
|
||||
source={{ uri: _image.image }}
|
||||
thumbnailSource={{ uri: _image.thumbnail }}
|
||||
style={[
|
||||
styles.thumbnail,
|
||||
{ width: scalePx(width - 16), height: scalePx(Math.min(calcImgHeight, height)) },
|
||||
{ width: scalePx(dim.width - 16), height: Math.min(calcImgHeight, dim.height) },
|
||||
]}
|
||||
source={_image}
|
||||
resizeMode={FastImage.resizeMode.cover}
|
||||
defaultSource={DEFAULT_IMAGE}
|
||||
onLoad={(evt) =>
|
||||
setCalcImgHeight((evt.nativeEvent.height / evt.nativeEvent.width) * width)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
<View style={[styles.postDescripton]}>
|
||||
|
@ -1,8 +1,8 @@
|
||||
import React, { useRef, useState, Fragment } from 'react';
|
||||
import React, { useRef, useState, useEffect, Fragment } from 'react';
|
||||
import ActionSheet from 'react-native-actionsheet';
|
||||
import { View, Text, TouchableOpacity, Dimensions } from 'react-native';
|
||||
import { injectIntl } from 'react-intl';
|
||||
import FastImage from 'react-native-fast-image';
|
||||
import ImageSize from 'react-native-image-size';
|
||||
|
||||
// Utils
|
||||
import { getTimeFromNow } from '../../../utils/time';
|
||||
@ -11,14 +11,16 @@ import scalePx from '../../../utils/scalePx';
|
||||
// Components
|
||||
import { PostHeaderDescription } from '../../postElements';
|
||||
import { IconButton } from '../../iconButton';
|
||||
|
||||
// Defaults
|
||||
import DEFAULT_IMAGE from '../../../assets/no_image.png';
|
||||
import ProgressiveImage from '../../progressiveImage';
|
||||
|
||||
// Styles
|
||||
import styles from './postListItemStyles';
|
||||
|
||||
const { width, height } = Dimensions.get('window');
|
||||
// Defaults
|
||||
const DEFAULT_IMAGE =
|
||||
'https://images.ecency.com/DQmT8R33geccEjJfzZEdsRHpP3VE8pu3peRCnQa1qukU4KR/no_image_3x.png';
|
||||
|
||||
const dim = Dimensions.get('window');
|
||||
|
||||
const PostListItemView = ({
|
||||
title,
|
||||
@ -28,6 +30,7 @@ const PostListItemView = ({
|
||||
reputation,
|
||||
created,
|
||||
image,
|
||||
thumbnail,
|
||||
handleOnPressItem,
|
||||
handleOnRemoveItem,
|
||||
id,
|
||||
@ -35,9 +38,15 @@ const PostListItemView = ({
|
||||
isFormatedDate,
|
||||
}) => {
|
||||
const actionSheet = useRef(null);
|
||||
const [calcImgHeight, setCalcImgHeight] = useState(0);
|
||||
const [calcImgHeight, setCalcImgHeight] = useState(300);
|
||||
// Component Life Cycles
|
||||
|
||||
useEffect(() => {
|
||||
if (image) {
|
||||
ImageSize.getSize(image.uri).then((size) => {
|
||||
setCalcImgHeight((size.height / size.width) * dim.width);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
// Component Functions
|
||||
|
||||
return (
|
||||
@ -63,16 +72,13 @@ const PostListItemView = ({
|
||||
</View>
|
||||
<View style={styles.body}>
|
||||
<TouchableOpacity onPress={() => handleOnPressItem(id)}>
|
||||
<FastImage
|
||||
<ProgressiveImage
|
||||
source={image}
|
||||
thumbnailSource={thumbnail}
|
||||
style={[
|
||||
styles.image,
|
||||
{ width: scalePx(width - 16), height: scalePx(Math.min(calcImgHeight, height)) },
|
||||
styles.thumbnail,
|
||||
{ width: scalePx(dim.width - 16), height: Math.min(calcImgHeight, dim.height) },
|
||||
]}
|
||||
defaultSource={DEFAULT_IMAGE}
|
||||
onLoad={(evt) =>
|
||||
setCalcImgHeight((evt.nativeEvent.height / evt.nativeEvent.width) * width)
|
||||
}
|
||||
/>
|
||||
<View style={[styles.postDescripton]}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
|
57
src/components/progressiveImage/index.js
Normal file
57
src/components/progressiveImage/index.js
Normal file
@ -0,0 +1,57 @@
|
||||
import React from 'react';
|
||||
import { View, StyleSheet, Animated } from 'react-native';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
imageOverlay: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
container: {
|
||||
backgroundColor: '#f6f6f6',
|
||||
},
|
||||
});
|
||||
|
||||
class ProgressiveImage extends React.Component {
|
||||
thumbnailAnimated = new Animated.Value(0);
|
||||
|
||||
imageAnimated = new Animated.Value(0);
|
||||
|
||||
handleThumbnailLoad = () => {
|
||||
Animated.timing(this.thumbnailAnimated, {
|
||||
toValue: 1,
|
||||
}).start();
|
||||
};
|
||||
|
||||
onImageLoad = () => {
|
||||
Animated.timing(this.imageAnimated, {
|
||||
toValue: 1,
|
||||
}).start();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { thumbnailSource, source, style, ...props } = this.props;
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Animated.Image
|
||||
{...props}
|
||||
source={thumbnailSource}
|
||||
style={[style, { opacity: this.thumbnailAnimated }]}
|
||||
onLoad={this.handleThumbnailLoad}
|
||||
blurRadius={1}
|
||||
/>
|
||||
<Animated.Image
|
||||
{...props}
|
||||
source={source}
|
||||
style={[styles.imageOverlay, { opacity: this.imageAnimated }, style]}
|
||||
onLoad={this.onImageLoad}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ProgressiveImage;
|
@ -47,6 +47,7 @@ const DraftsScreen = ({
|
||||
const tags = item.tags ? item.tags.split(/[ ,]+/) : [];
|
||||
const tag = tags[0] || '';
|
||||
const image = catchDraftImage(item.body);
|
||||
const thumbnail = catchDraftImage(item.body, 'match', true);
|
||||
const summary = postBodySummary({ item, last_update: item.created }, 100);
|
||||
const isSchedules = type === 'schedules';
|
||||
|
||||
@ -57,7 +58,8 @@ const DraftsScreen = ({
|
||||
title={item.title}
|
||||
summary={summary}
|
||||
isFormatedDate={isSchedules}
|
||||
image={image ? { uri: catchDraftImage(item.body) } : null}
|
||||
image={image ? { uri: image } : null}
|
||||
thumbnail={thumbnail ? { uri: thumbnail } : null}
|
||||
username={currentAccount.name}
|
||||
reputation={currentAccount.reputation}
|
||||
handleOnPressItem={() => (isSchedules ? setSelectedId(item._id) : editDraft(item._id))}
|
||||
|
@ -63,14 +63,16 @@ export const catchEntryImage = (entry, width = 0, height = 0, format = 'match')
|
||||
return null;
|
||||
};
|
||||
|
||||
export const catchDraftImage = (body, format = 'match') => {
|
||||
export const catchDraftImage = (body, format = 'match', thumbnail = false) => {
|
||||
const imgRegex = /(https?:\/\/.*\.(?:tiff?|jpe?g|gif|png|svg|ico|PNG|GIF|JPG))/g;
|
||||
format = whatOs === 'android' ? 'webp' : 'match';
|
||||
|
||||
if (body && imgRegex.test(body)) {
|
||||
const imageMatch = body.match(imgRegex);
|
||||
|
||||
return proxifyImageSrc(imageMatch[0], 0, 0, format);
|
||||
if (thumbnail) {
|
||||
return proxifyImageSrc(imageMatch[0], 60, 50, format);
|
||||
}
|
||||
return proxifyImageSrc(imageMatch[0], 600, 500, format);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
@ -33,6 +33,7 @@ export const parsePost = (post, currentUserName, isPromoted) => {
|
||||
post.json_metadata = {};
|
||||
}
|
||||
post.image = catchPostImage(post.body, 600, 500, webp ? 'webp' : 'match');
|
||||
post.thumbnail = catchPostImage(post.body, 60, 50, webp ? 'webp' : 'match');
|
||||
post.author_reputation = getReputation(post.author_reputation);
|
||||
post.avatar = getResizedAvatar(get(post, 'author'));
|
||||
|
||||
|
@ -7538,6 +7538,11 @@ react-native-image-pan-zoom@^2.1.9:
|
||||
resolved "https://registry.yarnpkg.com/react-native-image-pan-zoom/-/react-native-image-pan-zoom-2.1.12.tgz#eb98bf56fb5610379bdbfdb63219cc1baca98fd2"
|
||||
integrity sha512-BF66XeP6dzuANsPmmFsJshM2Jyh/Mo1t8FsGc1L9Q9/sVP8MJULDabB1hms+eAoqgtyhMr5BuXV3E1hJ5U5H6Q==
|
||||
|
||||
react-native-image-size@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/react-native-image-size/-/react-native-image-size-1.1.3.tgz#7d69c2cd4e1d1632947867e47643ed8cabb9de27"
|
||||
integrity sha512-jJvN6CjXVAm69LAVZNV7m7r50Qk9vuPZwLyrbs/k31/3Xs8bZyVCdvfP44FuBisITn/yFsiOo6i8NPrFBPH20w==
|
||||
|
||||
react-native-image-zoom-viewer@^2.2.27:
|
||||
version "2.2.27"
|
||||
resolved "https://registry.yarnpkg.com/react-native-image-zoom-viewer/-/react-native-image-zoom-viewer-2.2.27.tgz#fb3314c5dc86ac33da48cb31bf4920d97eecb6eb"
|
||||
|
Loading…
Reference in New Issue
Block a user