From 51e073a3589fdd141bb403f9a1b731338c8399aa Mon Sep 17 00:00:00 2001 From: Fionn Lennon Date: Tue, 23 Sep 2025 15:01:34 +0100 Subject: [PATCH 1/2] fix: improve Free Deposit banner styling --- src/views/MarketsStats.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/views/MarketsStats.tsx b/src/views/MarketsStats.tsx index 82ceb4ea90..fdad739074 100644 --- a/src/views/MarketsStats.tsx +++ b/src/views/MarketsStats.tsx @@ -83,8 +83,8 @@ export const MarketsStats = (props: MarketsStatsProps) => { <$FreeDepositBanner>
- - + + {stringGetter({ key: STRING_KEYS.FREE_DEPOSIT_BANNER_TITLE_FREE })} @@ -110,7 +110,7 @@ export const MarketsStats = (props: MarketsStatsProps) => { free deposit hedgie
From 50fdb651b95914e94f9965d01573902e02f13b37 Mon Sep 17 00:00:00 2001 From: Fionn Lennon Date: Fri, 26 Sep 2025 10:06:13 +0100 Subject: [PATCH 2/2] feat: Add VIP Telegram notification for high-value users --- package.json | 4 +- pnpm-lock.yaml | 13 +++- public/telegram-noti.svg | 13 ++++ src/constants/notifications.ts | 2 + src/hooks/useEnableTurnkey.ts | 3 +- src/hooks/useNotificationTypes.tsx | 71 +++++++++++++++++ src/views/MarketsStats.tsx | 2 +- .../notifications/VipTelegramNotification.tsx | 77 +++++++++++++++++++ 8 files changed, 176 insertions(+), 9 deletions(-) create mode 100644 public/telegram-noti.svg create mode 100644 src/views/notifications/VipTelegramNotification.tsx diff --git a/package.json b/package.json index fe4d4beeee..eaa2897cf5 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ "@cosmjs/tendermint-rpc": "^0.32.1", "@datadog/browser-logs": "^5.23.3", "@dydxprotocol/v4-client-js": "3.0.3", - "@dydxprotocol/v4-localization": "1.1.325", + "@dydxprotocol/v4-localization": "1.1.326", "@dydxprotocol/v4-proto": "^7.0.0-dev.0", "@emotion/is-prop-valid": "^1.3.0", "@hugocxl/react-to-image": "^0.0.9", @@ -234,4 +234,4 @@ "includeClassNames": true } } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36064aa62e..0a7fa79ba1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,8 +33,8 @@ dependencies: specifier: 3.0.3 version: 3.0.3 '@dydxprotocol/v4-localization': - specifier: 1.1.325 - version: 1.1.325 + specifier: 1.1.326 + version: 1.1.326 '@dydxprotocol/v4-proto': specifier: ^7.0.0-dev.0 version: 7.0.0-dev.0 @@ -1525,8 +1525,8 @@ packages: - utf-8-validate dev: false - /@dydxprotocol/v4-localization@1.1.325: - resolution: {integrity: sha512-IAVnOLYrPHRQJLMEmfn0s6jcT2UkfAsTF0bkVZj8G08uMIawhrNnvRkIqk8ck320me5W1qrO0GYI7Nbagzfx0g==} + /@dydxprotocol/v4-localization@1.1.326: + resolution: {integrity: sha512-Cwq7wxciWN5WOS6JkGO0u9qMS+csxF5g67oz7e9YZkUv7dL/tH7gBQvqWQtB+KFDPBY/PJFwFCnNjtK1f8Lc0Q==} dev: false /@dydxprotocol/v4-proto@7.0.0-dev.0: @@ -4539,6 +4539,7 @@ packages: dependencies: is-glob: 4.0.3 micromatch: 4.0.5 + napi-wasm: 1.1.3 dev: false bundledDependencies: - napi-wasm @@ -17948,6 +17949,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /napi-wasm@1.1.3: + resolution: {integrity: sha512-h/4nMGsHjZDCYmQVNODIrYACVJ+I9KItbG+0si6W/jSjdA9JbWDoU4LLeMXVcEQGHjttI2tuXqDrbGF7qkUHHg==} + dev: false + /native-abort-controller@1.0.4(abort-controller@3.0.0): resolution: {integrity: sha512-zp8yev7nxczDJMoP6pDxyD20IU0T22eX8VwN2ztDccKvSZhRaV33yP1BGwKSZfXuqWUzsXopVFjBdau9OOAwMQ==} peerDependencies: diff --git a/public/telegram-noti.svg b/public/telegram-noti.svg new file mode 100644 index 0000000000..f949f8aa15 --- /dev/null +++ b/public/telegram-noti.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/constants/notifications.ts b/src/constants/notifications.ts index 8c7cebd313..1afac0bc9a 100644 --- a/src/constants/notifications.ts +++ b/src/constants/notifications.ts @@ -22,6 +22,7 @@ export enum NotificationType { FillWithNoOrder = 'FillWithNoOrder', Order = 'Order', CosmosWalletLifecycle = 'CosmosWalletLifecycle', // lifecycle events for cosmos wallets + VipTelegram = 'VipTelegram', } export enum NotificationCategoryPreferences { @@ -55,6 +56,7 @@ export const NotificationTypeCategory: { [NotificationType.PredictionMarketConcluded]: NotificationCategoryPreferences.MustSee, [NotificationType.Custom]: NotificationCategoryPreferences.MustSee, [NotificationType.CosmosWalletLifecycle]: NotificationCategoryPreferences.MustSee, + [NotificationType.VipTelegram]: NotificationCategoryPreferences.General, }; export const SingleSessionNotificationTypes = [ diff --git a/src/hooks/useEnableTurnkey.ts b/src/hooks/useEnableTurnkey.ts index 8730dad497..a7e91e8d33 100644 --- a/src/hooks/useEnableTurnkey.ts +++ b/src/hooks/useEnableTurnkey.ts @@ -7,6 +7,5 @@ import { useStatsigGateValue } from './useStatsig'; export const useEnableTurnkey = () => { const forcedTurnkey = testFlags.enableTurnkey; const turnkeyFF = useStatsigGateValue(StatsigFlags.ffTurnkeyWeb); - - return forcedTurnkey || turnkeyFF; + return forcedTurnkey ?? turnkeyFF; }; diff --git a/src/hooks/useNotificationTypes.tsx b/src/hooks/useNotificationTypes.tsx index cc63898824..02380a95ac 100644 --- a/src/hooks/useNotificationTypes.tsx +++ b/src/hooks/useNotificationTypes.tsx @@ -45,9 +45,12 @@ import { CloseAllPositionsNotification } from '@/views/notifications/CloseAllPos import { OrderCancelNotification } from '@/views/notifications/OrderCancelNotification'; import { OrderStatusNotification } from '@/views/notifications/OrderStatusNotification'; import { TradeNotification } from '@/views/notifications/TradeNotification'; +import { VipTelegramNotification } from '@/views/notifications/VipTelegramNotification'; import { getUserWalletAddress } from '@/state/accountInfoSelectors'; import { + getIsAccountConnected, + getSubaccountEquity, selectOrphanedTriggerOrders, selectReclaimableChildSubaccountFunds, selectShouldAccountRebalanceUsdc, @@ -85,6 +88,7 @@ import { useAppSelectorWithArgs } from './useParameterizedSelector'; import { useAllStatsigDynamicConfigValues } from './useStatsig'; import { useStringGetter } from './useStringGetter'; import { useURLConfigs } from './useURLConfigs'; +import { useLoadedVaultAccount } from './vaultsHooks'; export const notificationTypes: NotificationTypeConfig[] = [ { @@ -1175,6 +1179,73 @@ export const notificationTypes: NotificationTypeConfig[] = [ }; }, }, + { + type: NotificationType.VipTelegram, + useTrigger: ({ trigger }) => { + const stringGetter = useStringGetter(); + const isConnected = useAppSelector(getIsAccountConnected); + const equity = useAppSelector(getSubaccountEquity, shallowEqual); + const { data: vaultAccount } = useLoadedVaultAccount(); + const vaultBalance = vaultAccount?.balanceUsdc; + + useEffect(() => { + if (!isConnected) { + return; + } + + if (sessionStorage.getItem('vip-telegram-notification-shown')) { + return; + } + + const totalValue = equity != null ? equity + (vaultBalance ?? 0) : undefined; + if (totalValue == null || totalValue < 50000) { + return; + } + + const title = stringGetter({ + key: STRING_KEYS.YOURE_INVITED, + fallback: "You're Invited", + }); + const body = stringGetter({ + key: STRING_KEYS.VIP_TELEGRAM_BODY, + fallback: + 'Private Telegram Group for VIP traders. Enjoy white glove service, special incentives and competitions', + }); + + if (!title || !body) { + return; + } + + sessionStorage.setItem('vip-telegram-notification-shown', 'true'); + + trigger({ + id: `vip-telegram-invitation-${Date.now()}`, + displayData: { + title, + body, + renderCustomBody: ({ isToast, notification }) => ( + + ), + groupKey: NotificationType.VipTelegram, + toastSensitivity: 'foreground', + toastDuration: 15000, + }, + updateKey: ['vip-telegram-invitation'], + }); + }, [equity, isConnected, stringGetter, trigger, vaultBalance]); + }, + + useNotificationAction: () => { + return () => { + // Optional: track analytics when user clicks + }; + }, + }, ]; const $Icon = tw.img`h-1.5 w-1.5`; diff --git a/src/views/MarketsStats.tsx b/src/views/MarketsStats.tsx index fdad739074..72172e8996 100644 --- a/src/views/MarketsStats.tsx +++ b/src/views/MarketsStats.tsx @@ -97,7 +97,7 @@ export const MarketsStats = (props: MarketsStatsProps) => { type={ButtonType.Button} tw="relative z-10 w-full border-none bg-color-layer-0 text-color-text-2" onClick={handleFreeDepositClick} - state={{ isDisabled: isOnboardingDisabled }} + state={{ isDisabled: Boolean(isOnboardingDisabled) }} > {stringGetter({ key: STRING_KEYS.FREE_DEPOSIT_BANNER_CTA })} diff --git a/src/views/notifications/VipTelegramNotification.tsx b/src/views/notifications/VipTelegramNotification.tsx new file mode 100644 index 0000000000..3baea7e431 --- /dev/null +++ b/src/views/notifications/VipTelegramNotification.tsx @@ -0,0 +1,77 @@ +import styled from 'styled-components'; +import tw from 'twin.macro'; + +import { ButtonAction, ButtonType } from '@/constants/buttons'; +import { STRING_KEYS } from '@/constants/localization'; + +import { useStringGetter } from '@/hooks/useStringGetter'; + +import { Button } from '@/components/Button'; +// eslint-disable-next-line import/no-cycle +import { Notification, type NotificationProps } from '@/components/Notification'; + +type ElementProps = { + portfolioValue: number; + telegramUrl?: string; +}; + +export type VipTelegramNotificationProps = NotificationProps & ElementProps; + +export const VipTelegramNotification = ({ + isToast, + notification, + portfolioValue: _portfolioValue, + telegramUrl = 'https://t.me/+NLOpf5jiAEVjODFh', +}: VipTelegramNotificationProps) => { + const stringGetter = useStringGetter(); + + const title = stringGetter({ + key: STRING_KEYS.YOURE_INVITED, + fallback: "You're Invited", + }); + + const description = stringGetter({ + key: STRING_KEYS.VIP_TELEGRAM_BODY, + fallback: + 'Private Telegram Group for VIP traders. Enjoy white glove service, special incentives and competitions', + }); + + const handleViewClick = () => { + window.open(telegramUrl, '_blank', 'noopener,noreferrer'); + }; + + return ( + } + slotTitle={title} + slotCustomContent={ + <$Content> + <$Description>{description} + + + } + /> + ); +}; + +const $Icon = styled.img` + ${tw`h-3 w-3 flex-shrink-0`}; +`; + +const $Content = styled.div` + ${tw`mt-0.5 flex items-center justify-between gap-1`}; +`; + +const $Description = styled.p` + ${tw`m-0 text-color-text-0 font-small-book`}; + line-height: 110%; +`;