mirror of
https://github.com/ecency/ecency-mobile.git
synced 2024-12-18 19:01:38 +03:00
Support for pagination, refresh, empty placeholders
This commit is contained in:
parent
1fc8b323e0
commit
258e5d0b09
@ -1,18 +1,28 @@
|
||||
import React, {forwardRef, memo, useRef, useImperativeHandle, useState, useEffect} from 'react'
|
||||
import PostCard from '../../postCard';
|
||||
import { get } from 'lodash';
|
||||
import { FlatListProps, FlatList } from 'react-native';
|
||||
import { FlatListProps, FlatList, RefreshControl, ActivityIndicator, View } from 'react-native';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { ThemeContainer } from '../../../containers';
|
||||
import { PostCardPlaceHolder } from '../..';
|
||||
import styles from '../view/postsListStyles';
|
||||
import { isDarkTheme } from '../../../redux/actions/applicationActions';
|
||||
|
||||
|
||||
interface postsListContainerProps extends FlatListProps<any> {
|
||||
promotedPosts:Array<any>;
|
||||
isFeedScreen:boolean;
|
||||
onLoadPosts?:(shouldReset:boolean)=>void;
|
||||
isLoading:boolean;
|
||||
isRefreshing:boolean;
|
||||
}
|
||||
|
||||
const postsListContainer = ({
|
||||
promotedPosts,
|
||||
isFeedScreen,
|
||||
onLoadPosts,
|
||||
isRefreshing,
|
||||
isLoading,
|
||||
...props
|
||||
}:postsListContainerProps, ref) => {
|
||||
const flatListRef = useRef(null);
|
||||
@ -65,6 +75,23 @@ const postsListContainer = ({
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
const _renderFooter = () => {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<View style={styles.flatlistFooter}>
|
||||
<ActivityIndicator animating size="large" color={'#2e3d51'} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const _renderItem = ({ item, index }:{item:any, index:number}) => {
|
||||
const e = [];
|
||||
|
||||
@ -109,25 +136,39 @@ const postsListContainer = ({
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
data={posts}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(content) => content.permlink}
|
||||
removeClippedSubviews
|
||||
onEndReachedThreshold={1}
|
||||
maxToRenderPerBatch={3}
|
||||
initialNumToRender={3}
|
||||
windowSize={5}
|
||||
extraData={imageHeights}
|
||||
{...props}
|
||||
/>
|
||||
<ThemeContainer>
|
||||
{({ isDarkTheme }) => (
|
||||
<FlatList
|
||||
ref={flatListRef}
|
||||
data={posts}
|
||||
showsVerticalScrollIndicator={false}
|
||||
renderItem={_renderItem}
|
||||
keyExtractor={(content) => content.permlink}
|
||||
removeClippedSubviews
|
||||
onEndReachedThreshold={1}
|
||||
maxToRenderPerBatch={3}
|
||||
initialNumToRender={3}
|
||||
windowSize={5}
|
||||
extraData={imageHeights}
|
||||
onEndReached={()=>{if(onLoadPosts){onLoadPosts(false)}}}
|
||||
ListFooterComponent={_renderFooter}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={isRefreshing}
|
||||
onRefresh={()=>{if(onLoadPosts){onLoadPosts(true)}}}
|
||||
progressBackgroundColor="#357CE6"
|
||||
tintColor={!isDarkTheme ? '#357ce6' : '#96c0ff'}
|
||||
titleColor="#fff"
|
||||
colors={['#fff']}
|
||||
/>
|
||||
}
|
||||
{...props}
|
||||
/>
|
||||
)}
|
||||
</ThemeContainer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default memo(forwardRef(postsListContainer));
|
||||
export default forwardRef(postsListContainer);
|
||||
|
14
src/components/postsList/view/postsListStyles.tsx
Normal file
14
src/components/postsList/view/postsListStyles.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
flatlistFooter: {
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 10,
|
||||
marginBottom: 60,
|
||||
borderColor: '$borderColor',
|
||||
},
|
||||
placeholderWrapper: {
|
||||
flex: 1,
|
||||
},
|
||||
})
|
@ -3,10 +3,11 @@ import { useIntl } from 'react-intl';
|
||||
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import PostsList from '../../postsList';
|
||||
import { loadPosts, LoadPostsOptions } from '../services/tabbedPostsFetch';
|
||||
import { loadPosts } from '../services/tabbedPostsFetch';
|
||||
import { TabbedPostsProps } from '../services/tabbedPostsModels';
|
||||
import { cacheReducer, initCacheState, setSelectedFilter, PostsCache } from '../services/tabbedPostsReducer';
|
||||
import { StackedTabBar, TabItem } from '../view/stackedTabBar';
|
||||
import { TabbedPostsProps } from './tabbedPostsProps';
|
||||
import TabContent from '../view/tabContent';
|
||||
|
||||
|
||||
export const TabbedPosts = ({
|
||||
@ -20,13 +21,6 @@ export const TabbedPosts = ({
|
||||
...props
|
||||
}:TabbedPostsProps) => {
|
||||
|
||||
const intl = useIntl();
|
||||
const isLoggedIn = useSelector((state) => state.application.isLoggedIn);
|
||||
|
||||
//redux properties
|
||||
const isAnalytics = useSelector((state) => state.application.isAnalytics);
|
||||
const nsfw = useSelector((state) => state.application.nsfw);
|
||||
const isConnected = useSelector((state) => state.application.isConnected);
|
||||
|
||||
|
||||
//initialize state
|
||||
@ -59,45 +53,22 @@ export const TabbedPosts = ({
|
||||
|
||||
//initialize first set of pages
|
||||
const pages = combinedFilters.map((filter)=>(
|
||||
<PostsList
|
||||
data={cache.cachedData[filter.filterKey].posts}
|
||||
<TabContent
|
||||
filterKey={filter.filterKey}
|
||||
tabLabel={filter.label}
|
||||
isFeedScreen={isFeedScreen}
|
||||
promotedPosts={[]}
|
||||
feedUsername={feedUsername}
|
||||
{...props}
|
||||
/>
|
||||
))
|
||||
|
||||
//side effects
|
||||
useEffect(() => {
|
||||
_loadPosts()
|
||||
}, [])
|
||||
|
||||
|
||||
|
||||
//load posts implementation
|
||||
const _loadPosts = (filter?:string) => {
|
||||
const options = {
|
||||
passedFilter:filter,
|
||||
cache,
|
||||
cacheDispatch,
|
||||
isLoggedIn,
|
||||
isAnalytics,
|
||||
nsfw,
|
||||
isConnected,
|
||||
feedUsername,
|
||||
isFeedScreen,
|
||||
...props
|
||||
} as LoadPostsOptions
|
||||
loadPosts(options)
|
||||
}
|
||||
|
||||
|
||||
//actions
|
||||
|
||||
const _onFilterSelect = (filter:string) => {
|
||||
cacheDispatch(setSelectedFilter(filter))
|
||||
if(cache.cachedData[filter].posts.length === 0){
|
||||
_loadPosts(filter);
|
||||
// _loadPosts(filter);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,12 +0,0 @@
|
||||
export interface TabbedPostsProps {
|
||||
filterOptions:string[],
|
||||
filterOptionsValue:string[],
|
||||
isFeedScreen:boolean,
|
||||
feedUsername:string,
|
||||
initialFilterIndex:number,
|
||||
feedSubfilterOptions:string[],
|
||||
feedSubfilterOptionsValue:string[],
|
||||
getFor:string,
|
||||
pageType:string,
|
||||
tag:string,
|
||||
}
|
@ -1,29 +1,15 @@
|
||||
import { Dispatch, ReducerAction } from "react";
|
||||
import { getAccountPosts, getRankedPosts } from "../../../providers/hive/dhive";
|
||||
import { CachedDataEntry, onLoadComplete, PostsCache, setFilterLoading, updateFilterCache } from "./tabbedPostsReducer";
|
||||
import { getUpdatedPosts } from "./tabbedPostsReducer";
|
||||
import Matomo from 'react-native-matomo-sdk';
|
||||
import { LoadPostsOptions } from "./tabbedPostsModels";
|
||||
|
||||
|
||||
export interface LoadPostsOptions {
|
||||
cache:PostsCache,
|
||||
cacheDispatch:Dispatch<ReducerAction<any>>,
|
||||
getFor:string,
|
||||
isConnected:boolean,
|
||||
isLoggedIn:boolean,
|
||||
feedUsername:string,
|
||||
pageType:string,
|
||||
tag:string,
|
||||
nsfw:string,
|
||||
isAnalytics:boolean,
|
||||
isLatestPostCheck?:boolean,
|
||||
refreshing?:boolean,
|
||||
passedFilter?:string;
|
||||
}
|
||||
const POSTS_FETCH_COUNT = 20;
|
||||
|
||||
export const loadPosts = async ({
|
||||
passedFilter,
|
||||
cache,
|
||||
cacheDispatch,
|
||||
filterKey,
|
||||
prevPosts,
|
||||
tabMeta,
|
||||
setTabMeta,
|
||||
isLatestPostCheck = false,
|
||||
getFor,
|
||||
isConnected,
|
||||
@ -36,33 +22,41 @@ export const loadPosts = async ({
|
||||
isAnalytics
|
||||
|
||||
}:LoadPostsOptions) => {
|
||||
let filter = passedFilter || cache.selectedFilter;
|
||||
let filter = filterKey;
|
||||
|
||||
//match filter with api if is friends
|
||||
if(filter === 'friends'){
|
||||
filter = 'feed';
|
||||
}
|
||||
|
||||
const {isLoading, startPermlink, startAuthor}:CachedDataEntry = cache.cachedData[filter];
|
||||
const {isLoading, startPermlink, startAuthor} = tabMeta;
|
||||
|
||||
if (
|
||||
isLoading ||
|
||||
!isConnected ||
|
||||
(!isLoggedIn && passedFilter === 'feed') ||
|
||||
(!isLoggedIn && passedFilter === 'blog')
|
||||
(!isLoggedIn && filterKey === 'feed') ||
|
||||
(!isLoggedIn && filterKey === 'blog')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isConnected && (refreshing || isLoading)) {
|
||||
cacheDispatch(onLoadComplete(filter))
|
||||
setTabMeta({
|
||||
...tabMeta,
|
||||
isLoading:false,
|
||||
isRefreshing:false,
|
||||
})
|
||||
return;
|
||||
}
|
||||
|
||||
cacheDispatch(setFilterLoading(filter, true));
|
||||
setTabMeta({
|
||||
...tabMeta,
|
||||
isLoading:true,
|
||||
isRefreshing:refreshing,
|
||||
})
|
||||
|
||||
let options = {} as any;
|
||||
const limit = isLatestPostCheck ? 5 : 20;
|
||||
const limit = isLatestPostCheck ? 5 : POSTS_FETCH_COUNT;
|
||||
let func = null;
|
||||
|
||||
if (
|
||||
@ -122,12 +116,30 @@ export const loadPosts = async ({
|
||||
if(filter === 'feed'){
|
||||
filter = 'friends'
|
||||
}
|
||||
cacheDispatch(updateFilterCache(filter, result, refreshing))
|
||||
cacheDispatch(onLoadComplete(filter));
|
||||
|
||||
// cacheDispatch(updateFilterCache(filter, result, refreshing))
|
||||
|
||||
|
||||
setTabMeta({
|
||||
...tabMeta,
|
||||
isLoading:false,
|
||||
isRefreshing:false,
|
||||
})
|
||||
|
||||
return getUpdatedPosts(
|
||||
prevPosts,
|
||||
result,
|
||||
refreshing,
|
||||
tabMeta,
|
||||
setTabMeta
|
||||
)
|
||||
|
||||
} catch (err) {
|
||||
|
||||
cacheDispatch(onLoadComplete(filter));
|
||||
setTabMeta({
|
||||
...tabMeta,
|
||||
isLoading:false,
|
||||
isRefreshing:false,
|
||||
})
|
||||
}
|
||||
|
||||
// track filter and tag views
|
||||
|
40
src/components/tabbedPosts/services/tabbedPostsModels.ts
Normal file
40
src/components/tabbedPosts/services/tabbedPostsModels.ts
Normal file
@ -0,0 +1,40 @@
|
||||
|
||||
export interface TabbedPostsProps {
|
||||
filterOptions:string[],
|
||||
filterOptionsValue:string[],
|
||||
isFeedScreen:boolean,
|
||||
feedUsername:string,
|
||||
initialFilterIndex:number,
|
||||
feedSubfilterOptions:string[],
|
||||
feedSubfilterOptionsValue:string[],
|
||||
getFor:string,
|
||||
pageType:string,
|
||||
tag:string,
|
||||
}
|
||||
|
||||
export interface TabMeta {
|
||||
startPermlink:string,
|
||||
startAuthor:string,
|
||||
isLoading:boolean,
|
||||
isRefreshing:boolean,
|
||||
isNoPost:boolean,
|
||||
}
|
||||
|
||||
export interface LoadPostsOptions {
|
||||
|
||||
filterKey:string;
|
||||
prevPosts:any[];
|
||||
tabMeta:TabMeta;
|
||||
setTabMeta:(meta:TabMeta)=>void,
|
||||
getFor:string,
|
||||
isConnected:boolean,
|
||||
isLoggedIn:boolean,
|
||||
feedUsername:string,
|
||||
pageType:string,
|
||||
tag:string,
|
||||
nsfw:string,
|
||||
isAnalytics:boolean,
|
||||
isLatestPostCheck?:boolean,
|
||||
refreshing?:boolean,
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import { TabItem } from "../view/stackedTabBar";
|
||||
import unionBy from 'lodash/unionBy';
|
||||
import { TabMeta } from "./tabbedPostsModels";
|
||||
import TabBarTop from "react-navigation-tabs/lib/typescript/src/views/MaterialTopTabBar";
|
||||
|
||||
export const CacheActions = {
|
||||
SET_FILTER_LOADING:'SET_FILTER_LOADING',
|
||||
@ -53,14 +55,64 @@ export const onLoadComplete = (filter:string) => ({
|
||||
type:CacheActions.ON_LOAD_COMPLETE
|
||||
})
|
||||
|
||||
export const updateFilterCache = (filter:string, posts:any[], refreshing:boolean) => ({
|
||||
payload: {
|
||||
filter,
|
||||
posts,
|
||||
shouldReset: refreshing,
|
||||
},
|
||||
type: CacheActions.UPDATE_FILTER_CACHE,
|
||||
})
|
||||
|
||||
export const getUpdatedPosts = (prevPosts:any[], nextPosts:any[], shouldReset:boolean, tabMeta:TabMeta, setTabMeta:(meta:TabMeta)=>void) => {
|
||||
//return state as is if component is unmounter
|
||||
let _posts = nextPosts;
|
||||
|
||||
|
||||
// const isFeedScreen = state.isFeedScreen
|
||||
// const cachedEntry:CachedDataEntry = state.cachedData[filter];
|
||||
// if (!cachedEntry) {
|
||||
// throw new Error('No cached entry available');
|
||||
// }
|
||||
|
||||
if(nextPosts.length === 0){
|
||||
setTabMeta({
|
||||
...tabMeta,
|
||||
isNoPost:true
|
||||
});
|
||||
return prevPosts;
|
||||
}
|
||||
|
||||
|
||||
const refreshing = tabMeta.isRefreshing;
|
||||
|
||||
|
||||
if (prevPosts.length > 0 && !shouldReset) {
|
||||
if (refreshing) {
|
||||
_posts = unionBy(_posts, prevPosts, 'permlink');
|
||||
} else {
|
||||
_posts = unionBy(prevPosts, _posts, 'permlink');
|
||||
}
|
||||
}
|
||||
//cache latest posts for main tab for returning user
|
||||
// else if (isFeedScreen) {
|
||||
// //schedule refetch of new posts by checking time of current post
|
||||
// _scheduleLatestPostsCheck(nextPosts[0]);
|
||||
|
||||
// if (filter == (get(currentAccount, 'name', null) == null ? 'hot' : 'friends')) {
|
||||
// _setInitPosts(nextPosts);
|
||||
// }
|
||||
// }
|
||||
|
||||
//update stat
|
||||
|
||||
setTabMeta({
|
||||
...tabMeta,
|
||||
startAuthor:_posts[_posts.length - 1] && _posts[_posts.length - 1].author,
|
||||
startPermlink: _posts[_posts.length - 1] && _posts[_posts.length - 1].permlink,
|
||||
})
|
||||
|
||||
|
||||
//dispatch to redux
|
||||
// if (
|
||||
// filter === (state.selectedFilter !== 'feed' ? state.selectedFilter : state.currentSubFilter)
|
||||
// ) {
|
||||
// _setFeedPosts(_posts);
|
||||
// }
|
||||
return _posts
|
||||
}
|
||||
|
||||
|
||||
export const initCacheState = (filters:TabItem[], selectedFilter:string, isFeedScreen:boolean) => {
|
||||
|
341
src/components/tabbedPosts/view/listEmptyView.tsx
Normal file
341
src/components/tabbedPosts/view/listEmptyView.tsx
Normal file
@ -0,0 +1,341 @@
|
||||
import React, {useEffect, useState} from 'react';
|
||||
import { useIntl } from 'react-intl';
|
||||
import { get } from 'lodash';
|
||||
import { Text, View, FlatList } from 'react-native';
|
||||
import { NoPost, PostCardPlaceHolder, UserListItem } from '../..';
|
||||
import globalStyles from '../../../globalStyles';
|
||||
import { CommunityListItem } from '../../basicUIElements';
|
||||
import styles from './tabbedPostsStyles';
|
||||
import { default as ROUTES } from '../../../constants/routeNames';
|
||||
import { withNavigation } from 'react-navigation';
|
||||
import {useSelector, useDispatch } from 'react-redux';
|
||||
import { fetchCommunities, leaveCommunity, subscribeCommunity } from '../../../redux/actions/communitiesAction';
|
||||
import { fetchLeaderboard, followUser, unfollowUser } from '../../../redux/actions/userAction';
|
||||
import { getCommunity } from '../../../providers/hive/dhive';
|
||||
|
||||
interface TabEmptyViewProps {
|
||||
filterKey:string,
|
||||
isNoPost:boolean,
|
||||
navigation:any,
|
||||
}
|
||||
|
||||
const TabEmptyView = ({
|
||||
filterKey,
|
||||
isNoPost,
|
||||
navigation
|
||||
}: TabEmptyViewProps) => {
|
||||
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
//redux properties
|
||||
const isLoggedIn = useSelector((state) => state.application.isLoggedIn);
|
||||
const subscribingCommunities = useSelector(
|
||||
(state) => state.communities.subscribingCommunitiesInFeedScreen,
|
||||
);
|
||||
const [recommendedCommunities, setRecommendedCommunities] = useState([]);
|
||||
const [recommendedUsers, setRecommendedUsers] = useState([]);
|
||||
const followingUsers = useSelector((state) => state.user.followingUsersInFeedScreen);
|
||||
const currentAccount = useSelector((state) => state.account.currentAccount);
|
||||
const pinCode = useSelector((state) => state.application.pin);
|
||||
|
||||
const leaderboard = useSelector((state) => state.user.leaderboard);
|
||||
const communities = useSelector((state) => state.communities.communities);
|
||||
|
||||
//hooks
|
||||
|
||||
useEffect(()=>{
|
||||
if (isNoPost) {
|
||||
if (filterKey === 'friends') {
|
||||
if (recommendedUsers.length === 0) {
|
||||
_getRecommendedUsers();
|
||||
}
|
||||
} else if(filterKey === 'communities') {
|
||||
if (recommendedCommunities.length === 0) {
|
||||
_getRecommendedCommunities();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [isNoPost])
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (!leaderboard.loading) {
|
||||
if (!leaderboard.error && leaderboard.data.length > 0) {
|
||||
_formatRecommendedUsers(leaderboard.data);
|
||||
}
|
||||
}
|
||||
}, [leaderboard]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!communities.loading) {
|
||||
if (!communities.error && communities.data?.length > 0) {
|
||||
_formatRecommendedCommunities(communities.data);
|
||||
}
|
||||
}
|
||||
}, [communities]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const recommendeds = [...recommendedCommunities];
|
||||
|
||||
Object.keys(subscribingCommunities).map((communityId) => {
|
||||
if (!subscribingCommunities[communityId].loading) {
|
||||
if (!subscribingCommunities[communityId].error) {
|
||||
if (subscribingCommunities[communityId].isSubscribed) {
|
||||
recommendeds.forEach((item) => {
|
||||
if (item.name === communityId) {
|
||||
item.isSubscribed = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
recommendeds.forEach((item) => {
|
||||
if (item.name === communityId) {
|
||||
item.isSubscribed = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setRecommendedCommunities(recommendeds);
|
||||
}, [subscribingCommunities]);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const recommendeds = [...recommendedUsers];
|
||||
|
||||
Object.keys(followingUsers).map((following) => {
|
||||
if (!followingUsers[following].loading) {
|
||||
if (!followingUsers[following].error) {
|
||||
if (followingUsers[following].isFollowing) {
|
||||
recommendeds.forEach((item) => {
|
||||
if (item._id === following) {
|
||||
item.isFollowing = true;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
recommendeds.forEach((item) => {
|
||||
if (item._id === following) {
|
||||
item.isFollowing = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setRecommendedUsers(recommendeds);
|
||||
}, [followingUsers]);
|
||||
|
||||
|
||||
//fetching
|
||||
const _getRecommendedUsers = () => dispatch(fetchLeaderboard());
|
||||
const _getRecommendedCommunities = () => dispatch(fetchCommunities('', 10));
|
||||
|
||||
//formating
|
||||
const _formatRecommendedCommunities = async (communitiesArray) => {
|
||||
try {
|
||||
const ecency = await getCommunity('hive-125125');
|
||||
|
||||
const recommendeds = [ecency, ...communitiesArray];
|
||||
recommendeds.forEach((item) => Object.assign(item, { isSubscribed: false }));
|
||||
|
||||
setRecommendedCommunities(recommendeds);
|
||||
} catch (err) {
|
||||
console.log(err, '_getRecommendedUsers Error');
|
||||
}
|
||||
};
|
||||
|
||||
const _formatRecommendedUsers = (usersArray) => {
|
||||
const recommendeds = usersArray.slice(0, 10);
|
||||
|
||||
recommendeds.unshift({ _id: 'good-karma' });
|
||||
recommendeds.unshift({ _id: 'ecency' });
|
||||
|
||||
recommendeds.forEach((item) => Object.assign(item, { isFollowing: false }));
|
||||
|
||||
setRecommendedUsers(recommendeds);
|
||||
};
|
||||
|
||||
//actions related routines
|
||||
const _handleSubscribeCommunityButtonPress = (data) => {
|
||||
let subscribeAction;
|
||||
let successToastText = '';
|
||||
let failToastText = '';
|
||||
|
||||
if (!data.isSubscribed) {
|
||||
subscribeAction = subscribeCommunity;
|
||||
|
||||
successToastText = intl.formatMessage({
|
||||
id: 'alert.success_subscribe',
|
||||
});
|
||||
failToastText = intl.formatMessage({
|
||||
id: 'alert.fail_subscribe',
|
||||
});
|
||||
} else {
|
||||
subscribeAction = leaveCommunity;
|
||||
|
||||
successToastText = intl.formatMessage({
|
||||
id: 'alert.success_leave',
|
||||
});
|
||||
failToastText = intl.formatMessage({
|
||||
id: 'alert.fail_leave',
|
||||
});
|
||||
}
|
||||
|
||||
dispatch(
|
||||
subscribeAction(currentAccount, pinCode, data, successToastText, failToastText, 'feedScreen'),
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const _handleFollowUserButtonPress = (data, isFollowing) => {
|
||||
let followAction;
|
||||
let successToastText = '';
|
||||
let failToastText = '';
|
||||
|
||||
if (!isFollowing) {
|
||||
followAction = followUser;
|
||||
|
||||
successToastText = intl.formatMessage({
|
||||
id: 'alert.success_follow',
|
||||
});
|
||||
failToastText = intl.formatMessage({
|
||||
id: 'alert.fail_follow',
|
||||
});
|
||||
} else {
|
||||
followAction = unfollowUser;
|
||||
|
||||
successToastText = intl.formatMessage({
|
||||
id: 'alert.success_unfollow',
|
||||
});
|
||||
failToastText = intl.formatMessage({
|
||||
id: 'alert.fail_unfollow',
|
||||
});
|
||||
}
|
||||
|
||||
data.follower = get(currentAccount, 'name', '');
|
||||
|
||||
dispatch(followAction(currentAccount, pinCode, data, successToastText, failToastText));
|
||||
};
|
||||
|
||||
|
||||
const _handleOnPressLogin = () => {
|
||||
navigation.navigate(ROUTES.SCREENS.LOGIN);
|
||||
};
|
||||
|
||||
|
||||
//render related operations
|
||||
if ((filterKey === 'feed' || filterKey === 'blog') && !isLoggedIn) {
|
||||
return (
|
||||
<NoPost
|
||||
imageStyle={styles.noImage}
|
||||
isButtonText
|
||||
defaultText={intl.formatMessage({
|
||||
id: 'profile.login_to_see',
|
||||
})}
|
||||
handleOnButtonPress={_handleOnPressLogin}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (isNoPost) {
|
||||
if (filterKey === 'friends') {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Text style={[globalStyles.subTitle, styles.noPostTitle]}>
|
||||
{intl.formatMessage({ id: 'profile.follow_people' })}
|
||||
</Text>
|
||||
<FlatList
|
||||
data={recommendedUsers}
|
||||
extraData={recommendedUsers}
|
||||
keyExtractor={(item, index) => `${item._id || item.id}${index}`}
|
||||
renderItem={({ item, index }) => (
|
||||
<UserListItem
|
||||
index={index}
|
||||
username={item._id}
|
||||
isHasRightItem
|
||||
rightText={
|
||||
item.isFollowing
|
||||
? intl.formatMessage({ id: 'user.unfollow' })
|
||||
: intl.formatMessage({ id: 'user.follow' })
|
||||
}
|
||||
//isRightColor={item.isFollowing}
|
||||
isLoggedIn={isLoggedIn}
|
||||
isFollowing={item.isFollowing}
|
||||
isLoadingRightAction={
|
||||
followingUsers.hasOwnProperty(item._id) && followingUsers[item._id].loading
|
||||
}
|
||||
onPressRightText={_handleFollowUserButtonPress}
|
||||
handleOnPress={(username) =>
|
||||
navigation.navigate({
|
||||
routeName: ROUTES.SCREENS.PROFILE,
|
||||
params: {
|
||||
username,
|
||||
},
|
||||
key: username,
|
||||
})
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
} else if (filterKey === 'communities') {
|
||||
return (
|
||||
<>
|
||||
<Text style={[globalStyles.subTitle, styles.noPostTitle]}>
|
||||
{intl.formatMessage({ id: 'profile.follow_communities' })}
|
||||
</Text>
|
||||
<FlatList
|
||||
data={recommendedCommunities}
|
||||
keyExtractor={(item, index) => `${item.id || item.title}${index}`}
|
||||
renderItem={({ item, index }) => (
|
||||
<CommunityListItem
|
||||
index={index}
|
||||
title={item.title}
|
||||
about={item.about}
|
||||
admins={item.admins}
|
||||
id={item.id}
|
||||
authors={item.num_authors}
|
||||
posts={item.num_pending}
|
||||
subscribers={item.subscribers}
|
||||
isNsfw={item.is_nsfw}
|
||||
name={item.name}
|
||||
handleOnPress={(name) =>
|
||||
navigation.navigate({
|
||||
routeName: ROUTES.SCREENS.COMMUNITY,
|
||||
params: {
|
||||
tag: name,
|
||||
},
|
||||
})
|
||||
}
|
||||
handleSubscribeButtonPress={_handleSubscribeCommunityButtonPress}
|
||||
isSubscribed={item.isSubscribed}
|
||||
isLoadingRightAction={
|
||||
subscribingCommunities.hasOwnProperty(item.name) &&
|
||||
subscribingCommunities[item.name].loading
|
||||
}
|
||||
isLoggedIn={isLoggedIn}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return <Text style={[globalStyles.subTitle, styles.noPostTitle]}>{intl.formatMessage({ id: 'profile.havent_posted' })}</Text>;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.placeholderWrapper}>
|
||||
<PostCardPlaceHolder />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default withNavigation(TabEmptyView);
|
||||
|
92
src/components/tabbedPosts/view/tabContent.tsx
Normal file
92
src/components/tabbedPosts/view/tabContent.tsx
Normal file
@ -0,0 +1,92 @@
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import PostsList from '../../postsList';
|
||||
import { loadPosts } from '../services/tabbedPostsFetch';
|
||||
import { LoadPostsOptions, TabMeta } from '../services/tabbedPostsModels';
|
||||
import {useSelector } from 'react-redux';
|
||||
import TabEmptyView from './listEmptyView';
|
||||
|
||||
interface TabContentProps {
|
||||
filterKey:string,
|
||||
tabLabel:string,
|
||||
isFeedScreen:boolean,
|
||||
promotedPosts:any[],
|
||||
getFor:string,
|
||||
pageType:string,
|
||||
feedUsername:string,
|
||||
tag:string,
|
||||
}
|
||||
|
||||
const TabContent = ({
|
||||
filterKey,
|
||||
isFeedScreen,
|
||||
promotedPosts,
|
||||
...props
|
||||
}: TabContentProps) => {
|
||||
|
||||
//redux properties
|
||||
const isLoggedIn = useSelector((state) => state.application.isLoggedIn);
|
||||
const isAnalytics = useSelector((state) => state.application.isAnalytics);
|
||||
const nsfw = useSelector((state) => state.application.nsfw);
|
||||
const isConnected = useSelector((state) => state.application.isConnected);
|
||||
|
||||
//state
|
||||
const [posts, setPosts] = useState([]);
|
||||
const [tabMeta, setTabMeta] = useState({
|
||||
startAuthor:'',
|
||||
startPermlink:'',
|
||||
isLoading:false,
|
||||
isRefreshing:false,
|
||||
} as TabMeta)
|
||||
|
||||
|
||||
useEffect(()=>{
|
||||
_loadPosts();
|
||||
},[])
|
||||
|
||||
//load posts implementation
|
||||
const _loadPosts = async (shouldReset:boolean = false) => {
|
||||
const options = {
|
||||
filterKey,
|
||||
prevPosts:posts,
|
||||
tabMeta,
|
||||
setTabMeta,
|
||||
isLoggedIn,
|
||||
isAnalytics,
|
||||
nsfw,
|
||||
isConnected,
|
||||
isFeedScreen,
|
||||
refreshing:shouldReset,
|
||||
...props
|
||||
} as LoadPostsOptions
|
||||
|
||||
const updatedPosts = await loadPosts(options)
|
||||
if(updatedPosts){
|
||||
setPosts(updatedPosts);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
const _renderEmptyContent = () => {
|
||||
return <TabEmptyView filterKey={filterKey} isNoPost={tabMeta.isNoPost}/>
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<PostsList
|
||||
data={posts}
|
||||
isFeedScreen={isFeedScreen}
|
||||
promotedPosts={promotedPosts}
|
||||
onLoadPosts={_loadPosts}
|
||||
isRefreshing={tabMeta.isRefreshing}
|
||||
isLoading={tabMeta.isLoading}
|
||||
ListEmptyComponent={_renderEmptyContent}
|
||||
/>
|
||||
|
||||
);
|
||||
};
|
||||
|
||||
export default TabContent;
|
||||
|
||||
|
81
src/components/tabbedPosts/view/tabbedPostsStyles.tsx
Normal file
81
src/components/tabbedPosts/view/tabbedPostsStyles.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import EStyleSheet from 'react-native-extended-stylesheet';
|
||||
|
||||
export default EStyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '$primaryLightBackground',
|
||||
},
|
||||
placeholder: {
|
||||
backgroundColor: '$primaryBackgroundColor',
|
||||
padding: 20,
|
||||
borderStyle: 'solid',
|
||||
borderWidth: 1,
|
||||
borderTopWidth: 1,
|
||||
borderColor: '#e2e5e8',
|
||||
borderRadius: 5,
|
||||
marginRight: 0,
|
||||
marginLeft: 0,
|
||||
marginTop: 10,
|
||||
},
|
||||
tabs: {
|
||||
position: 'absolute',
|
||||
top: '$deviceWidth / 30',
|
||||
alignItems: 'center',
|
||||
},
|
||||
flatlistFooter: {
|
||||
alignContent: 'center',
|
||||
alignItems: 'center',
|
||||
marginTop: 10,
|
||||
marginBottom: 60,
|
||||
borderColor: '$borderColor',
|
||||
},
|
||||
noImage: {
|
||||
width: 193,
|
||||
height: 189,
|
||||
},
|
||||
placeholderWrapper: {
|
||||
flex: 1,
|
||||
},
|
||||
noPostTitle: {
|
||||
textAlign: 'center',
|
||||
marginVertical: 16,
|
||||
color: '$primaryBlack',
|
||||
},
|
||||
popupContainer: {
|
||||
position: 'absolute',
|
||||
top: 80,
|
||||
left: 0,
|
||||
right: 0,
|
||||
alignItems: 'center',
|
||||
},
|
||||
popupContentContainer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: '$primaryBlue',
|
||||
paddingHorizontal: 0,
|
||||
paddingVertical: 2,
|
||||
borderRadius: 32,
|
||||
},
|
||||
popupText: {
|
||||
fontWeight: '500',
|
||||
color: '$white',
|
||||
marginLeft: 6,
|
||||
},
|
||||
closeIcon: {
|
||||
color: '$white',
|
||||
margin: 0,
|
||||
padding: 6,
|
||||
},
|
||||
arrowUpIcon: {
|
||||
color: '$white',
|
||||
margin: 0,
|
||||
marginHorizontal: 4,
|
||||
},
|
||||
popupImage: {
|
||||
height: 24,
|
||||
width: 24,
|
||||
borderRadius: 12,
|
||||
borderWidth: 2,
|
||||
marginLeft: -8,
|
||||
borderColor: '$primaryBlue',
|
||||
},
|
||||
});
|
Loading…
Reference in New Issue
Block a user