From 105b63a11af92305d471e078aa33b6e264bfb867 Mon Sep 17 00:00:00 2001 From: Roie Natan Date: Mon, 27 Nov 2023 17:46:43 -0500 Subject: [PATCH 01/32] fix: discussion creation system message don't work --- .../common/components/ChatComponent/ChatComponent.tsx | 1 + .../ChatComponent/hooks/useDiscussionChatAdapter.ts | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/common/components/ChatComponent/ChatComponent.tsx b/src/pages/common/components/ChatComponent/ChatComponent.tsx index 6f5ddc2663..3903aa4e78 100644 --- a/src/pages/common/components/ChatComponent/ChatComponent.tsx +++ b/src/pages/common/components/ChatComponent/ChatComponent.tsx @@ -174,6 +174,7 @@ export default function ChatComponent({ singleEmojiText: styles.singleEmojiText, multipleEmojiText: styles.multipleEmojiText, }, + onFeedItemClick, }); const { chatMessagesData, diff --git a/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts b/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts index ba39c257c5..77c2fcf37f 100644 --- a/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts +++ b/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts @@ -6,8 +6,8 @@ import { useDiscussionMessagesById, useMarkFeedItemAsSeen, } from "@/shared/hooks/useCases"; -import { DirectParent, User } from "@/shared/models"; import { TextStyles } from "@/shared/hooks/useCases/useDiscussionMessagesById"; +import { DirectParent, User } from "@/shared/models"; interface Options { hasPermissionToHide: boolean; @@ -28,8 +28,8 @@ interface Return { } export const useDiscussionChatAdapter = (options: Options): Return => { - const { hasPermissionToHide, textStyles, discussionId } = options; - + const { hasPermissionToHide, textStyles, discussionId, onFeedItemClick } = + options; const user = useSelector(selectUser()); const userId = user?.uid; const { data: commonMembers, fetchCommonMembers } = useCommonMembers(); @@ -44,7 +44,8 @@ export const useDiscussionChatAdapter = (options: Options): Return => { discussionId, hasPermissionToHide, users, - textStyles + textStyles, + onFeedItemClick, }); const { markFeedItemAsSeen } = useMarkFeedItemAsSeen(); From 4b407d393cb791564383ac2124bf5b9dece1b182 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 11:40:50 +0400 Subject: [PATCH 02/32] add logic to get and update user activity --- src/services/UserActivity.ts | 41 +++++++++++++++++++++++++++++++ src/services/index.ts | 1 + src/shared/models/UserActivity.ts | 3 +++ src/shared/models/index.tsx | 1 + src/shared/models/shared.tsx | 1 + 5 files changed, 47 insertions(+) create mode 100644 src/services/UserActivity.ts create mode 100644 src/shared/models/UserActivity.ts diff --git a/src/services/UserActivity.ts b/src/services/UserActivity.ts new file mode 100644 index 0000000000..9b21a9ce19 --- /dev/null +++ b/src/services/UserActivity.ts @@ -0,0 +1,41 @@ +import { Collection, UserActivity } from "@/shared/models"; +import { firestoreDataConverter } from "@/shared/utils"; +import firebase, { isFirestoreCacheError } from "@/shared/utils/firebase"; + +const userActivityConverter = firestoreDataConverter(); + +class UserActivityService { + private getUsersActivityCollection = () => + firebase + .firestore() + .collection(Collection.UsersActivity) + .withConverter(userActivityConverter); + + public getUserActivity = async ( + userId: string, + cached = false, + ): Promise => { + try { + const snapshot = await this.getUsersActivityCollection() + .doc(userId) + .get({ source: cached ? "cache" : "default" }); + + return snapshot?.data() || null; + } catch (error) { + if (cached && isFirestoreCacheError(error)) { + return this.getUserActivity(userId); + } else { + throw error; + } + } + }; + + public updateUserActivity = async ( + userId: string, + data: Partial, + ): Promise => { + await this.getUsersActivityCollection().doc(userId).update(data); + }; +} + +export default new UserActivityService(); diff --git a/src/services/index.ts b/src/services/index.ts index 4baa44fd2f..b5b8adc604 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -18,5 +18,6 @@ export { default as PayMeService } from "./PayMeService"; export { default as ProjectService } from "./Project"; export { default as ProposalService } from "./Proposal"; export { default as UserService } from "./User"; +export { default as UserActivityService } from "./UserActivity"; export { default as DiscussionMessageService } from "./DiscussionMessage"; export { default as NotionService } from "./Notion"; diff --git a/src/shared/models/UserActivity.ts b/src/shared/models/UserActivity.ts new file mode 100644 index 0000000000..3451afaa49 --- /dev/null +++ b/src/shared/models/UserActivity.ts @@ -0,0 +1,3 @@ +export interface UserActivity { + lastVisitedCommon?: string; +} diff --git a/src/shared/models/index.tsx b/src/shared/models/index.tsx index d1c61c51e4..fbb74b0468 100644 --- a/src/shared/models/index.tsx +++ b/src/shared/models/index.tsx @@ -24,4 +24,5 @@ export * from "./BankAccountDetails"; export * from "./Currency"; export * from "./SupportersData"; export * from "./Timestamp"; +export * from "./UserActivity"; export * from "./NotionIntegration"; diff --git a/src/shared/models/shared.tsx b/src/shared/models/shared.tsx index 8ff4b09431..60980a54d1 100644 --- a/src/shared/models/shared.tsx +++ b/src/shared/models/shared.tsx @@ -33,6 +33,7 @@ export enum Collection { Supporters = "supporters", Notifications = "notification", BankAccountDetails = "bankAccountDetails", + UsersActivity = "usersActivity", } export enum SubCollections { From 6ed61416242a05b87350d6c47cdd4e01cf86c41d Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 11:55:36 +0400 Subject: [PATCH 03/32] create use-case to update user activity --- src/shared/hooks/useCases/index.ts | 1 + .../hooks/useCases/useUserActivityUpdate.ts | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 src/shared/hooks/useCases/useUserActivityUpdate.ts diff --git a/src/shared/hooks/useCases/index.ts b/src/shared/hooks/useCases/index.ts index 51b09e439f..4694e458e0 100644 --- a/src/shared/hooks/useCases/index.ts +++ b/src/shared/hooks/useCases/index.ts @@ -23,6 +23,7 @@ export { useProposalById } from "./useProposalById"; export { useRootCommonMembershipIntro } from "./useRootCommonMembershipIntro"; export { useSubCommons } from "./useSubCommons"; export { useSupportersData } from "./useSupportersData"; +export { useUserActivityUpdate } from "./useUserActivityUpdate"; export { useUserById } from "./useUserById"; export { default as useUserCards } from "./useUserCards"; export { useUserCommonIds } from "./useUserCommonIds"; diff --git a/src/shared/hooks/useCases/useUserActivityUpdate.ts b/src/shared/hooks/useCases/useUserActivityUpdate.ts new file mode 100644 index 0000000000..b4a0064766 --- /dev/null +++ b/src/shared/hooks/useCases/useUserActivityUpdate.ts @@ -0,0 +1,22 @@ +import { useCallback } from "react"; +import { Logger, UserActivityService } from "@/services"; +import { UserActivity } from "@/shared/models"; + +interface Return { + updateUserActivity: (userId: string, data: Partial) => void; +} + +export const useUserActivityUpdate = (): Return => { + const updateUserActivity = useCallback( + async (userId: string, data: Partial) => { + try { + await UserActivityService.updateUserActivity(userId, data); + } catch (error) { + Logger.error(error); + } + }, + [], + ); + + return { updateUserActivity }; +}; From 28bf1842c54c3e39413e9a15714135eb07086574 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 14:01:56 +0400 Subject: [PATCH 04/32] add logic to store last visited common by user --- src/pages/commonFeed/CommonFeed.tsx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index cef65399b3..8b1ba8bc81 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -27,7 +27,11 @@ import { } from "@/shared/constants"; import { useRoutesContext } from "@/shared/contexts"; import { useAuthorizedModal, useQueryParams } from "@/shared/hooks"; -import { useCommonFeedItems, useUserCommonIds } from "@/shared/hooks/useCases"; +import { + useCommonFeedItems, + useUserActivityUpdate, + useUserCommonIds, +} from "@/shared/hooks/useCases"; import { useCommonPinnedFeedItems } from "@/shared/hooks/useCases/useCommonPinnedFeedItems"; import { SidebarIcon } from "@/shared/icons"; import { @@ -114,6 +118,7 @@ const CommonFeedComponent: FC = (props) => { fetched: isCommonDataFetched, fetchCommonData, } = useCommonData(userId); + const { updateUserActivity } = useUserActivityUpdate(); const parentCommonId = commonData?.common.directParent?.commonId; const anotherCommonId = userCommonIds[0] === commonId ? userCommonIds[1] : userCommonIds[0]; @@ -447,6 +452,16 @@ const CommonFeedComponent: FC = (props) => { }; }, [commonId]); + useEffect(() => { + if (!userId) { + return; + } + + updateUserActivity(userId, { + lastVisitedCommon: commonId, + }); + }, [userId, commonId]); + if (!isDataFetched) { const headerEl = renderLoadingHeader ? ( renderLoadingHeader() From 0317aff2cc028ad05e79933282dce29201d4135c Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 14:05:20 +0400 Subject: [PATCH 05/32] fix user activity update method --- src/services/UserActivity.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/services/UserActivity.ts b/src/services/UserActivity.ts index 9b21a9ce19..20733a2578 100644 --- a/src/services/UserActivity.ts +++ b/src/services/UserActivity.ts @@ -34,7 +34,9 @@ class UserActivityService { userId: string, data: Partial, ): Promise => { - await this.getUsersActivityCollection().doc(userId).update(data); + await this.getUsersActivityCollection() + .doc(userId) + .set(data, { merge: true }); }; } From de5557d8df6cc4f67d8ea28a86cd48b79234b12d Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 14:11:32 +0400 Subject: [PATCH 06/32] refactor user activity use-case to be more generic --- src/pages/commonFeed/CommonFeed.tsx | 12 ++++-------- src/shared/hooks/useCases/index.ts | 2 +- .../{useUserActivityUpdate.ts => useUserActivity.ts} | 12 ++++++++---- 3 files changed, 13 insertions(+), 13 deletions(-) rename src/shared/hooks/useCases/{useUserActivityUpdate.ts => useUserActivity.ts} (63%) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 8b1ba8bc81..07cee3e329 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, - useUserActivityUpdate, + useUserActivity, useUserCommonIds, } from "@/shared/hooks/useCases"; import { useCommonPinnedFeedItems } from "@/shared/hooks/useCases/useCommonPinnedFeedItems"; @@ -118,7 +118,7 @@ const CommonFeedComponent: FC = (props) => { fetched: isCommonDataFetched, fetchCommonData, } = useCommonData(userId); - const { updateUserActivity } = useUserActivityUpdate(); + const { updateUserActivity } = useUserActivity(userId); const parentCommonId = commonData?.common.directParent?.commonId; const anotherCommonId = userCommonIds[0] === commonId ? userCommonIds[1] : userCommonIds[0]; @@ -453,14 +453,10 @@ const CommonFeedComponent: FC = (props) => { }, [commonId]); useEffect(() => { - if (!userId) { - return; - } - - updateUserActivity(userId, { + updateUserActivity({ lastVisitedCommon: commonId, }); - }, [userId, commonId]); + }, [updateUserActivity, commonId]); if (!isDataFetched) { const headerEl = renderLoadingHeader ? ( diff --git a/src/shared/hooks/useCases/index.ts b/src/shared/hooks/useCases/index.ts index 4694e458e0..72c3a2816f 100644 --- a/src/shared/hooks/useCases/index.ts +++ b/src/shared/hooks/useCases/index.ts @@ -23,7 +23,7 @@ export { useProposalById } from "./useProposalById"; export { useRootCommonMembershipIntro } from "./useRootCommonMembershipIntro"; export { useSubCommons } from "./useSubCommons"; export { useSupportersData } from "./useSupportersData"; -export { useUserActivityUpdate } from "./useUserActivityUpdate"; +export { useUserActivity } from "./useUserActivity"; export { useUserById } from "./useUserById"; export { default as useUserCards } from "./useUserCards"; export { useUserCommonIds } from "./useUserCommonIds"; diff --git a/src/shared/hooks/useCases/useUserActivityUpdate.ts b/src/shared/hooks/useCases/useUserActivity.ts similarity index 63% rename from src/shared/hooks/useCases/useUserActivityUpdate.ts rename to src/shared/hooks/useCases/useUserActivity.ts index b4a0064766..9dddef80ba 100644 --- a/src/shared/hooks/useCases/useUserActivityUpdate.ts +++ b/src/shared/hooks/useCases/useUserActivity.ts @@ -3,19 +3,23 @@ import { Logger, UserActivityService } from "@/services"; import { UserActivity } from "@/shared/models"; interface Return { - updateUserActivity: (userId: string, data: Partial) => void; + updateUserActivity: (data: Partial) => void; } -export const useUserActivityUpdate = (): Return => { +export const useUserActivity = (userId?: string): Return => { const updateUserActivity = useCallback( - async (userId: string, data: Partial) => { + async (data: Partial) => { + if (!userId) { + return; + } + try { await UserActivityService.updateUserActivity(userId, data); } catch (error) { Logger.error(error); } }, - [], + [userId], ); return { updateUserActivity }; From 95fce21e828c479530ca55cd27bd38248690716c Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 14:22:28 +0400 Subject: [PATCH 07/32] add subscription to user activity use-case --- src/services/UserActivity.ts | 37 +++++++++----------- src/shared/hooks/useCases/useUserActivity.ts | 33 +++++++++++++++-- 2 files changed, 47 insertions(+), 23 deletions(-) diff --git a/src/services/UserActivity.ts b/src/services/UserActivity.ts index 20733a2578..12ef932d2e 100644 --- a/src/services/UserActivity.ts +++ b/src/services/UserActivity.ts @@ -1,6 +1,7 @@ +import { UnsubscribeFunction } from "@/shared/interfaces"; import { Collection, UserActivity } from "@/shared/models"; import { firestoreDataConverter } from "@/shared/utils"; -import firebase, { isFirestoreCacheError } from "@/shared/utils/firebase"; +import firebase from "@/shared/utils/firebase"; const userActivityConverter = firestoreDataConverter(); @@ -11,25 +12,6 @@ class UserActivityService { .collection(Collection.UsersActivity) .withConverter(userActivityConverter); - public getUserActivity = async ( - userId: string, - cached = false, - ): Promise => { - try { - const snapshot = await this.getUsersActivityCollection() - .doc(userId) - .get({ source: cached ? "cache" : "default" }); - - return snapshot?.data() || null; - } catch (error) { - if (cached && isFirestoreCacheError(error)) { - return this.getUserActivity(userId); - } else { - throw error; - } - } - }; - public updateUserActivity = async ( userId: string, data: Partial, @@ -38,6 +20,21 @@ class UserActivityService { .doc(userId) .set(data, { merge: true }); }; + + public subscribeToUserActivity = ( + userId: string, + callback: (userActivity: UserActivity) => void, + ): UnsubscribeFunction => { + const query = this.getUsersActivityCollection().doc(userId); + + return query.onSnapshot((snapshot) => { + const userActivity = snapshot.data(); + + if (userActivity) { + callback(userActivity); + } + }); + }; } export default new UserActivityService(); diff --git a/src/shared/hooks/useCases/useUserActivity.ts b/src/shared/hooks/useCases/useUserActivity.ts index 9dddef80ba..46fd284b7e 100644 --- a/src/shared/hooks/useCases/useUserActivity.ts +++ b/src/shared/hooks/useCases/useUserActivity.ts @@ -1,12 +1,17 @@ -import { useCallback } from "react"; +import { useCallback, useEffect } from "react"; import { Logger, UserActivityService } from "@/services"; +import { useLoadingState } from "@/shared/hooks"; +import { LoadingState } from "@/shared/interfaces"; import { UserActivity } from "@/shared/models"; -interface Return { +interface Return extends LoadingState { updateUserActivity: (data: Partial) => void; } export const useUserActivity = (userId?: string): Return => { + const [userActivityState, setUserActivityState] = + useLoadingState(null, { loading: true }); + const updateUserActivity = useCallback( async (data: Partial) => { if (!userId) { @@ -22,5 +27,27 @@ export const useUserActivity = (userId?: string): Return => { [userId], ); - return { updateUserActivity }; + useEffect(() => { + if (!userId) { + return; + } + + const unsubscribe = UserActivityService.subscribeToUserActivity( + userId, + (updatedUserActivity) => { + setUserActivityState({ + loading: false, + fetched: true, + data: updatedUserActivity, + }); + }, + ); + + return unsubscribe; + }, [userId]); + + return { + ...userActivityState, + updateUserActivity, + }; }; From fc55fca0686c88a036bf003841912f5e996400d7 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 14:35:30 +0400 Subject: [PATCH 08/32] remove breadcrumbs data from cache --- src/store/store.tsx | 8 +++++++- src/store/transforms.ts | 16 ++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/store/store.tsx b/src/store/store.tsx index f62f433117..232a0bc8e5 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -22,6 +22,7 @@ import { inboxTransform, lastCommonFromFeedTransform, cacheTransform, + multipleSpacesLayoutTransform, } from "./transforms"; const persistConfig: PersistConfig = { @@ -37,7 +38,12 @@ const persistConfig: PersistConfig = { "multipleSpacesLayout", ], stateReconciler: autoMergeLevel2, - transforms: [inboxTransform, lastCommonFromFeedTransform, cacheTransform], + transforms: [ + inboxTransform, + lastCommonFromFeedTransform, + cacheTransform, + multipleSpacesLayoutTransform, + ], }; const sagaMiddleware = createSagaMiddleware(); diff --git a/src/store/transforms.ts b/src/store/transforms.ts index 625133dfc4..86030684a0 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -1,9 +1,10 @@ import { createTransform } from "redux-persist"; import { deserializeFeedLayoutItemWithFollowData } from "@/shared/interfaces"; import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; +import { MultipleSpacesLayoutState } from "@/store/states"; import { getFeedLayoutItemDateForSorting } from "@/store/states/inbox/utils"; -import { CommonLayoutState } from "./states/commonLayout"; import { CacheState } from "./states/cache"; +import { CommonLayoutState } from "./states/commonLayout"; import { InboxItems, InboxState, @@ -89,7 +90,18 @@ export const cacheTransform = createTransform( (inboundState: CacheState) => inboundState, (outboundState: CacheState) => ({ ...outboundState, - discussionMessagesStates: {} + discussionMessagesStates: {}, }), { whitelist: ["cache"] }, ); + +export const multipleSpacesLayoutTransform = createTransform( + (inboundState: MultipleSpacesLayoutState) => ({ + ...inboundState, + breadcrumbs: null, + previousBreadcrumbs: null, + backUrl: null, + }), + (outboundState: MultipleSpacesLayoutState) => outboundState, + { whitelist: ["multipleSpacesLayout"] }, +); From cee21a7f2ce3b8e08918e2c7ae008932dd583967 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 14:36:27 +0400 Subject: [PATCH 09/32] use last visited by user common id in "My Spaces" --- .../Header/components/Navigation/Navigation.tsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx index b5a2baa7a0..c61c9e1224 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx @@ -4,12 +4,13 @@ import { useLocation } from "react-router"; import classNames from "classnames"; import { authentificated, + selectUser, selectUserStreamsWithNotificationsAmount, } from "@/pages/Auth/store/selectors"; import { InboxItemType, ROUTE_PATHS } from "@/shared/constants"; import { useRoutesContext } from "@/shared/contexts"; import { useModal } from "@/shared/hooks"; -import { useUserCommonIds } from "@/shared/hooks/useCases"; +import { useUserActivity, useUserCommonIds } from "@/shared/hooks/useCases"; import { BlocksIcon, InboxIcon } from "@/shared/icons"; import { CommonSidenavLayoutTab, @@ -44,13 +45,20 @@ const Navigation: FC = (props) => { const previousBreadcrumbs = useSelector( selectMultipleSpacesLayoutPreviousBreadcrumbs, ); + const user = useSelector(selectUser()); + const userId = user?.uid; const { data: userCommonIds } = useUserCommonIds(); + const { data: userActivity } = useUserActivity(userId); const breadcrumbs = previousBreadcrumbs || currentBreadcrumbs; const breadcrumbsCommonId = breadcrumbs?.type === InboxItemType.FeedItemFollow ? breadcrumbs.activeCommonId : ""; - const mySpacesCommonId = breadcrumbsCommonId || userCommonIds[0] || ""; + const mySpacesCommonId = + breadcrumbsCommonId || + userActivity?.lastVisitedCommon || + userCommonIds[0] || + ""; const mySpacesPagePath = ( mySpacesCommonId ? getCommonPagePath(mySpacesCommonId) : "" ) as ROUTE_PATHS; From 17a2840e2bcb5069f6077a2cd1cdfba09e9e31bb Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 14:49:22 +0400 Subject: [PATCH 10/32] use last visited by user common id to Spaces tab click handler --- .../components/LayoutTabs/LayoutTabs.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx index d698b0fb55..1931bd7a57 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx @@ -4,12 +4,13 @@ import { useHistory } from "react-router-dom"; import classNames from "classnames"; import { authentificated, + selectUser, selectUserStreamsWithNotificationsAmount, } 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 { useUserActivity, useUserCommonIds } from "@/shared/hooks/useCases"; import { Avatar2Icon, Blocks2Icon, InboxIcon } from "@/shared/icons"; import { CreateCommonPrompt } from "@/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components"; import { selectCommonLayoutLastCommonFromFeed } from "@/store/states"; @@ -41,7 +42,10 @@ const LayoutTabs: FC = (props) => { const userStreamsWithNotificationsAmount = useSelector( selectUserStreamsWithNotificationsAmount(), ); + const user = useSelector(selectUser()); + const userId = user?.uid; const { data: userCommonIds } = useUserCommonIds(); + const { data: userActivity } = useUserActivity(userId); const { isShowing: isCreateCommonPromptOpen, onOpen: onCreateCommonPromptOpen, @@ -81,7 +85,10 @@ const LayoutTabs: FC = (props) => { } as CSSProperties; const handleSpacesClick = () => { - const commonForRedirectId = lastCommonIdFromFeed?.id || userCommonIds[0]; + const commonForRedirectId = + lastCommonIdFromFeed?.id || + userActivity?.lastVisitedCommon || + userCommonIds[0]; if (commonForRedirectId) { history.push(getCommonPagePath(commonForRedirectId)); From f54671f22558d6df03f260ade89f6bbe319828f0 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 15:11:20 +0400 Subject: [PATCH 11/32] remove previous breadcrumbs related logic --- src/pages/commonFeed/CommonFeedPage.tsx | 2 +- .../components/Navigation/Navigation.tsx | 18 +----------------- .../states/multipleSpacesLayout/actions.ts | 4 ++-- .../states/multipleSpacesLayout/constants.ts | 2 +- .../states/multipleSpacesLayout/reducer.ts | 6 +----- .../states/multipleSpacesLayout/selectors.ts | 4 ---- src/store/states/multipleSpacesLayout/types.ts | 1 - src/store/transforms.ts | 1 - 8 files changed, 6 insertions(+), 32 deletions(-) diff --git a/src/pages/commonFeed/CommonFeedPage.tsx b/src/pages/commonFeed/CommonFeedPage.tsx index e5feeb8e05..233a90a473 100644 --- a/src/pages/commonFeed/CommonFeedPage.tsx +++ b/src/pages/commonFeed/CommonFeedPage.tsx @@ -95,7 +95,7 @@ const CommonFeedPage: FC = () => { useEffect(() => { return () => { - dispatch(multipleSpacesLayoutActions.moveBreadcrumbsToPrevious()); + dispatch(multipleSpacesLayoutActions.clearBreadcrumbs()); }; }, []); diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx index c61c9e1224..0133191d91 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx @@ -16,10 +16,6 @@ import { CommonSidenavLayoutTab, getActiveLayoutTab, } from "@/shared/layouts/CommonSidenavLayout"; -import { - selectMultipleSpacesLayoutBreadcrumbs, - selectMultipleSpacesLayoutPreviousBreadcrumbs, -} from "@/store/states"; import { CreateCommonPrompt, NavigationItem } from "./components"; import { NavigationItemOptions } from "./types"; import styles from "./Navigation.module.scss"; @@ -41,24 +37,12 @@ const Navigation: FC = (props) => { const userStreamsWithNotificationsAmount = useSelector( selectUserStreamsWithNotificationsAmount(), ); - const currentBreadcrumbs = useSelector(selectMultipleSpacesLayoutBreadcrumbs); - const previousBreadcrumbs = useSelector( - selectMultipleSpacesLayoutPreviousBreadcrumbs, - ); const user = useSelector(selectUser()); const userId = user?.uid; const { data: userCommonIds } = useUserCommonIds(); const { data: userActivity } = useUserActivity(userId); - const breadcrumbs = previousBreadcrumbs || currentBreadcrumbs; - const breadcrumbsCommonId = - breadcrumbs?.type === InboxItemType.FeedItemFollow - ? breadcrumbs.activeCommonId - : ""; const mySpacesCommonId = - breadcrumbsCommonId || - userActivity?.lastVisitedCommon || - userCommonIds[0] || - ""; + userActivity?.lastVisitedCommon || userCommonIds[0] || ""; const mySpacesPagePath = ( mySpacesCommonId ? getCommonPagePath(mySpacesCommonId) : "" ) as ROUTE_PATHS; diff --git a/src/store/states/multipleSpacesLayout/actions.ts b/src/store/states/multipleSpacesLayout/actions.ts index cb8210e550..251514f6d0 100644 --- a/src/store/states/multipleSpacesLayout/actions.ts +++ b/src/store/states/multipleSpacesLayout/actions.ts @@ -36,8 +36,8 @@ export const setBreadcrumbsData = createStandardAction( MultipleSpacesLayoutActionType.SET_BREADCRUMBS_DATA, )(); -export const moveBreadcrumbsToPrevious = createStandardAction( - MultipleSpacesLayoutActionType.MOVE_BREADCRUMBS_TO_PREVIOUS, +export const clearBreadcrumbs = createStandardAction( + MultipleSpacesLayoutActionType.CLEAR_BREADCRUMBS, )(); export const addOrUpdateProjectInBreadcrumbs = createStandardAction( diff --git a/src/store/states/multipleSpacesLayout/constants.ts b/src/store/states/multipleSpacesLayout/constants.ts index 7c5574dedb..6811fa17ad 100644 --- a/src/store/states/multipleSpacesLayout/constants.ts +++ b/src/store/states/multipleSpacesLayout/constants.ts @@ -10,7 +10,7 @@ export enum MultipleSpacesLayoutActionType { SET_BREADCRUMBS_DATA = "@MULTIPLE_SPACES_LAYOUT/SET_BREADCRUMBS_DATA", - MOVE_BREADCRUMBS_TO_PREVIOUS = "@MULTIPLE_SPACES_LAYOUT/MOVE_BREADCRUMBS_TO_PREVIOUS", + CLEAR_BREADCRUMBS = "@MULTIPLE_SPACES_LAYOUT/CLEAR_BREADCRUMBS", ADD_OR_UPDATE_PROJECT_IN_BREADCRUMBS = "@MULTIPLE_SPACES_LAYOUT/ADD_OR_UPDATE_PROJECT_IN_BREADCRUMBS", diff --git a/src/store/states/multipleSpacesLayout/reducer.ts b/src/store/states/multipleSpacesLayout/reducer.ts index 25fd6537ec..a05f3f182e 100644 --- a/src/store/states/multipleSpacesLayout/reducer.ts +++ b/src/store/states/multipleSpacesLayout/reducer.ts @@ -10,7 +10,6 @@ type Action = ActionType; const initialState: MultipleSpacesLayoutState = { breadcrumbs: null, - previousBreadcrumbs: null, backUrl: null, mainWidth: window.innerWidth, }; @@ -49,11 +48,8 @@ export const reducer = createReducer( nextState.breadcrumbs = payload && { ...payload }; }), ) - .handleAction(actions.moveBreadcrumbsToPrevious, (state) => + .handleAction(actions.clearBreadcrumbs, (state) => produce(state, (nextState) => { - nextState.previousBreadcrumbs = nextState.breadcrumbs && { - ...nextState.breadcrumbs, - }; nextState.breadcrumbs = null; }), ) diff --git a/src/store/states/multipleSpacesLayout/selectors.ts b/src/store/states/multipleSpacesLayout/selectors.ts index fff25a04d6..6c5600bd0a 100644 --- a/src/store/states/multipleSpacesLayout/selectors.ts +++ b/src/store/states/multipleSpacesLayout/selectors.ts @@ -3,10 +3,6 @@ import { AppState } from "@/shared/interfaces"; export const selectMultipleSpacesLayoutBreadcrumbs = (state: AppState) => state.multipleSpacesLayout.breadcrumbs; -export const selectMultipleSpacesLayoutPreviousBreadcrumbs = ( - state: AppState, -) => state.multipleSpacesLayout.previousBreadcrumbs; - export const selectMultipleSpacesLayoutBackUrl = (state: AppState) => state.multipleSpacesLayout.backUrl; diff --git a/src/store/states/multipleSpacesLayout/types.ts b/src/store/states/multipleSpacesLayout/types.ts index 3e2976f09f..8ba7a3534d 100644 --- a/src/store/states/multipleSpacesLayout/types.ts +++ b/src/store/states/multipleSpacesLayout/types.ts @@ -27,7 +27,6 @@ export type MultipleSpacesLayoutBreadcrumbs = export interface MultipleSpacesLayoutState { breadcrumbs: MultipleSpacesLayoutBreadcrumbs | null; - previousBreadcrumbs: MultipleSpacesLayoutBreadcrumbs | null; backUrl: string | null; mainWidth: number; } diff --git a/src/store/transforms.ts b/src/store/transforms.ts index 86030684a0..f407415742 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -99,7 +99,6 @@ export const multipleSpacesLayoutTransform = createTransform( (inboundState: MultipleSpacesLayoutState) => ({ ...inboundState, breadcrumbs: null, - previousBreadcrumbs: null, backUrl: null, }), (outboundState: MultipleSpacesLayoutState) => outboundState, From d68f5a9f26fe375ca7ff0578e10202d84af97408 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 15:21:41 +0400 Subject: [PATCH 12/32] remove root common from last common from feed --- src/pages/commonFeed/CommonFeed.tsx | 14 -------------- src/store/states/commonLayout/types.ts | 21 ++++++--------------- src/store/store.tsx | 8 +------- src/store/transforms.ts | 22 ---------------------- 4 files changed, 7 insertions(+), 58 deletions(-) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 07cee3e329..919a4b9030 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -421,7 +421,6 @@ const CommonFeedComponent: FC = (props) => { useEffect(() => { return () => { const common = stateRef.current?.data?.common; - const rootCommon = stateRef.current?.data?.rootCommon; dispatch( commonLayoutActions.setLastCommonFromFeed({ @@ -432,19 +431,6 @@ 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 fbe4fc9664..7e16b8515a 100644 --- a/src/store/states/commonLayout/types.ts +++ b/src/store/states/commonLayout/types.ts @@ -1,22 +1,13 @@ 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; + data: { + name: string; + image: string; + isProject: boolean; + memberCount: number; + } | null; } export interface CommonLayoutState { diff --git a/src/store/store.tsx b/src/store/store.tsx index 232a0bc8e5..e82f8979ad 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -20,7 +20,6 @@ import rootReducer from "./reducer"; import appSagas from "./saga"; import { inboxTransform, - lastCommonFromFeedTransform, cacheTransform, multipleSpacesLayoutTransform, } from "./transforms"; @@ -38,12 +37,7 @@ const persistConfig: PersistConfig = { "multipleSpacesLayout", ], stateReconciler: autoMergeLevel2, - transforms: [ - inboxTransform, - lastCommonFromFeedTransform, - cacheTransform, - multipleSpacesLayoutTransform, - ], + transforms: [inboxTransform, cacheTransform, multipleSpacesLayoutTransform], }; const sagaMiddleware = createSagaMiddleware(); diff --git a/src/store/transforms.ts b/src/store/transforms.ts index f407415742..0bde9e2597 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -4,7 +4,6 @@ import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; import { MultipleSpacesLayoutState } from "@/store/states"; import { getFeedLayoutItemDateForSorting } from "@/store/states/inbox/utils"; import { CacheState } from "./states/cache"; -import { CommonLayoutState } from "./states/commonLayout"; import { InboxItems, InboxState, @@ -65,27 +64,6 @@ 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"] }, -); - export const cacheTransform = createTransform( (inboundState: CacheState) => inboundState, (outboundState: CacheState) => ({ From af62d59cc4e47df2a86ebea55cdabbef6fd542ae Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 15:24:14 +0400 Subject: [PATCH 13/32] update last common from feed usage in layout tabs --- .../components/LayoutTabs/LayoutTabs.tsx | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx index 1931bd7a57..dec0d59f1a 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx @@ -1,5 +1,5 @@ import React, { CSSProperties, FC, ReactNode } from "react"; -import { useSelector } from "react-redux"; +import { useDispatch, useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; import classNames from "classnames"; import { @@ -13,7 +13,10 @@ import { useModal } from "@/shared/hooks"; import { useUserActivity, useUserCommonIds } from "@/shared/hooks/useCases"; import { Avatar2Icon, Blocks2Icon, InboxIcon } from "@/shared/icons"; import { CreateCommonPrompt } from "@/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components"; -import { selectCommonLayoutLastCommonFromFeed } from "@/store/states"; +import { + commonLayoutActions, + selectCommonLayoutLastCommonFromFeed, +} from "@/store/states"; import { LayoutTab } from "../../constants"; import { getActiveLayoutTab, getLayoutTabName } from "./utils"; import styles from "./LayoutTabs.module.scss"; @@ -32,6 +35,7 @@ interface TabConfiguration { const LayoutTabs: FC = (props) => { const { className } = props; + const dispatch = useDispatch(); const history = useHistory(); const { getCommonPagePath, getInboxPagePath, getProfilePagePath } = useRoutesContext(); @@ -85,10 +89,15 @@ const LayoutTabs: FC = (props) => { } as CSSProperties; const handleSpacesClick = () => { + if ( + lastCommonIdFromFeed && + lastCommonIdFromFeed.id !== userActivity?.lastVisitedCommon + ) { + dispatch(commonLayoutActions.setLastCommonFromFeed(null)); + } + const commonForRedirectId = - lastCommonIdFromFeed?.id || - userActivity?.lastVisitedCommon || - userCommonIds[0]; + userActivity?.lastVisitedCommon || userCommonIds[0]; if (commonForRedirectId) { history.push(getCommonPagePath(commonForRedirectId)); From 392bc5ad8a0eed3059e1fd5632e2e8d3ecfb5c8c Mon Sep 17 00:00:00 2001 From: Kiryl Budnik Date: Wed, 29 Nov 2023 12:25:07 +0100 Subject: [PATCH 14/32] open a chat only if it exists --- src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx index e045a84dbc..c26fe9a6e4 100644 --- a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx +++ b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx @@ -620,7 +620,13 @@ const FeedLayout: ForwardRefRenderFunction = ( }, [batchNumber]); useEffect(() => { - if (sharedFeedItemId && isTabletView && allFeedItems) { + if ( + isTabletView && + sharedFeedItemId && + allFeedItems.length > 0 && + allFeedItems.some((item) => item.itemId === sharedFeedItemId) + ) { + deleteQueryParam(QueryParamKey.Item, true); setActiveChatItem({ feedItemId: sharedFeedItemId }); } }, [sharedFeedItemId, isTabletView, allFeedItems]); From f7df2144df32fcfa2d292eb56c2700a533702e36 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 15:41:38 +0400 Subject: [PATCH 15/32] add method to get user activity --- src/services/UserActivity.ts | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/services/UserActivity.ts b/src/services/UserActivity.ts index 12ef932d2e..4de4e458be 100644 --- a/src/services/UserActivity.ts +++ b/src/services/UserActivity.ts @@ -1,7 +1,7 @@ import { UnsubscribeFunction } from "@/shared/interfaces"; import { Collection, UserActivity } from "@/shared/models"; import { firestoreDataConverter } from "@/shared/utils"; -import firebase from "@/shared/utils/firebase"; +import firebase, { isFirestoreCacheError } from "@/shared/utils/firebase"; const userActivityConverter = firestoreDataConverter(); @@ -12,6 +12,25 @@ class UserActivityService { .collection(Collection.UsersActivity) .withConverter(userActivityConverter); + public getUserActivity = async ( + userId: string, + cached = false, + ): Promise => { + try { + const snapshot = await this.getUsersActivityCollection() + .doc(userId) + .get({ source: cached ? "cache" : "default" }); + + return snapshot?.data() || null; + } catch (error) { + if (cached && isFirestoreCacheError(error)) { + return this.getUserActivity(userId); + } else { + throw error; + } + } + }; + public updateUserActivity = async ( userId: string, data: Partial, From 5227d92d7e8e6d3a914a4c3f8e4d70880fbb27ce Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 15:46:38 +0400 Subject: [PATCH 16/32] add last visited common in sidenav data fetch --- .../states/commonLayout/saga/getCommons.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/store/states/commonLayout/saga/getCommons.ts b/src/store/states/commonLayout/saga/getCommons.ts index 72ed1e171b..78b2956aac 100644 --- a/src/store/states/commonLayout/saga/getCommons.ts +++ b/src/store/states/commonLayout/saga/getCommons.ts @@ -1,6 +1,11 @@ import { call, put, select } from "redux-saga/effects"; import { selectUser } from "@/pages/Auth/store/selectors"; -import { CommonService, GovernanceService, ProjectService } from "@/services"; +import { + CommonService, + GovernanceService, + ProjectService, + UserActivityService, +} from "@/services"; import { Awaited } from "@/shared/interfaces"; import { User } from "@/shared/models"; import { compareCommonsByLastActivity, isError } from "@/shared/utils"; @@ -70,11 +75,20 @@ const getProjectsInfo = async ( export function* getCommons( action: ReturnType, ) { - const { payload: commonId = "" } = action; + let { payload: commonId = "" } = action; try { const user = (yield select(selectUser())) as User | null; const userId = user?.uid; + + if (!commonId && userId) { + const userActivity = (yield call( + UserActivityService.getUserActivity, + userId, + )) as Awaited>; + commonId = userActivity?.lastVisitedCommon || ""; + } + const { data, currentCommonId } = (yield call( getProjectsInfo, commonId, From f7b409946a345b1342faad6590d673ce67f14fb9 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Wed, 29 Nov 2023 15:09:51 +0300 Subject: [PATCH 17/32] CW-2355 Fixed flickering --- .../components/Scrollbar/Scrollbar.module.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/Scrollbar/Scrollbar.module.scss b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/Scrollbar/Scrollbar.module.scss index 0c33878853..52901cf672 100644 --- a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/Scrollbar/Scrollbar.module.scss +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/Scrollbar/Scrollbar.module.scss @@ -34,7 +34,9 @@ &:active { --scroll-thumb-color: #{$c-neutrals-300}; } - &:hover { - --scroll-thumb-color: $c-gray-20; + @media (hover: hover) and (pointer: fine) { + &:hover { + --scroll-thumb-color: $c-gray-20; + } } } From ccd140ad7b8068463516b6305020bbf1fd711937 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 16:53:49 +0400 Subject: [PATCH 18/32] update user activity use-case to use last common from feed --- src/shared/hooks/useCases/useUserActivity.ts | 37 +++++++++++++------ .../components/LayoutTabs/LayoutTabs.tsx | 22 ++--------- .../components/Navigation/Navigation.tsx | 5 +-- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/shared/hooks/useCases/useUserActivity.ts b/src/shared/hooks/useCases/useUserActivity.ts index 46fd284b7e..1379a0f51d 100644 --- a/src/shared/hooks/useCases/useUserActivity.ts +++ b/src/shared/hooks/useCases/useUserActivity.ts @@ -1,19 +1,31 @@ import { useCallback, useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { Logger, UserActivityService } from "@/services"; -import { useLoadingState } from "@/shared/hooks"; -import { LoadingState } from "@/shared/interfaces"; import { UserActivity } from "@/shared/models"; +import { + commonLayoutActions, + selectCommonLayoutLastCommonFromFeed, +} from "@/store/states"; -interface Return extends LoadingState { +interface Return { + lastVisitedCommon?: string; updateUserActivity: (data: Partial) => void; } export const useUserActivity = (userId?: string): Return => { - const [userActivityState, setUserActivityState] = - useLoadingState(null, { loading: true }); + const dispatch = useDispatch(); + const lastCommonFromFeed = useSelector(selectCommonLayoutLastCommonFromFeed); const updateUserActivity = useCallback( async (data: Partial) => { + if (data.lastVisitedCommon) { + dispatch( + commonLayoutActions.setLastCommonFromFeed({ + id: data.lastVisitedCommon, + data: null, + }), + ); + } if (!userId) { return; } @@ -35,11 +47,14 @@ export const useUserActivity = (userId?: string): Return => { const unsubscribe = UserActivityService.subscribeToUserActivity( userId, (updatedUserActivity) => { - setUserActivityState({ - loading: false, - fetched: true, - data: updatedUserActivity, - }); + if (updatedUserActivity.lastVisitedCommon) { + dispatch( + commonLayoutActions.setLastCommonFromFeed({ + id: updatedUserActivity.lastVisitedCommon, + data: null, + }), + ); + } }, ); @@ -47,7 +62,7 @@ export const useUserActivity = (userId?: string): Return => { }, [userId]); return { - ...userActivityState, + lastVisitedCommon: lastCommonFromFeed?.id, updateUserActivity, }; }; diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx index dec0d59f1a..40d759d824 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx @@ -1,5 +1,5 @@ import React, { CSSProperties, FC, ReactNode } from "react"; -import { useDispatch, useSelector } from "react-redux"; +import { useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; import classNames from "classnames"; import { @@ -13,10 +13,6 @@ import { useModal } from "@/shared/hooks"; import { useUserActivity, useUserCommonIds } from "@/shared/hooks/useCases"; import { Avatar2Icon, Blocks2Icon, InboxIcon } from "@/shared/icons"; import { CreateCommonPrompt } from "@/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components"; -import { - commonLayoutActions, - selectCommonLayoutLastCommonFromFeed, -} from "@/store/states"; import { LayoutTab } from "../../constants"; import { getActiveLayoutTab, getLayoutTabName } from "./utils"; import styles from "./LayoutTabs.module.scss"; @@ -35,13 +31,9 @@ interface TabConfiguration { const LayoutTabs: FC = (props) => { const { className } = props; - const dispatch = useDispatch(); const history = useHistory(); const { getCommonPagePath, getInboxPagePath, getProfilePagePath } = useRoutesContext(); - const lastCommonIdFromFeed = useSelector( - selectCommonLayoutLastCommonFromFeed, - ); const isAuthenticated = useSelector(authentificated()); const userStreamsWithNotificationsAmount = useSelector( selectUserStreamsWithNotificationsAmount(), @@ -49,7 +41,7 @@ const LayoutTabs: FC = (props) => { const user = useSelector(selectUser()); const userId = user?.uid; const { data: userCommonIds } = useUserCommonIds(); - const { data: userActivity } = useUserActivity(userId); + const { lastVisitedCommon } = useUserActivity(userId); const { isShowing: isCreateCommonPromptOpen, onOpen: onCreateCommonPromptOpen, @@ -89,15 +81,7 @@ const LayoutTabs: FC = (props) => { } as CSSProperties; const handleSpacesClick = () => { - if ( - lastCommonIdFromFeed && - lastCommonIdFromFeed.id !== userActivity?.lastVisitedCommon - ) { - dispatch(commonLayoutActions.setLastCommonFromFeed(null)); - } - - const commonForRedirectId = - userActivity?.lastVisitedCommon || userCommonIds[0]; + const commonForRedirectId = lastVisitedCommon || userCommonIds[0]; if (commonForRedirectId) { history.push(getCommonPagePath(commonForRedirectId)); diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx index 0133191d91..962edabb97 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx @@ -40,9 +40,8 @@ const Navigation: FC = (props) => { const user = useSelector(selectUser()); const userId = user?.uid; const { data: userCommonIds } = useUserCommonIds(); - const { data: userActivity } = useUserActivity(userId); - const mySpacesCommonId = - userActivity?.lastVisitedCommon || userCommonIds[0] || ""; + const { lastVisitedCommon } = useUserActivity(userId); + const mySpacesCommonId = lastVisitedCommon || userCommonIds[0] || ""; const mySpacesPagePath = ( mySpacesCommonId ? getCommonPagePath(mySpacesCommonId) : "" ) as ROUTE_PATHS; From e5d45150e7e4dcb49fca2e02b2dd968246f05d75 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 16:58:05 +0400 Subject: [PATCH 19/32] change useUserActivity to useLastVisitedCommon --- src/pages/commonFeed/CommonFeed.tsx | 42 +++++++++---------- src/pages/commonFeed/CommonFeedPage.tsx | 11 +++-- src/shared/hooks/useCases/index.ts | 2 +- ...serActivity.ts => useLastVisitedCommon.ts} | 38 ++++++++--------- .../components/LayoutTabs/LayoutTabs.tsx | 9 ++-- .../components/Navigation/Navigation.tsx | 11 +++-- 6 files changed, 60 insertions(+), 53 deletions(-) rename src/shared/hooks/useCases/{useUserActivity.ts => useLastVisitedCommon.ts} (56%) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 919a4b9030..97bd7b46fa 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, - useUserActivity, + useLastVisitedCommon, useUserCommonIds, } from "@/shared/hooks/useCases"; import { useCommonPinnedFeedItems } from "@/shared/hooks/useCases/useCommonPinnedFeedItems"; @@ -118,7 +118,7 @@ const CommonFeedComponent: FC = (props) => { fetched: isCommonDataFetched, fetchCommonData, } = useCommonData(userId); - const { updateUserActivity } = useUserActivity(userId); + const { updateLastVisitedCommon } = useLastVisitedCommon(userId); const parentCommonId = commonData?.common.directParent?.commonId; const anotherCommonId = userCommonIds[0] === commonId ? userCommonIds[1] : userCommonIds[0]; @@ -419,30 +419,28 @@ const CommonFeedComponent: FC = (props) => { }, [rootCommonMember?.id]); useEffect(() => { - return () => { + const updateLastVisited = () => { const common = stateRef.current?.data?.common; - dispatch( - commonLayoutActions.setLastCommonFromFeed({ - id: commonId, - data: common - ? { - name: common.name, - image: common.image, - isProject: checkIsProject(common), - memberCount: common.memberCount, - } - : null, - }), - ); + updateLastVisitedCommon({ + id: commonId, + data: common + ? { + name: common.name, + image: common.image, + isProject: checkIsProject(common), + memberCount: common.memberCount, + } + : null, + }); }; - }, [commonId]); - useEffect(() => { - updateUserActivity({ - lastVisitedCommon: commonId, - }); - }, [updateUserActivity, commonId]); + updateLastVisited(); + + return () => { + updateLastVisited(); + }; + }, [updateLastVisitedCommon, commonId]); if (!isDataFetched) { const headerEl = renderLoadingHeader ? ( diff --git a/src/pages/commonFeed/CommonFeedPage.tsx b/src/pages/commonFeed/CommonFeedPage.tsx index 233a90a473..256c09382e 100644 --- a/src/pages/commonFeed/CommonFeedPage.tsx +++ b/src/pages/commonFeed/CommonFeedPage.tsx @@ -1,12 +1,13 @@ import React, { FC, useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useParams } from "react-router-dom"; +import { selectUser } from "@/pages/Auth/store/selectors"; import { InboxItemType } from "@/shared/constants"; import { MainRoutesProvider } from "@/shared/contexts"; +import { useLastVisitedCommon } from "@/shared/hooks/useCases"; import { MultipleSpacesLayoutPageContent } from "@/shared/layouts"; import { multipleSpacesLayoutActions, - selectCommonLayoutLastCommonFromFeed, selectMultipleSpacesLayoutMainWidth, } from "@/store/states"; import BaseCommonFeedPage, { @@ -59,7 +60,9 @@ const CommonFeedPage: FC = () => { const { id: commonId } = useParams(); const dispatch = useDispatch(); const layoutMainWidth = useSelector(selectMultipleSpacesLayoutMainWidth); - const lastCommonFromFeed = useSelector(selectCommonLayoutLastCommonFromFeed); + const user = useSelector(selectUser()); + const userId = user?.uid; + const { lastVisitedCommon } = useLastVisitedCommon(userId); const onActiveItemDataChange = useActiveItemDataChange(); const feedLayoutSettings = useMemo( () => ({ @@ -68,13 +71,13 @@ const CommonFeedPage: FC = () => { }), [layoutMainWidth], ); - const lastCommonFromFeedData = lastCommonFromFeed?.data; + const lastCommonFromFeedData = lastVisitedCommon?.data; const renderLoadingHeader = lastCommonFromFeedData ? () => ( ) => void; + lastVisitedCommon: CommonLayoutState["lastCommonFromFeed"]; + updateLastVisitedCommon: ( + data: CommonLayoutState["lastCommonFromFeed"], + ) => void; } -export const useUserActivity = (userId?: string): Return => { +export const useLastVisitedCommon = (userId?: string): Return => { const dispatch = useDispatch(); - const lastCommonFromFeed = useSelector(selectCommonLayoutLastCommonFromFeed); + const lastVisitedCommon = useSelector(selectCommonLayoutLastCommonFromFeed); - const updateUserActivity = useCallback( - async (data: Partial) => { - if (data.lastVisitedCommon) { - dispatch( - commonLayoutActions.setLastCommonFromFeed({ - id: data.lastVisitedCommon, - data: null, - }), - ); - } - if (!userId) { + const updateLastVisitedCommon = useCallback< + Return["updateLastVisitedCommon"] + >( + async (data) => { + dispatch(commonLayoutActions.setLastCommonFromFeed(data)); + + if (!userId || !data?.id) { return; } try { - await UserActivityService.updateUserActivity(userId, data); + await UserActivityService.updateUserActivity(userId, { + lastVisitedCommon: data.id, + }); } catch (error) { Logger.error(error); } @@ -62,7 +62,7 @@ export const useUserActivity = (userId?: string): Return => { }, [userId]); return { - lastVisitedCommon: lastCommonFromFeed?.id, - updateUserActivity, + lastVisitedCommon, + updateLastVisitedCommon, }; }; diff --git a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx index 40d759d824..de37d17d22 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/LayoutTabs/LayoutTabs.tsx @@ -10,7 +10,10 @@ import { import { Tab, Tabs } from "@/shared/components"; import { useRoutesContext } from "@/shared/contexts"; import { useModal } from "@/shared/hooks"; -import { useUserActivity, useUserCommonIds } from "@/shared/hooks/useCases"; +import { + useLastVisitedCommon, + useUserCommonIds, +} from "@/shared/hooks/useCases"; import { Avatar2Icon, Blocks2Icon, InboxIcon } from "@/shared/icons"; import { CreateCommonPrompt } from "@/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/components"; import { LayoutTab } from "../../constants"; @@ -41,7 +44,7 @@ const LayoutTabs: FC = (props) => { const user = useSelector(selectUser()); const userId = user?.uid; const { data: userCommonIds } = useUserCommonIds(); - const { lastVisitedCommon } = useUserActivity(userId); + const { lastVisitedCommon } = useLastVisitedCommon(userId); const { isShowing: isCreateCommonPromptOpen, onOpen: onCreateCommonPromptOpen, @@ -81,7 +84,7 @@ const LayoutTabs: FC = (props) => { } as CSSProperties; const handleSpacesClick = () => { - const commonForRedirectId = lastVisitedCommon || userCommonIds[0]; + const commonForRedirectId = lastVisitedCommon?.id || userCommonIds[0]; if (commonForRedirectId) { history.push(getCommonPagePath(commonForRedirectId)); diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx index 962edabb97..ed1a2b51e0 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Navigation/Navigation.tsx @@ -7,10 +7,13 @@ import { selectUser, selectUserStreamsWithNotificationsAmount, } from "@/pages/Auth/store/selectors"; -import { InboxItemType, ROUTE_PATHS } from "@/shared/constants"; +import { ROUTE_PATHS } from "@/shared/constants"; import { useRoutesContext } from "@/shared/contexts"; import { useModal } from "@/shared/hooks"; -import { useUserActivity, useUserCommonIds } from "@/shared/hooks/useCases"; +import { + useLastVisitedCommon, + useUserCommonIds, +} from "@/shared/hooks/useCases"; import { BlocksIcon, InboxIcon } from "@/shared/icons"; import { CommonSidenavLayoutTab, @@ -40,8 +43,8 @@ const Navigation: FC = (props) => { const user = useSelector(selectUser()); const userId = user?.uid; const { data: userCommonIds } = useUserCommonIds(); - const { lastVisitedCommon } = useUserActivity(userId); - const mySpacesCommonId = lastVisitedCommon || userCommonIds[0] || ""; + const { lastVisitedCommon } = useLastVisitedCommon(userId); + const mySpacesCommonId = lastVisitedCommon?.id || userCommonIds[0] || ""; const mySpacesPagePath = ( mySpacesCommonId ? getCommonPagePath(mySpacesCommonId) : "" ) as ROUTE_PATHS; From bd9b723cd21f7187965a173f18a01a7df8d1c1f0 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 19:08:48 +0400 Subject: [PATCH 20/32] fix apple and google icons props cases --- src/shared/icons/auth/apple.icon.tsx | 4 ++-- src/shared/icons/auth/google.icon.tsx | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/shared/icons/auth/apple.icon.tsx b/src/shared/icons/auth/apple.icon.tsx index fff3aeb538..1344f4b37e 100644 --- a/src/shared/icons/auth/apple.icon.tsx +++ b/src/shared/icons/auth/apple.icon.tsx @@ -12,8 +12,8 @@ const AppleIcon: FC = () => { xmlns="http://www.w3.org/2000/svg" > diff --git a/src/shared/icons/auth/google.icon.tsx b/src/shared/icons/auth/google.icon.tsx index 2fd35cb30c..33719d667c 100644 --- a/src/shared/icons/auth/google.icon.tsx +++ b/src/shared/icons/auth/google.icon.tsx @@ -12,26 +12,26 @@ const GoogleIcon: FC = () => { xmlns="http://www.w3.org/2000/svg" > From 07ae59378fe569452a40497fc21d83ae7914da37 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 20:26:46 +0400 Subject: [PATCH 21/32] use cached commons in the breadcrumbs commons fetch --- .../saga/fetchBreadcrumbsItemsByCommonId.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/store/states/multipleSpacesLayout/saga/fetchBreadcrumbsItemsByCommonId.ts b/src/store/states/multipleSpacesLayout/saga/fetchBreadcrumbsItemsByCommonId.ts index c7e27de108..b99dbf8220 100644 --- a/src/store/states/multipleSpacesLayout/saga/fetchBreadcrumbsItemsByCommonId.ts +++ b/src/store/states/multipleSpacesLayout/saga/fetchBreadcrumbsItemsByCommonId.ts @@ -9,7 +9,7 @@ import { MultipleSpacesLayoutState, ProjectsStateItem } from "../types"; const fetchProjectsInfoByActiveCommonId = async ( commonId: string, ): Promise => { - const activeCommon = await CommonService.getCommonById(commonId); + const activeCommon = await CommonService.getCommonById(commonId, true); if (!activeCommon) { return []; @@ -17,6 +17,7 @@ const fetchProjectsInfoByActiveCommonId = async ( const commons = await CommonService.getAllParentCommonsForCommon( activeCommon, + true, ); return [...commons, activeCommon].map((common) => ({ From b8814e96d75cfadd60315d9ef277fd29ccc2cbed Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Wed, 29 Nov 2023 20:27:37 +0400 Subject: [PATCH 22/32] add subscription to breadcrumbs related commons --- .../FeedItemBreadcrumbs.tsx | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx index dd86ccc171..eac4ae6a42 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx @@ -1,5 +1,7 @@ -import React, { FC } from "react"; +import React, { FC, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { CommonEvent, CommonEventEmitter } from "@/events"; +import { CommonService } from "@/services"; import { commonLayoutActions, MultipleSpacesLayoutFeedItemBreadcrumbs, @@ -34,6 +36,28 @@ const FeedItemBreadcrumbs: FC = (props) => { } }; + useEffect(() => { + const commonIds = breadcrumbs.items.map((item) => item.commonId); + + if (commonIds.length === 0) { + return; + } + + const unsubscribe = CommonService.subscribeToCommons(commonIds, (data) => { + data.forEach(({ common }) => { + CommonEventEmitter.emit(CommonEvent.ProjectUpdated, { + commonId: common.id, + image: common.image, + name: common.name, + directParent: common.directParent, + rootCommonId: common.rootCommonId, + }); + }); + }); + + return unsubscribe; + }, [breadcrumbs.activeItem?.id]); + return (
    {breadcrumbs.areItemsLoading && } From 3c923d1238e4c6939e4eacfeada9895de6c62ce5 Mon Sep 17 00:00:00 2001 From: Roie Natan Date: Wed, 29 Nov 2023 20:28:46 -0500 Subject: [PATCH 23/32] clicking on a user mention now opens the chat panel with the info instead of the profile popup ; eliminated unnecessary UserInfoPopup components calls --- .../ChatComponent/ChatComponent.tsx | 1 + .../hooks/useDiscussionChatAdapter.ts | 10 ++++-- src/pages/commonFeed/CommonFeed.tsx | 1 - .../Chat/ChatMessage/ChatMessage.tsx | 20 +++-------- .../Chat/ChatMessage/DMChatMessage.tsx | 19 ++-------- .../components/UserMention/UserMention.tsx | 35 +++---------------- .../utils/getTextFromSystemMessage.tsx | 2 -- .../utils/getTextFromTextEditorString.tsx | 2 -- 8 files changed, 20 insertions(+), 70 deletions(-) diff --git a/src/pages/common/components/ChatComponent/ChatComponent.tsx b/src/pages/common/components/ChatComponent/ChatComponent.tsx index 3903aa4e78..a8c8677679 100644 --- a/src/pages/common/components/ChatComponent/ChatComponent.tsx +++ b/src/pages/common/components/ChatComponent/ChatComponent.tsx @@ -175,6 +175,7 @@ export default function ChatComponent({ multipleEmojiText: styles.multipleEmojiText, }, onFeedItemClick, + onUserClick, }); const { chatMessagesData, diff --git a/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts b/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts index 77c2fcf37f..2834d66445 100644 --- a/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts +++ b/src/pages/common/components/ChatComponent/hooks/useDiscussionChatAdapter.ts @@ -28,8 +28,13 @@ interface Return { } export const useDiscussionChatAdapter = (options: Options): Return => { - const { hasPermissionToHide, textStyles, discussionId, onFeedItemClick } = - options; + const { + hasPermissionToHide, + textStyles, + discussionId, + onFeedItemClick, + onUserClick, + } = options; const user = useSelector(selectUser()); const userId = user?.uid; const { data: commonMembers, fetchCommonMembers } = useCommonMembers(); @@ -46,6 +51,7 @@ export const useDiscussionChatAdapter = (options: Options): Return => { users, textStyles, onFeedItemClick, + onUserClick, }); const { markFeedItemAsSeen } = useMarkFeedItemAsSeen(); diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 97bd7b46fa..f2a1354851 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -51,7 +51,6 @@ import { import { cacheActions, commonActions, - commonLayoutActions, selectCommonAction, selectRecentStreamId, selectSharedFeedItem, diff --git a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx index e848e04513..6f8c91e61e 100644 --- a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx @@ -8,11 +8,7 @@ import React, { import classNames from "classnames"; import { useLongPress } from "use-long-press"; import { DiscussionMessageService } from "@/services"; -import { - ElementDropdown, - UserAvatar, - UserInfoPopup, -} from "@/shared/components"; +import { ElementDropdown, UserAvatar } from "@/shared/components"; import { Orientation, ChatType, @@ -20,7 +16,7 @@ import { QueryParamKey, } from "@/shared/constants"; import { Colors } from "@/shared/constants"; -import { useModal } from "@/shared/hooks"; +//import { useModal } from "@/shared/hooks"; import { useIsTabletView } from "@/shared/hooks/viewport"; import { ModerationFlags } from "@/shared/interfaces/Moderation"; import { @@ -146,17 +142,9 @@ export default function ChatMessage({ (discussionMessage.editedAt?.seconds ?? 0) * 1000, ); - const { - isShowing: isShowingUserProfile, - onClose: onCloseUserProfile, - onOpen: onOpenUserProfile, - } = useModal(false); - const handleUserClick = () => { if (onUserClick && discussionMessageUserId) { onUserClick(discussionMessageUserId); - } else { - onOpenUserProfile(); } }; @@ -414,7 +402,7 @@ export default function ChatMessage({ )} - {isShowingUserProfile && isUserDiscussionMessage && ( + {/* {isShowingUserProfile && isUserDiscussionMessage && ( - )} + )} */} ); } diff --git a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx index 42b3a3add8..88f056203e 100644 --- a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx @@ -8,11 +8,7 @@ import React, { import classNames from "classnames"; import { useLongPress } from "use-long-press"; import { Logger } from "@/services"; -import { - ElementDropdown, - UserAvatar, - UserInfoPopup, -} from "@/shared/components"; +import { ElementDropdown, UserAvatar } from "@/shared/components"; import { Orientation, ChatType, @@ -21,7 +17,6 @@ import { } from "@/shared/constants"; import { Colors } from "@/shared/constants"; import { useRoutesContext } from "@/shared/contexts"; -import { useModal } from "@/shared/hooks"; import { useIsTabletView } from "@/shared/hooks/viewport"; import { ModerationFlags } from "@/shared/interfaces/Moderation"; import { @@ -122,17 +117,9 @@ export default function DMChatMessage({ (discussionMessage.editedAt?.seconds ?? 0) * 1000, ); - const { - isShowing: isShowingUserProfile, - onClose: onCloseUserProfile, - onOpen: onOpenUserProfile, - } = useModal(false); - const handleUserClick = () => { if (onUserClick && discussionMessageUserId) { onUserClick(discussionMessageUserId); - } else { - onOpenUserProfile(); } }; @@ -472,7 +459,7 @@ export default function DMChatMessage({ )} - {isShowingUserProfile && isUserDiscussionMessage && ( + {/* {isShowingUserProfile && isUserDiscussionMessage && ( - )} + )} */} ); } diff --git a/src/shared/components/Chat/ChatMessage/components/UserMention/UserMention.tsx b/src/shared/components/Chat/ChatMessage/components/UserMention/UserMention.tsx index 6246aea93d..a0d6861502 100644 --- a/src/shared/components/Chat/ChatMessage/components/UserMention/UserMention.tsx +++ b/src/shared/components/Chat/ChatMessage/components/UserMention/UserMention.tsx @@ -1,8 +1,6 @@ import React, { FC } from "react"; import classNames from "classnames"; -import { UserInfoPopup } from "@/shared/components"; -import { useModal } from "@/shared/hooks"; -import { DirectParent, User } from "@/shared/models"; +import { User } from "@/shared/models"; import { getUserName } from "@/shared/utils"; import styles from "../../ChatMessage.module.scss"; @@ -11,26 +9,13 @@ interface UserMentionProps { userId: string; displayName: string; mentionTextClassName?: string; - commonId?: string; - directParent?: DirectParent | null; onUserClick?: (userId: string) => void; } const UserMention: FC = (props) => { - const { - users, - userId, - displayName, - mentionTextClassName, - commonId, - directParent, - onUserClick, - } = props; - const { - isShowing: isShowingUserProfile, - onClose: onCloseUserProfile, - onOpen: onOpenUserProfile, - } = useModal(false); + const { users, userId, displayName, mentionTextClassName, onUserClick } = + props; + const user = users.find(({ uid }) => uid === userId); const withSpace = displayName[displayName.length - 1] === " "; const userName = user @@ -40,8 +25,6 @@ const UserMention: FC = (props) => { const handleUserNameClick = () => { if (onUserClick) { onUserClick(userId); - } else { - onOpenUserProfile(); } }; @@ -53,16 +36,6 @@ const UserMention: FC = (props) => { > @{userName} - {!onUserClick && ( - - )} ); }; diff --git a/src/shared/components/Chat/ChatMessage/utils/getTextFromSystemMessage.tsx b/src/shared/components/Chat/ChatMessage/utils/getTextFromSystemMessage.tsx index 7267d13c75..8a8005b47d 100644 --- a/src/shared/components/Chat/ChatMessage/utils/getTextFromSystemMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/utils/getTextFromSystemMessage.tsx @@ -42,8 +42,6 @@ const renderUserMention = ( userId={user.uid} displayName={getUserName(user)} mentionTextClassName={data.mentionTextClassName} - commonId={data.commonId} - directParent={data.directParent} onUserClick={data.onUserClick} /> ) : ( diff --git a/src/shared/components/Chat/ChatMessage/utils/getTextFromTextEditorString.tsx b/src/shared/components/Chat/ChatMessage/utils/getTextFromTextEditorString.tsx index 622c859ca0..9d063f6719 100644 --- a/src/shared/components/Chat/ChatMessage/utils/getTextFromTextEditorString.tsx +++ b/src/shared/components/Chat/ChatMessage/utils/getTextFromTextEditorString.tsx @@ -72,8 +72,6 @@ const getTextFromDescendant = ({ userId={descendant.userId} displayName={descendant.displayName} mentionTextClassName={mentionTextClassName} - commonId={commonId} - directParent={directParent} onUserClick={onUserClick} /> ); From d44d1e2bf4643784d89243365f54070b20af1093 Mon Sep 17 00:00:00 2001 From: Roie Natan Date: Wed, 29 Nov 2023 20:32:32 -0500 Subject: [PATCH 24/32] clean unnecessary comments --- .../components/Chat/ChatMessage/ChatMessage.tsx | 11 ----------- .../components/Chat/ChatMessage/DMChatMessage.tsx | 10 ---------- 2 files changed, 21 deletions(-) diff --git a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx index 6f8c91e61e..c73d6bc80e 100644 --- a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx @@ -16,7 +16,6 @@ import { QueryParamKey, } from "@/shared/constants"; import { Colors } from "@/shared/constants"; -//import { useModal } from "@/shared/hooks"; import { useIsTabletView } from "@/shared/hooks/viewport"; import { ModerationFlags } from "@/shared/interfaces/Moderation"; import { @@ -402,16 +401,6 @@ export default function ChatMessage({ )} - {/* {isShowingUserProfile && isUserDiscussionMessage && ( - - )} */} ); } diff --git a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx index 88f056203e..b8ca5ec15f 100644 --- a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx @@ -459,16 +459,6 @@ export default function DMChatMessage({ )} - {/* {isShowingUserProfile && isUserDiscussionMessage && ( - - )} */} ); } From e4f200b6a67ef596415df62ba5d6d1a8e992e289 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 30 Nov 2023 11:59:32 +0400 Subject: [PATCH 25/32] filter out non-discussion/proposal items in inbox fetches --- src/shared/hooks/useCases/useInboxItems.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/shared/hooks/useCases/useInboxItems.ts b/src/shared/hooks/useCases/useInboxItems.ts index 5966ae3f4d..e52ed2c95a 100644 --- a/src/shared/hooks/useCases/useInboxItems.ts +++ b/src/shared/hooks/useCases/useInboxItems.ts @@ -13,6 +13,7 @@ import { useIsMounted } from "@/shared/hooks"; import { FeedLayoutItemWithFollowData } from "@/shared/interfaces"; import { ChatChannel, + CommonFeedType, FeedItemFollow, FeedItemFollowWithMetadata, } from "@/shared/models"; @@ -189,13 +190,16 @@ export const useInboxItems = ( return; } + const filteredData = data.filter(({ item }) => + [CommonFeedType.Discussion, CommonFeedType.Proposal].includes(item.type), + ); const finalData = feedItemIdsForNotListening && feedItemIdsForNotListening.length > 0 - ? data.filter( + ? filteredData.filter( (item) => !feedItemIdsForNotListening.includes(item.item.feedItemId), ) - : data; + : filteredData; setNewItemsBatches((currentItems) => [...currentItems, finalData]); }; From ca72b4ff41b5f9fb7d79399c709346d40da0352e Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 30 Nov 2023 12:36:24 +0400 Subject: [PATCH 26/32] remove loader from the breadcrumbs --- .../components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx index eac4ae6a42..f86df49344 100644 --- a/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx +++ b/src/shared/layouts/MultipleSpacesLayout/components/Header/components/Breadcrumbs/components/FeedItemBreadcrumbs/FeedItemBreadcrumbs.tsx @@ -9,7 +9,6 @@ import { selectCommonLayoutCommonId, } from "@/store/states"; import { useGoToCreateCommon } from "../../../../../../hooks"; -import { LoadingBreadcrumbsItem } from "../LoadingBreadcrumbsItem"; import { Separator } from "../Separator"; import { ActiveFeedBreadcrumbsItem, FeedBreadcrumbsItem } from "./components"; import styles from "./FeedItemBreadcrumbs.module.scss"; @@ -60,7 +59,6 @@ const FeedItemBreadcrumbs: FC = (props) => { return (
      - {breadcrumbs.areItemsLoading && } {!breadcrumbs.areItemsLoading && breadcrumbs.items.map((item, index) => ( @@ -75,7 +73,7 @@ const FeedItemBreadcrumbs: FC = (props) => { ))} {breadcrumbs.activeItem && ( <> - {(breadcrumbs.areItemsLoading || breadcrumbs.items.length > 0) && ( + {!breadcrumbs.areItemsLoading && breadcrumbs.items.length > 0 && ( )} Date: Thu, 30 Nov 2023 14:02:38 +0400 Subject: [PATCH 27/32] fix breadcrumbs when switching to parent common item --- .../saga/configureBreadcrumbsData.ts | 53 +++++++++++++++---- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/src/store/states/multipleSpacesLayout/saga/configureBreadcrumbsData.ts b/src/store/states/multipleSpacesLayout/saga/configureBreadcrumbsData.ts index 9a189226e1..c877af75e2 100644 --- a/src/store/states/multipleSpacesLayout/saga/configureBreadcrumbsData.ts +++ b/src/store/states/multipleSpacesLayout/saga/configureBreadcrumbsData.ts @@ -1,6 +1,7 @@ import { put, select } from "redux-saga/effects"; import { InboxItemType } from "@/shared/constants"; import { + MultipleSpacesLayoutFeedItemBreadcrumbs, selectCommonLayoutCommonsState, selectCommonLayoutProjectsState, } from "@/store/states"; @@ -39,6 +40,42 @@ const getItemsByExistingData = ( return items; }; +const getNextBreadcrumbsData = ( + items: ProjectsStateItem[] | null, + currentBreadcrumbs: MultipleSpacesLayoutFeedItemBreadcrumbs | null, + activeCommonId: string, +): Pick< + MultipleSpacesLayoutFeedItemBreadcrumbs, + "items" | "areItemsLoading" | "areItemsFetched" +> => { + if (items) { + return { + items, + areItemsLoading: false, + areItemsFetched: true, + }; + } + if (!currentBreadcrumbs) { + return { + items: [], + areItemsLoading: true, + areItemsFetched: false, + }; + } + + const activeItemIndex = currentBreadcrumbs.items.findIndex( + (item) => item.commonId === activeCommonId, + ); + + return { + ...currentBreadcrumbs, + items: + activeItemIndex > -1 + ? currentBreadcrumbs.items.slice(0, activeItemIndex + 1) + : currentBreadcrumbs.items, + }; +}; + export function* configureBreadcrumbsData( action: ReturnType, ) { @@ -88,17 +125,11 @@ export function* configureBreadcrumbsData( yield put( actions.setBreadcrumbsData({ - ...(items - ? { - items, - areItemsLoading: false, - areItemsFetched: true, - } - : currentBreadcrumbs || { - items: [], - areItemsLoading: true, - areItemsFetched: false, - }), + ...getNextBreadcrumbsData( + items, + currentBreadcrumbs, + payload.activeCommonId, + ), type: InboxItemType.FeedItemFollow, activeItem: payload.activeItem ? { ...payload.activeItem } : null, activeCommonId: payload.activeCommonId, From f734e67b4f29deeae4f0531f92f5faf4f04486ea Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 30 Nov 2023 14:27:38 +0400 Subject: [PATCH 28/32] remove chat store from caching --- src/store/store.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/store/store.tsx b/src/store/store.tsx index e82f8979ad..b56c38a915 100644 --- a/src/store/store.tsx +++ b/src/store/store.tsx @@ -32,7 +32,6 @@ const persistConfig: PersistConfig = { "commonLayout", "commonFeedFollows", "cache", - "chat", "inbox", "multipleSpacesLayout", ], From cafb91ce3e60b7ca3fe54dd051eafbec5dc64a0b Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 30 Nov 2023 15:45:48 +0400 Subject: [PATCH 29/32] clear non-finished user states before caching --- src/store/transforms.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/store/transforms.ts b/src/store/transforms.ts index 0bde9e2597..479866f1c3 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -1,5 +1,8 @@ import { createTransform } from "redux-persist"; -import { deserializeFeedLayoutItemWithFollowData } from "@/shared/interfaces"; +import { + deserializeFeedLayoutItemWithFollowData, + LoadingState, +} from "@/shared/interfaces"; import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; import { MultipleSpacesLayoutState } from "@/store/states"; import { getFeedLayoutItemDateForSorting } from "@/store/states/inbox/utils"; @@ -11,6 +14,20 @@ import { INITIAL_INBOX_STATE, } from "./states/inbox"; +const clearNonFinishedStates = ( + states: Record>, +): Record> => + Object.entries(states).reduce((acc, [key, value]) => { + if (value.loading || !value.fetched) { + return acc; + } + + return { + ...acc, + [key]: value, + }; + }, {}); + export const inboxTransform = createTransform( (inboundState: InboxState) => { if (inboundState.items.unread) { @@ -68,6 +85,7 @@ export const cacheTransform = createTransform( (inboundState: CacheState) => inboundState, (outboundState: CacheState) => ({ ...outboundState, + userStates: clearNonFinishedStates(outboundState.userStates), discussionMessagesStates: {}, }), { whitelist: ["cache"] }, From c7556b04800cc776c7129890dd438166ac27e9b0 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 30 Nov 2023 15:56:28 +0400 Subject: [PATCH 30/32] leave only user states in the cache --- src/store/states/cache/index.ts | 2 +- src/store/states/cache/reducer.tsx | 10 ++++------ src/store/transforms.ts | 11 +++++------ 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/src/store/states/cache/index.ts b/src/store/states/cache/index.ts index 2a0fca605d..ea30e2f27e 100644 --- a/src/store/states/cache/index.ts +++ b/src/store/states/cache/index.ts @@ -1,5 +1,5 @@ export * as cacheActions from "./actions"; -export { reducer as cacheReducer } from "./reducer"; +export { reducer as cacheReducer, INITIAL_CACHE_STATE } from "./reducer"; export { mainSaga as cacheSaga } from "./saga"; export * from "./selectors"; export * from "./types"; diff --git a/src/store/states/cache/reducer.tsx b/src/store/states/cache/reducer.tsx index d30e54de54..50874f2638 100644 --- a/src/store/states/cache/reducer.tsx +++ b/src/store/states/cache/reducer.tsx @@ -8,7 +8,7 @@ import { CacheState } from "./types"; type Action = ActionType; -const initialState: CacheState = { +export const INITIAL_CACHE_STATE: CacheState = { userStates: {}, governanceByCommonIdStates: {}, discussionStates: {}, @@ -19,7 +19,7 @@ const initialState: CacheState = { chatChannelUserStatusStates: {}, }; -export const reducer = createReducer(initialState) +export const reducer = createReducer(INITIAL_CACHE_STATE) .handleAction(actions.updateUserStateById, (state, { payload }) => produce(state, (nextState) => { const { userId, state } = payload; @@ -115,8 +115,7 @@ export const reducer = createReducer(initialState) const uniq = unionBy( payload.state?.data ?? [], - state.discussionMessagesStates[discussionId]?.data ?? - [], + state.discussionMessagesStates[discussionId]?.data ?? [], "id", ).sort( (a, b) => @@ -136,8 +135,7 @@ export const reducer = createReducer(initialState) const { discussionId, discussionMessage } = payload; const updatedDiscussionMessages = [ - ...(state.discussionMessagesStates[discussionId] - ?.data ?? []), + ...(state.discussionMessagesStates[discussionId]?.data ?? []), discussionMessage, ]; diff --git a/src/store/transforms.ts b/src/store/transforms.ts index 479866f1c3..ff67584549 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -6,7 +6,7 @@ import { import { convertObjectDatesToFirestoreTimestamps } from "@/shared/utils"; import { MultipleSpacesLayoutState } from "@/store/states"; import { getFeedLayoutItemDateForSorting } from "@/store/states/inbox/utils"; -import { CacheState } from "./states/cache"; +import { CacheState, INITIAL_CACHE_STATE } from "./states/cache"; import { InboxItems, InboxState, @@ -82,12 +82,11 @@ export const inboxTransform = createTransform( ); export const cacheTransform = createTransform( - (inboundState: CacheState) => inboundState, - (outboundState: CacheState) => ({ - ...outboundState, - userStates: clearNonFinishedStates(outboundState.userStates), - discussionMessagesStates: {}, + (inboundState: CacheState) => ({ + ...INITIAL_CACHE_STATE, + userStates: clearNonFinishedStates(inboundState.userStates), }), + (outboundState: CacheState) => outboundState, { whitelist: ["cache"] }, ); From 6c7b73e7aa65a3b250144fbf59a4053e9c9d02e7 Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 30 Nov 2023 16:38:56 +0400 Subject: [PATCH 31/32] add changes to cached user update to load it on error --- src/services/User.ts | 60 +++++++++++++++++++++++++++++++------------- 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/src/services/User.ts b/src/services/User.ts index 4e30a21f4b..18c7664f31 100644 --- a/src/services/User.ts +++ b/src/services/User.ts @@ -15,7 +15,6 @@ import { convertObjectDatesToFirestoreTimestamps, convertToTimestamp, firestoreDataConverter, - transformFirebaseDataList, } from "@/shared/utils"; import firebase from "@/shared/utils/firebase"; import * as cacheActions from "@/store/states/cache/actions"; @@ -51,31 +50,56 @@ class UserService { }; }; - public getUserById = async (userId: string): Promise => { - const userSnapshot = await this.getUsersCollection() + public getUserById = async ( + userId: string, + cached = false, + ): Promise => { + const snapshot = await this.getUsersCollection() .where("uid", "==", userId) - .get(); + .get({ source: cached ? "cache" : "default" }); + const users = snapshot.docs.map((doc) => doc.data()); + const user = users[0] || null; + + if (cached && !user) { + return this.getUserById(userId); + } - return transformFirebaseDataList(userSnapshot)[0] || null; + return user; }; public getCachedUserById = async (userId: string): Promise => { - const userState = store.getState().cache.userStates[userId]; + try { + const userState = store.getState().cache.userStates[userId]; - if (userState?.fetched) { - return userState.data; - } - if (userState?.loading) { - return await waitForUserToBeLoaded(userId); - } + if (userState?.fetched) { + return userState.data; + } + if (userState?.loading) { + return await waitForUserToBeLoaded(userId); + } - store.dispatch( - cacheActions.getUserStateById.request({ - payload: { userId }, - }), - ); + store.dispatch( + cacheActions.getUserStateById.request({ + payload: { userId }, + }), + ); + + return await waitForUserToBeLoaded(userId); + } catch (err) { + const user = await this.getUserById(userId, true); + store.dispatch( + cacheActions.updateUserStateById({ + userId, + state: { + loading: false, + fetched: true, + data: user, + }, + }), + ); - return await waitForUserToBeLoaded(userId); + return user; + } }; public getCachedUsersById = async (userIds: string[]): Promise => From f1940a1bf34ebb870221ee126890b093e83d8aad Mon Sep 17 00:00:00 2001 From: Andrey Mikhadyuk Date: Thu, 30 Nov 2023 16:44:01 +0400 Subject: [PATCH 32/32] return caching of feedByCommonIdStates --- src/store/transforms.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/store/transforms.ts b/src/store/transforms.ts index ff67584549..b8622cbe89 100644 --- a/src/store/transforms.ts +++ b/src/store/transforms.ts @@ -85,6 +85,7 @@ export const cacheTransform = createTransform( (inboundState: CacheState) => ({ ...INITIAL_CACHE_STATE, userStates: clearNonFinishedStates(inboundState.userStates), + feedByCommonIdStates: inboundState.feedByCommonIdStates, }), (outboundState: CacheState) => outboundState, { whitelist: ["cache"] },