From 7b163e7e8b3b604913a49abbdedac1941692615e Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 6 Nov 2023 12:07:34 +0300 Subject: [PATCH 01/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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/49] 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 7a7483a57683426c71a06ffa364658516eaf495e Mon Sep 17 00:00:00 2001 From: Kiryl Budnik Date: Wed, 8 Nov 2023 22:00:26 +0100 Subject: [PATCH 19/49] add "item" query param to inbox page --- src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx | 6 ++++++ src/pages/inbox/BaseInbox.tsx | 1 + 2 files changed, 7 insertions(+) diff --git a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx index 93441ebc08..5ffca2f17b 100644 --- a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx +++ b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx @@ -615,6 +615,12 @@ const FeedLayout: ForwardRefRenderFunction = ( } }, [batchNumber]); + useEffect(() => { + if (sharedFeedItemId && isTabletView && allFeedItems) { + setActiveChatItem({ feedItemId: sharedFeedItemId }); + } + }, [sharedFeedItemId, isTabletView, allFeedItems]); + useImperativeHandle( ref, () => ({ diff --git a/src/pages/inbox/BaseInbox.tsx b/src/pages/inbox/BaseInbox.tsx index 8be5cfa57e..fb3da68852 100644 --- a/src/pages/inbox/BaseInbox.tsx +++ b/src/pages/inbox/BaseInbox.tsx @@ -272,6 +272,7 @@ const InboxPage: FC = (props) => { renderChatChannelItem={renderChatChannelItem} onFeedItemUpdate={handleFeedItemUpdate} getLastMessage={getLastMessage} + sharedFeedItemId={sharedFeedItemId} emptyText={ isActiveUnreadInboxItemsQueryParam ? "Hurry! No unread items in your inbox :-)" From 67e8d5e77001a5795f063c1c9c8c3bada0c206ad Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 9 Nov 2023 14:16:14 +0300 Subject: [PATCH 20/49] 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 21/49] 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 22/49] 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 ; } From 17ee00dd22aeb376bd39d1c1ec2f29a80ee6bc29 Mon Sep 17 00:00:00 2001 From: Kiryl Budnik Date: Wed, 1 Nov 2023 14:56:34 +0100 Subject: [PATCH 23/49] show "loading" label only when message data is being fetched --- .../Chat/ChatMessage/ChatMessage.tsx | 51 +++++++++++-------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx index 76fe7c0841..1a1954ee95 100644 --- a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx @@ -7,6 +7,7 @@ import React, { } from "react"; import classNames from "classnames"; import { useLongPress } from "use-long-press"; +import { Logger } from "@/services"; import { ElementDropdown, UserAvatar, @@ -110,6 +111,7 @@ export default function ChatMessage({ !isUserDiscussionMessage || userId !== discussionMessageUserId; const [messageText, setMessageText] = useState<(string | JSX.Element)[]>([]); + const [isMessageDataFetching, setIsMessageDataFetching] = useState(false); const [replyMessageText, setReplyMessageText] = useState< (string | JSX.Element)[] @@ -144,26 +146,35 @@ export default function ChatMessage({ const emojiCount = countTextEditorEmojiElements( parseStringToTextEditorValue(discussionMessage.text), ); - const parsedText = await getTextFromTextEditorString({ - textEditorString: discussionMessage.text, - users, - mentionTextClassName: !isNotCurrentUserMessage - ? styles.mentionTextCurrentUser - : "", - emojiTextClassName: classNames({ - [styles.singleEmojiText]: emojiCount.isSingleEmoji, - [styles.multipleEmojiText]: emojiCount.isMultipleEmoji, - }), - commonId: discussionMessage.commonId, - systemMessage: isSystemMessage ? discussionMessage : undefined, - getCommonPagePath, - getCommonPageAboutTabPath, - directParent, - onUserClick, - onFeedItemClick, - }); - setMessageText(parsedText); + setIsMessageDataFetching(true); + + try { + const parsedText = await getTextFromTextEditorString({ + textEditorString: discussionMessage.text, + users, + mentionTextClassName: !isNotCurrentUserMessage + ? styles.mentionTextCurrentUser + : "", + emojiTextClassName: classNames({ + [styles.singleEmojiText]: emojiCount.isSingleEmoji, + [styles.multipleEmojiText]: emojiCount.isMultipleEmoji, + }), + commonId: discussionMessage.commonId, + systemMessage: isSystemMessage ? discussionMessage : undefined, + getCommonPagePath, + getCommonPageAboutTabPath, + directParent, + onUserClick, + onFeedItemClick, + }); + + setMessageText(parsedText); + } catch (error) { + Logger.error(error); + } finally { + setIsMessageDataFetching(false); + } })(); }, [ users, @@ -400,7 +411,7 @@ export default function ChatMessage({ - {!messageText.length ? ( + {!messageText.length && isMessageDataFetching ? ( Loading... ) : ( messageText.map((text) => text) From 005cc26a10b89c34ac6a735bd00749a43d291456 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 13 Nov 2023 16:24:47 +0300 Subject: [PATCH 24/49] fix commons sorting --- .../components/ProjectsTree/hooks/useMenuItems.ts | 12 +++++++++++- .../FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx | 13 ++++++++++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts index 1eb5f92517..78add419af 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts @@ -24,12 +24,22 @@ export const useMenuItems = (options: Options): MenuItem[] => { onCommonClick(stateItem.commonId); }, })) - .sort((item) => (item.id === activeStateItemId ? -1 : 1)) + .sort((prevItem, nextItem) => { + if (prevItem.id === activeStateItemId) { + return -1; + } + if (nextItem.id === activeStateItemId) { + return 1; + } + + return 0; + }) .concat({ id: CREATE_COMMON_ITEM_ID, text: "Create a common", onClick: onCommonCreationClick, }); + console.log(items.map((item) => item.text)); return items; }; diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx index 50b81a54e4..e28802c99f 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/components/FeedBreadcrumbsItem/FeedBreadcrumbsItem.tsx @@ -52,9 +52,16 @@ const FeedBreadcrumbsItem: FC = (props) => { () => baseItems.length === 0 ? [activeItem] - : [...baseItems].sort((prevItem) => - prevItem.commonId === activeItem.commonId ? -1 : 1, - ), + : [...baseItems].sort((prevItem, nextItem) => { + if (prevItem.commonId === activeItem.commonId) { + return -1; + } + if (nextItem.commonId === activeItem.commonId) { + return 1; + } + + return 0; + }), [baseItems, activeItem], ); From da94f7cd5d4504ee983ae46c61bc91fcd1431b0e Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 13 Nov 2023 16:38:01 +0300 Subject: [PATCH 25/49] change order of layout tabs --- .../CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx index 581b04d66f..fb51fcc612 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx @@ -60,7 +60,7 @@ const LayoutTabs: FC = (props) => { ]; if (isAuthenticated) { - tabs.splice(1, 0, { + tabs.unshift({ label: getLayoutTabName(LayoutTab.Inbox), value: LayoutTab.Inbox, icon: , From b34e227492d0bbd293fd4c52575b8c1c053e0346 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 13 Nov 2023 16:40:14 +0300 Subject: [PATCH 26/49] change spaces tab text and icon --- src/shared/icons/blocks2.icon.tsx | 35 +++++++++++++++++++ src/shared/icons/index.ts | 1 + .../components/LayoutTabs/LayoutTabs.tsx | 4 +-- .../LayoutTabs/utils/getLayoutTabName.ts | 2 +- 4 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 src/shared/icons/blocks2.icon.tsx diff --git a/src/shared/icons/blocks2.icon.tsx b/src/shared/icons/blocks2.icon.tsx new file mode 100644 index 0000000000..62378bb383 --- /dev/null +++ b/src/shared/icons/blocks2.icon.tsx @@ -0,0 +1,35 @@ +import React, { FC } from "react"; + +interface Blocks2IconProps { + className?: string; +} + +const Blocks2Icon: FC = ({ className }) => { + const color = "currentColor"; + + return ( + + + + + ); +}; + +export default Blocks2Icon; diff --git a/src/shared/icons/index.ts b/src/shared/icons/index.ts index 47872eacb9..21c9d273dd 100644 --- a/src/shared/icons/index.ts +++ b/src/shared/icons/index.ts @@ -4,6 +4,7 @@ export { default as Avatar2Icon } from "./avatar2.icon"; export { default as Avatar3Icon } from "./avatar3.icon"; export { default as BillingIcon } from "./billing.icon"; export { default as BlocksIcon } from "./blocks.icon"; +export { default as Blocks2Icon } from "./blocks2.icon"; export { default as BoldMarkIcon } from "./boldMark.icon"; export { default as BoldPlusIcon } from "./boldPlus.icon"; export { default as CaretIcon } from "./caret.icon"; diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx index fb51fcc612..85e41f62fb 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx @@ -8,7 +8,7 @@ import { } from "@/pages/Auth/store/selectors"; import { Tab, Tabs } from "@/shared/components"; import { useRoutesContext } from "@/shared/contexts"; -import { Avatar2Icon, BlocksIcon, InboxIcon } from "@/shared/icons"; +import { Avatar2Icon, Blocks2Icon, InboxIcon } from "@/shared/icons"; import { openSidenav } from "@/shared/utils"; import { selectCommonLayoutLastCommonFromFeed } from "@/store/states"; import { LayoutTab } from "../../constants"; @@ -50,7 +50,7 @@ const LayoutTabs: FC = (props) => { { label: getLayoutTabName(LayoutTab.Spaces), value: LayoutTab.Spaces, - icon: , + icon: , }, { label: getLayoutTabName(LayoutTab.Profile), diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/utils/getLayoutTabName.ts b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/utils/getLayoutTabName.ts index c21ca54758..1b53dd4722 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/utils/getLayoutTabName.ts +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/utils/getLayoutTabName.ts @@ -1,7 +1,7 @@ import { LayoutTab } from "../../../constants"; const LAYOUT_TAB_TO_NAME_MAP: Record = { - [LayoutTab.Spaces]: "Feed", + [LayoutTab.Spaces]: "Spaces", [LayoutTab.Inbox]: "Inbox", [LayoutTab.Profile]: "Profile", }; From 7f97f9efe8ab631937b2cfe125aa46b3cbb3fdb7 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 13 Nov 2023 16:52:28 +0300 Subject: [PATCH 27/49] disable header link to common/space back page on mobile --- .../HeaderCommonContent.module.scss | 8 +++++++ .../HeaderCommonContent.tsx | 21 ++++++++++++++----- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss index 4a17e06688..870a1f2430 100644 --- a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss +++ b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss @@ -26,6 +26,7 @@ padding: 0 1.5rem 0 1.375rem; display: flex; align-items: center; + color: inherit; text-decoration: none; overflow: hidden; box-sizing: border-box; @@ -40,6 +41,13 @@ @include tablet { padding-left: 0; padding-right: 0.5rem; + + &:hover { + .commonName { + color: inherit; + text-decoration: none; + } + } } } diff --git a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx index 468a1685b7..ca63d7d3a9 100644 --- a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx +++ b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx @@ -2,6 +2,7 @@ import React, { FC } from "react"; import { NavLink } from "react-router-dom"; import classNames from "classnames"; import { useRoutesContext } from "@/shared/contexts"; +import { useIsTabletView } from "@/shared/hooks/viewport"; import { RightArrowThinIcon, StarIcon } from "@/shared/icons"; import { CommonAvatar, TopNavigationOpenSidenavButton } from "@/shared/ui-kit"; import { getPluralEnding } from "@/shared/utils"; @@ -26,6 +27,19 @@ const HeaderCommonContent: FC = (props) => { showFollowIcon = false, } = props; const { getCommonPageAboutTabPath } = useRoutesContext(); + const isTabletView = useIsTabletView(); + + const ContentWrapper: FC = ({ children }) => + isTabletView ? ( +
{children}
+ ) : ( + + {children} + + ); return (
@@ -33,10 +47,7 @@ const HeaderCommonContent: FC = (props) => { className={styles.openSidenavButton} iconEl={} /> - + = (props) => { {memberCount} member{getPluralEnding(memberCount)}

- + ); }; From dccc80bddb95d7c80bd537a80771bb91813c38e1 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 13 Nov 2023 17:01:00 +0300 Subject: [PATCH 28/49] change sidebar icon in the mobile header --- src/pages/commonFeed/CommonFeed.module.scss | 2 -- src/pages/commonFeed/CommonFeed.tsx | 6 ++-- .../HeaderCommonContent.module.scss | 2 -- .../HeaderCommonContent.tsx | 4 +-- src/shared/icons/index.ts | 1 + src/shared/icons/sidebar.icon.tsx | 35 +++++++++++++++++++ 6 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 src/shared/icons/sidebar.icon.tsx diff --git a/src/pages/commonFeed/CommonFeed.module.scss b/src/pages/commonFeed/CommonFeed.module.scss index 443b1de73d..0d4c0298a2 100644 --- a/src/pages/commonFeed/CommonFeed.module.scss +++ b/src/pages/commonFeed/CommonFeed.module.scss @@ -16,8 +16,6 @@ width: 1.5rem; height: 1.5rem; margin-right: 0.625rem; - transform: rotate(180deg); - color: $c-neutrals-600; } .feedLayout { diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 8182a5d33c..34b83793b8 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -29,7 +29,7 @@ import { useRoutesContext } from "@/shared/contexts"; import { useAuthorizedModal, useQueryParams } from "@/shared/hooks"; import { useCommonFeedItems, useUserCommonIds } from "@/shared/hooks/useCases"; import { useCommonPinnedFeedItems } from "@/shared/hooks/useCases/useCommonPinnedFeedItems"; -import { RightArrowThinIcon } from "@/shared/icons"; +import { SidebarIcon } from "@/shared/icons"; import { checkIsFeedItemFollowLayoutItem, FeedLayoutItem, @@ -428,7 +428,7 @@ const CommonFeedComponent: FC = (props) => { ) : ( } + iconEl={} /> ); @@ -448,7 +448,7 @@ const CommonFeedComponent: FC = (props) => { <> } + iconEl={} />
diff --git a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss index 870a1f2430..54f01fae06 100644 --- a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss +++ b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.module.scss @@ -18,8 +18,6 @@ width: 1.5rem; height: 1.5rem; margin-right: 0.625rem; - transform: rotate(180deg); - color: $c-neutrals-600; } .commonLink { diff --git a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx index ca63d7d3a9..6f35431e1a 100644 --- a/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx +++ b/src/pages/commonFeed/components/HeaderContent/components/HeaderCommonContent/HeaderCommonContent.tsx @@ -3,7 +3,7 @@ import { NavLink } from "react-router-dom"; import classNames from "classnames"; import { useRoutesContext } from "@/shared/contexts"; import { useIsTabletView } from "@/shared/hooks/viewport"; -import { RightArrowThinIcon, StarIcon } from "@/shared/icons"; +import { SidebarIcon, StarIcon } from "@/shared/icons"; import { CommonAvatar, TopNavigationOpenSidenavButton } from "@/shared/ui-kit"; import { getPluralEnding } from "@/shared/utils"; import styles from "./HeaderCommonContent.module.scss"; @@ -45,7 +45,7 @@ const HeaderCommonContent: FC = (props) => {
} + iconEl={} /> = ({ className }) => { + const color = "currentColor"; + + return ( + + + + + + ); +}; + +export default SidebarIcon; From 8b4940cd3870af4a5c1025b1b2685a9e2f0081c1 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 13 Nov 2023 17:23:45 +0300 Subject: [PATCH 29/49] decrease mobile sidenav width to 90% --- .../MultipleSpacesLayout/MultipleSpacesLayout.module.scss | 2 +- src/shared/ui-kit/Sidenav/Sidenav.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss b/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss index 64b3e94956..6eee670672 100644 --- a/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss +++ b/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss @@ -30,7 +30,7 @@ --main-pl: unset; --sb-max-width: 100%; --sb-width: 100%; - --sb-content-width: 100%; + --sb-content-width: 90%; --layout-tabs-height: 4rem; --header-h: 0; } diff --git a/src/shared/ui-kit/Sidenav/Sidenav.tsx b/src/shared/ui-kit/Sidenav/Sidenav.tsx index 827f0a9962..4566d3e121 100644 --- a/src/shared/ui-kit/Sidenav/Sidenav.tsx +++ b/src/shared/ui-kit/Sidenav/Sidenav.tsx @@ -71,10 +71,10 @@ const Sidenav: FC = (props) => {
); From 0d24349de0943d68611b68b46177f63740dbc23d Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Mon, 13 Nov 2023 19:46:59 +0300 Subject: [PATCH 30/49] remove unnecessary log --- .../SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts index 78add419af..da65bfff9e 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/ProjectsTree/hooks/useMenuItems.ts @@ -39,7 +39,6 @@ export const useMenuItems = (options: Options): MenuItem[] => { text: "Create a common", onClick: onCommonCreationClick, }); - console.log(items.map((item) => item.text)); return items; }; From 6e49dfb7079298bcc8a3eef9ffaf4d2991714267 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 10:22:48 +0300 Subject: [PATCH 31/49] add background for mobile sidenav --- src/shared/ui-kit/Sidenav/Sidenav.module.scss | 32 +++++++++- src/shared/ui-kit/Sidenav/Sidenav.tsx | 59 +++++++++++-------- 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/src/shared/ui-kit/Sidenav/Sidenav.module.scss b/src/shared/ui-kit/Sidenav/Sidenav.module.scss index fbe0603aa8..9c9d27852e 100644 --- a/src/shared/ui-kit/Sidenav/Sidenav.module.scss +++ b/src/shared/ui-kit/Sidenav/Sidenav.module.scss @@ -1,12 +1,42 @@ @import "../../../constants"; @import "../../../styles/sizes"; +$zIndex: 3; + +.sidenavBackground { + display: none; +} +.sidenavBackgroundOpen { + @include tablet { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + z-index: $zIndex; + display: block; + background-color: $c-gray-5; + opacity: 0.5; + animation: fade var(--sb-transition-duration); + } +} + +@keyframes fade { + 0% { + opacity: 0; + } + + 100% { + opacity: 0.5; + } +} + .sidenav { position: fixed; top: 0; bottom: 0; left: 0; - z-index: 3; + z-index: $zIndex; max-width: var(--sb-max-width); width: var(--sb-width); diff --git a/src/shared/ui-kit/Sidenav/Sidenav.tsx b/src/shared/ui-kit/Sidenav/Sidenav.tsx index 4566d3e121..fcbd7234fd 100644 --- a/src/shared/ui-kit/Sidenav/Sidenav.tsx +++ b/src/shared/ui-kit/Sidenav/Sidenav.tsx @@ -49,34 +49,41 @@ const Sidenav: FC = (props) => { }, [isSidenavOpen]); return ( - + + ); }; From 05fcbaf07779793ee6a3a4e8c68ed1055a9c7e44 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 10:27:29 +0300 Subject: [PATCH 32/49] decrease sidenav width of v04 layout --- .../layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss b/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss index c4839e3539..21820b7174 100644 --- a/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss +++ b/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss @@ -27,7 +27,7 @@ @include tablet { --sb-max-width: 100%; --sb-width: 100%; - --sb-content-width: 100%; + --sb-content-width: 90%; --layout-tabs-height: 4rem; } } From 4ac37579d7a33bf92c1635b1bbb24e803b097c9a Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 10:34:10 +0300 Subject: [PATCH 33/49] remove common logo from mobile sidenav v04 --- .../components/SidenavContent/SidenavContent.module.scss | 9 +-------- .../components/SidenavContent/SidenavContent.tsx | 2 +- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss index 81ee25f476..44752c5166 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss @@ -23,14 +23,7 @@ background-color: $c-light-gray-2; @include tablet { - --logo-top-indent: 0; - --logo-right-indent: 0; - --logo-left-indent: 0; - --logo-bottom-indent: 0; - - height: 3.25rem; - align-items: center; - background-color: $c-shades-white; + display: none; } } diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx index bd684e6b20..ac1816bf0e 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx @@ -41,9 +41,9 @@ const SidenavContent: FC = (props) => { logoClassName={styles.commonLogo} logoSrc={commonLogoSrc} /> - {separatorEl} {!isTabletView && ( <> + {separatorEl} {separatorEl} From a7adbccc5c4697b411fb7e86fad942c1940b2ef8 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 10:36:46 +0300 Subject: [PATCH 34/49] remove layout tabs from mobile sidenav v04 --- .../components/SidenavContent/SidenavContent.module.scss | 4 ---- .../components/SidenavContent/SidenavContent.tsx | 8 -------- 2 files changed, 12 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss index 44752c5166..6c544b882b 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss @@ -49,10 +49,6 @@ margin-top: auto; } -.layoutTabs { - margin-top: auto; -} - .userInfoContentButton { padding: 1rem 0; diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx index ac1816bf0e..881bcc9499 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx @@ -4,7 +4,6 @@ import classNames from "classnames"; import { authentificated, selectUser } from "@/pages/Auth/store/selectors"; import commonLogoSrc from "@/shared/assets/images/logo-sidenav-2.svg"; import { useIsTabletView } from "@/shared/hooks/viewport"; -import { CommonSidenavLayoutTab } from "@/shared/layouts"; import { CommonLogo } from "@/shared/ui-kit"; import { getUserName } from "@/shared/utils"; import { @@ -13,7 +12,6 @@ import { UserInfo, } from "../../../SidenavLayout/components/SidenavContent"; import { useGoToCreateCommon } from "../../hooks"; -import { LayoutTabs } from "../LayoutTabs"; import { Footer, Navigation, Projects } from "./components"; import styles from "./SidenavContent.module.scss"; @@ -49,12 +47,6 @@ const SidenavContent: FC = (props) => { )} - {isTabletView && ( - - )} {!isTabletView && ( <>
From ce2619590aee997c45040efde43381222263e36a Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 10:50:05 +0300 Subject: [PATCH 35/49] decrease item trigger height in projects tree --- .../SidenavContent/components/Projects/Projects.module.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/Projects/Projects.module.scss b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/Projects/Projects.module.scss index c1938dec1e..c54856fec8 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/Projects/Projects.module.scss +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/Projects/Projects.module.scss @@ -9,16 +9,12 @@ --item-pl-per-level: 1.25rem; --item-arrow-pl: 0.5rem; - height: 3.375rem; + height: 3rem; border-radius: 0; &:hover { --bg-color: #{$c-pink-hover-feed-cards}; } - - @include tablet { - height: 4rem; - } } .projectsTreeItemTriggerActiveClassName { --bg-color: #{$c-pink-active-feed-cards}; From dfdb75b07d35f3f0d11e6cd8d24f9b61191bec8b Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 10:55:06 +0300 Subject: [PATCH 36/49] change back icon to sidebar icon in v04 feed mobile header --- .../HeaderContent_v04/HeaderContent_v04.module.scss | 2 -- .../components/HeaderContent_v04/HeaderContent_v04.tsx | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.module.scss b/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.module.scss index 7adfd958c3..4a8ea29ac2 100644 --- a/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.module.scss +++ b/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.module.scss @@ -57,8 +57,6 @@ width: 1.5rem; height: 1.5rem; margin-right: 0.625rem; - transform: rotate(180deg); - color: $c-neutrals-600; } .image { diff --git a/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.tsx b/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.tsx index bf4d0ea019..ad7da23405 100644 --- a/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.tsx +++ b/src/pages/commonFeed/components/HeaderContent_v04/HeaderContent_v04.tsx @@ -5,7 +5,7 @@ import { NewStreamButton } from "@/pages/common/components/CommonTabPanels/compo import { useRoutesContext } from "@/shared/contexts"; import { useCommonFollow } from "@/shared/hooks/useCases"; import { useIsTabletView } from "@/shared/hooks/viewport"; -import { RightArrowThinIcon, StarIcon } from "@/shared/icons"; +import { SidebarIcon, StarIcon } from "@/shared/icons"; import { CirclesPermissions, Common, @@ -39,7 +39,7 @@ const HeaderContent_v04: FC = (props) => {
} + iconEl={} /> Date: Tue, 14 Nov 2023 09:41:38 +0100 Subject: [PATCH 37/49] update craco configuration to disable source mapping on staging env --- craco.config.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/craco.config.js b/craco.config.js index 4398b2d0ed..5f90400839 100644 --- a/craco.config.js +++ b/craco.config.js @@ -25,10 +25,9 @@ module.exports = { { plugin: { overrideWebpackConfig: ({ webpackConfig }) => { - webpackConfig.devtool = - process.env.REACT_APP_ENV === "dev" - ? "source-map" - : "eval-cheap-module-source-map"; + if (process.env.REACT_APP_ENV === "stage") { + delete webpackConfig.devtool; + } return webpackConfig; }, From f610309d6c1d9423fc5be55811370d2871a40114 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 13:32:07 +0300 Subject: [PATCH 38/49] Revert "Disable source mapping in staging" --- craco.config.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/craco.config.js b/craco.config.js index 5f90400839..4398b2d0ed 100644 --- a/craco.config.js +++ b/craco.config.js @@ -25,9 +25,10 @@ module.exports = { { plugin: { overrideWebpackConfig: ({ webpackConfig }) => { - if (process.env.REACT_APP_ENV === "stage") { - delete webpackConfig.devtool; - } + webpackConfig.devtool = + process.env.REACT_APP_ENV === "dev" + ? "source-map" + : "eval-cheap-module-source-map"; return webpackConfig; }, From 2f0e27c284ab5d8ae90678f8258fe7a6aa1b43cb Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 13:42:15 +0300 Subject: [PATCH 39/49] change sidenav width to 87% --- .../layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss | 2 +- .../MultipleSpacesLayout/MultipleSpacesLayout.module.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss b/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss index 21820b7174..6ccf4ac9a9 100644 --- a/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss +++ b/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss @@ -27,7 +27,7 @@ @include tablet { --sb-max-width: 100%; --sb-width: 100%; - --sb-content-width: 90%; + --sb-content-width: 87%; --layout-tabs-height: 4rem; } } diff --git a/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss b/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss index 6eee670672..6b00334f99 100644 --- a/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss +++ b/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss @@ -30,7 +30,7 @@ --main-pl: unset; --sb-max-width: 100%; --sb-width: 100%; - --sb-content-width: 90%; + --sb-content-width: 87%; --layout-tabs-height: 4rem; --header-h: 0; } From 38c4a98b5583b082030c0cbc01f508dc5e574376 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 13:51:13 +0300 Subject: [PATCH 40/49] remove duplicated dm button from inbox v04 --- .../inbox/components/HeaderContent_v04/HeaderContent_v04.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/pages/inbox/components/HeaderContent_v04/HeaderContent_v04.tsx b/src/pages/inbox/components/HeaderContent_v04/HeaderContent_v04.tsx index 1697379141..b024903f9b 100644 --- a/src/pages/inbox/components/HeaderContent_v04/HeaderContent_v04.tsx +++ b/src/pages/inbox/components/HeaderContent_v04/HeaderContent_v04.tsx @@ -38,10 +38,6 @@ const HeaderContent_v04: FC = (props) => { isMobileVersion={isMobileVersion} ButtonComponent={PlusButton} /> -
); From 28fe5da53eb8fbb3a3702dbb4704e0df47fb1342 Mon Sep 17 00:00:00 2001 From: Kiryl Budnik Date: Tue, 14 Nov 2023 11:57:10 +0100 Subject: [PATCH 41/49] update craco configuration to disable source mapping on prod --- craco.config.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/craco.config.js b/craco.config.js index f4477e6d2f..1a0f6e1601 100644 --- a/craco.config.js +++ b/craco.config.js @@ -22,5 +22,16 @@ module.exports = { tsConfigPath: "./tsconfig.paths.json", }, }, + { + plugin: { + overrideWebpackConfig: ({ webpackConfig }) => { + if (process.env.REACT_APP_ENV === "production") { + delete webpackConfig.devtool; + } + + return webpackConfig; + }, + }, + }, ], }; From 13537f94706262c6577b89a9a58a46b1a9273868 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 13:58:58 +0300 Subject: [PATCH 42/49] change mobile sidenav bg --- .../CommonSidenavLayout/CommonSidenavLayout.module.scss | 1 + .../MultipleSpacesLayout/MultipleSpacesLayout.module.scss | 1 + src/shared/ui-kit/Sidenav/Sidenav.module.scss | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss b/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss index 6ccf4ac9a9..a9bab75222 100644 --- a/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss +++ b/src/shared/layouts/CommonSidenavLayout/CommonSidenavLayout.module.scss @@ -28,6 +28,7 @@ --sb-max-width: 100%; --sb-width: 100%; --sb-content-width: 87%; + --sb-shadow: 0.125rem 0 0.375rem #{$c-sidebar-shadow}; --layout-tabs-height: 4rem; } } diff --git a/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss b/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss index 6b00334f99..7ab633234d 100644 --- a/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss +++ b/src/shared/layouts/MultipleSpacesLayout/MultipleSpacesLayout.module.scss @@ -31,6 +31,7 @@ --sb-max-width: 100%; --sb-width: 100%; --sb-content-width: 87%; + --sb-shadow: 0.125rem 0 0.375rem #{$c-sidebar-shadow}; --layout-tabs-height: 4rem; --header-h: 0; } diff --git a/src/shared/ui-kit/Sidenav/Sidenav.module.scss b/src/shared/ui-kit/Sidenav/Sidenav.module.scss index 9c9d27852e..177a3092d9 100644 --- a/src/shared/ui-kit/Sidenav/Sidenav.module.scss +++ b/src/shared/ui-kit/Sidenav/Sidenav.module.scss @@ -15,7 +15,7 @@ $zIndex: 3; left: 0; z-index: $zIndex; display: block; - background-color: $c-gray-5; + background-color: $c-shades-white; opacity: 0.5; animation: fade var(--sb-transition-duration); } @@ -42,6 +42,7 @@ $zIndex: 3; @include tablet { right: 0; + backdrop-filter: blur(0.125rem); } @media (prefers-reduced-motion: reduce) { From 48c3040e4f1c0b5c4ddccb94f366759e226d0d91 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 14:05:23 +0300 Subject: [PATCH 43/49] add close sidenav button to v04 --- .../SidenavContent/SidenavContent.module.scss | 5 +++++ .../components/SidenavContent/SidenavContent.tsx | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss index 6c544b882b..143d213365 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.module.scss @@ -37,6 +37,11 @@ } } +.closeIconWrapper { + margin-left: auto; + padding: 1.25rem 1.5rem; +} + .separator { flex-shrink: 0; height: 0.0625rem; diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx index 881bcc9499..c8c2609f1a 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/SidenavContent.tsx @@ -3,7 +3,9 @@ import { useSelector } from "react-redux"; import classNames from "classnames"; import { authentificated, selectUser } from "@/pages/Auth/store/selectors"; import commonLogoSrc from "@/shared/assets/images/logo-sidenav-2.svg"; +import { ButtonIcon } from "@/shared/components"; import { useIsTabletView } from "@/shared/hooks/viewport"; +import { Close2Icon } from "@/shared/icons"; import { CommonLogo } from "@/shared/ui-kit"; import { getUserName } from "@/shared/utils"; import { @@ -17,10 +19,11 @@ import styles from "./SidenavContent.module.scss"; interface SidenavContentProps { className?: string; + onClose?: () => void; } const SidenavContent: FC = (props) => { - const { className } = props; + const { className, onClose } = props; const isAuthenticated = useSelector(authentificated()); const user = useSelector(selectUser()); const isTabletView = useIsTabletView(); @@ -39,6 +42,11 @@ const SidenavContent: FC = (props) => { logoClassName={styles.commonLogo} logoSrc={commonLogoSrc} /> + {isTabletView && ( + + + + )} {!isTabletView && ( <> {separatorEl} From 2979a19eeaaf31ae0a85bb4b5a41ad85518a63d3 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 14:38:45 +0300 Subject: [PATCH 44/49] add rootCommonId to last common from feed and change persistence --- src/pages/commonFeed/CommonFeed.tsx | 14 +++++++++++++ src/store/states/commonLayout/types.ts | 29 ++++++++++++++++++-------- src/store/store.tsx | 4 ++-- src/store/transforms.ts | 22 +++++++++++++++++++ 4 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index a852e46048..cef65399b3 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -416,6 +416,7 @@ const CommonFeedComponent: FC = (props) => { useEffect(() => { return () => { const common = stateRef.current?.data?.common; + const rootCommon = stateRef.current?.data?.rootCommon; dispatch( commonLayoutActions.setLastCommonFromFeed({ @@ -426,6 +427,19 @@ const CommonFeedComponent: FC = (props) => { image: common.image, isProject: checkIsProject(common), memberCount: common.memberCount, + rootCommon: common.rootCommonId + ? { + id: common.rootCommonId, + data: rootCommon + ? { + name: rootCommon.name, + image: rootCommon.image, + isProject: false, + memberCount: rootCommon.memberCount, + } + : null, + } + : null, } : null, }), diff --git a/src/store/states/commonLayout/types.ts b/src/store/states/commonLayout/types.ts index 64af0fd0a8..fbe4fc9664 100644 --- a/src/store/states/commonLayout/types.ts +++ b/src/store/states/commonLayout/types.ts @@ -1,16 +1,27 @@ import { ProjectsStateItem } from "../projects"; +interface LastCommonFromFeedData { + name: string; + image: string; + isProject: boolean; + memberCount: number; +} + +interface LastCommonFromFeed { + id: string; + data: + | (LastCommonFromFeedData & { + rootCommon: { + id: string; + data: LastCommonFromFeedData | null; + } | null; + }) + | null; +} + export interface CommonLayoutState { currentCommonId: string | null; - lastCommonFromFeed: { - id: string; - data: { - name: string; - image: string; - isProject: boolean; - memberCount: number; - } | null; - } | null; + lastCommonFromFeed: LastCommonFromFeed | null; commons: ProjectsStateItem[]; areCommonsLoading: boolean; areCommonsFetched: boolean; diff --git a/src/store/store.tsx b/src/store/store.tsx index 3ed1582821..3e49c96323 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -17,7 +17,7 @@ import createSagaMiddleware from "redux-saga"; import { AppState } from "@/shared/interfaces"; import rootReducer from "./reducer"; import appSagas from "./saga"; -import { inboxTransform } from "./transforms"; +import { inboxTransform, lastCommonFromFeedTransform } from "./transforms"; const persistConfig: PersistConfig = { key: "root", @@ -32,7 +32,7 @@ const persistConfig: PersistConfig = { "multipleSpacesLayout", ], stateReconciler: autoMergeLevel2, - transforms: [inboxTransform], + transforms: [inboxTransform, lastCommonFromFeedTransform], }; const sagaMiddleware = createSagaMiddleware(); diff --git a/src/store/transforms.ts b/src/store/transforms.ts index 98efc666da..11d3e618b8 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -2,6 +2,7 @@ import { createTransform } from "redux-persist"; import { deserializeFeedLayoutItemWithFollowData } from "@/shared/interfaces"; import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; import { getFeedLayoutItemDateForSorting } from "@/store/states/inbox/utils"; +import { CommonLayoutState } from "./states/commonLayout"; import { InboxItems, InboxState } from "./states/inbox"; export const inboxTransform = createTransform( @@ -43,3 +44,24 @@ export const inboxTransform = createTransform( }), { whitelist: ["inbox"] }, ); + +export const lastCommonFromFeedTransform = createTransform( + (inboundState: CommonLayoutState) => { + const rootCommon = inboundState.lastCommonFromFeed?.data?.rootCommon; + + return { + ...inboundState, + lastCommonFromFeed: rootCommon + ? { + id: rootCommon.id, + data: rootCommon.data && { + ...rootCommon.data, + rootCommon: null, + }, + } + : inboundState.lastCommonFromFeed, + }; + }, + (outboundState: CommonLayoutState) => outboundState, + { whitelist: ["commonLayout"] }, +); From e7260f145912ea1fbee7d42c7943f1710668d6d2 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 15:09:11 +0300 Subject: [PATCH 45/49] use subscription in useUserCommonIds use-case --- src/services/Common.ts | 17 +++++++ src/shared/hooks/useCases/useUserCommonIds.ts | 44 ++++++------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/src/services/Common.ts b/src/services/Common.ts index 7bc06fc7ea..50bd9192f5 100644 --- a/src/services/Common.ts +++ b/src/services/Common.ts @@ -160,6 +160,23 @@ class CommonService { .get() ).docs.map((ref) => ref.ref.path.split("/")[1]); + public subscribeToUserCommonIds = ( + userId: string, + callback: (data: string[]) => void, + ): UnsubscribeFunction => { + const query = firebase + .firestore() + .collectionGroup(SubCollections.Members) + .where("userId", "==", userId); + + return query.onSnapshot((snapshot) => { + const userCommonIds = snapshot.docs.map( + (ref) => ref.ref.path.split("/")[1], + ); + callback(userCommonIds); + }); + }; + public getAllUserCommonMemberInfo = async ( userId: string, ): Promise<(CommonMember & { commonId: string })[]> => { diff --git a/src/shared/hooks/useCases/useUserCommonIds.ts b/src/shared/hooks/useCases/useUserCommonIds.ts index 573e9aff85..5b2fbd0ece 100644 --- a/src/shared/hooks/useCases/useUserCommonIds.ts +++ b/src/shared/hooks/useCases/useUserCommonIds.ts @@ -1,13 +1,11 @@ -import { useCallback, useEffect } from "react"; +import { useEffect } from "react"; import { useSelector } from "react-redux"; import { selectUser } from "@/pages/Auth/store/selectors"; -import { CommonService, Logger } from "@/services"; +import { CommonService } from "@/services"; import { LoadingState } from "@/shared/interfaces"; -import { useIsMounted } from "../useIsMounted"; import { useLoadingState } from "../useLoadingState"; export const useUserCommonIds = (): LoadingState => { - const isMounted = useIsMounted(); const user = useSelector(selectUser()); const userId = user?.uid; const [state, setState] = useLoadingState([], { @@ -15,8 +13,13 @@ export const useUserCommonIds = (): LoadingState => { fetched: !userId, }); - const fetchUserCommonIds = useCallback(async () => { + useEffect(() => { if (!userId) { + setState({ + loading: false, + fetched: true, + data: [], + }); return; } @@ -26,37 +29,18 @@ export const useUserCommonIds = (): LoadingState => { data: [], }); - let userCommonIds: string[] = []; - - try { - userCommonIds = await CommonService.getUserCommonIds(userId); - } catch (error) { - Logger.error(error); - } finally { - if (isMounted()) { + const unsubscribe = CommonService.subscribeToUserCommonIds( + userId, + (userCommonIds) => { setState({ loading: false, fetched: true, data: userCommonIds, }); - } - } - }, [userId]); + }, + ); - const setUserCommonIds = useCallback((ids: string[]) => { - setState({ - loading: false, - fetched: true, - data: ids, - }); - }, []); - - useEffect(() => { - if (userId) { - fetchUserCommonIds(); - } else { - setUserCommonIds([]); - } + return unsubscribe; }, [userId]); return { From 0c2a6953d6f0d58b96299b7cdba752e6e3b3136c Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 15:13:12 +0300 Subject: [PATCH 46/49] add user common ids and create common modal to layout tabs spaces tab --- .../components/LayoutTabs/LayoutTabs.tsx | 77 +++++++++++-------- .../NoCommonsInfo/NoCommonsInfo.module.scss | 6 ++ 2 files changed, 52 insertions(+), 31 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx index 85e41f62fb..d698b0fb55 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx @@ -8,8 +8,10 @@ import { } from "@/pages/Auth/store/selectors"; import { Tab, Tabs } from "@/shared/components"; import { useRoutesContext } from "@/shared/contexts"; +import { useModal } from "@/shared/hooks"; +import { useUserCommonIds } from "@/shared/hooks/useCases"; import { Avatar2Icon, Blocks2Icon, InboxIcon } from "@/shared/icons"; -import { openSidenav } from "@/shared/utils"; +import { CreateCommonPrompt } from "@/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components"; import { selectCommonLayoutLastCommonFromFeed } from "@/store/states"; import { LayoutTab } from "../../constants"; import { getActiveLayoutTab, getLayoutTabName } from "./utils"; @@ -39,6 +41,12 @@ const LayoutTabs: FC = (props) => { const userStreamsWithNotificationsAmount = useSelector( selectUserStreamsWithNotificationsAmount(), ); + const { data: userCommonIds } = useUserCommonIds(); + const { + isShowing: isCreateCommonPromptOpen, + onOpen: onCreateCommonPromptOpen, + onClose: onCreateCommonPromptClose, + } = useModal(false); const finalUserStreamsWithNotificationsAmount = userStreamsWithNotificationsAmount && userStreamsWithNotificationsAmount > 99 @@ -73,10 +81,12 @@ const LayoutTabs: FC = (props) => { } as CSSProperties; const handleSpacesClick = () => { - if (lastCommonIdFromFeed) { - history.push(getCommonPagePath(lastCommonIdFromFeed.id)); + const commonForRedirectId = lastCommonIdFromFeed?.id || userCommonIds[0]; + + if (commonForRedirectId) { + history.push(getCommonPagePath(commonForRedirectId)); } else { - openSidenav(); + onCreateCommonPromptOpen(); } }; @@ -101,33 +111,38 @@ const LayoutTabs: FC = (props) => { }; return ( - - {tabs.map((tab) => ( - - {tab.icon} - {typeof tab.notificationsAmount === "number" && ( - - {tab.notificationsAmount} - - )} -
- } - includeDefaultMobileStyles={false} - /> - ))} - + <> + + {tabs.map((tab) => ( + + {tab.icon} + {typeof tab.notificationsAmount === "number" && ( + + {tab.notificationsAmount} + + )} + + } + includeDefaultMobileStyles={false} + /> + ))} + + {isCreateCommonPromptOpen && ( + + )} + ); }; diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components/CreateCommonPrompt/components/NoCommonsInfo/NoCommonsInfo.module.scss b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components/CreateCommonPrompt/components/NoCommonsInfo/NoCommonsInfo.module.scss index 12cabf9c9b..27e90a2667 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components/CreateCommonPrompt/components/NoCommonsInfo/NoCommonsInfo.module.scss +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components/CreateCommonPrompt/components/NoCommonsInfo/NoCommonsInfo.module.scss @@ -1,3 +1,5 @@ +@import "../../../../../../../../../../../styles/sizes"; + .modal { max-width: 26rem; } @@ -10,4 +12,8 @@ .createCommonButton { margin-left: auto; + + @include tablet { + margin-right: auto; + } } From b600c6c7f6cf80b8c0337f24721593a7b6240394 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 15:44:26 +0300 Subject: [PATCH 47/49] remove prefers-reduced-motion from sidenav --- src/shared/ui-kit/Sidenav/Sidenav.module.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/shared/ui-kit/Sidenav/Sidenav.module.scss b/src/shared/ui-kit/Sidenav/Sidenav.module.scss index 177a3092d9..4eaf31ad8a 100644 --- a/src/shared/ui-kit/Sidenav/Sidenav.module.scss +++ b/src/shared/ui-kit/Sidenav/Sidenav.module.scss @@ -44,10 +44,6 @@ $zIndex: 3; right: 0; backdrop-filter: blur(0.125rem); } - - @media (prefers-reduced-motion: reduce) { - --sb-transition-duration: 1ms; - } } .sidenavWithAnimation { display: flex; From 9f547f2a5f51b57c1168b93472b0cc463a507f3e Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 15:45:55 +0300 Subject: [PATCH 48/49] change sidenav bg color --- src/shared/ui-kit/Sidenav/Sidenav.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/ui-kit/Sidenav/Sidenav.module.scss b/src/shared/ui-kit/Sidenav/Sidenav.module.scss index 4eaf31ad8a..b92898b8e8 100644 --- a/src/shared/ui-kit/Sidenav/Sidenav.module.scss +++ b/src/shared/ui-kit/Sidenav/Sidenav.module.scss @@ -15,7 +15,7 @@ $zIndex: 3; left: 0; z-index: $zIndex; display: block; - background-color: $c-shades-white; + background-color: $c-gray-5; opacity: 0.5; animation: fade var(--sb-transition-duration); } From 15d48f5b2cf279250351c001d20f16a310b1d71e Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Tue, 14 Nov 2023 15:55:36 +0300 Subject: [PATCH 49/49] change size and padding of tree item trigger arrow --- .../PlaceholderTreeItem/PlaceholderTreeItem.module.scss | 2 +- .../components/TreeItemTrigger/TreeItemTrigger.module.scss | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/PlaceholderTreeItem/PlaceholderTreeItem.module.scss b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/PlaceholderTreeItem/PlaceholderTreeItem.module.scss index 9c10d677ce..36c21583b0 100644 --- a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/PlaceholderTreeItem/PlaceholderTreeItem.module.scss +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/PlaceholderTreeItem/PlaceholderTreeItem.module.scss @@ -3,7 +3,7 @@ // based on TreeItemTrigger arrow icon button .gap { // {item-arrow-pl} + {arrow-icon-button-pr} + {arrow-icon-button-width} - width: calc(var(--item-arrow-pl) + 0.625rem + 0.375rem); + width: calc(var(--item-arrow-pl) + 1.25rem + 0.5rem); } .image { diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.module.scss b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.module.scss index 57d9f17603..933ca60aa0 100644 --- a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.module.scss +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.module.scss @@ -36,13 +36,15 @@ } .arrowIconButton { - padding: 0.75rem 0.625rem 0.75rem var(--item-arrow-pl); + padding: 0.75rem 1.25rem 0.75rem var(--item-arrow-pl); } .arrowIconButtonHidden { visibility: hidden; } .arrowIcon { + width: 0.5rem; + height: 0.625rem; color: $c-neutrals-600; transition: transform 0.2s; }