From 045a49632d33427c94cca3b3ae349445b6880e13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nguy=E1=BB=85n=20Ho=C3=A0i=20Danh?= <33005392+nguyenhoaidanh@users.noreply.github.com> Date: Fri, 29 Sep 2023 09:59:02 +0700 Subject: [PATCH] Kyberai filter sort (#2182) * some improvements * improve for charts dont have data * add timeframe switcher in price chart * add new static farm contract (#2166) * improve token list (#2157) * improve token list * revert tooltip text * fix: reward is wrapped token (#2167) * update new contract (#2168) * fix(farm-v2): user info does not show (#2169) * refactor screen size show menu (#2171) * refactor ks setting use rtk query (#2170) * fix: keep position which still have reward (#2173) * refactor: split kyberAI tracking(#2174) * refactor: handle rejected tx & messages (#2172) * feat: add zkevm (#2152) * feat: add zkevm * point to prod * update subgraph link * fix minor bug * update logo chain * fix: zksync path * fix: build * update farm contract * revert env.adpr * Pool farm improvement (#2178) * feat: show static farm total reward * fix: addliquidity multiple pos for farm * env proid * revert env * remove: token info (#2180) * [QA-36] E2E - Add other EVM chains (#2131) * setup set up set up * css layout + select network * add sort arrow * set up sort * update layout * UI for new custom watchlist * css mobile * fix warning * rm import * update custom watchlist for list page * split tab btn * chore * update styled * update assetId and hooks to use new params * wip * wip * done ui, wait for api * add variant select in explore page * revert watchlist updates * fix failed errors * fix conflict * rename * fix issues * update select variant * fix hide button not works * fix bugs * remove console.log * fix header gap not consistent * connect api categories * use bff api * chore: move folder noti * refactor * navigate to explore page using provided chain and address * improve network select * fix kyberai banner fetch api event user closed it * connect real type * wip * update template * resolve conflict * build * fix: css * fix error * fix format fix score c chore * kyber calc at * revert s s r * fix: when asset too small * add template type * refactor type * check for elastic pool * resolve conflict * fix warning --------- Co-authored-by: XiaoYhun Co-authored-by: Nguyen Van Viet Co-authored-by: Nam Nguyen Co-authored-by: ltthienn <132639843+ltthienn@users.noreply.github.com> --- .../Announcement/AnnoucementView.tsx | 2 +- .../Announcement/PrivateAnnoucement/Icon.tsx | 1 + ...mTrendingSoon.tsx => InboxItemKyberAI.tsx} | 55 +- .../InboxItemKyberAIWatchList.tsx | 97 +++ .../{TrendingSoon.tsx => KyberAI.tsx} | 14 +- .../NotificationCenter/KyberAIWatchlist.tsx | 70 ++ .../NotificationCenter/PoolPosition.tsx | 19 +- .../NotificationCenter/index.tsx | 6 +- .../NotificationCenter/styled.tsx | 17 + .../Announcement/PrivateAnnoucement/index.tsx | 13 +- .../PrivateAnnoucement/styled.tsx | 2 +- src/components/Announcement/index.tsx | 27 +- src/components/Announcement/type.ts | 19 +- src/components/Logo/index.tsx | 12 + src/components/PoolList/index.tsx | 7 +- src/components/Select.tsx | 6 +- .../NotificationPreference/Header.tsx | 37 - .../Transactions/DeltaTokenAmount.tsx | 14 +- .../YieldPools/ElasticFarmGroup/index.tsx | 7 +- src/constants/env.ts | 11 +- src/constants/index.ts | 5 + src/hooks/useNotification/index.ts | 3 + src/pages/Farm/ElasticFarmv2/index.tsx | 3 +- src/pages/MyEarnings/ChainSelect.tsx | 18 +- .../MultipleChainSelect/PopoverBody.tsx | 75 +- .../MultipleChainSelect/SelectButton.tsx | 43 +- .../MyEarnings/MultipleChainSelect/index.tsx | 34 +- src/pages/NotificationCenter/Menu/index.tsx | 16 +- .../NotificationPreference/ActionButtons.tsx | 0 .../NotificationPreference/InputEmail.tsx | 0 .../NotificationPreference/index.tsx | 65 +- src/pages/NotificationCenter/Overview.tsx | 28 - .../PrivateAnnouncement.tsx | 4 +- .../NotificationCenter/Profile/index.tsx | 4 +- src/pages/NotificationCenter/const.ts | 1 + src/pages/NotificationCenter/index.tsx | 6 +- src/pages/ProAmmPools/index.tsx | 6 +- .../TrueSightV2/components/FeedbackSurvey.tsx | 4 +- .../TrueSightV2/components/KyberAIWidget.tsx | 1 - .../TrueSightV2/components/NetworkSelect.tsx | 144 ---- .../components/SubscireButtonKyberAI.tsx | 26 + .../TrueSightV2/components/TokenFilter.tsx | 238 ++++++ .../TrueSightV2/components/TokenOverview.tsx | 1 + src/pages/TrueSightV2/components/index.tsx | 2 +- .../TrueSightV2/components/table/index.tsx | 6 +- src/pages/TrueSightV2/constants/index.tsx | 6 + .../TrueSightV2/hooks/useKyberAIData.tsx | 34 +- src/pages/TrueSightV2/index.tsx | 23 +- .../TrueSightV2/pages/TokenAnalysisList.tsx | 736 +++++++++--------- src/pages/TrueSightV2/types/index.tsx | 9 + src/pages/TrueSightV2/utils/index.tsx | 10 + src/services/announcement.ts | 16 +- src/services/baseQueryOauth.ts | 2 +- src/state/farms/elasticv2/hooks.ts | 7 +- src/utils/errorMessage.ts | 6 +- src/utils/string.ts | 5 + 56 files changed, 1165 insertions(+), 858 deletions(-) rename src/components/Announcement/PrivateAnnoucement/{InboxItemTrendingSoon.tsx => InboxItemKyberAI.tsx} (70%) create mode 100644 src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx rename src/components/Announcement/PrivateAnnoucement/NotificationCenter/{TrendingSoon.tsx => KyberAI.tsx} (69%) create mode 100644 src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx delete mode 100644 src/components/SubscribeButton/NotificationPreference/Header.tsx rename src/{components/SubscribeButton => pages/NotificationCenter}/NotificationPreference/ActionButtons.tsx (100%) rename src/{components/SubscribeButton => pages/NotificationCenter}/NotificationPreference/InputEmail.tsx (100%) rename src/{components/SubscribeButton => pages/NotificationCenter}/NotificationPreference/index.tsx (92%) delete mode 100644 src/pages/NotificationCenter/Overview.tsx delete mode 100644 src/pages/TrueSightV2/components/NetworkSelect.tsx create mode 100644 src/pages/TrueSightV2/components/SubscireButtonKyberAI.tsx create mode 100644 src/pages/TrueSightV2/components/TokenFilter.tsx diff --git a/src/components/Announcement/AnnoucementView.tsx b/src/components/Announcement/AnnoucementView.tsx index d28362f447..b7503691ab 100644 --- a/src/components/Announcement/AnnoucementView.tsx +++ b/src/components/Announcement/AnnoucementView.tsx @@ -252,7 +252,7 @@ export default function AnnouncementView({ height={height} width={width} itemCount={itemCount} - itemSize={isMyInboxTab ? 116 : 126} + itemSize={isMyInboxTab ? 120 : 126} onItemsRendered={onItemsRendered} ref={ref} > diff --git a/src/components/Announcement/PrivateAnnoucement/Icon.tsx b/src/components/Announcement/PrivateAnnoucement/Icon.tsx index 7f7e7216dc..b188d28fc0 100644 --- a/src/components/Announcement/PrivateAnnoucement/Icon.tsx +++ b/src/components/Announcement/PrivateAnnoucement/Icon.tsx @@ -25,6 +25,7 @@ const mapIcon: Partial<{ [type in PrivateAnnouncementType]: ReactNode }> = { [PrivateAnnouncementType.LIMIT_ORDER]: , [PrivateAnnouncementType.ELASTIC_POOLS]: , [PrivateAnnouncementType.KYBER_AI]: , + [PrivateAnnouncementType.KYBER_AI_WATCHLIST]: , [PrivateAnnouncementType.PRICE_ALERT]: , [PrivateAnnouncementType.DIRECT_MESSAGE]: , } diff --git a/src/components/Announcement/PrivateAnnoucement/InboxItemTrendingSoon.tsx b/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAI.tsx similarity index 70% rename from src/components/Announcement/PrivateAnnoucement/InboxItemTrendingSoon.tsx rename to src/components/Announcement/PrivateAnnoucement/InboxItemKyberAI.tsx index 86892a1b45..aa33e2f442 100644 --- a/src/components/Announcement/PrivateAnnoucement/InboxItemTrendingSoon.tsx +++ b/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAI.tsx @@ -1,14 +1,15 @@ import { Trans } from '@lingui/macro' import { useNavigate } from 'react-router-dom' +import { Flex, Text } from 'rebass' import styled from 'styled-components' import { PrivateAnnouncementProp } from 'components/Announcement/PrivateAnnoucement' import InboxIcon from 'components/Announcement/PrivateAnnoucement/Icon' import { Dot, InboxItemRow, InboxItemWrapper, RowItem, Title } from 'components/Announcement/PrivateAnnoucement/styled' -import { AnnouncementTemplateTrendingSoon } from 'components/Announcement/type' +import { AnnouncementTemplateKyberAI } from 'components/Announcement/type' import DiscoverIcon from 'components/Icons/DiscoverIcon' import Icon from 'components/Icons/Icon' -import DeltaTokenAmount from 'components/WalletPopup/Transactions/DeltaTokenAmount' +import { TokenLogoWithShadow } from 'components/Logo' import { APP_PATHS } from 'constants/index' import useTheme from 'hooks/useTheme' @@ -18,12 +19,31 @@ const ItemWrapper = styled.div<{ color: string }>` gap: 4px; color: ${({ color }) => color}; ` +const TokenDetail = ({ + color, + symbol, + logoURL, + fontSize, +}: { + color: string + logoURL: string + symbol: string + fontSize: string +}) => ( + + + {symbol} + +) + export const TokenInfo = ({ templateBody, type, + fontSize = '12px', }: { - templateBody: AnnouncementTemplateTrendingSoon + templateBody: AnnouncementTemplateKyberAI type: 'bullish' | 'bearish' | 'trending' + fontSize?: string }) => { const theme = useTheme() const { @@ -41,9 +61,12 @@ export const TokenInfo = ({ case 'bullish': return ( - Bullish: - {' '} + + Bullish: + + - Bearish: - {' '} + + Bearish: + + - Trending Soon: - {' '} + + Trending Soon: + + ) { +}: PrivateAnnouncementProp) { const { templateBody, isRead, templateType } = announcement const navigate = useNavigate() diff --git a/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx b/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx new file mode 100644 index 0000000000..c1e091c5f6 --- /dev/null +++ b/src/components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList.tsx @@ -0,0 +1,97 @@ +import { useNavigate } from 'react-router-dom' +import { Text } from 'rebass' +import styled from 'styled-components' + +import { PrivateAnnouncementProp } from 'components/Announcement/PrivateAnnoucement' +import InboxIcon from 'components/Announcement/PrivateAnnoucement/Icon' +import { Dot, InboxItemRow, InboxItemWrapper, RowItem, Title } from 'components/Announcement/PrivateAnnoucement/styled' +import { AnnouncementTemplateKyberAIWatchlist, TokenInfoWatchlist } from 'components/Announcement/type' +import Column from 'components/Column' +import { TokenLogoWithShadow } from 'components/Logo' +import { APP_PATHS } from 'constants/index' +import useTheme from 'hooks/useTheme' +import { calculateValueToColor, getTypeByKyberScore } from 'pages/TrueSightV2/utils' +import { formatDisplayNumber } from 'utils/numbers' + +const ItemWrapper = styled.div` + display: flex; + align-items: center; + gap: 8px; +` +export const TokenInfo = ({ + showPrice = true, + logoSize = '12px', + token, +}: { + showPrice?: boolean + logoSize?: string + token: TokenInfoWatchlist +}) => { + const theme = useTheme() + const { logoURL, symbol, price, priceChange, kyberScore } = token || {} + return ( + + + + + {symbol}{' '} + + {kyberScore} ({getTypeByKyberScore(+kyberScore)}) + + + {showPrice && ( + + + {formatDisplayNumber(price, { style: 'currency', significantDigits: 4 })} + {' '} + 0 ? theme.apr : theme.red}> + ({+priceChange > 0 && '+'} + {formatDisplayNumber(+priceChange / 100, { style: 'percent', fractionDigits: 2, allowNegative: true })}) + + + )} + + + ) +} + +function InboxItemBridge({ + announcement, + onRead, + style, + time, + title, +}: PrivateAnnouncementProp) { + const { templateBody, isRead, templateType } = announcement + const { assets = [] } = templateBody || {} + const [token1, token2, token3] = assets + + const navigate = useNavigate() + const onClick = () => { + navigate(APP_PATHS.KYBERAI_RANKINGS) + onRead(announcement, 'kyberAI_watchlist') + } + + return ( + + + + + {title} + {!isRead && } + + + + + {token1 && } + {token2 && } + + + + {token3 ? :
} + {time} + + + ) +} +export default InboxItemBridge diff --git a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/TrendingSoon.tsx b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAI.tsx similarity index 69% rename from src/components/Announcement/PrivateAnnoucement/NotificationCenter/TrendingSoon.tsx rename to src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAI.tsx index 27caa5ba12..6e39f211f8 100644 --- a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/TrendingSoon.tsx +++ b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAI.tsx @@ -3,9 +3,9 @@ import { useNavigate } from 'react-router-dom' import { Flex } from 'rebass' import InboxIcon from 'components/Announcement/PrivateAnnoucement/Icon' -import { TokenInfo } from 'components/Announcement/PrivateAnnoucement/InboxItemTrendingSoon' +import { TokenInfo } from 'components/Announcement/PrivateAnnoucement/InboxItemKyberAI' import { PrivateAnnouncementPropCenter } from 'components/Announcement/PrivateAnnoucement/NotificationCenter' -import { AnnouncementTemplateTrendingSoon } from 'components/Announcement/type' +import { AnnouncementTemplateKyberAI } from 'components/Announcement/type' import { APP_PATHS } from 'constants/index' import useTheme from 'hooks/useTheme' import { formatTime } from 'utils/time' @@ -15,7 +15,7 @@ import { Desc, Time, Title, Wrapper } from './styled' export default function AnnouncementItem({ announcement, title, -}: PrivateAnnouncementPropCenter) { +}: PrivateAnnouncementPropCenter) { const { sentAt, templateType, templateBody } = announcement const theme = useTheme() const navigate = useNavigate() @@ -32,10 +32,10 @@ export default function AnnouncementItem({ - Here are our top tokens by KyberAI - , - , - + Here are our top tokens by KyberAI: + , + , + ) diff --git a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx new file mode 100644 index 0000000000..c077f940ef --- /dev/null +++ b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/KyberAIWatchlist.tsx @@ -0,0 +1,70 @@ +import { Trans } from '@lingui/macro' +import { Fragment, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { Flex } from 'rebass' +import styled from 'styled-components' + +import { ReactComponent as DropdownSVG } from 'assets/svg/down.svg' +import InboxIcon from 'components/Announcement/PrivateAnnoucement/Icon' +import { TokenInfo } from 'components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList' +import { PrivateAnnouncementPropCenter } from 'components/Announcement/PrivateAnnoucement/NotificationCenter' +import { AnnouncementTemplateKyberAIWatchlist } from 'components/Announcement/type' +import { APP_PATHS } from 'constants/index' +import useTheme from 'hooks/useTheme' +import { formatTime } from 'utils/time' + +import { ArrowWrapper, Desc, Time, Title, Wrapper } from './styled' + +const Detail = styled.div` + display: grid; + width: 100%; + grid-template-columns: 1fr 1fr; + gap: 12px; +` + +export default function AnnouncementItem({ + announcement, + title, +}: PrivateAnnouncementPropCenter) { + const { sentAt, templateType, templateBody } = announcement + const { assets = [] } = templateBody || {} + const theme = useTheme() + const navigate = useNavigate() + const [expand, setExpand] = useState(false) + const slice = 3 + const minimalAssets = assets.slice(0, slice) + + return ( + setExpand(!expand)}> + + navigate(APP_PATHS.KYBERAI_RANKINGS)}> + <InboxIcon type={templateType} /> + {title} + + + + + + + + + + Here is an update on the tokens in your watchlist: + {!expand && + minimalAssets.map((token, i) => ( + + + {i === minimalAssets.length - 1 ? (minimalAssets.length < slice ? '' : ', ...') : ', '} + + ))} + + {expand && ( + + {assets.map((token, i) => ( + + ))} + + )} + + ) +} diff --git a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/PoolPosition.tsx b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/PoolPosition.tsx index 0aca209c00..0bbb9cbec9 100644 --- a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/PoolPosition.tsx +++ b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/PoolPosition.tsx @@ -16,24 +16,7 @@ import { NETWORKS_INFO } from 'constants/networks' import useTheme from 'hooks/useTheme' import { formatTime } from 'utils/time' -import { Desc, Time, Title, Wrapper } from './styled' - -const ArrowWrapper = styled.div` - width: 20px; - height: 20px; - color: ${({ theme }) => theme.subText}; - display: flex; - justify-content: center; - align-items: center; - svg { - transition: all 150ms ease-in-out; - } - &[data-expanded='true'] { - svg { - transform: rotate(180deg); - } - } -` +import { ArrowWrapper, Desc, Time, Title, Wrapper } from './styled' const Detail = styled(Desc)` flex-direction: column; diff --git a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/index.tsx b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/index.tsx index aab6a3ebcc..2550190ed3 100644 --- a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/index.tsx +++ b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/index.tsx @@ -5,11 +5,12 @@ import { AnnouncementTemplate, PrivateAnnouncement, PrivateAnnouncementType } fr import Bridge from './Bridge' import CrossChain from './CrossChain' +import KyberAI from './KyberAI' +import KyberAIWatchlist from './KyberAIWatchlist' import LimitOrder from './LimitOrder' import PoolPosition from './PoolPosition' import PriceAlert from './PriceAlert' import PrivateMessage from './PrivateMessage' -import TrendingSoon from './TrendingSoon' export type PrivateAnnouncementPropCenter = { announcement: PrivateAnnouncement @@ -24,7 +25,8 @@ const ANNOUNCEMENT_MAP_IN_CENTER = { [PrivateAnnouncementType.LIMIT_ORDER]: LimitOrder, [PrivateAnnouncementType.BRIDGE_ASSET]: Bridge, [PrivateAnnouncementType.CROSS_CHAIN]: CrossChain, - [PrivateAnnouncementType.KYBER_AI]: TrendingSoon, + [PrivateAnnouncementType.KYBER_AI]: KyberAI, + [PrivateAnnouncementType.KYBER_AI_WATCHLIST]: KyberAIWatchlist, [PrivateAnnouncementType.PRICE_ALERT]: PriceAlert, [PrivateAnnouncementType.DIRECT_MESSAGE]: PrivateMessage, } as PrivateAnnouncementCenterMap diff --git a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/styled.tsx b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/styled.tsx index 20824946e0..9581011d8d 100644 --- a/src/components/Announcement/PrivateAnnoucement/NotificationCenter/styled.tsx +++ b/src/components/Announcement/PrivateAnnoucement/NotificationCenter/styled.tsx @@ -45,3 +45,20 @@ export const Time = styled.div<{ isLeft?: boolean }>` font-size: 10px; `} ` + +export const ArrowWrapper = styled.div` + width: 20px; + height: 20px; + color: ${({ theme }) => theme.subText}; + display: flex; + justify-content: center; + align-items: center; + svg { + transition: all 150ms ease-in-out; + } + &[data-expanded='true'] { + svg { + transform: rotate(180deg); + } + } +` diff --git a/src/components/Announcement/PrivateAnnoucement/index.tsx b/src/components/Announcement/PrivateAnnoucement/index.tsx index 6883f46404..7597a60537 100644 --- a/src/components/Announcement/PrivateAnnoucement/index.tsx +++ b/src/components/Announcement/PrivateAnnoucement/index.tsx @@ -4,11 +4,12 @@ import { CSSProperties } from 'styled-components' import InboxItemBridge from 'components/Announcement/PrivateAnnoucement/InboxItemBridge' import InboxItemCrossChain from 'components/Announcement/PrivateAnnoucement/InboxItemCrossChain' +import InboxItemTrendingSoon from 'components/Announcement/PrivateAnnoucement/InboxItemKyberAI' +import InboxItemKyberAIWatchList from 'components/Announcement/PrivateAnnoucement/InboxItemKyberAIWatchList' import InboxItemLO from 'components/Announcement/PrivateAnnoucement/InboxItemLO' import InboxItemPoolPosition from 'components/Announcement/PrivateAnnoucement/InboxItemPoolPosition' import InboxItemPriceAlert from 'components/Announcement/PrivateAnnoucement/InboxItemPriceAlert' import InboxItemPrivateMessage from 'components/Announcement/PrivateAnnoucement/InboxItemPrivateMessage' -import InboxItemTrendingSoon from 'components/Announcement/PrivateAnnoucement/InboxItemTrendingSoon' import { InboxItemTime } from 'components/Announcement/PrivateAnnoucement/styled' import { AnnouncementTemplate, PrivateAnnouncement, PrivateAnnouncementType } from 'components/Announcement/type' import useTheme from 'hooks/useTheme' @@ -33,15 +34,17 @@ const ANNOUNCEMENT_MAP: PrivateAnnouncementMap = { [PrivateAnnouncementType.CROSS_CHAIN]: InboxItemCrossChain, [PrivateAnnouncementType.PRICE_ALERT]: InboxItemPriceAlert, [PrivateAnnouncementType.DIRECT_MESSAGE]: InboxItemPrivateMessage, + [PrivateAnnouncementType.KYBER_AI_WATCHLIST]: InboxItemKyberAIWatchList, } as PrivateAnnouncementMap export const PRIVATE_ANN_TITLE: Partial<{ [type in PrivateAnnouncementType]: string }> = { - [PrivateAnnouncementType.ELASTIC_POOLS]: t`Liquidity Pool Alert`, - [PrivateAnnouncementType.LIMIT_ORDER]: t`Limit Order`, + [PrivateAnnouncementType.BRIDGE_ASSET]: t`Cross-Chain Bridge`, [PrivateAnnouncementType.CROSS_CHAIN]: t`Cross-Chain Swaps`, + [PrivateAnnouncementType.LIMIT_ORDER]: t`Limit Orders`, [PrivateAnnouncementType.KYBER_AI]: t`Top Tokens by KyberAI`, - [PrivateAnnouncementType.BRIDGE_ASSET]: t`Cross-Chain Bridge`, - [PrivateAnnouncementType.PRICE_ALERT]: t`Price Alert`, + [PrivateAnnouncementType.KYBER_AI_WATCHLIST]: t`KyberAI Watchlist`, + [PrivateAnnouncementType.PRICE_ALERT]: t`Price Alerts`, + [PrivateAnnouncementType.ELASTIC_POOLS]: t`Elastic Liquidity Positions`, [PrivateAnnouncementType.DIRECT_MESSAGE]: t`Notification`, } diff --git a/src/components/Announcement/PrivateAnnoucement/styled.tsx b/src/components/Announcement/PrivateAnnoucement/styled.tsx index b738664432..86be5d58c6 100644 --- a/src/components/Announcement/PrivateAnnoucement/styled.tsx +++ b/src/components/Announcement/PrivateAnnoucement/styled.tsx @@ -5,7 +5,7 @@ export const InboxItemWrapper = styled.div<{ isRead: boolean }>` border-bottom: 1px solid ${({ theme }) => theme.border}; background-color: ${({ theme }) => theme.background}; font-size: 12px; - padding: 20px; + padding: 20px 18px; gap: 8px; display: flex; flex-direction: column; diff --git a/src/components/Announcement/index.tsx b/src/components/Announcement/index.tsx index 146e5992d8..3abf63be97 100644 --- a/src/components/Announcement/index.tsx +++ b/src/components/Announcement/index.tsx @@ -16,7 +16,6 @@ import NotificationIcon from 'components/Icons/NotificationIcon' import MenuFlyout from 'components/MenuFlyout' import Modal from 'components/Modal' import { RTK_QUERY_TAGS } from 'constants/index' -import { useActiveWeb3React } from 'hooks' import useInterval from 'hooks/useInterval' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import { ApplicationModal } from 'state/application/actions' @@ -82,12 +81,11 @@ const browserCustomStyle = css` const responseDefault = { numberOfUnread: 0, pagination: { totalItems: 0 }, notifications: [] } export default function AnnouncementComponent() { - const { account } = useActiveWeb3React() const [activeTab, setActiveTab] = useState(Tab.ANNOUNCEMENT) const { mixpanelHandler } = useMixpanel() const scrollRef = useRef(null) - const isOpenNotificationCenter = useModalOpen(ApplicationModal.NOTIFICATION_CENTER) + const isOpenInbox = useModalOpen(ApplicationModal.NOTIFICATION_CENTER) const toggleNotificationCenter = useToggleNotificationCenter() const isMobile = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`) @@ -200,19 +198,18 @@ export default function AnnouncementComponent() { } }, [fetchPrivateAnnouncement, invalidateTag, numberOfUnread, userInfo?.identityId]) - const prevOpen = usePrevious(isOpenNotificationCenter) + const prevOpen = usePrevious(isOpenInbox) useEffect(() => { - const justClosedPopup = prevOpen !== isOpenNotificationCenter && !isOpenNotificationCenter + const justClosedPopup = prevOpen !== isOpenInbox && !isOpenInbox if (justClosedPopup) return - // prefetch data prefetchPrivateAnnouncements().then((data: PrivateAnnouncement[]) => { - const newTab = account && data.length ? Tab.INBOX : Tab.ANNOUNCEMENT + const newTab = data.length ? Tab.INBOX : Tab.ANNOUNCEMENT setActiveTab(newTab) - if (prevOpen !== isOpenNotificationCenter && isOpenNotificationCenter) { + if (prevOpen !== isOpenInbox && isOpenInbox) { trackingClickTabRef.current(newTab, 'auto') } - if (isOpenNotificationCenter && newTab === Tab.ANNOUNCEMENT) + if (isOpenInbox && newTab === Tab.ANNOUNCEMENT) fetchGeneralAnnouncement({ page: 1 }) .then(({ data }) => { setAnnouncements((data?.notifications ?? []) as Announcement[]) @@ -221,7 +218,7 @@ export default function AnnouncementComponent() { setAnnouncements([]) }) }) - }, [account, prefetchPrivateAnnouncements, fetchGeneralAnnouncement, prevOpen, isOpenNotificationCenter]) + }, [prefetchPrivateAnnouncements, fetchGeneralAnnouncement, prevOpen, isOpenInbox]) useEffect(() => { if (userInfo?.identityId) { @@ -235,7 +232,7 @@ export default function AnnouncementComponent() { const [readAllAnnouncement] = useAckPrivateAnnouncementsByIdsMutation() const togglePopupWithAckAllMessage = () => { toggleNotificationCenter() - if (isOpenNotificationCenter && numberOfUnread) { + if (isOpenInbox && numberOfUnread) { readAllAnnouncement({}) } } @@ -275,10 +272,10 @@ export default function AnnouncementComponent() { const badgeText = numberOfUnread > 0 ? formatNumberOfUnread(numberOfUnread) : null const bellIcon = ( 0} + active={isOpenInbox || numberOfUnread > 0} onClick={() => { togglePopupWithAckAllMessage() - if (!isOpenNotificationCenter) mixpanelHandler(MIXPANEL_TYPE.ANNOUNCEMENT_CLICK_BELL_ICON_OPEN_POPUP) + if (!isOpenInbox) mixpanelHandler(MIXPANEL_TYPE.ANNOUNCEMENT_CLICK_BELL_ICON_OPEN_POPUP) }} > @@ -290,7 +287,7 @@ export default function AnnouncementComponent() { {isMobile ? ( <> {bellIcon} - + @@ -298,7 +295,7 @@ export default function AnnouncementComponent() { diff --git a/src/components/Announcement/type.ts b/src/components/Announcement/type.ts index c1c97e6dfc..bf3dd8239b 100644 --- a/src/components/Announcement/type.ts +++ b/src/components/Announcement/type.ts @@ -15,6 +15,7 @@ export enum PrivateAnnouncementType { LIMIT_ORDER = 'LIMIT_ORDER', BRIDGE_ASSET = 'BRIDGE_ASSET', KYBER_AI = 'KYBER_AI', + KYBER_AI_WATCHLIST = 'KYBER_AI_WATCHLIST', ELASTIC_POOLS = 'ELASTIC_POOLS', CROSS_CHAIN = 'CROSS_CHAIN', PRICE_ALERT = 'PRICE_ALERT', @@ -71,7 +72,7 @@ export type AnnouncementTemplateLimitOrder = { } export type AnnouncementTemplateCrossChain = { transaction: CrossChainTransfer; popupType: PopupType } export type AnnouncementTemplateBridge = { transaction: MultichainTransfer; popupType: PopupType } -export type AnnouncementTemplateTrendingSoon = { +export type AnnouncementTemplateKyberAI = { bearishTokenLogoURL: string bearishTokenScore: string bearishTokenSymbol: string @@ -83,6 +84,19 @@ export type AnnouncementTemplateTrendingSoon = { trendingSoonTokenSymbol: string popupType: PopupType } + +export type TokenInfoWatchlist = { + logoURL: string + kyberScore: string + symbol: string + price: string + priceChange: string +} +export type AnnouncementTemplateKyberAIWatchlist = { + assets: Array + popupType: PopupType +} + export type AnnouncementTemplatePoolPosition = { position: PoolPositionAnnouncement popupType: PopupType @@ -112,7 +126,8 @@ export type AnnouncementTemplate = | AnnouncementTemplateLimitOrder | AnnouncementTemplateBridge | AnnouncementTemplateCrossChain - | AnnouncementTemplateTrendingSoon + | AnnouncementTemplateKyberAI + | AnnouncementTemplateKyberAIWatchlist | AnnouncementTemplatePoolPosition | AnnouncementTemplatePopup | AnnouncementTemplatePriceAlert diff --git a/src/components/Logo/index.tsx b/src/components/Logo/index.tsx index 409090cd37..6fa096af23 100644 --- a/src/components/Logo/index.tsx +++ b/src/components/Logo/index.tsx @@ -2,6 +2,7 @@ import { ChainId, Currency } from '@kyberswap/ks-sdk-core' import { CSSProperties, useState } from 'react' import { HelpCircle } from 'react-feather' import { ImageProps } from 'rebass' +import styled from 'styled-components' import { useGetNativeTokenLogo } from 'components/CurrencyLogo' import { NETWORKS_INFO } from 'constants/networks' @@ -82,3 +83,14 @@ export function TokenLogoWithChain(data: any) {
) } + +export const TokenLogoWithShadow = styled(Logo)<{ size: string }>` + width: ${({ size }) => size}; + height: ${({ size }) => size}; + border-radius: 100%; + box-shadow: ${({ theme }) => + (() => { + const color = theme.darkMode ? `rgba(256, 256, 256, 0.2)` : `rgba(0, 0, 0, 0.2)` + return `0 4px 5px 0 ${color}, 0 1px 70px 0 ${color};` + })()}; +` diff --git a/src/components/PoolList/index.tsx b/src/components/PoolList/index.tsx index ac2be52b6a..6f13847a56 100644 --- a/src/components/PoolList/index.tsx +++ b/src/components/PoolList/index.tsx @@ -14,7 +14,7 @@ import { Input as PaginationInput } from 'components/Pagination/PaginationInputO import ListItem from 'components/PoolList/ListItem' import ShareModal from 'components/ShareModal' import { ClickableText } from 'components/YieldPools/styleds' -import { AMP_HINT, AMP_LIQUIDITY_HINT, MAX_ALLOW_APY } from 'constants/index' +import { AMP_HINT, AMP_LIQUIDITY_HINT, MAX_ALLOW_APY, SORT_DIRECTION } from 'constants/index' import { useActiveWeb3React } from 'hooks' import { useStableCoins } from 'hooks/Tokens' import { SelectPairInstructionWrapper } from 'pages/Pools/styleds' @@ -97,11 +97,6 @@ enum SORT_FIELD { MY_LIQUIDITY = 'my_liquidity', } -enum SORT_DIRECTION { - ASC = 'asc', - DESC = 'desc', -} - const ITEM_PER_PAGE = 12 const PoolList = ({ currencies, searchValue, isShowOnlyActiveFarmPools, onlyShowStable }: PoolListProps) => { diff --git a/src/components/Select.tsx b/src/components/Select.tsx index 7556725af1..158e974e36 100644 --- a/src/components/Select.tsx +++ b/src/components/Select.tsx @@ -50,7 +50,7 @@ const SelectedWrap = styled.div` text-overflow: ellipsis; flex: 1; ` -type SelectOption = { value?: string | number; label: string | number; onSelect?: () => void } +export type SelectOption = { value?: string | number; label: string | number; onSelect?: () => void } const getOptionValue = (option: SelectOption | undefined) => { if (!option) return '' @@ -67,7 +67,7 @@ function isElementOverflowBottom(el: HTMLElement) { } function Select({ - options, + options = [], activeRender, optionRender, style = {}, @@ -91,7 +91,7 @@ function Select({ forceMenuPlacementTop?: boolean arrowColor?: string }) { - const [selected, setSelected] = useState(getOptionValue(options[0])) + const [selected, setSelected] = useState(getOptionValue(options?.[0])) const [showMenu, setShowMenu] = useState(false) const [menuPlacementTop, setForceMenuPlacementTop] = useState(forceMenuPlacementTop) diff --git a/src/components/SubscribeButton/NotificationPreference/Header.tsx b/src/components/SubscribeButton/NotificationPreference/Header.tsx deleted file mode 100644 index 36dbd37764..0000000000 --- a/src/components/SubscribeButton/NotificationPreference/Header.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Trans, t } from '@lingui/macro' -import { X } from 'react-feather' -import { useNavigate } from 'react-router-dom' -import styled from 'styled-components' - -import MailIcon from 'components/Icons/MailIcon' -import TransactionSettingsIcon from 'components/Icons/TransactionSettingsIcon' -import Row, { RowBetween } from 'components/Row' -import { MouseoverTooltip } from 'components/Tooltip' -import { APP_PATHS } from 'constants/index' -import { PROFILE_MANAGE_ROUTES } from 'pages/NotificationCenter/const' - -const CloseIcon = styled(X)` - cursor: pointer; - color: ${({ theme }) => theme.subText}; -` -export default function Header({ toggleModal }: { toggleModal: () => void }) { - const navigate = useNavigate() - return ( - - - Email Notifications - - - { - navigate(`${APP_PATHS.PROFILE_MANAGE}${PROFILE_MANAGE_ROUTES.PREFERENCE}`) - toggleModal() - }} - /> - - - - ) -} diff --git a/src/components/WalletPopup/Transactions/DeltaTokenAmount.tsx b/src/components/WalletPopup/Transactions/DeltaTokenAmount.tsx index 1c08607b09..cf944912db 100644 --- a/src/components/WalletPopup/Transactions/DeltaTokenAmount.tsx +++ b/src/components/WalletPopup/Transactions/DeltaTokenAmount.tsx @@ -2,7 +2,7 @@ import { ChainId } from '@kyberswap/ks-sdk-core' import { ReactNode } from 'react' import styled, { CSSProperties } from 'styled-components' -import Logo, { TokenLogoWithChain } from 'components/Logo' +import { TokenLogoWithChain, TokenLogoWithShadow } from 'components/Logo' import { PrimaryText } from 'components/WalletPopup/Transactions/TransactionItem' import { getTokenLogo } from 'components/WalletPopup/Transactions/helper' import useTheme from 'hooks/useTheme' @@ -13,16 +13,6 @@ export const TokenAmountWrapper = styled.div` gap: 4px; font-size: 12px; ` -const TokenLogo = styled(Logo)` - width: 12px; - height: 12px; - border-radius: 100%; - box-shadow: ${({ theme }) => - (() => { - const color = theme.darkMode ? `rgba(256, 256, 256, 0.2)` : `rgba(0, 0, 0, 0.2)` - return `0 4px 5px 0 ${color}, 0 1px 70px 0 ${color};` - })()}; -` const DeltaTokenAmount = ({ symbol, @@ -55,7 +45,7 @@ const DeltaTokenAmount = ({ (chainId ? ( ) : ( - + ))} {sign} {amount} {symbol} diff --git a/src/components/YieldPools/ElasticFarmGroup/index.tsx b/src/components/YieldPools/ElasticFarmGroup/index.tsx index 6afe935553..9a746f4ea3 100644 --- a/src/components/YieldPools/ElasticFarmGroup/index.tsx +++ b/src/components/YieldPools/ElasticFarmGroup/index.tsx @@ -15,7 +15,7 @@ import CurrencyLogo from 'components/CurrencyLogo' import HoverDropdown from 'components/HoverDropdown' import InfoHelper from 'components/InfoHelper' import { MouseoverTooltip, MouseoverTooltipDesktopOnly, TextDashed } from 'components/Tooltip' -import { FARM_TAB, ZERO_ADDRESS } from 'constants/index' +import { FARM_TAB, SORT_DIRECTION, ZERO_ADDRESS } from 'constants/index' import { NETWORKS_INFO, isEVM } from 'constants/networks' import { useActiveWeb3React } from 'hooks' import { useProAmmNFTPositionManagerContract } from 'hooks/useContract' @@ -74,11 +74,6 @@ enum SORT_FIELD { MY_REWARD = 'my_reward', } -enum SORT_DIRECTION { - ASC = 'asc', - DESC = 'desc', -} - const ElasticFarmGroup: React.FC = ({ address, onOpenModal, pools, onShowStepGuide, tokenPrices }) => { const theme = useTheme() const { account, chainId } = useActiveWeb3React() diff --git a/src/constants/env.ts b/src/constants/env.ts index 378da69d50..1dcc6fa861 100644 --- a/src/constants/env.ts +++ b/src/constants/env.ts @@ -63,7 +63,7 @@ type FirebaseConfig = { measurementId?: string } -export const FIREBASE: { [key: string]: { DEFAULT: FirebaseConfig; LIMIT_ORDER?: FirebaseConfig } } = { +export const FIREBASE: { [key in EnvKeys]: { DEFAULT: FirebaseConfig; LIMIT_ORDER?: FirebaseConfig } } = { development: { LIMIT_ORDER: { apiKey: 'AIzaSyBHRrinrQ3CXVrevZN442fjG0EZ-nYNNaU', @@ -113,14 +113,17 @@ export const FIREBASE: { [key: string]: { DEFAULT: FirebaseConfig; LIMIT_ORDER?: }, } -const ANNOUNCEMENT_TEMPLATE_IDS: { [key: string]: { [type: string]: string } } = { +type Config = { [type in PrivateAnnouncementType]: string } & { EXCLUDE: string } +const ANNOUNCEMENT_TEMPLATE_IDS: { [key in EnvKeys]: Config } = { development: { [PrivateAnnouncementType.PRICE_ALERT]: '53', [PrivateAnnouncementType.LIMIT_ORDER]: '8,9,10,11,33,34,35,36', [PrivateAnnouncementType.BRIDGE_ASSET]: '37,38', [PrivateAnnouncementType.CROSS_CHAIN]: '48,49', [PrivateAnnouncementType.KYBER_AI]: '46', + [PrivateAnnouncementType.KYBER_AI_WATCHLIST]: '54', [PrivateAnnouncementType.ELASTIC_POOLS]: '39,40', + [PrivateAnnouncementType.DIRECT_MESSAGE]: '', EXCLUDE: '2,29,1,47,50,44,45', }, staging: { @@ -129,7 +132,9 @@ const ANNOUNCEMENT_TEMPLATE_IDS: { [key: string]: { [type: string]: string } } = [PrivateAnnouncementType.BRIDGE_ASSET]: '12,13', [PrivateAnnouncementType.CROSS_CHAIN]: '25,26', [PrivateAnnouncementType.KYBER_AI]: '27', + [PrivateAnnouncementType.KYBER_AI_WATCHLIST]: '54', // todo [PrivateAnnouncementType.ELASTIC_POOLS]: '20,21', + [PrivateAnnouncementType.DIRECT_MESSAGE]: '', EXCLUDE: '2,11,1,28,29,22,23', }, production: { @@ -138,7 +143,9 @@ const ANNOUNCEMENT_TEMPLATE_IDS: { [key: string]: { [type: string]: string } } = [PrivateAnnouncementType.BRIDGE_ASSET]: '10,11', [PrivateAnnouncementType.CROSS_CHAIN]: '27,28', [PrivateAnnouncementType.KYBER_AI]: '26', + [PrivateAnnouncementType.KYBER_AI_WATCHLIST]: '54', // todo [PrivateAnnouncementType.ELASTIC_POOLS]: '17,18', + [PrivateAnnouncementType.DIRECT_MESSAGE]: '', EXCLUDE: '2,16,19,9,25,24,21,22', }, } diff --git a/src/constants/index.ts b/src/constants/index.ts index 675639d3a7..9323d6e17a 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -413,3 +413,8 @@ export const FRAX_FARMS: { [chainId in ChainId]?: string[] } = { [ChainId.ARBITRUM]: ['0x6a7dccf168fba624a81b293c2538d31427b5b4bd'], [ChainId.OPTIMISM]: ['0xa837d04a64acf66912d05cfd9b951e4e399ab680'], } + +export enum SORT_DIRECTION { + ASC = 'asc', + DESC = 'desc', +} diff --git a/src/hooks/useNotification/index.ts b/src/hooks/useNotification/index.ts index 214af53b4b..ceab132410 100644 --- a/src/hooks/useNotification/index.ts +++ b/src/hooks/useNotification/index.ts @@ -29,6 +29,7 @@ export type Topic = { type: TopicType isKyberAI: boolean isPriceAlert: boolean + isPriceElasticPool: boolean } type SaveNotificationParam = { @@ -58,8 +59,10 @@ const useNotification = () => { ...e, id: Date.now() + i, isSubscribed: e?.topics?.every(e => e.isSubscribed), + // special topic ids isKyberAI: e?.topics?.some(e => e.id + '' === KYBER_AI_TOPIC_ID), isPriceAlert: e?.topics?.some(e => e.id + '' === PRICE_ALERT_TOPIC_ID), + isPriceElasticPool: e?.topics?.some(e => e.id + '' === ELASTIC_POOL_TOPIC_ID), })) dispatch(setSubscribedNotificationTopic({ topicGroups })) }, [resp, dispatch]) diff --git a/src/pages/Farm/ElasticFarmv2/index.tsx b/src/pages/Farm/ElasticFarmv2/index.tsx index 05b488c73f..6e0739e432 100644 --- a/src/pages/Farm/ElasticFarmv2/index.tsx +++ b/src/pages/Farm/ElasticFarmv2/index.tsx @@ -19,12 +19,13 @@ import { MouseoverTooltip, MouseoverTooltipDesktopOnly, TextDashed } from 'compo import { ConnectWalletButton } from 'components/YieldPools/ElasticFarmGroup/buttons' import { FarmList } from 'components/YieldPools/ElasticFarmGroup/styleds' import { ClickableText, ElasticFarmV2TableHeader } from 'components/YieldPools/styleds' +import { SORT_DIRECTION } from 'constants/index' import { useActiveWeb3React } from 'hooks' import { useProAmmNFTPositionManagerContract } from 'hooks/useContract' import useTheme from 'hooks/useTheme' import { Dots } from 'pages/Pool/styleds' import { useWalletModalToggle } from 'state/application/hooks' -import { SORT_DIRECTION, SORT_FIELD, useFarmV2Action, useFilteredFarmsV2 } from 'state/farms/elasticv2/hooks' +import { SORT_FIELD, useFarmV2Action, useFilteredFarmsV2 } from 'state/farms/elasticv2/hooks' import { ElasticFarmV2 } from 'state/farms/elasticv2/types' import { useSingleCallResult } from 'state/multicall/hooks' import useGetElasticPools from 'state/prommPools/useGetElasticPools' diff --git a/src/pages/MyEarnings/ChainSelect.tsx b/src/pages/MyEarnings/ChainSelect.tsx index 6d3bf822a3..41b1a484f0 100644 --- a/src/pages/MyEarnings/ChainSelect.tsx +++ b/src/pages/MyEarnings/ChainSelect.tsx @@ -1,5 +1,6 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' import { Trans } from '@lingui/macro' -import { useDispatch } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import { useMedia } from 'react-use' import { Flex, Text } from 'rebass' import styled from 'styled-components' @@ -16,6 +17,7 @@ import { useActiveWeb3React } from 'hooks' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useTheme from 'hooks/useTheme' import MultipleChainSelect from 'pages/MyEarnings/MultipleChainSelect' +import { AppState } from 'state' import { useAppSelector } from 'state/hooks' import { selectChains } from 'state/myEarnings/actions' import { useShowMyEarningChart } from 'state/user/hooks' @@ -68,6 +70,8 @@ const ChainSelect = () => { const networkList = SUPPORTED_NETWORKS_FOR_MY_EARNINGS.filter(item => !comingSoonList.includes(item)) + const selectedChains = useSelector((state: AppState) => state.myEarnings.selectedChains) + const isValidNetwork = networkList.includes(chainId) const handleClickCurrentChain = () => { @@ -79,6 +83,10 @@ const ChainSelect = () => { dispatch(selectChains([chainId])) } + const handleChangeChains = (chains: ChainId[]) => { + dispatch(selectChains(chains)) + } + return ( { Current Chain - + mixpanelHandler(MIXPANEL_TYPE.EARNING_DASHBOARD_CLICK_ALL_CHAINS_BUTTON)} + /> ) diff --git a/src/pages/MyEarnings/MultipleChainSelect/PopoverBody.tsx b/src/pages/MyEarnings/MultipleChainSelect/PopoverBody.tsx index e431396389..4ccd478a59 100644 --- a/src/pages/MyEarnings/MultipleChainSelect/PopoverBody.tsx +++ b/src/pages/MyEarnings/MultipleChainSelect/PopoverBody.tsx @@ -1,7 +1,6 @@ -import { ChainId } from '@kyberswap/ks-sdk-core' import { Trans } from '@lingui/macro' +import { rgba } from 'polished' import { MouseEventHandler, useEffect, useRef, useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' import { Box, Flex, Text } from 'rebass' import styled from 'styled-components' @@ -9,22 +8,11 @@ import { ReactComponent as LogoKyber } from 'assets/svg/logo_kyber.svg' import { ButtonPrimary } from 'components/Button' import Checkbox from 'components/CheckBox' import { MouseoverTooltip } from 'components/Tooltip' -import { - COMING_SOON_NETWORKS_FOR_MY_EARNINGS, - COMING_SOON_NETWORKS_FOR_MY_EARNINGS_CLASSIC, - COMING_SOON_NETWORKS_FOR_MY_EARNINGS_LEGACY, - NETWORKS_INFO, - SUPPORTED_NETWORKS_FOR_MY_EARNINGS, -} from 'constants/networks' -import { VERSION } from 'constants/v2' +import { NETWORKS_INFO } from 'constants/networks' import useChainsConfig from 'hooks/useChainsConfig' -import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useTheme from 'hooks/useTheme' -import { AppState } from 'state' -import { useAppSelector } from 'state/hooks' -import { selectChains } from 'state/myEarnings/actions' -import { StyledLogo } from '.' +import { MultipleChainSelectProps, StyledLogo } from '.' const ChainListWrapper = styled.div` display: flex; @@ -48,7 +36,7 @@ const ChainListWrapper = styled.div` /* Handle */ ::-webkit-scrollbar-thumb { - background: ${({ theme }) => theme.buttonBlack}; + background: ${({ theme }) => rgba(theme.subText, 0.4)}; border-radius: 999px; } ` @@ -100,37 +88,28 @@ const ApplyButton: React.FC = ({ disabled, onClick, numOfChain ) } -type Props = { onClose: () => void } -const PopoverBody: React.FC = ({ onClose }) => { +const PopoverBody: React.FC void }> = ({ + onClose, + comingSoonList = [], + chainIds, + selectedChainIds, + handleChangeChains, + onTracking, + menuStyle, +}) => { const theme = useTheme() - const { mixpanelHandler } = useMixpanel() const selectAllRef = useRef(null) - const isLegacy = useAppSelector(state => state.myEarnings.activeTab === VERSION.ELASTIC_LEGACY) - const isClassic = useAppSelector(state => state.myEarnings.activeTab === VERSION.CLASSIC) const { activeChains } = useChainsConfig() - - const comingSoonList = isLegacy - ? COMING_SOON_NETWORKS_FOR_MY_EARNINGS_LEGACY - : isClassic - ? COMING_SOON_NETWORKS_FOR_MY_EARNINGS_CLASSIC - : COMING_SOON_NETWORKS_FOR_MY_EARNINGS - - const selectedChains = useSelector((state: AppState) => - state.myEarnings.selectedChains.filter(item => !comingSoonList.includes(item)), - ) - const dispatch = useDispatch() + const selectedChains = selectedChainIds.filter(item => !comingSoonList.includes(item)) const [localSelectedChains, setLocalSelectedChains] = useState(() => selectedChains) - const networkList = SUPPORTED_NETWORKS_FOR_MY_EARNINGS.filter( + const networkList = chainIds.filter( item => !comingSoonList.includes(item) && activeChains.some(e => e.chainId === item), ) const isAllSelected = localSelectedChains.length === networkList.length - const handleChangeChains = (chains: ChainId[]) => { - dispatch(selectChains(chains)) - } useEffect(() => { setLocalSelectedChains(selectedChains) @@ -148,6 +127,15 @@ const PopoverBody: React.FC = ({ onClose }) => { const allNetworks = [...networkList, ...comingSoonList] + const onChangeChain = () => { + if (isAllSelected) { + setLocalSelectedChains([]) + } else { + onTracking?.() + setLocalSelectedChains(networkList) + } + } + return ( = ({ onClose }) => { position: 'absolute', top: 'calc(100% + 8px)', right: '0', + ...menuStyle, }} > = ({ onClose }) => { padding: '4px', }} > - { - if (isAllSelected) { - setLocalSelectedChains([]) - } else { - mixpanelHandler(MIXPANEL_TYPE.EARNING_DASHBOARD_CLICK_ALL_CHAINS_BUTTON) - setLocalSelectedChains(networkList) - } - }} - /> + diff --git a/src/pages/MyEarnings/MultipleChainSelect/SelectButton.tsx b/src/pages/MyEarnings/MultipleChainSelect/SelectButton.tsx index 1bdf9696f4..a14e6b8bc0 100644 --- a/src/pages/MyEarnings/MultipleChainSelect/SelectButton.tsx +++ b/src/pages/MyEarnings/MultipleChainSelect/SelectButton.tsx @@ -1,15 +1,13 @@ import { Trans } from '@lingui/macro' -import { useSelector } from 'react-redux' import { Flex } from 'rebass' import styled from 'styled-components' import { ReactComponent as DropdownSVG } from 'assets/svg/down.svg' import { ReactComponent as LogoKyber } from 'assets/svg/logo_kyber.svg' -import { NETWORKS_INFO, SUPPORTED_NETWORKS_FOR_MY_EARNINGS } from 'constants/networks' +import { NETWORKS_INFO } from 'constants/networks' import useTheme from 'hooks/useTheme' -import { AppState } from 'state' -import { StyledLogo } from '.' +import { MultipleChainSelectProps, StyledLogo } from '.' const DropdownIcon = styled(DropdownSVG)` transition: transform 300ms; @@ -25,54 +23,64 @@ const ButtonBodyWrapper = styled.div` gap: 8px; ` -const Label = styled.span` +const Label = styled.span<{ labelColor?: string }>` font-weight: 500; font-size: 14px; line-height: 20px; - color: ${({ theme }) => theme.subText}; + color: ${({ theme, labelColor }) => labelColor || theme.subText}; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; ` - -const SelectButton: React.FC<{ expanded: boolean; onClick: () => void }> = ({ expanded, onClick }) => { +type Props = { + expanded: boolean + onClick: () => void +} & MultipleChainSelectProps +const SelectButton: React.FC = ({ + expanded, + onClick, + selectedChainIds, + chainIds, + activeRender, + activeStyle, + labelColor, +}) => { const theme = useTheme() - const selectedChains = useSelector((state: AppState) => state.myEarnings.selectedChains) const renderButtonBody = () => { - if (selectedChains.length === SUPPORTED_NETWORKS_FOR_MY_EARNINGS.length) { + if (selectedChainIds.length === chainIds.length) { return ( - ) } - if (selectedChains.length === 1) { - const config = NETWORKS_INFO[selectedChains[0]] + if (selectedChainIds.length === 1) { + const config = NETWORKS_INFO[selectedChainIds[0]] const iconSrc = theme.darkMode && config.iconDark ? config.iconDark : config.icon return ( - + ) } return ( - {selectedChains.slice(0, 3).map(chainId => { + {selectedChainIds.slice(0, 3).map(chainId => { const config = NETWORKS_INFO[chainId] const iconSrc = theme.darkMode && config.iconDark ? config.iconDark : config.icon return })} - {selectedChains.length > 3 && `+${selectedChains.length - 3}`} + {selectedChainIds.length > 3 && `+${selectedChainIds.length - 3}`} ) } @@ -90,10 +98,11 @@ const SelectButton: React.FC<{ expanded: boolean; onClick: () => void }> = ({ ex background: theme.background, userSelect: 'none', cursor: 'pointer', + ...activeStyle, }} onClick={onClick} > - {renderButtonBody()} + {activeRender ? activeRender(renderButtonBody()) : renderButtonBody()} ) diff --git a/src/pages/MyEarnings/MultipleChainSelect/index.tsx b/src/pages/MyEarnings/MultipleChainSelect/index.tsx index 027f28c6f1..a9b324c39f 100644 --- a/src/pages/MyEarnings/MultipleChainSelect/index.tsx +++ b/src/pages/MyEarnings/MultipleChainSelect/index.tsx @@ -1,6 +1,7 @@ -import { useRef, useState } from 'react' +import { ChainId } from '@kyberswap/ks-sdk-core' +import { ReactNode, useRef, useState } from 'react' import { Flex } from 'rebass' -import styled from 'styled-components' +import styled, { CSSProperties } from 'styled-components' import { useOnClickOutside } from 'hooks/useOnClickOutside' @@ -12,10 +13,20 @@ export const StyledLogo = styled.img` height: auto; ` -type Props = { +export type MultipleChainSelectProps = { className?: string + comingSoonList?: ChainId[] + chainIds: ChainId[] + selectedChainIds: ChainId[] + handleChangeChains: (v: ChainId[]) => void + onTracking?: () => void + menuStyle?: CSSProperties + style?: CSSProperties + activeStyle?: CSSProperties + labelColor?: string + activeRender?: (node: ReactNode) => ReactNode } -const MultipleChainSelect: React.FC = ({ className }) => { +const MultipleChainSelect: React.FC = ({ className, style, ...props }) => { const [expanded, setExpanded] = useState(false) const ref = useRef(null) @@ -33,19 +44,14 @@ const MultipleChainSelect: React.FC = ({ className }) => { width: '150px', height: '36px', position: 'relative', - zIndex: '2', + zIndex: '3', + ...style, }} className={className} > - setExpanded(e => !e)} /> - - {expanded && ( - { - setExpanded(false) - }} - /> - )} + setExpanded(e => !e)} {...props} /> + + {expanded && setExpanded(false)} {...props} />} ) } diff --git a/src/pages/NotificationCenter/Menu/index.tsx b/src/pages/NotificationCenter/Menu/index.tsx index c4ef4afc92..1a3279e558 100644 --- a/src/pages/NotificationCenter/Menu/index.tsx +++ b/src/pages/NotificationCenter/Menu/index.tsx @@ -6,6 +6,7 @@ import { Flex } from 'rebass' import { useGetTotalUnreadAnnouncementsQuery } from 'services/announcement' import { ReactComponent as AllIcon } from 'assets/svg/all_icon.svg' +import { PRIVATE_ANN_TITLE } from 'components/Announcement/PrivateAnnoucement' import InboxIcon from 'components/Announcement/PrivateAnnoucement/Icon' import { PrivateAnnouncementType } from 'components/Announcement/type' import Avatar from 'components/Avatar' @@ -27,15 +28,6 @@ import { MEDIA_WIDTHS } from 'theme' import getShortenAddress from 'utils/getShortenAddress' import { shortString } from 'utils/string' -export const MENU_TITLE: Partial<{ [type in PrivateAnnouncementType]: string }> = { - [PrivateAnnouncementType.BRIDGE_ASSET]: t`Cross-Chain Bridge`, - [PrivateAnnouncementType.CROSS_CHAIN]: t`Cross-Chain Swaps`, - [PrivateAnnouncementType.LIMIT_ORDER]: t`Limit Orders`, - [PrivateAnnouncementType.KYBER_AI]: t`Top Tokens by KyberAI`, - [PrivateAnnouncementType.PRICE_ALERT]: t`Price Alerts`, - [PrivateAnnouncementType.ELASTIC_POOLS]: t`Elastic Liquidity Positions`, -} - export type Unread = Partial<{ [type in PrivateAnnouncementType]: number | undefined }> & { ALL: number | undefined } export type MenuItemType = { @@ -104,6 +96,10 @@ const menuItems: MenuItemType[] = [ route: PROFILE_MANAGE_ROUTES.KYBER_AI_TOKENS, type: PrivateAnnouncementType.KYBER_AI, }, + { + route: PROFILE_MANAGE_ROUTES.KYBER_AI_WATCH_LIST, + type: PrivateAnnouncementType.KYBER_AI_WATCHLIST, + }, ], }, ].map(el => { @@ -116,7 +112,7 @@ const menuItems: MenuItemType[] = [ const type = child.type as PrivateAnnouncementType return { ...child, - title: child.title || MENU_TITLE[type], + title: child.title || PRIVATE_ANN_TITLE[type], icon: child.icon || , } }), diff --git a/src/components/SubscribeButton/NotificationPreference/ActionButtons.tsx b/src/pages/NotificationCenter/NotificationPreference/ActionButtons.tsx similarity index 100% rename from src/components/SubscribeButton/NotificationPreference/ActionButtons.tsx rename to src/pages/NotificationCenter/NotificationPreference/ActionButtons.tsx diff --git a/src/components/SubscribeButton/NotificationPreference/InputEmail.tsx b/src/pages/NotificationCenter/NotificationPreference/InputEmail.tsx similarity index 100% rename from src/components/SubscribeButton/NotificationPreference/InputEmail.tsx rename to src/pages/NotificationCenter/NotificationPreference/InputEmail.tsx diff --git a/src/components/SubscribeButton/NotificationPreference/index.tsx b/src/pages/NotificationCenter/NotificationPreference/index.tsx similarity index 92% rename from src/components/SubscribeButton/NotificationPreference/index.tsx rename to src/pages/NotificationCenter/NotificationPreference/index.tsx index 2086585c85..7973b1137f 100644 --- a/src/components/SubscribeButton/NotificationPreference/index.tsx +++ b/src/pages/NotificationCenter/NotificationPreference/index.tsx @@ -1,5 +1,5 @@ import { Trans, t } from '@lingui/macro' -import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useEffect, useMemo, useState } from 'react' import { Text } from 'rebass' import styled from 'styled-components' @@ -9,14 +9,14 @@ import Column from 'components/Column' import MailIcon from 'components/Icons/MailIcon' import Loader from 'components/Loader' import Row from 'components/Row' -import ActionButtons from 'components/SubscribeButton/NotificationPreference/ActionButtons' -import Header from 'components/SubscribeButton/NotificationPreference/Header' -import InputEmail from 'components/SubscribeButton/NotificationPreference/InputEmail' import { MouseoverTooltip } from 'components/Tooltip' import { PRICE_ALERT_TOPIC_ID } from 'constants/env' +import { useActiveWeb3React } from 'hooks' import useMixpanel, { MIXPANEL_TYPE } from 'hooks/useMixpanel' import useNotification, { Topic, TopicType } from 'hooks/useNotification' import useTheme from 'hooks/useTheme' +import ActionButtons from 'pages/NotificationCenter/NotificationPreference/ActionButtons' +import InputEmail from 'pages/NotificationCenter/NotificationPreference/InputEmail' import VerifyCodeModal from 'pages/Verify/VerifyCodeModal' import { useNotify } from 'state/application/hooks' import { useSessionInfo } from 'state/authen/hooks' @@ -99,9 +99,7 @@ const EmailColum = styled(Column)` `} ` -const noop = () => { - // -} +const noop = () => {} const sortGroup = (arr: Topic[]) => [...arr].sort((x, y) => y.priority - x.priority) @@ -140,18 +138,11 @@ export const useValidateEmail = (defaultEmail?: string) => { return { inputEmail: inputEmail.trim(), onChangeEmail, errorInput, errorColor, hasErrorInput, reset } } -function NotificationPreference({ - header, - isOpen, - toggleModal = noop, -}: { - header?: ReactNode - isOpen: boolean - toggleModal?: () => void -}) { +function NotificationPreference({ toggleModal = noop }: { toggleModal?: () => void }) { const theme = useTheme() const { isLoading, saveNotification, topicGroups: topicGroupsGlobal, unsubscribeAll } = useNotification() + const { account } = useActiveWeb3React() const { userInfo, isLogin } = useSessionInfo() const { isSignInEmail } = useSignedAccountInfo() const { isWhiteList } = useIsWhiteListKyberAI() @@ -190,23 +181,14 @@ function NotificationPreference({ ) useEffect(() => { - if (isOpen) { - setEmailPendingVerified('') - reset(userInfo?.email) - } - }, [userInfo, isOpen, reset]) + setEmailPendingVerified('') + reset(userInfo?.email) + }, [userInfo, reset]) useEffect(() => { - setTimeout( - () => { - setSelectedTopic(isOpen ? topicGroupsGlobal.filter(e => e.isSubscribed).map(e => e.id) : []) - if (isOpen) { - setTopicGroups(sortGroup(topicGroupsGlobal)) - } - }, - isOpen ? 0 : 400, - ) - }, [isOpen, topicGroupsGlobal]) + setSelectedTopic(topicGroupsGlobal.filter(e => e.isSubscribed).map(e => e.id)) + setTopicGroups(sortGroup(topicGroupsGlobal)) + }, [topicGroupsGlobal]) const getDiffChangeTopics = useCallback( (topicGroups: Topic[]) => { @@ -381,7 +363,9 @@ function NotificationPreference({ return ( - {header ||
} + + Email Notification +