diff --git a/packages/notification-services-controller/CHANGELOG.md b/packages/notification-services-controller/CHANGELOG.md index b4ec233980..cb395fff69 100644 --- a/packages/notification-services-controller/CHANGELOG.md +++ b/packages/notification-services-controller/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **BREAKING:** `createOnChainPushNotificationMessage` now derives its `title`, `description`, and `ctaLink` from the notification's `template` field and no longer accepts a `translations` argument. ([#9140](https://github.com/MetaMask/core/pull/9140)) + - Update call sites to drop the second `translations` argument: `createOnChainPushNotificationMessage(notification)`. + - `ctaLink` is now populated from `template.cta.link` when present. +- **BREAKING:** Remove the `createOnChainPushNotificationMessages` function and the `TranslationKeys` type. Push notification copy is now provided by the API via the notification `template`, so client-side translation keys are no longer required. ([#9140](https://github.com/MetaMask/core/pull/9140)) - Bump `@metamask/utils` from `^11.9.0` to `^11.11.0` ([#9074](https://github.com/MetaMask/core/pull/9074)) - Bump `@metamask/controller-utils` from `^12.1.1` to `^12.2.0` ([#9083](https://github.com/MetaMask/core/pull/9083)) - Bump `@metamask/profile-sync-controller` from `^28.1.1` to `^28.2.0` ([#9119](https://github.com/MetaMask/core/pull/9119)) diff --git a/packages/notification-services-controller/src/NotificationServicesController/mocks/mock-raw-notifications.ts b/packages/notification-services-controller/src/NotificationServicesController/mocks/mock-raw-notifications.ts index e4da01a6f5..e167b9a330 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/mocks/mock-raw-notifications.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/mocks/mock-raw-notifications.ts @@ -9,6 +9,15 @@ import type { NormalisedAPINotification } from '../types/notification-api/notifi export function createMockNotificationEthSent(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ETH_SENT, + template: { + image_url: 'https://example.com/eth-sent.svg', + title: 'Funds sent', + body: 'You successfully sent 0.005 ETH', + cta: { + content: 'View transaction', + link: 'https://etherscan.io/tx/eth-sent', + }, + }, notification_type: 'on-chain', id: '3fa85f64-5717-4562-b3fc-2c963f66afa7', unread: true, @@ -55,6 +64,11 @@ export function createMockNotificationEthSent(): NormalisedAPINotification { export function createMockNotificationEthReceived(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ETH_RECEIVED, + template: { + image_url: 'https://example.com/eth-received.svg', + title: 'Funds received', + body: 'You received 808 ETH', + }, notification_type: 'on-chain', id: '3fa85f64-5717-4562-b3fc-2c963f66afa8', unread: true, @@ -101,6 +115,11 @@ export function createMockNotificationEthReceived(): NormalisedAPINotification { export function createMockNotificationERC20Sent(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ERC20_SENT, + template: { + image_url: 'https://example.com/erc20-sent.svg', + title: 'Funds sent', + body: 'You successfully sent 4.96K USDC', + }, notification_type: 'on-chain', id: '3fa85f64-5717-4562-b3fc-2c963f66afa9', unread: true, @@ -153,6 +172,11 @@ export function createMockNotificationERC20Sent(): NormalisedAPINotification { export function createMockNotificationERC20Received(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ERC20_RECEIVED, + template: { + image_url: 'https://example.com/erc20-received.svg', + title: 'Funds received', + body: 'You received 8.38B SHIB', + }, notification_type: 'on-chain', id: '3fa85f64-5717-4562-b3fc-2c963f66afa6', unread: true, @@ -205,6 +229,11 @@ export function createMockNotificationERC20Received(): NormalisedAPINotification export function createMockNotificationERC721Sent(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ERC721_SENT, + template: { + image_url: 'https://example.com/erc721-sent.svg', + title: 'NFT sent', + body: 'You have successfully sent an NFT', + }, notification_type: 'on-chain', id: 'a4193058-9814-537e-9df4-79dcac727fb6', created_at: '2023-11-15T11:08:17.895407Z', @@ -260,6 +289,11 @@ export function createMockNotificationERC721Sent(): NormalisedAPINotification { export function createMockNotificationERC721Received(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ERC721_RECEIVED, + template: { + image_url: 'https://example.com/erc721-received.svg', + title: 'NFT received', + body: 'You received new NFTs', + }, notification_type: 'on-chain', id: '00a79d24-befa-57ed-a55a-9eb8696e1654', created_at: '2023-11-14T17:40:52.319281Z', @@ -315,6 +349,11 @@ export function createMockNotificationERC721Received(): NormalisedAPINotificatio export function createMockNotificationERC1155Sent(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ERC1155_SENT, + template: { + image_url: 'https://example.com/erc1155-sent.svg', + title: 'NFT sent', + body: 'You have successfully sent an NFT', + }, notification_type: 'on-chain', id: 'a09ff9d1-623a-52ab-a3d4-c7c8c9a58362', created_at: '2023-11-20T20:44:10.110706Z', @@ -370,6 +409,11 @@ export function createMockNotificationERC1155Sent(): NormalisedAPINotification { export function createMockNotificationERC1155Received(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ERC1155_RECEIVED, + template: { + image_url: 'https://example.com/erc1155-received.svg', + title: 'NFT received', + body: 'You received new NFTs', + }, notification_type: 'on-chain', id: 'b6b93c84-e8dc-54ed-9396-7ea50474843a', created_at: '2023-11-20T20:44:10.110706Z', @@ -425,6 +469,11 @@ export function createMockNotificationERC1155Received(): NormalisedAPINotificati export function createMockNotificationMetaMaskSwapsCompleted(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.METAMASK_SWAP_COMPLETED, + template: { + image_url: 'https://example.com/swap-completed.svg', + title: 'Swap completed', + body: 'Your MetaMask Swap was successful', + }, notification_type: 'on-chain', id: '7ddfe6a1-ac52-5ffe-aa40-f04242db4b8b', created_at: '2023-10-18T13:58:49.854596Z', @@ -486,6 +535,11 @@ export function createMockNotificationMetaMaskSwapsCompleted(): NormalisedAPINot export function createMockNotificationRocketPoolStakeCompleted(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ROCKETPOOL_STAKE_COMPLETED, + template: { + image_url: 'https://example.com/rocketpool-stake-completed.svg', + title: 'Stake complete', + body: 'Your RocketPool stake was successful', + }, notification_type: 'on-chain', id: 'c2a2f225-b2fb-5d6c-ba56-e27a5c71ffb9', created_at: '2023-11-20T12:02:48.796824Z', @@ -546,6 +600,11 @@ export function createMockNotificationRocketPoolStakeCompleted(): NormalisedAPIN export function createMockNotificationRocketPoolUnStakeCompleted(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.ROCKETPOOL_UNSTAKE_COMPLETED, + template: { + image_url: 'https://example.com/rocketpool-unstake-completed.svg', + title: 'Unstake complete', + body: 'Your RocketPool unstake was successful', + }, notification_type: 'on-chain', id: '291ec897-f569-4837-b6c0-21001b198dff', created_at: '2023-10-19T13:11:10.623042Z', @@ -606,6 +665,11 @@ export function createMockNotificationRocketPoolUnStakeCompleted(): NormalisedAP export function createMockNotificationLidoStakeCompleted(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.LIDO_STAKE_COMPLETED, + template: { + image_url: 'https://example.com/lido-stake-completed.svg', + title: 'Stake complete', + body: 'Your Lido stake was successful', + }, notification_type: 'on-chain', id: 'ec10d66a-f78f-461f-83c9-609aada8cc50', created_at: '2023-11-02T22:28:49.970865Z', @@ -666,6 +730,11 @@ export function createMockNotificationLidoStakeCompleted(): NormalisedAPINotific export function createMockNotificationLidoWithdrawalRequested(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.LIDO_WITHDRAWAL_REQUESTED, + template: { + image_url: 'https://example.com/lido-withdrawal-requested.svg', + title: 'Withdrawal requested', + body: 'Your Lido withdrawal request was submitted', + }, notification_type: 'on-chain', id: 'ef003925-3379-4ba7-9e2d-8218690cadc9', created_at: '2023-10-18T15:04:02.482526Z', @@ -726,6 +795,11 @@ export function createMockNotificationLidoWithdrawalRequested(): NormalisedAPINo export function createMockNotificationLidoWithdrawalCompleted(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.LIDO_WITHDRAWAL_COMPLETED, + template: { + image_url: 'https://example.com/lido-withdrawal-completed.svg', + title: 'Withdrawal completed', + body: 'Your Lido withdrawal was successful', + }, notification_type: 'on-chain', id: 'd73df14d-ce73-4f38-bad3-ab028154042f', created_at: '2023-10-18T16:35:03.147606Z', @@ -786,6 +860,11 @@ export function createMockNotificationLidoWithdrawalCompleted(): NormalisedAPINo export function createMockNotificationLidoReadyToBeWithdrawn(): NormalisedAPINotification { const mockNotification: NormalisedAPINotification = { type: TRIGGER_TYPES.LIDO_STAKE_READY_TO_BE_WITHDRAWN, + template: { + image_url: 'https://example.com/lido-ready-to-be-withdrawn.svg', + title: 'Stake ready for withdrawal', + body: 'Your Lido stake is now ready to be withdrawn', + }, notification_type: 'on-chain', id: 'd73df14d-ce73-4f38-bad3-ab028154042e', created_at: '2023-10-18T16:35:03.147606Z', diff --git a/packages/notification-services-controller/src/NotificationServicesController/types/notification-api/schema.ts b/packages/notification-services-controller/src/NotificationServicesController/types/notification-api/schema.ts index 63f7fbfe2b..5668d83421 100644 --- a/packages/notification-services-controller/src/NotificationServicesController/types/notification-api/schema.ts +++ b/packages/notification-services-controller/src/NotificationServicesController/types/notification-api/schema.ts @@ -167,6 +167,7 @@ export type components = { */ created_at: string; payload: components['schemas']['OnChainPayload']; + template: components['schemas']['LocalizedNotification']; }; OnChainPayload: { /** @example 1 */ diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.test.ts b/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.test.ts index 15c7947838..ce7224f1bf 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.test.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.test.ts @@ -16,64 +16,14 @@ import { createMockNotificationRocketPoolStakeCompleted, createMockNotificationRocketPoolUnStakeCompleted, } from '../../NotificationServicesController/mocks'; -import type { TranslationKeys } from './get-notification-message'; import { createOnChainPushNotificationMessage } from './get-notification-message'; -const mockTranslations: TranslationKeys = { - pushPlatformNotificationsFundsSentTitle: () => 'Funds sent', - pushPlatformNotificationsFundsSentDescriptionDefault: () => - 'You successfully sent some tokens', - pushPlatformNotificationsFundsSentDescription: (amount, token) => - `You successfully sent ${amount} ${token}`, - pushPlatformNotificationsFundsReceivedTitle: () => 'Funds received', - pushPlatformNotificationsFundsReceivedDescriptionDefault: () => - 'You received some tokens', - pushPlatformNotificationsFundsReceivedDescription: (amount, token) => - `You received ${amount} ${token}`, - pushPlatformNotificationsSwapCompletedTitle: () => 'Swap completed', - pushPlatformNotificationsSwapCompletedDescription: () => - 'Your MetaMask Swap was successful', - pushPlatformNotificationsNftSentTitle: () => 'NFT sent', - pushPlatformNotificationsNftSentDescription: () => - 'You have successfully sent an NFT', - pushPlatformNotificationsNftReceivedTitle: () => 'NFT received', - pushPlatformNotificationsNftReceivedDescription: () => - 'You received new NFTs', - pushPlatformNotificationsStakingRocketpoolStakeCompletedTitle: () => - 'Stake complete', - pushPlatformNotificationsStakingRocketpoolStakeCompletedDescription: () => - 'Your RocketPool stake was successful', - pushPlatformNotificationsStakingRocketpoolUnstakeCompletedTitle: () => - 'Unstake complete', - pushPlatformNotificationsStakingRocketpoolUnstakeCompletedDescription: () => - 'Your RocketPool unstake was successful', - pushPlatformNotificationsStakingLidoStakeCompletedTitle: () => - 'Stake complete', - pushPlatformNotificationsStakingLidoStakeCompletedDescription: () => - 'Your Lido stake was successful', - pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnTitle: () => - 'Stake ready for withdrawal', - pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnDescription: () => - 'Your Lido stake is now ready to be withdrawn', - pushPlatformNotificationsStakingLidoWithdrawalRequestedTitle: () => - 'Withdrawal requested', - pushPlatformNotificationsStakingLidoWithdrawalRequestedDescription: () => - 'Your Lido withdrawal request was submitted', - pushPlatformNotificationsStakingLidoWithdrawalCompletedTitle: () => - 'Withdrawal completed', - pushPlatformNotificationsStakingLidoWithdrawalCompletedDescription: () => - 'Your Lido withdrawal was successful', -}; - const { processNotification } = Processors; describe('notification-message tests', () => { it('displays erc20 sent notification', () => { const notification = processNotification(createMockNotificationERC20Sent()); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Funds sent'); expect(result?.description).toContain('You successfully sent 4.96K USDC'); @@ -83,10 +33,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationERC20Received(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Funds received'); expect(result?.description).toContain('You received 8.38B SHIB'); @@ -94,23 +41,33 @@ describe('notification-message tests', () => { it('displays eth/native sent notification', () => { const notification = processNotification(createMockNotificationEthSent()); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Funds sent'); expect(result?.description).toContain('You successfully sent 0.005 ETH'); }); - it('displays eth/native received notification', () => { + it('populates ctaLink from the template cta link', () => { + const notification = processNotification(createMockNotificationEthSent()); + const result = createOnChainPushNotificationMessage(notification); + + expect(result?.ctaLink).toBe('https://etherscan.io/tx/eth-sent'); + }); + + it('omits ctaLink when the template has no cta', () => { const notification = processNotification( createMockNotificationEthReceived(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, + const result = createOnChainPushNotificationMessage(notification); + + expect(result?.ctaLink).toBeUndefined(); + }); + + it('displays eth/native received notification', () => { + const notification = processNotification( + createMockNotificationEthReceived(), ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Funds received'); expect(result?.description).toContain('You received 808 ETH'); @@ -120,10 +77,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationMetaMaskSwapsCompleted(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Swap completed'); expect(result?.description).toContain('Your MetaMask Swap was successful'); @@ -133,10 +87,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationERC721Sent(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('NFT sent'); expect(result?.description).toContain('You have successfully sent an NFT'); @@ -146,10 +97,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationERC721Received(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('NFT received'); expect(result?.description).toContain('You received new NFTs'); @@ -159,10 +107,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationERC1155Sent(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('NFT sent'); expect(result?.description).toContain('You have successfully sent an NFT'); @@ -172,10 +117,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationERC1155Received(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('NFT received'); expect(result?.description).toContain('You received new NFTs'); @@ -185,10 +127,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationRocketPoolStakeCompleted(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Stake complete'); expect(result?.description).toContain( @@ -200,10 +139,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationRocketPoolUnStakeCompleted(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Unstake complete'); expect(result?.description).toContain( @@ -215,10 +151,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationLidoStakeCompleted(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Stake complete'); expect(result?.description).toContain('Your Lido stake was successful'); @@ -228,10 +161,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationLidoReadyToBeWithdrawn(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Stake ready for withdrawal'); expect(result?.description).toContain( @@ -243,10 +173,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationLidoWithdrawalRequested(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Withdrawal requested'); expect(result?.description).toContain( @@ -258,10 +185,7 @@ describe('notification-message tests', () => { const notification = processNotification( createMockNotificationLidoWithdrawalCompleted(), ); - const result = createOnChainPushNotificationMessage( - notification, - mockTranslations, - ); + const result = createOnChainPushNotificationMessage(notification); expect(result?.title).toBe('Withdrawal completed'); expect(result?.description).toContain( diff --git a/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.ts b/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.ts index 69a0a4641b..c5e24dc762 100644 --- a/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.ts +++ b/packages/notification-services-controller/src/NotificationServicesPushController/utils/get-notification-message.ts @@ -1,37 +1,5 @@ +import { Constants } from '../../NotificationServicesController'; import type { Types } from '../../NotificationServicesController'; -import type { Constants } from '../../NotificationServicesController'; -import { getAmount, formatAmount } from './get-notification-data'; - -export type TranslationKeys = { - pushPlatformNotificationsFundsSentTitle: () => string; - pushPlatformNotificationsFundsSentDescriptionDefault: () => string; - pushPlatformNotificationsFundsSentDescription: ( - ...args: [string, string] - ) => string; - pushPlatformNotificationsFundsReceivedTitle: () => string; - pushPlatformNotificationsFundsReceivedDescriptionDefault: () => string; - pushPlatformNotificationsFundsReceivedDescription: ( - ...args: [string, string] - ) => string; - pushPlatformNotificationsSwapCompletedTitle: () => string; - pushPlatformNotificationsSwapCompletedDescription: () => string; - pushPlatformNotificationsNftSentTitle: () => string; - pushPlatformNotificationsNftSentDescription: () => string; - pushPlatformNotificationsNftReceivedTitle: () => string; - pushPlatformNotificationsNftReceivedDescription: () => string; - pushPlatformNotificationsStakingRocketpoolStakeCompletedTitle: () => string; - pushPlatformNotificationsStakingRocketpoolStakeCompletedDescription: () => string; - pushPlatformNotificationsStakingRocketpoolUnstakeCompletedTitle: () => string; - pushPlatformNotificationsStakingRocketpoolUnstakeCompletedDescription: () => string; - pushPlatformNotificationsStakingLidoStakeCompletedTitle: () => string; - pushPlatformNotificationsStakingLidoStakeCompletedDescription: () => string; - pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnTitle: () => string; - pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnDescription: () => string; - pushPlatformNotificationsStakingLidoWithdrawalRequestedTitle: () => string; - pushPlatformNotificationsStakingLidoWithdrawalRequestedDescription: () => string; - pushPlatformNotificationsStakingLidoWithdrawalCompletedTitle: () => string; - pushPlatformNotificationsStakingLidoWithdrawalCompletedDescription: () => string; -}; type PushNotificationMessage = { title: string; @@ -39,268 +7,45 @@ type PushNotificationMessage = { ctaLink?: string; }; -type NotificationMessage = { - title: (notification: TNotification) => string | null; - defaultDescription: (notification: TNotification) => string | null; - getDescription?: (notification: TNotification) => string | null; - link?: (notification: TNotification) => string | null; -}; - -type NotificationMessageDict = { - [TriggerType in Constants.TRIGGER_TYPES]?: NotificationMessage< - Extract - >; -}; +type NotificationWithTemplate = Extract< + Types.INotification, + { template: unknown } +>; /** - * On Chain Push Notification Messages. - * This is a list of all the push notifications we support. Update this for synced notifications on mobile and extension + * Type guard for notifications that carry an API-provided `template` field + * (on-chain and platform notifications). * - * @param translationKeys - all translations supported - * @returns A translation push message object. + * @param notification - processed notification. + * @returns True if the notification has a `template` field. */ -export const createOnChainPushNotificationMessages = ( - translationKeys: TranslationKeys, -): NotificationMessageDict => { - type TranslationFn = ( - ...args: [TKey, ...Parameters] - ) => string; - const translate: TranslationFn = (...args) => { - const [key, ...otherArgs] = args; - - // Coerce types for the translation function - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const fn: any = translationKeys[key]; - return fn(...otherArgs); - }; - - return { - erc20_sent: { - title: (): string | null => - translate('pushPlatformNotificationsFundsSentTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsFundsSentDescriptionDefault'), - getDescription: (notification): string | null => { - const symbol = notification?.payload?.data?.token?.symbol; - const tokenAmount = notification?.payload?.data?.token?.amount; - const tokenDecimals = notification?.payload?.data?.token?.decimals; - if (!symbol || !tokenAmount || !tokenDecimals) { - return null; - } - - const amount = getAmount(tokenAmount, tokenDecimals, { - shouldEllipse: true, - }); - return translate( - 'pushPlatformNotificationsFundsSentDescription', - amount, - symbol, - ); - }, - }, - eth_sent: { - title: (): string | null => - translate('pushPlatformNotificationsFundsSentTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsFundsSentDescriptionDefault'), - getDescription: (notification): string | null => { - const symbol = notification?.payload?.network?.native_symbol; - const tokenAmount = notification?.payload?.data?.amount?.eth; - if (!symbol || !tokenAmount) { - return null; - } - - const amount = formatAmount(parseFloat(tokenAmount), { - shouldEllipse: true, - }); - return translate( - 'pushPlatformNotificationsFundsSentDescription', - amount, - symbol, - ); - }, - }, - erc20_received: { - title: (): string | null => - translate('pushPlatformNotificationsFundsReceivedTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsFundsReceivedDescriptionDefault'), - getDescription: (notification): string | null => { - const symbol = notification?.payload?.data?.token?.symbol; - const tokenAmount = notification?.payload?.data?.token?.amount; - const tokenDecimals = notification?.payload?.data?.token?.decimals; - if (!symbol || !tokenAmount || !tokenDecimals) { - return null; - } - - const amount = getAmount(tokenAmount, tokenDecimals, { - shouldEllipse: true, - }); - return translate( - 'pushPlatformNotificationsFundsReceivedDescription', - amount, - symbol, - ); - }, - }, - eth_received: { - title: (): string | null => - translate('pushPlatformNotificationsFundsReceivedTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsFundsReceivedDescriptionDefault'), - getDescription: (notification): string | null => { - const symbol = notification?.payload?.network?.native_symbol; - const tokenAmount = notification?.payload?.data?.amount?.eth; - if (!symbol || !tokenAmount) { - return null; - } - - const amount = formatAmount(parseFloat(tokenAmount), { - shouldEllipse: true, - }); - return translate( - 'pushPlatformNotificationsFundsReceivedDescription', - amount, - symbol, - ); - }, - }, - metamask_swap_completed: { - title: (): string | null => - translate('pushPlatformNotificationsSwapCompletedTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsSwapCompletedDescription'), - }, - erc721_sent: { - title: (): string | null => - translate('pushPlatformNotificationsNftSentTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsNftSentDescription'), - }, - erc1155_sent: { - title: (): string | null => - translate('pushPlatformNotificationsNftSentTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsNftSentDescription'), - }, - erc721_received: { - title: (): string | null => - translate('pushPlatformNotificationsNftReceivedTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsNftReceivedDescription'), - }, - erc1155_received: { - title: (): string | null => - translate('pushPlatformNotificationsNftReceivedTitle'), - defaultDescription: (): string | null => - translate('pushPlatformNotificationsNftReceivedDescription'), - }, - rocketpool_stake_completed: { - title: (): string | null => - translate( - 'pushPlatformNotificationsStakingRocketpoolStakeCompletedTitle', - ), - defaultDescription: (): string | null => - translate( - 'pushPlatformNotificationsStakingRocketpoolStakeCompletedDescription', - ), - }, - rocketpool_unstake_completed: { - title: (): string | null => - translate( - 'pushPlatformNotificationsStakingRocketpoolUnstakeCompletedTitle', - ), - defaultDescription: (): string | null => - translate( - 'pushPlatformNotificationsStakingRocketpoolUnstakeCompletedDescription', - ), - }, - lido_stake_completed: { - title: (): string | null => - translate('pushPlatformNotificationsStakingLidoStakeCompletedTitle'), - defaultDescription: (): string | null => - translate( - 'pushPlatformNotificationsStakingLidoStakeCompletedDescription', - ), - }, - lido_stake_ready_to_be_withdrawn: { - title: (): string | null => - translate( - 'pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnTitle', - ), - defaultDescription: (): string | null => - translate( - 'pushPlatformNotificationsStakingLidoStakeReadyToBeWithdrawnDescription', - ), - }, - lido_withdrawal_requested: { - title: (): string | null => - translate( - 'pushPlatformNotificationsStakingLidoWithdrawalRequestedTitle', - ), - defaultDescription: (): string | null => - translate( - 'pushPlatformNotificationsStakingLidoWithdrawalRequestedDescription', - ), - }, - lido_withdrawal_completed: { - title: (): string | null => - translate( - 'pushPlatformNotificationsStakingLidoWithdrawalCompletedTitle', - ), - defaultDescription: (): string | null => - translate( - 'pushPlatformNotificationsStakingLidoWithdrawalCompletedDescription', - ), - }, - platform: { - title: (notification): string | null => notification.template.title, - defaultDescription: (notification): string | null => - notification.template.body, - getDescription: (notification): string | null => - notification.template.body, - }, - }; -}; +function hasTemplate( + notification: Types.INotification, +): notification is NotificationWithTemplate { + return Constants.NOTIFICATION_API_TRIGGER_TYPES_SET.has(notification.type); +} /** * Creates a push notification message based on the given on-chain raw notification. * + * Title, description, and optional CTA link are sourced from the notification's + * `template` field provided by the notification API. + * * @param notification - processed notification. - * @param translations - translates keys into text - * @returns The push notification message object, or null if the notification is invalid. + * @returns The push notification message object, or null if the notification has no template. */ export function createOnChainPushNotificationMessage( notification: Types.INotification, - translations: TranslationKeys, ): PushNotificationMessage | null { - if (!notification?.type) { + if (!notification?.type || !hasTemplate(notification)) { return null; } - const notificationMessage = - createOnChainPushNotificationMessages(translations)[notification.type]; - if (!notificationMessage) { - return null; - } - - let description: string | null = null; - try { - description = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - notificationMessage?.getDescription?.(notification as any) ?? - // eslint-disable-next-line @typescript-eslint/no-explicit-any - notificationMessage.defaultDescription?.(notification as any) ?? - null; - } catch { - description = - // eslint-disable-next-line @typescript-eslint/no-explicit-any - notificationMessage.defaultDescription?.(notification as any) ?? null; - } + const { template } = notification; return { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - title: notificationMessage?.title?.(notification as any) ?? '', // Ensure title is always a string - description: description ?? '', // Fallback to empty string if null + title: template.title ?? '', + description: template.body ?? '', + ...(template.cta?.link ? { ctaLink: template.cta.link } : {}), }; }