Merge pull request #2365 from ecency/sa/cache-in-communities

Implemenetd cache on existing communities structure
This commit is contained in:
Feruz M 2022-07-06 15:41:03 +03:00 committed by GitHub
commit e489c2df45
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 402 additions and 90 deletions

View File

@ -27,6 +27,7 @@ import { getUnreadNotificationCount } from '../../../providers/ecency/ecency';
import { decryptKey } from '../../../utils/crypto'; import { decryptKey } from '../../../utils/crypto';
import { getPointsSummary} from '../../../providers/ecency/ePoint'; import { getPointsSummary} from '../../../providers/ecency/ePoint';
import { fetchSubscribedCommunities } from '../../../redux/actions/communitiesAction'; import { fetchSubscribedCommunities } from '../../../redux/actions/communitiesAction';
import { clearSubscribedCommunitiesCache } from '../../../redux/actions/cacheActions';
const AccountsBottomSheetContainer = ({ navigation }) => { const AccountsBottomSheetContainer = ({ navigation }) => {
const intl = useIntl(); const intl = useIntl();
@ -110,6 +111,7 @@ const AccountsBottomSheetContainer = ({ navigation }) => {
_currentAccount.mutes = await getMutes(_currentAccount.username); _currentAccount.mutes = await getMutes(_currentAccount.username);
dispatch(updateCurrentAccount(_currentAccount)); dispatch(updateCurrentAccount(_currentAccount));
dispatch(clearSubscribedCommunitiesCache());
dispatch(fetchSubscribedCommunities(_currentAccount.username)) dispatch(fetchSubscribedCommunities(_currentAccount.username))
} }

View File

@ -16,6 +16,7 @@ const CommunitiesList = ({
isLoggedIn, isLoggedIn,
noResult, noResult,
screen, screen,
isDiscoversLoading,
}) => { }) => {
const _renderItem = ({ item, index }) => { const _renderItem = ({ item, index }) => {
return ( return (
@ -45,28 +46,29 @@ const CommunitiesList = ({
const _renderEmptyContent = () => { const _renderEmptyContent = () => {
return ( return (
<> isDiscoversLoading && (
<CommunitiesPlaceHolder /> <>
<CommunitiesPlaceHolder /> <CommunitiesPlaceHolder />
<CommunitiesPlaceHolder /> <CommunitiesPlaceHolder />
<CommunitiesPlaceHolder /> <CommunitiesPlaceHolder />
<CommunitiesPlaceHolder /> <CommunitiesPlaceHolder />
<CommunitiesPlaceHolder /> <CommunitiesPlaceHolder />
<CommunitiesPlaceHolder /> <CommunitiesPlaceHolder />
</> <CommunitiesPlaceHolder />
</>
)
); );
}; };
return ( return (
<SafeAreaView style={styles.container}> <SafeAreaView style={styles.container}>
{!noResult && ( <FlatList
<FlatList data={data}
data={data} keyExtractor={(item, index) => index.toString()}
keyExtractor={(item, index) => index.toString()} renderItem={true && _renderItem}
renderItem={_renderItem} ListEmptyComponent={_renderEmptyContent}
ListEmptyComponent={_renderEmptyContent} ListFooterComponent={isDiscoversLoading && <CommunitiesPlaceHolder />}
/> />
)}
</SafeAreaView> </SafeAreaView>
); );
}; };

View File

@ -28,7 +28,10 @@ const CommunitiesListItem = ({
const intl = useIntl(); const intl = useIntl();
const _handleSubscribeButtonPress = () => { const _handleSubscribeButtonPress = () => {
handleSubscribeButtonPress({ isSubscribed: isSubscribed, communityId: name }, screen); handleSubscribeButtonPress(
{ isSubscribed: isSubscribed, communityId: name, communityTitle: title },
screen,
);
}; };
return ( return (

View File

@ -14,15 +14,18 @@ import {
fetchSubscribedCommunities, fetchSubscribedCommunities,
fetchSubscribedCommunitiesSuccess, fetchSubscribedCommunitiesSuccess,
} from '../../../../redux/actions/communitiesAction'; } from '../../../../redux/actions/communitiesAction';
import { mergeSubCommunitiesCacheInSubList } from '../../../../utils/communitiesUtils';
const SelectCommunityModalContainer = ({ onPressCommunity, currentAccount, onCloseModal }) => { const SelectCommunityModalContainer = ({ onPressCommunity, currentAccount, onCloseModal }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [searchedCommunities, setSearchedCommunities] = useState([]); const [searchedCommunities, setSearchedCommunities] = useState([]);
const [showSearchedCommunities, setShowSearchedCommunities] = useState(false); const [showSearchedCommunities, setShowSearchedCommunities] = useState(false);
const [subscriptions, setSubscriptions] = useState(null);
const topCommunities = useSelector((state) => state.communities.communities); const topCommunities = useSelector((state) => state.communities.communities);
const subscribedCommunities = useSelector((state) => state.communities.subscribedCommunities); const subscribedCommunities = useSelector((state) => state.communities.subscribedCommunities);
const subscribedCommunitiesCache = useSelector((state) => state.cache.subscribedCommunities);
useEffect(() => { useEffect(() => {
callTopCommunities(); callTopCommunities();
@ -31,7 +34,22 @@ const SelectCommunityModalContainer = ({ onPressCommunity, currentAccount, onClo
const callTopCommunities = () => dispatch(fetchCommunities('', 15, '', 'rank')); const callTopCommunities = () => dispatch(fetchCommunities('', 15, '', 'rank'));
const callSubscribedCommunities = () => dispatch(fetchSubscribedCommunities(currentAccount.name)); const callSubscribedCommunities = () => {
if (
subscribedCommunities &&
subscribedCommunities.data &&
subscribedCommunities.data.length > 0
) {
const updatedSubsList = mergeSubCommunitiesCacheInSubList(
subscribedCommunities.data,
subscribedCommunitiesCache,
);
if (updatedSubsList && updatedSubsList.length > 0) {
setSubscriptions(updatedSubsList.filter((item) => item[4] === true));
}
}
dispatch(fetchSubscribedCommunities(currentAccount.name));
};
const handleChangeSearch = (text) => { const handleChangeSearch = (text) => {
if (text.length >= 3) { if (text.length >= 3) {
@ -53,7 +71,7 @@ const SelectCommunityModalContainer = ({ onPressCommunity, currentAccount, onClo
<SelectCommunityModalView <SelectCommunityModalView
onPressCommunity={onPressCommunity} onPressCommunity={onPressCommunity}
topCommunities={topCommunities} topCommunities={topCommunities}
subscribedCommunities={subscribedCommunities} subscribedCommunities={subscriptions}
onChangeSearch={debounce(handleChangeSearch, 500)} onChangeSearch={debounce(handleChangeSearch, 500)}
searchedCommunities={searchedCommunities} searchedCommunities={searchedCommunities}
showSearchedCommunities={showSearchedCommunities} showSearchedCommunities={showSearchedCommunities}

View File

@ -60,32 +60,30 @@ const SelectCommunityModalView = ({
}} }}
onPress={() => onPressCommunity(null)} onPress={() => onPressCommunity(null)}
/> />
{!subscribedCommunities.loading && {subscribedCommunities && (
!subscribedCommunities.error && <View>
subscribedCommunities.data?.length > 0 && ( <Text style={[globalStyles.label, styles.title]}>
<View> {intl.formatMessage({ id: 'editor.my_communities' }).toUpperCase()}
<Text style={[globalStyles.label, styles.title]}> </Text>
{intl.formatMessage({ id: 'editor.my_communities' }).toUpperCase()} <FlatList
</Text> ItemSeparatorComponent={() => <Separator />}
<FlatList showsVerticalScrollIndicator={false}
ItemSeparatorComponent={() => <Separator />} data={subscribedCommunities}
showsVerticalScrollIndicator={false} keyExtractor={(item, index) => index.toString()}
data={subscribedCommunities.data} renderItem={({ item, index, separators }) => {
keyExtractor={(item, index) => index.toString()} const community = { name: item[0], title: item[1] };
renderItem={({ item, index, separators }) => { return (
const community = { name: item[0], title: item[1] }; <CommunityCard
return ( community={community}
<CommunityCard key={community.name}
community={community} onPress={onPressCommunity}
key={community.name} separators={separators}
onPress={onPressCommunity} />
separators={separators} );
/> }}
); />
}} </View>
/> )}
</View>
)}
{!topCommunities.loading && !topCommunities.error && topCommunities.data?.length > 0 && ( {!topCommunities.loading && !topCommunities.error && topCommunities.data?.length > 0 && (
<View> <View>
<Text style={[globalStyles.label, styles.title]}> <Text style={[globalStyles.label, styles.title]}>

View File

@ -79,8 +79,11 @@ const SubscribedCommunitiesListView = ({
onPress={() => onPress={() =>
handleSubscribeButtonPress( handleSubscribeButtonPress(
{ {
isSubscribed: item[4],
communityId: item[0], communityId: item[0],
communityTitle: item[1],
userRole: item[2],
userLabel: item[3],
isSubscribed: item[4],
}, },
'communitiesScreenJoinedTab', 'communitiesScreenJoinedTab',
) )

View File

@ -8,8 +8,11 @@ import {
DELETE_COMMENT_CACHE_ENTRY, DELETE_COMMENT_CACHE_ENTRY,
UPDATE_DRAFT_CACHE, UPDATE_DRAFT_CACHE,
DELETE_DRAFT_CACHE_ENTRY, DELETE_DRAFT_CACHE_ENTRY,
UPDATE_SUBSCRIBED_COMMUNITY_CACHE,
DELETE_SUBSCRIBED_COMMUNITY_CACHE,
CLEAR_SUBSCRIBED_COMMUNITIES_CACHE,
} from '../constants/constants'; } from '../constants/constants';
import { Comment, Draft, Vote } from '../reducers/cacheReducer'; import { Comment, Draft, SubscribedCommunity, Vote } from '../reducers/cacheReducer';
@ -87,6 +90,37 @@ export const deleteDraftCacheEntry = (id: string) => ({
type: DELETE_DRAFT_CACHE_ENTRY type: DELETE_DRAFT_CACHE_ENTRY
}) })
export const updateSubscribedCommunitiesCache = (data: any) => {
const path = data.communityId;
const created = new Date();
const communityTitle = data.communityTitle ? data.communityTitle : '';
const userRole = data.userRole ? data.userRole : '';
const userLabel = data.userLabel ? data.userLabel : '';
const subscribedCommunity:SubscribedCommunity = {
data : [data.communityId, communityTitle, userRole, userLabel, !data.isSubscribed],
expiresAt : created.getTime() + 86400000,
};
return ({
payload: {
path,
subscribedCommunity
},
type: UPDATE_SUBSCRIBED_COMMUNITY_CACHE
})
}
export const deleteSubscribedCommunityCacheEntry = (path: string) => ({
payload: path,
type: DELETE_SUBSCRIBED_COMMUNITY_CACHE
})
export const clearSubscribedCommunitiesCache = () => ({
type: CLEAR_SUBSCRIBED_COMMUNITIES_CACHE
})
export const purgeExpiredCache = () => ({ export const purgeExpiredCache = () => ({
type: PURGE_EXPIRED_CACHE type: PURGE_EXPIRED_CACHE
}) })

View File

@ -45,7 +45,11 @@ export const fetchSubscribedCommunities = (username) => {
return (dispatch) => { return (dispatch) => {
dispatch({ type: FETCH_SUBSCRIBED_COMMUNITIES }); dispatch({ type: FETCH_SUBSCRIBED_COMMUNITIES });
getSubscriptions(username) getSubscriptions(username)
.then((res) => dispatch(fetchSubscribedCommunitiesSuccess(res))) .then((res) => {
res.forEach((item) => item.push(true)); //add true value for subscribe status
res.sort((a, b) => a[1].localeCompare(b[1]));
dispatch(fetchSubscribedCommunitiesSuccess(res));
})
.catch((err) => dispatch(fetchSubscribedCommunitiesFail(err))); .catch((err) => dispatch(fetchSubscribedCommunitiesFail(err)));
}; };
}; };

View File

@ -0,0 +1,6 @@
export const statusMessage = {
PENDING : 'PENDING',
SUCCESS : 'SUCCESS',
FAIL : 'FAIL',
};

View File

@ -114,6 +114,9 @@ export const DELETE_COMMENT_CACHE_ENTRY = 'DELETE_COMMENT_CACHE_ENTRY';
export const UPDATE_DRAFT_CACHE = 'UPDATE_DRAFT_CACHE'; export const UPDATE_DRAFT_CACHE = 'UPDATE_DRAFT_CACHE';
export const DELETE_DRAFT_CACHE_ENTRY = 'DELETE_DRAFT_CACHE_ENTRY'; export const DELETE_DRAFT_CACHE_ENTRY = 'DELETE_DRAFT_CACHE_ENTRY';
export const DEFAULT_USER_DRAFT_ID = 'DEFAULT_USER_DRAFT_ID_'; export const DEFAULT_USER_DRAFT_ID = 'DEFAULT_USER_DRAFT_ID_';
export const UPDATE_SUBSCRIBED_COMMUNITY_CACHE = 'UPDATE_SUBSCRIBED_COMMUNITY_CACHE';
export const DELETE_SUBSCRIBED_COMMUNITY_CACHE = 'DELETE_SUBSCRIBED_COMMUNITY_CACHE';
export const CLEAR_SUBSCRIBED_COMMUNITIES_CACHE = 'CLEAR_SUBSCRIBED_COMMUNITIES_CACHE';
// TOOLTIPS // TOOLTIPS
export const REGISTER_TOOLTIP = 'REGISTER_TOOLTIP'; export const REGISTER_TOOLTIP = 'REGISTER_TOOLTIP';

View File

@ -1,4 +1,4 @@
import { PURGE_EXPIRED_CACHE, UPDATE_VOTE_CACHE, UPDATE_COMMENT_CACHE, DELETE_COMMENT_CACHE_ENTRY, DELETE_DRAFT_CACHE_ENTRY, UPDATE_DRAFT_CACHE, } from "../constants/constants"; import { PURGE_EXPIRED_CACHE, UPDATE_VOTE_CACHE, UPDATE_COMMENT_CACHE, DELETE_COMMENT_CACHE_ENTRY, DELETE_DRAFT_CACHE_ENTRY, UPDATE_DRAFT_CACHE, UPDATE_SUBSCRIBED_COMMUNITY_CACHE, DELETE_SUBSCRIBED_COMMUNITY_CACHE, CLEAR_SUBSCRIBED_COMMUNITIES_CACHE, } from "../constants/constants";
export interface Vote { export interface Vote {
amount:number; amount:number;
@ -37,10 +37,15 @@ export interface Draft {
expiresAt?:number; expiresAt?:number;
} }
export interface SubscribedCommunity {
data: Array<any>,
expiresAt?:number;
}
interface State { interface State {
votes:Map<string, Vote> votes:Map<string, Vote>
comments:Map<string, Comment> //TODO: handle comment array per post, if parent is same comments:Map<string, Comment> //TODO: handle comment array per post, if parent is same
drafts: Map<string, Draft> drafts: Map<string, Draft>
subscribedCommunities: Map<string, SubscribedCommunity>
lastUpdate:{ lastUpdate:{
postPath:string, postPath:string,
updatedAt:number, updatedAt:number,
@ -52,6 +57,7 @@ const initialState:State = {
votes:new Map(), votes:new Map(),
comments:new Map(), comments:new Map(),
drafts: new Map(), drafts: new Map(),
subscribedCommunities: new Map(),
lastUpdate:null, lastUpdate:null,
}; };
@ -121,6 +127,28 @@ const initialState:State = {
} }
return { ...state } return { ...state }
case UPDATE_SUBSCRIBED_COMMUNITY_CACHE:
if(!state.subscribedCommunities){
state.subscribedCommunities = new Map<string, SubscribedCommunity>();
}
const subscribedCommunities = new Map(state.subscribedCommunities);
subscribedCommunities.set(payload.path, payload.subscribedCommunity);
return {
...state, //spread operator in requried here, otherwise persist do not register change
subscribedCommunities: subscribedCommunities
};
case DELETE_SUBSCRIBED_COMMUNITY_CACHE:
if (state.subscribedCommunities && state.subscribedCommunities.has(payload)) {
state.subscribedCommunities.delete(payload);
}
return { ...state }
case CLEAR_SUBSCRIBED_COMMUNITIES_CACHE:
state.subscribedCommunities = new Map<string, SubscribedCommunity>();
return {...state}
case PURGE_EXPIRED_CACHE: case PURGE_EXPIRED_CACHE:
const currentTime = new Date().getTime(); const currentTime = new Date().getTime();
@ -148,6 +176,14 @@ const initialState:State = {
}) })
} }
if(state.subscribedCommunities && state.subscribedCommunities.size){
Array.from(state.subscribedCommunities).forEach((entry)=>{
if(entry[1].expiresAt < currentTime){
state.subscribedCommunities.delete(entry[0]);
}
})
}
return { return {
...state ...state
} }

View File

@ -12,6 +12,7 @@ import {
LEAVE_COMMUNITY_SUCCESS, LEAVE_COMMUNITY_SUCCESS,
LEAVE_COMMUNITY_FAIL, LEAVE_COMMUNITY_FAIL,
} from '../constants/constants'; } from '../constants/constants';
import { statusMessage } from '../constants/communitiesConstants';
const initialState = { const initialState = {
communities: { communities: {
@ -23,6 +24,7 @@ const initialState = {
data: [], data: [],
loading: false, loading: false,
error: false, error: false,
status: statusMessage.PENDING,
}, },
subscribingCommunitiesInFeedScreen: { subscribingCommunitiesInFeedScreen: {
//['name']: { //['name']: {
@ -90,6 +92,7 @@ export default function (state = initialState, action) {
data: [], data: [],
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}; };
case FETCH_SUBSCRIBED_COMMUNITIES_SUCCESS: case FETCH_SUBSCRIBED_COMMUNITIES_SUCCESS:
@ -99,6 +102,7 @@ export default function (state = initialState, action) {
data: action.payload || [], data: action.payload || [],
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}; };
case FETCH_SUBSCRIBED_COMMUNITIES_FAIL: case FETCH_SUBSCRIBED_COMMUNITIES_FAIL:
@ -108,6 +112,7 @@ export default function (state = initialState, action) {
data: [], data: [],
loading: false, loading: false,
error: action.payload, error: action.payload,
status: statusMessage.FAIL,
}, },
}; };
case SUBSCRIBE_COMMUNITY: case SUBSCRIBE_COMMUNITY:
@ -121,6 +126,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -133,6 +139,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -145,6 +152,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -157,6 +165,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -174,6 +183,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -186,6 +196,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -198,6 +209,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -210,6 +222,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -227,6 +240,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };
@ -239,6 +253,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };
@ -251,6 +266,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };
@ -263,6 +279,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };
@ -280,6 +297,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -292,6 +310,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -304,6 +323,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -316,6 +336,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: true, loading: true,
error: false, error: false,
status: statusMessage.PENDING,
}, },
}, },
}; };
@ -333,6 +354,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -345,6 +367,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -357,6 +380,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -369,6 +393,7 @@ export default function (state = initialState, action) {
isSubscribed: false, isSubscribed: false,
loading: false, loading: false,
error: false, error: false,
status: statusMessage.SUCCESS,
}, },
}, },
}; };
@ -386,6 +411,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };
@ -398,6 +424,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };
@ -410,6 +437,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };
@ -422,6 +450,7 @@ export default function (state = initialState, action) {
isSubscribed: true, isSubscribed: true,
loading: false, loading: false,
error: true, error: true,
status: statusMessage.FAIL,
}, },
}, },
}; };

