This commit is contained in:
Nouman Tahir 2023-03-03 16:52:03 +05:00
parent d7e35983c1
commit ee127637bf
30 changed files with 655 additions and 766 deletions

View File

@ -3,7 +3,6 @@ import { Image, TouchableOpacity } from 'react-native';
import EStyleSheet from 'react-native-extended-stylesheet';
import FastImage from 'react-native-fast-image';
interface AutoHeightImageProps {
contentWidth: number;
imgUrl: string;
@ -44,7 +43,6 @@ export const AutoHeightImage = ({
const _onLoad = () => {
setOnLoadCalled(true);
};
return (

View File

@ -38,7 +38,7 @@ const CommentView = ({
openReplyThread,
repliesToggle,
handleOnToggleReplies,
onUpvotePress
onUpvotePress,
}) => {
const intl = useIntl();
const dispatch = useDispatch();
@ -55,17 +55,15 @@ const CommentView = ({
const activeVotes = comment?.active_votes || [];
const [isOpeningReplies, setIsOpeningReplies] = useState(false);
const childCount = comment.children;
const { replies } = comment;
const _depth = commentNumber || comment.level;
const _currentUsername = currentAccountUsername || currentAccount?.username;
const _showSubCommentsToggle = async (force = false) => {
if ((replies && replies.length > 0) || force) {
setIsOpeningReplies(true);
await delay(10); //hack to rendering inidcator first before start loading comments
await delay(10); // hack to rendering inidcator first before start loading comments
handleOnToggleReplies(comment.commentKey);
setIsOpeningReplies(false);
} else if (openReplyThread) {
@ -73,7 +71,6 @@ const CommentView = ({
}
};
const _handleOnReplyPress = () => {
if (isLoggedIn) {
dispatch(showReplyModal(comment));
@ -128,8 +125,12 @@ const CommentView = ({
activeVotes={activeVotes}
isShowPayoutValue={true}
parentType={PostTypes.COMMENT}
onUpvotePress={(anchorRect, onVotingStart) => {onUpvotePress({content:comment, anchorRect, onVotingStart})}}
onPayoutDetailsPress={(anchorRect) => {onUpvotePress({content:comment, anchorRect, showPayoutDetails:true})}}
onUpvotePress={(anchorRect, onVotingStart) => {
onUpvotePress({ content: comment, anchorRect, onVotingStart });
}}
onPayoutDetailsPress={(anchorRect) => {
onUpvotePress({ content: comment, anchorRect, showPayoutDetails: true });
}}
/>
<TextWithIcon
iconName="heart-outline"
@ -168,14 +169,14 @@ const CommentView = ({
iconType="MaterialIcons"
/>
{!childCount && !activeVotes.length && comment.isDeletable && (
<IconButton
size={20}
iconStyle={styles.leftIcon}
style={styles.leftButton}
name="delete-forever"
onPress={() => handleDeleteComment(comment.permlink)}
iconType="MaterialIcons"
/>
<IconButton
size={20}
iconStyle={styles.leftIcon}
style={styles.leftButton}
name="delete-forever"
onPress={() => handleDeleteComment(comment.permlink)}
iconType="MaterialIcons"
/>
)}
</Fragment>
)}

View File

@ -55,7 +55,6 @@ const CommentsContainer = ({
const navigation = useNavigation();
const postsCachePrimer = postQueries.usePostsCachePrimer();
const [lcomments, setLComments] = useState([]);
const [propComments, setPropComments] = useState(comments);
@ -69,13 +68,11 @@ const CommentsContainer = ({
setLComments(sortedComments);
}, [commentCount, selectedFilter]);
useEffect(() => {
let _comments = comments;
const _comments = comments;
setPropComments(_comments);
}, [comments]);
// Component Functions
const _sortComments = (sortOrder = 'trending', _comments) => {
@ -254,7 +251,7 @@ const CommentsContainer = ({
author: comment.author,
permlink: comment.permlink,
},
key: `${comment.author}/${comment.permlink}`
key: `${comment.author}/${comment.permlink}`,
});
};

View File

@ -71,17 +71,17 @@ const CommentsView = ({
setSelectedComment(null);
};
const _onUpvotePress = ({content, anchorRect, showPayoutDetails, onVotingStart}) => {
if(upvotePopoverRef.current){
const _onUpvotePress = ({ content, anchorRect, showPayoutDetails, onVotingStart }) => {
if (upvotePopoverRef.current) {
upvotePopoverRef.current.showPopover({
anchorRect,
showPayoutDetails,
content,
postType:PostTypes.COMMENT,
onVotingStart
})
anchorRect,
showPayoutDetails,
content,
postType: PostTypes.COMMENT,
onVotingStart,
});
}
}
};
const menuItems = [
intl.formatMessage({ id: 'post.copy_link' }),
@ -178,12 +178,8 @@ const CommentsView = ({
cancelButtonIndex={3}
onPress={_onMenuItemPress}
/>
<UpvotePopover
ref={upvotePopoverRef}
/>
<PostHtmlInteractionHandler
ref={postInteractionRef}
/>
<UpvotePopover ref={upvotePopoverRef} />
<PostHtmlInteractionHandler ref={postInteractionRef} />
</Fragment>
);
};

View File

@ -1,36 +1,36 @@
import EStyleSheet from "react-native-extended-stylesheet";
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container:{
flexDirection:'row',
alignItems:'center'
},
upvoteButton: {
flexDirection: 'row',
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
},
upvoteIcon: {
alignSelf: 'center',
fontSize: 24,
color: '$primaryBlue',
marginRight: 5,
},
payoutTextButton: {
alignSelf: 'center',
},
payoutValue: {
alignSelf: 'center',
fontSize: 10,
color: '$primaryDarkGray',
marginLeft: 8,
},
declinedPayout: {
textDecorationLine: 'line-through',
textDecorationStyle: 'solid',
},
boldText: {
fontWeight: 'bold',
},
})
container: {
flexDirection: 'row',
alignItems: 'center',
},
upvoteButton: {
flexDirection: 'row',
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
},
upvoteIcon: {
alignSelf: 'center',
fontSize: 24,
color: '$primaryBlue',
marginRight: 5,
},
payoutTextButton: {
alignSelf: 'center',
},
payoutValue: {
alignSelf: 'center',
fontSize: 10,
color: '$primaryDarkGray',
marginLeft: 8,
},
declinedPayout: {
textDecorationLine: 'line-through',
textDecorationStyle: 'solid',
},
boldText: {
fontWeight: 'bold',
},
});

View File

@ -2,95 +2,94 @@ import React from 'react';
import get from 'lodash/get';
import { TouchableOpacity, View } from 'react-native';
// Components
import { TextWithIcon } from '../../basicUIElements';
// Styles
import styles from '../children/postCardStyles';
import { UpvoteButton } from '../children/upvoteButton';
import styles from './postCardStyles';
import { UpvoteButton } from './upvoteButton';
import { PostTypes } from '../../../constants/postTypes';
import { PostCardActionIds } from '../container/postCard';
import ROUTES from '../../../constants/routeNames';
interface Props {
content: any;
reblogs: any[];
handleCardInteraction: (id: PostCardActionIds, payload?: any, onCallback?:(data:any)=>void) => void;
content: any;
reblogs: any[];
handleCardInteraction: (
id: PostCardActionIds,
payload?: any,
onCallback?: (data: any) => void,
) => void;
}
export const PostCardActionsPanel = ({ content, reblogs, handleCardInteraction }: Props) => {
const activeVotes = content?.active_votes || [];
export const PostCardActionsPanel = ({
content,
reblogs,
handleCardInteraction
}: Props) => {
const _onVotersPress = () => {
handleCardInteraction(PostCardActionIds.NAVIGATE, {
name: ROUTES.SCREENS.VOTERS,
params: {
content,
},
key: content.permlink,
});
};
const activeVotes = content?.active_votes || [];
const _onVotersPress = () => {
handleCardInteraction(PostCardActionIds.NAVIGATE, {
name: ROUTES.SCREENS.VOTERS,
params: {
content: content,
},
key: content.permlink,
});
const _onReblogsPress = () => {
if (reblogs?.length) {
handleCardInteraction(PostCardActionIds.NAVIGATE, {
name: ROUTES.SCREENS.REBLOGS,
params: {
reblogs,
},
});
}
};
return (
<View style={styles.bodyFooter}>
<View style={styles.leftFooterWrapper}>
<UpvoteButton
content={content}
activeVotes={activeVotes}
isShowPayoutValue={true}
parentType={PostTypes.POST}
onUpvotePress={(anchorRect, onVotingStart) =>
handleCardInteraction(PostCardActionIds.UPVOTE, anchorRect, onVotingStart)
}
onPayoutDetailsPress={(anchorRect) =>
handleCardInteraction(PostCardActionIds.PAYOUT_DETAILS, anchorRect)
}
/>
const _onReblogsPress = () => {
if (reblogs?.length) {
handleCardInteraction(PostCardActionIds.NAVIGATE, {
name: ROUTES.SCREENS.REBLOGS,
params: {
reblogs: reblogs,
}
});
}
}
return (
<View style={styles.bodyFooter}>
<View style={styles.leftFooterWrapper}>
<UpvoteButton
content={content}
activeVotes={activeVotes}
isShowPayoutValue={true}
parentType={PostTypes.POST}
onUpvotePress={(anchorRect, onVotingStart) => handleCardInteraction(PostCardActionIds.UPVOTE, anchorRect, onVotingStart)}
onPayoutDetailsPress={(anchorRect) => handleCardInteraction(PostCardActionIds.PAYOUT_DETAILS, anchorRect)}
/>
<TouchableOpacity style={styles.commentButton} onPress={_onVotersPress}>
<TextWithIcon
iconName="heart-outline"
iconStyle={styles.commentIcon}
iconType="MaterialCommunityIcons"
isClickable
text={activeVotes.length}
/>
</TouchableOpacity>
</View>
<View style={styles.rightFooterWrapper}>
<TextWithIcon
iconName="repeat"
iconStyle={styles.commentIcon}
iconType="MaterialIcons"
isClickable
text={get(reblogs, 'length', 0)}
onPress={_onReblogsPress}
/>
<TextWithIcon
iconName="comment-outline"
iconStyle={styles.commentIcon}
iconType="MaterialCommunityIcons"
isClickable
text={get(content, 'children', 0)}
onPress={() => handleCardInteraction(PostCardActionIds.REPLY)}
/>
</View>
</View>
)
}
<TouchableOpacity style={styles.commentButton} onPress={_onVotersPress}>
<TextWithIcon
iconName="heart-outline"
iconStyle={styles.commentIcon}
iconType="MaterialCommunityIcons"
isClickable
text={activeVotes.length}
/>
</TouchableOpacity>
</View>
<View style={styles.rightFooterWrapper}>
<TextWithIcon
iconName="repeat"
iconStyle={styles.commentIcon}
iconType="MaterialIcons"
isClickable
text={get(reblogs, 'length', 0)}
onPress={_onReblogsPress}
/>
<TextWithIcon
iconName="comment-outline"
iconStyle={styles.commentIcon}
iconType="MaterialCommunityIcons"
isClickable
text={get(content, 'children', 0)}
onPress={() => handleCardInteraction(PostCardActionIds.REPLY)}
/>
</View>
</View>
);
};

View File

@ -2,15 +2,13 @@ import React, { useState } from 'react';
import { TouchableOpacity, Text, View } from 'react-native';
// Utils
import FastImage from 'react-native-fast-image';
// Components
// Styles
import styles from '../children/postCardStyles';
import styles from './postCardStyles';
import { PostCardActionIds } from '../container/postCard';
import getWindowDimensions from '../../../utils/getWindowDimensions';
import ROUTES from '../../../constants/routeNames';
@ -21,34 +19,36 @@ const DEFAULT_IMAGE =
const NSFW_IMAGE =
'https://images.ecency.com/DQmZ1jW4p7o5GyoqWyCib1fSLE2ftbewsMCt2GvbmT9kmoY/nsfw_3x.png';
interface Props {
content: any,
isHideImage: boolean,
thumbHeight: number,
content: any;
isHideImage: boolean;
thumbHeight: number;
nsfw: string;
setThumbHeight: (postPath: string, height: number) => void;
handleCardInteraction: (id: PostCardActionIds, payload?: any) => void;
}
export const PostCardContent = ({ content, isHideImage, thumbHeight, nsfw, setThumbHeight, handleCardInteraction }: Props) => {
export const PostCardContent = ({
content,
isHideImage,
thumbHeight,
nsfw,
setThumbHeight,
handleCardInteraction,
}: Props) => {
const [calcImgHeight, setCalcImgHeight] = useState(thumbHeight || 300);
const _onPress = () => {
handleCardInteraction(PostCardActionIds.NAVIGATE, {
name: ROUTES.SCREENS.POST,
params: {
content: content,
content,
author: content.author,
permlink: content.permlink,
},
key: `${content.author}/${content.permlink}`
})
}
key: `${content.author}/${content.permlink}`,
});
};
let images = { image: DEFAULT_IMAGE, thumbnail: DEFAULT_IMAGE };
if (content.thumbnail) {
@ -61,14 +61,9 @@ export const PostCardContent = ({ content, isHideImage, thumbHeight, nsfw, setTh
images = { image: DEFAULT_IMAGE, thumbnail: DEFAULT_IMAGE };
}
return (
<View style={styles.postBodyWrapper}>
<TouchableOpacity
activeOpacity={0.8}
style={styles.hiddenImages}
onPress={_onPress}
>
<TouchableOpacity activeOpacity={0.8} style={styles.hiddenImages} onPress={_onPress}>
{!isHideImage && (
<FastImage
source={{ uri: images.image }}
@ -80,14 +75,11 @@ export const PostCardContent = ({ content, isHideImage, thumbHeight, nsfw, setTh
},
]}
resizeMode={
calcImgHeight < dim.height
? FastImage.resizeMode.contain
: FastImage.resizeMode.cover
calcImgHeight < dim.height ? FastImage.resizeMode.contain : FastImage.resizeMode.cover
}
onLoad={(evt) => {
if (!thumbHeight) {
const height =
(evt.nativeEvent.height / evt.nativeEvent.width) * (dim.width - 18);
const height = (evt.nativeEvent.height / evt.nativeEvent.width) * (dim.width - 18);
setCalcImgHeight(height);
setThumbHeight(content.author + content.permlink, height);
}
@ -99,8 +91,7 @@ export const PostCardContent = ({ content, isHideImage, thumbHeight, nsfw, setTh
<Text style={styles.title}>{content.title}</Text>
<Text style={styles.summary}>{content.summary}</Text>
</View>
</TouchableOpacity>
</View>
)
}
);
};

View File

@ -3,6 +3,7 @@ import get from 'lodash/get';
import { View } from 'react-native';
// Components
import { IntlShape } from 'react-intl';
import { PostHeaderDescription } from '../../postElements';
import { TextWithIcon } from '../../basicUIElements';
import { Icon } from '../../icon';
@ -11,25 +12,22 @@ import { Icon } from '../../icon';
import styles from './postCardStyles';
import { IconButton } from '../..';
import { getTimeFromNow } from '../../../utils/time';
import { IntlShape } from 'react-intl';
import { PostCardActionIds } from '../container/postCard';
interface Props {
intl:IntlShape;
intl: IntlShape;
content: any;
isHideImage: boolean;
handleCardInteraction: (id: PostCardActionIds, payload?: any) => void
handleCardInteraction: (id: PostCardActionIds, payload?: any) => void;
}
export const PostCardHeader = ( {intl, content, isHideImage, handleCardInteraction }: Props) => {
export const PostCardHeader = ({ intl, content, isHideImage, handleCardInteraction }: Props) => {
const rebloggedBy = get(content, 'reblogged_by[0]', null);
const dateString = useMemo(() => getTimeFromNow(content?.created), [content])
const dateString = useMemo(() => getTimeFromNow(content?.created), [content]);
const _handleOnTagPress = (navParams) => {
handleCardInteraction(PostCardActionIds.NAVIGATE, navParams)
}
handleCardInteraction(PostCardActionIds.NAVIGATE, navParams);
};
return (
<>
@ -52,7 +50,7 @@ export const PostCardHeader = ( {intl, content, isHideImage, handleCardInteracti
isHideImage={isHideImage}
name={get(content, 'author')}
profileOnPress={() => handleCardInteraction(PostCardActionIds.USER, content.author)}
handleOnTagPress = {_handleOnTagPress}
handleOnTagPress={_handleOnTagPress}
reputation={get(content, 'author_reputation')}
size={50}
content={content}
@ -74,5 +72,5 @@ export const PostCardHeader = ( {intl, content, isHideImage, handleCardInteracti
</View>
</View>
</>
)
}
);
};

View File

@ -12,12 +12,11 @@ export default EStyleSheet.create({
height: 1,
},
},
optionsIconContainer:{
marginLeft:12,
optionsIconContainer: {
marginLeft: 12,
},
optionsIcon:{
color:'$iconColor'
optionsIcon: {
color: '$iconColor',
},
commentButton: {
padding: 0,

View File

@ -4,7 +4,6 @@ import { PostCardActionsPanel } from '../children/postCardActionsPanel';
import { PostCardContent } from '../children/postCardContent';
import { PostCardHeader } from '../children/postCardHeader';
import styles from '../children/postCardStyles';
/*
@ -13,16 +12,14 @@ import styles from '../children/postCardStyles';
*
*/
export enum PostCardActionIds {
USER = 'USER',
OPTIONS = 'OPTIONS',
UNMUTE = 'UNMUTE',
REPLY = 'REPLY',
UPVOTE = 'UPVOTE',
PAYOUT_DETAILS = 'PAYOUT_DETAILS',
NAVIGATE = 'NAVIGATE'
NAVIGATE = 'NAVIGATE',
}
const PostCard = ({
@ -35,21 +32,22 @@ const PostCard = ({
setImageHeight,
handleCardInteraction,
}) => {
return (
<View style={styles.post}>
<PostCardHeader
intl={intl}
content={content}
isHideImage={isHideImage}
handleCardInteraction={handleCardInteraction} />
handleCardInteraction={handleCardInteraction}
/>
<PostCardContent
content={content}
isHideImage={isHideImage}
nsfw={nsfw}
thumbHeight={imageHeight}
setThumbHeight={setImageHeight}
handleCardInteraction={handleCardInteraction} />
handleCardInteraction={handleCardInteraction}
/>
<PostCardActionsPanel
content={content}
reblogs={reblogs || []}

View File

@ -18,7 +18,7 @@ export const CommentsSection = ({ item, index, revealReplies, ...props }) => {
const _enteringAnim = SlideInRight.duration(150)
.springify()
.delay(index * 100);
return (
<Animated.View key={item.author + item.permlink} entering={_enteringAnim}>
<Comment

View File

@ -1,93 +1,93 @@
export const sortComments = (sortOrder = 'trending', _comments) => {
const sortedComments: any[] = _comments;
const sortedComments: any[] = _comments;
const absNegative = (a) => a.net_rshares < 0;
const absNegative = (a) => a.net_rshares < 0;
const sortOrders = {
trending: (a, b) => {
if (a.renderOnTop) {
return -1;
}
const sortOrders = {
trending: (a, b) => {
if (a.renderOnTop) {
return -1;
}
if (absNegative(a)) {
return 1;
}
if (absNegative(a)) {
return 1;
}
if (absNegative(b)) {
return -1;
}
if (absNegative(b)) {
return -1;
}
const apayout = a.total_payout;
const bpayout = b.total_payout;
const apayout = a.total_payout;
const bpayout = b.total_payout;
if (apayout !== bpayout) {
return bpayout - apayout;
}
if (apayout !== bpayout) {
return bpayout - apayout;
}
return 0;
},
reputation: (a, b) => {
if (a.renderOnTop) {
return -1;
}
return 0;
},
reputation: (a, b) => {
if (a.renderOnTop) {
return -1;
}
const keyA = a.author_reputation;
const keyB = b.author_reputation;
const keyA = a.author_reputation;
const keyB = b.author_reputation;
if (keyA > keyB) {
return -1;
}
if (keyA < keyB) {
return 1;
}
if (keyA > keyB) {
return -1;
}
if (keyA < keyB) {
return 1;
}
return 0;
},
votes: (a, b) => {
if (a.renderOnTop) {
return -1;
}
return 0;
},
votes: (a, b) => {
if (a.renderOnTop) {
return -1;
}
const keyA = a.active_votes.length;
const keyB = b.active_votes.length;
const keyA = a.active_votes.length;
const keyB = b.active_votes.length;
if (keyA > keyB) {
return -1;
}
if (keyA < keyB) {
return 1;
}
if (keyA > keyB) {
return -1;
}
if (keyA < keyB) {
return 1;
}
return 0;
},
age: (a, b) => {
if (a.renderOnTop) {
return -1;
}
return 0;
},
age: (a, b) => {
if (a.renderOnTop) {
return -1;
}
if (absNegative(a)) {
return 1;
}
if (absNegative(a)) {
return 1;
}
if (absNegative(b)) {
return -1;
}
if (absNegative(b)) {
return -1;
}
const keyA = Date.parse(a.created);
const keyB = Date.parse(b.created);
const keyA = Date.parse(a.created);
const keyB = Date.parse(b.created);
if (keyA > keyB) {
return -1;
}
if (keyA < keyB) {
return 1;
}
if (keyA > keyB) {
return -1;
}
if (keyA < keyB) {
return 1;
}
return 0;
},
};
return 0;
},
};
sortedComments.sort(sortOrders[sortOrder]);
sortedComments.sort(sortOrders[sortOrder]);
return sortedComments;
return sortedComments;
};

View File

@ -43,7 +43,6 @@ const CommentBody = ({
const intl = useIntl();
const _onLongPressStateChange = ({ nativeEvent }) => {
if (nativeEvent.state === State.ACTIVE) {
handleOnLongPress();
@ -54,8 +53,6 @@ const CommentBody = ({
setRevealComment(true);
};
const _handleTagPress = (tag: string, filter: string = GLOBAL_POST_FILTERS_VALUE[0]) => {
if (tag) {
const name = isCommunity(tag) ? ROUTES.SCREENS.COMMUNITY : ROUTES.SCREENS.TAG_RESULT;
@ -71,8 +68,6 @@ const CommentBody = ({
}
};
const _handleOnPostPress = (permlink, author) => {
if (handleOnPostPress) {
handleOnPostPress(permlink, author);
@ -114,8 +109,6 @@ const CommentBody = ({
}
};
return (
<Fragment>
{revealComment ? (

View File

@ -65,9 +65,9 @@ class PostHeaderDescription extends PureComponent {
}
if (handleTagPress) {
handleTagPress(navParams)
handleTagPress(navParams);
} else {
RootNavigation.navigate(navParams)
RootNavigation.navigate(navParams);
}
};
@ -179,5 +179,4 @@ class PostHeaderDescription extends PureComponent {
}
}
export default injectIntl(PostHeaderDescription);

View File

@ -1,2 +1,2 @@
export * from './postHtmlRenderer';
export * from './postInteractionHandler';
export * from './postInteractionHandler';

View File

@ -6,6 +6,8 @@ import EStyleSheet from 'react-native-extended-stylesheet';
// Services and Actions
import { useNavigation } from '@react-navigation/native';
import { FlatList } from 'react-native-gesture-handler';
import ActionSheet from 'react-native-actions-sheet';
import { ignoreUser, pinCommunityPost, profileUpdate, reblog } from '../../../providers/hive/dhive';
import { addBookmark, addReport } from '../../../providers/ecency/ecency';
import { toastNotification, setRcOffer, showActionModal } from '../../../redux/actions/uiAction';
@ -24,8 +26,6 @@ import { updateCurrentAccount } from '../../../redux/actions/accountAction';
import showLoginAlert from '../../../utils/showLoginAlert';
import { useUserActivityMutation } from '../../../providers/queries/pointQueries';
import { PointActivityIds } from '../../../providers/ecency/ecency.types';
import { FlatList } from 'react-native-gesture-handler';
import ActionSheet from 'react-native-actions-sheet';
import { useAppDispatch, useAppSelector } from '../../../hooks';
import styles from '../styles/postOptionsModal.styles';
@ -36,54 +36,46 @@ import styles from '../styles/postOptionsModal.styles';
*/
interface Props {
pageType?: string
pageType?: string;
}
const PostOptionsModal = ({
pageType,
}: Props, ref) => {
const PostOptionsModal = ({ pageType }: Props, ref) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const navigation = useNavigation();
const userActivityMutation = useUserActivityMutation();
const bottomSheetModalRef = useRef<ActionSheet | null>(null);
const alertTimer = useRef<any>(null);
const shareTimer = useRef<any>(null);
const actionSheetTimer = useRef<any>(null);
const reportTimer = useRef<any>(null);
const isLoggedIn = useAppSelector(state => state.application.isLoggedIn);
const currentAccount = useAppSelector(state => state.account.currentAccount);
const pinCode = useAppSelector(state => state.application.pin);
const isPinCodeOpen = useAppSelector(state => state.application.isPinCodeOpen);
const subscribedCommunities = useAppSelector(state => state.communities.subscribedCommunities);
const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn);
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const pinCode = useAppSelector((state) => state.application.pin);
const isPinCodeOpen = useAppSelector((state) => state.application.isPinCodeOpen);
const subscribedCommunities = useAppSelector((state) => state.communities.subscribedCommunities);
const [content, setContent] = useState<any>(null);
const [options, setOptions] = useState(OPTIONS);
useImperativeHandle(ref, () => ({
show: (_content) => {
if (!_content) {
Alert.alert(
intl.formatMessage({ id: 'alert.something_wrong' }),
"Post content not passed for viewing post options"
'Post content not passed for viewing post options',
);
return;
}
if (bottomSheetModalRef.current) {
setContent(_content)
setContent(_content);
bottomSheetModalRef.current.show();
}
}
}))
},
}));
useEffect(() => {
if (content) {
@ -109,11 +101,8 @@ const PostOptionsModal = ({
clearTimeout(reportTimer.current);
reportTimer.current = null;
}
}
}, [content])
};
}, [content]);
const _initOptions = () => {
// check if post is owned by current user or not, if so pinned or not
@ -125,11 +114,11 @@ const PostOptionsModal = ({
const _canUpdateCommunityPin =
subscribedCommunities.data && !!content && content.community
? subscribedCommunities.data.reduce((role, subscription) => {
if (content.community === subscription[0]) {
return ['owner', 'admin', 'mod'].includes(subscription[2]);
}
return role;
}, false)
if (content.community === subscription[0]) {
return ['owner', 'admin', 'mod'].includes(subscription[2]);
}
return role;
}, false)
: false;
const _isPinnedInCommunity = !!content && content.stats?.is_pinned;
@ -152,11 +141,7 @@ const PostOptionsModal = ({
setOptions(_options);
};
const _muteUser = () => {
const username = content.author;
const follower = currentAccount.name;
const following = username;
@ -206,7 +191,6 @@ const PostOptionsModal = ({
};
const _share = () => {
const postUrl = getPostUrl(get(content, 'url'));
Share.share({
@ -214,9 +198,7 @@ const PostOptionsModal = ({
});
};
const _report = (url) => {
const _onConfirm = () => {
addReport('content', url)
.then(() => {
@ -246,7 +228,7 @@ const PostOptionsModal = ({
buttons: [
{
text: intl.formatMessage({ id: 'alert.cancel' }),
onPress: () => { },
onPress: () => {},
},
{
text: intl.formatMessage({ id: 'alert.confirm' }),
@ -258,7 +240,6 @@ const PostOptionsModal = ({
};
const _addToBookmarks = () => {
if (!isLoggedIn) {
showLoginAlert({ intl });
return;
@ -285,7 +266,6 @@ const PostOptionsModal = ({
};
const _reblog = () => {
if (!isLoggedIn) {
showLoginAlert({ intl });
return;
@ -329,9 +309,9 @@ const PostOptionsModal = ({
}
};
const _updatePinnedPost = async ({ unpinPost }: { unpinPost: boolean } = { unpinPost: false }) => {
const _updatePinnedPost = async (
{ unpinPost }: { unpinPost: boolean } = { unpinPost: false },
) => {
const params = {
...currentAccount.about.profile,
pinned: unpinPost ? null : content.permlink,
@ -359,8 +339,6 @@ const PostOptionsModal = ({
const _updatePinnedPostCommunity = async (
{ unpinPost }: { unpinPost: boolean } = { unpinPost: false },
) => {
try {
await pinCommunityPost(
currentAccount,
@ -383,8 +361,6 @@ const PostOptionsModal = ({
};
const _redirectToReply = () => {
if (isLoggedIn) {
navigation.navigate({
name: ROUTES.SCREENS.EDITOR,
@ -398,7 +374,6 @@ const PostOptionsModal = ({
};
const _redirectToPromote = (name, from, redeemType) => {
const params = {
from,
permlink: `${get(content, 'author')}/${get(content, 'permlink')}`,
@ -421,10 +396,8 @@ const PostOptionsModal = ({
}
};
// Component Functions
const _handleOnDropdownSelect = async (index) => {
const username = content.author;
const isOwnProfile = !username || currentAccount.username === username;
@ -473,7 +446,7 @@ const PostOptionsModal = ({
case 'report':
reportTimer.current = setTimeout(() => {
_report(get(content, 'url'));
}, 300)
}, 300);
break;
case 'pin-blog':
@ -505,21 +478,23 @@ const PostOptionsModal = ({
}
};
const _renderItem = ({ item, index }: { item: string, index: number }) => {
const _renderItem = ({ item, index }: { item: string; index: number }) => {
const _onPress = () => {
bottomSheetModalRef.current?.hide();
_handleOnDropdownSelect(index)
}
_handleOnDropdownSelect(index);
};
return (
<TouchableHighlight underlayColor={EStyleSheet.value('$primaryLightBackground')} onPress={_onPress}>
<Text style={styles.dropdownItem} >
<TouchableHighlight
underlayColor={EStyleSheet.value('$primaryLightBackground')}
onPress={_onPress}
>
<Text style={styles.dropdownItem}>
{intl.formatMessage({ id: `post_dropdown.${item}` }).toLocaleUpperCase()}
</Text>
</TouchableHighlight>
)
}
);
};
return (
<ActionSheet
@ -537,8 +512,6 @@ const PostOptionsModal = ({
/>
</ActionSheet>
);
}
};
export default forwardRef(PostOptionsModal)
export default forwardRef(PostOptionsModal);

View File

@ -1,2 +1,3 @@
import PostOptionsModal from './container/postOptionsModal';
export { PostOptionsModal };

View File

@ -9,15 +9,15 @@ export default EStyleSheet.create({
sheetContent: {
backgroundColor: '$modalBackground',
},
dropdownItem:{
paddingHorizontal:32,
paddingVertical:12,
fontSize:14,
fontWeight:'600',
color:'$primaryDarkText'
dropdownItem: {
paddingHorizontal: 32,
paddingVertical: 12,
fontSize: 14,
fontWeight: '600',
color: '$primaryDarkText',
},
listContainer: {
paddingTop: 16,
paddingBottom: 40,
},
listContainer:{
paddingTop:16,
paddingBottom:40
}
});

View File

@ -67,8 +67,6 @@ const PostDisplayView = ({
const [tags, setTags] = useState([]);
const [postBodyHeight, setPostBodyHeight] = useState(0);
// Component Life Cycles
useEffect(() => {
if (isLoggedIn && get(currentAccount, 'name') && !isNewPost) {
@ -89,9 +87,6 @@ const PostDisplayView = ({
}
}, [post]);
// Component Functions
const onRefresh = useCallback(() => {
setRefreshing(true);
@ -110,25 +105,23 @@ const PostDisplayView = ({
}
};
const _onUpvotePress = ({
anchorRect,
content,
onVotingStart,
showPayoutDetails = false,
postType = parentPost ? PostTypes.COMMENT : PostTypes.POST
}:any) => {
postType = parentPost ? PostTypes.COMMENT : PostTypes.POST,
}: any) => {
if (upvotePopoverRef.current) {
upvotePopoverRef.current.showPopover({
anchorRect,
content,
showPayoutDetails,
upvotePopoverRef.current.showPopover({
anchorRect,
content,
showPayoutDetails,
postType,
onVotingStart
})
onVotingStart,
});
}
}
};
const _renderActionPanel = (isFixedFooter = false) => {
return (
@ -140,8 +133,12 @@ const PostDisplayView = ({
content={post}
parentType={parentPost ? PostTypes.COMMENT : PostTypes.POST}
boldPayout={true}
onUpvotePress={(anchorRect, onVotingStart) => { _onUpvotePress({ anchorRect, content: post, onVotingStart }) }}
onPayoutDetailsPress={(anchorRect) => { _onUpvotePress({ anchorRect, content: post, showPayoutDetails: true }) }}
onUpvotePress={(anchorRect, onVotingStart) => {
_onUpvotePress({ anchorRect, content: post, onVotingStart });
}}
onPayoutDetailsPress={(anchorRect) => {
_onUpvotePress({ anchorRect, content: post, showPayoutDetails: true });
}}
/>
<TextWithIcon
iconName="heart-outline"
@ -250,8 +247,6 @@ const PostDisplayView = ({
setIsLoadedComments(true);
};
const _postContentView = (
<>
{parentPost && <ParentPost post={parentPost} />}
@ -312,7 +307,6 @@ const PostDisplayView = ({
postContentView={_postContentView}
onRefresh={onRefresh}
onUpvotePress={_onUpvotePress}
/>
</View>
{post && _renderActionPanel(true)}

View File

@ -1,10 +1,25 @@
import React, { forwardRef, useRef, useImperativeHandle, useState, useEffect, Fragment, useMemo } from 'react';
import { FlatListProps, FlatList, RefreshControl, ActivityIndicator, View, Alert } from 'react-native';
import React, {
forwardRef,
useRef,
useImperativeHandle,
useState,
useEffect,
Fragment,
useMemo,
} from 'react';
import {
FlatListProps,
FlatList,
RefreshControl,
ActivityIndicator,
View,
Alert,
} from 'react-native';
import { useSelector } from 'react-redux';
import PostCard from '../../postCard';
import styles from '../view/postsListStyles';
import { useNavigation } from '@react-navigation/native';
import { useIntl } from 'react-intl';
import PostCard from '../../postCard';
import styles from '../view/postsListStyles';
import { UpvotePopover } from '../..';
import { PostTypes } from '../../../constants/postTypes';
import { PostOptionsModal } from '../../postOptionsModal';
@ -51,11 +66,9 @@ const postsListContainer = (
const navigation = useNavigation();
const upvotePopoverRef = useRef(null);
const postDropdownRef = useRef(null);
const isHideImages = useSelector((state) => state.application.hidePostsThumbnails);
const nsfw = useSelector((state) => state.application.hidePostsThumbnails);
const isDarkTheme = useSelector((state) => state.application.isDarkThem);
@ -75,41 +88,36 @@ const postsListContainer = (
const reblogsCollectionRef = useRef({});
const data = useMemo(() => {
let _data = posts || cachedPosts
let _data = posts || cachedPosts;
if (!_data || !_data.length) {
return []
return [];
}
//also skip muted posts
// also skip muted posts
_data = _data.filter((item) => {
const isMuted = mutes && mutes.indexOf(item.author) > -1;
return !isMuted && !!item?.author;
})
});
const _promotedPosts = promotedPosts.filter((item) => {
const isMuted = mutes && mutes.indexOf(item.author) > -1;
const notInPosts = _data.filter((x) => x.permlink === item.permlink).length <= 0
return !isMuted && !!item?.author && notInPosts
})
const notInPosts = _data.filter((x) => x.permlink === item.permlink).length <= 0;
return !isMuted && !!item?.author && notInPosts;
});
//inject promoted posts in flat list data,
// inject promoted posts in flat list data,
_promotedPosts.forEach((pPost, index) => {
const pIndex = (index * 4) + 3;
const pIndex = index * 4 + 3;
if (_data.length > pIndex) {
_data.splice(pIndex, 0, pPost)
_data.splice(pIndex, 0, pPost);
}
})
});
return _data;
}, [posts, promotedPosts, cachedPosts, mutes]);
const cacheInjectedData = useInjectVotesCache(data);
useImperativeHandle(ref, () => ({
scrollToTop() {
flatListRef.current?.scrollToOffset({ x: 0, y: 0, animated: true });
@ -135,31 +143,30 @@ const postsListContainer = (
});
}, [scrollPosition]);
useEffect(() => {
//fetch reblogs here
// fetch reblogs here
_updateReblogsCollection();
}, [data, votesCache])
}, [data, votesCache]);
const _updateReblogsCollection = async () => {
//improve routine using list or promises
// improve routine using list or promises
for (const i in data) {
const _item = data[i]
const _postPath = _item.author + _item.permlink
const _item = data[i];
const _postPath = _item.author + _item.permlink;
if (!reblogsCollectionRef.current[_postPath]) {
try {
const reblogs = await getPostReblogs(_item);
reblogsCollectionRef.current = { ...reblogsCollectionRef.current, [_postPath]: reblogs || [] }
reblogsCollectionRef.current = {
...reblogsCollectionRef.current,
[_postPath]: reblogs || [],
};
} catch (err) {
console.warn("failed to fetch reblogs for post");
reblogsCollectionRef.current = { ...reblogsCollectionRef.current, [_postPath]: [] }
console.warn('failed to fetch reblogs for post');
reblogsCollectionRef.current = { ...reblogsCollectionRef.current, [_postPath]: [] };
}
}
}
}
};
const _setImageHeightInMap = (mapKey: string, height: number) => {
if (mapKey && height) {
@ -186,13 +193,15 @@ const postsListContainer = (
}
};
const _handleCardInteraction = (id: PostCardActionIds, payload: any, content: any, onCallback) => {
const _handleCardInteraction = (
id: PostCardActionIds,
payload: any,
content: any,
onCallback,
) => {
switch (id) {
case PostCardActionIds.USER:
dispatch(showProfileModal(payload))
dispatch(showProfileModal(payload));
break;
case PostCardActionIds.OPTIONS:
@ -202,46 +211,57 @@ const postsListContainer = (
break;
case PostCardActionIds.NAVIGATE:
navigation.navigate(payload)
navigation.navigate(payload);
break;
case PostCardActionIds.REPLY:
showQuickReplyModal(content)
showQuickReplyModal(content);
break;
case PostCardActionIds.UPVOTE:
if (upvotePopoverRef.current && payload && content) {
upvotePopoverRef.current.showPopover({ anchorRect: payload, content, onVotingStart:onCallback });
upvotePopoverRef.current.showPopover({
anchorRect: payload,
content,
onVotingStart: onCallback,
});
}
break;
case PostCardActionIds.PAYOUT_DETAILS:
if (upvotePopoverRef.current && payload && content) {
upvotePopoverRef.current.showPopover({ anchorRect: payload, content, showPayoutDetails: true });
upvotePopoverRef.current.showPopover({
anchorRect: payload,
content,
showPayoutDetails: true,
});
}
break;
}
}
};
const _renderItem = ({ item }: { item: any }) => {
// get image height from cache if available
const localId = item.author + item.permlink;
const imgHeight = imageHeights.get(localId);
const reblogs = reblogsCollectionRef.current[localId]
const reblogs = reblogsCollectionRef.current[localId];
// e.push(
return <PostCard
intl={intl}
key={`${item.author}-${item.permlink}`}
content={item}
isHideImage={isHideImages}
nsfw={nsfw}
reblogs={reblogs}
imageHeight={imgHeight}
setImageHeight={_setImageHeightInMap}
handleCardInteraction={(id: PostCardActionIds, payload: any, onCallback) => _handleCardInteraction(id, payload, item, onCallback)}
/>
return (
<PostCard
intl={intl}
key={`${item.author}-${item.permlink}`}
content={item}
isHideImage={isHideImages}
nsfw={nsfw}
reblogs={reblogs}
imageHeight={imgHeight}
setImageHeight={_setImageHeightInMap}
handleCardInteraction={(id: PostCardActionIds, payload: any, onCallback) =>
_handleCardInteraction(id, payload, item, onCallback)
}
/>
);
};
return (
@ -269,7 +289,7 @@ const postsListContainer = (
onRefresh={() => {
if (onLoadPosts) {
onLoadPosts(true);
reblogsCollectionRef.current = {}
reblogsCollectionRef.current = {};
}
}}
progressBackgroundColor="#357CE6"
@ -280,16 +300,9 @@ const postsListContainer = (
}
{...props}
/>
<UpvotePopover
ref={upvotePopoverRef}
parentType={PostTypes.POST}
/>
<PostOptionsModal
ref={postDropdownRef}
pageType={pageType}
/>
<UpvotePopover ref={upvotePopoverRef} parentType={PostTypes.POST} />
<PostOptionsModal ref={postDropdownRef} pageType={pageType} />
</Fragment>
);
};

View File

@ -6,147 +6,121 @@ import { useAppSelector } from '../../../hooks';
import parseAsset from '../../../utils/parseAsset';
import { getTimeFromNow } from '../../../utils/time';
import { FormattedCurrency } from '../../formatedElements';
// Styles
import styles from './upvoteStyles';
interface Props {
content: any
content: any;
}
export const PayoutDetailsContent = ({
content
}: Props) => {
const intl = useIntl();
export const PayoutDetailsContent = ({ content }: Props) => {
const intl = useIntl();
const globalProps = useAppSelector(state => state.account.globalProps);
const globalProps = useAppSelector((state) => state.account.globalProps);
const authorPayout = parseAsset(content.author_payout_value).amount;
const curationPayout = parseAsset(content.curator_payout_value).amount;
const promotedPayout = parseAsset(content.promoted).amount;
const authorPayout = parseAsset(content.author_payout_value).amount;
const curationPayout = parseAsset(content.curator_payout_value).amount;
const promotedPayout = parseAsset(content.promoted).amount;
const pendingPayout = parseAsset(content.pending_payout_value).amount;
const pendingPayout = parseAsset(content.pending_payout_value).amount;
const totalPayout = content.total_payout;
const totalPayout = content.total_payout;
const maxPayout = content.max_payout;
const maxPayout = content.max_payout;
const payoutDate = getTimeFromNow(content.payout_at);
const payoutDate = getTimeFromNow(content.payout_at);
const payoutLimitHit = totalPayout >= maxPayout;
const payoutLimitHit = totalPayout >= maxPayout;
// assemble breakdown
const base = globalProps?.base || 0;
const quote = globalProps?.quote || 0;
const hbdPrintRate = globalProps?.hbdPrintRate || 0;
const SBD_PRINT_RATE_MAX = 10000;
const percent_steem_dollars = (content.percent_hbd || 10000) / 20000;
// assemble breakdown
const base = globalProps?.base || 0;
const quote = globalProps?.quote || 0;
const hbdPrintRate = globalProps?.hbdPrintRate || 0;
const SBD_PRINT_RATE_MAX = 10000;
const percent_steem_dollars = (content.percent_hbd || 10000) / 20000;
const pending_payout_hbd = pendingPayout * percent_steem_dollars;
const price_per_steem = base / quote;
const pending_payout_hp = (pendingPayout - pending_payout_hbd) / price_per_steem;
const pending_payout_printed_hbd = pending_payout_hbd * (hbdPrintRate / SBD_PRINT_RATE_MAX);
const pending_payout_printed_hive =
(pending_payout_hbd - pending_payout_printed_hbd) / price_per_steem;
const pending_payout_hbd = pendingPayout * percent_steem_dollars;
const price_per_steem = base / quote;
const breakdownPayout =
(pending_payout_printed_hbd > 0 ? `${pending_payout_printed_hbd.toFixed(3)} HBD\n` : '') +
(pending_payout_printed_hive > 0 ? `${pending_payout_printed_hive.toFixed(3)} HIVE\n` : '') +
(pending_payout_hp > 0 ? `${pending_payout_hp.toFixed(3)} HP` : '');
const beneficiaries = [];
const beneficiary = content?.beneficiaries;
if (beneficiary) {
beneficiary.forEach((key, index) => {
beneficiaries.push(
`${index !== 0 ? '\n' : ''}${key?.account}: ${(parseFloat(key?.weight) / 100).toFixed(2)}%`,
);
});
}
const minimumAmountForPayout = 0.02;
let warnZeroPayout = false;
if (pendingPayout > 0 && pendingPayout < minimumAmountForPayout) {
warnZeroPayout = true;
}
const pending_payout_hp = (pendingPayout - pending_payout_hbd) / price_per_steem;
const pending_payout_printed_hbd = pending_payout_hbd * (hbdPrintRate / SBD_PRINT_RATE_MAX);
const pending_payout_printed_hive =
(pending_payout_hbd - pending_payout_printed_hbd) / price_per_steem;
const breakdownPayout =
(pending_payout_printed_hbd > 0 ? `${pending_payout_printed_hbd.toFixed(3)} HBD\n` : '') +
(pending_payout_printed_hive > 0 ? `${pending_payout_printed_hive.toFixed(3)} HIVE\n` : '') +
(pending_payout_hp > 0 ? `${pending_payout_hp.toFixed(3)} HP` : '');
const beneficiaries = [];
const beneficiary = content?.beneficiaries;
if (beneficiary) {
beneficiary.forEach((key, index) => {
beneficiaries.push(
`${index !== 0 ? '\n' : ''}${key?.account}: ${(
parseFloat(key?.weight) / 100
).toFixed(2)}%`,
);
});
}
const minimumAmountForPayout = 0.02;
let warnZeroPayout = false;
if (pendingPayout > 0 && pendingPayout < minimumAmountForPayout) {
warnZeroPayout = true;
}
const _payoutPopupItem = (label, value) => {
return (
<View style={styles.popoverItemContent}>
<Text style={styles.detailsLabel}>{label}</Text>
<Text style={styles.detailsText}>{value}</Text>
</View>
);
};
const _payoutPopupItem = (label, value) => {
return (
<View style={styles.popoverContent}>
{promotedPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.promoted' }),
<FormattedCurrency value={promotedPayout} isApproximate={true} />,
)}
<View style={styles.popoverItemContent}>
<Text style={styles.detailsLabel}>{label}</Text>
<Text style={styles.detailsText}>{value}</Text>
</View>
);
};
{pendingPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.potential_payout' }),
<FormattedCurrency value={pendingPayout} isApproximate={true} />,
)}
return (
<View style={styles.popoverContent}>
{promotedPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.promoted' }),
<FormattedCurrency value={promotedPayout} isApproximate={true} />,
)}
{authorPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.author_payout' }),
<FormattedCurrency value={authorPayout} isApproximate={true} />,
)}
{pendingPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.potential_payout' }),
<FormattedCurrency value={pendingPayout} isApproximate={true} />,
)}
{curationPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.curation_payout' }),
<FormattedCurrency value={curationPayout} isApproximate={true} />,
)}
{payoutLimitHit &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.max_accepted' }),
<FormattedCurrency value={maxPayout} isApproximate={true} />,
)}
{authorPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.author_payout' }),
<FormattedCurrency value={authorPayout} isApproximate={true} />,
)}
{!!breakdownPayout &&
pendingPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.breakdown' }),
breakdownPayout,
)}
{curationPayout > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.curation_payout' }),
<FormattedCurrency value={curationPayout} isApproximate={true} />,
)}
{payoutLimitHit &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.max_accepted' }),
<FormattedCurrency value={maxPayout} isApproximate={true} />,
)}
{beneficiaries.length > 0 &&
_payoutPopupItem(
intl.formatMessage({ id: 'payout.beneficiaries' }),
beneficiaries,
)}
{!!breakdownPayout &&
pendingPayout > 0 &&
_payoutPopupItem(intl.formatMessage({ id: 'payout.breakdown' }), breakdownPayout)}
{!!payoutDate &&
_payoutPopupItem(intl.formatMessage({ id: 'payout.payout_date' }), payoutDate)}
{beneficiaries.length > 0 &&
_payoutPopupItem(intl.formatMessage({ id: 'payout.beneficiaries' }), beneficiaries)}
{warnZeroPayout &&
_payoutPopupItem(intl.formatMessage({ id: 'payout.warn_zero_payout' }), '')}
</View>
)
}
{!!payoutDate &&
_payoutPopupItem(intl.formatMessage({ id: 'payout.payout_date' }), payoutDate)}
{warnZeroPayout &&
_payoutPopupItem(intl.formatMessage({ id: 'payout.warn_zero_payout' }), '')}
</View>
);
};

View File

@ -1,7 +1,19 @@
import React, { Fragment, useState, useEffect, forwardRef, useImperativeHandle, useRef } from 'react';
import React, {
Fragment,
useState,
useEffect,
forwardRef,
useImperativeHandle,
useRef,
} from 'react';
import get from 'lodash/get';
// Services and Actions
import { Rect } from 'react-native-modal-popover/lib/PopoverGeometry';
import { View, TouchableOpacity, Text, Alert } from 'react-native';
import { Popover } from 'react-native-modal-popover';
import Slider from '@esteemapp/react-native-slider';
import { useIntl } from 'react-intl';
import {
setCommentUpvotePercent,
setPostUpvotePercent,
@ -13,13 +25,8 @@ import { isVoted as isVotedFunc, isDownVoted as isDownVotedFunc } from '../../..
// Component
import { updateVoteCache } from '../../../redux/actions/cacheActions';
import { useAppDispatch, useAppSelector } from '../../../hooks';
import { Rect } from 'react-native-modal-popover/lib/PopoverGeometry';
import { PostTypes } from '../../../constants/postTypes';
import { View, TouchableOpacity, Text, Alert } from 'react-native';
import { Popover } from 'react-native-modal-popover';
import Slider from '@esteemapp/react-native-slider';
// Utils
import { getEstimatedAmount } from '../../../utils/vote';
@ -37,21 +44,19 @@ import { vote } from '../../../providers/hive/dhive';
import styles from '../children/upvoteStyles';
import { PointActivityIds } from '../../../providers/ecency/ecency.types';
import { useIntl } from 'react-intl';
import { useUserActivityMutation } from '../../../providers/queries';
import { PayoutDetailsContent } from '../children/payoutDetailsContent';
import { CacheStatus } from '../../../redux/reducers/cacheReducer';
import showLoginAlert from '../../../utils/showLoginAlert';
import { delay } from '../../../utils/editor';
interface Props { }
interface Props {}
interface PopoverOptions {
anchorRect: Rect,
content: any,
postType?: PostTypes,
showPayoutDetails?: boolean,
onVotingStart?:(isVoting:boolean) => void
anchorRect: Rect;
content: any;
postType?: PostTypes;
showPayoutDetails?: boolean;
onVotingStart?: (isVoting: boolean) => void;
}
/*
@ -60,8 +65,7 @@ interface PopoverOptions {
*
*/
const UpvotePopover = forwardRef(({ }: Props, ref) => {
const UpvotePopover = forwardRef(({}: Props, ref) => {
const intl = useIntl();
const dispatch = useAppDispatch();
@ -69,12 +73,12 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
const onVotingStartRef = useRef<any>(null);
const isLoggedIn = useAppSelector(state => state.application.isLoggedIn);
const postUpvotePercent = useAppSelector(state => state.application.postUpvotePercent);
const commentUpvotePercent = useAppSelector(state => state.application.commentUpvotePercent);
const pinCode = useAppSelector(state => state.application.pin);
const currentAccount = useAppSelector(state => state.account.currentAccount);
const globalProps = useAppSelector(state => state.account.globalProps);
const isLoggedIn = useAppSelector((state) => state.application.isLoggedIn);
const postUpvotePercent = useAppSelector((state) => state.application.postUpvotePercent);
const commentUpvotePercent = useAppSelector((state) => state.application.commentUpvotePercent);
const pinCode = useAppSelector((state) => state.application.pin);
const currentAccount = useAppSelector((state) => state.account.currentAccount);
const globalProps = useAppSelector((state) => state.account.globalProps);
const [content, setContent] = useState<any>(null);
const [postType, setPostType] = useState<PostTypes>(PostTypes.POST);
@ -88,30 +92,26 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
const [amount, setAmount] = useState('0.00000');
const [upvotePercent, setUpvotePercent] = useState(1);
useImperativeHandle(
ref, () => ({
showPopover: ({
anchorRect: _anchorRect,
content: _content,
postType: _postType,
showPayoutDetails: _showPayoutDetails,
onVotingStart
}: PopoverOptions) => {
if (!isLoggedIn && !_showPayoutDetails) {
showLoginAlert({ intl })
return;
}
onVotingStartRef.current = onVotingStart
setPostType(_postType || PostTypes.POST)
setContent(_content);
setShowPayoutDetails(_showPayoutDetails || false)
setAcnhorRect(_anchorRect)
useImperativeHandle(ref, () => ({
showPopover: ({
anchorRect: _anchorRect,
content: _content,
postType: _postType,
showPayoutDetails: _showPayoutDetails,
onVotingStart,
}: PopoverOptions) => {
if (!isLoggedIn && !_showPayoutDetails) {
showLoginAlert({ intl });
return;
}
})
)
onVotingStartRef.current = onVotingStart;
setPostType(_postType || PostTypes.POST);
setContent(_content);
setShowPayoutDetails(_showPayoutDetails || false);
setAcnhorRect(_anchorRect);
},
}));
useEffect(() => {
let _isMounted = true;
@ -128,16 +128,15 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
};
_calculateVoteStatus();
return () => { _isMounted = false };
return () => {
_isMounted = false;
};
}, [content]);
useEffect(() => {
_calculateEstimatedAmount();
}, []);
useEffect(() => {
if (postType === PostTypes.POST) {
setUpvotePercent(postUpvotePercent);
@ -147,8 +146,6 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
}
}, [postUpvotePercent, commentUpvotePercent, postType]);
useEffect(() => {
const value = isVoted || isDownVoted ? 1 : upvotePercent <= 1 ? upvotePercent : 1;
@ -156,8 +153,6 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
_calculateEstimatedAmount(value);
}, [upvotePercent]);
// Component Functions
const _calculateEstimatedAmount = async (value: number = sliderValue) => {
if (currentAccount && Object.entries(currentAccount).length !== 0) {
@ -166,13 +161,12 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
};
const _upvoteContent = async () => {
if (!isDownVoted) {
const _onVotingStart = onVotingStartRef.current; //keeping a reference of call to avoid mismatch in case back to back voting
const _onVotingStart = onVotingStartRef.current; // keeping a reference of call to avoid mismatch in case back to back voting
_closePopover();
_onVotingStart ? _onVotingStart(1) : null;
await delay(300)
await delay(300);
_setUpvotePercent(sliderValue);
@ -181,7 +175,7 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
const _permlink = content?.permlink;
console.log(`casting up vote: ${weight}`);
_updateVoteCache(_author, _permlink, amount, false, CacheStatus.PENDING)
_updateVoteCache(_author, _permlink, amount, false, CacheStatus.PENDING);
vote(currentAccount, pinCode, _author, _permlink, weight)
.then((response) => {
@ -249,13 +243,13 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
};
const _downvoteContent = async () => {
const _onVotingStart = onVotingStartRef.current; //keeping a reference of call to avoid mismatch in case back to back voting
const _onVotingStart = onVotingStartRef.current; // keeping a reference of call to avoid mismatch in case back to back voting
if (isDownVoted) {
_closePopover();
_onVotingStart ? _onVotingStart(-1) : null;
await delay(300)
await delay(300);
_setUpvotePercent(sliderValue);
const weight = sliderValue ? Math.trunc(sliderValue * 100) * -100 : 0;
@ -285,17 +279,12 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
setIsVoted(false);
_onVotingStart ? _onVotingStart(0) : null;
});
} else {
setSliderValue(1);
setIsDownVoted(true);
}
};
const _setUpvotePercent = (value) => {
if (value) {
if (postType === PostTypes.POST) {
@ -307,7 +296,6 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
}
};
const _updateVoteCache = (author, permlink, amount, isDownvote, status) => {
// do all relevant processing here to show local upvote
const amountNum = parseFloat(amount);
@ -325,38 +313,32 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
amount: amountNum,
isDownvote,
incrementStep,
voter:currentAccount.username,
voter: currentAccount.username,
expiresAt: curTime + 30000,
status,
};
dispatch(updateVoteCache(postPath, vote));
};
const _closePopover = () => {
setAcnhorRect(null);
setTimeout(() => {
setShowPayoutDetails(false);
}, 300)
}
}, 300);
};
if (!content) {
return null;
}
let iconName = 'upcircleo';
let downVoteIconName = 'downcircleo';
const iconName = 'upcircleo';
const downVoteIconName = 'downcircleo';
const _percent = `${isDownVoted ? '-' : ''}${(sliderValue * 100).toFixed(0)}%`;
const _amount = `$${amount}`;
const sliderColor = isDownVoted ? '#ec8b88' : '#357ce6';
return (
<Fragment>
<Popover
@ -376,10 +358,7 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
<PayoutDetailsContent content={content} />
) : (
<Fragment>
<TouchableOpacity
onPress={_upvoteContent}
style={styles.upvoteButton}
>
<TouchableOpacity onPress={_upvoteContent} style={styles.upvoteButton}>
<Icon
size={20}
style={[styles.upvoteIcon, { color: '#007ee5' }]}
@ -404,10 +383,7 @@ const UpvotePopover = forwardRef(({ }: Props, ref) => {
}}
/>
<Text style={styles.percent}>{_percent}</Text>
<TouchableOpacity
onPress={_downvoteContent}
style={styles.upvoteButton}
>
<TouchableOpacity onPress={_downvoteContent} style={styles.upvoteButton}>
<Icon
size={20}
style={[styles.upvoteIcon, { color: '#ec8b88' }]}

View File

@ -1,5 +1,4 @@
export enum PostTypes {
POST = 'post',
COMMENT = 'comment',
};
}

View File

@ -2,11 +2,11 @@ import { renderPostBody } from '@ecency/render-helper';
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { useState, useEffect, useMemo } from 'react';
import { Platform } from 'react-native';
import { isArray } from 'lodash';
import { useAppSelector } from '../../../hooks';
import { getDiscussionCollection, getPost } from '../../hive/dhive';
import QUERIES from '../queryKeys';
import { Comment, CacheStatus, LastUpdateMeta } from '../../../redux/reducers/cacheReducer';
import { isArray } from 'lodash';
/** hook used to return user drafts */
export const useGetPostQuery = (_author?: string, _permlink?: string, initialPost?: any) => {
@ -15,41 +15,46 @@ export const useGetPostQuery = (_author?: string, _permlink?: string, initialPos
const [author, setAuthor] = useState(_author);
const [permlink, setPermlink] = useState(_permlink);
//post process initial post if available
// post process initial post if available
const _initialPost = useMemo(() => {
const _post = initialPost;
if (!_post) {
return _post;
}
_post.body = renderPostBody({ ..._post, last_update: _post.updated }, true, Platform.OS !== 'ios');
_post.body = renderPostBody(
{ ..._post, last_update: _post.updated },
true,
Platform.OS !== 'ios',
);
return _post;
}, [initialPost?.body]);
const query = useQuery([QUERIES.POST.GET, author, permlink], async () => {
if (!author || !permlink) {
return null;
}
try {
const post = await getPost(author, permlink, currentAccount?.username);
if (post?.post_id > 0) {
return post;
const query = useQuery(
[QUERIES.POST.GET, author, permlink],
async () => {
if (!author || !permlink) {
return null;
}
new Error('Post unavailable');
} catch (err) {
console.warn('Failed to get post', err);
throw err;
}
}, {
initialData: _initialPost,
cacheTime: 30 * 60 * 1000 //keeps cache for 30 minutes
});
try {
const post = await getPost(author, permlink, currentAccount?.username);
if (post?.post_id > 0) {
return post;
}
new Error('Post unavailable');
} catch (err) {
console.warn('Failed to get post', err);
throw err;
}
},
{
initialData: _initialPost,
cacheTime: 30 * 60 * 1000, // keeps cache for 30 minutes
},
);
const data = useInjectVotesCache(query.data);
@ -96,7 +101,7 @@ export const useDiscussionQuery = (_author?: string, _permlink?: string) => {
(state) => state.cache.commentsCollection,
);
const cachedVotes: { [key: string]: Comment } = useAppSelector(
(state) => state.cache.votesCollection
(state) => state.cache.votesCollection,
);
const lastCacheUpdate: LastUpdateMeta = useAppSelector((state) => state.cache.lastUpdate);
@ -110,9 +115,9 @@ export const useDiscussionQuery = (_author?: string, _permlink?: string) => {
const query = useQuery<{ [key: string]: Comment }>(
[QUERIES.POST.GET_DISCUSSION, author, permlink],
_fetchComments,
{
cacheTime: 5 * 60 * 1000 //keeps comments cache for 5 minutes
}
{
cacheTime: 5 * 60 * 1000, // keeps comments cache for 5 minutes
},
);
useEffect(() => {
@ -129,20 +134,20 @@ export const useDiscussionQuery = (_author?: string, _permlink?: string) => {
const _comments = query.data || {};
console.log('updating with cache', _comments, cachedComments);
if (!cachedComments || !_comments) {
console.log("Skipping cache injection")
console.log('Skipping cache injection');
return _comments;
}
//process votes cache
for (const path in cachedVotes){
// process votes cache
for (const path in cachedVotes) {
const cachedVote = cachedVotes[path];
if(_comments[path]){
console.log("injection vote cache")
_comments[path] = _injectVoteFunc(_comments[path], cachedVote)
if (_comments[path]) {
console.log('injection vote cache');
_comments[path] = _injectVoteFunc(_comments[path], cachedVote);
}
}
//process comments cache
// process comments cache
for (const path in cachedComments) {
const currentTime = new Date().getTime();
const cachedComment = cachedComments[path];
@ -158,7 +163,7 @@ export const useDiscussionQuery = (_author?: string, _permlink?: string) => {
break;
case CacheStatus.UPDATED:
case CacheStatus.PENDING:
//check if commentKey already exist in comments map,
// check if commentKey already exist in comments map,
if (_comments[path]) {
// check if we should update comments map with cached map based on updat timestamp
const remoteUpdateTimestamp = new Date(_comments[path].updated).getTime();
@ -262,57 +267,49 @@ export const useDiscussionQuery = (_author?: string, _permlink?: string) => {
};
};
/**
*
* @param _data single post content or array of posts
* @returns post data or array of data with votes cache injected
*/
*
* @param _data single post content or array of posts
* @returns post data or array of data with votes cache injected
*/
export const useInjectVotesCache = (_data: any | any[]) => {
const votesCollection = useAppSelector((state) => state.cache.votesCollection);
const lastUpdate = useAppSelector((state) => state.cache.lastUpdate);
const [retData, setRetData] = useState<any | any[] | null>(null);
useEffect(() => {
if (retData && lastUpdate.type === 'vote') {
const _postPath = lastUpdate.postPath;
const _voteCache = votesCollection[_postPath];
let _postData:any = null;
let _postData: any = null;
let _postIndex = -1;
//get post data that need updating
const _comparePath = (item) => _postPath === `${item.author}/${item.permlink}`
// get post data that need updating
const _comparePath = (item) => _postPath === `${item.author}/${item.permlink}`;
if (isArray(retData)) {
_postIndex = retData.findIndex(_comparePath);
_postData = retData[_postIndex]
_postData = retData[_postIndex];
} else if (retData && _comparePath(retData)) {
_postData = retData;
}
//if post available, inject cache and update state
// if post available, inject cache and update state
if (_postData) {
_postData = _injectVoteFunc(_postData, _voteCache);
if (_postIndex < 0) {
console.log("updating data", _postData)
console.log('updating data', _postData);
setRetData({ ..._postData });
} else {
retData[_postIndex] = _postData
setRetData([...retData])
retData[_postIndex] = _postData;
setRetData([...retData]);
}
}
}
}, [votesCollection])
}, [votesCollection]);
useEffect(() => {
if (!_data) {
setRetData(null);
return;
@ -321,41 +318,41 @@ export const useInjectVotesCache = (_data: any | any[]) => {
const _itemFunc = (item) => {
if (item) {
const _path = `${item.author}/${item.permlink}`;
const voteCache = votesCollection[_path]
const voteCache = votesCollection[_path];
item = _injectVoteFunc(item, voteCache);
}
return item;
}
const _cData = isArray(_data) ? _data.map(_itemFunc) : _itemFunc({ ..._data })
console.log("data received", _cData.length, _cData);
setRetData(_cData)
}, [_data])
};
const _cData = isArray(_data) ? _data.map(_itemFunc) : _itemFunc({ ..._data });
console.log('data received', _cData.length, _cData);
setRetData(_cData);
}, [_data]);
return retData || _data;
}
};
const _injectVoteFunc = (post, voteCache) => {
if (voteCache && (voteCache.status !== CacheStatus.FAILED || voteCache.status !== CacheStatus.DELETED)) {
const _voteIndex = post.active_votes.findIndex(i => i.voter === voteCache.voter);
if (
voteCache &&
(voteCache.status !== CacheStatus.FAILED || voteCache.status !== CacheStatus.DELETED)
) {
const _voteIndex = post.active_votes.findIndex((i) => i.voter === voteCache.voter);
if (_voteIndex < 0) {
post.total_payout += voteCache.amount * (voteCache.isDownvote ? -1 : 1);
post.active_votes = [...post.active_votes, {
voter: voteCache.voter,
rshares: voteCache.isDownvote ? -1000 : 1000
}]
post.active_votes = [
...post.active_votes,
{
voter: voteCache.voter,
rshares: voteCache.isDownvote ? -1000 : 1000,
},
];
} else {
post.active_votes[_voteIndex].rshares = voteCache.isDownvote ? -1000 : 1000;
post.active_votes = [...post.active_votes];
}
}
return post
}
return post;
};

View File

@ -52,7 +52,7 @@ export interface Comment {
updated?: string;
expiresAt?: number;
expandedReplies?: boolean;
renderOnTop?:boolean;
renderOnTop?: boolean;
status: CacheStatus;
}
@ -89,8 +89,8 @@ export interface LastUpdateMeta {
}
interface State {
votesCollection:{[key: string]:Vote};
commentsCollection:{ [key: string]: Comment}; // TODO: handle comment array per post, if parent is same
votesCollection: { [key: string]: Vote };
commentsCollection: { [key: string]: Comment }; // TODO: handle comment array per post, if parent is same
draftsCollection: { [key: string]: Draft };
claimsCollection: ClaimsCollection;
subscribedCommunities: Map<string, SubscribedCommunity>;
@ -115,7 +115,7 @@ export default function (state = initialState, action) {
if (!state.votesCollection) {
state.votesCollection = {};
}
state.votesCollection = {...state.votesCollection, [payload.postPath]:payload.vote};
state.votesCollection = { ...state.votesCollection, [payload.postPath]: payload.vote };
return {
...state, // spread operator in requried here, otherwise persist do not register change
lastUpdate: {
@ -252,7 +252,7 @@ export default function (state = initialState, action) {
for (const key in state.votesCollection) {
if (state.votesCollection.hasOwnProperty(key)) {
const vote = state.votesCollection[key];
if (vote && ((vote?.expiresAt || 0) < currentTime)) {
if (vote && (vote?.expiresAt || 0) < currentTime) {
delete state.votesCollection[key];
}
}
@ -263,7 +263,7 @@ export default function (state = initialState, action) {
for (const key in state.commentsCollection) {
if (state.commentsCollection.hasOwnProperty(key)) {
const comment = state.commentsCollection[key];
if (comment && ((comment?.expiresAt || 0) < currentTime)) {
if (comment && (comment?.expiresAt || 0) < currentTime) {
delete state.commentsCollection[key];
}
}

View File

@ -54,7 +54,7 @@ export const useInitApplication = () => {
});
useEffect(() => {
BackgroundTimer.start() //ref: https://github.com/ocetnik/react-native-background-timer#ios
BackgroundTimer.start(); // ref: https://github.com/ocetnik/react-native-background-timer#ios
appStateSubRef.current = AppState.addEventListener('change', _handleAppStateChange);
@ -101,7 +101,7 @@ export const useInitApplication = () => {
messagingEventRef.current();
}
BackgroundTimer.stop(); //ref: https://github.com/ocetnik/react-native-background-timer#ios
BackgroundTimer.stop(); // ref: https://github.com/ocetnik/react-native-background-timer#ios
};
const _initPushListener = async () => {

View File

@ -5,18 +5,12 @@ import { View } from 'react-native';
import { BasicHeader, IconButton, PostDisplay, PostOptionsModal } from '../../../components';
import styles from '../styles/postScreen.styles';
// Component
import { postQueries } from '../../../providers/queries';
const PostScreen = ({
route
}) => {
const PostScreen = ({ route }) => {
const params = route.params || {};
// // refs
const isNewPost = useRef(route.params?.isNewPost).current;
const postOptionsModalRef = useRef<typeof PostOptionsModal | null>(null);
@ -24,11 +18,9 @@ const PostScreen = ({
const [author, setAuthor] = useState(params.content?.author || params.author);
const [permlink, setPermlink] = useState(params.content?.permlink || params.permlink);
const getPostQuery = postQueries.useGetPostQuery(author, permlink, params.content);
const getParentPostQuery = postQueries.useGetPostQuery();
useEffect(() => {
const post = getPostQuery.data;
if (post) {
@ -50,12 +42,11 @@ const PostScreen = ({
const _isPostUnavailable = !getPostQuery.isLoading && getPostQuery.error;
const _onPostOptionsBtnPress = (content = getPostQuery.data) => {
if (postOptionsModalRef.current) {
postOptionsModalRef.current.show(content);
}
}
};
const _postOptionsBtn = (
<IconButton
@ -65,12 +56,10 @@ const PostScreen = ({
onPress={_onPostOptionsBtnPress}
size={24}
/>
)
);
return (
<View style={styles.container} >
<View style={styles.container}>
<BasicHeader
isHasDropdown={true}
title="Post"
@ -88,9 +77,7 @@ const PostScreen = ({
parentPost={getParentPostQuery.data}
post={getPostQuery.data}
/>
<PostOptionsModal
ref={postOptionsModalRef}
/>
<PostOptionsModal ref={postOptionsModalRef} />
</View>
);
};

View File

@ -1,10 +1,10 @@
import EStyleSheet from 'react-native-extended-stylesheet';
export default EStyleSheet.create({
container:{
flex:1,
},
optionsIcon:{
color:'$iconColor'
}
container: {
flex: 1,
},
optionsIcon: {
color: '$iconColor',
},
});

View File

@ -22,15 +22,13 @@ import { showProfileModal } from '../../../../../../redux/actions/uiAction';
const filterOptions = ['relevance', 'popularity', 'newest'];
const PostsResults = ({ navigation, searchValue }) => {
const dispatch = useAppDispatch();
const _showProfileModal = (username) => {
if(username){
dispatch(showProfileModal(username))
if (username) {
dispatch(showProfileModal(username));
}
}
};
const _renderItem = (item, index) => {
const reputation =

View File

@ -1,5 +1,13 @@
import React, { Component, Fragment } from 'react';
import { View, Text, Platform, ScrollView, KeyboardAvoidingView, Alert, TouchableOpacity } from 'react-native';
import {
View,
Text,
Platform,
ScrollView,
KeyboardAvoidingView,
Alert,
TouchableOpacity,
} from 'react-native';
import { WebView } from 'react-native-webview';
import { injectIntl } from 'react-intl';
import Slider from '@esteemapp/react-native-slider';