diff --git a/package.json b/package.json index e3cd54c91..7a8454525 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/components/index.js b/src/components/index.js index a46c84ecd..4c778930d 100644 --- a/src/components/index.js +++ b/src/components/index.js @@ -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'; diff --git a/src/components/postCard/view/postCardView.js b/src/components/postCard/view/postCardView.js index dac027623..3c0140e78 100644 --- a/src/components/postCard/view/postCardView.js +++ b/src/components/postCard/view/postCardView.js @@ -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 = ({ {!isHideImage && ( - - setCalcImgHeight((evt.nativeEvent.height / evt.nativeEvent.width) * width) - } /> )} diff --git a/src/components/postListItem/view/postListItemView.js b/src/components/postListItem/view/postListItemView.js index 36c8ff8df..08d8d964f 100644 --- a/src/components/postListItem/view/postListItemView.js +++ b/src/components/postListItem/view/postListItemView.js @@ -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 = ({ handleOnPressItem(id)}> - - setCalcImgHeight((evt.nativeEvent.height / evt.nativeEvent.width) * width) - } /> {title} diff --git a/src/components/progressiveImage/index.js b/src/components/progressiveImage/index.js new file mode 100644 index 000000000..734fc9afa --- /dev/null +++ b/src/components/progressiveImage/index.js @@ -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 ( + + + + + ); + } +} + +export default ProgressiveImage; diff --git a/src/screens/drafts/screen/draftsScreen.js b/src/screens/drafts/screen/draftsScreen.js index 247b38a24..4c2ba126d 100644 --- a/src/screens/drafts/screen/draftsScreen.js +++ b/src/screens/drafts/screen/draftsScreen.js @@ -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))} diff --git a/src/utils/image.js b/src/utils/image.js index a71b1d7b7..a9f91540a 100644 --- a/src/utils/image.js +++ b/src/utils/image.js @@ -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; }; diff --git a/src/utils/postParser.js b/src/utils/postParser.js index e633cce4f..6d7f8ca22 100644 --- a/src/utils/postParser.js +++ b/src/utils/postParser.js @@ -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')); diff --git a/yarn.lock b/yarn.lock index 6d3da8f86..ac93de96e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"