From a6577d8f0f6a28c6fe9fac4ccdf39340ac02ba1a Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 1 Aug 2024 19:08:35 +0300 Subject: [PATCH 01/28] CW-some-stream-disappear Optimized FeedItem component. FIxed condition in FeedLayout --- .../common/components/FeedItem/FeedItem.tsx | 114 ++++++++++++------ .../components/FeedLayout/FeedLayout.tsx | 7 +- 2 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/pages/common/components/FeedItem/FeedItem.tsx b/src/pages/common/components/FeedItem/FeedItem.tsx index 366753b49..0c77addd8 100644 --- a/src/pages/common/components/FeedItem/FeedItem.tsx +++ b/src/pages/common/components/FeedItem/FeedItem.tsx @@ -6,10 +6,7 @@ import React, { useMemo, } from "react"; import { useFeedItemFollow } from "@/shared/hooks/useCases"; -import { - FeedLayoutItemChangeData, - SpaceListVisibility, -} from "@/shared/interfaces"; +import { FeedLayoutItemChangeData } from "@/shared/interfaces"; import { Circles, CirclesPermissions, @@ -20,7 +17,7 @@ import { CommonNotion, DirectParent, } from "@/shared/models"; -import { checkIsItemVisibleForUser, InternalLinkData } from "@/shared/utils"; +import { checkIsItemVisibleForUser } from "@/shared/utils"; import { useFeedItemSubscription } from "../../hooks"; import { DiscussionFeedCard } from "../DiscussionFeedCard"; import { ProposalFeedCard } from "../ProposalFeedCard"; @@ -113,14 +110,20 @@ const FeedItem = forwardRef((props, ref) => { const getNonAllowedItems = outerGetNonAllowedItems || contextGetNonAllowedItems; - const handleUserClick = useMemo( - () => onUserSelect && ((userId: string) => onUserSelect(userId, commonId)), + const handleUserClick = useCallback( + (userId: string) => { + if (onUserSelect) { + onUserSelect(userId, commonId); + } + }, [onUserSelect, commonId], ); const handleActiveItemDataChange = useCallback( (data: FeedLayoutItemChangeData) => { - onActiveItemDataChange?.(data, commonId); + if (onActiveItemDataChange) { + onActiveItemDataChange(data, commonId); + } }, [onActiveItemDataChange, commonId], ); @@ -130,11 +133,15 @@ const FeedItem = forwardRef((props, ref) => { feedItemFollow.isUserFeedItemFollowDataFetched && !feedItemFollow.userFeedItemFollowData ) { - onFeedItemUnfollowed?.(item.id); + if (onFeedItemUnfollowed) { + onFeedItemUnfollowed(item.id); + } } }, [ feedItemFollow.isUserFeedItemFollowDataFetched, feedItemFollow.userFeedItemFollowData, + item.id, + onFeedItemUnfollowed, ]); if ( @@ -150,35 +157,66 @@ const FeedItem = forwardRef((props, ref) => { return null; } - const generalProps = { - ref, - item, - commonId, - commonName, - commonImage, - commonNotion, - pinnedFeedItems, - isActive, - isExpanded, - isProject, - isPinned, - governanceCircles, - isPreviewMode, - getLastMessage, - commonMember, - getNonAllowedItems, - isMobileVersion, - onActiveItemDataChange: handleActiveItemDataChange, - directParent, - rootCommonId, - feedItemFollow, - onUserSelect, - shouldPreLoadMessages, - withoutMenu, - onUserClick: handleUserClick, - onFeedItemClick, - onInternalLinkClick, - }; + const generalProps = useMemo( + () => ({ + ref, + item, + commonId, + commonName, + commonImage, + commonNotion, + pinnedFeedItems, + isActive, + isExpanded, + isProject, + isPinned, + governanceCircles, + isPreviewMode, + getLastMessage, + commonMember, + getNonAllowedItems, + isMobileVersion, + onActiveItemDataChange: handleActiveItemDataChange, + directParent, + rootCommonId, + feedItemFollow, + onUserSelect, + shouldPreLoadMessages, + withoutMenu, + onUserClick: handleUserClick, + onFeedItemClick, + onInternalLinkClick, + }), + [ + ref, + item, + commonId, + commonName, + commonImage, + commonNotion, + pinnedFeedItems, + isActive, + isExpanded, + isProject, + isPinned, + governanceCircles, + isPreviewMode, + getLastMessage, + commonMember, + getNonAllowedItems, + isMobileVersion, + handleActiveItemDataChange, + directParent, + rootCommonId, + feedItemFollow, + onUserSelect, + shouldPreLoadMessages, + withoutMenu, + handleUserClick, + onFeedItemClick, + onInternalLinkClick, + ], + ); if (item.data.type === CommonFeedType.Discussion) { return ; diff --git a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx index 0f730acc8..c349b456a 100644 --- a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx +++ b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx @@ -1,14 +1,14 @@ import React, { CSSProperties, forwardRef, - ForwardRefRenderFunction, - ReactNode, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, + ReactNode, + ForwardRefRenderFunction, } from "react"; import { useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; @@ -221,8 +221,9 @@ const FeedLayout: ForwardRefRenderFunction = ( fetchedCommonMember; const userForProfile = useUserForProfile(); const governance = chatItem?.nestedItemData - ? fetchedGovernance + ? fetchedGovernance || outerGovernance : outerGovernance || fetchedGovernance; + const [splitPaneRef, setSplitPaneRef] = useState(null); const maxContentSize = settings?.getSplitViewMaxSize?.(windowWidth) ?? From 0e0bde068773e2693da6606b4e07e5e8c383bfdc Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 5 Aug 2024 15:37:52 +0300 Subject: [PATCH 02/28] CW-some-stream-disappear Fixed Render fewer hooks issue --- .../common/components/FeedItem/FeedItem.tsx | 26 ++++++++++--------- .../components/FeedLayout/FeedLayout.tsx | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/pages/common/components/FeedItem/FeedItem.tsx b/src/pages/common/components/FeedItem/FeedItem.tsx index 0c77addd8..56b8afca4 100644 --- a/src/pages/common/components/FeedItem/FeedItem.tsx +++ b/src/pages/common/components/FeedItem/FeedItem.tsx @@ -144,18 +144,6 @@ const FeedItem = forwardRef((props, ref) => { onFeedItemUnfollowed, ]); - if ( - shouldCheckItemVisibility && - !checkIsItemVisibleForUser({ - itemCircleVisibility: item.circleVisibility, - userCircleIds, - itemUserId: item.userId, - currentUserId, - itemDataType: item.data.type, - }) - ) { - return null; - } const generalProps = useMemo( () => ({ @@ -218,6 +206,20 @@ const FeedItem = forwardRef((props, ref) => { ], ); + + if ( + shouldCheckItemVisibility && + !checkIsItemVisibleForUser({ + itemCircleVisibility: item.circleVisibility, + userCircleIds, + itemUserId: item.userId, + currentUserId, + itemDataType: item.data.type, + }) + ) { + return null; + } + if (item.data.type === CommonFeedType.Discussion) { return ; } diff --git a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx index c349b456a..c57d97461 100644 --- a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx +++ b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx @@ -221,7 +221,7 @@ const FeedLayout: ForwardRefRenderFunction = ( fetchedCommonMember; const userForProfile = useUserForProfile(); const governance = chatItem?.nestedItemData - ? fetchedGovernance || outerGovernance + ? (fetchedGovernance || outerGovernance) : outerGovernance || fetchedGovernance; const [splitPaneRef, setSplitPaneRef] = useState(null); From c14a3be245e166ba5e18d3aae3f5ed629f77c442 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Tue, 6 Aug 2024 13:03:43 +0300 Subject: [PATCH 03/28] CW-some-stream-disappear Added names to components for better debugging --- .../DiscussionFeedCard/DiscussionFeedCard.tsx | 762 +++++++++--------- .../common/components/FeedCard/FeedCard.tsx | 6 +- src/pages/common/components/FeedCard/index.ts | 2 +- .../ProposalFeedCard/ProposalFeedCard.tsx | 740 +++++++++-------- 4 files changed, 754 insertions(+), 756 deletions(-) diff --git a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx index 3da4b2a80..06a09cc7f 100644 --- a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx +++ b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx @@ -78,409 +78,407 @@ interface DiscussionFeedCardProps { onInternalLinkClick: (data: InternalLinkData) => void; } -const DiscussionFeedCard = forwardRef( - (props, ref) => { - const { - setChatItem, - feedItemIdForAutoChatOpen, - shouldAllowChatAutoOpen, - nestedItemData, - } = useChatContext(); - const { notify } = useNotification(); - const { - item, - governanceCircles, - isMobileVersion = false, +function DiscussionFeedCard(props, ref) { + const { + setChatItem, + feedItemIdForAutoChatOpen, + shouldAllowChatAutoOpen, + nestedItemData, + } = useChatContext(); + const { notify } = useNotification(); + const { + item, + governanceCircles, + isMobileVersion = false, + commonId, + commonName, + commonImage, + commonNotion: outerCommonNotion, + pinnedFeedItems, + commonMember, + isProject, + isPinned, + isPreviewMode, + isActive, + isExpanded, + getLastMessage, + getNonAllowedItems, + onActiveItemDataChange, + directParent, + rootCommonId, + feedItemFollow, + shouldPreLoadMessages, + withoutMenu, + onUserClick, + onFeedItemClick, + onInternalLinkClick, + } = props; + const { + isShowing: isReportModalOpen, + onOpen: onReportModalOpen, + onClose: onReportModalClose, + } = useModal(false); + const { + isShowing: isShareModalOpen, + onOpen: onShareModalOpen, + onClose: onShareModalClose, + } = useModal(false); + const { + isShowing: isDeleteModalOpen, + onOpen: onDeleteModalOpen, + onClose: onDeleteModalClose, + } = useModal(false); + const { + isShowing: isLinkStreamModalOpen, + onOpen: onLinkStreamModalOpen, + onClose: onLinkStreamModalClose, + } = useModal(false); + const { + isShowing: isUnlinkStreamModalOpen, + onOpen: onUnlinkStreamModalOpen, + onClose: onUnlinkStreamModalClose, + } = useModal(false); + const { + isShowing: isMoveStreamModalOpen, + onOpen: onMoveStreamModalOpen, + onClose: onMoveStreamModalClose, + } = useModal(false); + const [isDeletingInProgress, setDeletingInProgress] = useState(false); + const { + fetchUser: fetchDiscussionCreator, + data: discussionCreator, + fetched: isDiscussionCreatorFetched, + } = useUserById(); + const { + fetchDiscussion, + data: discussion, + fetched: isDiscussionFetched, + } = useDiscussionById(); + const isHome = discussion?.predefinedType === PredefinedTypes.General; + const discussionNotion = commonId + ? discussion?.notionByCommon?.[commonId] + : undefined; + const { + data: feedItemUserMetadata, + fetched: isFeedItemUserMetadataFetched, + fetchFeedItemUserMetadata, + } = useFeedItemUserMetadata(); + const shouldLoadCommonData = + isHome || (discussionNotion && !outerCommonNotion); + const { data: common } = useCommon(shouldLoadCommonData ? commonId : ""); + const preloadDiscussionMessagesData = usePreloadDiscussionMessagesById({ + commonId, + discussionId: discussion?.id, + onUserClick, + onFeedItemClick, + onInternalLinkClick, + }); + const { markFeedItemAsSeen, markFeedItemAsUnseen } = + useUpdateFeedItemSeenState(); + const menuItems = useMenuItems( + { commonId, - commonName, - commonImage, - commonNotion: outerCommonNotion, pinnedFeedItems, + feedItem: item, + discussion, + governanceCircles, commonMember, - isProject, - isPinned, - isPreviewMode, - isActive, - isExpanded, - getLastMessage, - getNonAllowedItems, - onActiveItemDataChange, - directParent, - rootCommonId, feedItemFollow, - shouldPreLoadMessages, + getNonAllowedItems, + feedItemUserMetadata, withoutMenu, - onUserClick, - onFeedItemClick, - onInternalLinkClick, - } = props; - const { - isShowing: isReportModalOpen, - onOpen: onReportModalOpen, - onClose: onReportModalClose, - } = useModal(false); - const { - isShowing: isShareModalOpen, - onOpen: onShareModalOpen, - onClose: onShareModalClose, - } = useModal(false); - const { - isShowing: isDeleteModalOpen, - onOpen: onDeleteModalOpen, - onClose: onDeleteModalClose, - } = useModal(false); - const { - isShowing: isLinkStreamModalOpen, - onOpen: onLinkStreamModalOpen, - onClose: onLinkStreamModalClose, - } = useModal(false); - const { - isShowing: isUnlinkStreamModalOpen, - onOpen: onUnlinkStreamModalOpen, - onClose: onUnlinkStreamModalClose, - } = useModal(false); - const { - isShowing: isMoveStreamModalOpen, - onOpen: onMoveStreamModalOpen, - onClose: onMoveStreamModalClose, - } = useModal(false); - const [isDeletingInProgress, setDeletingInProgress] = useState(false); - const { - fetchUser: fetchDiscussionCreator, - data: discussionCreator, - fetched: isDiscussionCreatorFetched, - } = useUserById(); - const { - fetchDiscussion, - data: discussion, - fetched: isDiscussionFetched, - } = useDiscussionById(); - const isHome = discussion?.predefinedType === PredefinedTypes.General; - const discussionNotion = commonId - ? discussion?.notionByCommon?.[commonId] - : undefined; - const { - data: feedItemUserMetadata, - fetched: isFeedItemUserMetadataFetched, - fetchFeedItemUserMetadata, - } = useFeedItemUserMetadata(); - const shouldLoadCommonData = - isHome || (discussionNotion && !outerCommonNotion); - const { data: common } = useCommon(shouldLoadCommonData ? commonId : ""); - const preloadDiscussionMessagesData = usePreloadDiscussionMessagesById({ - commonId, - discussionId: discussion?.id, - onUserClick, - onFeedItemClick, - onInternalLinkClick, - }); - const { markFeedItemAsSeen, markFeedItemAsUnseen } = - useUpdateFeedItemSeenState(); - const menuItems = useMenuItems( - { - commonId, - pinnedFeedItems, - feedItem: item, - discussion, - governanceCircles, - commonMember, - feedItemFollow, - getNonAllowedItems, - feedItemUserMetadata, - withoutMenu, - }, - { - report: onReportModalOpen, - share: () => onShareModalOpen(), - remove: onDeleteModalOpen, - linkStream: onLinkStreamModalOpen, - unlinkStream: onUnlinkStreamModalOpen, - moveStream: onMoveStreamModalOpen, - markFeedItemAsSeen, - markFeedItemAsUnseen, - }, - ); - const user = useSelector(selectUser()); - const [isHovering, setHovering] = useState(false); - const onHover = (isMouseEnter: boolean): void => { - setHovering(isMouseEnter); - }; - const userId = user?.uid; - const isLoading = - !isDiscussionCreatorFetched || - !isDiscussionFetched || - !isFeedItemUserMetadataFetched || - !commonId; - const cardTitle = discussion?.title; - const commonNotion = outerCommonNotion ?? common?.notion; + }, + { + report: onReportModalOpen, + share: () => onShareModalOpen(), + remove: onDeleteModalOpen, + linkStream: onLinkStreamModalOpen, + unlinkStream: onUnlinkStreamModalOpen, + moveStream: onMoveStreamModalOpen, + markFeedItemAsSeen, + markFeedItemAsUnseen, + }, + ); + const user = useSelector(selectUser()); + const [isHovering, setHovering] = useState(false); + const onHover = (isMouseEnter: boolean): void => { + setHovering(isMouseEnter); + }; + const userId = user?.uid; + const isLoading = + !isDiscussionCreatorFetched || + !isDiscussionFetched || + !isFeedItemUserMetadataFetched || + !commonId; + const cardTitle = discussion?.title; + const commonNotion = outerCommonNotion ?? common?.notion; - const handleOpenChat = useCallback(() => { - if (discussion && !isPreviewMode) { - setChatItem({ - feedItemId: item.id, - discussion, - circleVisibility: item.circleVisibility, - lastSeenItem: feedItemUserMetadata?.lastSeen, - lastSeenAt: feedItemUserMetadata?.lastSeenAt, - count: feedItemUserMetadata?.count, - seenOnce: feedItemUserMetadata?.seenOnce, - seen: feedItemUserMetadata?.seen, - hasUnseenMention: feedItemUserMetadata?.hasUnseenMention, - nestedItemData: nestedItemData && { - ...nestedItemData, - feedItem: { - type: InboxItemType.FeedItemFollow, - itemId: item.id, - feedItem: item, - }, + const handleOpenChat = useCallback(() => { + if (discussion && !isPreviewMode) { + setChatItem({ + feedItemId: item.id, + discussion, + circleVisibility: item.circleVisibility, + lastSeenItem: feedItemUserMetadata?.lastSeen, + lastSeenAt: feedItemUserMetadata?.lastSeenAt, + count: feedItemUserMetadata?.count, + seenOnce: feedItemUserMetadata?.seenOnce, + seen: feedItemUserMetadata?.seen, + hasUnseenMention: feedItemUserMetadata?.hasUnseenMention, + nestedItemData: nestedItemData && { + ...nestedItemData, + feedItem: { + type: InboxItemType.FeedItemFollow, + itemId: item.id, + feedItem: item, }, - }); - } - }, [ - discussion, - item.id, - item.circleVisibility, - feedItemUserMetadata?.lastSeen, - feedItemUserMetadata?.lastSeenAt, - feedItemUserMetadata?.count, - feedItemUserMetadata?.seenOnce, - feedItemUserMetadata?.seen, - feedItemUserMetadata?.hasUnseenMention, - nestedItemData, - isPreviewMode, - ]); + }, + }); + } + }, [ + discussion, + item.id, + item.circleVisibility, + feedItemUserMetadata?.lastSeen, + feedItemUserMetadata?.lastSeenAt, + feedItemUserMetadata?.count, + feedItemUserMetadata?.seenOnce, + feedItemUserMetadata?.seen, + feedItemUserMetadata?.hasUnseenMention, + nestedItemData, + isPreviewMode, + ]); - const onDiscussionDelete = useCallback(async () => { - try { - if (discussion) { - setDeletingInProgress(true); - await DiscussionService.deleteDiscussion(discussion.id); - onDeleteModalClose(); - } - } catch { - notify("Something went wrong"); - } finally { - setDeletingInProgress(false); + const onDiscussionDelete = useCallback(async () => { + try { + if (discussion) { + setDeletingInProgress(true); + await DiscussionService.deleteDiscussion(discussion.id); + onDeleteModalClose(); } - }, [discussion]); + } catch { + notify("Something went wrong"); + } finally { + setDeletingInProgress(false); + } + }, [discussion]); - const preloadDiscussionMessages = useMemo( - () => - debounce< - typeof preloadDiscussionMessagesData.preloadDiscussionMessages - >( - (...args) => - preloadDiscussionMessagesData.preloadDiscussionMessages(...args), - 6000, - ), - [preloadDiscussionMessagesData.preloadDiscussionMessages], - ); + const preloadDiscussionMessages = useMemo( + () => + debounce( + (...args) => + preloadDiscussionMessagesData.preloadDiscussionMessages(...args), + 6000, + ), + [preloadDiscussionMessagesData.preloadDiscussionMessages], + ); - useEffect(() => { - fetchDiscussionCreator(item.userId); - }, [item.userId]); + useEffect(() => { + fetchDiscussionCreator(item.userId); + }, [item.userId]); - useEffect(() => { - fetchDiscussion(item.data.id); - }, [item.data.id]); + useEffect(() => { + fetchDiscussion(item.data.id); + }, [item.data.id]); - useEffect(() => { - if (commonId) { - fetchFeedItemUserMetadata({ - userId: userId || "", - commonId, - feedObjectId: item.id, - }); - } - }, [userId, commonId, item.id]); + useEffect(() => { + if (commonId) { + fetchFeedItemUserMetadata({ + userId: userId || "", + commonId, + feedObjectId: item.id, + }); + } + }, [userId, commonId, item.id]); - useEffect(() => { - if ( - (!isActive || - shouldAllowChatAutoOpen === null || - shouldAllowChatAutoOpen) && - isDiscussionFetched && - isFeedItemUserMetadataFetched && - item.id === feedItemIdForAutoChatOpen && - !isMobileVersion - ) { - handleOpenChat(); - } - }, [ - isDiscussionFetched, - isFeedItemUserMetadataFetched, - shouldAllowChatAutoOpen, - ]); + useEffect(() => { + if ( + (!isActive || + shouldAllowChatAutoOpen === null || + shouldAllowChatAutoOpen) && + isDiscussionFetched && + isFeedItemUserMetadataFetched && + item.id === feedItemIdForAutoChatOpen && + !isMobileVersion + ) { + handleOpenChat(); + } + }, [ + isDiscussionFetched, + isFeedItemUserMetadataFetched, + shouldAllowChatAutoOpen, + ]); - useEffect(() => { - if (isActive && shouldAllowChatAutoOpen !== null) { - handleOpenChat(); - } - }, [isActive, shouldAllowChatAutoOpen, handleOpenChat]); + useEffect(() => { + if (isActive && shouldAllowChatAutoOpen !== null) { + handleOpenChat(); + } + }, [isActive, shouldAllowChatAutoOpen, handleOpenChat]); - useEffect(() => { - if (isActive && cardTitle) { - onActiveItemDataChange?.({ - itemId: item.id, - title: cardTitle, - }); - } - }, [isActive, cardTitle]); + useEffect(() => { + if (isActive && cardTitle) { + onActiveItemDataChange?.({ + itemId: item.id, + title: cardTitle, + }); + } + }, [isActive, cardTitle]); - useEffect(() => { - if ( - shouldPreLoadMessages && - !isActive && - commonId && - item.circleVisibility - ) { - preloadDiscussionMessages(item.circleVisibility); - } - }, [shouldPreLoadMessages, isActive]); + useEffect(() => { + if ( + shouldPreLoadMessages && + !isActive && + commonId && + item.circleVisibility + ) { + preloadDiscussionMessages(item.circleVisibility); + } + }, [shouldPreLoadMessages, isActive]); - useUpdateEffect(() => { - if ( - shouldPreLoadMessages && - !isActive && - commonId && - item.circleVisibility - ) { - preloadDiscussionMessages(item.circleVisibility, true); - } - }, [item.data.lastMessage?.content]); + useUpdateEffect(() => { + if ( + shouldPreLoadMessages && + !isActive && + commonId && + item.circleVisibility + ) { + preloadDiscussionMessages(item.circleVisibility, true); + } + }, [item.data.lastMessage?.content]); - return ( - <> - - {(isExpanded || isActive) && ( - onUserClick(item.userId))} - discussionCreator={discussionCreator} - isHome={isHome} - menuItems={menuItems} - discussion={discussion} - common={common} - discussionNotion={discussionNotion} - handleOpenChat={handleOpenChat} - onHover={onHover} - isLoading={isLoading} - /> - )} - - {userId && discussion && ( - + + {(isExpanded || isActive) && ( + onUserClick(item.userId))} + discussionCreator={discussionCreator} + isHome={isHome} + menuItems={menuItems} + discussion={discussion} + common={common} + discussionNotion={discussionNotion} + handleOpenChat={handleOpenChat} + onHover={onHover} + isLoading={isLoading} /> )} - {discussion && ( - + {userId && discussion && ( + + )} + {discussion && ( + + )} + {isDeleteModalOpen && ( + + + + )} + {commonId && ( + <> + - )} - {isDeleteModalOpen && ( - - - - )} - {commonId && ( - <> - - - - - )} - - ); - }, -); + + + + )} + + ); +} -export default DiscussionFeedCard; +export default forwardRef( + DiscussionFeedCard, +); diff --git a/src/pages/common/components/FeedCard/FeedCard.tsx b/src/pages/common/components/FeedCard/FeedCard.tsx index 65f87967a..353d5bd9e 100644 --- a/src/pages/common/components/FeedCard/FeedCard.tsx +++ b/src/pages/common/components/FeedCard/FeedCard.tsx @@ -60,7 +60,7 @@ type FeedCardProps = PropsWithChildren<{ linkedCommonIds?: string[]; }>; -export const FeedCard = forwardRef((props, ref) => { +const FeedCard = (props, ref) => { const { className, feedItemId, @@ -278,4 +278,6 @@ export const FeedCard = forwardRef((props, ref) => { ); -}); +}; + +export default forwardRef(FeedCard); diff --git a/src/pages/common/components/FeedCard/index.ts b/src/pages/common/components/FeedCard/index.ts index 71201cbdf..257e55820 100644 --- a/src/pages/common/components/FeedCard/index.ts +++ b/src/pages/common/components/FeedCard/index.ts @@ -1,4 +1,4 @@ -export * from "./FeedCard"; +export { default as FeedCard } from "./FeedCard"; export * from "./components"; export * from "./types"; export * from "./utils"; diff --git a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx index 949dcea0c..591068e5e 100644 --- a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx +++ b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx @@ -72,400 +72,398 @@ interface ProposalFeedCardProps { onInternalLinkClick: (data: InternalLinkData) => void; } -const ProposalFeedCard = forwardRef( - (props, ref) => { - const { +function ProposalFeedCard(props: ProposalFeedCardProps, ref: React.Ref) { + const { + commonId, + commonName, + commonImage, + commonNotion: outerCommonNotion, + pinnedFeedItems, + isProject, + isPinned, + item, + governanceCircles, + isPreviewMode, + isActive, + isExpanded, + getLastMessage, + getNonAllowedItems, + isMobileVersion, + feedItemFollow, + onActiveItemDataChange, + shouldPreLoadMessages, + withoutMenu, + onUserClick, + onFeedItemClick, + onInternalLinkClick, + } = props; + const user = useSelector(selectUser()); + const userId = user?.uid; + const { + setChatItem, + feedItemIdForAutoChatOpen, + shouldAllowChatAutoOpen, + nestedItemData, + } = useChatContext(); + const { notify } = useNotification(); + const forceUpdate = useForceUpdate(); + const { getCommonPagePath } = useRoutesContext(); + const { + fetchUser: fetchFeedItemUser, + data: feedItemUser, + fetched: isFeedItemUserFetched, + } = useUserById(); + const { + fetchDiscussion, + data: discussion, + fetched: isDiscussionFetched, + } = useDiscussionById(); + const { + fetchProposal, + data: proposal, + fetched: isProposalFetched, + } = useProposalById(); + const { + fetched: isCommonMemberFetched, + data: commonMember, + fetchCommonMember, + } = useCommonMember(); + const { + data: userVote, + loading: isUserVoteLoading, + fetchProposalVote, + setVote, + } = useProposalUserVote(); + const { + data: proposalSpecificData, + fetched: isProposalSpecificDataFetched, + fetchData: fetchProposalSpecificData, + } = useProposalSpecificData(); + const { + data: feedItemUserMetadata, + fetched: isFeedItemUserMetadataFetched, + fetchFeedItemUserMetadata, + } = useFeedItemUserMetadata(); + const discussionNotion = commonId + ? discussion?.notionByCommon?.[commonId] + : undefined; + const shouldLoadCommonData = discussionNotion && !outerCommonNotion; + const { data: common } = useCommon(shouldLoadCommonData ? commonId : ""); + const { + isShowing: isProposalDeleteModalOpen, + onOpen: onProposalDeleteModalOpen, + onClose: onProposalDeleteModalClose, + } = useModal(false); + const [isProposalDeletingInProgress, setProposalDeletingInProgress] = + useState(false); + const isLoading = + !isFeedItemUserFetched || + !isDiscussionFetched || + !isProposalFetched || + !proposal || + isUserVoteLoading || + !isCommonMemberFetched || + !isProposalSpecificDataFetched || + !isFeedItemUserMetadataFetched || + !commonId || + !governanceCircles; + const [isHovering, setHovering] = useState(false); + const onHover = (isMouseEnter: boolean): void => { + setHovering(isMouseEnter); + }; + const proposalId = item.data.id; + const { + isShowing: isShareModalOpen, + onOpen: onShareModalOpen, + onClose: onShareModalClose, + } = useModal(false); + const preloadDiscussionMessagesData = usePreloadDiscussionMessagesById({ + commonId, + discussionId: discussion?.id, + onUserClick, + onFeedItemClick, + onInternalLinkClick, + }); + const { markFeedItemAsSeen, markFeedItemAsUnseen } = + useUpdateFeedItemSeenState(); + const menuItems = useMenuItems( + { commonId, - commonName, - commonImage, - commonNotion: outerCommonNotion, pinnedFeedItems, - isProject, - isPinned, - item, + feedItem: item, + discussion, governanceCircles, - isPreviewMode, - isActive, - isExpanded, - getLastMessage, - getNonAllowedItems, - isMobileVersion, + commonMember, feedItemFollow, - onActiveItemDataChange, - shouldPreLoadMessages, + getNonAllowedItems, + feedItemUserMetadata, withoutMenu, - onUserClick, - onFeedItemClick, - onInternalLinkClick, - } = props; - const user = useSelector(selectUser()); - const userId = user?.uid; - const { - setChatItem, - feedItemIdForAutoChatOpen, - shouldAllowChatAutoOpen, - nestedItemData, - } = useChatContext(); - const { notify } = useNotification(); - const forceUpdate = useForceUpdate(); - const { getCommonPagePath } = useRoutesContext(); - const { - fetchUser: fetchFeedItemUser, - data: feedItemUser, - fetched: isFeedItemUserFetched, - } = useUserById(); - const { - fetchDiscussion, - data: discussion, - fetched: isDiscussionFetched, - } = useDiscussionById(); - const { - fetchProposal, - data: proposal, - fetched: isProposalFetched, - } = useProposalById(); - const { - fetched: isCommonMemberFetched, - data: commonMember, - fetchCommonMember, - } = useCommonMember(); - const { - data: userVote, - loading: isUserVoteLoading, - fetchProposalVote, - setVote, - } = useProposalUserVote(); - const { - data: proposalSpecificData, - fetched: isProposalSpecificDataFetched, - fetchData: fetchProposalSpecificData, - } = useProposalSpecificData(); - const { - data: feedItemUserMetadata, - fetched: isFeedItemUserMetadataFetched, - fetchFeedItemUserMetadata, - } = useFeedItemUserMetadata(); - const discussionNotion = commonId - ? discussion?.notionByCommon?.[commonId] - : undefined; - const shouldLoadCommonData = discussionNotion && !outerCommonNotion; - const { data: common } = useCommon(shouldLoadCommonData ? commonId : ""); - const { - isShowing: isProposalDeleteModalOpen, - onOpen: onProposalDeleteModalOpen, - onClose: onProposalDeleteModalClose, - } = useModal(false); - const [isProposalDeletingInProgress, setProposalDeletingInProgress] = - useState(false); - const isLoading = - !isFeedItemUserFetched || - !isDiscussionFetched || - !isProposalFetched || - !proposal || - isUserVoteLoading || - !isCommonMemberFetched || - !isProposalSpecificDataFetched || - !isFeedItemUserMetadataFetched || - !commonId || - !governanceCircles; - const [isHovering, setHovering] = useState(false); - const onHover = (isMouseEnter: boolean): void => { - setHovering(isMouseEnter); - }; - const proposalId = item.data.id; - const { - isShowing: isShareModalOpen, - onOpen: onShareModalOpen, - onClose: onShareModalClose, - } = useModal(false); - const preloadDiscussionMessagesData = usePreloadDiscussionMessagesById({ - commonId, - discussionId: discussion?.id, - onUserClick, - onFeedItemClick, - onInternalLinkClick, - }); - const { markFeedItemAsSeen, markFeedItemAsUnseen } = - useUpdateFeedItemSeenState(); - const menuItems = useMenuItems( - { - commonId, - pinnedFeedItems, - feedItem: item, - discussion, - governanceCircles, - commonMember, - feedItemFollow, - getNonAllowedItems, - feedItemUserMetadata, - withoutMenu, - }, - { - report: () => {}, - share: () => onShareModalOpen(), - remove: onProposalDeleteModalOpen, - markFeedItemAsSeen, - markFeedItemAsUnseen, - }, - ); - const cardTitle = discussion?.title; - const commonNotion = outerCommonNotion ?? common?.notion; + }, + { + report: () => {}, + share: () => onShareModalOpen(), + remove: onProposalDeleteModalOpen, + markFeedItemAsSeen, + markFeedItemAsUnseen, + }, + ); + const cardTitle = discussion?.title; + const commonNotion = outerCommonNotion ?? common?.notion; - const onProposalDelete = useCallback(async () => { - try { - setProposalDeletingInProgress(true); - await ProposalService.deleteProposal(proposalId); - onProposalDeleteModalClose(); - } catch { - notify("Something went wrong"); - } finally { - setProposalDeletingInProgress(false); - } - }, [proposalId]); + const onProposalDelete = useCallback(async () => { + try { + setProposalDeletingInProgress(true); + await ProposalService.deleteProposal(proposalId); + onProposalDeleteModalClose(); + } catch { + notify("Something went wrong"); + } finally { + setProposalDeletingInProgress(false); + } + }, [proposalId]); - const preloadDiscussionMessages = useMemo( - () => - debounce< - typeof preloadDiscussionMessagesData.preloadDiscussionMessages - >( - (...args) => - preloadDiscussionMessagesData.preloadDiscussionMessages(...args), - 6000, - ), - [preloadDiscussionMessagesData.preloadDiscussionMessages], - ); + const preloadDiscussionMessages = useMemo( + () => + debounce< + typeof preloadDiscussionMessagesData.preloadDiscussionMessages + >( + (...args) => + preloadDiscussionMessagesData.preloadDiscussionMessages(...args), + 6000, + ), + [preloadDiscussionMessagesData.preloadDiscussionMessages], + ); - useEffect(() => { - fetchFeedItemUser(item.userId); - }, [item.userId]); + useEffect(() => { + fetchFeedItemUser(item.userId); + }, [item.userId]); - useEffect(() => { - if (item.data.discussionId) { - fetchDiscussion(item.data.discussionId); - } - }, [item.data.discussionId]); + useEffect(() => { + if (item.data.discussionId) { + fetchDiscussion(item.data.discussionId); + } + }, [item.data.discussionId]); - useEffect(() => { - fetchProposal(item.data.id); - }, [item.data.id]); + useEffect(() => { + fetchProposal(item.data.id); + }, [item.data.id]); - useEffect(() => { - fetchProposalVote(proposalId); - }, [fetchProposalVote, proposalId]); + useEffect(() => { + fetchProposalVote(proposalId); + }, [fetchProposalVote, proposalId]); - useEffect(() => { - if (commonId) { - fetchCommonMember(commonId, {}); - } - }, [fetchCommonMember, commonId]); + useEffect(() => { + if (commonId) { + fetchCommonMember(commonId, {}); + } + }, [fetchCommonMember, commonId]); - useEffect(() => { - if (commonId) { - fetchFeedItemUserMetadata({ - userId: userId || "", - commonId, - feedObjectId: item.id, - }); - } - }, [userId, commonId, item.id]); + useEffect(() => { + if (commonId) { + fetchFeedItemUserMetadata({ + userId: userId || "", + commonId, + feedObjectId: item.id, + }); + } + }, [userId, commonId, item.id]); - useEffect(() => { - if (proposal) { - fetchProposalSpecificData(proposal, true); - } - }, [proposal?.id]); + useEffect(() => { + if (proposal) { + fetchProposalSpecificData(proposal, true); + } + }, [proposal?.id]); - useEffect(() => { - if (isActive && cardTitle) { - onActiveItemDataChange?.({ - itemId: item.id, - title: cardTitle, - }); - } - }, [isActive, cardTitle]); + useEffect(() => { + if (isActive && cardTitle) { + onActiveItemDataChange?.({ + itemId: item.id, + title: cardTitle, + }); + } + }, [isActive, cardTitle]); - const handleOpenChat = useCallback(() => { - if (discussion && proposal && !isPreviewMode) { - setChatItem({ - feedItemId: item.id, - discussion, - proposal, - circleVisibility: item.circleVisibility, - lastSeenItem: feedItemUserMetadata?.lastSeen, - lastSeenAt: feedItemUserMetadata?.lastSeenAt, - seenOnce: feedItemUserMetadata?.seenOnce, - seen: feedItemUserMetadata?.seen, - hasUnseenMention: feedItemUserMetadata?.hasUnseenMention, - nestedItemData: nestedItemData && { - ...nestedItemData, - feedItem: { - type: InboxItemType.FeedItemFollow, - itemId: item.id, - feedItem: item, - }, + const handleOpenChat = useCallback(() => { + if (discussion && proposal && !isPreviewMode) { + setChatItem({ + feedItemId: item.id, + discussion, + proposal, + circleVisibility: item.circleVisibility, + lastSeenItem: feedItemUserMetadata?.lastSeen, + lastSeenAt: feedItemUserMetadata?.lastSeenAt, + seenOnce: feedItemUserMetadata?.seenOnce, + seen: feedItemUserMetadata?.seen, + hasUnseenMention: feedItemUserMetadata?.hasUnseenMention, + nestedItemData: nestedItemData && { + ...nestedItemData, + feedItem: { + type: InboxItemType.FeedItemFollow, + itemId: item.id, + feedItem: item, }, - }); - } - }, [ - item.id, - proposal, - discussion, - setChatItem, - item.circleVisibility, - feedItemUserMetadata?.lastSeen, - feedItemUserMetadata?.lastSeenAt, - feedItemUserMetadata?.seenOnce, - feedItemUserMetadata?.seen, - feedItemUserMetadata?.hasUnseenMention, - nestedItemData, - isPreviewMode, - ]); + }, + }); + } + }, [ + item.id, + proposal, + discussion, + setChatItem, + item.circleVisibility, + feedItemUserMetadata?.lastSeen, + feedItemUserMetadata?.lastSeenAt, + feedItemUserMetadata?.seenOnce, + feedItemUserMetadata?.seen, + feedItemUserMetadata?.hasUnseenMention, + nestedItemData, + isPreviewMode, + ]); - useEffect(() => { - if ( - (!isActive || - shouldAllowChatAutoOpen === null || - shouldAllowChatAutoOpen) && - isDiscussionFetched && - isProposalFetched && - isFeedItemUserMetadataFetched && - item.id === feedItemIdForAutoChatOpen && - !isMobileVersion - ) { - handleOpenChat(); - } - }, [ - isDiscussionFetched, - isProposalFetched, - isFeedItemUserMetadataFetched, - shouldAllowChatAutoOpen, - ]); + useEffect(() => { + if ( + (!isActive || + shouldAllowChatAutoOpen === null || + shouldAllowChatAutoOpen) && + isDiscussionFetched && + isProposalFetched && + isFeedItemUserMetadataFetched && + item.id === feedItemIdForAutoChatOpen && + !isMobileVersion + ) { + handleOpenChat(); + } + }, [ + isDiscussionFetched, + isProposalFetched, + isFeedItemUserMetadataFetched, + shouldAllowChatAutoOpen, + ]); - useEffect(() => { - if (isActive && shouldAllowChatAutoOpen !== null) { - handleOpenChat(); - } - }, [isActive, shouldAllowChatAutoOpen, handleOpenChat]); + useEffect(() => { + if (isActive && shouldAllowChatAutoOpen !== null) { + handleOpenChat(); + } + }, [isActive, shouldAllowChatAutoOpen, handleOpenChat]); - useEffect(() => { - if (isExpanded) { - forceUpdate(); - } - }, [isExpanded]); + useEffect(() => { + if (isExpanded) { + forceUpdate(); + } + }, [isExpanded]); - useEffect(() => { - if ( - shouldPreLoadMessages && - !isActive && - commonId && - item.circleVisibility - ) { - preloadDiscussionMessages(item.circleVisibility); - } - }, [shouldPreLoadMessages, isActive]); + useEffect(() => { + if ( + shouldPreLoadMessages && + !isActive && + commonId && + item.circleVisibility + ) { + preloadDiscussionMessages(item.circleVisibility); + } + }, [shouldPreLoadMessages, isActive]); - useUpdateEffect(() => { - if ( - shouldPreLoadMessages && - !isActive && - commonId && - item.circleVisibility - ) { - preloadDiscussionMessages(item.circleVisibility, true); - } - }, [item.data.lastMessage?.content]); + useUpdateEffect(() => { + if ( + shouldPreLoadMessages && + !isActive && + commonId && + item.circleVisibility + ) { + preloadDiscussionMessages(item.circleVisibility, true); + } + }, [item.data.lastMessage?.content]); - return ( - <> - - {(isActive || isExpanded) && ( - - )} - - {discussion && ( - + + {(isActive || isExpanded) && ( + )} - {isProposalDeleteModalOpen && ( - - - - )} - - ); - }, -); + + {discussion && ( + + )} + {isProposalDeleteModalOpen && ( + + + + )} + + ); +} -export default ProposalFeedCard; +export default forwardRef(ProposalFeedCard); From b90ebbf6e50c626a4884444e0ab8b86ed5c0a087 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Wed, 7 Aug 2024 14:48:14 +0300 Subject: [PATCH 04/28] CW-some-stream-disappear Added commonId related logic for Feed --- src/pages/OldCommon/hooks/useCommonMember.ts | 3 +- src/pages/OldCommon/hooks/useCommonMembers.ts | 6 +-- .../components/FeedLayout/FeedLayout.tsx | 53 ++++++++++++++++--- src/shared/models/Common.tsx | 1 + 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/src/pages/OldCommon/hooks/useCommonMember.ts b/src/pages/OldCommon/hooks/useCommonMember.ts index f749b2b2e..8d01168e8 100644 --- a/src/pages/OldCommon/hooks/useCommonMember.ts +++ b/src/pages/OldCommon/hooks/useCommonMember.ts @@ -141,6 +141,7 @@ export const useCommonMember = (options: Options = {}): Return => { loading: false, fetched: true, data: { + commonId, ...commonMember, ...generateCirclesDataForCommonMember( governance.circles, @@ -189,7 +190,7 @@ export const useCommonMember = (options: Options = {}): Return => { } } }, - [state, userId], + [state, userId, commonId], ); const setCommonMember = useCallback( diff --git a/src/pages/OldCommon/hooks/useCommonMembers.ts b/src/pages/OldCommon/hooks/useCommonMembers.ts index 3f16ad080..f07c8221f 100644 --- a/src/pages/OldCommon/hooks/useCommonMembers.ts +++ b/src/pages/OldCommon/hooks/useCommonMembers.ts @@ -141,7 +141,7 @@ export const useCommonMembers = ({ commonId }: Options): Return => { const user = cachedUserStates[commonMember.userId]?.data; - return user ? [...acc, { ...commonMember, user }] : acc; + return user ? [...acc, { ...commonMember, user, commonId }] : acc; }, []); return { @@ -178,7 +178,7 @@ export const useCommonMembers = ({ commonId }: Options): Return => { ({ uid }) => uid === commonMember.userId, ); - return user ? [...acc, { ...commonMember, user }] : acc; + return user ? [...acc, { ...commonMember, user, commonId }] : acc; }, []); return { @@ -198,7 +198,7 @@ export const useCommonMembers = ({ commonId }: Options): Return => { })); } })(); - }, [commonMembersState.data]); + }, [commonMembersState.data, commonId]); return { ...state, diff --git a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx index c57d97461..7f2e19cd8 100644 --- a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx +++ b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx @@ -13,7 +13,7 @@ import React, { import { useSelector } from "react-redux"; import { useHistory } from "react-router-dom"; import PullToRefresh from "react-simple-pull-to-refresh"; -import { useWindowSize } from "react-use"; +import { useDeepCompareEffect, useWindowSize } from "react-use"; import classNames from "classnames"; import { selectUser } from "@/pages/Auth/store/selectors"; import { useCommonMember } from "@/pages/OldCommon/hooks"; @@ -219,9 +219,39 @@ const FeedLayout: ForwardRefRenderFunction = ( chatItem?.nestedItemData?.commonMember || outerCommonMember || fetchedCommonMember; + + const [ + commonMemberForSpecificCommonIds, + setCommonMemberForSpecificCommonIds, + ] = useState({}); + + useDeepCompareEffect(() => { + const chatItemCommonMember = { ...chatItem?.nestedItemData?.commonMember }; + + setCommonMemberForSpecificCommonIds((prevCommonMembers) => { + if (chatItemCommonMember?.commonId) { + prevCommonMembers[chatItemCommonMember.commonId] = chatItemCommonMember; + } + + if (outerCommonMember?.commonId) { + prevCommonMembers[outerCommonMember.commonId] = outerCommonMember; + } + + if (fetchedCommonMember?.commonId) { + prevCommonMembers[fetchedCommonMember.commonId] = fetchedCommonMember; + } + + return prevCommonMembers; + }); + }, [ + fetchedCommonMember, + chatItem?.nestedItemData?.commonMember, + outerCommonMember, + ]); + const userForProfile = useUserForProfile(); const governance = chatItem?.nestedItemData - ? (fetchedGovernance || outerGovernance) + ? fetchedGovernance || outerGovernance : outerGovernance || fetchedGovernance; const [splitPaneRef, setSplitPaneRef] = useState(null); @@ -312,9 +342,16 @@ const FeedLayout: ForwardRefRenderFunction = ( ]); const activeFeedItemId = chatItem?.feedItemId || feedItemIdForAutoChatOpen; const sizeKey = `${windowWidth}_${contentWidth}`; - const userCircleIds = useMemo( - () => Object.values(commonMember?.circles.map ?? {}), - [commonMember?.circles.map], + + const getUserCircleIds = useCallback( + (commonId) => { + return Object.values( + commonMemberForSpecificCommonIds[commonId]?.circles.map ?? + commonMember?.circles.map ?? + {}, + ) as string[]; + }, + [commonMemberForSpecificCommonIds, commonMember?.circles.map], ); const selectedFeedItem = useMemo( @@ -831,7 +868,7 @@ const FeedLayout: ForwardRefRenderFunction = ( item={item.feedItem} governanceCircles={governance?.circles} isMobileVersion={isTabletView} - userCircleIds={userCircleIds} + userCircleIds={getUserCircleIds(commonData?.id)} isActive={isActive} isExpanded={item.feedItem.id === expandedFeedItemId} sizeKey={isActive ? sizeKey : undefined} @@ -917,7 +954,9 @@ const FeedLayout: ForwardRefRenderFunction = ( isProject={selectedItemCommonData.isProject} governanceCircles={governance?.circles} selectedFeedItem={selectedFeedItem?.feedItem} - userCircleIds={userCircleIds} + userCircleIds={getUserCircleIds( + selectedItemCommonData.id, + )} isShowFeedItemDetailsModal={isShowFeedItemDetailsModal} sizeKey={sizeKey} isMainModalOpen={Boolean(chatItem)} diff --git a/src/shared/models/Common.tsx b/src/shared/models/Common.tsx index c5d79b1d1..ca92fefb3 100644 --- a/src/shared/models/Common.tsx +++ b/src/shared/models/Common.tsx @@ -172,6 +172,7 @@ export interface CommonMember { isFollowing: boolean; streamsUnreadCountByProjectStream?: Record; unreadCountByProjectStream?: Record; + commonId?: string; } export interface CirclesPermissions { From e0e046cd4b10fefea66c814d893a8adfa5ee682c Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 8 Aug 2024 12:52:43 +0300 Subject: [PATCH 05/28] CW-mobile-performance Added memoization for ProjectFeedItem --- .../ProjectFeedItem/ProjectFeedItem.tsx | 140 +++++++++++------- src/shared/hooks/ModalHook.tsx | 10 +- 2 files changed, 90 insertions(+), 60 deletions(-) diff --git a/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx b/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx index 76e3ad9d8..32b3eb141 100644 --- a/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx +++ b/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx @@ -2,10 +2,11 @@ import React, { CSSProperties, FC, MouseEventHandler, - ReactNode, useEffect, + useMemo, useRef, useState, + useCallback, } from "react"; import { useCollapse } from "react-collapsed"; import { useSelector } from "react-redux"; @@ -93,45 +94,56 @@ export const ProjectFeedItem: FC = (props) => { ); const commonPath = getCommonPagePath(commonId); const isProject = checkIsProject(common); - const titleEl = ( - <> - {common?.name} - - + const titleEl = useMemo( + () => ( + <> + {common?.name} + + + ), + [common?.name], ); - const handleClick = () => { + const handleClick = useCallback(() => { history.push(commonPath); - }; + }, [history, commonPath]); - const handleExpand: MouseEventHandler = (event) => { - event.stopPropagation(); - setIsExpanded((v) => !v); - }; + const handleExpand: MouseEventHandler = useCallback( + (event) => { + event.stopPropagation(); + setIsExpanded((v) => !v); + }, + [setIsExpanded], + ); - const renderLeftContent = (): ReactNode => ( -
- - ( +
+ + + + - - -
+
+ ), + [common?.image, common?.name, isExpanded, isProject, handleExpand], ); useEffect(() => { @@ -146,6 +158,45 @@ export const ProjectFeedItem: FC = (props) => { } }, [isExpanded]); + const feedItemProps = useMemo( + () => ({ + className: styles.container, + titleWrapperClassName: styles.titleWrapper, + lastActivity: item.updatedAt.seconds * 1000, + isMobileView: isMobileVersion, + title: titleEl, + onClick: handleClick, + onExpand: handleExpand, + seenOnce: true, + isLoading: !isCommonFetched, + unreadMessages, + lastMessage, + seen: !( + unreadStreamsCount && + unreadStreamsCount > 0 && + unreadMessages === 0 + ), + renderLeftContent, + shouldHideBottomContent: !lastMessage, + isFollowing: feedItemFollow.isFollowing, + notion: common?.notion, + }), + [ + item.updatedAt.seconds, + isMobileVersion, + titleEl, + handleClick, + handleExpand, + isCommonFetched, + unreadMessages, + lastMessage, + unreadStreamsCount, + renderLeftContent, + feedItemFollow.isFollowing, + common?.notion, + ], + ); + if ( !isCommonMemberFetched || (!commonMember && common?.listVisibility === SpaceListVisibility.Members) @@ -164,28 +215,7 @@ export const ProjectFeedItem: FC = (props) => { style={itemStyles} >
- {renderFeedItemBaseContent?.({ - className: styles.container, - titleWrapperClassName: styles.titleWrapper, - lastActivity: item.updatedAt.seconds * 1000, - isMobileView: isMobileVersion, - title: titleEl, - onClick: handleClick, - onExpand: handleExpand, - seenOnce: true, - isLoading: !isCommonFetched, - unreadMessages, - lastMessage, - seen: !( - unreadStreamsCount && - unreadStreamsCount > 0 && - unreadMessages === 0 - ), - renderLeftContent, - shouldHideBottomContent: !lastMessage, - isFollowing: feedItemFollow.isFollowing, - notion: common?.notion, - })} + {renderFeedItemBaseContent?.(feedItemProps)}
{ const [isShowing, setIsShowing] = useState(show); - function onClose() { + const onClose = useCallback(() => { setIsShowing(false); - } + }, []); - function onOpen() { + const onOpen = useCallback(() => { setIsShowing(true); - } + }, []); return { isShowing, From 795445be654785fa1870abfbf4e4d46b65fda2d0 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 8 Aug 2024 13:05:35 +0300 Subject: [PATCH 06/28] CW-mobile-performance Added memoization for FeedCard --- .../common/components/FeedCard/FeedCard.tsx | 141 +++++++++++------- 1 file changed, 91 insertions(+), 50 deletions(-) diff --git a/src/pages/common/components/FeedCard/FeedCard.tsx b/src/pages/common/components/FeedCard/FeedCard.tsx index 65f87967a..62c018eaa 100644 --- a/src/pages/common/components/FeedCard/FeedCard.tsx +++ b/src/pages/common/components/FeedCard/FeedCard.tsx @@ -5,6 +5,8 @@ import React, { forwardRef, useImperativeHandle, PropsWithChildren, + useCallback, + useMemo, } from "react"; import { useCollapse } from "react-collapsed"; import classNames from "classnames"; @@ -106,11 +108,11 @@ export const FeedCard = forwardRef((props, ref) => { }); const containerRef = useRef(null); - const toggleExpanding = () => { + const toggleExpanding = useCallback(() => { if (setExpandedFeedItemId) { setExpandedFeedItemId(isExpanded ? null : feedItemId); } - }; + }, [setExpandedFeedItemId, isExpanded, feedItemId]); const scrollToTargetTop = ( headerOffset: number, @@ -140,7 +142,7 @@ export const FeedCard = forwardRef((props, ref) => { } }; - const scrollToTargetAdjusted = () => { + const scrollToTargetAdjusted = useCallback(() => { if (scrollTimeoutRef.current) { clearTimeout(scrollTimeoutRef.current); } @@ -192,7 +194,7 @@ export const FeedCard = forwardRef((props, ref) => { }); } }, COLLAPSE_DURATION + EXTRA_WAITING_TIME_FOR_TIMEOUT); - }; + }, [isTabletView]); useEffect(() => { if (isExpanded && containerRef?.current) { @@ -203,63 +205,102 @@ export const FeedCard = forwardRef((props, ref) => { clearTimeout(scrollTimeoutRef.current); scrollTimeoutRef.current = null; } - }, [isExpanded]); + }, [isExpanded, scrollToTargetAdjusted]); - const handleClick = () => { + const handleClick = useCallback(() => { onClick?.(); if (!isTabletView && isActive) { toggleExpanding(); } - }; + }, [onClick, isTabletView, isActive, toggleExpanding]); - const handleExpand: MouseEventHandler = (event) => { - event.stopPropagation(); - toggleExpanding(); - }; + const handleExpand: MouseEventHandler = useCallback( + (event) => { + event.stopPropagation(); + toggleExpanding(); + }, + [toggleExpanding], + ); + + useImperativeHandle( + ref, + () => ({ + itemId: feedItemId, + scrollToItem: scrollToTargetAdjusted, + }), + [feedItemId, scrollToTargetAdjusted], + ); - useImperativeHandle(ref, () => ({ - itemId: feedItemId, - scrollToItem: scrollToTargetAdjusted, - })); + const feedItemBaseContent = useMemo(() => { + return renderFeedItemBaseContent?.({ + lastActivity, + unreadMessages, + isMobileView: isTabletView, + isActive, + isExpanded, + canBeExpanded, + onClick: handleClick, + onExpand: handleExpand, + title, + lastMessage: !isLoading ? lastMessage : undefined, + menuItems, + commonName, + commonId, + image, + imageAlt, + isProject, + isPinned, + isFollowing, + type, + seenOnce, + seen, + ownerId, + discussionPredefinedType, + hasFiles, + hasImages, + hasUnseenMention, + notion, + originalCommonIdForLinking, + linkedCommonIds, + }); + }, [ + lastActivity, + unreadMessages, + isTabletView, + isActive, + isExpanded, + canBeExpanded, + handleClick, + handleExpand, + title, + lastMessage, + isLoading, + menuItems, + commonName, + commonId, + image, + imageAlt, + isProject, + isPinned, + isFollowing, + type, + seenOnce, + seen, + ownerId, + discussionPredefinedType, + hasFiles, + hasImages, + hasUnseenMention, + notion, + originalCommonIdForLinking, + linkedCommonIds, + renderFeedItemBaseContent, + ]); return (
- {!isPreviewMode && ( -
- {renderFeedItemBaseContent?.({ - lastActivity, - unreadMessages, - isMobileView: isTabletView, - isActive, - isExpanded, - canBeExpanded, - onClick: handleClick, - onExpand: handleExpand, - title, - lastMessage: !isLoading ? lastMessage : undefined, - menuItems, - commonName, - commonId, - image, - imageAlt, - isProject, - isPinned, - isFollowing, - type, - seenOnce, - seen, - ownerId, - discussionPredefinedType, - hasFiles, - hasImages, - hasUnseenMention, - notion, - originalCommonIdForLinking, - linkedCommonIds, - })} -
- )} + {!isPreviewMode &&
{feedItemBaseContent}
}
Date: Thu, 8 Aug 2024 14:18:52 +0300 Subject: [PATCH 07/28] CW-mobile-performance Added useMemo for lastMessages and menuItems --- .../DiscussionFeedCard/DiscussionFeedCard.tsx | 36 +++++++++----- .../ProjectFeedItem/ProjectFeedItem.tsx | 4 +- .../ProposalFeedCard/ProposalFeedCard.tsx | 36 +++++++++----- .../ChatChannelItem/ChatChannelItem.tsx | 4 +- .../ChatChannelItem/hooks/useMenuItems.tsx | 49 ++++++++++--------- 5 files changed, 81 insertions(+), 48 deletions(-) diff --git a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx index 3da4b2a80..5ae93d503 100644 --- a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx +++ b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx @@ -349,6 +349,30 @@ const DiscussionFeedCard = forwardRef( } }, [item.data.lastMessage?.content]); + const lastMessage = useMemo(() => { + return getLastMessage({ + commonFeedType: item.data.type, + lastMessage: item.data.lastMessage, + discussion, + currentUserId: userId, + feedItemCreatorName: getUserName(discussionCreator), + commonName, + isProject, + hasFiles: item.data.hasFiles, + hasImages: item.data.hasImages, + }); + }, [ + item.data.type, + item.data.lastMessage, + discussion, + userId, + discussionCreator, + commonName, + isProject, + item.data.hasFiles, + item.data.hasImages, + ]); + return ( <> ( isExpanded={isExpanded} onClick={handleOpenChat} title={cardTitle} - lastMessage={getLastMessage({ - commonFeedType: item.data.type, - lastMessage: item.data.lastMessage, - discussion, - currentUserId: userId, - feedItemCreatorName: getUserName(discussionCreator), - commonName, - isProject, - hasFiles: item.data.hasFiles, - hasImages: item.data.hasImages, - })} + lastMessage={lastMessage} isPreviewMode={isPreviewMode} isPinned={isPinned} commonName={commonName} diff --git a/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx b/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx index 32b3eb141..d90904780 100644 --- a/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx +++ b/src/pages/common/components/FeedItem/components/ProjectFeedItem/ProjectFeedItem.tsx @@ -87,11 +87,11 @@ export const ProjectFeedItem: FC = (props) => { duration: COLLAPSE_DURATION, }); const isLoading = !fetched; - const lastMessage = parseStringToTextEditorValue( + const lastMessage = useMemo(() => parseStringToTextEditorValue( `${unreadStreamsCount ?? 0} unread stream${ unreadStreamsCount === 1 ? "" : "s" }`, - ); + ),[unreadStreamsCount]); const commonPath = getCommonPagePath(commonId); const isProject = checkIsProject(common); const titleEl = useMemo( diff --git a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx index 949dcea0c..3396b0756 100644 --- a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx +++ b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx @@ -375,6 +375,30 @@ const ProposalFeedCard = forwardRef( } }, [item.data.lastMessage?.content]); + const lastMessage = useMemo(() => { + return getLastMessage({ + commonFeedType: item.data.type, + lastMessage: item.data.lastMessage, + discussion, + currentUserId: userId, + feedItemCreatorName: getUserName(feedItemUser), + commonName, + isProject, + hasFiles: item.data.hasFiles, + hasImages: item.data.hasImages, + }); + }, [ + item.data.type, + item.data.lastMessage, + discussion, + userId, + feedItemUser, + commonName, + isProject, + item.data.hasFiles, + item.data.hasImages, + ]); + return ( <> ( isExpanded={isExpanded} unreadMessages={feedItemUserMetadata?.count || 0} title={cardTitle} - lastMessage={getLastMessage({ - commonFeedType: item.data.type, - lastMessage: item.data.lastMessage, - discussion, - currentUserId: userId, - feedItemCreatorName: getUserName(feedItemUser), - commonName, - isProject, - hasFiles: item.data.hasFiles, - hasImages: item.data.hasImages, - })} + lastMessage={lastMessage} canBeExpanded={discussion?.predefinedType !== PredefinedTypes.General} isPreviewMode={isPreviewMode} commonName={commonName} diff --git a/src/pages/inbox/components/ChatChannelItem/ChatChannelItem.tsx b/src/pages/inbox/components/ChatChannelItem/ChatChannelItem.tsx index ccb85d5d8..cd9016cf7 100644 --- a/src/pages/inbox/components/ChatChannelItem/ChatChannelItem.tsx +++ b/src/pages/inbox/components/ChatChannelItem/ChatChannelItem.tsx @@ -149,6 +149,8 @@ export const ChatChannelItem: FC = (props) => { } }, [isActive, finalTitle, dmUsers?.[0]?.photoURL, dmUsersNames?.[0]]); + const lastMessage = useMemo(() => getLastMessage(chatChannel.lastMessage), [chatChannel.lastMessage]); + return ( = (props) => { isMobileView={isTabletView} isActive={isActive} title={finalTitle} - lastMessage={getLastMessage(chatChannel.lastMessage)} + lastMessage={lastMessage} canBeExpanded={false} onClick={handleOpenChat} menuItems={menuItems} diff --git a/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx b/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx index 05095fc9b..4970ee0c7 100644 --- a/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx +++ b/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useMemo } from "react"; import { Message3Icon } from "@/shared/icons"; import { ContextMenuItem as Item } from "@/shared/interfaces"; import { ChatChannelMenuItem } from "../constants"; @@ -15,32 +15,35 @@ export const useMenuItems = ( ): Item[] => { const { chatChannelUserStatus } = options; const { markChatChannelAsSeen, markChatChannelAsUnseen } = actions; - const items: Item[] = [ - { - id: ChatChannelMenuItem.MarkUnread, - text: "Mark as unread", - onClick: async () => { - if (!chatChannelUserStatus) { - return; - } + const items: Item[] = useMemo( + () => [ + { + id: ChatChannelMenuItem.MarkUnread, + text: "Mark as unread", + onClick: async () => { + if (!chatChannelUserStatus) { + return; + } - markChatChannelAsUnseen(chatChannelUserStatus.chatChannelId); + markChatChannelAsUnseen(chatChannelUserStatus.chatChannelId); + }, + icon: , }, - icon: , - }, - { - id: ChatChannelMenuItem.MarkRead, - text: "Mark as read", - onClick: async () => { - if (!chatChannelUserStatus) { - return; - } + { + id: ChatChannelMenuItem.MarkRead, + text: "Mark as read", + onClick: async () => { + if (!chatChannelUserStatus) { + return; + } - markChatChannelAsSeen(chatChannelUserStatus.chatChannelId); + markChatChannelAsSeen(chatChannelUserStatus.chatChannelId); + }, + icon: , }, - icon: , - }, - ]; + ], + [chatChannelUserStatus?.chatChannelId], + ); return getAllowedItems(items, options); }; From 5c9a8601d6dd4d7e41be6fa01bbac5a55fa03969 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 8 Aug 2024 16:35:21 +0300 Subject: [PATCH 08/28] CW-mobile-performance Added memo for CommonFeed --- src/pages/commonFeed/CommonFeed.tsx | 65 +++++++++++++++++------------ 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 8d85ba7a8..0c1cb6b2c 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -237,11 +237,14 @@ const CommonFeedComponent: FC = (props) => { fetchUserRelatedData(); }; - const fetchMoreCommonFeedItems = (feedItemId?: string) => { - if (hasMoreCommonFeedItems && !isSearchingFeedItems) { - fetchCommonFeedItems(feedItemId); - } - }; + const fetchMoreCommonFeedItems = useCallback( + (feedItemId?: string) => { + if (hasMoreCommonFeedItems && !isSearchingFeedItems) { + fetchCommonFeedItems(feedItemId); + } + }, + [hasMoreCommonFeedItems, isSearchingFeedItems, fetchCommonFeedItems] + ); const renderFeedItemBaseContent = useCallback( (props: FeedItemBaseContentProps) => , @@ -281,7 +284,7 @@ const CommonFeedComponent: FC = (props) => { : onProjectJoinModalOpen : onCommonJoinModalOpen; - const renderChatInput = (): ReactNode => { + const renderChatInput = useCallback((): ReactNode => { if (commonMember) { return; } @@ -333,9 +336,19 @@ const CommonFeedComponent: FC = (props) => { )} ); - }; + }, [ + commonMember, + isJoinPending, + commonData, + isRootCommonMember, + canJoin, + onRootCommonJoinModalOpen, + getCommonPagePath, + parentCommonMember, + onJoinCommon + ]); - const renderLayoutTabs = (): ReactElement => { + const renderLayoutTabs = useCallback((): ReactElement => { return (
onJoinCommon()}> @@ -343,7 +356,24 @@ const CommonFeedComponent: FC = (props) => {
); - }; + }, [onJoinCommon]); + + const renderContentWrapper = useCallback( + (children: ReactNode, wrapperStyles?: CSSProperties): ReactNode => + outerContentWrapperRenderer({ + children, + wrapperStyles, + commonData, + commonMember, + isGlobalDataFetched, + }), + [outerContentWrapperRenderer, commonData, commonMember, isGlobalDataFetched] + ); + + const onPullToRefresh = useCallback(() => { + dispatch(cacheActions.clearFeedStateByCommonId(commonId)); + dispatch(commonActions.resetFeedItems()); + }, [dispatch, commonId]); useEffect(() => { if ( @@ -538,23 +568,6 @@ const CommonFeedComponent: FC = (props) => { ); } - const onPullToRefresh = () => { - dispatch(cacheActions.clearFeedStateByCommonId(commonId)); - dispatch(commonActions.resetFeedItems()); - }; - - const renderContentWrapper = ( - children: ReactNode, - wrapperStyles?: CSSProperties, - ): ReactNode => - outerContentWrapperRenderer({ - children, - wrapperStyles, - commonData, - commonMember, - isGlobalDataFetched, - }); - return ( <> Date: Thu, 8 Aug 2024 16:43:44 +0300 Subject: [PATCH 09/28] CW-mobile-performance Fix type issue --- src/pages/commonFeed/CommonFeed.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 0c1cb6b2c..6e85d1c37 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -363,7 +363,7 @@ const CommonFeedComponent: FC = (props) => { outerContentWrapperRenderer({ children, wrapperStyles, - commonData, + commonData: commonData!, commonMember, isGlobalDataFetched, }), From 5e0d1c565361c713ea62622e9135bee746e94aa9 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 8 Aug 2024 17:09:42 +0300 Subject: [PATCH 10/28] CW-mobile-performance Optimize FeedLayout --- src/pages/commonFeed/CommonFeed.tsx | 90 +++++++++++++--------- src/pages/inbox/BaseInbox.tsx | 4 +- src/shared/hooks/useCases/useInboxItems.ts | 6 +- 3 files changed, 59 insertions(+), 41 deletions(-) diff --git a/src/pages/commonFeed/CommonFeed.tsx b/src/pages/commonFeed/CommonFeed.tsx index 6e85d1c37..b9e341b8a 100644 --- a/src/pages/commonFeed/CommonFeed.tsx +++ b/src/pages/commonFeed/CommonFeed.tsx @@ -243,7 +243,7 @@ const CommonFeedComponent: FC = (props) => { fetchCommonFeedItems(feedItemId); } }, - [hasMoreCommonFeedItems, isSearchingFeedItems, fetchCommonFeedItems] + [hasMoreCommonFeedItems, isSearchingFeedItems, fetchCommonFeedItems], ); const renderFeedItemBaseContent = useCallback( @@ -345,7 +345,7 @@ const CommonFeedComponent: FC = (props) => { onRootCommonJoinModalOpen, getCommonPagePath, parentCommonMember, - onJoinCommon + onJoinCommon, ]); const renderLayoutTabs = useCallback((): ReactElement => { @@ -367,7 +367,12 @@ const CommonFeedComponent: FC = (props) => { commonMember, isGlobalDataFetched, }), - [outerContentWrapperRenderer, commonData, commonMember, isGlobalDataFetched] + [ + outerContentWrapperRenderer, + commonData, + commonMember, + isGlobalDataFetched, + ], ); const onPullToRefresh = useCallback(() => { @@ -526,6 +531,51 @@ const CommonFeedComponent: FC = (props) => { } }, [commonAction]); + const FeedLayoutTopContent = useCallback(() => { + if (!commonData) { + return null; + } + + return ( + { + dispatch(commonActions.setCommonAction(null)); + }} + > + {(commonAction === CommonAction.NewDiscussion || + commonAction === CommonAction.EditDiscussion) && ( + + )} + {commonAction === CommonAction.NewProposal && ( + + )} + + ); + }, [ + JSON.stringify(commonData), + JSON.stringify(commonMember), + commonAction, + scrollToItemsTop, + ]); + if (!isDataFetched) { const headerEl = renderLoadingHeader ? ( renderLoadingHeader() @@ -574,39 +624,7 @@ const CommonFeedComponent: FC = (props) => { ref={setFeedLayoutRef} className={styles.feedLayout} renderContentWrapper={renderContentWrapper} - topContent={ - { - dispatch(commonActions.setCommonAction(null)); - }} - > - {(commonAction === CommonAction.NewDiscussion || - commonAction === CommonAction.EditDiscussion) && ( - - )} - {commonAction === CommonAction.NewProposal && ( - - )} - - } + topContent={FeedLayoutTopContent} common={commonData.common} governance={commonData.governance} commonMember={commonMember} diff --git a/src/pages/inbox/BaseInbox.tsx b/src/pages/inbox/BaseInbox.tsx index 4a92ccae1..6b34a11d8 100644 --- a/src/pages/inbox/BaseInbox.tsx +++ b/src/pages/inbox/BaseInbox.tsx @@ -143,11 +143,11 @@ const InboxPage: FC = (props) => { }); }; - const fetchMoreInboxItems = () => { + const fetchMoreInboxItems = useCallback(() => { if (hasMoreInboxItems && !isSearchingInboxItems && !areInboxItemsLoading) { fetchInboxItems(); } - }; + },[hasMoreInboxItems, isSearchingInboxItems, areInboxItemsLoading]); const renderFeedItemBaseContent = useCallback( (props: FeedItemBaseContentProps) => , diff --git a/src/shared/hooks/useCases/useInboxItems.ts b/src/shared/hooks/useCases/useInboxItems.ts index 9e15e5508..19b5fde24 100644 --- a/src/shared/hooks/useCases/useInboxItems.ts +++ b/src/shared/hooks/useCases/useInboxItems.ts @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; import { selectUser } from "@/pages/Auth/store/selectors"; import { Logger, UserService } from "@/services"; @@ -87,10 +87,10 @@ export const useInboxItems = ( ); }; - const refetch = () => { + const refetch = useCallback(() => { setNewItemsBatches([]); dispatch(inboxActions.refetchInboxItems(Boolean(unread))); - }; + },[unread, setNewItemsBatches]); const addNewInboxItems = ( data: { From c38e29123b0a137f0d088407efa975919252ad82 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Fri, 9 Aug 2024 02:39:33 +0300 Subject: [PATCH 11/28] CW-mobile-performance Added memo for TreeItemTrigger and useMenuItems --- .../ChatChannelItem/hooks/useMenuItems.tsx | 4 +- .../DesktopCommonDropdown.tsx | 95 +++++++++++-------- .../components/TreeItem/TreeItem.tsx | 12 ++- 3 files changed, 64 insertions(+), 47 deletions(-) diff --git a/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx b/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx index 4970ee0c7..a1f2883cb 100644 --- a/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx +++ b/src/pages/inbox/components/ChatChannelItem/hooks/useMenuItems.tsx @@ -45,5 +45,7 @@ export const useMenuItems = ( [chatChannelUserStatus?.chatChannelId], ); - return getAllowedItems(items, options); + const menuItems = useMemo(() => getAllowedItems(items, options), [items, options]); + + return menuItems; }; diff --git a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/CommonDropdown/components/DesktopCommonDropdown/DesktopCommonDropdown.tsx b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/CommonDropdown/components/DesktopCommonDropdown/DesktopCommonDropdown.tsx index b74bbf19c..ba44d1735 100644 --- a/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/CommonDropdown/components/DesktopCommonDropdown/DesktopCommonDropdown.tsx +++ b/src/shared/layouts/CommonSidenavLayout/components/SidenavContent/components/CommonDropdown/components/DesktopCommonDropdown/DesktopCommonDropdown.tsx @@ -1,4 +1,10 @@ -import React, { FC, MouseEventHandler, useState } from "react"; +import React, { + FC, + MouseEventHandler, + useState, + useMemo, + useCallback, +} from "react"; import { useSelector } from "react-redux"; import classNames from "classnames"; import { ButtonIcon } from "@/shared/components"; @@ -27,51 +33,56 @@ const DesktopCommonDropdown: FC = (props) => { setMenuRerenderHack((value) => !value); }; - const finalItems = items.map((item) => ({ - ...item, - className: classNames(item.className, styles.menuItem, { - [styles.menuItemForCommonCreation]: item.id === CREATE_COMMON_ITEM_ID, - }), - activeClassName: classNames(item.activeClassName, styles.menuItemActive), - text: ( - <> - - {item.text} - - {item.id === activeItemId && ( - - )} - - ), - onClick: (event) => { - event.preventDefault(); - handleItemClick(); + const finalItems = useMemo(() => { + return items.map((item) => ({ + ...item, + className: classNames(item.className, styles.menuItem, { + [styles.menuItemForCommonCreation]: item.id === CREATE_COMMON_ITEM_ID, + }), + activeClassName: classNames(item.activeClassName, styles.menuItemActive), + text: ( + <> + + {item.text} + + {item.id === activeItemId && ( + + )} + + ), + onClick: (event) => { + event.preventDefault(); + handleItemClick(); - if (item.type !== MenuItemType.Link) { - item.onClick(event); - } - }, - })); + if (item.type !== MenuItemType.Link) { + item.onClick(event); + } + }, + })); + }, [items]); - const onClick: MouseEventHandler = (event) => { + const onClick: MouseEventHandler = useCallback((event) => { event.stopPropagation(); - }; + }, []); - const triggerEl = ( - - {isMobileView ? ( - Change - ) : ( - - )} - + const triggerEl = useMemo( + () => ( + + {isMobileView ? ( + Change + ) : ( + + )} + + ), + [onClick, isActive, isMobileView], ); return ( diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx index 4436f04e6..714b2c429 100644 --- a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx @@ -1,4 +1,4 @@ -import React, { FC, useEffect, useState } from "react"; +import React, { FC, useCallback, useEffect, useState, useMemo } from "react"; import classNames from "classnames"; import { Item } from "../../types"; import { TreeItemTrigger } from "../TreeItemTrigger"; @@ -40,10 +40,14 @@ const TreeItem: FC = (props) => { } }, [isActive, hasNestedContent, isOpenedManually, hasActiveChild]); - const handleTriggerToggle = () => { + const handleTriggerToggle = useCallback(() => { + if (!hasNestedContent) { + return; + } + setIsOpenedManually(!isOpen); setIsOpen((value) => !value); - }; + }, [hasNestedContent]); return (
  • = (props) => { level={level} isActive={isActive} isOpen={isOpen} - onToggle={hasNestedContent ? handleTriggerToggle : undefined} + onToggle={handleTriggerToggle} /> {isOpen ? children : null}
  • From a5d19af53ebdd41cb0a3de21ee631f95f211276a Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Fri, 9 Aug 2024 11:11:39 +0300 Subject: [PATCH 12/28] CW-mobile-performance Fix TreeItemTrigger component --- .../components/TreeItem/TreeItem.tsx | 2 +- .../TreeItemTrigger.module.scss | 60 ---------------- .../TreeItemTrigger/TreeItemTrigger.tsx | 69 +++++------------- .../TreeItemTriggerContent.module.scss | 57 +++++++++++++++ .../TreeItemTriggerContent.tsx | 70 +++++++++++++++++++ .../TreeItemTriggerContent/index.ts | 1 + 6 files changed, 148 insertions(+), 111 deletions(-) create mode 100644 src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.module.scss create mode 100644 src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.tsx create mode 100644 src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/index.ts diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx index 714b2c429..ca5e9eb2d 100644 --- a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItem/TreeItem.tsx @@ -1,4 +1,4 @@ -import React, { FC, useCallback, useEffect, useState, useMemo } from "react"; +import React, { FC, useCallback, useEffect, useState } from "react"; import classNames from "classnames"; import { Item } from "../../types"; import { TreeItemTrigger } from "../TreeItemTrigger"; 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 2ddcd7584..0bb9d1668 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,63 +36,3 @@ .itemDisabled { cursor: not-allowed; } - -.arrowIconButton { - 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; -} - -.arrowIconOpen { - transform: rotate(90deg); -} - -.image { - flex-shrink: 0; - margin-right: var(--item-image-mr); - width: 1.5rem; - height: 1.5rem; - object-fit: cover; -} -.imageNonRounded { - border-radius: 0.1875rem; -} -.imageRounded { - border-radius: 50%; -} - -.name { - font-family: PoppinsSans, sans-serif; - font-weight: 600; - font-size: $small; - color: var(--item-text-color); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.nameActive { - color: $white; -} - -.notificationsAmount { - height: 1rem; - margin-left: auto; - padding: 0 0.25rem; - display: flex; - align-items: center; - justify-content: center; - font-weight: 600; - font-size: $xxsmall-2; - color: $c-shades-white; - background-color: $c-primary-400; - border-radius: 0.5rem; -} diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.tsx b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.tsx index acaddb127..3b62a1039 100644 --- a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.tsx +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTrigger/TreeItemTrigger.tsx @@ -1,11 +1,9 @@ import React, { FC, MouseEventHandler } from "react"; import { NavLink } from "react-router-dom"; import classNames from "classnames"; -import { ButtonIcon } from "@/shared/components/ButtonIcon"; -import { SmallArrowIcon } from "@/shared/icons"; -import { CommonAvatar } from "@/shared/ui-kit"; import { useTreeContext } from "../../context"; import { Item } from "../../types"; +import { TreeItemTriggerContent } from "../TreeItemTriggerContent"; import styles from "./TreeItemTrigger.module.scss"; interface TreeItemTriggerProps { @@ -50,51 +48,6 @@ const TreeItemTrigger: FC = (props) => { className, treeItemTriggerStyles?.container, ); - const contentEl = ( - <> - - - - - - - - {item.name} - - {item.nameRightContent} - {item.rightContent} - {!!item.notificationsAmount && ( - - {item.notificationsAmount} - - )} - - ); if (onItemClick || item.disabled) { return ( @@ -106,7 +59,15 @@ const TreeItemTrigger: FC = (props) => { tabIndex={0} onClick={handleItemClick} > - {contentEl} +
    ); } @@ -118,7 +79,15 @@ const TreeItemTrigger: FC = (props) => { title={item.name} aria-label={`Go to ${item.name}`} > - {contentEl} + ); }; diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.module.scss b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.module.scss new file mode 100644 index 000000000..9cb218cf6 --- /dev/null +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.module.scss @@ -0,0 +1,57 @@ +@import "../../../../../../../../../constants"; + +.arrowIconButton { + 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; +} + +.arrowIconOpen { + transform: rotate(90deg); +} + +.image { + flex-shrink: 0; + margin-right: var(--item-image-mr); + width: 1.5rem; + height: 1.5rem; + object-fit: cover; +} +.imageNonRounded { + border-radius: 0.1875rem; +} +.imageRounded { + border-radius: 50%; +} + +.name { + font-family: PoppinsSans, sans-serif; + font-weight: 600; + font-size: $small; + color: var(--item-text-color); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.notificationsAmount { + height: 1rem; + margin-left: auto; + padding: 0 0.25rem; + display: flex; + align-items: center; + justify-content: center; + font-weight: 600; + font-size: $xxsmall-2; + color: $c-shades-white; + background-color: $c-primary-400; + border-radius: 0.5rem; +} diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.tsx b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.tsx new file mode 100644 index 000000000..743c24f1c --- /dev/null +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/TreeItemTriggerContent.tsx @@ -0,0 +1,70 @@ +import React, { FC } from "react"; +import classNames from "classnames"; +import { ButtonIcon } from "@/shared/components/ButtonIcon"; +import { SmallArrowIcon } from "@/shared/icons"; +import { CommonAvatar } from "@/shared/ui-kit"; +import { TreeItemTriggerStyles } from "../../context"; +import { Item } from "../../types"; +import styles from "./TreeItemTriggerContent.module.scss"; + +interface TreeItemTriggerContentProps { + treeItemTriggerStyles?: TreeItemTriggerStyles; + item: Item; + level: number; + isActive: boolean; + isOpen: boolean; + onToggle?: () => void; + handleToggle: (event: React.MouseEvent) => void; +} + +const TreeItemTriggerContent: FC = (props) => { + const { treeItemTriggerStyles, item, level, isOpen, handleToggle, onToggle } = props; + + return ( + <> + + + + + + + + {item.name} + + {item.nameRightContent} + {item.rightContent} + {!!item.notificationsAmount && ( + + {item.notificationsAmount} + + )} + + ); +}; + +export default TreeItemTriggerContent; diff --git a/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/index.ts b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/index.ts new file mode 100644 index 000000000..1c215a96e --- /dev/null +++ b/src/shared/layouts/SidenavLayout/components/SidenavContent/components/ProjectsTree/components/TreeItemTriggerContent/index.ts @@ -0,0 +1 @@ +export { default as TreeItemTriggerContent } from "./TreeItemTriggerContent"; \ No newline at end of file From fb6414dccc7339dc7a9ae7a7129b8673b00425a3 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Fri, 9 Aug 2024 17:04:18 +0300 Subject: [PATCH 13/28] CW-mobile-performance Optimize components --- .../components/ChatComponent/ChatComponent.tsx | 13 +++++++++---- .../components/FeedLayout/FeedLayout.tsx | 15 +++++++++++---- .../hooks/useCases/useDiscussionMessagesById.ts | 4 ++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/pages/common/components/ChatComponent/ChatComponent.tsx b/src/pages/common/components/ChatComponent/ChatComponent.tsx index 096516cea..de1e7796f 100644 --- a/src/pages/common/components/ChatComponent/ChatComponent.tsx +++ b/src/pages/common/components/ChatComponent/ChatComponent.tsx @@ -206,10 +206,15 @@ export default function ChatComponent({ chatChannelId: chatChannel?.id || "", participants: chatChannel?.participants, }); - const users = chatChannel ? chatUsers : discussionUsers; - const discussionMessages = chatChannel - ? chatMessagesData.data - : discussionMessagesData.data || []; + const users = useMemo( + () => (chatChannel ? chatUsers : discussionUsers), + [chatUsers, discussionUsers, chatChannel], + ); + const discussionMessages = useMemo( + () => + chatChannel ? chatMessagesData.data : discussionMessagesData.data || [], + [chatChannel, chatMessagesData.data, discussionMessagesData.data], + ); const isFetchedDiscussionMessages = discussionMessagesData.fetched || chatMessagesData.fetched; const areInitialMessagesLoading = isChatChannel diff --git a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx index 0f730acc8..205c35254 100644 --- a/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx +++ b/src/pages/commonFeed/components/FeedLayout/FeedLayout.tsx @@ -215,10 +215,17 @@ const FeedLayout: ForwardRefRenderFunction = ( } = useCommonMember({ shouldAutoReset: false, }); - const commonMember = - chatItem?.nestedItemData?.commonMember || - outerCommonMember || - fetchedCommonMember; + const commonMember = useMemo( + () => + chatItem?.nestedItemData?.commonMember || + outerCommonMember || + fetchedCommonMember, + [ + chatItem?.nestedItemData?.commonMember, + outerCommonMember, + fetchedCommonMember, + ], + ); const userForProfile = useUserForProfile(); const governance = chatItem?.nestedItemData ? fetchedGovernance diff --git a/src/shared/hooks/useCases/useDiscussionMessagesById.ts b/src/shared/hooks/useCases/useDiscussionMessagesById.ts index 6d03f2126..7e6fc36d3 100644 --- a/src/shared/hooks/useCases/useDiscussionMessagesById.ts +++ b/src/shared/hooks/useCases/useDiscussionMessagesById.ts @@ -231,7 +231,7 @@ export const useDiscussionMessagesById = ({ ], ); - const fetchDiscussionMessages = () => { + const fetchDiscussionMessages = useCallback(() => { if ( !discussionId || isEndOfList[discussionId] || @@ -324,7 +324,7 @@ export const useDiscussionMessagesById = ({ } catch(err) { setIsBatchLoading(false); } - }; + },[discussionId, isEndOfList, state.loading, state.data, isBatchLoading, lastVisible, userId, users, directParent, getCommonPagePath, getCommonPageAboutTabPath, onUserClick, onFeedItemClick, onInternalLinkClick, dispatch]); useDeepCompareEffect(() => { (async () => { From 51142e5d33cf3bd3a1f77d0c5d49aa159137d904 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 12:39:55 +0300 Subject: [PATCH 14/28] CW-mobile-perfromance Remove unnecessary libraries --- package.json | 4 +-- .../AddDiscussionComponent.tsx | 2 +- .../components/WhitepaperMembers/helpers.ts | 2 +- .../EditRulesModal/EditSteps/Rules/Rules.tsx | 2 +- .../ChatComponent/ChatComponent.tsx | 2 +- .../utils/getLastNonUserMessage.ts | 2 +- .../DiscussionFormPersist.tsx | 2 +- .../ProposalFormPersist.tsx | 2 +- .../VotingSettings/VotingSettings.tsx | 2 +- .../DiscussionFeedCard/DiscussionFeedCard.tsx | 2 +- .../UnlinkStreamModal/UnlinkStreamModal.tsx | 8 +++-- .../ProposalFeedCard/ProposalFeedCard.tsx | 2 +- .../FeedLayout/utils/splitViewSize.ts | 2 +- .../HeaderContent/hooks/useSearchFeedItems.ts | 2 +- .../commonFeed/hooks/useCommonData/index.ts | 2 +- src/services/Common.ts | 2 +- .../Chat/ChatMessage/ChatMessage.tsx | 2 +- .../Chat/ChatMessage/DMChatMessage.tsx | 2 +- .../components/Reactions/Reactions.tsx | 2 +- src/shared/components/Dropdown/Dropdown.tsx | 2 +- .../AllFilesCarousel/AllFilesCarousel.tsx | 35 ++++++++++--------- .../AdvancedSettingsModal.tsx | 2 +- .../RadioButton/RadioButton.tsx | 2 +- .../useCases/useCommonPinnedFeedItems.ts | 2 +- .../hooks/useCases/useFullCommonData/index.ts | 2 +- .../Header/components/Breadcrumbs/utils.ts | 2 +- .../ui-kit/TextEditor/BaseTextEditor.tsx | 2 +- .../MentionDropdown/MentionDropdown.tsx | 2 +- src/shared/utils/circles.ts | 2 +- .../convertDatesToFirestoreTimestamps.ts | 2 +- src/shared/utils/proposals.ts | 2 +- src/shared/utils/shared.tsx | 2 +- src/store/states/cache/reducer.tsx | 2 +- src/store/states/common/reducer.ts | 2 +- src/store/states/common/saga/getFeedItems.ts | 2 +- src/store/states/inbox/reducer.ts | 2 +- .../states/multipleSpacesLayout/reducer.ts | 2 +- yarn.lock | 10 ------ 38 files changed, 59 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index 1553bfedc..80ff33fa4 100644 --- a/package.json +++ b/package.json @@ -24,13 +24,11 @@ "firebase": "^10.7.1", "formik": "^2.2.1", "google-libphonenumber": "^3.2.28", - "http-status-codes": "^2.2.0", "i18next": "^21.9.1", "immer": "^9.0.16", - "immutable": "^4.2.0", "is-hotkey": "^0.2.0", "jszip": "^3.7.1", - "lodash": "^4.17.20", + "lodash-es": "^4.17.21", "millify": "^3.5.2", "moment": "^2.29.1", "node-sass": "^4.14.1", diff --git a/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx b/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx index 7037f4c72..7ba21e358 100644 --- a/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx +++ b/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux"; import { MultiValue, SingleValue } from "react-select"; import classNames from "classnames"; import { Formik } from "formik"; -import { omit } from "lodash"; +import { omit } from "lodash-es"; import * as Yup from "yup"; import { createDiscussion } from "@/pages/OldCommon/store/actions"; import { getCommonGovernanceCircles } from "@/pages/OldCommon/store/api"; diff --git a/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts b/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts index 0ebc73d35..a4a9d32cd 100644 --- a/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts +++ b/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts @@ -1,4 +1,4 @@ -import { lowerCase, startCase } from "lodash"; +import { lowerCase, startCase } from "lodash-es"; import { GovernanceActions } from "@/shared/constants"; import { AllowedActions } from "@/shared/models"; import { commonTypeText } from "@/shared/utils"; diff --git a/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx b/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx index 813a6cc3d..c89e57d31 100644 --- a/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx +++ b/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx @@ -8,7 +8,7 @@ import { intersectionBy, isEmpty, isEqual, -} from "lodash"; +} from "lodash-es"; import { Button, Separator } from "@/shared/components"; import { Form, RulesArray } from "@/shared/components/Form/Formik"; import { ModalFooter, ModalHeaderContent } from "@/shared/components/Modal"; diff --git a/src/pages/common/components/ChatComponent/ChatComponent.tsx b/src/pages/common/components/ChatComponent/ChatComponent.tsx index de1e7796f..17a20c141 100644 --- a/src/pages/common/components/ChatComponent/ChatComponent.tsx +++ b/src/pages/common/components/ChatComponent/ChatComponent.tsx @@ -11,7 +11,7 @@ import { useDispatch, useSelector } from "react-redux"; import { useDebounce, useMeasure, useScroll } from "react-use"; import classNames from "classnames"; import isHotkey from "is-hotkey"; -import { debounce, delay, omit } from "lodash"; +import { debounce, delay, omit } from "lodash-es"; import { v4 as uuidv4 } from "uuid"; import { selectUser } from "@/pages/Auth/store/selectors"; import { ChatService, DiscussionMessageService, FileService } from "@/services"; diff --git a/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts b/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts index 1653bea56..8b2f163a0 100644 --- a/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts +++ b/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts @@ -1,4 +1,4 @@ -import findLast from "lodash/findLast"; +import { findLast } from "lodash-es"; import { checkIsUserDiscussionMessage, DiscussionMessage, diff --git a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx index b04e92185..757fda4bc 100644 --- a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx +++ b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx @@ -1,7 +1,7 @@ import React, { FC, useEffect, useMemo } from "react"; import { useDispatch } from "react-redux"; import { useFormikContext } from "formik"; -import debounce from "lodash/debounce"; +import { debounce } from "lodash-es"; import { NewDiscussionCreationFormValues } from "@/shared/interfaces"; import { commonActions } from "@/store/states"; diff --git a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx index 5b69531cd..d9eba8e16 100644 --- a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx +++ b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx @@ -1,7 +1,7 @@ import React, { FC, useEffect, useMemo } from "react"; import { useDispatch } from "react-redux"; import { useFormikContext } from "formik"; -import debounce from "lodash/debounce"; +import { debounce } from "lodash-es"; import { NewProposalCreationFormValues } from "@/shared/interfaces"; import { commonActions } from "@/store/states"; diff --git a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx index fb7d9dcd3..e7c798b50 100644 --- a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx +++ b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx @@ -1,5 +1,5 @@ import React, { FC, useMemo } from "react"; -import { flattenDeep, uniq } from "lodash"; +import { flattenDeep, uniq } from "lodash-es"; import { ProposalsTypes } from "@/shared/constants"; import { Circles, Governance } from "@/shared/models"; import { diff --git a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx index 5ae93d503..1fa0fd3c3 100644 --- a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx +++ b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx @@ -7,7 +7,7 @@ import React, { } from "react"; import { useSelector } from "react-redux"; import { useUpdateEffect } from "react-use"; -import { debounce } from "lodash"; +import { debounce } from "lodash-es"; import { selectUser } from "@/pages/Auth/store/selectors"; import { DiscussionService } from "@/services"; import { DeletePrompt, GlobalOverlay, ReportModal } from "@/shared/components"; diff --git a/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx b/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx index 76f088697..0cc50eec3 100644 --- a/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx +++ b/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx @@ -2,7 +2,7 @@ import React, { FC, useRef, useState } from "react"; import { useSelector } from "react-redux"; import { CancelTokenSource } from "axios"; import classNames from "classnames"; -import { isError } from "lodash"; +import { isError } from "lodash-es"; import { selectUser } from "@/pages/Auth/store/selectors"; import { CommonFeedService, @@ -71,7 +71,11 @@ const UnlinkStreamModal: FC = (props) => { cancelTokenRef.current = null; } setIsUnlinking(false); - setErrorText(isError(error) ? error.message : "Something went wrong..."); + setErrorText( + isError(error) + ? (error as Record)?.message + : "Something went wrong...", + ); } }; diff --git a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx index 3396b0756..49f2262a7 100644 --- a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx +++ b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx @@ -7,7 +7,7 @@ import React, { } from "react"; import { useSelector } from "react-redux"; import { useUpdateEffect } from "react-use"; -import { debounce } from "lodash"; +import { debounce } from "lodash-es"; import { selectUser } from "@/pages/Auth/store/selectors"; import { useCommonMember, useProposalUserVote } from "@/pages/OldCommon/hooks"; import { ProposalService } from "@/services"; diff --git a/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts b/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts index f9f1c48ad..02ab48a81 100644 --- a/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts +++ b/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts @@ -1,4 +1,4 @@ -import { debounce } from "lodash"; +import { debounce } from "lodash-es"; import { StorageKey } from "@/shared/constants"; export const saveChatSize = debounce((size: number) => { diff --git a/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts b/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts index a75080246..f36438b64 100644 --- a/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts +++ b/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import { debounce } from "lodash"; +import { debounce } from "lodash-es"; import { QueryParamKey } from "@/shared/constants"; import { ToggleState, useQueryParams, useToggle } from "@/shared/hooks"; import { addQueryParam, deleteQueryParam } from "@/shared/utils"; diff --git a/src/pages/commonFeed/hooks/useCommonData/index.ts b/src/pages/commonFeed/hooks/useCommonData/index.ts index 655be3d88..a93847385 100644 --- a/src/pages/commonFeed/hooks/useCommonData/index.ts +++ b/src/pages/commonFeed/hooks/useCommonData/index.ts @@ -1,5 +1,5 @@ import { RefObject, useCallback, useRef, useState } from "react"; -import { last } from "lodash"; +import { last } from "lodash-es"; import { CommonFeedService, CommonService, diff --git a/src/services/Common.ts b/src/services/Common.ts index 7429ff78d..a62be3d7e 100644 --- a/src/services/Common.ts +++ b/src/services/Common.ts @@ -1,4 +1,4 @@ -import { isEqual } from "lodash"; +import { isEqual } from "lodash-es"; import { getCommonState, updateCommonState, diff --git a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx index 8b4b56a0b..832fe196c 100644 --- a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx @@ -8,7 +8,7 @@ import React, { } from "react"; import { useDispatch } from "react-redux"; import classNames from "classnames"; -import { isEmpty } from "lodash"; +import { isEmpty } from "lodash-es"; import { Element } from "slate"; import { useLongPress } from "use-long-press"; import * as oldCommonActions from "@/pages/OldCommon/store/actions"; diff --git a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx index 99f8c2aff..8ae0a4547 100644 --- a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx @@ -9,7 +9,7 @@ import React, { import { useDispatch } from "react-redux"; import { usePrevious } from "react-use"; import classNames from "classnames"; -import { isEmpty, isEqual } from "lodash"; +import { isEmpty, isEqual } from "lodash-es"; import { Element } from "slate"; import { useLongPress } from "use-long-press"; import { ChatService, Logger } from "@/services"; diff --git a/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx b/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx index 2e9585e4b..4c8e4f284 100644 --- a/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx +++ b/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx @@ -7,7 +7,7 @@ import React, { useState, } from "react"; import { useSelector } from "react-redux"; -import { isEmpty } from "lodash"; +import { isEmpty } from "lodash-es"; import { selectUser } from "@/pages/Auth/store/selectors"; import { Logger } from "@/services"; import { UserAvatar } from "@/shared/components/UserAvatar"; diff --git a/src/shared/components/Dropdown/Dropdown.tsx b/src/shared/components/Dropdown/Dropdown.tsx index 8f7e34c68..83cec6cdc 100644 --- a/src/shared/components/Dropdown/Dropdown.tsx +++ b/src/shared/components/Dropdown/Dropdown.tsx @@ -19,7 +19,7 @@ import { closeMenu, } from "react-aria-menubutton"; import classNames from "classnames"; -import { isEqual } from "lodash"; +import { isEqual } from "lodash-es"; import { v4 as uuidv4 } from "uuid"; import { useChatContentContext } from "@/pages/common/components/CommonContent/context"; import { Loader } from "@/shared/components"; diff --git a/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx b/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx index 8b032dc89..848144770 100644 --- a/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx +++ b/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx @@ -9,14 +9,14 @@ import React, { ForwardRefRenderFunction, } from "react"; import classNames from "classnames"; +import throttle from "lodash-es/throttle"; import { Swiper, SwiperSlide } from "swiper/react"; import SwiperClass from "swiper/types/swiper-class"; -import throttle from "lodash/throttle"; import DownloadIcon from "../../../icons/download.icon"; import LeftArrowIcon from "../../../icons/leftArrow.icon"; import RightArrowIcon from "../../../icons/rightArrow.icon"; -import { saveZip } from "../../../utils"; import { DocInfo } from "../../../models"; +import { saveZip } from "../../../utils"; import { ButtonIcon } from "../../ButtonIcon"; import { InvoiceTile } from "../../InvoiceTile"; import { getSwiperConfig } from "./helpers"; @@ -45,10 +45,8 @@ const AllFilesCarousel: ForwardRefRenderFunction< initialDocIndex, onDocClick, } = props; - const [ - swiperWrapperRef, - setSwiperWrapperRef, - ] = useState(null); + const [swiperWrapperRef, setSwiperWrapperRef] = + useState(null); const [{ isBeginning, isEnd }, setSlideState] = useState<{ isBeginning: boolean; isEnd: boolean; @@ -59,7 +57,7 @@ const AllFilesCarousel: ForwardRefRenderFunction< const swiperClientWidth = swiperWrapperRef?.clientWidth || 0; const swiperConfig = useMemo( () => getSwiperConfig(payoutDocs.length, swiperClientWidth), - [payoutDocs.length, swiperClientWidth] + [payoutDocs.length, swiperClientWidth], ); const handleSlideChange = useCallback( @@ -70,17 +68,17 @@ const AllFilesCarousel: ForwardRefRenderFunction< : { isBeginning, isEnd, - } + }, ); }, - [] + [], ); const handleSwiper = useCallback( (swiper: SwiperClass) => { swiperRef.current = swiper; handleSlideChange(swiper); }, - [handleSlideChange] + [handleSlideChange], ); const handleLeftClick = () => { @@ -131,14 +129,14 @@ const AllFilesCarousel: ForwardRefRenderFunction< swiperRef.current?.slideTo(index); }, }), - [] + [], ); const contentWrapperClassName = classNames( "all-files-carousel-wrapper__content-wrapper", { "all-files-carousel-wrapper__content-wrapper--without-actions": false, - } + }, ); return ( @@ -159,7 +157,8 @@ const AllFilesCarousel: ForwardRefRenderFunction<
    Date: Mon, 12 Aug 2024 13:11:12 +0300 Subject: [PATCH 15/28] CW-mobile-performance Fix jest config --- jest.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/jest.config.js b/jest.config.js index 7dd0e821f..a36638aea 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,5 +16,6 @@ module.exports = { "^@/(.*)$": "/src/$1", }, testPathIgnorePatterns: ["/node_modules/", "/public/"], + transformIgnorePatterns: ["/node_modules/(?!lodash-es)"], testEnvironment: "jsdom", }; From d9aa6e865a18562f22e9c4c4a0fbf3ee63e31742 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 13:22:29 +0300 Subject: [PATCH 16/28] CW-mobile-performance Fix words func call --- src/shared/utils/shared.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/shared.tsx b/src/shared/utils/shared.tsx index 86213243f..40496a7d1 100644 --- a/src/shared/utils/shared.tsx +++ b/src/shared/utils/shared.tsx @@ -124,7 +124,7 @@ export const getRandomUserAvatarURL = ( name?: string | null, textColor = "ffffff", ): string => { - const isOneWord = words(name ?? "").length === 1; + const isOneWord = words(name ?? "", null, null).length === 1; return `https://eu.ui-avatars.com/api/?background=99999D&color=${textColor.substring( 1, )}&name=${name?.replace(/\s/gi, "+")}&rounded=true&${ From 9fecc76d4d3b7b7cb18e0a25fc3ecfda82eb4d1b Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 13:32:00 +0300 Subject: [PATCH 17/28] CW-mobile-performance Fix eslint --- src/shared/utils/shared.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/shared.tsx b/src/shared/utils/shared.tsx index 40496a7d1..c5efb3083 100644 --- a/src/shared/utils/shared.tsx +++ b/src/shared/utils/shared.tsx @@ -124,7 +124,7 @@ export const getRandomUserAvatarURL = ( name?: string | null, textColor = "ffffff", ): string => { - const isOneWord = words(name ?? "", null, null).length === 1; + const isOneWord = words(name ?? "", undefined, undefined).length === 1; return `https://eu.ui-avatars.com/api/?background=99999D&color=${textColor.substring( 1, )}&name=${name?.replace(/\s/gi, "+")}&rounded=true&${ From 8b4d4af07545d31ead46698149daa43519ee333c Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 14:49:08 +0300 Subject: [PATCH 18/28] CW-mobile-performance Added lodash-es types --- package.json | 2 +- src/shared/utils/shared.tsx | 2 +- yarn.lock | 17 ++++++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 80ff33fa4..a8e6024b3 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "@types/google-libphonenumber": "^7.4.23", "@types/is-hotkey": "^0.1.7", "@types/jest": "^27.5.2", - "@types/lodash": "^4.14.163", + "@types/lodash-es": "^4.17.12", "@types/node": "^12.19.3", "@types/openpgp": "4.4.12", "@types/react": "^16.9.55", diff --git a/src/shared/utils/shared.tsx b/src/shared/utils/shared.tsx index c5efb3083..86213243f 100644 --- a/src/shared/utils/shared.tsx +++ b/src/shared/utils/shared.tsx @@ -124,7 +124,7 @@ export const getRandomUserAvatarURL = ( name?: string | null, textColor = "ffffff", ): string => { - const isOneWord = words(name ?? "", undefined, undefined).length === 1; + const isOneWord = words(name ?? "").length === 1; return `https://eu.ui-avatars.com/api/?background=99999D&color=${textColor.substring( 1, )}&name=${name?.replace(/\s/gi, "+")}&rounded=true&${ diff --git a/yarn.lock b/yarn.lock index f3a87f9ba..ac95f0b29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5520,16 +5520,23 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== +"@types/lodash-es@^4.17.12": + version "4.17.12" + resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" + integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.17.7" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" + integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== + "@types/lodash@^4.14.149": version "4.14.191" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== -"@types/lodash@^4.14.163": - version "4.14.182" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" - integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== - "@types/lodash@^4.14.167": version "4.14.187" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.187.tgz#122ff0a7192115b4c1a19444ab4482caa77e2c9d" From dbcf4b678f7e8a9127b196b52522870f25e37616 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 14:55:17 +0300 Subject: [PATCH 19/28] Revert "CW-mobile-performance Added lodash-es types" This reverts commit 8b4d4af07545d31ead46698149daa43519ee333c. --- package.json | 2 +- src/shared/utils/shared.tsx | 2 +- yarn.lock | 17 +++++------------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index a8e6024b3..80ff33fa4 100644 --- a/package.json +++ b/package.json @@ -139,7 +139,7 @@ "@types/google-libphonenumber": "^7.4.23", "@types/is-hotkey": "^0.1.7", "@types/jest": "^27.5.2", - "@types/lodash-es": "^4.17.12", + "@types/lodash": "^4.14.163", "@types/node": "^12.19.3", "@types/openpgp": "4.4.12", "@types/react": "^16.9.55", diff --git a/src/shared/utils/shared.tsx b/src/shared/utils/shared.tsx index 86213243f..c5efb3083 100644 --- a/src/shared/utils/shared.tsx +++ b/src/shared/utils/shared.tsx @@ -124,7 +124,7 @@ export const getRandomUserAvatarURL = ( name?: string | null, textColor = "ffffff", ): string => { - const isOneWord = words(name ?? "").length === 1; + const isOneWord = words(name ?? "", undefined, undefined).length === 1; return `https://eu.ui-avatars.com/api/?background=99999D&color=${textColor.substring( 1, )}&name=${name?.replace(/\s/gi, "+")}&rounded=true&${ diff --git a/yarn.lock b/yarn.lock index ac95f0b29..f3a87f9ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5520,23 +5520,16 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/lodash-es@^4.17.12": - version "4.17.12" - resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b" - integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ== - dependencies: - "@types/lodash" "*" - -"@types/lodash@*": - version "4.17.7" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.7.tgz#2f776bcb53adc9e13b2c0dfd493dfcbd7de43612" - integrity sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA== - "@types/lodash@^4.14.149": version "4.14.191" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== +"@types/lodash@^4.14.163": + version "4.14.182" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" + integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== + "@types/lodash@^4.14.167": version "4.14.187" resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.187.tgz#122ff0a7192115b4c1a19444ab4482caa77e2c9d" From 218e3628f401e795d7699bd613fd7b746ad77b33 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 14:55:24 +0300 Subject: [PATCH 20/28] Revert "CW-mobile-performance Fix eslint" This reverts commit 9fecc76d4d3b7b7cb18e0a25fc3ecfda82eb4d1b. --- src/shared/utils/shared.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/shared.tsx b/src/shared/utils/shared.tsx index c5efb3083..40496a7d1 100644 --- a/src/shared/utils/shared.tsx +++ b/src/shared/utils/shared.tsx @@ -124,7 +124,7 @@ export const getRandomUserAvatarURL = ( name?: string | null, textColor = "ffffff", ): string => { - const isOneWord = words(name ?? "", undefined, undefined).length === 1; + const isOneWord = words(name ?? "", null, null).length === 1; return `https://eu.ui-avatars.com/api/?background=99999D&color=${textColor.substring( 1, )}&name=${name?.replace(/\s/gi, "+")}&rounded=true&${ From 5813f1bfb9ca5cf0da296314a19652437dce8226 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 14:55:28 +0300 Subject: [PATCH 21/28] Revert "CW-mobile-performance Fix words func call" This reverts commit d9aa6e865a18562f22e9c4c4a0fbf3ee63e31742. --- src/shared/utils/shared.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/utils/shared.tsx b/src/shared/utils/shared.tsx index 40496a7d1..86213243f 100644 --- a/src/shared/utils/shared.tsx +++ b/src/shared/utils/shared.tsx @@ -124,7 +124,7 @@ export const getRandomUserAvatarURL = ( name?: string | null, textColor = "ffffff", ): string => { - const isOneWord = words(name ?? "", null, null).length === 1; + const isOneWord = words(name ?? "").length === 1; return `https://eu.ui-avatars.com/api/?background=99999D&color=${textColor.substring( 1, )}&name=${name?.replace(/\s/gi, "+")}&rounded=true&${ From 9d245d0f9ffb0b765541819ce73571ff643de3d4 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 14:55:33 +0300 Subject: [PATCH 22/28] Revert "CW-mobile-performance Fix jest config" This reverts commit d7792d49f1c722cd95e32c9e424444dc16795074. --- jest.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index a36638aea..7dd0e821f 100644 --- a/jest.config.js +++ b/jest.config.js @@ -16,6 +16,5 @@ module.exports = { "^@/(.*)$": "/src/$1", }, testPathIgnorePatterns: ["/node_modules/", "/public/"], - transformIgnorePatterns: ["/node_modules/(?!lodash-es)"], testEnvironment: "jsdom", }; From 2be85e81df08f830db607529f70a26ac573fff27 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Mon, 12 Aug 2024 14:55:39 +0300 Subject: [PATCH 23/28] Revert "CW-mobile-perfromance Remove unnecessary libraries" This reverts commit 51142e5d33cf3bd3a1f77d0c5d49aa159137d904. --- package.json | 4 ++- .../AddDiscussionComponent.tsx | 2 +- .../components/WhitepaperMembers/helpers.ts | 2 +- .../EditRulesModal/EditSteps/Rules/Rules.tsx | 2 +- .../ChatComponent/ChatComponent.tsx | 2 +- .../utils/getLastNonUserMessage.ts | 2 +- .../DiscussionFormPersist.tsx | 2 +- .../ProposalFormPersist.tsx | 2 +- .../VotingSettings/VotingSettings.tsx | 2 +- .../DiscussionFeedCard/DiscussionFeedCard.tsx | 2 +- .../UnlinkStreamModal/UnlinkStreamModal.tsx | 8 ++--- .../ProposalFeedCard/ProposalFeedCard.tsx | 2 +- .../FeedLayout/utils/splitViewSize.ts | 2 +- .../HeaderContent/hooks/useSearchFeedItems.ts | 2 +- .../commonFeed/hooks/useCommonData/index.ts | 2 +- src/services/Common.ts | 2 +- .../Chat/ChatMessage/ChatMessage.tsx | 2 +- .../Chat/ChatMessage/DMChatMessage.tsx | 2 +- .../components/Reactions/Reactions.tsx | 2 +- src/shared/components/Dropdown/Dropdown.tsx | 2 +- .../AllFilesCarousel/AllFilesCarousel.tsx | 35 +++++++++---------- .../AdvancedSettingsModal.tsx | 2 +- .../RadioButton/RadioButton.tsx | 2 +- .../useCases/useCommonPinnedFeedItems.ts | 2 +- .../hooks/useCases/useFullCommonData/index.ts | 2 +- .../Header/components/Breadcrumbs/utils.ts | 2 +- .../ui-kit/TextEditor/BaseTextEditor.tsx | 2 +- .../MentionDropdown/MentionDropdown.tsx | 2 +- src/shared/utils/circles.ts | 2 +- .../convertDatesToFirestoreTimestamps.ts | 2 +- src/shared/utils/proposals.ts | 2 +- src/shared/utils/shared.tsx | 2 +- src/store/states/cache/reducer.tsx | 2 +- src/store/states/common/reducer.ts | 2 +- src/store/states/common/saga/getFeedItems.ts | 2 +- src/store/states/inbox/reducer.ts | 2 +- .../states/multipleSpacesLayout/reducer.ts | 2 +- yarn.lock | 10 ++++++ 38 files changed, 66 insertions(+), 59 deletions(-) diff --git a/package.json b/package.json index 80ff33fa4..1553bfedc 100644 --- a/package.json +++ b/package.json @@ -24,11 +24,13 @@ "firebase": "^10.7.1", "formik": "^2.2.1", "google-libphonenumber": "^3.2.28", + "http-status-codes": "^2.2.0", "i18next": "^21.9.1", "immer": "^9.0.16", + "immutable": "^4.2.0", "is-hotkey": "^0.2.0", "jszip": "^3.7.1", - "lodash-es": "^4.17.21", + "lodash": "^4.17.20", "millify": "^3.5.2", "moment": "^2.29.1", "node-sass": "^4.14.1", diff --git a/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx b/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx index 7ba21e358..7037f4c72 100644 --- a/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx +++ b/src/pages/OldCommon/components/CommonDetailContainer/AddDiscussionComponent/AddDiscussionComponent.tsx @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from "react-redux"; import { MultiValue, SingleValue } from "react-select"; import classNames from "classnames"; import { Formik } from "formik"; -import { omit } from "lodash-es"; +import { omit } from "lodash"; import * as Yup from "yup"; import { createDiscussion } from "@/pages/OldCommon/store/actions"; import { getCommonGovernanceCircles } from "@/pages/OldCommon/store/api"; diff --git a/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts b/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts index a4a9d32cd..0ebc73d35 100644 --- a/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts +++ b/src/pages/OldCommon/components/CommonDetailContainer/CommonWhitepaper/components/WhitepaperMembers/helpers.ts @@ -1,4 +1,4 @@ -import { lowerCase, startCase } from "lodash-es"; +import { lowerCase, startCase } from "lodash"; import { GovernanceActions } from "@/shared/constants"; import { AllowedActions } from "@/shared/models"; import { commonTypeText } from "@/shared/utils"; diff --git a/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx b/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx index c89e57d31..813a6cc3d 100644 --- a/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx +++ b/src/pages/OldCommon/components/CommonListContainer/EditRulesModal/EditSteps/Rules/Rules.tsx @@ -8,7 +8,7 @@ import { intersectionBy, isEmpty, isEqual, -} from "lodash-es"; +} from "lodash"; import { Button, Separator } from "@/shared/components"; import { Form, RulesArray } from "@/shared/components/Form/Formik"; import { ModalFooter, ModalHeaderContent } from "@/shared/components/Modal"; diff --git a/src/pages/common/components/ChatComponent/ChatComponent.tsx b/src/pages/common/components/ChatComponent/ChatComponent.tsx index 17a20c141..de1e7796f 100644 --- a/src/pages/common/components/ChatComponent/ChatComponent.tsx +++ b/src/pages/common/components/ChatComponent/ChatComponent.tsx @@ -11,7 +11,7 @@ import { useDispatch, useSelector } from "react-redux"; import { useDebounce, useMeasure, useScroll } from "react-use"; import classNames from "classnames"; import isHotkey from "is-hotkey"; -import { debounce, delay, omit } from "lodash-es"; +import { debounce, delay, omit } from "lodash"; import { v4 as uuidv4 } from "uuid"; import { selectUser } from "@/pages/Auth/store/selectors"; import { ChatService, DiscussionMessageService, FileService } from "@/services"; diff --git a/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts b/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts index 8b2f163a0..1653bea56 100644 --- a/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts +++ b/src/pages/common/components/ChatComponent/utils/getLastNonUserMessage.ts @@ -1,4 +1,4 @@ -import { findLast } from "lodash-es"; +import findLast from "lodash/findLast"; import { checkIsUserDiscussionMessage, DiscussionMessage, diff --git a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx index 757fda4bc..b04e92185 100644 --- a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx +++ b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewDiscussionCreation/components/DiscussionFormPersist/DiscussionFormPersist.tsx @@ -1,7 +1,7 @@ import React, { FC, useEffect, useMemo } from "react"; import { useDispatch } from "react-redux"; import { useFormikContext } from "formik"; -import { debounce } from "lodash-es"; +import debounce from "lodash/debounce"; import { NewDiscussionCreationFormValues } from "@/shared/interfaces"; import { commonActions } from "@/store/states"; diff --git a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx index d9eba8e16..5b69531cd 100644 --- a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx +++ b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/ProposalFormPersist/ProposalFormPersist.tsx @@ -1,7 +1,7 @@ import React, { FC, useEffect, useMemo } from "react"; import { useDispatch } from "react-redux"; import { useFormikContext } from "formik"; -import { debounce } from "lodash-es"; +import debounce from "lodash/debounce"; import { NewProposalCreationFormValues } from "@/shared/interfaces"; import { commonActions } from "@/store/states"; diff --git a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx index e7c798b50..fb7d9dcd3 100644 --- a/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx +++ b/src/pages/common/components/CommonTabPanels/components/FeedTab/components/NewProposalCreation/components/VotingSettings/VotingSettings.tsx @@ -1,5 +1,5 @@ import React, { FC, useMemo } from "react"; -import { flattenDeep, uniq } from "lodash-es"; +import { flattenDeep, uniq } from "lodash"; import { ProposalsTypes } from "@/shared/constants"; import { Circles, Governance } from "@/shared/models"; import { diff --git a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx index 1fa0fd3c3..5ae93d503 100644 --- a/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx +++ b/src/pages/common/components/DiscussionFeedCard/DiscussionFeedCard.tsx @@ -7,7 +7,7 @@ import React, { } from "react"; import { useSelector } from "react-redux"; import { useUpdateEffect } from "react-use"; -import { debounce } from "lodash-es"; +import { debounce } from "lodash"; import { selectUser } from "@/pages/Auth/store/selectors"; import { DiscussionService } from "@/services"; import { DeletePrompt, GlobalOverlay, ReportModal } from "@/shared/components"; diff --git a/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx b/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx index 0cc50eec3..76f088697 100644 --- a/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx +++ b/src/pages/common/components/DiscussionFeedCard/components/UnlinkStreamModal/UnlinkStreamModal.tsx @@ -2,7 +2,7 @@ import React, { FC, useRef, useState } from "react"; import { useSelector } from "react-redux"; import { CancelTokenSource } from "axios"; import classNames from "classnames"; -import { isError } from "lodash-es"; +import { isError } from "lodash"; import { selectUser } from "@/pages/Auth/store/selectors"; import { CommonFeedService, @@ -71,11 +71,7 @@ const UnlinkStreamModal: FC = (props) => { cancelTokenRef.current = null; } setIsUnlinking(false); - setErrorText( - isError(error) - ? (error as Record)?.message - : "Something went wrong...", - ); + setErrorText(isError(error) ? error.message : "Something went wrong..."); } }; diff --git a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx index 49f2262a7..3396b0756 100644 --- a/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx +++ b/src/pages/common/components/ProposalFeedCard/ProposalFeedCard.tsx @@ -7,7 +7,7 @@ import React, { } from "react"; import { useSelector } from "react-redux"; import { useUpdateEffect } from "react-use"; -import { debounce } from "lodash-es"; +import { debounce } from "lodash"; import { selectUser } from "@/pages/Auth/store/selectors"; import { useCommonMember, useProposalUserVote } from "@/pages/OldCommon/hooks"; import { ProposalService } from "@/services"; diff --git a/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts b/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts index 02ab48a81..f9f1c48ad 100644 --- a/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts +++ b/src/pages/commonFeed/components/FeedLayout/utils/splitViewSize.ts @@ -1,4 +1,4 @@ -import { debounce } from "lodash-es"; +import { debounce } from "lodash"; import { StorageKey } from "@/shared/constants"; export const saveChatSize = debounce((size: number) => { diff --git a/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts b/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts index f36438b64..a75080246 100644 --- a/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts +++ b/src/pages/commonFeed/components/HeaderContent/hooks/useSearchFeedItems.ts @@ -1,5 +1,5 @@ import { useCallback, useEffect, useState } from "react"; -import { debounce } from "lodash-es"; +import { debounce } from "lodash"; import { QueryParamKey } from "@/shared/constants"; import { ToggleState, useQueryParams, useToggle } from "@/shared/hooks"; import { addQueryParam, deleteQueryParam } from "@/shared/utils"; diff --git a/src/pages/commonFeed/hooks/useCommonData/index.ts b/src/pages/commonFeed/hooks/useCommonData/index.ts index a93847385..655be3d88 100644 --- a/src/pages/commonFeed/hooks/useCommonData/index.ts +++ b/src/pages/commonFeed/hooks/useCommonData/index.ts @@ -1,5 +1,5 @@ import { RefObject, useCallback, useRef, useState } from "react"; -import { last } from "lodash-es"; +import { last } from "lodash"; import { CommonFeedService, CommonService, diff --git a/src/services/Common.ts b/src/services/Common.ts index a62be3d7e..7429ff78d 100644 --- a/src/services/Common.ts +++ b/src/services/Common.ts @@ -1,4 +1,4 @@ -import { isEqual } from "lodash-es"; +import { isEqual } from "lodash"; import { getCommonState, updateCommonState, diff --git a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx index 832fe196c..8b4b56a0b 100644 --- a/src/shared/components/Chat/ChatMessage/ChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/ChatMessage.tsx @@ -8,7 +8,7 @@ import React, { } from "react"; import { useDispatch } from "react-redux"; import classNames from "classnames"; -import { isEmpty } from "lodash-es"; +import { isEmpty } from "lodash"; import { Element } from "slate"; import { useLongPress } from "use-long-press"; import * as oldCommonActions from "@/pages/OldCommon/store/actions"; diff --git a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx index 8ae0a4547..99f8c2aff 100644 --- a/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx +++ b/src/shared/components/Chat/ChatMessage/DMChatMessage.tsx @@ -9,7 +9,7 @@ import React, { import { useDispatch } from "react-redux"; import { usePrevious } from "react-use"; import classNames from "classnames"; -import { isEmpty, isEqual } from "lodash-es"; +import { isEmpty, isEqual } from "lodash"; import { Element } from "slate"; import { useLongPress } from "use-long-press"; import { ChatService, Logger } from "@/services"; diff --git a/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx b/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx index 4c8e4f284..2e9585e4b 100644 --- a/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx +++ b/src/shared/components/Chat/ChatMessage/components/Reactions/Reactions.tsx @@ -7,7 +7,7 @@ import React, { useState, } from "react"; import { useSelector } from "react-redux"; -import { isEmpty } from "lodash-es"; +import { isEmpty } from "lodash"; import { selectUser } from "@/pages/Auth/store/selectors"; import { Logger } from "@/services"; import { UserAvatar } from "@/shared/components/UserAvatar"; diff --git a/src/shared/components/Dropdown/Dropdown.tsx b/src/shared/components/Dropdown/Dropdown.tsx index 83cec6cdc..8f7e34c68 100644 --- a/src/shared/components/Dropdown/Dropdown.tsx +++ b/src/shared/components/Dropdown/Dropdown.tsx @@ -19,7 +19,7 @@ import { closeMenu, } from "react-aria-menubutton"; import classNames from "classnames"; -import { isEqual } from "lodash-es"; +import { isEqual } from "lodash"; import { v4 as uuidv4 } from "uuid"; import { useChatContentContext } from "@/pages/common/components/CommonContent/context"; import { Loader } from "@/shared/components"; diff --git a/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx b/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx index 848144770..8b032dc89 100644 --- a/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx +++ b/src/shared/components/FilesCarousel/AllFilesCarousel/AllFilesCarousel.tsx @@ -9,14 +9,14 @@ import React, { ForwardRefRenderFunction, } from "react"; import classNames from "classnames"; -import throttle from "lodash-es/throttle"; import { Swiper, SwiperSlide } from "swiper/react"; import SwiperClass from "swiper/types/swiper-class"; +import throttle from "lodash/throttle"; import DownloadIcon from "../../../icons/download.icon"; import LeftArrowIcon from "../../../icons/leftArrow.icon"; import RightArrowIcon from "../../../icons/rightArrow.icon"; -import { DocInfo } from "../../../models"; import { saveZip } from "../../../utils"; +import { DocInfo } from "../../../models"; import { ButtonIcon } from "../../ButtonIcon"; import { InvoiceTile } from "../../InvoiceTile"; import { getSwiperConfig } from "./helpers"; @@ -45,8 +45,10 @@ const AllFilesCarousel: ForwardRefRenderFunction< initialDocIndex, onDocClick, } = props; - const [swiperWrapperRef, setSwiperWrapperRef] = - useState(null); + const [ + swiperWrapperRef, + setSwiperWrapperRef, + ] = useState(null); const [{ isBeginning, isEnd }, setSlideState] = useState<{ isBeginning: boolean; isEnd: boolean; @@ -57,7 +59,7 @@ const AllFilesCarousel: ForwardRefRenderFunction< const swiperClientWidth = swiperWrapperRef?.clientWidth || 0; const swiperConfig = useMemo( () => getSwiperConfig(payoutDocs.length, swiperClientWidth), - [payoutDocs.length, swiperClientWidth], + [payoutDocs.length, swiperClientWidth] ); const handleSlideChange = useCallback( @@ -68,17 +70,17 @@ const AllFilesCarousel: ForwardRefRenderFunction< : { isBeginning, isEnd, - }, + } ); }, - [], + [] ); const handleSwiper = useCallback( (swiper: SwiperClass) => { swiperRef.current = swiper; handleSlideChange(swiper); }, - [handleSlideChange], + [handleSlideChange] ); const handleLeftClick = () => { @@ -129,14 +131,14 @@ const AllFilesCarousel: ForwardRefRenderFunction< swiperRef.current?.slideTo(index); }, }), - [], + [] ); const contentWrapperClassName = classNames( "all-files-carousel-wrapper__content-wrapper", { "all-files-carousel-wrapper__content-wrapper--without-actions": false, - }, + } ); return ( @@ -157,8 +159,7 @@ const AllFilesCarousel: ForwardRefRenderFunction<
    Date: Wed, 14 Aug 2024 15:54:16 +0300 Subject: [PATCH 24/28] CW-firebase-messaging-for-web Added push notification config for web --- public/firebase-messaging-sw.js | 39 +++++++++ public/logo.png | Bin 0 -> 21322 bytes src/config.tsx | 8 ++ src/pages/App/App.tsx | 2 + .../NotificationsHandler.tsx | 58 +++++++++++++ .../handlers/NotificationsHandler/index.ts | 1 + src/pages/App/handlers/index.ts | 1 + src/services/Notification.ts | 78 ++++++++++++++++++ src/services/index.ts | 1 + src/shared/interfaces/Configuration.tsx | 1 + src/shared/utils/firebase.tsx | 1 + src/shared/utils/tests/mockConfig.ts | 1 + 12 files changed, 191 insertions(+) create mode 100644 public/firebase-messaging-sw.js create mode 100644 public/logo.png create mode 100644 src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx create mode 100644 src/pages/App/handlers/NotificationsHandler/index.ts create mode 100644 src/services/Notification.ts diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js new file mode 100644 index 000000000..8d7336b2e --- /dev/null +++ b/public/firebase-messaging-sw.js @@ -0,0 +1,39 @@ +/* eslint-disable */ +// firebase-messaging-sw.js +importScripts( + "https://www.gstatic.com/firebasejs/10.1.0/firebase-app-compat.js", +); +importScripts( + "https://www.gstatic.com/firebasejs/10.1.0/firebase-messaging-compat.js", +); + +let firebaseConfig = {}; + +self.addEventListener("message", (event) => { + if (event.data && event.data.type === "INIT_ENV") { + firebaseConfig = event.data.env; + initializeFirebase(); + } +}); + +function initializeFirebase() { + if (firebaseConfig.apiKey) { + firebase.initializeApp(firebaseConfig); + + const messaging = firebase.messaging(); + + messaging.onBackgroundMessage((payload) => { + const notificationTitle = payload.notification.title; + const notificationOptions = { + body: payload.notification.body, + data: payload.data, + icon: "/logo.png", + }; + + self.registration.showNotification( + notificationTitle, + notificationOptions, + ); + }); + } +} diff --git a/public/logo.png b/public/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..94e4735b4d9f056089265ae2b003d2b324fbedfd GIT binary patch literal 21322 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¥(;3@vm6LFN^T8keiynulZy_ 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 { + diff --git a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx new file mode 100644 index 000000000..ba2f8c1cf --- /dev/null +++ b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx @@ -0,0 +1,58 @@ +import { FC, useEffect, useState } from "react"; +import { useSelector } from "react-redux"; +import firebaseConfig from "@/config"; +import { selectUser } from "@/pages/Auth/store/selectors"; +import { NotificationService } from "@/services"; + +const NotificationsHandler: FC = () => { + const user = useSelector(selectUser()); + const [isRegistered, setIsRegistered] = useState(false); + const userId = user?.uid; + + useEffect(() => { + NotificationService.requestPermissions(); + }, []); + + useEffect(() => { + if ("serviceWorker" in navigator) { + const unsubscribe = window.addEventListener("load", () => { + navigator.serviceWorker + .register("/firebase-messaging-sw.js") + .then((registration) => { + console.log( + "ServiceWorker registration successful with scope: ", + registration.scope, + ); + registration.active?.postMessage({ + type: "INIT_ENV", + env: firebaseConfig.firebase, + }); + setIsRegistered(true); + return registration; + }) + .catch((err) => { + console.log("ServiceWorker registration failed: ", err); + }); + }); + + return unsubscribe; + } + }, [setIsRegistered]); + + useEffect(() => { + console.log("--userId", userId, "--isRegistered", isRegistered); + if (!userId || !isRegistered) { + return; + } + + NotificationService.saveFCMToken(); + + const unsubscribe = NotificationService.onForegroundMessage(); + + return unsubscribe; + }, [userId, isRegistered]); + + return null; +}; + +export default NotificationsHandler; diff --git a/src/pages/App/handlers/NotificationsHandler/index.ts b/src/pages/App/handlers/NotificationsHandler/index.ts new file mode 100644 index 000000000..c7c53200f --- /dev/null +++ b/src/pages/App/handlers/NotificationsHandler/index.ts @@ -0,0 +1 @@ +export { default as NotificationsHandler } from "./NotificationsHandler"; \ No newline at end of file diff --git a/src/pages/App/handlers/index.ts b/src/pages/App/handlers/index.ts index 712e7a4d4..37e8eb0fb 100644 --- a/src/pages/App/handlers/index.ts +++ b/src/pages/App/handlers/index.ts @@ -3,3 +3,4 @@ export * from "./TextDirectionHandler"; export * from "./UserNotificationsAmountHandler"; export * from "./WebViewLoginHandler"; export * from "./ThemeHandler"; +export * from "./NotificationsHandler"; diff --git a/src/services/Notification.ts b/src/services/Notification.ts new file mode 100644 index 000000000..47e03a088 --- /dev/null +++ b/src/services/Notification.ts @@ -0,0 +1,78 @@ +import firebase from "@/shared/utils/firebase"; +import firebaseConfig from "@/config"; +import Api from "./Api"; + +enum NOTIFICATIONS_PERMISSIONS { + DEFAULT = "default", + DENIED = "denied", + GRANTED = "granted" +} + + +class NotificationService { + private endpoints: { + setFCMToken: string; + }; + + constructor() { + this.endpoints = { + setFCMToken: '/users/auth/google/set-fcm-token', + }; + } + + public requestPermissions = async (): Promise => { + try { + const permission = await Notification.requestPermission(); + if (permission === NOTIFICATIONS_PERMISSIONS.GRANTED) { + console.log('Notification permission granted.'); + return true; + } else { + return false; + } + } catch (err) { + return false; + } + } + + public saveFCMToken = async (): Promise => { + try { + console.log('-hastPermissions', firebaseConfig.vapidKey); + const token = await firebase.messaging().getToken({ vapidKey: firebaseConfig.vapidKey }); + if (token) { + try { + console.log("FCM Token:", token); + + await Api.post( + this.endpoints.setFCMToken, + { + token, + } + ); + } catch (error) { + console.error("An error occurred while retrieving token. ", error); + } + } else { + console.log("No registration token available. Request permission to generate one."); + } + } catch (error) { + console.error("An error occurred while retrieving token. ", error); + } + } + + public onForegroundMessage = () => { + return firebase.messaging().onMessage((payload) => { + console.log('Message received. ', payload); + + const { title, body } = payload.notification; + if (Notification.permission === 'granted') { + new Notification(title, { + body, + data: payload.data, + icon: '/logo.png', // Replace with your icon + }); + } + }); + } +} + +export default new NotificationService(); diff --git a/src/services/index.ts b/src/services/index.ts index 03d9bf706..bf6d10f1e 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -25,3 +25,4 @@ export { } from "./DiscussionMessage"; export { default as NotionService } from "./Notion"; export { default as FeatureFlagService } from "./FeatureFlag"; +export { default as NotificationService } from "./Notification"; diff --git a/src/shared/interfaces/Configuration.tsx b/src/shared/interfaces/Configuration.tsx index 94c71bcfc..73f6e96ef 100644 --- a/src/shared/interfaces/Configuration.tsx +++ b/src/shared/interfaces/Configuration.tsx @@ -15,6 +15,7 @@ export interface Configuration { deadSeaCommonId: string; parentsForClimateCommonId: string; saadiaCommonId: string; + vapidKey: string; } export type ConfigurationObject = Record; diff --git a/src/shared/utils/firebase.tsx b/src/shared/utils/firebase.tsx index 3db4544c9..a7f790cf0 100644 --- a/src/shared/utils/firebase.tsx +++ b/src/shared/utils/firebase.tsx @@ -1,6 +1,7 @@ import firebase from "firebase/compat/app"; import "firebase/compat/auth"; import "firebase/compat/firestore"; +import "firebase/compat/messaging"; import "firebase/compat/performance"; import "firebase/compat/storage"; import { getPerformance } from "firebase/performance"; diff --git a/src/shared/utils/tests/mockConfig.ts b/src/shared/utils/tests/mockConfig.ts index 502ef3bcf..8f9410f5a 100644 --- a/src/shared/utils/tests/mockConfig.ts +++ b/src/shared/utils/tests/mockConfig.ts @@ -18,5 +18,6 @@ jest.mock( deadSeaCommonId: "958dca85-7bc1-4714-95bd-1fc6343f0654", parentsForClimateCommonId: "958dca85-7bc1-4714-95bd-1fc6343f0654", saadiaCommonId: "958dca85-7bc1-4714-95bd-1fc6343f0654", + vapidKey: "VAPID_KEY", }) ); From 6b7537e27f47786c8615832ba6cea2845a85f104 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 15 Aug 2024 13:34:04 +0300 Subject: [PATCH 25/28] CW-firebase-messaging-for-web Update settings --- public/firebase-messaging-sw.js | 7 +- .../NotificationsHandler.tsx | 117 ++++++++++++------ src/services/Notification.ts | 11 +- 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js index 8d7336b2e..29db6ca46 100644 --- a/public/firebase-messaging-sw.js +++ b/public/firebase-messaging-sw.js @@ -1,15 +1,16 @@ /* eslint-disable */ // firebase-messaging-sw.js importScripts( - "https://www.gstatic.com/firebasejs/10.1.0/firebase-app-compat.js", + "https://www.gstatic.com/firebasejs/10.7.1/firebase-app-compat.js", ); importScripts( - "https://www.gstatic.com/firebasejs/10.1.0/firebase-messaging-compat.js", + "https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging-compat.js", ); let firebaseConfig = {}; self.addEventListener("message", (event) => { + console.log("--INIT_ENV", event); if (event.data && event.data.type === "INIT_ENV") { firebaseConfig = event.data.env; initializeFirebase(); @@ -18,11 +19,13 @@ self.addEventListener("message", (event) => { function initializeFirebase() { if (firebaseConfig.apiKey) { + console.log("--firebaseConfig", firebaseConfig); firebase.initializeApp(firebaseConfig); const messaging = firebase.messaging(); messaging.onBackgroundMessage((payload) => { + console.log("--notif-back", payload); const notificationTitle = payload.notification.title; const notificationOptions = { body: payload.notification.body, diff --git a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx index ba2f8c1cf..4cde6340f 100644 --- a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx +++ b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState } from "react"; +import { FC, useEffect } from "react"; import { useSelector } from "react-redux"; import firebaseConfig from "@/config"; import { selectUser } from "@/pages/Auth/store/selectors"; @@ -6,51 +6,94 @@ import { NotificationService } from "@/services"; const NotificationsHandler: FC = () => { const user = useSelector(selectUser()); - const [isRegistered, setIsRegistered] = useState(false); const userId = user?.uid; - useEffect(() => { - NotificationService.requestPermissions(); - }, []); - - useEffect(() => { - if ("serviceWorker" in navigator) { - const unsubscribe = window.addEventListener("load", () => { - navigator.serviceWorker - .register("/firebase-messaging-sw.js") - .then((registration) => { - console.log( - "ServiceWorker registration successful with scope: ", - registration.scope, - ); - registration.active?.postMessage({ - type: "INIT_ENV", - env: firebaseConfig.firebase, - }); - setIsRegistered(true); - return registration; - }) - .catch((err) => { - console.log("ServiceWorker registration failed: ", err); - }); - }); - - return unsubscribe; - } - }, [setIsRegistered]); + // useEffect(() => { + // NotificationService.requestPermissions(); + // }, []); useEffect(() => { - console.log("--userId", userId, "--isRegistered", isRegistered); - if (!userId || !isRegistered) { + if (!userId) { return; } - NotificationService.saveFCMToken(); + let unsubscribeOnMessage; + let unsubscribeLoad; + (async () => { + const hasPermissions = await NotificationService.requestPermissions(); + console.log("-hasPermissions", hasPermissions); + if (hasPermissions) { + await NotificationService.saveFCMToken(); + + unsubscribeOnMessage = NotificationService.onForegroundMessage(); + + if ("serviceWorker" in navigator) { + console.log("--here"); + navigator.serviceWorker + .register("/firebase-messaging-sw.js") + .then((registration) => { + console.log( + "ServiceWorker registration successful with scope: ", + registration.scope, + ); + // registration.active?.postMessage({ + // type: "INIT_ENV", + // env: firebaseConfig.firebase, + // }); + + console.log("---registration", registration); + if (registration.active) { + registration.active.postMessage({ + type: "INIT_ENV", + env: firebaseConfig.firebase, + }); + } else if (registration?.installing) { + registration.installing.addEventListener("statechange", () => { + if (registration?.installing?.state === "activated") { + registration.installing.postMessage({ + type: "INIT_ENV", + env: firebaseConfig.firebase, + }); + } + }); + } else if (registration?.waiting) { + registration.waiting.addEventListener("statechange", () => { + if (registration?.waiting?.state === "activated") { + registration.waiting.postMessage({ + type: "INIT_ENV", + env: firebaseConfig.firebase, + }); + } + }); + } + return registration; + }) + .catch((err) => { + console.log("ServiceWorker registration failed: ", err); + }); + unsubscribeLoad = window.addEventListener("load", () => {}); + } + } + })(); + + return () => { + unsubscribeLoad && unsubscribeLoad(); + unsubscribeOnMessage && unsubscribeOnMessage(); + }; + }, [userId]); + + // useEffect(() => { + // console.log("--userId", userId, "--isRegistered", isRegistered); + // if (!userId || !isRegistered) { + // return; + // } + + // NotificationService.saveFCMToken(); - const unsubscribe = NotificationService.onForegroundMessage(); + // const unsubscribe = NotificationService.onForegroundMessage(); - return unsubscribe; - }, [userId, isRegistered]); + // return unsubscribe; + // }, [userId, isRegistered]); return null; }; diff --git a/src/services/Notification.ts b/src/services/Notification.ts index 47e03a088..f5cf00a7a 100644 --- a/src/services/Notification.ts +++ b/src/services/Notification.ts @@ -1,6 +1,7 @@ import firebase from "@/shared/utils/firebase"; import firebaseConfig from "@/config"; import Api from "./Api"; +import { NODATA } from "dns"; enum NOTIFICATIONS_PERMISSIONS { DEFAULT = "default", @@ -22,6 +23,9 @@ class NotificationService { public requestPermissions = async (): Promise => { try { + if(Notification.permission === NOTIFICATIONS_PERMISSIONS.GRANTED) { + return true; + } const permission = await Notification.requestPermission(); if (permission === NOTIFICATIONS_PERMISSIONS.GRANTED) { console.log('Notification permission granted.'); @@ -60,18 +64,21 @@ class NotificationService { } public onForegroundMessage = () => { - return firebase.messaging().onMessage((payload) => { + console.log('-subscribe'); + const unsubscribe = firebase.messaging().onMessage((payload) => { console.log('Message received. ', payload); const { title, body } = payload.notification; if (Notification.permission === 'granted') { new Notification(title, { body, - data: payload.data, + data: payload?.data, icon: '/logo.png', // Replace with your icon }); } }); + + return unsubscribe; } } From 92e2dfc771851b95dcdd27ebbed2c1e232cc4aad Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Thu, 15 Aug 2024 16:09:33 +0300 Subject: [PATCH 26/28] CW-firebase-messaging-for-web Updated config --- public/firebase-messaging-sw.js | 59 ++++++++++--------- .../NotificationsHandler.tsx | 8 +-- src/services/Notification.ts | 2 +- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js index 29db6ca46..0b5d82416 100644 --- a/public/firebase-messaging-sw.js +++ b/public/firebase-messaging-sw.js @@ -7,36 +7,41 @@ importScripts( "https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging-compat.js", ); -let firebaseConfig = {}; +let firebaseConfig = { + apiKey: "AIzaSyDbTFuksgOkIVWDiFe_HG7-BE8X6Dwsg-0", + authDomain: "common-dev-34b09.firebaseapp.com", + databaseURL: "https://common-dev-34b09.firebaseio.com", + projectId: "common-dev-34b09", + storageBucket: "common-dev-34b09.appspot.com", + messagingSenderId: "870639147922", + appId: "1:870639147922:web:9ee954bb1dd52e25cb7f4b", +}; -self.addEventListener("message", (event) => { - console.log("--INIT_ENV", event); - if (event.data && event.data.type === "INIT_ENV") { - firebaseConfig = event.data.env; - initializeFirebase(); - } -}); +// self.addEventListener("message", (event) => { +// console.log("--INIT_ENV", event); +// if (event.data && event.data.type === "INIT_ENV") { +// firebaseConfig = event.data.env; +// initializeFirebase(); +// } +// }); -function initializeFirebase() { - if (firebaseConfig.apiKey) { - console.log("--firebaseConfig", firebaseConfig); - firebase.initializeApp(firebaseConfig); +// function initializeFirebase() { +if (firebaseConfig.apiKey) { + console.log("--firebaseConfig", firebaseConfig); + firebase.initializeApp(firebaseConfig); - const messaging = firebase.messaging(); + const messaging = firebase.messaging(); - messaging.onBackgroundMessage((payload) => { - console.log("--notif-back", payload); - const notificationTitle = payload.notification.title; - const notificationOptions = { - body: payload.notification.body, - data: payload.data, - icon: "/logo.png", - }; + messaging.onBackgroundMessage((payload) => { + console.log("--notif-back", payload); + const notificationTitle = payload.notification.title; + const notificationOptions = { + body: payload.notification.body, + data: payload.data, + icon: "/logo.png", + }; - self.registration.showNotification( - notificationTitle, - notificationOptions, - ); - }); - } + self.registration.showNotification(notificationTitle, notificationOptions); + }); } +// } diff --git a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx index 4cde6340f..db19bce43 100644 --- a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx +++ b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx @@ -76,10 +76,10 @@ const NotificationsHandler: FC = () => { } })(); - return () => { - unsubscribeLoad && unsubscribeLoad(); - unsubscribeOnMessage && unsubscribeOnMessage(); - }; + // return () => { + // unsubscribeLoad && unsubscribeLoad(); + // unsubscribeOnMessage && unsubscribeOnMessage(); + // }; }, [userId]); // useEffect(() => { diff --git a/src/services/Notification.ts b/src/services/Notification.ts index f5cf00a7a..ad86ad403 100644 --- a/src/services/Notification.ts +++ b/src/services/Notification.ts @@ -64,7 +64,7 @@ class NotificationService { } public onForegroundMessage = () => { - console.log('-subscribe'); + console.log('-subscribe', firebase.messaging().onMessage); const unsubscribe = firebase.messaging().onMessage((payload) => { console.log('Message received. ', payload); From a71b7cadee0dbea073162c406c14650845685068 Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Fri, 16 Aug 2024 13:34:45 +0300 Subject: [PATCH 27/28] CW-firebase-messaging-for-web Fixed env issue for service worker --- public/firebase-messaging-sw.js | 105 ++++++++++++------ .../NotificationsHandler.tsx | 93 ++++------------ src/services/Notification.ts | 26 ++--- 3 files changed, 97 insertions(+), 127 deletions(-) diff --git a/public/firebase-messaging-sw.js b/public/firebase-messaging-sw.js index 0b5d82416..6d0bd55e6 100644 --- a/public/firebase-messaging-sw.js +++ b/public/firebase-messaging-sw.js @@ -7,41 +7,76 @@ importScripts( "https://www.gstatic.com/firebasejs/10.7.1/firebase-messaging-compat.js", ); -let firebaseConfig = { - apiKey: "AIzaSyDbTFuksgOkIVWDiFe_HG7-BE8X6Dwsg-0", - authDomain: "common-dev-34b09.firebaseapp.com", - databaseURL: "https://common-dev-34b09.firebaseio.com", - projectId: "common-dev-34b09", - storageBucket: "common-dev-34b09.appspot.com", - messagingSenderId: "870639147922", - appId: "1:870639147922:web:9ee954bb1dd52e25cb7f4b", +const ENV = { + LOCAL: "http://localhost:3000", + DEV: "https://web-dev.common.io", + STAGE: "https://web-staging.common.io", + PRODUCTION: "https://common.io", }; -// self.addEventListener("message", (event) => { -// console.log("--INIT_ENV", event); -// if (event.data && event.data.type === "INIT_ENV") { -// firebaseConfig = event.data.env; -// initializeFirebase(); -// } -// }); - -// function initializeFirebase() { -if (firebaseConfig.apiKey) { - console.log("--firebaseConfig", firebaseConfig); - firebase.initializeApp(firebaseConfig); - - const messaging = firebase.messaging(); - - messaging.onBackgroundMessage((payload) => { - console.log("--notif-back", payload); - const notificationTitle = payload.notification.title; - const notificationOptions = { - body: payload.notification.body, - data: payload.data, - icon: "/logo.png", - }; - - self.registration.showNotification(notificationTitle, notificationOptions); - }); +const FIREBASE_CONFIG_ENV = { + DEV: { + apiKey: "AIzaSyDbTFuksgOkIVWDiFe_HG7-BE8X6Dwsg-0", + authDomain: "common-dev-34b09.firebaseapp.com", + databaseURL: "https://common-dev-34b09.firebaseio.com", + projectId: "common-dev-34b09", + storageBucket: "common-dev-34b09.appspot.com", + messagingSenderId: "870639147922", + appId: "1:870639147922:web:9ee954bb1dd52e25cb7f4b", + }, + STAGE: { + apiKey: "AIzaSyBASCWJMV64mZJObeFEitLmdUC1HqmtjJk", + authDomain: "common-staging-1d426.firebaseapp.com", + databaseURL: "https://common-staging-1d426.firebaseio.com", + projectId: "common-staging-1d426", + storageBucket: "common-staging-1d426.appspot.com", + messagingSenderId: "701579202562", + appId: "1:701579202562:web:5729d8a875f98f6709571b", + }, + PRODUCTION: { + apiKey: "AIzaSyAlYrKLd6KNKVkhmNEMKfb0cWHSWicCBOY", + authDomain: "common-production-67641.firebaseapp.com", + databaseURL: "https://common-production-67641.firebaseio.com", + projectId: "common-production-67641", + storageBucket: "common-production-67641.appspot.com", + messagingSenderId: "461029494046", + appId: "1:461029494046:web:4e2e4afbbeb7b487b48d0f", + }, +}; + +let firebaseConfig = {}; + +switch (location.origin) { + case ENV.LOCAL: + case ENV.DEV: { + firebaseConfig = FIREBASE_CONFIG_ENV.DEV; + break; + } + case ENV.STAGE: { + firebaseConfig = FIREBASE_CONFIG_ENV.STAGE; + break; + } + case ENV.PRODUCTION: { + firebaseConfig = FIREBASE_CONFIG_ENV.PRODUCTION; + break; + } + default: { + firebaseConfig = FIREBASE_CONFIG_ENV.DEV; + break; + } } -// } + +firebase.initializeApp(firebaseConfig); + +const messaging = firebase.messaging(); + +messaging.onBackgroundMessage((payload) => { + const notificationTitle = payload.notification.title; + const notificationOptions = { + body: payload.notification.body, + data: payload.data, + icon: "/logo.png", + }; + + self.registration.showNotification(notificationTitle, notificationOptions); +}); diff --git a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx index db19bce43..885102c75 100644 --- a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx +++ b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx @@ -1,99 +1,46 @@ -import { FC, useEffect } from "react"; +import { FC, useEffect, useState } from "react"; import { useSelector } from "react-redux"; -import firebaseConfig from "@/config"; import { selectUser } from "@/pages/Auth/store/selectors"; import { NotificationService } from "@/services"; const NotificationsHandler: FC = () => { const user = useSelector(selectUser()); const userId = user?.uid; + const [isRegistered, setIsRegistered] = useState(false); - // useEffect(() => { - // NotificationService.requestPermissions(); - // }, []); + useEffect(() => { + if ("serviceWorker" in navigator) { + navigator.serviceWorker + .register("/firebase-messaging-sw-dev.js") + .then((registration) => { + setIsRegistered(true); + return registration; + }) + .catch((err) => { + console.log("ServiceWorker registration failed: ", err); + }); + } + }, []); useEffect(() => { - if (!userId) { + if (!userId && !isRegistered) { return; } let unsubscribeOnMessage; - let unsubscribeLoad; (async () => { const hasPermissions = await NotificationService.requestPermissions(); - console.log("-hasPermissions", hasPermissions); if (hasPermissions) { await NotificationService.saveFCMToken(); unsubscribeOnMessage = NotificationService.onForegroundMessage(); - - if ("serviceWorker" in navigator) { - console.log("--here"); - navigator.serviceWorker - .register("/firebase-messaging-sw.js") - .then((registration) => { - console.log( - "ServiceWorker registration successful with scope: ", - registration.scope, - ); - // registration.active?.postMessage({ - // type: "INIT_ENV", - // env: firebaseConfig.firebase, - // }); - - console.log("---registration", registration); - if (registration.active) { - registration.active.postMessage({ - type: "INIT_ENV", - env: firebaseConfig.firebase, - }); - } else if (registration?.installing) { - registration.installing.addEventListener("statechange", () => { - if (registration?.installing?.state === "activated") { - registration.installing.postMessage({ - type: "INIT_ENV", - env: firebaseConfig.firebase, - }); - } - }); - } else if (registration?.waiting) { - registration.waiting.addEventListener("statechange", () => { - if (registration?.waiting?.state === "activated") { - registration.waiting.postMessage({ - type: "INIT_ENV", - env: firebaseConfig.firebase, - }); - } - }); - } - return registration; - }) - .catch((err) => { - console.log("ServiceWorker registration failed: ", err); - }); - unsubscribeLoad = window.addEventListener("load", () => {}); - } } })(); - // return () => { - // unsubscribeLoad && unsubscribeLoad(); - // unsubscribeOnMessage && unsubscribeOnMessage(); - // }; - }, [userId]); - - // useEffect(() => { - // console.log("--userId", userId, "--isRegistered", isRegistered); - // if (!userId || !isRegistered) { - // return; - // } - - // NotificationService.saveFCMToken(); - - // const unsubscribe = NotificationService.onForegroundMessage(); - - // return unsubscribe; - // }, [userId, isRegistered]); + return () => { + unsubscribeOnMessage && unsubscribeOnMessage(); + }; + }, [userId, isRegistered]); return null; }; diff --git a/src/services/Notification.ts b/src/services/Notification.ts index ad86ad403..25e7597b3 100644 --- a/src/services/Notification.ts +++ b/src/services/Notification.ts @@ -1,7 +1,6 @@ import firebase from "@/shared/utils/firebase"; import firebaseConfig from "@/config"; import Api from "./Api"; -import { NODATA } from "dns"; enum NOTIFICATIONS_PERMISSIONS { DEFAULT = "default", @@ -28,7 +27,6 @@ class NotificationService { } const permission = await Notification.requestPermission(); if (permission === NOTIFICATIONS_PERMISSIONS.GRANTED) { - console.log('Notification permission granted.'); return true; } else { return false; @@ -40,23 +38,15 @@ class NotificationService { public saveFCMToken = async (): Promise => { try { - console.log('-hastPermissions', firebaseConfig.vapidKey); const token = await firebase.messaging().getToken({ vapidKey: firebaseConfig.vapidKey }); if (token) { - try { - console.log("FCM Token:", token); - await Api.post( - this.endpoints.setFCMToken, - { - token, - } - ); - } catch (error) { - console.error("An error occurred while retrieving token. ", error); - } - } else { - console.log("No registration token available. Request permission to generate one."); + await Api.post( + this.endpoints.setFCMToken, + { + token, + } + ); } } catch (error) { console.error("An error occurred while retrieving token. ", error); @@ -64,16 +54,14 @@ class NotificationService { } public onForegroundMessage = () => { - console.log('-subscribe', firebase.messaging().onMessage); const unsubscribe = firebase.messaging().onMessage((payload) => { - console.log('Message received. ', payload); const { title, body } = payload.notification; if (Notification.permission === 'granted') { new Notification(title, { body, data: payload?.data, - icon: '/logo.png', // Replace with your icon + icon: "/logo.png", }); } }); From de573f6fcf0756edf95fe88a6d8e5c1fd409a80b Mon Sep 17 00:00:00 2001 From: Pavel Meyer Date: Fri, 16 Aug 2024 20:06:41 +0300 Subject: [PATCH 28/28] CW-latest-PRs Fix service worker name --- .../App/handlers/NotificationsHandler/NotificationsHandler.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx index 885102c75..de144355b 100644 --- a/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx +++ b/src/pages/App/handlers/NotificationsHandler/NotificationsHandler.tsx @@ -11,7 +11,7 @@ const NotificationsHandler: FC = () => { useEffect(() => { if ("serviceWorker" in navigator) { navigator.serviceWorker - .register("/firebase-messaging-sw-dev.js") + .register("/firebase-messaging-sw.js") .then((registration) => { setIsRegistered(true); return registration;