View File

@ -13,12 +13,14 @@ const transformCacheVoteMap = createTransform(
votes : Array.from(inboundState.votes), votes : Array.from(inboundState.votes),
comments : Array.from(inboundState.comments), comments : Array.from(inboundState.comments),
drafts : Array.from(inboundState.drafts), drafts : Array.from(inboundState.drafts),
subscribedCommunities: Array.from(inboundState.subscribedCommunities)
}), }),
(outboundState) => ({ (outboundState) => ({
...outboundState, ...outboundState,
votes:new Map(outboundState.votes), votes:new Map(outboundState.votes),
comments:new Map(outboundState.comments), comments:new Map(outboundState.comments),
drafts: new Map(outboundState.drafts) drafts: new Map(outboundState.drafts),
subscribedCommunities: new Map(outboundState.subscribedCommunities)
}), }),
{whitelist:['cache']} {whitelist:['cache']}
); );

View File

@ -1,14 +1,27 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { withNavigation } from 'react-navigation'; import { withNavigation } from 'react-navigation';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { shuffle } from 'lodash'; import { shuffle, isEmpty } from 'lodash';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import ROUTES from '../../../constants/routeNames'; import ROUTES from '../../../constants/routeNames';
import { getCommunities, getSubscriptions } from '../../../providers/hive/dhive'; import { getCommunities, getSubscriptions } from '../../../providers/hive/dhive';
import { subscribeCommunity, leaveCommunity } from '../../../redux/actions/communitiesAction'; import {
subscribeCommunity,
leaveCommunity,
fetchSubscribedCommunitiesSuccess,
} from '../../../redux/actions/communitiesAction';
import { statusMessage } from '../../../redux/constants/communitiesConstants';
import {
deleteSubscribedCommunityCacheEntry,
updateSubscribedCommunitiesCache,
} from '../../../redux/actions/cacheActions';
import {
mergeSubCommunitiesCacheInDiscoverList,
mergeSubCommunitiesCacheInSubList,
} from '../../../utils/communitiesUtils';
const CommunitiesContainer = ({ children, navigation }) => { const CommunitiesContainer = ({ children, navigation }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -17,23 +30,67 @@ const CommunitiesContainer = ({ children, navigation }) => {
const [discovers, setDiscovers] = useState([]); const [discovers, setDiscovers] = useState([]);
const [subscriptions, setSubscriptions] = useState([]); const [subscriptions, setSubscriptions] = useState([]);
const [isSubscriptionsLoading, setIsSubscriptionsLoading] = useState(true); const [isSubscriptionsLoading, setIsSubscriptionsLoading] = useState(true);
const [isDiscoversLoading, setIsDiscoversLoading] = useState(true);
const [selectedCommunityItem, setSelectedCommunityItem] = useState(null);
const currentAccount = useSelector((state) => state.account.currentAccount); const currentAccount = useSelector((state) => state.account.currentAccount);
const pinCode = useSelector((state) => state.application.pin); const pinCode = useSelector((state) => state.application.pin);
const subscribedCommunities = useSelector((state) => state.communities.subscribedCommunities);
const subscribingCommunitiesInDiscoverTab = useSelector( const subscribingCommunitiesInDiscoverTab = useSelector(
(state) => state.communities.subscribingCommunitiesInCommunitiesScreenDiscoverTab, (state) => state.communities.subscribingCommunitiesInCommunitiesScreenDiscoverTab,
); );
const subscribingCommunitiesInJoinedTab = useSelector( const subscribingCommunitiesInJoinedTab = useSelector(
(state) => state.communities.subscribingCommunitiesInCommunitiesScreenJoinedTab, (state) => state.communities.subscribingCommunitiesInCommunitiesScreenJoinedTab,
); );
const subscribedCommunitiesCache = useSelector((state) => state.cache.subscribedCommunities);
useEffect(() => { useEffect(() => {
_getSubscriptions(); _getSubscriptions();
}, []); }, []);
// handle cache in joined/membership tab
useEffect(() => {
if (subscribingCommunitiesInJoinedTab && selectedCommunityItem) {
const { status } = subscribingCommunitiesInJoinedTab[selectedCommunityItem.communityId];
if (status === statusMessage.SUCCESS) {
dispatch(updateSubscribedCommunitiesCache(selectedCommunityItem));
}
}
}, [subscribingCommunitiesInJoinedTab]);
// handle cache in discover tab
useEffect(() => {
if (subscribingCommunitiesInDiscoverTab && selectedCommunityItem) {
const { status } = subscribingCommunitiesInDiscoverTab[selectedCommunityItem.communityId];
if (status === statusMessage.SUCCESS) {
dispatch(updateSubscribedCommunitiesCache(selectedCommunityItem));
}
}
}, [subscribingCommunitiesInDiscoverTab]);
// side effect for subscribed communities cache update
useEffect(() => {
if (
subscribedCommunitiesCache &&
subscribedCommunitiesCache.size &&
subscriptions &&
subscriptions.length > 0
) {
const updatedSubsList = mergeSubCommunitiesCacheInSubList(
subscriptions,
subscribedCommunitiesCache,
);
const updatedDiscoversList = mergeSubCommunitiesCacheInDiscoverList(
discovers,
subscribedCommunitiesCache,
);
setSubscriptions(updatedSubsList.slice());
setDiscovers(updatedDiscoversList);
}
}, [subscribedCommunitiesCache]);
useEffect(() => { useEffect(() => {
const discoversData = [...discovers]; const discoversData = [...discovers];
Object.keys(subscribingCommunitiesInDiscoverTab).map((communityId) => { Object.keys(subscribingCommunitiesInDiscoverTab).map((communityId) => {
if (!subscribingCommunitiesInDiscoverTab[communityId].loading) { if (!subscribingCommunitiesInDiscoverTab[communityId].loading) {
if (!subscribingCommunitiesInDiscoverTab[communityId].error) { if (!subscribingCommunitiesInDiscoverTab[communityId].error) {
@ -58,36 +115,54 @@ const CommunitiesContainer = ({ children, navigation }) => {
}, [subscribingCommunitiesInDiscoverTab]); }, [subscribingCommunitiesInDiscoverTab]);
useEffect(() => { useEffect(() => {
const subscribedsData = [...subscriptions]; if (!isEmpty(subscribingCommunitiesInJoinedTab)) {
const subscribedsData = mergeSubCommunitiesCacheInSubList(
Object.keys(subscribingCommunitiesInJoinedTab).map((communityId) => { subscribedCommunities.data,
if (!subscribingCommunitiesInJoinedTab[communityId].loading) { subscribedCommunitiesCache,
if (!subscribingCommunitiesInJoinedTab[communityId].error) { );
if (subscribingCommunitiesInJoinedTab[communityId].isSubscribed) { Object.keys(subscribingCommunitiesInJoinedTab).map((communityId) => {
subscribedsData.forEach((item) => { if (!subscribingCommunitiesInJoinedTab[communityId].loading) {
if (item[0] === communityId) { if (!subscribingCommunitiesInJoinedTab[communityId].error) {
item[4] = true; if (subscribingCommunitiesInJoinedTab[communityId].isSubscribed) {
} subscribedsData.forEach((item) => {
}); if (item[0] === communityId) {
} else { item[4] = true;
subscribedsData.forEach((item) => { }
if (item[0] === communityId) { });
item[4] = false; } else {
} subscribedsData.forEach((item) => {
}); if (item[0] === communityId) {
item[4] = false;
}
});
}
} }
} }
} });
});
setSubscriptions(subscribedsData); setSubscriptions(subscribedsData);
}
}, [subscribingCommunitiesInJoinedTab]); }, [subscribingCommunitiesInJoinedTab]);
const _getSubscriptions = () => { const _getSubscriptions = () => {
setIsSubscriptionsLoading(true); setIsSubscriptionsLoading(true);
setIsDiscoversLoading(true);
if (
subscribedCommunities &&
subscribedCommunities.data &&
subscribedCommunities.data.length > 0
) {
const updatedSubsList = mergeSubCommunitiesCacheInSubList(
subscribedCommunities.data,
subscribedCommunitiesCache,
);
setSubscriptions(updatedSubsList.slice());
setIsSubscriptionsLoading(false);
}
getSubscriptions(currentAccount.username) getSubscriptions(currentAccount.username)
.then((subs) => { .then((subs) => {
subs.forEach((item) => item.push(true)); subs.forEach((item) => item.push(true));
_invalidateSubscribedCommunityCache(subs); // invalidate subscribed communities cache item when latest data is available
getCommunities('', 50, null, 'rank').then((communities) => { getCommunities('', 50, null, 'rank').then((communities) => {
communities.forEach((community) => communities.forEach((community) =>
Object.assign(community, { Object.assign(community, {
@ -97,17 +172,30 @@ const CommunitiesContainer = ({ children, navigation }) => {
}), }),
); );
setSubscriptions(subs); setSubscriptions(mergeSubCommunitiesCacheInSubList(subs, subscribedCommunitiesCache)); //merge cache with fetched data
setDiscovers(shuffle(communities)); setDiscovers(communities);
setIsSubscriptionsLoading(false); setIsSubscriptionsLoading(false);
setIsDiscoversLoading(false);
dispatch(
fetchSubscribedCommunitiesSuccess(subs.sort((a, b) => a[1].localeCompare(b[1]))),
); //register subscribed data in communities store
}); });
}) })
.catch((err) => { .catch((err) => {
console.warn('Failed to get subscriptions', err); console.warn('Failed to get subscriptions', err);
setIsSubscriptionsLoading(false); setIsSubscriptionsLoading(false);
setIsDiscoversLoading(false);
}); });
}; };
const _invalidateSubscribedCommunityCache = (fetchedList) => {
fetchedList.map((listItem) => {
let itemExists = subscribedCommunitiesCache.get(listItem[0]);
if (itemExists) {
dispatch(deleteSubscribedCommunityCacheEntry(listItem[0]));
}
});
};
// Component Functions // Component Functions
const _handleOnPress = (name) => { const _handleOnPress = (name) => {
navigation.navigate({ navigation.navigate({
@ -119,6 +207,7 @@ const CommunitiesContainer = ({ children, navigation }) => {
}; };
const _handleSubscribeButtonPress = (data, screen) => { const _handleSubscribeButtonPress = (data, screen) => {
setSelectedCommunityItem(data); //set selected item to handle its cache
let subscribeAction; let subscribeAction;
let successToastText = ''; let successToastText = '';
let failToastText = ''; let failToastText = '';
@ -156,6 +245,7 @@ const CommunitiesContainer = ({ children, navigation }) => {
subscribingCommunitiesInDiscoverTab, subscribingCommunitiesInDiscoverTab,
subscribingCommunitiesInJoinedTab, subscribingCommunitiesInJoinedTab,
isSubscriptionsLoading, isSubscriptionsLoading,
isDiscoversLoading,
handleOnPress: _handleOnPress, handleOnPress: _handleOnPress,
handleSubscribeButtonPress: _handleSubscribeButtonPress, handleSubscribeButtonPress: _handleSubscribeButtonPress,
handleGetSubscriptions: _getSubscriptions, handleGetSubscriptions: _getSubscriptions,

View File

@ -47,6 +47,7 @@ const CommunitiesScreen = () => {
subscribingCommunitiesInJoinedTab, subscribingCommunitiesInJoinedTab,
handleGetSubscriptions, handleGetSubscriptions,
isSubscriptionsLoading, isSubscriptionsLoading,
isDiscoversLoading,
}) => { }) => {
return ( return (
<View style={styles.container}> <View style={styles.container}>
@ -87,6 +88,7 @@ const CommunitiesScreen = () => {
isLoggedIn={true} isLoggedIn={true}
noResult={discovers.length === 0} noResult={discovers.length === 0}
screen="communitiesScreenDiscoverTab" screen="communitiesScreenDiscoverTab"
isDiscoversLoading={isDiscoversLoading}
/> />
</View> </View>
</ScrollableTabView> </ScrollableTabView>

View File

@ -1,7 +1,7 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { withNavigation } from 'react-navigation'; import { withNavigation } from 'react-navigation';
import get from 'lodash/get'; import get from 'lodash/get';
import { connect, useDispatch } from 'react-redux'; import { connect, useDispatch, useSelector } from 'react-redux';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { getCommunity, getSubscriptions } from '../../../providers/hive/dhive'; import { getCommunity, getSubscriptions } from '../../../providers/hive/dhive';
@ -9,14 +9,32 @@ import { getCommunity, getSubscriptions } from '../../../providers/hive/dhive';
import { subscribeCommunity, leaveCommunity } from '../../../redux/actions/communitiesAction'; import { subscribeCommunity, leaveCommunity } from '../../../redux/actions/communitiesAction';
import ROUTES from '../../../constants/routeNames'; import ROUTES from '../../../constants/routeNames';
import { updateSubscribedCommunitiesCache } from '../../../redux/actions/cacheActions';
import { statusMessage } from '../../../redux/constants/communitiesConstants';
const CommunityContainer = ({ children, navigation, currentAccount, pinCode, isLoggedIn }) => { const CommunityContainer = ({ children, navigation, currentAccount, pinCode, isLoggedIn }) => {
const [data, setData] = useState(null); const [data, setData] = useState(null);
const [isSubscribed, setIsSubscribed] = useState(false); const [isSubscribed, setIsSubscribed] = useState(false);
const [selectedCommunityItem, setSelectedCommunityItem] = useState(null);
const tag = get(navigation, 'state.params.tag'); const tag = get(navigation, 'state.params.tag');
const dispatch = useDispatch(); const dispatch = useDispatch();
const intl = useIntl(); const intl = useIntl();
const subscribingCommunitiesInDiscoverTab = useSelector(
(state) => state.communities.subscribingCommunitiesInCommunitiesScreenDiscoverTab,
);
const subscribedCommunitiesCache = useSelector((state) => state.cache.subscribedCommunities);
useEffect(() => {
if (subscribingCommunitiesInDiscoverTab && selectedCommunityItem) {
const { status } = subscribingCommunitiesInDiscoverTab[selectedCommunityItem.communityId];
if (status === statusMessage.SUCCESS) {
dispatch(updateSubscribedCommunitiesCache(selectedCommunityItem));
}
}
}, [subscribingCommunitiesInDiscoverTab]);
useEffect(() => { useEffect(() => {
getCommunity(tag) getCommunity(tag)
.then((res) => { .then((res) => {
@ -29,17 +47,26 @@ const CommunityContainer = ({ children, navigation, currentAccount, pinCode, isL
useEffect(() => { useEffect(() => {
if (data) { if (data) {
//check and set user role if (
getSubscriptions(currentAccount.username) subscribedCommunitiesCache &&
.then((result) => { subscribedCommunitiesCache.size &&
if (result) { subscribedCommunitiesCache.get(data.name)
const _isSubscribed = result.some((item) => item[0] === data.name); ) {
setIsSubscribed(_isSubscribed); const itemExistInCache = subscribedCommunitiesCache.get(data.name);
} setIsSubscribed(itemExistInCache.data[4]); //if item exist in cache, get isSubscribed value from cache
}) } else {
.catch((e) => { //check and set user role
console.log(e); getSubscriptions(currentAccount.username)
}); .then((result) => {
if (result) {
const _isSubscribed = result.some((item) => item[0] === data.name);
setIsSubscribed(_isSubscribed);
}
})
.catch((e) => {
console.log(e);
});
}
} }
}, [data]); }, [data]);
@ -48,6 +75,7 @@ const CommunityContainer = ({ children, navigation, currentAccount, pinCode, isL
isSubscribed: isSubscribed, isSubscribed: isSubscribed,
communityId: data.name, communityId: data.name,
}; };
setSelectedCommunityItem(_data); //set selected item to handle its cache
const screen = 'communitiesScreenDiscoverTab'; const screen = 'communitiesScreenDiscoverTab';
let subscribeAction; let subscribeAction;
let successToastText = ''; let successToastText = '';

View File

@ -33,7 +33,7 @@ const CommunitiesResultsContainer = ({ children, navigation, searchValue }) => {
const [data, setData] = useState([]); const [data, setData] = useState([]);
const [noResult, setNoResult] = useState(false); const [noResult, setNoResult] = useState(false);
const [isDiscoversLoading, setIsDiscoversLoading] = useState(false);
const pinCode = useSelector((state) => state.application.pin); const pinCode = useSelector((state) => state.application.pin);
const currentAccount = useSelector((state) => state.account.currentAccount); const currentAccount = useSelector((state) => state.account.currentAccount);
const isLoggedIn = useSelector((state) => state.application.isLoggedIn); const isLoggedIn = useSelector((state) => state.application.isLoggedIn);
@ -44,7 +44,7 @@ const CommunitiesResultsContainer = ({ children, navigation, searchValue }) => {
useEffect(() => { useEffect(() => {
setData([]); setData([]);
setNoResult(false); setNoResult(false);
setIsDiscoversLoading(true);
getCommunities('', searchValue ? 100 : 20, searchValue || null, 'rank') getCommunities('', searchValue ? 100 : 20, searchValue || null, 'rank')
.then((communities) => { .then((communities) => {
if (currentAccount && currentAccount.username) { if (currentAccount && currentAccount.username) {
@ -78,10 +78,12 @@ const CommunitiesResultsContainer = ({ children, navigation, searchValue }) => {
setNoResult(true); setNoResult(true);
} }
} }
setIsDiscoversLoading(false);
}) })
.catch((err) => { .catch((err) => {
setNoResult(true); setNoResult(true);
setData([]); setData([]);
setIsDiscoversLoading(false);
}); });
}, [searchValue]); }, [searchValue]);
@ -160,6 +162,7 @@ const CommunitiesResultsContainer = ({ children, navigation, searchValue }) => {
handleSubscribeButtonPress: _handleSubscribeButtonPress, handleSubscribeButtonPress: _handleSubscribeButtonPress,
isLoggedIn, isLoggedIn,
noResult, noResult,
isDiscoversLoading,
}) })
); );
}; };

View File

@ -18,6 +18,7 @@ const CommunitiesResultsScreen = ({ navigation, searchValue }) => {
handleSubscribeButtonPress, handleSubscribeButtonPress,
isLoggedIn, isLoggedIn,
noResult, noResult,
isDiscoversLoading,
}) => }) =>
noResult ? ( noResult ? (
<EmptyScreen /> <EmptyScreen />
@ -30,6 +31,7 @@ const CommunitiesResultsScreen = ({ navigation, searchValue }) => {
isLoggedIn={isLoggedIn} isLoggedIn={isLoggedIn}
noResult={noResult} noResult={noResult}
screen="searchResultsScreen" screen="searchResultsScreen"
isDiscoversLoading={isDiscoversLoading}
/> />
) )
} }

View File

@ -0,0 +1,47 @@
import { SubscribedCommunity } from '../redux/reducers/cacheReducer';
/**
* Accepts Array of subscription items arrays as 1st argument, community cache map as second argument.
* Returns single array with union of both lists, sorted alphabatically
* Example subList = [['id', 'title', 'role', 'label', 'true/false'],['id', 'title', 'role', 'label', 'true/false']]
*
**/
export const mergeSubCommunitiesCacheInSubList = (
subList: any[],
cacheMap: Map<string, SubscribedCommunity>,
) => {
if (!cacheMap || !cacheMap.size) {
return subList.sort((a, b) => a[1].localeCompare(b[1]));
}
const cacheList = Array.from(cacheMap, ([path, item]) => item.data);
cacheList.map((cacheListItem) => {
let index = subList.findIndex((subListItem) => subListItem[0] === cacheListItem[0]);
if (index !== -1) {
subList[index] = [...cacheListItem];
} else {
subList.push(cacheListItem);
}
});
return subList.sort((a, b) => a[1].localeCompare(b[1]));
};
/**
* Accepts Array of discover items arrays as 1st argument, community cache map as second argument.
* Returns discovers list with updated isSubscribed status
*
**/
export const mergeSubCommunitiesCacheInDiscoverList = (
discoverList: any[],
cacheMap: Map<string, SubscribedCommunity>,
) => {
if (!cacheMap || !cacheMap.size) {
return discoverList;
}
discoverList.forEach((discoverListItem) => {
let itemExist = cacheMap.get(discoverListItem.name);
if (itemExist) {
discoverListItem.isSubscribed = itemExist.data[4];
}
});
return discoverList;
};