mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-22 21:01:31 +03:00
Merge branch 'development' into sa/quick-reply-on-post
This commit is contained in:
commit
47a10ede92
@ -772,4 +772,4 @@ SPEC CHECKSUMS:
|
||||
|
||||
PODFILE CHECKSUM: 9c48318ea254e2c78005a7a0c2d8bfc14ddd783d
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
COCOAPODS: 1.10.1
|
||||
|
@ -29,7 +29,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"@bugsnag/react-native": "^7.11.0",
|
||||
"@ecency/render-helper": "^2.2.9",
|
||||
"@ecency/render-helper": "^2.2.12",
|
||||
"@esteemapp/dhive": "0.15.0",
|
||||
"@esteemapp/react-native-autocomplete-input": "^4.2.1",
|
||||
"@esteemapp/react-native-multi-slider": "^1.1.0",
|
||||
|
@ -47,7 +47,7 @@ import { TouchableWithoutFeedback } from "react-native-gesture-handler";
|
||||
}
|
||||
|
||||
return (
|
||||
<TouchableWithoutFeedback onPress={onPress} disabled={!isAnchored}>
|
||||
<TouchableWithoutFeedback onPress={onPress} disabled={isAnchored}>
|
||||
<FastImage
|
||||
style={imgStyle}
|
||||
source={{uri:imgUrl}}
|
||||
|
@ -45,13 +45,10 @@ const PostCardView = ({
|
||||
isMuted,
|
||||
}) => {
|
||||
//local state to manage fake upvote if available
|
||||
const [activeVotesCount, setActiveVotesCount] = useState(0);
|
||||
const activeVotesCount = activeVotes ? activeVotes.length : 0;
|
||||
const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0);
|
||||
const [calcImgHeight, setCalcImgHeight] = useState(imageHeight || 300);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveVotesCount(activeVotes ? activeVotes.length : 0);
|
||||
}, [activeVotes]);
|
||||
|
||||
// Component Functions
|
||||
const _handleOnUserPress = (username) => {
|
||||
if (handleOnUserPress) {
|
||||
@ -76,7 +73,7 @@ const PostCardView = ({
|
||||
|
||||
const _handleIncrementVoteCount = () => {
|
||||
//fake increment vote using based on local change
|
||||
setActiveVotesCount(activeVotesCount + 1);
|
||||
setCacheVoteIcrement(1);
|
||||
};
|
||||
|
||||
const rebloggedBy = get(content, 'reblogged_by[0]', null);
|
||||
@ -183,7 +180,7 @@ const PostCardView = ({
|
||||
iconStyle={styles.commentIcon}
|
||||
iconType="MaterialCommunityIcons"
|
||||
isClickable
|
||||
text={activeVotesCount}
|
||||
text={activeVotesCount + cacheVoteIcrement}
|
||||
onPress={_handleOnVotersPress}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
|
@ -21,7 +21,6 @@ import styles from './commentBodyStyles';
|
||||
// Services and Actions
|
||||
import { writeToClipboard } from '../../../../utils/clipboard';
|
||||
import { toastNotification } from '../../../../redux/actions/uiAction';
|
||||
import getYoutubeId from '../../../../utils/getYoutubeId';
|
||||
import VideoPlayerSheet from './videoPlayerSheet';
|
||||
import { LongPressGestureHandler, State } from 'react-native-gesture-handler';
|
||||
import { useCallback } from 'react';
|
||||
@ -29,6 +28,7 @@ import { OptionsModal } from '../../../atoms';
|
||||
import { useAppDispatch } from '../../../../hooks';
|
||||
import { isCommunity } from '../../../../utils/communityValidation';
|
||||
import { GLOBAL_POST_FILTERS_VALUE } from '../../../../constants/options/filters';
|
||||
import { startsWith } from 'core-js/core/string';
|
||||
|
||||
const WIDTH = Dimensions.get('window').width;
|
||||
|
||||
@ -54,6 +54,7 @@ const CommentBody = ({
|
||||
const [revealComment, setRevealComment] = useState(reputation > 0 && !isMuted);
|
||||
const [videoUrl, setVideoUrl] = useState(null);
|
||||
const [youtubeVideoId, setYoutubeVideoId] = useState(null)
|
||||
const [videoStartTime, setVideoStartTime] = useState(0);
|
||||
|
||||
const intl = useIntl();
|
||||
const actionImage = useRef(null);
|
||||
@ -265,10 +266,10 @@ const CommentBody = ({
|
||||
}
|
||||
};
|
||||
|
||||
const _handleYoutubePress = (embedUrl) => {
|
||||
const videoId = getYoutubeId(embedUrl);
|
||||
const _handleYoutubePress = (videoId, startTime) => {
|
||||
if (videoId && youtubePlayerRef.current) {
|
||||
setYoutubeVideoId(videoId);
|
||||
setVideoStartTime(startTime);
|
||||
youtubePlayerRef.current.setModalVisible(true);
|
||||
}
|
||||
};
|
||||
@ -276,6 +277,7 @@ const CommentBody = ({
|
||||
const _handleVideoPress = (embedUrl) => {
|
||||
if (embedUrl && youtubePlayerRef.current) {
|
||||
setVideoUrl(embedUrl);
|
||||
setVideoStartTime(0)
|
||||
youtubePlayerRef.current.setModalVisible(true);
|
||||
}
|
||||
};
|
||||
@ -366,7 +368,7 @@ const CommentBody = ({
|
||||
setVideoUrl(null);
|
||||
}}
|
||||
>
|
||||
<VideoPlayerSheet youtubeVideoId={youtubeVideoId} videoUrl={videoUrl} />
|
||||
<VideoPlayerSheet youtubeVideoId={youtubeVideoId} videoUrl={videoUrl} startTime={videoStartTime} />
|
||||
</ActionsSheetView>
|
||||
</Fragment>
|
||||
);
|
||||
|
@ -15,7 +15,6 @@ import { toastNotification } from '../../../../redux/actions/uiAction';
|
||||
|
||||
// Constants
|
||||
import { default as ROUTES } from '../../../../constants/routeNames';
|
||||
import getYoutubeId from '../../../../utils/getYoutubeId';
|
||||
import VideoPlayerSheet from './videoPlayerSheet';
|
||||
import { OptionsModal } from '../../../atoms';
|
||||
import { isCommunity } from '../../../../utils/communityValidation';
|
||||
@ -33,6 +32,7 @@ const PostBody = ({ navigation, body, dispatch, onLoadEnd }) => {
|
||||
const [html, setHtml] = useState('');
|
||||
const [youtubeVideoId, setYoutubeVideoId] = useState(null);
|
||||
const [videoUrl, setVideoUrl] = useState(null);
|
||||
const [videoStartTime, setVideoStartTime] = useState(0);
|
||||
|
||||
const intl = useIntl();
|
||||
const actionImage = useRef(null);
|
||||
@ -45,10 +45,10 @@ const PostBody = ({ navigation, body, dispatch, onLoadEnd }) => {
|
||||
}
|
||||
}, [body]);
|
||||
|
||||
const _handleYoutubePress = (embedUrl) => {
|
||||
const videoId = getYoutubeId(embedUrl);
|
||||
const _handleYoutubePress = (videoId, startTime) => {
|
||||
if (videoId && youtubePlayerRef.current) {
|
||||
setYoutubeVideoId(videoId);
|
||||
setVideoStartTime(startTime);
|
||||
youtubePlayerRef.current.setModalVisible(true);
|
||||
}
|
||||
};
|
||||
@ -56,6 +56,7 @@ const PostBody = ({ navigation, body, dispatch, onLoadEnd }) => {
|
||||
const _handleVideoPress = (embedUrl) => {
|
||||
if (embedUrl && youtubePlayerRef.current) {
|
||||
setVideoUrl(embedUrl);
|
||||
setVideoStartTime(0);
|
||||
youtubePlayerRef.current.setModalVisible(true);
|
||||
}
|
||||
};
|
||||
@ -293,7 +294,11 @@ const PostBody = ({ navigation, body, dispatch, onLoadEnd }) => {
|
||||
setVideoUrl(null);
|
||||
}}
|
||||
>
|
||||
<VideoPlayerSheet youtubeVideoId={youtubeVideoId} videoUrl={videoUrl} />
|
||||
<VideoPlayerSheet
|
||||
youtubeVideoId={youtubeVideoId}
|
||||
videoUrl={videoUrl}
|
||||
startTime={videoStartTime}
|
||||
/>
|
||||
</ActionSheetView>
|
||||
|
||||
<OptionsModal
|
||||
|
@ -1,16 +1,16 @@
|
||||
import React, {useState} from 'react';
|
||||
import { Dimensions } from 'react-native';
|
||||
import { View, StyleSheet, ActivityIndicator } from 'react-native';
|
||||
import AutoHeightWebView from 'react-native-autoheight-webview';
|
||||
import WebView from 'react-native-webview';
|
||||
import YoutubeIframe from 'react-native-youtube-iframe';
|
||||
import YoutubeIframe, { InitialPlayerParams } from 'react-native-youtube-iframe';
|
||||
|
||||
interface VideoPlayerSheetProps {
|
||||
youtubeVideoId?:string;
|
||||
videoUrl?:string;
|
||||
startTime?:number;
|
||||
}
|
||||
|
||||
const VideoPlayerSheet = ({youtubeVideoId, videoUrl}: VideoPlayerSheetProps) => {
|
||||
const VideoPlayerSheet = ({youtubeVideoId, videoUrl, startTime}: VideoPlayerSheetProps) => {
|
||||
|
||||
const PLAYER_HEIGHT = Dimensions.get('screen').width * (9/16);
|
||||
|
||||
@ -30,6 +30,10 @@ const VideoPlayerSheet = ({youtubeVideoId, videoUrl}: VideoPlayerSheetProps) =>
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
const initialParams:InitialPlayerParams = {
|
||||
start:startTime
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
|
||||
@ -37,6 +41,7 @@ const VideoPlayerSheet = ({youtubeVideoId, videoUrl}: VideoPlayerSheetProps) =>
|
||||
<YoutubeIframe
|
||||
height={PLAYER_HEIGHT}
|
||||
videoId={youtubeVideoId}
|
||||
initialPlayerParams={initialParams}
|
||||
onReady={_onReady}
|
||||
play={shouldPlay}
|
||||
onChangeState={_onChangeState}
|
||||
|
@ -8,7 +8,10 @@ export interface LinkData {
|
||||
tag?:string,
|
||||
proposal?:string,
|
||||
videoHref?:string,
|
||||
youtubeId?:string,
|
||||
startTime?:number,
|
||||
filter?:string,
|
||||
community?:string,
|
||||
}
|
||||
|
||||
export const parseLinkData = (tnode:TNode):LinkData => {
|
||||
@ -17,10 +20,27 @@ export const parseLinkData = (tnode:TNode):LinkData => {
|
||||
}
|
||||
|
||||
if(tnode.classes.includes('markdown-external-link')){
|
||||
|
||||
//inline external links can contain video links, for such tags and video id or url is contained as
|
||||
//attribute if that is the case, use in app video modal to play content
|
||||
//for now, only youtube id is supported
|
||||
const youtubeId = tnode.attributes['data-youtube']
|
||||
const startTime= tnode.attributes['data-start-time'];
|
||||
if(youtubeId){
|
||||
|
||||
return {
|
||||
type:'markdown-external-link',
|
||||
href: tnode.attributes['data-href']
|
||||
type:'markdown-video-link-youtube',
|
||||
youtubeId:youtubeId,
|
||||
startTime:parseInt(startTime) || 0,
|
||||
}
|
||||
}
|
||||
//TOOD: support other video link later
|
||||
|
||||
//use default markdown-external-link with url;
|
||||
return {
|
||||
type:'markdown-external-link',
|
||||
href: tnode.attributes['data-href']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -90,15 +110,23 @@ export const parseLinkData = (tnode:TNode):LinkData => {
|
||||
|
||||
}
|
||||
|
||||
if(tnode.classes.includes('markdown-community-link')){
|
||||
return {
|
||||
type: 'markdown-community-link',
|
||||
community: tnode.attributes['data-community'],
|
||||
filter: tnode.attributes['data-filter'],
|
||||
}
|
||||
}
|
||||
|
||||
if (tnode.classes.includes('markdown-video-link-youtube')) {
|
||||
var embedUrl = tnode.attributes['data-embed-src'];
|
||||
var youtubeId = tnode.attributes['data-youtube'];
|
||||
const startTime= tnode.attributes['data-start-time'];
|
||||
|
||||
|
||||
|
||||
if (embedUrl) {
|
||||
if (youtubeId) {
|
||||
return {
|
||||
type: 'markdown-video-link-youtube',
|
||||
tag: embedUrl
|
||||
youtubeId : youtubeId,
|
||||
startTime : parseInt(startTime) || 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { AutoHeightImage } from "../autoHeightImage/autoHeightImage";
|
||||
interface PostHtmlRendererProps {
|
||||
contentWidth:number;
|
||||
body:string;
|
||||
onLoaded:()=>void;
|
||||
onLoaded?:()=>void;
|
||||
setSelectedImage:(imgUrl:string)=>void;
|
||||
setSelectedLink:(url:string)=>void;
|
||||
onElementIsImage:(imgUrl:string)=>void;
|
||||
@ -17,7 +17,7 @@ interface PostHtmlRendererProps {
|
||||
handleOnUserPress:(username:string)=>void;
|
||||
handleTagPress:(tag:string, filter?:string)=>void;
|
||||
handleVideoPress:(videoUrl:string)=>void;
|
||||
handleYoutubePress:(videoId:string)=>void;
|
||||
handleYoutubePress:(videoId:string, startTime:number)=>void;
|
||||
}
|
||||
|
||||
export const PostHtmlRenderer = memo(({
|
||||
@ -37,6 +37,7 @@ export const PostHtmlRenderer = memo(({
|
||||
//new renderer functions
|
||||
body = body.replace(/<center>/g, '<div class="text-center">').replace(/<\/center>/g,'</div>');
|
||||
|
||||
|
||||
console.log("Comment body:", body);
|
||||
|
||||
const _handleOnLinkPress = (data:LinkData) => {
|
||||
@ -51,8 +52,11 @@ export const PostHtmlRenderer = memo(({
|
||||
author,
|
||||
permlink,
|
||||
tag,
|
||||
youtubeId,
|
||||
startTime,
|
||||
filter,
|
||||
videoHref,
|
||||
community
|
||||
} = data;
|
||||
|
||||
try {
|
||||
@ -85,7 +89,7 @@ export const PostHtmlRenderer = memo(({
|
||||
break;
|
||||
case 'markdown-video-link-youtube':
|
||||
if(handleYoutubePress){
|
||||
handleYoutubePress(tag)
|
||||
handleYoutubePress(youtubeId, startTime)
|
||||
}
|
||||
|
||||
break;
|
||||
@ -98,6 +102,13 @@ export const PostHtmlRenderer = memo(({
|
||||
case 'markdown-proposal-link':
|
||||
setSelectedLink(href);
|
||||
break;
|
||||
|
||||
case 'markdown-community-link':
|
||||
//tag press also handles community by default
|
||||
if(handleTagPress){
|
||||
handleTagPress(community, filter)
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
@ -147,6 +158,26 @@ export const PostHtmlRenderer = memo(({
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
//this method checks if image is a child of table column
|
||||
//and calculates img width accordingly,
|
||||
//returns full width if img is not part of table
|
||||
const getMaxImageWidth = (tnode:TNode)=>{
|
||||
|
||||
//return full width if not parent exist
|
||||
if(!tnode.parent || tnode.parent.tagName === 'body'){
|
||||
return contentWidth;
|
||||
}
|
||||
|
||||
//return divided width based on number td tags
|
||||
if(tnode.parent.tagName === 'td'){
|
||||
const cols = tnode.parent.parent.children.length
|
||||
return contentWidth/cols;
|
||||
}
|
||||
|
||||
//check next parent
|
||||
return getMaxImageWidth(tnode.parent);
|
||||
}
|
||||
|
||||
|
||||
const _imageRenderer = ({
|
||||
@ -160,16 +191,17 @@ export const PostHtmlRenderer = memo(({
|
||||
};
|
||||
|
||||
const isVideoThumb = tnode.classes?.indexOf('video-thumbnail') >= 0;
|
||||
const isAnchored = !(tnode.parent?.classes?.indexOf('markdown-external-link') >= 0)
|
||||
const isAnchored = tnode.parent?.tagName === 'a';
|
||||
|
||||
|
||||
if(isVideoThumb){
|
||||
return <VideoThumb contentWidth={contentWidth} uri={imgUrl}/>;
|
||||
}
|
||||
|
||||
else {
|
||||
const maxImgWidth = getMaxImageWidth(tnode);
|
||||
return (
|
||||
<AutoHeightImage
|
||||
contentWidth={contentWidth}
|
||||
contentWidth={maxImgWidth}
|
||||
imgUrl={imgUrl}
|
||||
isAnchored={isAnchored}
|
||||
onPress={_onPress}
|
||||
|
@ -17,7 +17,6 @@ export default EStyleSheet.create({
|
||||
marginBottom:6,
|
||||
flexDirection:'row',
|
||||
alignItems:'center',
|
||||
justifyContent:'center',
|
||||
flexWrap:'wrap'
|
||||
|
||||
} as TextStyle,
|
||||
|
@ -143,7 +143,6 @@ const PostDisplayContainer = ({
|
||||
post={post}
|
||||
activeVotes={activeVotes}
|
||||
activeVotesCount={activeVotesCount}
|
||||
setActiveVotesCount={setActiveVotesCount}
|
||||
reblogs={reblogs}
|
||||
/>
|
||||
);
|
||||
|
@ -41,15 +41,14 @@ const PostDisplayView = ({
|
||||
activeVotes,
|
||||
reblogs,
|
||||
activeVotesCount,
|
||||
setActiveVotesCount,
|
||||
}) => {
|
||||
const [postHeight, setPostHeight] = useState(0);
|
||||
const [scrollHeight, setScrollHeight] = useState(0);
|
||||
const [cacheVoteIcrement, setCacheVoteIcrement] = useState(0);
|
||||
const [isLoadedComments, setIsLoadedComments] = useState(false);
|
||||
const actionSheet = useRef(null);
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
const [postBodyLoading, setPostBodyLoading] = useState(false);
|
||||
|
||||
const [tags, setTags] = useState([]);
|
||||
|
||||
// Component Life Cycles
|
||||
@ -94,7 +93,7 @@ const PostDisplayView = ({
|
||||
};
|
||||
|
||||
const _handleIncrementActiveVotesCount = () => {
|
||||
setActiveVotesCount(activeVotesCount + 1);
|
||||
setCacheVoteIcrement(1);
|
||||
};
|
||||
|
||||
const _getTabBar = (isFixedFooter = false) => {
|
||||
@ -114,7 +113,7 @@ const PostDisplayView = ({
|
||||
iconType="MaterialCommunityIcons"
|
||||
isClickable
|
||||
onPress={() => handleOnVotersPress && handleOnVotersPress()}
|
||||
text={activeVotesCount}
|
||||
text={activeVotesCount + cacheVoteIcrement}
|
||||
textMarginLeft={20}
|
||||
/>
|
||||
<TextWithIcon
|
||||
|
@ -24,7 +24,6 @@ import {
|
||||
filterSelected,
|
||||
setOtherPosts,
|
||||
setInitPosts,
|
||||
resetLocalVoteMap,
|
||||
} from '../../../redux/actions/postsAction';
|
||||
import { hidePostsThumbnails } from '../../../redux/actions/uiAction';
|
||||
import { fetchLeaderboard, followUser, unfollowUser } from '../../../redux/actions/userAction';
|
||||
@ -90,10 +89,6 @@ const PostsContainer = ({
|
||||
const [recommendedCommunities, setRecommendedCommunities] = useState([]);
|
||||
const [newPostsPopupPictures, setNewPostsPopupPictures] = useState(null);
|
||||
|
||||
const _resetLocalVoteMap = () => {
|
||||
dispatch(resetLocalVoteMap());
|
||||
};
|
||||
|
||||
const _setFeedPosts = (_posts, scrollPos = 0) => {
|
||||
if (isFeedScreen) {
|
||||
dispatch(setFeedPosts(_posts, scrollPos));
|
||||
@ -305,7 +300,6 @@ const PostsContainer = ({
|
||||
if (isFeedScreen) {
|
||||
AppState.addEventListener('change', _handleAppStateChange);
|
||||
_setFeedPosts(initPosts || []);
|
||||
_resetLocalVoteMap();
|
||||
} else {
|
||||
_setFeedPosts([]);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
import { connect } from 'react-redux';
|
||||
import get from 'lodash/get';
|
||||
|
||||
// Realm
|
||||
@ -7,7 +7,6 @@ import { setUpvotePercent } from '../../../realm/realm';
|
||||
|
||||
// Services and Actions
|
||||
import { setUpvotePercent as upvoteAction } from '../../../redux/actions/applicationActions';
|
||||
import { updateLocalVoteMap } from '../../../redux/actions/postsAction';
|
||||
|
||||
// Utils
|
||||
import { getTimeFromNow } from '../../../utils/time';
|
||||
@ -16,6 +15,8 @@ import parseAsset from '../../../utils/parseAsset';
|
||||
|
||||
// Component
|
||||
import UpvoteView from '../view/upvoteView';
|
||||
import { updateVoteCache } from '../../../redux/actions/cacheActions';
|
||||
import { useAppSelector } from '../../../hooks';
|
||||
|
||||
/*
|
||||
* Props Name Description Value
|
||||
@ -41,7 +42,8 @@ const UpvoteContainer = (props) => {
|
||||
const [isVoted, setIsVoted] = useState(null);
|
||||
const [isDownVoted, setIsDownVoted] = useState(null);
|
||||
const [totalPayout, setTotalPayout] = useState(get(content, 'total_payout'));
|
||||
const localVoteMap = useSelector((state) => state.posts.localVoteMap);
|
||||
const cachedVotes = useAppSelector((state) => state.cache.votes);
|
||||
const lastCacheUpdate = useAppSelector((state) => state.cache.lastUpdate);
|
||||
|
||||
useEffect(() => {
|
||||
let _isMounted = true;
|
||||
@ -54,8 +56,8 @@ const UpvoteContainer = (props) => {
|
||||
setIsVoted(_isVoted && parseInt(_isVoted, 10) / 10000);
|
||||
setIsDownVoted(_isDownVoted && (parseInt(_isDownVoted, 10) / 10000) * -1);
|
||||
|
||||
if (localVoteMap) {
|
||||
_handleLocalVote();
|
||||
if (cachedVotes && cachedVotes.size > 0) {
|
||||
_handleCachedVote();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -64,6 +66,19 @@ const UpvoteContainer = (props) => {
|
||||
return () => (_isMounted = false);
|
||||
}, [activeVotes]);
|
||||
|
||||
useEffect(() => {
|
||||
const postPath = `${content.author || ''}/${content.permlink || ''}`;
|
||||
//this conditional makes sure on targetted already fetched post is updated
|
||||
//with new cache status, this is to avoid duplicate cache merging
|
||||
if (
|
||||
lastCacheUpdate &&
|
||||
lastCacheUpdate.postPath === postPath &&
|
||||
content.post_fetched_at < lastCacheUpdate.updatedAt
|
||||
) {
|
||||
_handleCachedVote();
|
||||
}
|
||||
}, [lastCacheUpdate]);
|
||||
|
||||
const _setUpvotePercent = (value) => {
|
||||
if (value) {
|
||||
setUpvotePercent(String(value));
|
||||
@ -71,14 +86,15 @@ const UpvoteContainer = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const _handleLocalVote = () => {
|
||||
const postId = `${content.author || ''}-${content.permlink || ''}`;
|
||||
const _handleCachedVote = () => {
|
||||
const postPath = `${content.author || ''}/${content.permlink || ''}`;
|
||||
const postFetchedAt = get(content, 'post_fetched_at', 0);
|
||||
const localVote = localVoteMap[postId] || null;
|
||||
if (localVote) {
|
||||
const { votedAt, amount, isDownvote, incrementStep } = localVote;
|
||||
|
||||
if (postFetchedAt > votedAt) {
|
||||
if (cachedVotes.has(postPath)) {
|
||||
const cachedVote = cachedVotes.get(postPath);
|
||||
const { expiresAt, amount, isDownvote, incrementStep } = cachedVote;
|
||||
|
||||
if (postFetchedAt > expiresAt) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -97,24 +113,21 @@ const UpvoteContainer = (props) => {
|
||||
const amountNum = parseFloat(amount);
|
||||
|
||||
let incrementStep = 0;
|
||||
if (!isVoted && !isDownVoted && incrementVoteCount) {
|
||||
if (!isVoted && !isDownVoted) {
|
||||
incrementStep = 1;
|
||||
incrementVoteCount();
|
||||
}
|
||||
|
||||
setIsDownVoted(isDownvote ? true : false);
|
||||
setIsVoted(isDownvote ? false : true);
|
||||
|
||||
setTotalPayout(totalPayout + amountNum);
|
||||
//update redux
|
||||
const postId = `${content.author || ''}-${content.permlink || ''}`;
|
||||
const postPath = `${content.author || ''}/${content.permlink || ''}`;
|
||||
const curTime = new Date().getTime();
|
||||
const vote = {
|
||||
votedAt: new Date().getTime(),
|
||||
votedAt: curTime,
|
||||
amount: amountNum,
|
||||
isDownvote,
|
||||
incrementStep,
|
||||
expiresAt: curTime + 30000,
|
||||
};
|
||||
dispatch(updateLocalVoteMap(postId, vote));
|
||||
dispatch(updateVoteCache(postPath, vote));
|
||||
};
|
||||
|
||||
const author = get(content, 'author');
|
||||
|
20
src/redux/actions/cacheActions.ts
Normal file
20
src/redux/actions/cacheActions.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import {
|
||||
UPDATE_VOTE_CACHE,
|
||||
PURGE_EXPIRED_CACHE
|
||||
} from '../constants/constants';
|
||||
import { Vote } from '../reducers/cacheReducer';
|
||||
|
||||
|
||||
export const updateVoteCache = (postPath:string, vote:Vote) => ({
|
||||
payload:{
|
||||
postPath,
|
||||
vote
|
||||
},
|
||||
type: UPDATE_VOTE_CACHE
|
||||
})
|
||||
|
||||
export const purgeExpiredCache = () => ({
|
||||
type: PURGE_EXPIRED_CACHE
|
||||
})
|
||||
|
||||
|
@ -6,8 +6,6 @@ import {
|
||||
RESET,
|
||||
FILTER_SELECTED,
|
||||
SET_INIT_POSTS,
|
||||
UPDATE_LOCAL_VOTE_MAP,
|
||||
RESET_LOCAL_VOTE_MAP,
|
||||
SET_FEED_SCREEN_FILTERS,
|
||||
} from '../constants/constants';
|
||||
|
||||
@ -31,23 +29,7 @@ export const setOtherPosts = (posts, scrollPosition = 0) => ({
|
||||
});
|
||||
|
||||
|
||||
export const updateLocalVoteMap = (postId:string, localVote:{
|
||||
votedAt: number,
|
||||
amount: number,
|
||||
isDownvote: boolean,
|
||||
incrementStep: number
|
||||
}) => ({
|
||||
payload: {
|
||||
postId,
|
||||
localVote,
|
||||
},
|
||||
type: UPDATE_LOCAL_VOTE_MAP,
|
||||
});
|
||||
|
||||
|
||||
export const resetLocalVoteMap = () => ({
|
||||
type: RESET_LOCAL_VOTE_MAP,
|
||||
});
|
||||
export const fetchPosts = (payload) => ({
|
||||
payload,
|
||||
type: FETCH_POSTS,
|
||||
|
@ -81,8 +81,6 @@ export const SUBSCRIBE_COMMUNITY_FAIL = 'SUBSCRIBE_COMMUNITY_FAIL';
|
||||
export const LEAVE_COMMUNITY = 'LEAVE_COMMUNITY';
|
||||
export const LEAVE_COMMUNITY_SUCCESS = 'LEAVE_COMMUNITY_SUCCESS';
|
||||
export const LEAVE_COMMUNITY_FAIL = 'LEAVE_COMMUNITY_FAIL';
|
||||
export const UPDATE_LOCAL_VOTE_MAP = 'UPDATE_LOCAL_VOTE_MAP';
|
||||
export const RESET_LOCAL_VOTE_MAP = 'RESET_LOCAL_VOTE_MAP';
|
||||
|
||||
// USER
|
||||
export const FOLLOW_USER = 'FOLLOW_USER';
|
||||
@ -105,3 +103,7 @@ export const SET_OWN_PROFILE_TABS = 'SET_OWN_PROFILE_TABS';
|
||||
export const SET_BENEFICIARIES = 'SET_BENEFICIARIES';
|
||||
export const REMOVE_BENEFICIARIES = 'REMOVE_BENEFICIARIES';
|
||||
export const TEMP_BENEFICIARIES_ID = 'temp-beneficiaries';
|
||||
|
||||
//CACHE
|
||||
export const PURGE_EXPIRED_CACHE = 'PURGE_EXPIRED_CACHE';
|
||||
export const UPDATE_VOTE_CACHE = 'UPDATE_VOTE_CACHE';
|
||||
|
57
src/redux/reducers/cacheReducer.ts
Normal file
57
src/redux/reducers/cacheReducer.ts
Normal file
@ -0,0 +1,57 @@
|
||||
import { PURGE_EXPIRED_CACHE, UPDATE_VOTE_CACHE } from "../constants/constants";
|
||||
|
||||
export interface Vote {
|
||||
amount:number;
|
||||
isDownvote:boolean;
|
||||
incrementStep:number;
|
||||
votedAt:number;
|
||||
expiresAt:number;
|
||||
}
|
||||
|
||||
interface State {
|
||||
votes:Map<string, Vote>
|
||||
lastUpdate:{
|
||||
postPath:string,
|
||||
updatedAt:number,
|
||||
}
|
||||
}
|
||||
|
||||
const initialState:State = {
|
||||
votes:new Map(),
|
||||
lastUpdate:null,
|
||||
};
|
||||
|
||||
export default function (state = initialState, action) {
|
||||
const {type, payload} = action;
|
||||
switch (type) {
|
||||
case UPDATE_VOTE_CACHE:
|
||||
if(!state.votes){
|
||||
state.votes = new Map<string, Vote>();
|
||||
}
|
||||
state.votes.set(payload.postPath, payload.vote);
|
||||
return {
|
||||
...state, //spread operator in requried here, otherwise persist do not register change
|
||||
lastUpdate:{
|
||||
postPath:payload.postPath,
|
||||
updatedAt: new Date().getTime()
|
||||
}
|
||||
};
|
||||
case PURGE_EXPIRED_CACHE:
|
||||
const currentTime = new Date().getTime();
|
||||
|
||||
if(state.votes && state.votes.entries){
|
||||
Array.from(state.votes).forEach((entry)=>{
|
||||
if(entry[1].expiresAt < currentTime){
|
||||
state.votes.delete(entry[0]);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
...state
|
||||
}
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import communities from './communitiesReducer';
|
||||
import user from './userReducer';
|
||||
import customTabsReducer from './customTabsReducer';
|
||||
import editorReducer from './editorReducer';
|
||||
import cacheReducer from './cacheReducer';
|
||||
|
||||
export default combineReducers({
|
||||
account: accountReducer,
|
||||
@ -19,4 +20,5 @@ export default combineReducers({
|
||||
ui,
|
||||
communities,
|
||||
user,
|
||||
cache: cacheReducer,
|
||||
});
|
||||
|
@ -7,8 +7,6 @@ import {
|
||||
FETCH_POSTS,
|
||||
FETCH_POSTS_SUCCESS,
|
||||
RESET,
|
||||
UPDATE_LOCAL_VOTE_MAP,
|
||||
RESET_LOCAL_VOTE_MAP,
|
||||
SET_FEED_SCREEN_FILTERS,
|
||||
} from '../constants/constants';
|
||||
|
||||
@ -19,7 +17,6 @@ const initialState = {
|
||||
posts: [],
|
||||
loading: false,
|
||||
selectedFilterValue: '',
|
||||
localVoteMap: new Map(),
|
||||
feedScreenFilters:DEFAULT_FEED_FILTERS
|
||||
};
|
||||
|
||||
@ -44,20 +41,6 @@ export default function (state = initialState, action) {
|
||||
otherScrollPosition: action.payload.scrollPosition,
|
||||
posts: action.payload,
|
||||
};
|
||||
case UPDATE_LOCAL_VOTE_MAP:
|
||||
const { postId, localVote } = action.payload;
|
||||
const voteMap = state.localVoteMap || new Map();
|
||||
voteMap[postId] = localVote;
|
||||
return {
|
||||
...state,
|
||||
localVoteMap: voteMap,
|
||||
};
|
||||
|
||||
case RESET_LOCAL_VOTE_MAP:
|
||||
return {
|
||||
...state,
|
||||
localVoteMap: new Map(),
|
||||
};
|
||||
|
||||
case FILTER_SELECTED: {
|
||||
return {
|
||||
|
@ -1,11 +1,17 @@
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import thunk from 'redux-thunk';
|
||||
import { persistStore, persistReducer } from 'redux-persist';
|
||||
import { persistStore, persistReducer, createTransform } from 'redux-persist';
|
||||
import AsyncStorage from '@react-native-community/async-storage';
|
||||
import Reactotron from '../../../reactotron-config';
|
||||
|
||||
import reducer from '../reducers';
|
||||
|
||||
const transformCacheVoteMap = createTransform(
|
||||
(inboundState:any) => ({ ...inboundState, votes : Array.from(inboundState.votes)}),
|
||||
(outboundState) => ({ ...outboundState, votes:new Map(outboundState.votes)}),
|
||||
{whitelist:['cache']}
|
||||
);
|
||||
|
||||
// Middleware: Redux Persist Config
|
||||
const persistConfig = {
|
||||
// Root
|
||||
@ -15,6 +21,7 @@ const persistConfig = {
|
||||
// Blacklist (Don't Save Specific Reducers)
|
||||
blacklist: ['nav', 'application', 'communities', 'user'],
|
||||
timeout: 0,
|
||||
transforms:[transformCacheVoteMap]
|
||||
};
|
||||
|
||||
// Middleware: Redux Persist Persisted Reducer
|
||||
|
@ -94,12 +94,7 @@ import {
|
||||
toastNotification,
|
||||
updateActiveBottomTab,
|
||||
} from '../../../redux/actions/uiAction';
|
||||
import {
|
||||
resetLocalVoteMap,
|
||||
setFeedPosts,
|
||||
setFeedScreenFilters,
|
||||
setInitPosts,
|
||||
} from '../../../redux/actions/postsAction';
|
||||
import { setFeedPosts, setInitPosts } from '../../../redux/actions/postsAction';
|
||||
|
||||
import { encryptKey } from '../../../utils/crypto';
|
||||
|
||||
@ -109,6 +104,7 @@ import persistAccountGenerator from '../../../utils/persistAccountGenerator';
|
||||
import parseVersionNumber from '../../../utils/parseVersionNumber';
|
||||
import { getTimeFromNow, setMomentLocale } from '../../../utils/time';
|
||||
import parseAuthUrl from '../../../utils/parseAuthUrl';
|
||||
import { purgeExpiredCache } from '../../../redux/actions/cacheActions';
|
||||
|
||||
// Workaround
|
||||
let previousAppState = 'background';
|
||||
@ -777,7 +773,7 @@ class ApplicationContainer extends Component {
|
||||
dispatch(hideActionModal());
|
||||
dispatch(hideProfileModal());
|
||||
dispatch(toastNotification(''));
|
||||
dispatch(resetLocalVoteMap());
|
||||
dispatch(purgeExpiredCache());
|
||||
dispatch(setRcOffer(false));
|
||||
|
||||
const settings = await getSettings();
|
||||
|
@ -1,10 +0,0 @@
|
||||
|
||||
|
||||
export default (url) => {
|
||||
var regExp = /^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/;
|
||||
var match = url.match(regExp);
|
||||
var videoId = (match&&match[7].length==11)? match[7] : false;
|
||||
|
||||
console.log("Extracting id ", videoId, url);
|
||||
return videoId;
|
||||
}
|
@ -1031,10 +1031,10 @@
|
||||
exec-sh "^0.3.2"
|
||||
minimist "^1.2.0"
|
||||
|
||||
"@ecency/render-helper@^2.2.9":
|
||||
version "2.2.9"
|
||||
resolved "https://registry.yarnpkg.com/@ecency/render-helper/-/render-helper-2.2.9.tgz#067a4f3d3c27bcb814648c692a4dfb868035436b"
|
||||
integrity sha512-Oiz2N7qbwRAuS7dtbSRmCr7Yp2wUyEmBNtEun1byYoE+1g4k7dxJLsigs9ErrJzZ/yFQVcLEB1YrfjZczl/vfQ==
|
||||
"@ecency/render-helper@^2.2.12":
|
||||
version "2.2.12"
|
||||
resolved "https://registry.yarnpkg.com/@ecency/render-helper/-/render-helper-2.2.12.tgz#2eed5cf2fb05e9a6581a06e37fe0fda60d664450"
|
||||
integrity sha512-QGUS0rkXJiO5QUXggo5u1HZs89pNsIc0F7lD5lWhwHWG5l/CZV9WHrzsxHmtwE5AomlE6KqrZ/B6Ly8nWc5ytA==
|
||||
dependencies:
|
||||
he "^1.2.0"
|
||||
lolight "^1.4.0"
|
||||
|
Loading…
Reference in New Issue
Block a user