From 7b163e7e8b3b604913a49abbdedac1941692615e Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 12:07:34 +0300 Subject: [PATCH 01/21] add caching of inbox items --- src/pages/Auth/store/saga.tsx | 2 ++ src/pages/inbox/BaseInbox.tsx | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/Auth/store/saga.tsx b/src/pages/Auth/store/saga.tsx index 6aa36334b2..d4ae6d2b1e 100755 --- a/src/pages/Auth/store/saga.tsx +++ b/src/pages/Auth/store/saga.tsx @@ -16,6 +16,7 @@ import { getFundingRequestNotification } from "@/shared/utils/notifications"; import { cacheActions, commonLayoutActions, + inboxActions, multipleSpacesLayoutActions, } from "@/store/states"; import { @@ -467,6 +468,7 @@ function* logOut() { yield put(multipleSpacesLayoutActions.resetMultipleSpacesLayout()); yield put(commonLayoutActions.clearData()); + yield put(inboxActions.resetInbox()); history.push(ROUTE_PATHS.HOME); yield true; } diff --git a/src/pages/inbox/BaseInbox.tsx b/src/pages/inbox/BaseInbox.tsx index 8be5cfa57e..9f39be4a29 100644 --- a/src/pages/inbox/BaseInbox.tsx +++ b/src/pages/inbox/BaseInbox.tsx @@ -221,10 +221,6 @@ const InboxPage: FC = (props) => { useEffect(() => { fetchData(); - - return () => { - dispatch(inboxActions.resetInbox()); - }; }, [userId]); useEffect(() => { From 04881b84468e2ea79e2328dcded6c7e6748b0467 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 13:04:33 +0300 Subject: [PATCH 02/21] install and configure redux-persist library --- package.json | 1 + src/pages/App/AppWrapper.tsx | 7 ++- src/shared/appConfig.ts | 4 +- src/store/store.tsx | 82 ++++++++++++++++++++++-------------- yarn.lock | 5 +++ 5 files changed, 64 insertions(+), 35 deletions(-) diff --git a/package.json b/package.json index 84a2b15b3a..309a040132 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "react-virtualized": "^9.22.3", "react-zoom-pan-pinch": "^3.2.0", "redux": "^4.0.4", + "redux-persist": "^6.0.0", "redux-saga": "^1.1.3", "reselect": "^4.0.0", "slate": "^0.94.1", diff --git a/src/pages/App/AppWrapper.tsx b/src/pages/App/AppWrapper.tsx index 3d8e85deb3..1a769f9468 100644 --- a/src/pages/App/AppWrapper.tsx +++ b/src/pages/App/AppWrapper.tsx @@ -1,11 +1,14 @@ import React, { FC } from "react"; import { Provider } from "react-redux"; -import { store } from "@/shared/appConfig"; +import { PersistGate } from "redux-persist/integration/react"; +import { store, persistor } from "@/shared/appConfig"; import { NotificationProvider } from "./providers"; const AppWrapper: FC = ({ children }) => ( - {children} + + {children} + ); diff --git a/src/shared/appConfig.ts b/src/shared/appConfig.ts index 2650847bfd..07f6406505 100644 --- a/src/shared/appConfig.ts +++ b/src/shared/appConfig.ts @@ -1,6 +1,6 @@ import configureStore from "@/store"; import history from "./history"; -const { store } = configureStore(history); +const { store, persistor } = configureStore(history); -export { history, store }; +export { history, store, persistor }; diff --git a/src/store/store.tsx b/src/store/store.tsx index 9cb6565f86..bb12a565a2 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -1,3 +1,5 @@ +import { routerMiddleware } from "connected-react-router"; +import { History } from "history"; import { createStore, applyMiddleware, @@ -6,14 +8,19 @@ import { Dispatch, Store, } from "redux"; -import { History } from "history"; -import { routerMiddleware } from "connected-react-router"; import { composeWithDevTools } from "redux-devtools-extension"; import freeze from "redux-freeze"; +import { persistStore, persistReducer, PersistConfig } from "redux-persist"; +import storage from "redux-persist/lib/storage"; import createSagaMiddleware from "redux-saga"; -import { AppState } from '@/shared/interfaces'; -import appSagas from "./saga"; +import { AppState } from "@/shared/interfaces"; import rootReducer from "./reducer"; +import appSagas from "./saga"; + +const persistConfig: PersistConfig = { + key: "root", + storage, +}; const sagaMiddleware = createSagaMiddleware(); let middleware: Array; @@ -28,52 +35,65 @@ if (process.env.NODE_ENV === "development") { composer = compose; } -const errorHandlerMiddleware: Middleware = () => (next: Dispatch) => (action) => { - if (action.type.includes("FAILURE")) { - // next( - // showNotification({ - // message: action.payload.error || action.payload.message, - // appearance: "error", - // }), - // ); +const errorHandlerMiddleware: Middleware = + () => (next: Dispatch) => (action) => { + if (action.type.includes("FAILURE")) { + // next( + // showNotification({ + // message: action.payload.error || action.payload.message, + // appearance: "error", + // }), + // ); - if (action.payload && (action.payload.code === 401 || action.payload.code === 403)) { - localStorage.clear(); + if ( + action.payload && + (action.payload.code === 401 || action.payload.code === 403) + ) { + localStorage.clear(); + } } - } - if (action.type.includes("SUCCESS") && action.payload && action.payload.message) { - // next( - // showNotification({ - // message: action.payload.message, - // appearance: "success", - // }), - // ); - } + if ( + action.type.includes("SUCCESS") && + action.payload && + action.payload.message + ) { + // next( + // showNotification({ + // message: action.payload.message, + // appearance: "success", + // }), + // ); + } - return next(action); -}; + return next(action); + }; +// defaults to localStorage for web export default function configureStore(history: History) { + const persistedReducer = persistReducer(persistConfig, rootReducer(history)); const store: Store = createStore( - rootReducer(history), + persistedReducer, undefined, composer( applyMiddleware( ...middleware, routerMiddleware(history), - errorHandlerMiddleware - ) - ) + errorHandlerMiddleware, + ), + ), ); + const persistor = persistStore(store); sagaMiddleware.run(appSagas); // eslint-disable-next-line if ((module as any).hot) { // eslint-disable-next-line - (module as any).hot.accept(() => store.replaceReducer(rootReducer(history))); + (module as any).hot.accept(() => + store.replaceReducer(persistedReducer as any), + ); } - return { store }; + return { store, persistor }; } diff --git a/yarn.lock b/yarn.lock index e4e8ed095d..a0cc55ab8e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17145,6 +17145,11 @@ redux-freeze@^0.1.7: dependencies: deep-freeze-strict "1.1.1" +redux-persist@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/redux-persist/-/redux-persist-6.0.0.tgz#b4d2972f9859597c130d40d4b146fecdab51b3a8" + integrity sha512-71LLMbUq2r02ng2We9S215LtPu3fY0KgaGE0k8WRgl6RkqxtGfl7HUozz1Dftwsb0D/5mZ8dwAaPbtnzfvbEwQ== + redux-saga@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/redux-saga/-/redux-saga-1.1.3.tgz#9f3e6aebd3c994bbc0f6901a625f9a42b51d1112" From ca1d308ffe9d626ed9db9f36249fa3ee2bafb48f Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 13:53:00 +0300 Subject: [PATCH 03/21] remove timestamp methods usage --- src/store/states/common/reducer.ts | 3 +-- src/store/states/inbox/reducer.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/store/states/common/reducer.ts b/src/store/states/common/reducer.ts index 5e4b5fbd07..03efb7c2f6 100644 --- a/src/store/states/common/reducer.ts +++ b/src/store/states/common/reducer.ts @@ -47,8 +47,7 @@ const initialState: CommonState = { const sortFeedItems = (data: FeedItemFollowLayoutItem[]): void => { data.sort( (prevItem, nextItem) => - nextItem.feedItem.updatedAt.toMillis() - - prevItem.feedItem.updatedAt.toMillis(), + nextItem.feedItem.updatedAt.seconds - prevItem.feedItem.updatedAt.seconds, ); }; diff --git a/src/store/states/inbox/reducer.ts b/src/store/states/inbox/reducer.ts index 55d625cfa3..87dca602b7 100644 --- a/src/store/states/inbox/reducer.ts +++ b/src/store/states/inbox/reducer.ts @@ -34,8 +34,8 @@ const initialState: InboxState = { const sortInboxItems = (data: FeedLayoutItemWithFollowData[]): void => { data.sort( (prevItem, nextItem) => - getFeedLayoutItemDateForSorting(nextItem).toMillis() - - getFeedLayoutItemDateForSorting(prevItem).toMillis(), + getFeedLayoutItemDateForSorting(nextItem).seconds - + getFeedLayoutItemDateForSorting(prevItem).seconds, ); }; From df5ccabdc6ce8ff2c14075fdf126dd5bd9afd03a Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 14:05:53 +0300 Subject: [PATCH 04/21] add whitelist to redux persist --- src/store/store.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/store/store.tsx b/src/store/store.tsx index bb12a565a2..ff3522cebf 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -20,6 +20,15 @@ import appSagas from "./saga"; const persistConfig: PersistConfig = { key: "root", storage, + whitelist: [ + "projects", + "commonLayout", + "commonFeedFollows", + "cache", + "chat", + "inbox", + "multipleSpacesLayout", + ], }; const sagaMiddleware = createSagaMiddleware(); From d3fc3ab78e35fba86b3cff112986a86f5387e4b0 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 16:21:54 +0300 Subject: [PATCH 05/21] add logic to save feed state by common id --- src/pages/commonFeed/CommonFeed.tsx | 7 +++++++ src/store/states/cache/actions.ts | 12 ++++++++++++ src/store/states/cache/constants.ts | 3 +++ src/store/states/cache/reducer.tsx | 8 ++++++++ .../cache/saga/copyFeedStateByCommonId.ts | 19 +++++++++++++++++++ src/store/states/cache/saga/index.ts | 3 +++ src/store/states/cache/types.ts | 7 +++++++ src/store/states/common/selectors.ts | 2 ++ 8 files changed, 61 insertions(+) create mode 100644 src/store/states/cache/saga/copyFeedStateByCommonId.ts diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 8182a5d33c..4196906a0c 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -45,6 +45,7 @@ import { getCommonPageAboutTabPath, } from "@/shared/utils"; import { + cacheActions, commonActions, commonLayoutActions, selectCommonAction, @@ -330,7 +331,13 @@ const CommonFeedComponent: FC = (props) => { useEffect(() => { fetchData(); + const interval = setInterval(() => { + dispatch(cacheActions.copyFeedStateByCommonId(commonId)); + }, 5000); + return () => { + clearInterval(interval); + dispatch(cacheActions.copyFeedStateByCommonId(commonId)); dispatch(commonActions.resetCommon()); }; }, [commonId]); diff --git a/src/store/states/cache/actions.ts b/src/store/states/cache/actions.ts index c46f98db89..7ded09d94a 100644 --- a/src/store/states/cache/actions.ts +++ b/src/store/states/cache/actions.ts @@ -10,6 +10,7 @@ import { User, } from "@/shared/models"; import { CacheActionType } from "./constants"; +import { FeedState } from "./types"; export const getUserStateById = createAsyncAction( CacheActionType.GET_USER_STATE_BY_ID, @@ -131,6 +132,17 @@ export const updateProposalStateById = createStandardAction( state: LoadingState; }>(); +export const copyFeedStateByCommonId = createStandardAction( + CacheActionType.COPY_FEED_STATE_BY_COMMON_ID, +)(); + +export const updateFeedStateByCommonId = createStandardAction( + CacheActionType.UPDATE_FEED_STATE_BY_COMMON_ID, +)<{ + commonId: string; + state: FeedState; +}>(); + export const getFeedItemUserMetadata = createAsyncAction( CacheActionType.GET_FEED_ITEM_USER_METADATA, CacheActionType.GET_FEED_ITEM_USER_METADATA_SUCCESS, diff --git a/src/store/states/cache/constants.ts b/src/store/states/cache/constants.ts index 042ff0840c..509c8bec1d 100644 --- a/src/store/states/cache/constants.ts +++ b/src/store/states/cache/constants.ts @@ -30,6 +30,9 @@ export enum CacheActionType { UPDATE_PROPOSAL_STATE_BY_ID = "@CACHE/UPDATE_PROPOSAL_STATE_BY_ID", + COPY_FEED_STATE_BY_COMMON_ID = "@CACHE/COPY_FEED_STATE_BY_COMMON_ID", + UPDATE_FEED_STATE_BY_COMMON_ID = "@CACHE/UPDATE_FEED_STATE_BY_COMMON_ID", + GET_FEED_ITEM_USER_METADATA = "@CACHE/GET_FEED_ITEM_USER_METADATA", GET_FEED_ITEM_USER_METADATA_SUCCESS = "@CACHE/GET_FEED_ITEM_USER_METADATA_SUCCESS", GET_FEED_ITEM_USER_METADATA_FAILURE = "@CACHE/GET_FEED_ITEM_USER_METADATA_FAILURE", diff --git a/src/store/states/cache/reducer.tsx b/src/store/states/cache/reducer.tsx index 95df165190..d2a13520e0 100644 --- a/src/store/states/cache/reducer.tsx +++ b/src/store/states/cache/reducer.tsx @@ -13,6 +13,7 @@ const initialState: CacheState = { discussionStates: {}, discussionMessagesStates: {}, proposalStates: {}, + feedByCommonIdStates: {}, feedItemUserMetadataStates: {}, chatChannelUserStatusStates: {}, }; @@ -97,6 +98,13 @@ export const reducer = createReducer(initialState) nextState.proposalStates[proposalId] = { ...state }; }), ) + .handleAction(actions.updateFeedStateByCommonId, (state, { payload }) => + produce(state, (nextState) => { + const { commonId, state } = payload; + + nextState.feedByCommonIdStates[commonId] = { ...state }; + }), + ) .handleAction(actions.updateFeedItemUserMetadata, (state, { payload }) => produce(state, (nextState) => { const { commonId, userId, feedObjectId, state } = payload; diff --git a/src/store/states/cache/saga/copyFeedStateByCommonId.ts b/src/store/states/cache/saga/copyFeedStateByCommonId.ts new file mode 100644 index 0000000000..c0dc20c6fc --- /dev/null +++ b/src/store/states/cache/saga/copyFeedStateByCommonId.ts @@ -0,0 +1,19 @@ +import { put, select } from "redux-saga/effects"; +import { selectCommonState, CommonState } from "../../common"; +import * as actions from "../actions"; + +export function* copyFeedStateByCommonId({ + payload: commonId, +}: ReturnType) { + const commonState = (yield select(selectCommonState)) as CommonState; + yield put( + actions.updateFeedStateByCommonId({ + commonId, + state: { + feedItems: commonState.feedItems, + pinnedFeedItems: commonState.pinnedFeedItems, + sharedFeedItem: commonState.sharedFeedItem, + }, + }), + ); +} diff --git a/src/store/states/cache/saga/index.ts b/src/store/states/cache/saga/index.ts index 802ac87ead..0eeba6aa13 100644 --- a/src/store/states/cache/saga/index.ts +++ b/src/store/states/cache/saga/index.ts @@ -1,6 +1,8 @@ +import { takeLatest } from "redux-saga/effects"; import { getFeedItemUserMetadataKey } from "@/shared/constants/getFeedItemUserMetadataKey"; import { takeLeadingByIdentifier } from "@/shared/utils/saga"; import * as actions from "../actions"; +import { copyFeedStateByCommonId } from "./copyFeedStateByCommonId"; import { getDiscussionStateById } from "./getDiscussionStateById"; import { getFeedItemUserMetadataState } from "./getFeedItemUserMetadataState"; import { getGovernanceStateByCommonId } from "./getGovernanceStateByCommonId"; @@ -33,4 +35,5 @@ export function* mainSaga() { ({ payload: { payload } }) => getFeedItemUserMetadataKey(payload), getFeedItemUserMetadataState, ); + yield takeLatest(actions.copyFeedStateByCommonId, copyFeedStateByCommonId); } diff --git a/src/store/states/cache/types.ts b/src/store/states/cache/types.ts index 17bf5b0e6f..4d99eec351 100644 --- a/src/store/states/cache/types.ts +++ b/src/store/states/cache/types.ts @@ -8,6 +8,12 @@ import { Proposal, User, } from "@/shared/models"; +import { CommonState } from "../common"; + +export type FeedState = Pick< + CommonState, + "feedItems" | "pinnedFeedItems" | "sharedFeedItem" +>; export interface CacheState { userStates: Record>; @@ -18,6 +24,7 @@ export interface CacheState { string, LoadingState >; + feedByCommonIdStates: Record; // key: {commonId}_{userId}_{feedObjectId} feedItemUserMetadataStates: Record< string, diff --git a/src/store/states/common/selectors.ts b/src/store/states/common/selectors.ts index ba61e5c62d..5b1005b293 100644 --- a/src/store/states/common/selectors.ts +++ b/src/store/states/common/selectors.ts @@ -1,5 +1,7 @@ import { AppState } from "@/shared/interfaces"; +export const selectCommonState = (state: AppState) => state.common; + export const selectCommonAction = (state: AppState) => state.common.commonAction; From ec8b0c124b62aa67b27dfb3d78b4bb35253ef8d9 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 16:27:03 +0300 Subject: [PATCH 06/21] change state reconciler of redux-persist to auto-merge level 2 --- src/store/store.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/store/store.tsx b/src/store/store.tsx index ff3522cebf..5f3edd6351 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -11,6 +11,7 @@ import { import { composeWithDevTools } from "redux-devtools-extension"; import freeze from "redux-freeze"; import { persistStore, persistReducer, PersistConfig } from "redux-persist"; +import autoMergeLevel2 from "redux-persist/lib/stateReconciler/autoMergeLevel2"; import storage from "redux-persist/lib/storage"; import createSagaMiddleware from "redux-saga"; import { AppState } from "@/shared/interfaces"; @@ -29,6 +30,7 @@ const persistConfig: PersistConfig = { "inbox", "multipleSpacesLayout", ], + stateReconciler: autoMergeLevel2, }; const sagaMiddleware = createSagaMiddleware(); From 6ca47d968faaa398d95497d07e0600626e15a4d9 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 17:02:30 +0300 Subject: [PATCH 07/21] add logic to set cached feed state for active common --- src/pages/commonFeed/CommonFeed.tsx | 6 ++- .../hooks/useCases/useCommonFeedItems.ts | 2 + src/store/states/cache/selectors.ts | 4 ++ src/store/states/common/actions.ts | 10 ++++- src/store/states/common/constants.ts | 2 + src/store/states/common/reducer.ts | 40 +++++++++++++++++++ src/store/states/common/saga/getFeedItems.ts | 15 ++++++- 7 files changed, 76 insertions(+), 3 deletions(-) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 4196906a0c..1bea857b83 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -163,7 +163,11 @@ const CommonFeedComponent: FC = (props) => { hasMore: hasMoreCommonFeedItems, fetch: fetchCommonFeedItems, batchNumber, - } = useCommonFeedItems(commonId, commonFeedItemIdsForNotListening); + } = useCommonFeedItems( + commonId, + commonFeedItemIdsForNotListening, + sharedFeedItemId, + ); const { isModalOpen: isCommonJoinModalOpen, diff --git a/src/shared/hooks/useCases/useCommonFeedItems.ts b/src/shared/hooks/useCases/useCommonFeedItems.ts index 3a4a6c2f66..09c5fc09dc 100644 --- a/src/shared/hooks/useCases/useCommonFeedItems.ts +++ b/src/shared/hooks/useCases/useCommonFeedItems.ts @@ -11,6 +11,7 @@ interface Return export const useCommonFeedItems = ( commonId: string, idsForNotListening?: string[], + sharedFeedItemId?: string | null, ): Return => { const dispatch = useDispatch(); const feedItems = useSelector(selectFeedItems); @@ -20,6 +21,7 @@ export const useCommonFeedItems = ( dispatch( commonActions.getFeedItems.request({ commonId, + sharedFeedItemId, feedItemId, limit: 15, }), diff --git a/src/store/states/cache/selectors.ts b/src/store/states/cache/selectors.ts index 60ec9ed317..3bfb83d902 100644 --- a/src/store/states/cache/selectors.ts +++ b/src/store/states/cache/selectors.ts @@ -21,6 +21,10 @@ export const selectDiscussionMessagesStateByDiscussionId = (discussionId: string) => (state: AppState) => state.cache.discussionMessagesStates[discussionId] || null; +export const selectFeedStateByCommonId = + (commonId: string) => (state: AppState) => + state.cache.feedByCommonIdStates[commonId] || null; + export const selectFeedItemUserMetadata = (info: { commonId: string; userId: string; feedObjectId: string }) => (state: AppState) => diff --git a/src/store/states/common/actions.ts b/src/store/states/common/actions.ts index a83093fe8e..e71f074cb5 100644 --- a/src/store/states/common/actions.ts +++ b/src/store/states/common/actions.ts @@ -20,7 +20,7 @@ import { Proposal, } from "@/shared/models"; import { CommonActionType } from "./constants"; -import { FeedItems, PinnedFeedItems } from "./types"; +import { CommonState, FeedItems, PinnedFeedItems } from "./types"; export const resetCommon = createStandardAction( CommonActionType.RESET_COMMON, @@ -116,6 +116,7 @@ export const getFeedItems = createAsyncAction( )< { commonId: string; + sharedFeedItemId?: string | null; feedItemId?: string; limit?: number; }, @@ -138,6 +139,13 @@ export const getPinnedFeedItems = createAsyncAction( string >(); +export const setFeedState = createStandardAction( + CommonActionType.SET_FEED_STATE, +)<{ + data: Pick; + sharedFeedItemId?: string | null; +}>(); + export const addNewFeedItems = createStandardAction( CommonActionType.ADD_NEW_FEED_ITEMS, )< diff --git a/src/store/states/common/constants.ts b/src/store/states/common/constants.ts index bbb9cdb0dc..a0e1f79e8a 100644 --- a/src/store/states/common/constants.ts +++ b/src/store/states/common/constants.ts @@ -26,6 +26,8 @@ export enum CommonActionType { GET_PINNED_FEED_ITEMS_FAILURE = "@COMMON/GET_PINNED_FEED_ITEMS_FAILURE", GET_PINNED_FEED_ITEMS_CANCEL = "@COMMON/GET_PINNED_FEED_ITEMS_CANCEL", + SET_FEED_STATE = "@COMMON/SET_FEED_STATE", + ADD_NEW_FEED_ITEMS = "@COMMON/ADD_NEW_FEED_ITEMS", ADD_NEW_PINNED_FEED_ITEMS = "@COMMON/ADD_NEW_PINNED_FEED_ITEMS", diff --git a/src/store/states/common/reducer.ts b/src/store/states/common/reducer.ts index 03efb7c2f6..bdca9e9086 100644 --- a/src/store/states/common/reducer.ts +++ b/src/store/states/common/reducer.ts @@ -468,6 +468,46 @@ export const reducer = createReducer(initialState) }; }), ) + .handleAction(actions.setFeedState, (state, { payload }) => + produce(state, (nextState) => { + const { + data: { feedItems, pinnedFeedItems, sharedFeedItem }, + sharedFeedItemId, + } = payload; + nextState.feedItems = { + ...feedItems, + hasMore: true, + }; + nextState.pinnedFeedItems = { ...pinnedFeedItems }; + + if (sharedFeedItem && sharedFeedItem.itemId === sharedFeedItemId) { + return; + } + if ( + sharedFeedItem && + !pinnedFeedItems.data?.some( + (item) => item.itemId === sharedFeedItem.itemId, + ) && + !feedItems.data?.some((item) => item.itemId === sharedFeedItem.itemId) + ) { + const data = [sharedFeedItem, ...(feedItems.data || [])]; + sortFeedItems(data); + nextState.feedItems.data = data; + } + if (sharedFeedItemId) { + nextState.feedItems.data = + nextState.feedItems.data && + nextState.feedItems.data.filter( + (item) => item.itemId !== sharedFeedItemId, + ); + nextState.pinnedFeedItems.data = + nextState.pinnedFeedItems.data && + nextState.pinnedFeedItems.data.filter( + (item) => item.itemId !== sharedFeedItemId, + ); + } + }), + ) .handleAction(actions.addNewFeedItems, (state, { payload }) => produce(state, (nextState) => { addNewFeedItems(nextState, payload); diff --git a/src/store/states/common/saga/getFeedItems.ts b/src/store/states/common/saga/getFeedItems.ts index 3ab56ccb61..5077852c33 100644 --- a/src/store/states/common/saga/getFeedItems.ts +++ b/src/store/states/common/saga/getFeedItems.ts @@ -3,6 +3,7 @@ import { CommonFeedService } from "@/services"; import { InboxItemType } from "@/shared/constants"; import { Awaited, FeedItemFollowLayoutItem } from "@/shared/interfaces"; import { isError } from "@/shared/utils"; +import { selectFeedStateByCommonId } from "@/store/states"; import * as actions from "../actions"; import { selectFeedItems } from "../selectors"; import { FeedItems } from "../types"; @@ -11,11 +12,23 @@ export function* getFeedItems( action: ReturnType, ) { const { - payload: { commonId, feedItemId, limit }, + payload: { commonId, sharedFeedItemId, feedItemId, limit }, } = action; try { const currentFeedItems = (yield select(selectFeedItems)) as FeedItems; + const cachedFeedState = yield select(selectFeedStateByCommonId(commonId)); + + if (!currentFeedItems.data && !feedItemId && cachedFeedState) { + yield put( + actions.setFeedState({ + data: cachedFeedState, + sharedFeedItemId, + }), + ); + return; + } + const isFirstRequest = !currentFeedItems.lastDocTimestamp; const { data, firstDocTimestamp, lastDocTimestamp, hasMore } = (yield call( CommonFeedService.getCommonFeedItemsByUpdatedAt, From a1d1590280bd645be528580ff1ab48c32d294bda Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 17:55:05 +0300 Subject: [PATCH 08/21] reset global data on user change --- src/pages/Auth/store/saga.tsx | 20 +++++++++++++++++--- src/store/states/cache/actions.ts | 4 ++++ src/store/states/cache/constants.ts | 1 + src/store/states/cache/reducer.tsx | 5 +++++ 4 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/pages/Auth/store/saga.tsx b/src/pages/Auth/store/saga.tsx index d4ae6d2b1e..81641a1cb1 100755 --- a/src/pages/Auth/store/saga.tsx +++ b/src/pages/Auth/store/saga.tsx @@ -15,6 +15,7 @@ import { getProvider } from "@/shared/utils/authProvider"; import { getFundingRequestNotification } from "@/shared/utils/notifications"; import { cacheActions, + commonActions, commonLayoutActions, inboxActions, multipleSpacesLayoutActions, @@ -302,6 +303,16 @@ const updateUserData = async (user: User): Promise => { }); }; +const resetGlobalData = (fullReset: boolean) => { + if (fullReset) { + store.dispatch(multipleSpacesLayoutActions.resetMultipleSpacesLayout()); + store.dispatch(commonLayoutActions.clearData()); + } + store.dispatch(inboxActions.resetInbox()); + store.dispatch(cacheActions.resetFeedStates()); + store.dispatch(commonActions.resetCommon()); +}; + function* socialLoginSaga({ payload, }: ReturnType) { @@ -466,9 +477,6 @@ function* logOut() { window.ReactNativeWebView.postMessage(WebviewActions.logout); } - yield put(multipleSpacesLayoutActions.resetMultipleSpacesLayout()); - yield put(commonLayoutActions.clearData()); - yield put(inboxActions.resetInbox()); history.push(ROUTE_PATHS.HOME); yield true; } @@ -569,6 +577,12 @@ function* authSagas() { firebase.auth().onAuthStateChanged(async (res) => { try { + const { user: userInStore } = store.getState().auth; + + if (userInStore?.uid !== res?.uid) { + resetGlobalData(!res); + } + store.dispatch( actions.setAuthProvider( getAuthProviderFromProviderData(res?.providerData), diff --git a/src/store/states/cache/actions.ts b/src/store/states/cache/actions.ts index 7ded09d94a..24f00c60e3 100644 --- a/src/store/states/cache/actions.ts +++ b/src/store/states/cache/actions.ts @@ -143,6 +143,10 @@ export const updateFeedStateByCommonId = createStandardAction( state: FeedState; }>(); +export const resetFeedStates = createStandardAction( + CacheActionType.RESET_FEED_STATES, +)(); + export const getFeedItemUserMetadata = createAsyncAction( CacheActionType.GET_FEED_ITEM_USER_METADATA, CacheActionType.GET_FEED_ITEM_USER_METADATA_SUCCESS, diff --git a/src/store/states/cache/constants.ts b/src/store/states/cache/constants.ts index 509c8bec1d..cfa3c62449 100644 --- a/src/store/states/cache/constants.ts +++ b/src/store/states/cache/constants.ts @@ -32,6 +32,7 @@ export enum CacheActionType { COPY_FEED_STATE_BY_COMMON_ID = "@CACHE/COPY_FEED_STATE_BY_COMMON_ID", UPDATE_FEED_STATE_BY_COMMON_ID = "@CACHE/UPDATE_FEED_STATE_BY_COMMON_ID", + RESET_FEED_STATES = "@CACHE/RESET_FEED_STATES", GET_FEED_ITEM_USER_METADATA = "@CACHE/GET_FEED_ITEM_USER_METADATA", GET_FEED_ITEM_USER_METADATA_SUCCESS = "@CACHE/GET_FEED_ITEM_USER_METADATA_SUCCESS", diff --git a/src/store/states/cache/reducer.tsx b/src/store/states/cache/reducer.tsx index d2a13520e0..0a1a60cefa 100644 --- a/src/store/states/cache/reducer.tsx +++ b/src/store/states/cache/reducer.tsx @@ -105,6 +105,11 @@ export const reducer = createReducer(initialState) nextState.feedByCommonIdStates[commonId] = { ...state }; }), ) + .handleAction(actions.resetFeedStates, (state) => + produce(state, (nextState) => { + nextState.feedByCommonIdStates = {}; + }), + ) .handleAction(actions.updateFeedItemUserMetadata, (state, { payload }) => produce(state, (nextState) => { const { commonId, userId, feedObjectId, state } = payload; From 3d3f0844ddf3adc2ea221576cddeb2b52fbc0e47 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 09:43:31 +0300 Subject: [PATCH 09/21] remove wrong feed items reset --- src/shared/hooks/useCases/useCommonFeedItems.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shared/hooks/useCases/useCommonFeedItems.ts b/src/shared/hooks/useCases/useCommonFeedItems.ts index 09c5fc09dc..f4f5a87edd 100644 --- a/src/shared/hooks/useCases/useCommonFeedItems.ts +++ b/src/shared/hooks/useCases/useCommonFeedItems.ts @@ -69,7 +69,6 @@ export const useCommonFeedItems = ( dispatch( commonActions.getFeedItems.cancel("Cancel feed items fetch on unmount"), ); - dispatch(commonActions.resetFeedItems()); }; }, []); From 272d0ea4e68876c3f47750cb74ae5ca4af87873b Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 11:46:54 +0300 Subject: [PATCH 10/21] fix sidenav content projects fetch --- .../SidenavContent/hooks/useProjectsData.ts | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/hooks/useProjectsData.ts b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/hooks/useProjectsData.ts index 3e7b0fb903..24fdce0ec0 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/hooks/useProjectsData.ts +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/hooks/useProjectsData.ts @@ -104,22 +104,16 @@ export const useProjectsData = (): Return => { }, [isAuthenticated]); useEffect(() => { - if (areCommonsLoading) { - return; - } if (!areCommonsFetched) { dispatch(commonLayoutActions.getCommons.request(activeItemId)); } - }, [areCommonsLoading, areCommonsFetched]); + }, [areCommonsFetched]); useEffect(() => { - if (areProjectsLoading || !currentCommonId) { - return; - } - if (!areProjectsFetched) { + if (currentCommonId && !areProjectsFetched) { dispatch(commonLayoutActions.getProjects.request(currentCommonId)); } - }, [areProjectsLoading, areProjectsFetched, currentCommonId]); + }, [areProjectsFetched, currentCommonId]); useEffect(() => { if ( From fb7511733b0828631241e5f7a6edbdfb8197fd9d Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 12:32:23 +0300 Subject: [PATCH 11/21] deserialize feed item related data from cache --- src/shared/interfaces/SynchronizedDate.ts | 3 ++ .../convertDatesToFirestoreTimestamps.ts | 12 +++--- src/store/states/common/reducer.ts | 40 ++++++++++++++++++- 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/shared/interfaces/SynchronizedDate.ts b/src/shared/interfaces/SynchronizedDate.ts index a30db01741..f02f0b370b 100644 --- a/src/shared/interfaces/SynchronizedDate.ts +++ b/src/shared/interfaces/SynchronizedDate.ts @@ -2,3 +2,6 @@ export interface SynchronizedDate { _seconds: number; _nanoseconds: number; } + +export const checkIsSynchronizedDate = (date: any): date is SynchronizedDate => + Boolean(date && date._seconds); diff --git a/src/shared/utils/convertDatesToFirestoreTimestamps.ts b/src/shared/utils/convertDatesToFirestoreTimestamps.ts index 4398fed138..df6361d631 100644 --- a/src/shared/utils/convertDatesToFirestoreTimestamps.ts +++ b/src/shared/utils/convertDatesToFirestoreTimestamps.ts @@ -1,11 +1,13 @@ -import firebase from "firebase"; import { get, set } from "lodash"; -import { SynchronizedDate } from "@/shared/interfaces"; +import { checkIsSynchronizedDate, SynchronizedDate } from "@/shared/interfaces"; +import { Timestamp } from "@/shared/models"; export const convertToTimestamp = ( - date: SynchronizedDate, -): firebase.firestore.Timestamp => - new firebase.firestore.Timestamp(date._seconds, date._nanoseconds); + date: SynchronizedDate | Timestamp, +): Timestamp => + checkIsSynchronizedDate(date) + ? new Timestamp(date._seconds, date._nanoseconds) + : new Timestamp(date.seconds, date.nanoseconds); const convertDateInObject = ( data: Record, diff --git a/src/store/states/common/reducer.ts b/src/store/states/common/reducer.ts index bdca9e9086..df0574f736 100644 --- a/src/store/states/common/reducer.ts +++ b/src/store/states/common/reducer.ts @@ -3,7 +3,11 @@ import { WritableDraft } from "immer/dist/types/types-external"; import { ActionType, createReducer } from "typesafe-actions"; import { InboxItemType } from "@/shared/constants"; import { FeedItemFollowLayoutItem } from "@/shared/interfaces"; -import { CommonFeed } from "@/shared/models"; +import { CommonFeed, FeedItemFollowWithMetadata } from "@/shared/models"; +import { + convertObjectDatesToFirestoreTimestamps, + convertToTimestamp, +} from "@/shared/utils"; import * as actions from "./actions"; import { CommonState, FeedItems, PinnedFeedItems } from "./types"; @@ -284,6 +288,22 @@ const updateSharedFeedItem = ( } }; +const deserializeFeedItemFollowLayoutItem = ( + item: FeedItemFollowLayoutItem, +): FeedItemFollowLayoutItem => ({ + ...item, + feedItem: convertObjectDatesToFirestoreTimestamps(item.feedItem), + feedItemFollowWithMetadata: item.feedItemFollowWithMetadata && { + ...convertObjectDatesToFirestoreTimestamps( + item.feedItemFollowWithMetadata, + ["lastSeen", "lastActivity"], + ), + feedItem: convertObjectDatesToFirestoreTimestamps( + item.feedItemFollowWithMetadata.feedItem, + ), + }, +}); + export const reducer = createReducer(initialState) .handleAction(actions.resetCommon, () => ({ ...initialState })) .handleAction(actions.setCommonAction, (state, { payload }) => @@ -476,9 +496,25 @@ export const reducer = createReducer(initialState) } = payload; nextState.feedItems = { ...feedItems, + data: + feedItems.data && + feedItems.data.map(deserializeFeedItemFollowLayoutItem), + firstDocTimestamp: + (feedItems.firstDocTimestamp && + convertToTimestamp(feedItems.firstDocTimestamp)) || + null, + lastDocTimestamp: + (feedItems.lastDocTimestamp && + convertToTimestamp(feedItems.lastDocTimestamp)) || + null, hasMore: true, }; - nextState.pinnedFeedItems = { ...pinnedFeedItems }; + nextState.pinnedFeedItems = { + ...pinnedFeedItems, + data: + pinnedFeedItems.data && + pinnedFeedItems.data.map(deserializeFeedItemFollowLayoutItem), + }; if (sharedFeedItem && sharedFeedItem.itemId === sharedFeedItemId) { return; From 905fb529dcf29e0f08feaa52d2694d0170dda7e3 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 13:53:03 +0300 Subject: [PATCH 12/21] create inbox transform for dehydration of persisted data --- src/shared/interfaces/feedLayout.ts | 43 +++++++++++++++++++++++++++++ src/store/states/common/reducer.ts | 26 ++++------------- src/store/store.tsx | 2 ++ src/store/transforms.ts | 26 +++++++++++++++++ 4 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 src/store/transforms.ts diff --git a/src/shared/interfaces/feedLayout.ts b/src/shared/interfaces/feedLayout.ts index 48407f0d3e..284d03781a 100644 --- a/src/shared/interfaces/feedLayout.ts +++ b/src/shared/interfaces/feedLayout.ts @@ -5,6 +5,7 @@ import { CommonFeed, FeedItemFollowWithMetadata, } from "@/shared/models"; +import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; export interface FeedLayoutRef { setExpandedFeedItemId: (feedItemId: string | null) => void; @@ -67,3 +68,45 @@ export type FeedLayoutItemChangeDataWithType = FeedLayoutItemChangeData & commonId: string; } ); + +export const deserializeFeedItemFollowLayoutItem = < + T extends FeedItemFollowLayoutItem | FeedItemFollowLayoutItemWithFollowData, +>( + item: T, +): T => ({ + ...item, + feedItem: convertObjectDatesToFirestoreTimestamps(item.feedItem), + feedItemFollowWithMetadata: item.feedItemFollowWithMetadata && { + ...convertObjectDatesToFirestoreTimestamps( + item.feedItemFollowWithMetadata, + ["lastSeen", "lastActivity"], + ), + feedItem: convertObjectDatesToFirestoreTimestamps( + item.feedItemFollowWithMetadata.feedItem, + ), + }, +}); + +export const deserializeChatChannelLayoutItem = ( + item: ChatChannelLayoutItem, +): ChatChannelLayoutItem => ({ + ...item, + chatChannel: convertObjectDatesToFirestoreTimestamps( + item.chatChannel, + ["lastMessage.createdAt"], + ), +}); + +export const deserializeFeedLayoutItem = ( + item: FeedLayoutItem, +): FeedLayoutItem => + checkIsChatChannelLayoutItem(item) + ? deserializeChatChannelLayoutItem(item) + : deserializeFeedItemFollowLayoutItem(item); + +export const deserializeFeedLayoutItemWithFollowData = ( + item: FeedLayoutItemWithFollowData, +): FeedLayoutItemWithFollowData => + checkIsChatChannelLayoutItem(item) + ? deserializeChatChannelLayoutItem(item) + : deserializeFeedItemFollowLayoutItem(item); diff --git a/src/store/states/common/reducer.ts b/src/store/states/common/reducer.ts index df0574f736..94bf974e80 100644 --- a/src/store/states/common/reducer.ts +++ b/src/store/states/common/reducer.ts @@ -2,12 +2,12 @@ import produce from "immer"; import { WritableDraft } from "immer/dist/types/types-external"; import { ActionType, createReducer } from "typesafe-actions"; import { InboxItemType } from "@/shared/constants"; -import { FeedItemFollowLayoutItem } from "@/shared/interfaces"; -import { CommonFeed, FeedItemFollowWithMetadata } from "@/shared/models"; import { - convertObjectDatesToFirestoreTimestamps, - convertToTimestamp, -} from "@/shared/utils"; + deserializeFeedItemFollowLayoutItem, + FeedItemFollowLayoutItem, +} from "@/shared/interfaces"; +import { CommonFeed } from "@/shared/models"; +import { convertToTimestamp } from "@/shared/utils"; import * as actions from "./actions"; import { CommonState, FeedItems, PinnedFeedItems } from "./types"; @@ -288,22 +288,6 @@ const updateSharedFeedItem = ( } }; -const deserializeFeedItemFollowLayoutItem = ( - item: FeedItemFollowLayoutItem, -): FeedItemFollowLayoutItem => ({ - ...item, - feedItem: convertObjectDatesToFirestoreTimestamps(item.feedItem), - feedItemFollowWithMetadata: item.feedItemFollowWithMetadata && { - ...convertObjectDatesToFirestoreTimestamps( - item.feedItemFollowWithMetadata, - ["lastSeen", "lastActivity"], - ), - feedItem: convertObjectDatesToFirestoreTimestamps( - item.feedItemFollowWithMetadata.feedItem, - ), - }, -}); - export const reducer = createReducer(initialState) .handleAction(actions.resetCommon, () => ({ ...initialState })) .handleAction(actions.setCommonAction, (state, { payload }) => diff --git a/src/store/store.tsx b/src/store/store.tsx index 5f3edd6351..3ed1582821 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -17,6 +17,7 @@ import createSagaMiddleware from "redux-saga"; import { AppState } from "@/shared/interfaces"; import rootReducer from "./reducer"; import appSagas from "./saga"; +import { inboxTransform } from "./transforms"; const persistConfig: PersistConfig = { key: "root", @@ -31,6 +32,7 @@ const persistConfig: PersistConfig = { "multipleSpacesLayout", ], stateReconciler: autoMergeLevel2, + transforms: [inboxTransform], }; const sagaMiddleware = createSagaMiddleware(); diff --git a/src/store/transforms.ts b/src/store/transforms.ts new file mode 100644 index 0000000000..ce081b068a --- /dev/null +++ b/src/store/transforms.ts @@ -0,0 +1,26 @@ +import { createTransform } from "redux-persist"; +import { deserializeFeedLayoutItemWithFollowData } from "@/shared/interfaces"; +import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; +import { InboxItems, InboxState } from "./states/inbox"; + +export const inboxTransform = createTransform( + (inboundState: InboxState) => inboundState, + (outboundState: InboxState) => ({ + ...outboundState, + sharedItem: + outboundState.sharedItem && + deserializeFeedLayoutItemWithFollowData(outboundState.sharedItem), + chatChannelItems: [], + items: { + ...convertObjectDatesToFirestoreTimestamps( + outboundState.items, + ["firstDocTimestamp", "lastDocTimestamp"], + ), + hasMore: true, + data: + outboundState.items.data && + outboundState.items.data.map(deserializeFeedLayoutItemWithFollowData), + }, + }), + { whitelist: ["inbox"] }, +); From 9e1c9da95aa4a9ae0f9bda0f8455e5f13d9840df Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 14:54:12 +0300 Subject: [PATCH 13/21] move checkIsSynchronizedDate to utils folder --- src/shared/interfaces/SynchronizedDate.ts | 3 --- src/shared/utils/checkIsSynchronizedDate.ts | 4 ++++ src/shared/utils/convertDatesToFirestoreTimestamps.ts | 3 ++- src/shared/utils/index.tsx | 1 + 4 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 src/shared/utils/checkIsSynchronizedDate.ts diff --git a/src/shared/interfaces/SynchronizedDate.ts b/src/shared/interfaces/SynchronizedDate.ts index f02f0b370b..a30db01741 100644 --- a/src/shared/interfaces/SynchronizedDate.ts +++ b/src/shared/interfaces/SynchronizedDate.ts @@ -2,6 +2,3 @@ export interface SynchronizedDate { _seconds: number; _nanoseconds: number; } - -export const checkIsSynchronizedDate = (date: any): date is SynchronizedDate => - Boolean(date && date._seconds); diff --git a/src/shared/utils/checkIsSynchronizedDate.ts b/src/shared/utils/checkIsSynchronizedDate.ts new file mode 100644 index 0000000000..b06ed1f63d --- /dev/null +++ b/src/shared/utils/checkIsSynchronizedDate.ts @@ -0,0 +1,4 @@ +import { SynchronizedDate } from "@/shared/interfaces"; + +export const checkIsSynchronizedDate = (date: any): date is SynchronizedDate => + Boolean(date && date._seconds); diff --git a/src/shared/utils/convertDatesToFirestoreTimestamps.ts b/src/shared/utils/convertDatesToFirestoreTimestamps.ts index df6361d631..ed5022b2f1 100644 --- a/src/shared/utils/convertDatesToFirestoreTimestamps.ts +++ b/src/shared/utils/convertDatesToFirestoreTimestamps.ts @@ -1,6 +1,7 @@ import { get, set } from "lodash"; -import { checkIsSynchronizedDate, SynchronizedDate } from "@/shared/interfaces"; +import { SynchronizedDate } from "@/shared/interfaces"; import { Timestamp } from "@/shared/models"; +import { checkIsSynchronizedDate } from "@/shared/utils"; export const convertToTimestamp = ( date: SynchronizedDate | Timestamp, diff --git a/src/shared/utils/index.tsx b/src/shared/utils/index.tsx index 74e159b1d9..c0151ea0ec 100755 --- a/src/shared/utils/index.tsx +++ b/src/shared/utils/index.tsx @@ -25,6 +25,7 @@ export * from "./routes"; export * from "./checkIsAutomaticJoin"; export * from "./checkIsIFrame"; export * from "./checkIsProject"; +export * from "./checkIsSynchronizedDate"; export * from "./checkIsURL"; export * from "./circles"; export * from "./notifications"; From 2d798b65ba15561c8262715b31b78247e9915978 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 15:20:43 +0300 Subject: [PATCH 14/21] cache only 30 first inbox items --- src/store/transforms.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/store/transforms.ts b/src/store/transforms.ts index ce081b068a..98efc666da 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -1,10 +1,30 @@ import { createTransform } from "redux-persist"; import { deserializeFeedLayoutItemWithFollowData } from "@/shared/interfaces"; import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; +import { getFeedLayoutItemDateForSorting } from "@/store/states/inbox/utils"; import { InboxItems, InboxState } from "./states/inbox"; export const inboxTransform = createTransform( - (inboundState: InboxState) => inboundState, + (inboundState: InboxState) => { + const data = + inboundState.items.data && inboundState.items.data.slice(0, 30); + + return { + ...inboundState, + items: { + ...inboundState.items, + data, + loading: false, + hasMore: true, + firstDocTimestamp: data?.[0] + ? getFeedLayoutItemDateForSorting(data[0]) + : null, + lastDocTimestamp: data?.[data.length - 1] + ? getFeedLayoutItemDateForSorting(data[data.length - 1]) + : null, + }, + }; + }, (outboundState: InboxState) => ({ ...outboundState, sharedItem: @@ -16,7 +36,6 @@ export const inboxTransform = createTransform( outboundState.items, ["firstDocTimestamp", "lastDocTimestamp"], ), - hasMore: true, data: outboundState.items.data && outboundState.items.data.map(deserializeFeedLayoutItemWithFollowData), From dc7922e315a4f595a4bf58cb42d44365aaaa9e7c Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 15:23:46 +0300 Subject: [PATCH 15/21] cache only first 30 feed items --- .../cache/saga/copyFeedStateByCommonId.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/store/states/cache/saga/copyFeedStateByCommonId.ts b/src/store/states/cache/saga/copyFeedStateByCommonId.ts index c0dc20c6fc..ab3405ba70 100644 --- a/src/store/states/cache/saga/copyFeedStateByCommonId.ts +++ b/src/store/states/cache/saga/copyFeedStateByCommonId.ts @@ -1,4 +1,5 @@ import { put, select } from "redux-saga/effects"; +import { getFeedLayoutItemDateForSorting } from "@/store/states/inbox/utils"; import { selectCommonState, CommonState } from "../../common"; import * as actions from "../actions"; @@ -6,11 +7,26 @@ export function* copyFeedStateByCommonId({ payload: commonId, }: ReturnType) { const commonState = (yield select(selectCommonState)) as CommonState; + const data = + commonState.feedItems.data && commonState.feedItems.data.slice(0, 30); + const feedItems = { + ...commonState.feedItems, + data, + loading: false, + hasMore: true, + firstDocTimestamp: data?.[0] + ? getFeedLayoutItemDateForSorting(data[0]) + : null, + lastDocTimestamp: data?.[data.length - 1] + ? getFeedLayoutItemDateForSorting(data[data.length - 1]) + : null, + }; + yield put( actions.updateFeedStateByCommonId({ commonId, state: { - feedItems: commonState.feedItems, + feedItems, pinnedFeedItems: commonState.pinnedFeedItems, sharedFeedItem: commonState.sharedFeedItem, }, From 16372709fac4c92bdd85a321d13715485f761b13 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 16:28:31 +0300 Subject: [PATCH 16/21] add updating of the existing caches inbox items --- src/services/Chat.ts | 22 +++ src/services/FeedItemFollows.ts | 23 +++ src/shared/hooks/useCases/useInboxItems.ts | 156 ++++++++++++++++----- 3 files changed, 163 insertions(+), 38 deletions(-) diff --git a/src/services/Chat.ts b/src/services/Chat.ts index d49b6aa7cd..8b4a8aefad 100644 --- a/src/services/Chat.ts +++ b/src/services/Chat.ts @@ -234,6 +234,28 @@ class ChatService { }); }; + public getChatChannels = async (options: { + participantId: string; + startAfter?: Timestamp; + endBefore?: Timestamp; + }): Promise => { + const { participantId, startAfter, endBefore } = options; + let query = this.getChatChannelCollection() + .where("participants", "array-contains", participantId) + .orderBy("updatedAt", "desc"); + + if (startAfter) { + query = query.startAfter(startAfter); + } + if (endBefore) { + query = query.endBefore(endBefore); + } + + const snapshot = await query.get(); + + return snapshot.docs.map((doc) => doc.data()); + }; + public subscribeToNewUpdatedChatChannels = ( participantId: string, endBefore: Timestamp, diff --git a/src/services/FeedItemFollows.ts b/src/services/FeedItemFollows.ts index c74bf40d10..3629178240 100644 --- a/src/services/FeedItemFollows.ts +++ b/src/services/FeedItemFollows.ts @@ -116,6 +116,29 @@ class FeedItemFollowsService { await Api.post(ApiEndpoint.FollowFeedItem, data, { cancelToken }); }; + public getFollowFeedItems = async (options: { + userId: string; + startAfter?: Timestamp; + endBefore?: Timestamp; + }): Promise => { + const { userId, startAfter, endBefore } = options; + let query = this.getFeedItemFollowsSubCollection(userId).orderBy( + "lastActivity", + "desc", + ); + + if (startAfter) { + query = query.startAfter(startAfter); + } + if (endBefore) { + query = query.endBefore(endBefore); + } + + const snapshot = await query.get(); + + return snapshot.docs.map((doc) => doc.data()); + }; + public subscribeToNewUpdatedFollowFeedItem = ( userId: string, endBefore: Timestamp, diff --git a/src/shared/hooks/useCases/useInboxItems.ts b/src/shared/hooks/useCases/useInboxItems.ts index f91ecd7967..9ad7b9b389 100644 --- a/src/shared/hooks/useCases/useInboxItems.ts +++ b/src/shared/hooks/useCases/useInboxItems.ts @@ -9,8 +9,13 @@ import { Logger, } from "@/services"; import { InboxItemType } from "@/shared/constants"; +import { useIsMounted } from "@/shared/hooks"; import { FeedLayoutItemWithFollowData } from "@/shared/interfaces"; -import { FeedItemFollow, FeedItemFollowWithMetadata } from "@/shared/models"; +import { + ChatChannel, + FeedItemFollow, + FeedItemFollowWithMetadata, +} from "@/shared/models"; import { inboxActions, InboxItems, selectInboxItems } from "@/store/states"; interface Return @@ -115,6 +120,7 @@ export const useInboxItems = ( options?: { unread?: boolean }, ): Return => { const dispatch = useDispatch(); + const isMounted = useIsMounted(); const [newItemsBatches, setNewItemsBatches] = useState([]); const inboxItems = useSelector(selectInboxItems); const user = useSelector(selectUser()); @@ -133,7 +139,115 @@ export const useInboxItems = ( const refetch = () => { dispatch(inboxActions.resetInboxItems()); fetch(); - } + }; + + const addNewChatChannels = ( + data: { + chatChannel: ChatChannel; + statuses: { + isAdded: boolean; + isRemoved: boolean; + }; + }[], + ) => { + const finalData = + feedItemIdsForNotListening && feedItemIdsForNotListening.length > 0 + ? data.filter( + (item) => !feedItemIdsForNotListening.includes(item.chatChannel.id), + ) + : data; + + if (finalData.length === 0) { + return; + } + + dispatch( + inboxActions.addNewInboxItems( + finalData.map((item) => ({ + item: { + itemId: item.chatChannel.id, + type: InboxItemType.ChatChannel, + chatChannel: item.chatChannel, + }, + statuses: item.statuses, + })), + ), + ); + }; + + const addNewFollowFeedItems = ( + data: { + item: FeedItemFollow; + statuses: { + isAdded: boolean; + isRemoved: boolean; + }; + }[], + ) => { + if (data.length === 0) { + return; + } + + const finalData = + feedItemIdsForNotListening && feedItemIdsForNotListening.length > 0 + ? data.filter( + (item) => + !feedItemIdsForNotListening.includes(item.item.feedItemId), + ) + : data; + setNewItemsBatches((currentItems) => [...currentItems, finalData]); + }; + + useEffect(() => { + (async () => { + try { + const { firstDocTimestamp: startAfter, lastDocTimestamp: endBefore } = + inboxItems; + + if (!userId || !startAfter || !endBefore) { + return; + } + + const [chatChannels, feedItemFollows] = await Promise.all([ + ChatService.getChatChannels({ + participantId: userId, + startAfter, + endBefore, + }), + FeedItemFollowsService.getFollowFeedItems({ + userId, + startAfter, + endBefore, + }), + ]); + + if (!isMounted()) { + return; + } + + addNewChatChannels( + chatChannels.map((chatChannel) => ({ + chatChannel, + statuses: { + isAdded: false, + isRemoved: false, + }, + })), + ); + addNewFollowFeedItems( + feedItemFollows.map((item) => ({ + item, + statuses: { + isAdded: false, + isRemoved: false, + }, + })), + ); + } catch (err) { + Logger.error(err); + } + })(); + }, []); useEffect(() => { if (!inboxItems.firstDocTimestamp || !userId) { @@ -144,30 +258,7 @@ export const useInboxItems = ( userId, inboxItems.firstDocTimestamp, (data) => { - const finalData = - feedItemIdsForNotListening && feedItemIdsForNotListening.length > 0 - ? data.filter( - (item) => - !feedItemIdsForNotListening.includes(item.chatChannel.id), - ) - : data; - - if (finalData.length === 0) { - return; - } - - dispatch( - inboxActions.addNewInboxItems( - finalData.map((item) => ({ - item: { - itemId: item.chatChannel.id, - type: InboxItemType.ChatChannel, - chatChannel: item.chatChannel, - }, - statuses: item.statuses, - })), - ), - ); + addNewChatChannels(data); }, ); @@ -184,18 +275,7 @@ export const useInboxItems = ( userId, inboxItems.firstDocTimestamp, (data) => { - if (data.length === 0) { - return; - } - - const finalData = - feedItemIdsForNotListening && feedItemIdsForNotListening.length > 0 - ? data.filter( - (item) => - !feedItemIdsForNotListening.includes(item.item.feedItemId), - ) - : data; - setNewItemsBatches((currentItems) => [...currentItems, finalData]); + addNewFollowFeedItems(data); }, ); From 0d249b120f6d366f25c4da2fc1a60d3dd1bfd4d8 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 16:57:08 +0300 Subject: [PATCH 17/21] fix middle inbox items fetch --- src/services/Chat.ts | 14 +++++++------- src/services/FeedItemFollows.ts | 14 +++++++------- src/shared/hooks/useCases/useInboxItems.ts | 12 ++++++------ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/services/Chat.ts b/src/services/Chat.ts index 8b4a8aefad..b46e3a0284 100644 --- a/src/services/Chat.ts +++ b/src/services/Chat.ts @@ -236,19 +236,19 @@ class ChatService { public getChatChannels = async (options: { participantId: string; - startAfter?: Timestamp; - endBefore?: Timestamp; + startAt?: Timestamp; + endAt?: Timestamp; }): Promise => { - const { participantId, startAfter, endBefore } = options; + const { participantId, startAt, endAt } = options; let query = this.getChatChannelCollection() .where("participants", "array-contains", participantId) .orderBy("updatedAt", "desc"); - if (startAfter) { - query = query.startAfter(startAfter); + if (startAt) { + query = query.startAt(startAt); } - if (endBefore) { - query = query.endBefore(endBefore); + if (endAt) { + query = query.endAt(endAt); } const snapshot = await query.get(); diff --git a/src/services/FeedItemFollows.ts b/src/services/FeedItemFollows.ts index 3629178240..98a3d99626 100644 --- a/src/services/FeedItemFollows.ts +++ b/src/services/FeedItemFollows.ts @@ -118,20 +118,20 @@ class FeedItemFollowsService { public getFollowFeedItems = async (options: { userId: string; - startAfter?: Timestamp; - endBefore?: Timestamp; + startAt?: Timestamp; + endAt?: Timestamp; }): Promise => { - const { userId, startAfter, endBefore } = options; + const { userId, startAt, endAt } = options; let query = this.getFeedItemFollowsSubCollection(userId).orderBy( "lastActivity", "desc", ); - if (startAfter) { - query = query.startAfter(startAfter); + if (startAt) { + query = query.startAt(startAt); } - if (endBefore) { - query = query.endBefore(endBefore); + if (endAt) { + query = query.endAt(endAt); } const snapshot = await query.get(); diff --git a/src/shared/hooks/useCases/useInboxItems.ts b/src/shared/hooks/useCases/useInboxItems.ts index 9ad7b9b389..82805954d4 100644 --- a/src/shared/hooks/useCases/useInboxItems.ts +++ b/src/shared/hooks/useCases/useInboxItems.ts @@ -201,23 +201,23 @@ export const useInboxItems = ( useEffect(() => { (async () => { try { - const { firstDocTimestamp: startAfter, lastDocTimestamp: endBefore } = + const { firstDocTimestamp: startAt, lastDocTimestamp: endAt } = inboxItems; - if (!userId || !startAfter || !endBefore) { + if (!userId || !startAt || !endAt) { return; } const [chatChannels, feedItemFollows] = await Promise.all([ ChatService.getChatChannels({ participantId: userId, - startAfter, - endBefore, + startAt, + endAt, }), FeedItemFollowsService.getFollowFeedItems({ userId, - startAfter, - endBefore, + startAt, + endAt, }), ]); From 116123028ee90a4f354139e104b108b46462f151 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 7 Nov 2023 17:18:53 +0300 Subject: [PATCH 18/21] add unfollowed items removal --- .../DiscussionFeedCard/DiscussionFeedCard.tsx | 8 ++--- .../common/components/FeedItem/FeedItem.tsx | 29 +++++++++++++++++-- .../common/components/FeedItem/context.ts | 1 + .../ProposalFeedCard/ProposalFeedCard.tsx | 8 ++--- .../components/FeedLayout/FeedLayout.tsx | 4 +++ src/pages/inbox/BaseInbox.tsx | 13 +++++++++ .../hooks/useCases/useFeedItemFollow.ts | 5 ++++ 7 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx index 3ddf95d5c7..95ce9b291b 100644 --- a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx +++ b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx @@ -12,9 +12,9 @@ import { DeletePrompt, GlobalOverlay, ReportModal } from "@/shared/components"; import { EntityTypes } from "@/shared/constants"; import { useModal, useNotification } from "@/shared/hooks"; import { + FeedItemFollowState, useCommon, useDiscussionById, - useFeedItemFollow, useFeedItemUserMetadata, useUserById, } from "@/shared/hooks/useCases"; @@ -63,6 +63,7 @@ interface DiscussionFeedCardProps { getNonAllowedItems?: GetNonAllowedItemsOptions; onActiveItemDataChange?: (data: FeedLayoutItemChangeData) => void; directParent?: DirectParent | null; + feedItemFollow: FeedItemFollowState; onUserSelect?: (userId: string, commonId?: string) => void; } @@ -89,6 +90,7 @@ const DiscussionFeedCard = forwardRef( getNonAllowedItems, onActiveItemDataChange, directParent, + feedItemFollow, onUserSelect, } = props; const { @@ -124,10 +126,6 @@ const DiscussionFeedCard = forwardRef( fetchFeedItemUserMetadata, } = useFeedItemUserMetadata(); const { data: common } = useCommon(isHome ? commonId : ""); - const feedItemFollow = useFeedItemFollow( - { feedItemId: item.id, commonId }, - { withSubscription: true }, - ); const menuItems = useMenuItems( { commonId, diff --git a/src/pages/common/components/FeedItem/FeedItem.tsx b/src/pages/common/components/FeedItem/FeedItem.tsx index 96b79f19fb..0dc3ff5d7b 100644 --- a/src/pages/common/components/FeedItem/FeedItem.tsx +++ b/src/pages/common/components/FeedItem/FeedItem.tsx @@ -1,4 +1,5 @@ -import React, { forwardRef, memo } from "react"; +import React, { forwardRef, memo, useEffect } from "react"; +import { useFeedItemFollow } from "@/shared/hooks/useCases"; import { FeedLayoutItemChangeData } from "@/shared/interfaces"; import { Circles, @@ -64,8 +65,17 @@ const FeedItem = forwardRef((props, ref) => { onActiveItemDataChange, directParent, } = props; - const { onFeedItemUpdate, getLastMessage, getNonAllowedItems, onUserSelect } = - useFeedItemContext(); + const { + onFeedItemUpdate, + onFeedItemUnfollowed, + getLastMessage, + getNonAllowedItems, + onUserSelect, + } = useFeedItemContext(); + const feedItemFollow = useFeedItemFollow( + { feedItemId: item.id, commonId }, + { withSubscription: true }, + ); useFeedItemSubscription(item.id, commonId, onFeedItemUpdate); if ( @@ -103,9 +113,22 @@ const FeedItem = forwardRef((props, ref) => { isMobileVersion, onActiveItemDataChange: handleActiveItemDataChange, directParent, + feedItemFollow, onUserSelect, }; + useEffect(() => { + if ( + feedItemFollow.isUserFeedItemFollowDataFetched && + !feedItemFollow.userFeedItemFollowData + ) { + onFeedItemUnfollowed?.(item.id); + } + }, [ + feedItemFollow.isUserFeedItemFollowDataFetched, + feedItemFollow.userFeedItemFollowData, + ]); + if (item.data.type === CommonFeedType.Discussion) { return ; } diff --git a/src/pages/common/components/FeedItem/context.ts b/src/pages/common/components/FeedItem/context.ts index 67cf765193..38ede3843f 100644 --- a/src/pages/common/components/FeedItem/context.ts +++ b/src/pages/common/components/FeedItem/context.ts @@ -63,6 +63,7 @@ export interface FeedItemContextValue { setExpandedFeedItemId?: (feedItemId: string | null) => void; renderFeedItemBaseContent?: (props: FeedItemBaseContentProps) => ReactNode; onFeedItemUpdate?: (item: CommonFeed, isRemoved: boolean) => void; + onFeedItemUnfollowed?: (itemId: string) => void; feedCardSettings?: FeedCardSettings; getLastMessage: (options: GetLastMessageOptions) => TextEditorValue; getNonAllowedItems?: GetNonAllowedItemsOptions; diff --git a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx index 934cd0720d..ac99d3ec4b 100644 --- a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx +++ b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx @@ -13,8 +13,8 @@ import { DeletePrompt, GlobalOverlay } from "@/shared/components"; import { useRoutesContext } from "@/shared/contexts"; import { useForceUpdate, useModal, useNotification } from "@/shared/hooks"; import { + FeedItemFollowState, useDiscussionById, - useFeedItemFollow, useFeedItemUserMetadata, useProposalById, useUserById, @@ -80,6 +80,7 @@ interface ProposalFeedCardProps { getLastMessage: (options: GetLastMessageOptions) => TextEditorValue; getNonAllowedItems?: GetNonAllowedItemsOptions; isMobileVersion?: boolean; + feedItemFollow: FeedItemFollowState; onActiveItemDataChange?: (data: FeedLayoutItemChangeData) => void; onUserSelect?: (userId: string, commonId?: string) => void; } @@ -101,6 +102,7 @@ const ProposalFeedCard = forwardRef( getLastMessage, getNonAllowedItems, isMobileVersion, + feedItemFollow, onActiveItemDataChange, onUserSelect, } = props; @@ -175,10 +177,6 @@ const ProposalFeedCard = forwardRef( onOpen: onShareModalOpen, onClose: onShareModalClose, } = useModal(false); - const feedItemFollow = useFeedItemFollow( - { feedItemId: item.id, commonId }, - { withSubscription: true }, - ); const menuItems = useMenuItems( { commonId, diff --git a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx index 1ad096f4b4..b87f8d6147 100644 --- a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx +++ b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx @@ -120,6 +120,7 @@ interface FeedLayoutProps { renderFeedItemBaseContent: (props: FeedItemBaseContentProps) => ReactNode; renderChatChannelItem?: (props: ChatChannelFeedLayoutItemProps) => ReactNode; onFeedItemUpdate?: (item: CommonFeed, isRemoved: boolean) => void; + onFeedItemUnfollowed?: (itemId: string) => void; getLastMessage: (options: GetLastMessageOptions) => TextEditorValue; sharedFeedItemId?: string | null; emptyText?: string; @@ -160,6 +161,7 @@ const FeedLayout: ForwardRefRenderFunction = ( renderFeedItemBaseContent, renderChatChannelItem, onFeedItemUpdate, + onFeedItemUnfollowed, getLastMessage, sharedFeedItemId, emptyText, @@ -327,6 +329,7 @@ const FeedLayout: ForwardRefRenderFunction = ( setExpandedFeedItemId, renderFeedItemBaseContent, onFeedItemUpdate, + onFeedItemUnfollowed, getLastMessage, getNonAllowedItems, onUserSelect: handleUserWithCommonClick, @@ -334,6 +337,7 @@ const FeedLayout: ForwardRefRenderFunction = ( [ renderFeedItemBaseContent, onFeedItemUpdate, + onFeedItemUnfollowed, getLastMessage, getNonAllowedItems, handleUserWithCommonClick, diff --git a/src/pages/inbox/BaseInbox.tsx b/src/pages/inbox/BaseInbox.tsx index 9f39be4a29..a5af1f88a3 100644 --- a/src/pages/inbox/BaseInbox.tsx +++ b/src/pages/inbox/BaseInbox.tsx @@ -150,6 +150,18 @@ const InboxPage: FC = (props) => { [dispatch], ); + const handleFeedItemUnfollowed = useCallback( + (itemId: string) => { + dispatch( + inboxActions.updateFeedItem({ + item: { id: itemId }, + isRemoved: true, + }), + ); + }, + [dispatch], + ); + const handleActiveItemChange = useCallback( (activeItemId?: string) => { dispatch(inboxActions.removeEmptyChatChannelItems(activeItemId)); @@ -267,6 +279,7 @@ const InboxPage: FC = (props) => { renderFeedItemBaseContent={renderFeedItemBaseContent} renderChatChannelItem={renderChatChannelItem} onFeedItemUpdate={handleFeedItemUpdate} + onFeedItemUnfollowed={handleFeedItemUnfollowed} getLastMessage={getLastMessage} emptyText={ isActiveUnreadInboxItemsQueryParam diff --git a/src/shared/hooks/useCases/useFeedItemFollow.ts b/src/shared/hooks/useCases/useFeedItemFollow.ts index 0fc1e02687..3524366d07 100644 --- a/src/shared/hooks/useCases/useFeedItemFollow.ts +++ b/src/shared/hooks/useCases/useFeedItemFollow.ts @@ -2,6 +2,7 @@ import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { selectUser } from "@/pages/Auth/store/selectors"; import { FollowFeedItemAction } from "@/shared/constants"; +import { FeedItemFollow } from "@/shared/models"; import { selectCommonFeedFollows, selectFollowFeedItemMutationState, @@ -15,6 +16,8 @@ export interface FeedItemFollowState { isFollowing: boolean; isDisabled: boolean; onFollowToggle: (action?: FollowFeedItemAction) => void; + isUserFeedItemFollowDataFetched: boolean; + userFeedItemFollowData: FeedItemFollow | null; } interface Data { @@ -105,5 +108,7 @@ export function useFeedItemFollow( isFollowing, isDisabled, onFollowToggle, + isUserFeedItemFollowDataFetched, + userFeedItemFollowData, }; } From 67e8d5e77001a5795f063c1c9c8c3bada0c206ad Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 9 Nov 2023 14:16:14 +0300 Subject: [PATCH 19/21] add subscription to common member log --- src/pages/OldCommon/hooks/useCommonMember.ts | 65 +++++++++++--------- 1 file changed, 35 insertions(+), 30 deletions(-) diff --git a/src/pages/OldCommon/hooks/useCommonMember.ts b/src/pages/OldCommon/hooks/useCommonMember.ts index 0ed5fb54f5..c474a8ca63 100644 --- a/src/pages/OldCommon/hooks/useCommonMember.ts +++ b/src/pages/OldCommon/hooks/useCommonMember.ts @@ -184,38 +184,43 @@ export const useCommonMember = (options: Options = {}): Return => { return; } - const unsubscribe = - CommonService.subscribeToCommonMemberByCommonIdAndUserId( - commonId, - userId, - (commonMember, { isAdded, isRemoved }) => { - let data: State["data"] = null; - - if (isAdded) { - CommonEventEmitter.emit(CommonEvent.ProjectUpdated, { - commonId, - hasMembership: true, + try { + console.log({ commonId, userId }); + const unsubscribe = + CommonService.subscribeToCommonMemberByCommonIdAndUserId( + commonId, + userId, + (commonMember, { isAdded, isRemoved }) => { + let data: State["data"] = null; + + if (isAdded) { + CommonEventEmitter.emit(CommonEvent.ProjectUpdated, { + commonId, + hasMembership: true, + }); + } + if (!isRemoved) { + data = { + ...commonMember, + ...generateCirclesDataForCommonMember( + governanceCircles, + commonMember.circleIds, + ), + }; + } + + setState({ + loading: false, + fetched: true, + data, }); - } - if (!isRemoved) { - data = { - ...commonMember, - ...generateCirclesDataForCommonMember( - governanceCircles, - commonMember.circleIds, - ), - }; - } - - setState({ - loading: false, - fetched: true, - data, - }); - }, - ); + }, + ); - return unsubscribe; + return unsubscribe; + } catch (error) { + console.error(error); + } }, [withSubscription, commonId, userId, governanceCircles]); return { From 57c2a54539f991113d32db69b1b3eee6f0bf9c16 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 9 Nov 2023 14:22:32 +0300 Subject: [PATCH 20/21] revert subscription log --- src/pages/OldCommon/hooks/useCommonMember.ts | 65 +++++++++----------- 1 file changed, 30 insertions(+), 35 deletions(-) diff --git a/src/pages/OldCommon/hooks/useCommonMember.ts b/src/pages/OldCommon/hooks/useCommonMember.ts index c474a8ca63..0ed5fb54f5 100644 --- a/src/pages/OldCommon/hooks/useCommonMember.ts +++ b/src/pages/OldCommon/hooks/useCommonMember.ts @@ -184,43 +184,38 @@ export const useCommonMember = (options: Options = {}): Return => { return; } - try { - console.log({ commonId, userId }); - const unsubscribe = - CommonService.subscribeToCommonMemberByCommonIdAndUserId( - commonId, - userId, - (commonMember, { isAdded, isRemoved }) => { - let data: State["data"] = null; - - if (isAdded) { - CommonEventEmitter.emit(CommonEvent.ProjectUpdated, { - commonId, - hasMembership: true, - }); - } - if (!isRemoved) { - data = { - ...commonMember, - ...generateCirclesDataForCommonMember( - governanceCircles, - commonMember.circleIds, - ), - }; - } - - setState({ - loading: false, - fetched: true, - data, + const unsubscribe = + CommonService.subscribeToCommonMemberByCommonIdAndUserId( + commonId, + userId, + (commonMember, { isAdded, isRemoved }) => { + let data: State["data"] = null; + + if (isAdded) { + CommonEventEmitter.emit(CommonEvent.ProjectUpdated, { + commonId, + hasMembership: true, }); - }, - ); + } + if (!isRemoved) { + data = { + ...commonMember, + ...generateCirclesDataForCommonMember( + governanceCircles, + commonMember.circleIds, + ), + }; + } - return unsubscribe; - } catch (error) { - console.error(error); - } + setState({ + loading: false, + fetched: true, + data, + }); + }, + ); + + return unsubscribe; }, [withSubscription, commonId, userId, governanceCircles]); return { From aca420c92958c84464b6f8c5a28ccf6e3b60d372 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 9 Nov 2023 14:36:08 +0300 Subject: [PATCH 21/21] fix wrong useEffect placement --- .../common/components/FeedItem/FeedItem.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/pages/common/components/FeedItem/FeedItem.tsx b/src/pages/common/components/FeedItem/FeedItem.tsx index 0dc3ff5d7b..6936c90e34 100644 --- a/src/pages/common/components/FeedItem/FeedItem.tsx +++ b/src/pages/common/components/FeedItem/FeedItem.tsx @@ -78,6 +78,18 @@ const FeedItem = forwardRef((props, ref) => { ); useFeedItemSubscription(item.id, commonId, onFeedItemUpdate); + useEffect(() => { + if ( + feedItemFollow.isUserFeedItemFollowDataFetched && + !feedItemFollow.userFeedItemFollowData + ) { + onFeedItemUnfollowed?.(item.id); + } + }, [ + feedItemFollow.isUserFeedItemFollowDataFetched, + feedItemFollow.userFeedItemFollowData, + ]); + if ( shouldCheckItemVisibility && !checkIsItemVisibleForUser( @@ -117,18 +129,6 @@ const FeedItem = forwardRef((props, ref) => { onUserSelect, }; - useEffect(() => { - if ( - feedItemFollow.isUserFeedItemFollowDataFetched && - !feedItemFollow.userFeedItemFollowData - ) { - onFeedItemUnfollowed?.(item.id); - } - }, [ - feedItemFollow.isUserFeedItemFollowDataFetched, - feedItemFollow.userFeedItemFollowData, - ]); - if (item.data.type === CommonFeedType.Discussion) { return ; }