Merge branch 'development' of https://github.com/ecency/ecency-mobile into development

This commit is contained in:
feruz 2020-09-07 09:43:52 +03:00
commit c620ce16a6
15 changed files with 470 additions and 448 deletions

View File

@ -696,4 +696,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 84e32ce5543427579b8c72d77fd175fe29268d92
COCOAPODS: 1.9.3
COCOAPODS: 1.8.4

View File

@ -1,12 +1,16 @@
import React, { Fragment, useState, useRef } from 'react';
import React, { Fragment, useState, useRef, useEffect } from 'react';
import { View } from 'react-native';
import ActionSheet from 'react-native-actionsheet';
import { useIntl } from 'react-intl';
import get from 'lodash/get';
import { getTimeFromNow } from '../../../utils/time';
import { parseActiveVotes } from '../../../utils/postParser';
// Constants
// Actions
import { getActiveVotes } from '../../../providers/steem/dsteem';
// Components
import { CommentBody, PostHeaderDescription } from '../../postElements';
import { Upvote } from '../../upvote';
@ -33,7 +37,6 @@ const CommentView = ({
isShowComments,
isShowMoreButton,
marginLeft,
voteCount,
mainAuthor = { mainAuthor },
isHideImage,
showAllComments,
@ -42,8 +45,20 @@ const CommentView = ({
}) => {
const [_isShowSubComments, setIsShowSubComments] = useState(isShowSubComments || false);
const [isPressedShowButton, setIsPressedShowButton] = useState(false);
const [activeVotes, setActiveVotes] = useState([]);
const intl = useIntl();
const actionSheet = useRef(null);
useEffect(() => {
getActiveVotes(get(comment, 'author'), get(comment, 'permlink')).then((result) => {
result.sort((a, b) => b.rshares - a.rshares);
const _votes = parseActiveVotes({ ...comment, active_votes: result }, currentAccountUsername);
setActiveVotes(_votes);
});
}, [comment]);
const _showSubCommentsToggle = () => {
setIsShowSubComments(!_isShowSubComments);
setIsPressedShowButton(true);
@ -76,7 +91,7 @@ const CommentView = ({
<View style={styles.footerWrapper}>
{isLoggedIn && (
<Fragment>
<Upvote isShowPayoutValue content={comment} />
<Upvote activeVotes={activeVotes} isShowPayoutValue content={comment} />
<TextWithIcon
iconName="heart-outline"
iconSize={20}
@ -85,10 +100,10 @@ const CommentView = ({
isClickable
onPress={() =>
handleOnVotersPress &&
voteCount > 0 &&
handleOnVotersPress(get(comment, 'active_votes'))
activeVotes.length > 0 &&
handleOnVotersPress(activeVotes)
}
text={voteCount}
text={activeVotes.length}
textStyle={styles.voteCountText}
/>
<IconButton
@ -117,7 +132,7 @@ const CommentView = ({
onPress={() => handleOnEditPress && handleOnEditPress(comment)}
iconType="MaterialIcons"
/>
{!comment.children && !voteCount && (
{!comment.children && !activeVotes.length && (
<Fragment>
<IconButton
size={20}

View File

@ -1,5 +1,5 @@
import React, { useState, Fragment, useRef } from 'react';
import { FlatList, RefreshControl, ActivityIndicator } from 'react-native';
import { FlatList } from 'react-native';
import ActionSheet from 'react-native-actionsheet';
import get from 'lodash/get';
import { useIntl } from 'react-intl';
@ -93,7 +93,6 @@ const CommentsView = ({
isLoggedIn={isLoggedIn}
isShowMoreButton={commentNumber === 1 && get(item, 'children') > 0}
showAllComments={showAllComments}
voteCount={get(item, 'vote_count')}
isShowSubComments={isShowSubComments}
key={get(item, 'permlink')}
marginLeft={marginLeft}

View File

@ -1,10 +1,13 @@
import React, { PureComponent } from 'react';
import React, { useState, useEffect } from 'react';
import { withNavigation } from 'react-navigation';
import { connect } from 'react-redux';
import get from 'lodash/get';
// Services
import { getPost } from '../../../providers/steem/dsteem';
import { getPost, getActiveVotes } from '../../../providers/steem/dsteem';
import { getPostReblogs } from '../../../providers/esteem/esteem';
import { parseActiveVotes } from '../../../utils/postParser';
import PostCardView from '../view/postCardView';
@ -16,16 +19,41 @@ import { default as ROUTES } from '../../../constants/routeNames';
*
*/
class PostCardContainer extends PureComponent {
constructor(props) {
super(props);
this.state = {
_content: null,
};
}
const PostCardContainer = ({
isRefresh,
navigation,
currentAccount,
content,
isHideImage,
nsfw,
}) => {
const [activeVotes, setActiveVotes] = useState([]);
const [reblogs, setReblogs] = useState([]);
const [_content, setContent] = useState(null);
_handleOnUserPress = () => {
const { navigation, currentAccount, content } = this.props;
useEffect(() => {
if (isRefresh) {
_fetchPost();
}
}, [isRefresh]);
useEffect(() => {
getActiveVotes(get(content, 'author'), get(content, 'permlink')).then((result) => {
result.sort((a, b) => b.rshares - a.rshares);
const _votes = parseActiveVotes(
{ ...content, active_votes: result },
get(currentAccount, 'name'),
);
setActiveVotes(_votes);
});
getPostReblogs(content).then((result) => {
setReblogs(result);
});
}, [content]);
const _handleOnUserPress = () => {
if (content && get(currentAccount, 'name') !== get(content, 'author')) {
navigation.navigate({
routeName: ROUTES.SCREENS.PROFILE,
@ -38,23 +66,19 @@ class PostCardContainer extends PureComponent {
}
};
_handleOnContentPress = (content) => {
const { navigation } = this.props;
if (content) {
const _handleOnContentPress = (value) => {
if (value) {
navigation.navigate({
routeName: ROUTES.SCREENS.POST,
params: {
content,
content: value,
},
key: get(content, 'permlink'),
key: get(value, 'permlink'),
});
}
};
_handleOnVotersPress = (activeVotes) => {
const { navigation, content } = this.props;
const _handleOnVotersPress = () => {
navigation.navigate({
routeName: ROUTES.SCREENS.VOTERS,
params: {
@ -64,9 +88,7 @@ class PostCardContainer extends PureComponent {
});
};
_handleOnReblogsPress = (reblogs) => {
const { navigation, content } = this.props;
const _handleOnReblogsPress = () => {
navigation.navigate({
routeName: ROUTES.SCREENS.REBLOGS,
params: {
@ -76,44 +98,31 @@ class PostCardContainer extends PureComponent {
});
};
_fetchPost = async () => {
const { currentAccount, content } = this.props;
const _fetchPost = async () => {
await getPost(get(content, 'author'), get(content, 'permlink'), get(currentAccount, 'username'))
.then((result) => {
if (result) {
this.setState({ _content: result });
setContent(result);
}
})
.catch(() => {});
};
UNSAFE_componentWillReceiveProps(nextProps) {
if (get(nextProps, 'isRefresh')) {
this._fetchPost();
}
}
render() {
const { content, isHideImage, nsfw } = this.props;
const { _content } = this.state;
const isNsfwPost = nsfw === '1';
return (
<PostCardView
handleOnUserPress={this._handleOnUserPress}
handleOnContentPress={this._handleOnContentPress}
handleOnVotersPress={this._handleOnVotersPress}
handleOnReblogsPress={this._handleOnReblogsPress}
fetchPost={this._fetchPost}
content={_content || content}
isHideImage={isHideImage}
isNsfwPost={isNsfwPost}
/>
);
}
}
return (
<PostCardView
handleOnUserPress={_handleOnUserPress}
handleOnContentPress={_handleOnContentPress}
handleOnVotersPress={_handleOnVotersPress}
handleOnReblogsPress={_handleOnReblogsPress}
fetchPost={_fetchPost}
content={_content || content}
isHideImage={isHideImage}
isNsfwPost={nsfw === '1'}
activeVotes={activeVotes}
reblogs={reblogs}
/>
);
};
const mapStateToProps = (state) => ({
currentAccount: state.account.currentAccount,

View File

@ -53,16 +53,16 @@ class PostCardView extends Component {
};
_handleOnVotersPress = () => {
const { handleOnVotersPress, content } = this.props;
const { handleOnVotersPress } = this.props;
handleOnVotersPress(get(content, 'active_votes'));
handleOnVotersPress();
};
_handleOnReblogsPress = () => {
const { handleOnReblogsPress, content } = this.props;
const { handleOnReblogsPress, reblogs } = this.props;
if (content.reblogs.length > 0) {
handleOnReblogsPress(get(content, 'reblogs'));
if (reblogs.length > 0) {
handleOnReblogsPress();
}
};
@ -88,7 +88,7 @@ class PostCardView extends Component {
}
render() {
const { content, isHideImage, fetchPost, isNsfwPost, intl } = this.props;
const { content, isHideImage, fetchPost, isNsfwPost, intl, activeVotes, reblogs } = this.props;
const { rebloggedBy } = this.state;
const _image = this._getPostImage(content, isNsfwPost);
@ -131,14 +131,19 @@ class PostCardView extends Component {
</View>
<View style={styles.bodyFooter}>
<View style={styles.leftFooterWrapper}>
<Upvote fetchPost={fetchPost} isShowPayoutValue content={content} />
<Upvote
activeVotes={activeVotes}
fetchPost={fetchPost}
isShowPayoutValue
content={content}
/>
<TouchableOpacity style={styles.commentButton} onPress={this._handleOnVotersPress}>
<TextWithIcon
iconName="heart-outline"
iconStyle={styles.commentIcon}
iconType="MaterialCommunityIcons"
isClickable
text={get(content, 'vote_count', 0)}
text={activeVotes.length}
onPress={this._handleOnVotersPress}
/>
</TouchableOpacity>
@ -149,7 +154,7 @@ class PostCardView extends Component {
iconStyle={styles.commentIcon}
iconType="MaterialIcons"
isClickable
text={get(content, 'reblogCount', 0)}
text={reblogs.length}
onPress={this._handleOnReblogsPress}
/>
<TextWithIcon

View File

@ -1,18 +1,21 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { withNavigation } from 'react-navigation';
import { connect } from 'react-redux';
import { injectIntl } from 'react-intl';
import get from 'lodash/get';
// Action
import { toastNotification } from '../../../redux/actions/uiAction';
// Dsteem
import { deleteComment } from '../../../providers/steem/dsteem';
import { deleteComment, getActiveVotes } from '../../../providers/steem/dsteem';
import { getPostReblogs } from '../../../providers/esteem/esteem';
// Constants
import { default as ROUTES } from '../../../constants/routeNames';
// Utilities
import { parseActiveVotes } from '../../../utils/postParser';
// Component
import PostDisplayView from '../view/postDisplayView';
@ -32,8 +35,34 @@ const PostDisplayContainer = ({
isPostUnavailable,
author,
}) => {
const [activeVotes, setActiveVotes] = useState([]);
const [reblogs, setReblogs] = useState([]);
useEffect(() => {
if (post) {
getActiveVotes(get(post, 'author'), get(post, 'permlink')).then((result) => {
result.sort((a, b) => b.rshares - a.rshares);
const _votes = parseActiveVotes(
{ ...post, active_votes: result },
get(currentAccount, 'name'),
);
setActiveVotes(_votes);
});
getPostReblogs(post).then((result) => {
setReblogs(result);
});
}
}, [post]);
useEffect(() => {
_fetchPost();
}, [isFetchPost]);
// Component Functions
const _handleOnVotersPress = (activeVotes) => {
const _handleOnVotersPress = () => {
navigation.navigate({
routeName: ROUTES.SCREENS.VOTERS,
params: {
@ -44,7 +73,7 @@ const PostDisplayContainer = ({
});
};
const _handleOnReblogsPress = (reblogs) => {
const _handleOnReblogsPress = () => {
if (reblogs.length > 0) {
navigation.navigate({
routeName: ROUTES.SCREENS.REBLOGS,
@ -102,10 +131,6 @@ const PostDisplayContainer = ({
}
};
useEffect(() => {
_fetchPost();
}, [isFetchPost]);
return (
<PostDisplayView
author={author}
@ -121,6 +146,8 @@ const PostDisplayContainer = ({
isPostUnavailable={isPostUnavailable}
parentPost={parentPost}
post={post}
activeVotes={activeVotes}
reblogs={reblogs}
/>
);
};

View File

@ -38,6 +38,8 @@ const PostDisplayView = ({
isPostUnavailable,
author,
handleOnRemovePress,
activeVotes,
reblogs,
}) => {
const [postHeight, setPostHeight] = useState(0);
const [scrollHeight, setScrollHeight] = useState(0);
@ -70,19 +72,30 @@ const PostDisplayView = ({
setPostHeight(height);
};
const _handleOnReblogsPress = () => {
if (reblogs.length > 0 && handleOnReblogsPress) {
handleOnReblogsPress();
}
};
const _getTabBar = (isFixedFooter = false) => {
return (
<SafeAreaView>
<StickyBar isFixedFooter={isFixedFooter}>
<View style={styles.stickyWrapper}>
<Upvote fetchPost={fetchPost} isShowPayoutValue content={post} />
<Upvote
activeVotes={activeVotes}
fetchPost={fetchPost}
isShowPayoutValue
content={post}
/>
<TextWithIcon
iconName="heart-outline"
iconStyle={styles.barIcons}
iconType="MaterialCommunityIcons"
isClickable
onPress={() => handleOnVotersPress && handleOnVotersPress(get(post, 'active_votes'))}
text={get(post, 'vote_count', 0)}
onPress={() => handleOnVotersPress && handleOnVotersPress()}
text={activeVotes.length}
textMarginLeft={20}
/>
<TextWithIcon
@ -90,8 +103,8 @@ const PostDisplayView = ({
iconStyle={styles.barIcons}
iconType="MaterialIcons"
isClickable
onPress={() => handleOnReblogsPress && handleOnReblogsPress(get(post, 'reblogs'))}
text={get(post, 'reblogCount', 0)}
onPress={_handleOnReblogsPress}
text={reblogs.length}
textMarginLeft={20}
/>
{isLoggedIn && (
@ -118,7 +131,7 @@ const PostDisplayView = ({
<View style={styles.stickyRightWrapper}>
{get(currentAccount, 'name') === get(post, 'author') && (
<Fragment>
{!get(post, 'children') && !get(post, 'vote_count') && (
{!get(post, 'children') && !activeVotes.length && (
<IconButton
iconStyle={styles.barIconRight}
iconType="MaterialIcons"

View File

@ -1,77 +1,268 @@
import React from 'react';
import { connect, useDispatch } from 'react-redux';
import React, { useCallback, useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import get from 'lodash/get';
import unionBy from 'lodash/unionBy';
// STEEM
import { getPostsSummary, getPost } from '../../../providers/steem/dsteem';
import { getPromotePosts } from '../../../providers/esteem/esteem';
// Component
import PostsView from '../view/postsView';
// Container
import { AccountContainer } from '../../../containers';
// Actions
import { setFeedPosts } from '../../../redux/actions/postsAction';
import { hidePostsThumbnails } from '../../../redux/actions/uiAction';
let _isLoadingPost = false;
const PostsContainer = ({
changeForceLoadPostState,
feedPosts,
filterOptions,
forceLoadPost,
getFor,
handleOnScroll,
isConnected,
isHideImages,
pageType,
selectedOptionIndex,
tag,
nsfw,
filterOptionsValue,
feedUsername,
}) => {
const dispatch = useDispatch();
const _setFeedPosts = (posts) => {
dispatch(setFeedPosts(posts));
const nsfw = useSelector((state) => state.application.nsfw);
const feedPosts = useSelector((state) => state.posts.feedPosts);
const isConnected = useSelector((state) => state.application.isConnected);
const isHideImages = useSelector((state) => state.ui.hidePostsThumbnails);
const username = useSelector((state) => state.account.currentAccount.name);
const isLoggedIn = useSelector((state) => state.application.isLoggedIn);
const [isNoPost, setIsNoPost] = useState(false);
const [startPermlink, setStartPermlink] = useState('');
const [startAuthor, setStartAuthor] = useState('');
const [promotedPosts, setPromotedPosts] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const [posts, setPosts] = useState(isConnected ? [] : feedPosts);
const [selectedFilterIndex, setSelectedFilterIndex] = useState(selectedOptionIndex || 0);
const [selectedFilterValue, setSelectedFilterValue] = useState(
filterOptionsValue && filterOptionsValue[selectedFilterIndex],
);
useEffect(() => {
if (isConnected) {
_getPromotePosts();
_loadPosts();
}
}, [
_getPromotePosts,
_loadPosts,
changeForceLoadPostState,
username,
forceLoadPost,
isConnected,
pageType,
selectedOptionIndex,
]);
useEffect(() => {
if (forceLoadPost) {
setPosts([]);
setStartAuthor('');
setStartPermlink('');
setSelectedFilterIndex(selectedOptionIndex || 0);
setIsNoPost(false);
_loadPosts();
if (changeForceLoadPostState) {
changeForceLoadPostState(false);
}
}
}, [_loadPosts, changeForceLoadPostState, username, forceLoadPost, selectedOptionIndex]);
useEffect(() => {
if (!startAuthor && !startPermlink) {
_loadPosts(selectedFilterValue);
}
}, [_loadPosts, selectedFilterValue]);
useEffect(() => {
if (refreshing) {
_loadPosts();
}
}, [refreshing]);
const _setFeedPosts = (_posts) => {
dispatch(setFeedPosts(_posts));
};
const _handleImagesHide = () => {
dispatch(hidePostsThumbnails(!isHideImages));
};
const _getPromotePosts = useCallback(async () => {
if (pageType === 'profiles') {
return;
}
await getPromotePosts()
.then(async (res) => {
if (res && res.length) {
const _promotedPosts = await Promise.all(
res.map((item) =>
getPost(get(item, 'author'), get(item, 'permlink'), username, true).then(
(post) => post,
),
),
);
setPromotedPosts(_promotedPosts);
}
})
.catch(() => {});
}, [username]);
const _loadPosts = useCallback(
(type) => {
if (
_isLoadingPost ||
isLoading ||
!isConnected ||
(!isLoggedIn && type === 'feed') ||
(!isLoggedIn && type === 'blog')
) {
return;
}
setIsLoading(true);
_isLoadingPost = true;
if (!isConnected && (refreshing || isLoading)) {
setRefreshing(false);
setIsLoading(false);
_isLoadingPost = false;
return;
}
const filter = type || selectedFilterValue;
let options;
const limit = 6;
if (filter === 'feed' || filter === 'blog' || getFor === 'blog' || filter === 'reblogs') {
options = {
tag: feedUsername,
limit,
};
} else {
options = {
tag,
limit,
};
}
if (startAuthor && startPermlink && !refreshing) {
options.start_author = startAuthor;
options.start_permlink = startPermlink;
}
// options.truncate_body = 200;
getPostsSummary(filter, options, username, nsfw)
.then((result) => {
if (result.length > 0) {
let _posts = result;
if (filter === 'reblogs') {
for (let i = _posts.length - 1; i >= 0; i--) {
if (_posts[i].author === username) {
_posts.splice(i, 1);
}
}
}
if (_posts.length > 0) {
if (posts.length > 0) {
if (refreshing) {
_posts = unionBy(_posts, posts, 'permlink');
} else {
_posts = unionBy(posts, _posts, 'permlink');
}
}
if (posts.length < 7 && pageType !== 'profiles') {
_setFeedPosts(_posts);
}
if (!refreshing) {
setStartAuthor(result[result.length - 1] && result[result.length - 1].author);
setStartPermlink(result[result.length - 1] && result[result.length - 1].permlink);
}
setPosts(_posts);
}
} else if (result.length === 0) {
setIsNoPost(true);
}
setRefreshing(false);
setIsLoading(false);
_isLoadingPost = false;
})
.catch(() => {
setRefreshing(false);
setIsLoading(false);
_isLoadingPost = false;
});
},
[
username,
getFor,
isConnected,
isLoading,
isLoggedIn,
nsfw,
pageType,
posts,
refreshing,
selectedFilterValue,
setFeedPosts,
startAuthor,
startPermlink,
tag,
],
);
const _handleOnRefreshPosts = () => {
setRefreshing(true);
_getPromotePosts();
};
const _handleOnDropdownSelect = (index) => {
setSelectedFilterIndex(index);
setPosts([]);
setStartPermlink('');
setStartAuthor('');
setIsNoPost(false);
};
return (
<AccountContainer>
{({ username, isLoggedIn, isLoginDone }) => (
<PostsView
changeForceLoadPostState={changeForceLoadPostState}
currentAccountUsername={username}
feedPosts={feedPosts}
filterOptions={filterOptions}
forceLoadPost={forceLoadPost}
getFor={getFor}
handleImagesHide={_handleImagesHide}
handleOnScroll={handleOnScroll}
hidePostsThumbnails={hidePostsThumbnails}
isConnected={isConnected}
isHideImage={isHideImages}
isLoggedIn={isLoggedIn}
isLoginDone={isLoginDone}
nsfw={nsfw}
pageType={pageType}
selectedOptionIndex={selectedOptionIndex}
setFeedPosts={_setFeedPosts}
tag={tag}
filterOptionsValue={filterOptionsValue}
feedUsername={feedUsername}
/>
)}
</AccountContainer>
<PostsView
filterOptions={filterOptions}
handleImagesHide={_handleImagesHide}
handleOnScroll={handleOnScroll}
isHideImage={isHideImages}
isLoggedIn={isLoggedIn}
selectedOptionIndex={selectedOptionIndex}
tag={tag}
filterOptionsValue={filterOptionsValue}
posts={posts}
isLoading={isLoading}
refreshing={refreshing}
selectedFilterIndex={selectedFilterIndex}
isNoPost={isNoPost}
promotedPosts={promotedPosts}
selectedFilterValue={selectedFilterValue}
setSelectedFilterValue={setSelectedFilterValue}
handleOnDropdownSelect={_handleOnDropdownSelect}
loadPosts={_loadPosts}
handleOnRefreshPosts={_handleOnRefreshPosts}
/>
);
};
const mapStateToProps = (state) => ({
nsfw: state.application.nsfw,
feedPosts: state.posts.feedPosts,
isConnected: state.application.isConnected,
isHideImages: state.ui.hidePostsThumbnails,
});
export default connect(mapStateToProps)(PostsContainer);
export default PostsContainer;

View File

@ -1,13 +1,9 @@
/* eslint-disable react/jsx-wrap-multilines */
import React, { useState, useEffect, useCallback, useRef } from 'react';
import React, { useRef } from 'react';
import { FlatList, View, ActivityIndicator, RefreshControl } from 'react-native';
import { useIntl } from 'react-intl';
import { withNavigation } from 'react-navigation';
import { get, unionBy } from 'lodash';
// STEEM
import { getPostsSummary, getPost } from '../../../providers/steem/dsteem';
import { getPromotePosts } from '../../../providers/esteem/esteem';
import { get } from 'lodash';
// COMPONENTS
import { PostCard } from '../../postCard';
@ -19,253 +15,45 @@ import { ThemeContainer } from '../../../containers';
import styles from './postsStyles';
import { default as ROUTES } from '../../../constants/routeNames';
let _onEndReachedCalledDuringMomentum = true;
const PostsView = ({
filterOptions,
selectedOptionIndex,
isHideImage,
handleImagesHide,
feedPosts,
isConnected,
currentAccountUsername,
getFor,
tag,
nsfw,
setFeedPosts,
pageType,
isLoginDone,
isLoggedIn,
handleOnScroll,
navigation,
changeForceLoadPostState,
forceLoadPost,
posts,
isLoading,
refreshing,
selectedFilterIndex,
isNoPost,
promotedPosts,
selectedFilterValue,
setSelectedFilterValue,
filterOptionsValue,
feedUsername,
handleOnDropdownSelect,
handleOnRefreshPosts,
loadPosts,
}) => {
const [posts, setPosts] = useState(isConnected ? [] : feedPosts);
const [startAuthor, setStartAuthor] = useState('');
const [startPermlink, setStartPermlink] = useState('');
const [refreshing, setRefreshing] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [selectedFilterIndex, setSelectedFilterIndex] = useState(selectedOptionIndex || 0);
const [isNoPost, setIsNoPost] = useState(false);
const [promotedPosts, setPromotedPosts] = useState([]);
const [selectedFilterValue, setSelectedFilterValue] = useState(
filterOptionsValue && filterOptionsValue[selectedFilterIndex],
);
const intl = useIntl();
const postsList = useRef(null);
useEffect(() => {
if (isConnected) {
const fetchPromotePost = async () => {
if (pageType !== 'profiles') {
await _getPromotePosts();
}
};
fetchPromotePost();
_loadPosts();
}
}, [
_getPromotePosts,
_loadPosts,
changeForceLoadPostState,
currentAccountUsername,
forceLoadPost,
isConnected,
pageType,
selectedOptionIndex,
]);
useEffect(() => {
if (forceLoadPost) {
setPosts([]);
setStartAuthor('');
setStartPermlink('');
setSelectedFilterIndex(selectedOptionIndex || 0);
setIsNoPost(false);
_loadPosts();
if (changeForceLoadPostState) {
changeForceLoadPostState(false);
}
}
}, [
_loadPosts,
changeForceLoadPostState,
currentAccountUsername,
forceLoadPost,
selectedOptionIndex,
]);
useEffect(() => {
if (!startAuthor && !startPermlink) {
_loadPosts(selectedFilterValue);
}
}, [
_loadPosts,
filterOptions,
filterOptionsValue,
selectedFilterValue,
startAuthor,
startPermlink,
]);
const _handleOnDropdownSelect = async (index) => {
if (index === selectedFilterIndex) {
_scrollTop();
} else {
if (filterOptions && filterOptions.length > 0) {
await setSelectedFilterValue(filterOptionsValue[index]);
setSelectedFilterValue(filterOptionsValue[index]);
}
setSelectedFilterIndex(index);
setPosts([]);
setStartPermlink('');
setStartAuthor('');
setIsNoPost(false);
handleOnDropdownSelect(index);
}
};
const _getPromotePosts = useCallback(async () => {
await getPromotePosts()
.then(async (res) => {
if (res && res.length) {
const _promotedPosts = await Promise.all(
res.map((item) =>
getPost(
get(item, 'author'),
get(item, 'permlink'),
currentAccountUsername,
true,
).then((post) => post),
),
);
setPromotedPosts(_promotedPosts);
}
})
.catch(() => {});
}, [currentAccountUsername]);
const _loadPosts = useCallback(
(type) => {
if (
isLoading ||
!isConnected ||
(!isLoggedIn && type === 'feed') ||
(!isLoggedIn && type === 'blog')
) {
return;
}
if (!isConnected && (refreshing || isLoading)) {
setRefreshing(false);
setIsLoading(false);
return;
}
if (posts.length > 2) {
setIsLoading(true);
}
const filter = type || selectedFilterValue;
let options;
const limit = 6;
if (filter === 'feed' || filter === 'blog' || getFor === 'blog' || filter === 'reblogs') {
options = {
tag: feedUsername,
limit,
};
} else {
options = {
tag,
limit,
};
}
if (startAuthor && startPermlink && !refreshing) {
options.start_author = startAuthor;
options.start_permlink = startPermlink;
}
// options.truncate_body = 200;
getPostsSummary(filter, options, currentAccountUsername, nsfw)
.then((result) => {
if (result.length > 0) {
let _posts = result;
if (filter === 'reblogs') {
for (let i = _posts.length - 1; i >= 0; i--) {
if (_posts[i].author === currentAccountUsername) {
_posts.splice(i, 1);
}
}
}
if (_posts.length > 0) {
if (posts.length > 0) {
if (refreshing) {
_posts = unionBy(_posts, posts, 'permlink');
} else {
_posts = unionBy(posts, _posts, 'permlink');
}
}
if (posts.length < 7 && pageType !== 'profiles') {
setFeedPosts(_posts);
}
if (!refreshing) {
setStartAuthor(result[result.length - 1] && result[result.length - 1].author);
setStartPermlink(result[result.length - 1] && result[result.length - 1].permlink);
}
setPosts(_posts);
setRefreshing(false);
setIsLoading(false);
}
} else if (result.length === 0) {
setIsNoPost(true);
setRefreshing(false);
setIsLoading(false);
}
})
.catch(() => {
setRefreshing(false);
setIsLoading(false);
});
},
[
currentAccountUsername,
getFor,
isConnected,
isLoading,
isLoggedIn,
nsfw,
pageType,
posts,
//promotedPosts,
refreshing,
selectedFilterValue,
setFeedPosts,
startAuthor,
startPermlink,
tag,
],
);
const _handleOnRefreshPosts = async () => {
setRefreshing(true);
if (pageType !== 'profiles') {
await _getPromotePosts();
}
};
useEffect(() => {
if (refreshing) {
_loadPosts();
}
}, [refreshing]);
const _renderFooter = () => {
if (isLoading) {
return (
@ -360,6 +148,13 @@ const PostsView = ({
return e;
};
const _onEndReached = ({ distanceFromEnd }) => {
if (!_onEndReachedCalledDuringMomentum) {
loadPosts();
_onEndReachedCalledDuringMomentum = true;
}
};
return (
<ThemeContainer>
{({ isDarkTheme }) => (
@ -385,19 +180,21 @@ const PostsView = ({
showsVerticalScrollIndicator={false}
renderItem={_renderItem}
keyExtractor={(content, i) => `key-${i.toString()}`}
onEndReached={() => _loadPosts()}
onEndReached={_onEndReached}
onMomentumScrollBegin={() => {
_onEndReachedCalledDuringMomentum = false;
}}
removeClippedSubviews
refreshing={refreshing}
onRefresh={_handleOnRefreshPosts}
onEndReachedThreshold={0.1}
initialNumToRender={4}
onRefresh={handleOnRefreshPosts}
onEndReachedThreshold={2}
ListFooterComponent={_renderFooter}
onScrollEndDrag={_handleOnScroll}
ListEmptyComponent={_renderEmptyContent}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={_handleOnRefreshPosts}
onRefresh={handleOnRefreshPosts}
progressBackgroundColor="#357CE6"
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
titleColor="#fff"

View File

@ -11,6 +11,7 @@ import { setUpvotePercent as upvoteAction } from '../../../redux/actions/applica
// Utils
import parseToken from '../../../utils/parseToken';
import { isEmptyContentDate, getTimeFromNow } from '../../../utils/time';
import { isVoted as isVotedFunc, isDownVoted as isDownVotedFunc } from '../../../utils/postParser';
// Component
import UpvoteView from '../view/upvoteView';
@ -50,14 +51,15 @@ class UpvoteContainer extends PureComponent {
pinCode,
upvotePercent,
globalProps,
activeVotes = [],
} = this.props;
const _isVoted = isVotedFunc(activeVotes, get(currentAccount, 'name'));
const _isDownVoted = isDownVotedFunc(activeVotes, get(currentAccount, 'name'));
const author = get(content, 'author');
const isVoted =
get(content, 'is_voted', false) && parseInt(get(content, 'is_voted'), 10) / 10000;
const isDownVoted =
get(content, 'is_down_voted', false) &&
(parseInt(get(content, 'is_down_voted'), 10) / 10000) * -1;
const isVoted = _isVoted && parseInt(_isVoted, 10) / 10000;
const isDownVoted = _isDownVoted && (parseInt(_isDownVoted, 10) / 10000) * -1;
const totalPayout = get(content, 'total_payout');
const isDecinedPayout = get(content, 'is_declined_payout');

View File

@ -74,7 +74,9 @@ export const addDraft = (data) =>
.post('/draft', data)
.then((res) => {
const { drafts } = res.data;
resolve(drafts.pop());
if (drafts) {
resolve(drafts.pop());
}
})
.catch((error) => {
bugsnag.notify(error);

View File

@ -390,7 +390,7 @@ export const getPosts = async (by, query, user) => {
let posts = await client.database.getDiscussions(by, query);
if (posts) {
posts = await parsePosts(posts, user);
posts = parsePosts(posts, user);
}
return posts;
} catch (error) {
@ -415,7 +415,7 @@ export const getPostsSummary = async (by, query, currentUserName, filterNsfw) =>
let posts = await client.database.getDiscussions(by, query);
if (posts) {
posts = await parsePosts(posts, currentUserName, true);
posts = parsePosts(posts, currentUserName, true);
if (filterNsfw !== '0') {
const updatedPosts = filterNsfwPost(posts, filterNsfw);
@ -431,7 +431,7 @@ export const getPostsSummary = async (by, query, currentUserName, filterNsfw) =>
export const getUserComments = async (query) => {
try {
const comments = await client.database.getDiscussions('comments', query);
const groomedComments = await parseComments(comments);
const groomedComments = parseComments(comments);
return groomedComments;
} catch (error) {
return error;
@ -445,7 +445,7 @@ export const getRepliesByLastUpdate = async (query) => {
query.start_permlink,
query.limit,
]);
const groomedComments = await parseComments(replies);
const groomedComments = parseComments(replies);
return groomedComments;
} catch (error) {
return error;
@ -456,7 +456,7 @@ export const getPost = async (author, permlink, currentUserName = null, isPromot
try {
const post = await client.database.call('get_content', [author, permlink]);
return post ? await parsePost(post, currentUserName, isPromoted) : null;
return post ? parsePost(post, currentUserName, isPromoted) : null;
} catch (error) {
return error;
}
@ -522,7 +522,7 @@ export const getComments = async (author, permlink, currentUserName = null) => {
try {
const comments = await client.database.call('get_content_replies', [author, permlink]);
const groomedComments = await parseComments(comments, currentUserName);
const groomedComments = parseComments(comments, currentUserName);
return comments ? groomedComments : null;
} catch (error) {

View File

@ -56,7 +56,6 @@ import {
import {
activeApplication,
isDarkTheme,
isLoginDone,
changeNotificationSettings,
changeAllNotificationSettings,
login,
@ -150,7 +149,6 @@ class ApplicationContainer extends Component {
ReceiveSharingIntent.getReceivedFiles(
(files) => {
console.log('files :>> ', files);
navigate({
routeName: ROUTES.SCREENS.EDITOR,
params: { upload: files },
@ -213,9 +211,8 @@ class ApplicationContainer extends Component {
const { isConnected, dispatch } = this.props;
if (state.isConnected !== isConnected) {
dispatch(setConnectivityStatus(state.isConnected));
//this._fetchApp();
this._fetchApp();
}
this._fetchApp();
});
};
@ -315,16 +312,11 @@ class ApplicationContainer extends Component {
_fetchApp = async () => {
await this._getSettings();
await this._refreshGlobalProps();
const userRealmObject = await this._getUserDataFromRealm();
this.setState({
isReady: true,
});
const { isConnected } = this.props;
if (isConnected && userRealmObject) {
await this._fetchUserDataFromDsteem(userRealmObject);
}
this._refreshGlobalProps();
this._getUserDataFromRealm();
};
_pushNavigate = (notification) => {
@ -461,13 +453,15 @@ class ApplicationContainer extends Component {
};
_getUserDataFromRealm = async () => {
const { dispatch, pinCode, isPinCodeOpen: _isPinCodeOpen } = this.props;
const { dispatch, pinCode, isPinCodeOpen: _isPinCodeOpen, isConnected } = this.props;
let realmData = [];
const res = await getAuthStatus();
const { currentUsername } = res;
if (res) {
dispatch(activeApplication());
dispatch(login(true));
const userData = await getUserData();
if (userData && userData.length > 0) {
@ -526,16 +520,15 @@ class ApplicationContainer extends Component {
dispatch(savePinCode(encryptedPin));
}
dispatch(activeApplication());
dispatch(isLoginDone());
dispatch(login(true));
if (isConnected) {
this._fetchUserDataFromDsteem(realmObject[0]);
}
return realmObject[0];
}
dispatch(updateCurrentAccount({}));
dispatch(activeApplication());
dispatch(isLoginDone());
return null;
};
@ -572,7 +565,7 @@ class ApplicationContainer extends Component {
this.setState({
isThemeReady: true,
});
if (settings.isPinCodeOpen !== '') dispatch(isPinCodeOpen(settings.isPinCodeOpen));
if (settings.isPinCodeOpen !== '') await dispatch(isPinCodeOpen(settings.isPinCodeOpen));
if (settings.language !== '') dispatch(setLanguage(settings.language));
if (settings.server !== '') dispatch(setApi(settings.server));
if (settings.upvotePercent !== '') {

View File

@ -1,34 +1,27 @@
import isEmpty from 'lodash/isEmpty';
import forEach from 'lodash/forEach';
import { get, uniqBy } from 'lodash';
import { get } from 'lodash';
import { Platform } from 'react-native';
import { postBodySummary, renderPostBody } from '@esteemapp/esteem-render-helpers';
// Dsteem
// eslint-disable-next-line import/no-cycle
import { getActiveVotes } from '../providers/steem/dsteem';
import { getPostReblogs } from '../providers/esteem/esteem';
// Utils
import { getReputation } from './reputation';
import { getResizedImage, getResizedAvatar } from './image';
const webp = Platform.OS === 'ios' ? false : true;
export const parsePosts = async (posts, currentUserName) => {
export const parsePosts = (posts, currentUserName) => {
if (posts) {
const promises = posts.map((post) => parsePost(post, currentUserName));
const formattedPosts = await Promise.all(promises);
const formattedPosts = posts.map((post) => parsePost(post, currentUserName));
return formattedPosts;
}
return null;
};
export const parsePost = async (post, currentUserName, isPromoted) => {
export const parsePost = (post, currentUserName, isPromoted) => {
if (!post) {
return null;
}
const activeVotes = await getActiveVotes(get(post, 'author'), get(post, 'permlink'));
if (currentUserName === post.author) {
post.markdownBody = post.body;
}
@ -39,28 +32,19 @@ export const parsePost = async (post, currentUserName, isPromoted) => {
post.json_metadata = {};
}
post.image = postImage(post.json_metadata, post.body);
post.active_votes = activeVotes;
post.vote_count = post.active_votes.length;
post.author_reputation = getReputation(post.author_reputation);
post.avatar = getResizedAvatar(get(post, 'author'));
post.active_votes.sort((a, b) => b.rshares - a.rshares);
post.body = renderPostBody(post, true, webp);
post.summary = postBodySummary(post, 150);
post.is_declined_payout = Number(parseFloat(post.max_accepted_payout)) === 0;
if (currentUserName) {
post.is_voted = isVoted(post.active_votes, currentUserName);
post.is_down_voted = isDownVoted(post.active_votes, currentUserName);
} else {
post.is_voted = false;
post.is_down_voted = false;
}
const totalPayout =
parseFloat(post.pending_payout_value) +
parseFloat(post.total_payout_value) +
parseFloat(post.curator_payout_value);
post.active_votes = parseActiveVotes(post, currentUserName);
post.reblogs = await getPostReblogs(post);
post.reblogCount = get(post, 'reblogs', []).length;
post.total_payout = totalPayout.toFixed(3);
return post;
};
@ -102,37 +86,22 @@ const postImage = (metaData, body) => {
return '';
};
export const parseComments = async (comments, currentUserName) => {
const pArray = comments.map(async (comment) => {
const activeVotes = await getActiveVotes(get(comment, 'author'), get(comment, 'permlink'));
export const parseComments = async (comments) => {
return comments.map((comment) => {
comment.pending_payout_value = parseFloat(get(comment, 'pending_payout_value', 0)).toFixed(3);
comment.author_reputation = getReputation(get(comment, 'author_reputation'));
comment.avatar = getResizedAvatar(get(comment, 'author'));
comment.markdownBody = get(comment, 'body');
comment.body = renderPostBody(comment, true, webp);
comment.active_votes = activeVotes;
comment.vote_count = activeVotes && activeVotes.length;
if (currentUserName && activeVotes && activeVotes.length > 0) {
comment.is_voted = isVoted(activeVotes, currentUserName);
comment.is_down_voted = isDownVoted(comment.active_votes, currentUserName);
} else {
comment.is_voted = false;
comment.is_down_voted = false;
}
comment.active_votes = parseActiveVotes(comment, currentUserName);
return comment;
});
const _comments = await Promise.all(pArray);
return _comments;
};
const isVoted = (activeVotes, currentUserName) => {
export const isVoted = (activeVotes, currentUserName) => {
if (!currentUserName) {
return false;
}
const result = activeVotes.find(
(element) => get(element, 'voter') === currentUserName && get(element, 'percent', 0) > 0,
);
@ -142,7 +111,10 @@ const isVoted = (activeVotes, currentUserName) => {
return false;
};
const isDownVoted = (activeVotes, currentUserName) => {
export const isDownVoted = (activeVotes, currentUserName) => {
if (!currentUserName) {
return false;
}
const result = activeVotes.find(
(element) => get(element, 'voter') === currentUserName && get(element, 'percent') < 0,
);
@ -152,20 +124,17 @@ const isDownVoted = (activeVotes, currentUserName) => {
return false;
};
const parseActiveVotes = (post, currentUserName) => {
export const parseActiveVotes = (post) => {
const totalPayout =
parseFloat(post.pending_payout_value) +
parseFloat(post.total_payout_value) +
parseFloat(post.curator_payout_value);
post.total_payout = totalPayout.toFixed(3);
const voteRshares = post.active_votes.reduce((a, b) => a + parseFloat(b.rshares), 0);
const ratio = totalPayout / voteRshares || 0;
if (!isEmpty(post.active_votes)) {
forEach(post.active_votes, (value) => {
post.vote_percent = value.voter === currentUserName ? value.percent : null;
value.value = (value.rshares * ratio).toFixed(3);
value.reputation = getReputation(get(value, 'reputation'));
value.percent /= 100;

View File

@ -2030,9 +2030,9 @@ bip66@^1.1.5:
safe-buffer "^5.0.1"
bl@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
integrity sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==
version "4.0.3"
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489"
integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==
dependencies:
buffer "^5.5.0"
inherits "^2.0.4"