From bba12c7083e04d68ddcda91eafca4a60b2ca9b51 Mon Sep 17 00:00:00 2001 From: Sam Peters Date: Mon, 18 Sep 2023 15:15:21 -0500 Subject: [PATCH] chore: refactor interface --- dev/apollo-federation/supergraph.graphql | 76 ++++++++--- ...sable-notification-category-for-channel.ts | 31 +++++ .../accounts/disable-notification-channel.ts | 23 ++++ ...nable-notification-category-for-channel.ts | 31 +++++ .../accounts/enable-notification-channel.ts | 23 ++++ src/app/accounts/index.ts | 5 +- .../send-default-wallet-balance-to-users.ts | 2 +- .../update-push-notification-settings.ts | 28 ---- src/app/admin/send-admin-push-notification.ts | 20 +-- src/app/payments/send-intraledger.ts | 2 +- src/app/payments/send-lightning.ts | 2 +- .../add-pending-on-chain-transaction.ts | 2 +- .../add-settled-on-chain-transaction.ts | 2 +- src/app/wallets/send-on-chain.ts | 2 +- src/app/wallets/settle-payout-txn.ts | 2 +- src/app/wallets/update-pending-invoices.ts | 2 +- src/domain/accounts/index.types.d.ts | 2 +- src/domain/notifications/index.ts | 125 +++++++++++++----- src/domain/notifications/index.types.d.ts | 17 ++- src/domain/primitives/index.types.d.ts | 2 +- .../mutation/admin-push-notification-send.ts | 12 +- src/graphql/admin/schema.graphql | 6 +- src/graphql/public/mutations.ts | 14 +- ...sable-notification-category-for-channel.ts | 60 +++++++++ .../account-disable-notification-channel.ts | 54 ++++++++ ...nable-notification-category-for-channel.ts | 60 +++++++++ .../account-enable-notification-channel.ts | 54 ++++++++ ...count-update-push-notification-settings.ts | 56 -------- src/graphql/public/schema.graphql | 56 +++++--- src/graphql/public/types/abstract/account.ts | 6 +- .../public/types/object/business-account.ts | 8 +- .../public/types/object/consumer-account.ts | 8 +- .../object/notification-channel-settings.ts | 19 +++ .../types/object/notification-settings.ts | 16 +++ .../object/push-notification-settings.ts | 19 --- ...> account-update-notification-settings.ts} | 6 +- .../types/scalar/notification-category.ts | 28 ++++ .../types/scalar/notification-channel.ts | 11 ++ .../types/scalar/push-notification-type.ts | 28 ---- src/services/mongoose/accounts.ts | 16 ++- src/services/mongoose/schema.ts | 21 +-- src/services/mongoose/schema.types.d.ts | 2 +- .../create-push-notification-content.ts | 12 +- src/services/notifications/index.ts | 68 ++++++---- .../notifications/notification-filtering.ts | 17 +++ .../push-notification-filtering.ts | 15 --- .../notifications/push-notifications.ts | 20 ++- .../push-notifications.types.d.ts | 4 +- .../notifications/notification.spec.ts | 30 +++-- .../notifications/push-notification.spec.ts | 14 +- .../wallets/payment-input-validator.spec.ts | 8 +- .../push-notification-filtering.spec.ts | 16 +-- 52 files changed, 792 insertions(+), 371 deletions(-) create mode 100644 src/app/accounts/disable-notification-category-for-channel.ts create mode 100644 src/app/accounts/disable-notification-channel.ts create mode 100644 src/app/accounts/enable-notification-category-for-channel.ts create mode 100644 src/app/accounts/enable-notification-channel.ts delete mode 100644 src/app/accounts/update-push-notification-settings.ts create mode 100644 src/graphql/public/root/mutation/account-disable-notification-category-for-channel.ts create mode 100644 src/graphql/public/root/mutation/account-disable-notification-channel.ts create mode 100644 src/graphql/public/root/mutation/account-enable-notification-category-for-channel.ts create mode 100644 src/graphql/public/root/mutation/account-enable-notification-channel.ts delete mode 100644 src/graphql/public/root/mutation/account-update-push-notification-settings.ts create mode 100644 src/graphql/public/types/object/notification-channel-settings.ts create mode 100644 src/graphql/public/types/object/notification-settings.ts delete mode 100644 src/graphql/public/types/object/push-notification-settings.ts rename src/graphql/public/types/payload/{account-update-push-notification-settings.ts => account-update-notification-settings.ts} (61%) create mode 100644 src/graphql/shared/types/scalar/notification-category.ts create mode 100644 src/graphql/shared/types/scalar/notification-channel.ts delete mode 100644 src/graphql/shared/types/scalar/push-notification-type.ts create mode 100644 src/services/notifications/notification-filtering.ts delete mode 100644 src/services/notifications/push-notification-filtering.ts diff --git a/dev/apollo-federation/supergraph.graphql b/dev/apollo-federation/supergraph.graphql index 577f5d39363..24459835a0a 100644 --- a/dev/apollo-federation/supergraph.graphql +++ b/dev/apollo-federation/supergraph.graphql @@ -31,7 +31,7 @@ interface Account id: ID! level: AccountLevel! limits: AccountLimits! - pushNotificationSettings: PushNotificationSettings! + notificationSettings: NotificationSettings! realtimePrice: RealtimePrice! transactions( """Returns the items in the list that come after the specified cursor.""" @@ -57,6 +57,32 @@ type AccountDeletePayload success: Boolean! } +input AccountDisableNotificationCategoryForChannelInput + @join__type(graph: PUBLIC) +{ + category: NotificationCategory! + channel: NotificationChannel! +} + +input AccountDisableNotificationChannelInput + @join__type(graph: PUBLIC) +{ + channel: NotificationChannel! +} + +input AccountEnableNotificationCategoryForChannelInput + @join__type(graph: PUBLIC) +{ + category: NotificationCategory! + channel: NotificationChannel! +} + +input AccountEnableNotificationChannelInput + @join__type(graph: PUBLIC) +{ + channel: NotificationChannel! +} + enum AccountLevel @join__type(graph: PUBLIC) { @@ -121,14 +147,7 @@ type AccountUpdateDisplayCurrencyPayload errors: [Error!]! } -input AccountUpdatePushNotificationSettingsInput - @join__type(graph: PUBLIC) -{ - disabledPushNotificationTypes: [PushNotificationType]! - pushNotificationsEnabled: Boolean! -} - -type AccountUpdatePushNotificationSettingsPayload +type AccountUpdateNotificationSettingsPayload @join__type(graph: PUBLIC) { account: ConsumerAccount @@ -282,7 +301,7 @@ type ConsumerAccount implements Account id: ID! level: AccountLevel! limits: AccountLimits! - pushNotificationSettings: PushNotificationSettings! + notificationSettings: NotificationSettings! """List the quiz questions of the consumer account""" quiz: [Quiz!]! @@ -832,9 +851,12 @@ type Mutation @join__type(graph: PUBLIC) { accountDelete: AccountDeletePayload! + accountDisableNotificationCategoryForChannel(input: AccountDisableNotificationCategoryForChannelInput!): AccountUpdateNotificationSettingsPayload! + accountDisableNotificationChannel(input: AccountDisableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! + accountEnableNotificationCategoryForChannel(input: AccountEnableNotificationCategoryForChannelInput!): AccountUpdateNotificationSettingsPayload! + accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! - accountUpdatePushNotificationSettings(input: AccountUpdatePushNotificationSettingsInput!): AccountUpdatePushNotificationSettingsPayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! captchaCreateChallenge: CaptchaCreateChallengePayload! @@ -966,6 +988,28 @@ enum Network testnet @join__enumValue(graph: PUBLIC) } +scalar NotificationCategory + @join__type(graph: PUBLIC) + +enum NotificationChannel + @join__type(graph: PUBLIC) +{ + PUSH @join__enumValue(graph: PUBLIC) +} + +type NotificationChannelSettings + @join__type(graph: PUBLIC) +{ + disabledCategories: [NotificationCategory!]! + enabled: Boolean! +} + +type NotificationSettings + @join__type(graph: PUBLIC) +{ + push: NotificationChannelSettings! +} + """An address for an on-chain bitcoin destination""" scalar OnChainAddress @join__type(graph: PUBLIC) @@ -1231,16 +1275,6 @@ type PublicWallet walletCurrency: WalletCurrency! } -type PushNotificationSettings - @join__type(graph: PUBLIC) -{ - disabledPushNotificationTypes: [PushNotificationType]! - pushNotificationsEnabled: Boolean! -} - -scalar PushNotificationType - @join__type(graph: PUBLIC) - type Query @join__type(graph: PUBLIC) { diff --git a/src/app/accounts/disable-notification-category-for-channel.ts b/src/app/accounts/disable-notification-category-for-channel.ts new file mode 100644 index 00000000000..ddbd2a3386e --- /dev/null +++ b/src/app/accounts/disable-notification-category-for-channel.ts @@ -0,0 +1,31 @@ +import { + checkedToNotificationCategory, + disableNotificationCategoryForChannel as disableNotificationCategory, +} from "@domain/notifications" +import { AccountsRepository } from "@services/mongoose" + +export const disableNotificationCategoryForChannel = async ({ + accountId, + notificationChannel, + notificationCategory, +}: { + accountId: AccountId + notificationChannel: NotificationChannel + notificationCategory: string +}): Promise => { + const checkedNotificationCategory = checkedToNotificationCategory(notificationCategory) + if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + + const newNotificationSettings = disableNotificationCategory({ + notificationSettings: account.notificationSettings, + notificationChannel, + notificationCategory: checkedNotificationCategory, + }) + + account.notificationSettings = newNotificationSettings + + return AccountsRepository().update(account) +} diff --git a/src/app/accounts/disable-notification-channel.ts b/src/app/accounts/disable-notification-channel.ts new file mode 100644 index 00000000000..ea7dfa98831 --- /dev/null +++ b/src/app/accounts/disable-notification-channel.ts @@ -0,0 +1,23 @@ +import { setNotificationChannelIsEnabled } from "@domain/notifications" +import { AccountsRepository } from "@services/mongoose" + +export const disableNotificationChannel = async ({ + accountId, + notificationChannel, +}: { + accountId: AccountId + notificationChannel: NotificationChannel +}): Promise => { + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + + const newNotificationSettings = setNotificationChannelIsEnabled({ + notificationSettings: account.notificationSettings, + notificationChannel, + enabled: false, + }) + + account.notificationSettings = newNotificationSettings + + return AccountsRepository().update(account) +} diff --git a/src/app/accounts/enable-notification-category-for-channel.ts b/src/app/accounts/enable-notification-category-for-channel.ts new file mode 100644 index 00000000000..90aac3a1e9a --- /dev/null +++ b/src/app/accounts/enable-notification-category-for-channel.ts @@ -0,0 +1,31 @@ +import { + checkedToNotificationCategory, + enableNotificationCategoryForChannel as enableNotificationChannel, +} from "@domain/notifications" +import { AccountsRepository } from "@services/mongoose" + +export const enableNotificationCategoryForChannel = async ({ + accountId, + notificationChannel, + notificationCategory, +}: { + accountId: AccountId + notificationChannel: NotificationChannel + notificationCategory: string +}): Promise => { + const checkedNotificationCategory = checkedToNotificationCategory(notificationCategory) + if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory + + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + + const newNotificationSettings = enableNotificationChannel({ + notificationSettings: account.notificationSettings, + notificationChannel, + notificationCategory: checkedNotificationCategory, + }) + + account.notificationSettings = newNotificationSettings + + return AccountsRepository().update(account) +} diff --git a/src/app/accounts/enable-notification-channel.ts b/src/app/accounts/enable-notification-channel.ts new file mode 100644 index 00000000000..761fd421e2e --- /dev/null +++ b/src/app/accounts/enable-notification-channel.ts @@ -0,0 +1,23 @@ +import { setNotificationChannelIsEnabled } from "@domain/notifications" +import { AccountsRepository } from "@services/mongoose" + +export const enableNotificationChannel = async ({ + accountId, + notificationChannel, +}: { + accountId: AccountId + notificationChannel: NotificationChannel +}): Promise => { + const account = await AccountsRepository().findById(accountId) + if (account instanceof Error) return account + + const newNotificationSettings = setNotificationChannelIsEnabled({ + notificationSettings: account.notificationSettings, + notificationChannel, + enabled: true, + }) + + account.notificationSettings = newNotificationSettings + + return AccountsRepository().update(account) +} diff --git a/src/app/accounts/index.ts b/src/app/accounts/index.ts index 60192e4401c..acbf3d7f18e 100644 --- a/src/app/accounts/index.ts +++ b/src/app/accounts/index.ts @@ -21,7 +21,10 @@ export * from "./update-display-currency" export * from "./username-available" export * from "./delete-business-map-info" export * from "./upgrade-device-account" -export * from "./update-push-notification-settings" +export * from "./disable-notification-category-for-channel" +export * from "./enable-notification-category-for-channel" +export * from "./enable-notification-channel" +export * from "./disable-notification-channel" const accounts = AccountsRepository() diff --git a/src/app/accounts/send-default-wallet-balance-to-users.ts b/src/app/accounts/send-default-wallet-balance-to-users.ts index 3257ebc0431..9a74f279ac0 100644 --- a/src/app/accounts/send-default-wallet-balance-to-users.ts +++ b/src/app/accounts/send-default-wallet-balance-to-users.ts @@ -64,7 +64,7 @@ export const sendDefaultWalletBalanceToAccounts = async () => { deviceTokens: user.deviceTokens, displayBalanceAmount: displayAmount, recipientLanguage: user.language, - pushNotificationSettings: account.pushNotificationSettings, + notificationSettings: account.notificationSettings, }) if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) { diff --git a/src/app/accounts/update-push-notification-settings.ts b/src/app/accounts/update-push-notification-settings.ts deleted file mode 100644 index cc5e5d0dd05..00000000000 --- a/src/app/accounts/update-push-notification-settings.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { checkedToPushNotificationSettings } from "@domain/notifications" - -import { AccountsRepository } from "@services/mongoose" - -export const updatePushNotificationSettings = async ({ - accountId, - pushNotificationsEnabled, - disabledPushNotificationTypes, -}: { - accountId: AccountId - pushNotificationsEnabled: boolean - disabledPushNotificationTypes: string[] -}): Promise => { - const checkedPushNotificationSettings = checkedToPushNotificationSettings({ - disabledPushNotificationTypes, - pushNotificationsEnabled, - }) - - if (checkedPushNotificationSettings instanceof Error) - return checkedPushNotificationSettings - - const account = await AccountsRepository().findById(accountId) - if (account instanceof Error) return account - - account.pushNotificationSettings = checkedPushNotificationSettings - - return AccountsRepository().update(account) -} diff --git a/src/app/admin/send-admin-push-notification.ts b/src/app/admin/send-admin-push-notification.ts index 3ee36eddcd0..56942144239 100644 --- a/src/app/admin/send-admin-push-notification.ts +++ b/src/app/admin/send-admin-push-notification.ts @@ -1,7 +1,7 @@ import { checkedToAccountUuid } from "@domain/accounts" import { - GaloyPushNotifications, - checkedToPushNotificationType, + GaloyNotificationCategories, + checkedToNotificationCategory, } from "@domain/notifications" import { AccountsRepository } from "@services/mongoose/accounts" import { UsersRepository } from "@services/mongoose/users" @@ -12,19 +12,19 @@ export const sendAdminPushNotification = async ({ title, body, data, - pushNotificationType, + notificationCategory, }: { accountId: string title: string body: string data?: { [key: string]: string } - pushNotificationType?: string + notificationCategory?: string }): Promise => { - const checkedPushNotificationType = pushNotificationType - ? checkedToPushNotificationType(pushNotificationType) - : GaloyPushNotifications.AdminPushNotification + const checkedNotificationCategory = notificationCategory + ? checkedToNotificationCategory(notificationCategory) + : GaloyNotificationCategories.AdminPushNotification - if (checkedPushNotificationType instanceof Error) return checkedPushNotificationType + if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory const accountId = checkedToAccountUuid(accountIdRaw) if (accountId instanceof Error) return accountId @@ -43,8 +43,8 @@ export const sendAdminPushNotification = async ({ title, body, data, - pushNotificationType: checkedPushNotificationType, - pushNotificationSettings: account.pushNotificationSettings, + notificationCategory: checkedNotificationCategory, + notificationSettings: account.notificationSettings, }) return success diff --git a/src/app/payments/send-intraledger.ts b/src/app/payments/send-intraledger.ts index 620598c0c41..c78ff870090 100644 --- a/src/app/payments/send-intraledger.ts +++ b/src/app/payments/send-intraledger.ts @@ -365,7 +365,7 @@ const executePaymentViaIntraledger = async < recipientDeviceTokens: recipientUser.deviceTokens, recipientLanguage: recipientUser.language, paymentAmount: { amount, currency: recipientWallet.currency }, - recipientPushNotificationSettings: recipientAccount.pushNotificationSettings, + recipientNotificationSettings: recipientAccount.notificationSettings, displayPaymentAmount: recipientDisplayAmount, }) diff --git a/src/app/payments/send-lightning.ts b/src/app/payments/send-lightning.ts index 1e5755ce848..9369d04f2fb 100644 --- a/src/app/payments/send-lightning.ts +++ b/src/app/payments/send-lightning.ts @@ -531,7 +531,7 @@ const executePaymentViaIntraledger = async < displayPaymentAmount: recipientDisplayAmount, paymentHash, recipientDeviceTokens: recipientUser.deviceTokens, - recipientPushNotificationSettings: recipientAccount.pushNotificationSettings, + recipientNotificationSettings: recipientAccount.notificationSettings, recipientLanguage: recipientUser.language, }) diff --git a/src/app/wallets/add-pending-on-chain-transaction.ts b/src/app/wallets/add-pending-on-chain-transaction.ts index 5c54f941763..53f5ed07cf4 100644 --- a/src/app/wallets/add-pending-on-chain-transaction.ts +++ b/src/app/wallets/add-pending-on-chain-transaction.ts @@ -152,7 +152,7 @@ export const addPendingTransaction = async ({ displayPaymentAmount: settlementDisplayAmount, txHash: txId, recipientDeviceTokens: recipientUser.deviceTokens, - recipientPushNotificationSettings: account.pushNotificationSettings, + recipientNotificationSettings: account.notificationSettings, recipientLanguage: recipientUser.language, }) diff --git a/src/app/wallets/add-settled-on-chain-transaction.ts b/src/app/wallets/add-settled-on-chain-transaction.ts index 3945455c01f..3c049f67a97 100644 --- a/src/app/wallets/add-settled-on-chain-transaction.ts +++ b/src/app/wallets/add-settled-on-chain-transaction.ts @@ -179,7 +179,7 @@ const addSettledTransactionBeforeFinally = async ({ displayPaymentAmount: displayAmount, txHash, recipientDeviceTokens: user.deviceTokens, - recipientPushNotificationSettings: account.pushNotificationSettings, + recipientNotificationSettings: account.notificationSettings, recipientLanguage: user.language, }) diff --git a/src/app/wallets/send-on-chain.ts b/src/app/wallets/send-on-chain.ts index 8f51376dc46..9ef46b62d19 100644 --- a/src/app/wallets/send-on-chain.ts +++ b/src/app/wallets/send-on-chain.ts @@ -430,7 +430,7 @@ const executePaymentViaIntraledger = async < paymentAmount: { amount, currency: recipientWalletCurrency }, displayPaymentAmount: recipientDisplayAmount, recipientDeviceTokens: recipientUser.deviceTokens, - recipientPushNotificationSettings: recipientAccount.pushNotificationSettings, + recipientNotificationSettings: recipientAccount.notificationSettings, recipientLanguage: recipientUser.language, }) diff --git a/src/app/wallets/settle-payout-txn.ts b/src/app/wallets/settle-payout-txn.ts index cff43088f1f..2d5c7a6d658 100644 --- a/src/app/wallets/settle-payout-txn.ts +++ b/src/app/wallets/settle-payout-txn.ts @@ -59,7 +59,7 @@ export const settlePayout = async ( displayPaymentAmount, txHash, senderDeviceTokens: user.deviceTokens, - senderPushNotificationSettings: account.pushNotificationSettings, + senderNotificationSettings: account.notificationSettings, senderLanguage: user.language, }) diff --git a/src/app/wallets/update-pending-invoices.ts b/src/app/wallets/update-pending-invoices.ts index b0aa80a254f..5645bdc0813 100644 --- a/src/app/wallets/update-pending-invoices.ts +++ b/src/app/wallets/update-pending-invoices.ts @@ -309,7 +309,7 @@ const updatePendingInvoiceBeforeFinally = async ({ displayPaymentAmount, paymentHash, recipientDeviceTokens: recipientUser.deviceTokens, - recipientPushNotificationSettings: recipientAccount.pushNotificationSettings, + recipientNotificationSettings: recipientAccount.notificationSettings, recipientLanguage: recipientUser.language, }) diff --git a/src/domain/accounts/index.types.d.ts b/src/domain/accounts/index.types.d.ts index b85b5117cde..c7ea17b3c39 100644 --- a/src/domain/accounts/index.types.d.ts +++ b/src/domain/accounts/index.types.d.ts @@ -77,7 +77,7 @@ type Account = { readonly isEditor: boolean readonly quizQuestions: UserQuizQuestion[] // deprecated readonly quiz: Quiz[] - pushNotificationSettings: PushNotificationSettings // QUESTION: should this be here or should the notification service have its own storage of notification settings? + notificationSettings: NotificationSettings kratosUserId: UserId displayCurrency: DisplayCurrency // temp diff --git a/src/domain/notifications/index.ts b/src/domain/notifications/index.ts index efefe18fea4..2e2bfdbc09c 100644 --- a/src/domain/notifications/index.ts +++ b/src/domain/notifications/index.ts @@ -1,4 +1,4 @@ -import { InvalidPushNotificationSettingError } from "./errors" +import { InvalidPushNotificationSettingError as InvalidNotificationSettingsError } from "./errors" export * from "./errors" @@ -11,39 +11,19 @@ export const NotificationType = { LnInvoicePaid: "paid-invoice", } as const -export const checkedToPushNotificationSettings = ({ - disabledPushNotificationTypes, - pushNotificationsEnabled, -}: { - disabledPushNotificationTypes: string[] - pushNotificationsEnabled: boolean -}): PushNotificationSettings | InvalidPushNotificationSettingError => { - const checkedDisabledPushNotificationTypes: PushNotificationType[] = [] - - for (const pushNotification of disabledPushNotificationTypes) { - const checkedPushNotification = checkedToPushNotificationType(pushNotification) - if (checkedPushNotification instanceof Error) { - return checkedPushNotification - } else { - checkedDisabledPushNotificationTypes.push(checkedPushNotification) - } - } - - return { - pushNotificationsEnabled, - disabledPushNotificationTypes: checkedDisabledPushNotificationTypes, - } -} +export const NotificationChannel = { + Push: "push", +} as const -export const GaloyPushNotifications = { - Payments: "Payments" as PushNotificationType, - Balance: "Balance" as PushNotificationType, - AdminPushNotification: "AdminPushNotification" as PushNotificationType, +export const GaloyNotificationCategories = { + Payments: "Payments" as NotificationCategory, + Balance: "Balance" as NotificationCategory, + AdminPushNotification: "AdminPushNotification" as NotificationCategory, } as const -export const mapNotificationTypeToPushNotificationType = ( +export const mapNotificationTypeToNotificationCategory = ( notificationType: NotificationType, -): PushNotificationType => { +): NotificationCategory => { switch (notificationType) { case NotificationType.IntraLedgerReceipt: case NotificationType.IntraLedgerPayment: @@ -51,17 +31,92 @@ export const mapNotificationTypeToPushNotificationType = ( case NotificationType.OnchainReceiptPending: case NotificationType.LnInvoicePaid: case NotificationType.OnchainPayment: - return GaloyPushNotifications.Payments + return GaloyNotificationCategories.Payments } } -export const checkedToPushNotificationType = ( +export const checkedToNotificationCategory = ( type: string, -): PushNotificationType | ValidationError => { +): NotificationCategory | ValidationError => { // TODO: add validation if (!type) { - return new InvalidPushNotificationSettingError("Invalid notification type") + return new InvalidNotificationSettingsError("Invalid notification category") + } + + return type as NotificationCategory +} + +export const setNotificationChannelIsEnabled = ({ + notificationSettings, + notificationChannel, + enabled, +}: { + notificationSettings: NotificationSettings + notificationChannel: NotificationChannel + enabled: boolean +}): NotificationSettings => { + const notificationChannelSettings = notificationSettings[notificationChannel] + const enabledChanged = notificationChannelSettings.enabled !== enabled + + const newNotificationSettings = { + enabled, + disabledCategories: enabledChanged + ? [] + : notificationChannelSettings.disabledCategories, } - return type as PushNotificationType + return { + ...notificationSettings, + [notificationChannel]: newNotificationSettings, + } +} + +export const enableNotificationCategoryForChannel = ({ + notificationSettings, + notificationChannel, + notificationCategory, +}: { + notificationSettings: NotificationSettings + notificationChannel: NotificationChannel + notificationCategory: NotificationCategory +}): NotificationSettings => { + const notificationChannelSettings = notificationSettings[notificationChannel] + const disabledCategories = notificationChannelSettings.disabledCategories + + const newNotificationSettings = { + enabled: notificationChannelSettings.enabled, + disabledCategories: disabledCategories.filter( + (category) => category !== notificationCategory, + ), + } + + return { + ...notificationSettings, + [notificationChannel]: newNotificationSettings, + } +} + +export const disableNotificationCategoryForChannel = ({ + notificationSettings, + notificationChannel, + notificationCategory, +}: { + notificationSettings: NotificationSettings + notificationChannel: NotificationChannel + notificationCategory: NotificationCategory +}): NotificationSettings => { + const notificationChannelSettings = notificationSettings[notificationChannel] + const disabledCategories = notificationChannelSettings.disabledCategories + disabledCategories.push(notificationCategory) + const uniqueDisabledCategories = [...new Set(disabledCategories)] + + const newNotificationSettings = { + enabled: notificationChannelSettings.enabled, + disabledCategories: uniqueDisabledCategories, + } + + return { + ...notificationSettings, + [notificationChannel]: newNotificationSettings, + } } diff --git a/src/domain/notifications/index.types.d.ts b/src/domain/notifications/index.types.d.ts index 83dd2885cc6..d66a2463541 100644 --- a/src/domain/notifications/index.types.d.ts +++ b/src/domain/notifications/index.types.d.ts @@ -13,7 +13,7 @@ type TransactionReceivedNotificationBaseArgs = TransactionNotificationBaseArgs & recipientAccountId: AccountId recipientWalletId: WalletId recipientDeviceTokens: DeviceToken[] - recipientPushNotificationSettings: PushNotificationSettings + recipientNotificationSettings: NotificationSettings recipientLanguage: UserLanguageOrEmpty } @@ -21,7 +21,7 @@ type TransactionSentNotificationBaseArgs = TransactionNotificationBaseArgs & { senderAccountId: AccountId senderWalletId: WalletId senderDeviceTokens: DeviceToken[] - senderPushNotificationSettings: PushNotificationSettings + senderNotificationSettings: NotificationSettings senderLanguage: UserLanguageOrEmpty } @@ -43,7 +43,7 @@ type OnChainTxSentArgs = TransactionSentNotificationBaseArgs & OnChainTxBaseArgs type SendBalanceArgs = { balanceAmount: BalanceAmount deviceTokens: DeviceToken[] - pushNotificationSettings: PushNotificationSettings + notificationSettings: NotificationSettings displayBalanceAmount?: DisplayAmount recipientLanguage: UserLanguageOrEmpty } @@ -80,7 +80,12 @@ interface INotificationsService { ): Promise } -type PushNotificationSettings = { - pushNotificationsEnabled: boolean - disabledPushNotificationTypes: PushNotificationType[] +type NotificationChannel = + (typeof import("./index").NotificationChannel)[keyof typeof import("./index").NotificationChannel] + +type NotificationSettings = Record + +type NotificationChannelSettings = { + enabled: boolean + disabledCategories: NotificationCategory[] } diff --git a/src/domain/primitives/index.types.d.ts b/src/domain/primitives/index.types.d.ts index 7bfa578b587..b6d59b19dc6 100644 --- a/src/domain/primitives/index.types.d.ts +++ b/src/domain/primitives/index.types.d.ts @@ -11,7 +11,7 @@ type MilliSeconds = number & { readonly brand: unique symbol } type Days = number & { readonly brand: unique symbol } type JwtToken = string & { readonly brand: unique symbol } // short lived asymmetric token from oathkeeper type Memo = string & { readonly brand: unique symbol } -type PushNotificationType = string & { readonly brand: unique symbol } +type NotificationCategory = string & { readonly brand: unique symbol } type XOR = | (T1 & { [k in Exclude]?: never }) diff --git a/src/graphql/admin/root/mutation/admin-push-notification-send.ts b/src/graphql/admin/root/mutation/admin-push-notification-send.ts index a4d530dabcf..41123f5bdc2 100644 --- a/src/graphql/admin/root/mutation/admin-push-notification-send.ts +++ b/src/graphql/admin/root/mutation/admin-push-notification-send.ts @@ -3,7 +3,7 @@ import { GT } from "@graphql/index" import AdminPushNotificationSendPayload from "@graphql/admin/types/payload/admin-push-notification-send" import { Admin } from "@app" import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" -import PushNotificationType from "@graphql/shared/types/scalar/push-notification-type" +import NotificationCategory from "@graphql/shared/types/scalar/notification-category" const AdminPushNotificationSendInput = GT.Input({ name: "AdminPushNotificationSendInput", @@ -20,8 +20,8 @@ const AdminPushNotificationSendInput = GT.Input({ data: { type: GT.Scalar(Object), }, - pushNotificationType: { - type: PushNotificationType, + notificationCategory: { + type: NotificationCategory, }, }), }) @@ -35,7 +35,7 @@ const AdminPushNotificationSendMutation = GT.Field< title: string body: string data?: { [key: string]: string } - pushNotificationType?: string + notificationCategory?: string } } >({ @@ -47,14 +47,14 @@ const AdminPushNotificationSendMutation = GT.Field< input: { type: GT.NonNull(AdminPushNotificationSendInput) }, }, resolve: async (_, args) => { - const { accountId, body, title, data, pushNotificationType } = args.input + const { accountId, body, title, data, notificationCategory } = args.input const success = await Admin.sendAdminPushNotification({ accountId, title, body, data, - pushNotificationType, + notificationCategory, }) if (success instanceof Error) { diff --git a/src/graphql/admin/schema.graphql b/src/graphql/admin/schema.graphql index 689277df456..0998abf30a9 100644 --- a/src/graphql/admin/schema.graphql +++ b/src/graphql/admin/schema.graphql @@ -32,7 +32,7 @@ input AdminPushNotificationSendInput { accountId: String! body: String! data: Object - pushNotificationType: PushNotificationType + notificationCategory: NotificationCategory title: String! } @@ -253,6 +253,8 @@ type Mutation { userUpdatePhone(input: UserUpdatePhoneInput!): AccountDetailPayload! } +scalar NotificationCategory + scalar Object """An address for an on-chain bitcoin destination""" @@ -304,8 +306,6 @@ type PriceOfOneSettlementMinorUnitInDisplayMinorUnit implements PriceInterface { offset: Int! } -scalar PushNotificationType - type Query { accountDetailsByAccountId(accountId: ID!): AuditedAccount! accountDetailsByEmail(email: EmailAddress!): AuditedAccount! diff --git a/src/graphql/public/mutations.ts b/src/graphql/public/mutations.ts index 9188aea5931..35722fe5056 100644 --- a/src/graphql/public/mutations.ts +++ b/src/graphql/public/mutations.ts @@ -49,7 +49,10 @@ import UserTotpDeleteMutation from "@graphql/public/root/mutation/user-totp-dele import CallbackEndpointAdd from "./root/mutation/callback-endpoint-add" import CallbackEndpointDelete from "./root/mutation/callback-endpoint-delete" -import AccountUpdatePushNotificationSettingsMutation from "./root/mutation/account-update-push-notification-settings" +import AccountEnableNotificationCategoryForChannelMutation from "./root/mutation/account-enable-notification-category-for-channel" +import AccountDisableNotificationCategoryForChannelMutation from "./root/mutation/account-disable-notification-category-for-channel" +import AccountEnableNotificationChannelMutation from "./root/mutation/account-enable-notification-channel" +import AccountDisableNotificationChannelMutation from "./root/mutation/account-disable-notification-channel" // TODO: // const fields: { [key: string]: GraphQLFieldConfig } export const mutationFields = { @@ -87,8 +90,13 @@ export const mutationFields = { userContactUpdateAlias: UserContactUpdateAliasMutation, accountUpdateDefaultWalletId: AccountUpdateDefaultWalletIdMutation, accountUpdateDisplayCurrency: AccountUpdateDisplayCurrencyMutation, - accountUpdatePushNotificationSettings: - AccountUpdatePushNotificationSettingsMutation, + accountEnableNotificationCategoryForChannel: + AccountEnableNotificationCategoryForChannelMutation, + accountDisableNotificationCategoryForChannel: + AccountDisableNotificationCategoryForChannelMutation, + accountEnableNotificationChannel: AccountEnableNotificationChannelMutation, + accountDisableNotificationChannel: AccountDisableNotificationChannelMutation, + accountDelete: AccountDeleteMutation, feedbackSubmit: FeedbackSubmitMutation, diff --git a/src/graphql/public/root/mutation/account-disable-notification-category-for-channel.ts b/src/graphql/public/root/mutation/account-disable-notification-category-for-channel.ts new file mode 100644 index 00000000000..4c0d82e3fa4 --- /dev/null +++ b/src/graphql/public/root/mutation/account-disable-notification-category-for-channel.ts @@ -0,0 +1,60 @@ +import { GT } from "@graphql/index" + +import AccountUpdateNotificationSettingsPayload from "@graphql/public/types/payload/account-update-notification-settings" +import { Accounts } from "@app/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import NotificationChannel from "@graphql/shared/types/scalar/notification-channel" +import NotificationCategory from "@graphql/shared/types/scalar/notification-category" + +const AccountDisableNotificationCategoryForChannelInput = GT.Input({ + name: "AccountDisableNotificationCategoryForChannelInput", + fields: () => ({ + channel: { + type: GT.NonNull(NotificationChannel), + }, + category: { + type: GT.NonNull(NotificationCategory), + }, + }), +}) + +const AccountDisableNotificationCategoryForChannelMutation = GT.Field< + null, + GraphQLPublicContextAuth, + { + input: { + channel: NotificationChannel | Error + category: NotificationCategory + } + } +>({ + extensions: { + complexity: 120, + }, + type: GT.NonNull(AccountUpdateNotificationSettingsPayload), + args: { + input: { type: GT.NonNull(AccountDisableNotificationCategoryForChannelInput) }, + }, + resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => { + const { channel, category } = args.input + + if (channel instanceof Error) return { errors: [{ message: channel.message }] } + + const result = await Accounts.disableNotificationCategoryForChannel({ + accountId: domainAccount.id, + notificationChannel: channel, + notificationCategory: category, + }) + + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { + errors: [], + account: result, + } + }, +}) + +export default AccountDisableNotificationCategoryForChannelMutation diff --git a/src/graphql/public/root/mutation/account-disable-notification-channel.ts b/src/graphql/public/root/mutation/account-disable-notification-channel.ts new file mode 100644 index 00000000000..52fcbfc49e5 --- /dev/null +++ b/src/graphql/public/root/mutation/account-disable-notification-channel.ts @@ -0,0 +1,54 @@ +import { GT } from "@graphql/index" + +import AccountUpdateNotificationSettingsPayload from "@graphql/public/types/payload/account-update-notification-settings" +import { Accounts } from "@app/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import NotificationChannel from "@graphql/shared/types/scalar/notification-channel" + +const AccountDisableNotificationChannelInput = GT.Input({ + name: "AccountDisableNotificationChannelInput", + fields: () => ({ + channel: { + type: GT.NonNull(NotificationChannel), + }, + }), +}) + +const AccountDisableNotificationChannelMutation = GT.Field< + null, + GraphQLPublicContextAuth, + { + input: { + channel: NotificationChannel | Error + } + } +>({ + extensions: { + complexity: 120, + }, + type: GT.NonNull(AccountUpdateNotificationSettingsPayload), + args: { + input: { type: GT.NonNull(AccountDisableNotificationChannelInput) }, + }, + resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => { + const { channel } = args.input + + if (channel instanceof Error) return { errors: [{ message: channel.message }] } + + const result = await Accounts.disableNotificationChannel({ + accountId: domainAccount.id, + notificationChannel: channel, + }) + + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { + errors: [], + account: result, + } + }, +}) + +export default AccountDisableNotificationChannelMutation diff --git a/src/graphql/public/root/mutation/account-enable-notification-category-for-channel.ts b/src/graphql/public/root/mutation/account-enable-notification-category-for-channel.ts new file mode 100644 index 00000000000..89c72b799a1 --- /dev/null +++ b/src/graphql/public/root/mutation/account-enable-notification-category-for-channel.ts @@ -0,0 +1,60 @@ +import { GT } from "@graphql/index" + +import AccountUpdateNotificationSettingsPayload from "@graphql/public/types/payload/account-update-notification-settings" +import { Accounts } from "@app/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import NotificationChannel from "@graphql/shared/types/scalar/notification-channel" +import NotificationCategory from "@graphql/shared/types/scalar/notification-category" + +const AccountEnableNotificationCategoryForChannelInput = GT.Input({ + name: "AccountEnableNotificationCategoryForChannelInput", + fields: () => ({ + channel: { + type: GT.NonNull(NotificationChannel), + }, + category: { + type: GT.NonNull(NotificationCategory), + }, + }), +}) + +const AccountEnableNotificationCategoryForChannelMutation = GT.Field< + null, + GraphQLPublicContextAuth, + { + input: { + channel: NotificationChannel | Error + category: NotificationCategory + } + } +>({ + extensions: { + complexity: 120, + }, + type: GT.NonNull(AccountUpdateNotificationSettingsPayload), + args: { + input: { type: GT.NonNull(AccountEnableNotificationCategoryForChannelInput) }, + }, + resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => { + const { channel, category } = args.input + + if (channel instanceof Error) return { errors: [{ message: channel.message }] } + + const result = await Accounts.enableNotificationCategoryForChannel({ + accountId: domainAccount.id, + notificationChannel: channel, + notificationCategory: category, + }) + + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { + errors: [], + account: result, + } + }, +}) + +export default AccountEnableNotificationCategoryForChannelMutation diff --git a/src/graphql/public/root/mutation/account-enable-notification-channel.ts b/src/graphql/public/root/mutation/account-enable-notification-channel.ts new file mode 100644 index 00000000000..3102a2656d2 --- /dev/null +++ b/src/graphql/public/root/mutation/account-enable-notification-channel.ts @@ -0,0 +1,54 @@ +import { GT } from "@graphql/index" + +import AccountUpdateNotificationSettingsPayload from "@graphql/public/types/payload/account-update-notification-settings" +import { Accounts } from "@app/index" +import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" +import NotificationChannel from "@graphql/shared/types/scalar/notification-channel" + +const AccountEnableNotificationChannelInput = GT.Input({ + name: "AccountEnableNotificationChannelInput", + fields: () => ({ + channel: { + type: GT.NonNull(NotificationChannel), + }, + }), +}) + +const AccountEnableNotificationChannelMutation = GT.Field< + null, + GraphQLPublicContextAuth, + { + input: { + channel: NotificationChannel | Error + } + } +>({ + extensions: { + complexity: 120, + }, + type: GT.NonNull(AccountUpdateNotificationSettingsPayload), + args: { + input: { type: GT.NonNull(AccountEnableNotificationChannelInput) }, + }, + resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => { + const { channel } = args.input + + if (channel instanceof Error) return { errors: [{ message: channel.message }] } + + const result = await Accounts.enableNotificationChannel({ + accountId: domainAccount.id, + notificationChannel: channel, + }) + + if (result instanceof Error) { + return { errors: [mapAndParseErrorForGqlResponse(result)] } + } + + return { + errors: [], + account: result, + } + }, +}) + +export default AccountEnableNotificationChannelMutation diff --git a/src/graphql/public/root/mutation/account-update-push-notification-settings.ts b/src/graphql/public/root/mutation/account-update-push-notification-settings.ts deleted file mode 100644 index 9e10c4fa232..00000000000 --- a/src/graphql/public/root/mutation/account-update-push-notification-settings.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { GT } from "@graphql/index" - -import PushNotificationType from "@graphql/shared/types/scalar/push-notification-type" -import AccountUpdatePushNotificationSettingsPayload from "@graphql/public/types/payload/account-update-push-notification-settings" -import { Accounts } from "@app/index" -import { mapAndParseErrorForGqlResponse } from "@graphql/error-map" - -const AccountUpdatePushNotificationSettingsInput = GT.Input({ - name: "AccountUpdatePushNotificationSettingsInput", - fields: () => ({ - pushNotificationsEnabled: { - type: GT.NonNull(GT.Boolean), - }, - disabledPushNotificationTypes: { - type: GT.NonNull(GT.List(PushNotificationType)), - }, - }), -}) - -const AccountUpdatePushNotificationSettingsMutation = GT.Field< - null, - GraphQLPublicContextAuth, - { - input: { - disabledPushNotificationTypes: string[] - pushNotificationsEnabled: boolean - } - } ->({ - extensions: { - complexity: 120, - }, - type: GT.NonNull(AccountUpdatePushNotificationSettingsPayload), - args: { - input: { type: GT.NonNull(AccountUpdatePushNotificationSettingsInput) }, - }, - resolve: async (_, args, { domainAccount }: { domainAccount: Account }) => { - const { disabledPushNotificationTypes, pushNotificationsEnabled } = args.input - const result = await Accounts.updatePushNotificationSettings({ - accountId: domainAccount.id, - disabledPushNotificationTypes, - pushNotificationsEnabled, - }) - - if (result instanceof Error) { - return { errors: [mapAndParseErrorForGqlResponse(result)] } - } - - return { - errors: [], - account: result, - } - }, -}) - -export default AccountUpdatePushNotificationSettingsMutation diff --git a/src/graphql/public/schema.graphql b/src/graphql/public/schema.graphql index 29ea30988d3..17d9d3b8ab4 100644 --- a/src/graphql/public/schema.graphql +++ b/src/graphql/public/schema.graphql @@ -6,7 +6,7 @@ interface Account { id: ID! level: AccountLevel! limits: AccountLimits! - pushNotificationSettings: PushNotificationSettings! + notificationSettings: NotificationSettings! realtimePrice: RealtimePrice! transactions( """Returns the items in the list that come after the specified cursor.""" @@ -30,6 +30,24 @@ type AccountDeletePayload { success: Boolean! } +input AccountDisableNotificationCategoryForChannelInput { + category: NotificationCategory! + channel: NotificationChannel! +} + +input AccountDisableNotificationChannelInput { + channel: NotificationChannel! +} + +input AccountEnableNotificationCategoryForChannelInput { + category: NotificationCategory! + channel: NotificationChannel! +} + +input AccountEnableNotificationChannelInput { + channel: NotificationChannel! +} + enum AccountLevel { ONE TWO @@ -80,12 +98,7 @@ type AccountUpdateDisplayCurrencyPayload { errors: [Error!]! } -input AccountUpdatePushNotificationSettingsInput { - disabledPushNotificationTypes: [PushNotificationType]! - pushNotificationsEnabled: Boolean! -} - -type AccountUpdatePushNotificationSettingsPayload { +type AccountUpdateNotificationSettingsPayload { account: ConsumerAccount errors: [Error!]! } @@ -209,7 +222,7 @@ type ConsumerAccount implements Account { id: ID! level: AccountLevel! limits: AccountLimits! - pushNotificationSettings: PushNotificationSettings! + notificationSettings: NotificationSettings! """List the quiz questions of the consumer account""" quiz: [Quiz!]! @@ -632,9 +645,12 @@ type MobileVersions { type Mutation { accountDelete: AccountDeletePayload! + accountDisableNotificationCategoryForChannel(input: AccountDisableNotificationCategoryForChannelInput!): AccountUpdateNotificationSettingsPayload! + accountDisableNotificationChannel(input: AccountDisableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! + accountEnableNotificationCategoryForChannel(input: AccountEnableNotificationCategoryForChannelInput!): AccountUpdateNotificationSettingsPayload! + accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload! accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload! accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload! - accountUpdatePushNotificationSettings(input: AccountUpdatePushNotificationSettingsInput!): AccountUpdatePushNotificationSettingsPayload! callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload! callbackEndpointDelete(input: CallbackEndpointDeleteInput!): SuccessPayload! captchaCreateChallenge: CaptchaCreateChallengePayload! @@ -762,6 +778,21 @@ enum Network { testnet } +scalar NotificationCategory + +enum NotificationChannel { + PUSH +} + +type NotificationChannelSettings { + disabledCategories: [NotificationCategory!]! + enabled: Boolean! +} + +type NotificationSettings { + push: NotificationChannelSettings! +} + """An address for an on-chain bitcoin destination""" scalar OnChainAddress @@ -966,13 +997,6 @@ type PublicWallet { walletCurrency: WalletCurrency! } -type PushNotificationSettings { - disabledPushNotificationTypes: [PushNotificationType]! - pushNotificationsEnabled: Boolean! -} - -scalar PushNotificationType - type Query { accountDefaultWallet(username: Username!, walletCurrency: WalletCurrency): PublicWallet! btcPrice(currency: DisplayCurrency! = "USD"): Price @deprecated(reason: "Deprecated in favor of realtimePrice") diff --git a/src/graphql/public/types/abstract/account.ts b/src/graphql/public/types/abstract/account.ts index 232fa9ea61c..8b96aadd8f2 100644 --- a/src/graphql/public/types/abstract/account.ts +++ b/src/graphql/public/types/abstract/account.ts @@ -11,7 +11,7 @@ import AccountLevel from "../../../shared/types/scalar/account-level" import Wallet from "../../../shared/types/abstract/wallet" import CallbackEndpoint from "../object/callback-endpoint" -import { PushNotificationSettings } from "../object/push-notification-settings" +import { NotificationSettings } from "../object/notification-settings" const IAccount = GT.Interface({ name: "Account", @@ -57,8 +57,8 @@ const IAccount = GT.Interface({ }, }, }, - pushNotificationSettings: { - type: GT.NonNull(PushNotificationSettings), + notificationSettings: { + type: GT.NonNull(NotificationSettings), }, // FUTURE-PLAN: Support a `users: [User!]!` field here diff --git a/src/graphql/public/types/object/business-account.ts b/src/graphql/public/types/object/business-account.ts index 1cdf714f072..934c4c52e7e 100644 --- a/src/graphql/public/types/object/business-account.ts +++ b/src/graphql/public/types/object/business-account.ts @@ -28,7 +28,7 @@ import AccountLevel from "../../../shared/types/scalar/account-level" import { TransactionConnection } from "../../../shared/types/object/transaction" import RealtimePrice from "./realtime-price" -import { PushNotificationSettings } from "./push-notification-settings" +import { NotificationSettings } from "./notification-settings" const BusinessAccount = GT.Object({ name: "BusinessAccount", @@ -160,9 +160,9 @@ const BusinessAccount = GT.Object({ ) }, }, - pushNotificationSettings: { - type: GT.NonNull(PushNotificationSettings), - resolve: (source) => source.pushNotificationSettings, + notificationSettings: { + type: GT.NonNull(NotificationSettings), + resolve: (source) => source.notificationSettings, }, }), }) diff --git a/src/graphql/public/types/object/consumer-account.ts b/src/graphql/public/types/object/consumer-account.ts index 3d35976365d..64acb10e1cf 100644 --- a/src/graphql/public/types/object/consumer-account.ts +++ b/src/graphql/public/types/object/consumer-account.ts @@ -32,7 +32,7 @@ import { TransactionConnection } from "../../../shared/types/object/transaction" import AccountLimits from "./account-limits" import Quiz from "./quiz" import CallbackEndpoint from "./callback-endpoint" -import { PushNotificationSettings } from "./push-notification-settings" +import { NotificationSettings } from "./notification-settings" const ConsumerAccount = GT.Object({ name: "ConsumerAccount", @@ -184,9 +184,9 @@ const ConsumerAccount = GT.Object({ }, }, - pushNotificationSettings: { - type: GT.NonNull(PushNotificationSettings), - resolve: (source) => source.pushNotificationSettings, + notificationSettings: { + type: GT.NonNull(NotificationSettings), + resolve: (source) => source.notificationSettings, }, }), }) diff --git a/src/graphql/public/types/object/notification-channel-settings.ts b/src/graphql/public/types/object/notification-channel-settings.ts new file mode 100644 index 00000000000..09d965b5a19 --- /dev/null +++ b/src/graphql/public/types/object/notification-channel-settings.ts @@ -0,0 +1,19 @@ +import { GT } from "@graphql/index" +import NotificationCategory from "@graphql/shared/types/scalar/notification-category" + +export const NotificationChannelSettings = GT.Object< + NotificationChannelSettings, + GraphQLPublicContextAuth +>({ + name: "NotificationChannelSettings", + fields: () => ({ + enabled: { + type: GT.NonNull(GT.Boolean), + resolve: (source) => source.enabled, + }, + disabledCategories: { + type: GT.NonNullList(NotificationCategory), + resolve: (source) => source.disabledCategories, + }, + }), +}) diff --git a/src/graphql/public/types/object/notification-settings.ts b/src/graphql/public/types/object/notification-settings.ts new file mode 100644 index 00000000000..231238ba5b3 --- /dev/null +++ b/src/graphql/public/types/object/notification-settings.ts @@ -0,0 +1,16 @@ +import { GT } from "@graphql/index" + +import { NotificationChannelSettings } from "./notification-channel-settings" + +export const NotificationSettings = GT.Object< + NotificationSettings, + GraphQLPublicContextAuth +>({ + name: "NotificationSettings", + fields: () => ({ + push: { + type: GT.NonNull(NotificationChannelSettings), + resolve: (source) => source.push, + }, + }), +}) diff --git a/src/graphql/public/types/object/push-notification-settings.ts b/src/graphql/public/types/object/push-notification-settings.ts deleted file mode 100644 index 2603756a8fa..00000000000 --- a/src/graphql/public/types/object/push-notification-settings.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { GT } from "@graphql/index" -import PushNotificationType from "@graphql/shared/types/scalar/push-notification-type" - -export const PushNotificationSettings = GT.Object< - PushNotificationSettings, - GraphQLPublicContextAuth ->({ - name: "PushNotificationSettings", - fields: () => ({ - pushNotificationsEnabled: { - type: GT.NonNull(GT.Boolean), - resolve: (source) => source.pushNotificationsEnabled, - }, - disabledPushNotificationTypes: { - type: GT.NonNull(GT.NonNullList(PushNotificationType)), - resolve: (source) => source.disabledPushNotificationTypes, - }, - }), -}) diff --git a/src/graphql/public/types/payload/account-update-push-notification-settings.ts b/src/graphql/public/types/payload/account-update-notification-settings.ts similarity index 61% rename from src/graphql/public/types/payload/account-update-push-notification-settings.ts rename to src/graphql/public/types/payload/account-update-notification-settings.ts index 61bf2a84df9..0ef91e31244 100644 --- a/src/graphql/public/types/payload/account-update-push-notification-settings.ts +++ b/src/graphql/public/types/payload/account-update-notification-settings.ts @@ -3,8 +3,8 @@ import { GT } from "@graphql/index" import IError from "../../../shared/types/abstract/error" import ConsumerAccount from "../object/consumer-account" -const AccountUpdatePushNotificationSettingsPayload = GT.Object({ - name: "AccountUpdatePushNotificationSettingsPayload", +const AccountUpdateNotificationSettingsPayload = GT.Object({ + name: "AccountUpdateNotificationSettingsPayload", fields: () => ({ errors: { type: GT.NonNullList(IError), @@ -15,4 +15,4 @@ const AccountUpdatePushNotificationSettingsPayload = GT.Object({ }), }) -export default AccountUpdatePushNotificationSettingsPayload +export default AccountUpdateNotificationSettingsPayload diff --git a/src/graphql/shared/types/scalar/notification-category.ts b/src/graphql/shared/types/scalar/notification-category.ts new file mode 100644 index 00000000000..27207eeccd8 --- /dev/null +++ b/src/graphql/shared/types/scalar/notification-category.ts @@ -0,0 +1,28 @@ +import { InputValidationError } from "@graphql/error" +import { GT } from "@graphql/index" + +const NotificationCategory = GT.Scalar({ + name: "NotificationCategory", + parseValue(value) { + if (typeof value !== "string") { + return new InputValidationError({ + message: "Invalid type for NotificationCategory", + }) + } + return validNotificationCategory(value) + }, + parseLiteral(ast) { + if (ast.kind === GT.Kind.STRING) { + return validNotificationCategory(ast.value) + } + return new InputValidationError({ message: "Invalid type for NotificationCategory" }) + }, +}) + +function validNotificationCategory( + value: string, +): NotificationCategory | InputValidationError { + return value as NotificationCategory +} + +export default NotificationCategory diff --git a/src/graphql/shared/types/scalar/notification-channel.ts b/src/graphql/shared/types/scalar/notification-channel.ts new file mode 100644 index 00000000000..8481ea5205f --- /dev/null +++ b/src/graphql/shared/types/scalar/notification-channel.ts @@ -0,0 +1,11 @@ +import { NotificationChannel as NotificationChannelDomain } from "@domain/notifications" +import { GT } from "@graphql/index" + +const NotificationChannel = GT.Enum({ + name: "NotificationChannel", + values: { + PUSH: { value: NotificationChannelDomain.Push }, + }, +}) + +export default NotificationChannel diff --git a/src/graphql/shared/types/scalar/push-notification-type.ts b/src/graphql/shared/types/scalar/push-notification-type.ts deleted file mode 100644 index 4731c2f84ae..00000000000 --- a/src/graphql/shared/types/scalar/push-notification-type.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { InputValidationError } from "@graphql/error" -import { GT } from "@graphql/index" - -const PushNotificationType = GT.Scalar({ - name: "PushNotificationType", - parseValue(value) { - if (typeof value !== "string") { - return new InputValidationError({ - message: "Invalid type for PushNotificationType", - }) - } - return validPushNotificationType(value) - }, - parseLiteral(ast) { - if (ast.kind === GT.Kind.STRING) { - return validPushNotificationType(ast.value) - } - return new InputValidationError({ message: "Invalid type for PushNotificationType" }) - }, -}) - -function validPushNotificationType( - value: string, -): PushNotificationType | InputValidationError { - return value as PushNotificationType -} - -export default PushNotificationType diff --git a/src/services/mongoose/accounts.ts b/src/services/mongoose/accounts.ts index a1e01459e0d..1d8cec8fa19 100644 --- a/src/services/mongoose/accounts.ts +++ b/src/services/mongoose/accounts.ts @@ -118,7 +118,7 @@ export const AccountsRepository = (): IAccountsRepository => { withdrawFee, kratosUserId, displayCurrency, - pushNotificationSettings, + notificationSettings, role, }: Account): Promise => { @@ -143,7 +143,7 @@ export const AccountsRepository = (): IAccountsRepository => { withdrawFee, kratosUserId, displayCurrency, - pushNotificationSettings, + notificationSettings, role, }, @@ -227,12 +227,14 @@ const translateToAccount = (result: AccountRecord): Account => ({ ), withdrawFee: result.withdrawFee as Satoshis, isEditor: result.role === "editor", - pushNotificationSettings: - result.pushNotificationSettings || + notificationSettings: + result.notificationSettings || ({ - pushNotificationsEnabled: true, - disabledPushNotificationTypes: [], - } as PushNotificationSettings), + push: { + enabled: true, + disabledCategories: [], + }, + } as NotificationSettings), // TODO: remove quizQuestions: diff --git a/src/services/mongoose/schema.ts b/src/services/mongoose/schema.ts index f4ec82868b3..7b6bc919f5f 100644 --- a/src/services/mongoose/schema.ts +++ b/src/services/mongoose/schema.ts @@ -259,16 +259,19 @@ const AccountSchema = new Schema( }, ], }, - pushNotificationSettings: { + notificationSettings: { type: { - pushNotificationsEnabled: { - type: Boolean, - default: true, - }, - disabledPushNotificationTypes: { - type: [String], - required: true, - default: [], + push: { + type: { + enabled: { + type: Boolean, + default: true, + }, + disabledCategories: { + type: [String], + default: [], + }, + }, }, }, }, diff --git a/src/services/mongoose/schema.types.d.ts b/src/services/mongoose/schema.types.d.ts index 21be2e55ca4..d6b87d4662e 100644 --- a/src/services/mongoose/schema.types.d.ts +++ b/src/services/mongoose/schema.types.d.ts @@ -84,7 +84,7 @@ interface AccountRecord { onchain: OnChainObjectForUser[] defaultWalletId: WalletId displayCurrency?: string - pushNotificationSettings?: PushNotificationSettings + notificationSettings?: NotificationSettings // business: title?: string diff --git a/src/services/notifications/create-push-notification-content.ts b/src/services/notifications/create-push-notification-content.ts index 2a05b68b006..99c87501589 100644 --- a/src/services/notifications/create-push-notification-content.ts +++ b/src/services/notifications/create-push-notification-content.ts @@ -3,10 +3,6 @@ import { getI18nInstance } from "@config" import { getCurrencyMajorExponent, MajorExponent } from "@domain/fiat" import { WalletCurrency } from "@domain/shared" import { getLanguageOrDefault } from "@domain/locale" -import { - GaloyPushNotifications, - mapNotificationTypeToPushNotificationType, -} from "@domain/notifications" const i18n = getI18nInstance() @@ -37,7 +33,6 @@ export const createPushNotificationContent = ({ }): { title: string body: string - pushNotificationType: PushNotificationType } => { const locale = getLanguageOrDefault(userLanguage) const baseCurrency = amount.currency @@ -92,10 +87,5 @@ export const createPushNotificationContent = ({ ) } - const pushNotificationType = - type === "balance" - ? GaloyPushNotifications.Balance - : mapNotificationTypeToPushNotificationType(type) - - return { title, body, pushNotificationType } + return { title, body } } diff --git a/src/services/notifications/index.ts b/src/services/notifications/index.ts index 92306a0f50e..f6fd7738792 100644 --- a/src/services/notifications/index.ts +++ b/src/services/notifications/index.ts @@ -2,7 +2,11 @@ import { toSats } from "@domain/bitcoin" import { WalletCurrency } from "@domain/shared" import { toCents, UsdDisplayCurrency } from "@domain/fiat" import { customPubSubTrigger, PubSubDefaultTriggers } from "@domain/pubsub" -import { NotificationsServiceError, NotificationType } from "@domain/notifications" +import { + GaloyNotificationCategories, + NotificationsServiceError, + NotificationType, +} from "@domain/notifications" import { PubSubService } from "@services/pubsub" import { wrapAsyncFunctionsToRunInSpan } from "@services/tracing" @@ -24,7 +28,7 @@ export const NotificationsService = (): INotificationsService => { displayPaymentAmount, paymentHash, recipientDeviceTokens, - recipientPushNotificationSettings, + recipientNotificationSettings, recipientLanguage, }: LightningTxReceivedArgs): Promise => { try { @@ -55,7 +59,9 @@ export const NotificationsService = (): INotificationsService => { }) if (recipientDeviceTokens && recipientDeviceTokens.length > 0) { - const { title, body, pushNotificationType } = createPushNotificationContent({ + const notificationCategory = GaloyNotificationCategories.Payments + + const { title, body } = createPushNotificationContent({ type: NotificationType.LnInvoicePaid, userLanguage: recipientLanguage, amount: paymentAmount, @@ -66,8 +72,8 @@ export const NotificationsService = (): INotificationsService => { deviceTokens: recipientDeviceTokens, title, body, - pushNotificationType, - pushNotificationSettings: recipientPushNotificationSettings, + notificationCategory, + notificationSettings: recipientNotificationSettings, }) if (result instanceof NotificationsServiceError) { @@ -89,7 +95,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, recipientDeviceTokens, - recipientPushNotificationSettings, + recipientNotificationSettings, recipientLanguage, }: IntraLedgerTxReceivedArgs): Promise => { try { @@ -124,7 +130,9 @@ export const NotificationsService = (): INotificationsService => { }) if (recipientDeviceTokens && recipientDeviceTokens.length > 0) { - const { title, body, pushNotificationType } = createPushNotificationContent({ + const notificationCategory = GaloyNotificationCategories.Payments + + const { title, body } = createPushNotificationContent({ type: NotificationType.IntraLedgerReceipt, userLanguage: recipientLanguage, amount: paymentAmount, @@ -135,8 +143,8 @@ export const NotificationsService = (): INotificationsService => { deviceTokens: recipientDeviceTokens, title, body, - pushNotificationType, - pushNotificationSettings: recipientPushNotificationSettings, + notificationCategory, + notificationSettings: recipientNotificationSettings, }) if (result instanceof NotificationsServiceError) { @@ -159,7 +167,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, deviceTokens, - pushNotificationSettings, + notificationSettings, language, txHash, }: { @@ -169,7 +177,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount: PaymentAmount displayPaymentAmount?: DisplayAmount deviceTokens: DeviceToken[] - pushNotificationSettings: PushNotificationSettings + notificationSettings: NotificationSettings language: UserLanguageOrEmpty txHash: OnChainTxHash }): Promise => { @@ -200,7 +208,9 @@ export const NotificationsService = (): INotificationsService => { }) if (deviceTokens.length > 0) { - const { title, body, pushNotificationType } = createPushNotificationContent({ + const notificationCategory = GaloyNotificationCategories.Payments + + const { title, body } = createPushNotificationContent({ type, userLanguage: language, amount: paymentAmount, @@ -211,8 +221,8 @@ export const NotificationsService = (): INotificationsService => { deviceTokens, title, body, - pushNotificationType, - pushNotificationSettings, + notificationCategory, + notificationSettings, }) if (result instanceof NotificationsServiceError) { @@ -234,7 +244,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, recipientDeviceTokens, - recipientPushNotificationSettings, + recipientNotificationSettings, recipientLanguage, txHash, }: OnChainTxReceivedArgs) => @@ -245,7 +255,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, deviceTokens: recipientDeviceTokens, - pushNotificationSettings: recipientPushNotificationSettings, + notificationSettings: recipientNotificationSettings, language: recipientLanguage, txHash, }) @@ -256,7 +266,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, recipientDeviceTokens, - recipientPushNotificationSettings, + recipientNotificationSettings, recipientLanguage, txHash, }: OnChainTxReceivedPendingArgs) => @@ -267,7 +277,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, deviceTokens: recipientDeviceTokens, - pushNotificationSettings: recipientPushNotificationSettings, + notificationSettings: recipientNotificationSettings, language: recipientLanguage, txHash, }) @@ -278,7 +288,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, senderDeviceTokens, - senderPushNotificationSettings, + senderNotificationSettings, senderLanguage, txHash, }: OnChainTxSentArgs) => @@ -289,7 +299,7 @@ export const NotificationsService = (): INotificationsService => { paymentAmount, displayPaymentAmount, deviceTokens: senderDeviceTokens, - pushNotificationSettings: senderPushNotificationSettings, + notificationSettings: senderNotificationSettings, language: senderLanguage, txHash, }) @@ -329,7 +339,7 @@ export const NotificationsService = (): INotificationsService => { const sendBalance = async ({ balanceAmount, deviceTokens, - pushNotificationSettings, + notificationSettings, displayBalanceAmount, recipientLanguage, }: SendBalanceArgs): Promise => { @@ -337,7 +347,9 @@ export const NotificationsService = (): INotificationsService => { if (!hasDeviceTokens) return true try { - const { title, body, pushNotificationType } = createPushNotificationContent({ + const notificationCategory = GaloyNotificationCategories.Payments + + const { title, body } = createPushNotificationContent({ type: "balance", userLanguage: recipientLanguage, amount: balanceAmount, @@ -348,8 +360,8 @@ export const NotificationsService = (): INotificationsService => { deviceTokens, title, body, - pushNotificationType, - pushNotificationSettings, + notificationCategory, + notificationSettings, }) if (result instanceof NotificationsServiceError) { @@ -388,8 +400,8 @@ export const NotificationsService = (): INotificationsService => { body, data, deviceTokens, - pushNotificationSettings, - pushNotificationType, + notificationSettings, + notificationCategory, }: SendFilteredPushNotificationArgs): Promise => { const hasDeviceTokens = deviceTokens && deviceTokens.length > 0 if (!hasDeviceTokens) return true @@ -400,8 +412,8 @@ export const NotificationsService = (): INotificationsService => { title, body, data, - pushNotificationSettings, - pushNotificationType, + notificationSettings, + notificationCategory, }) if (result instanceof NotificationsServiceError) { diff --git a/src/services/notifications/notification-filtering.ts b/src/services/notifications/notification-filtering.ts new file mode 100644 index 00000000000..1ecf682fa5a --- /dev/null +++ b/src/services/notifications/notification-filtering.ts @@ -0,0 +1,17 @@ +export const shouldSendNotification = ({ + notificationChannel, + notificationSettings, + notificationCategory, +}: { + notificationChannel: NotificationChannel + notificationSettings: NotificationSettings + notificationCategory: NotificationCategory +}): boolean => { + const channelNotificationSettings = notificationSettings[notificationChannel] + + if (channelNotificationSettings.enabled) { + return !channelNotificationSettings.disabledCategories.includes(notificationCategory) + } + + return false +} diff --git a/src/services/notifications/push-notification-filtering.ts b/src/services/notifications/push-notification-filtering.ts deleted file mode 100644 index 07057ef8bd8..00000000000 --- a/src/services/notifications/push-notification-filtering.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const shouldSendPushNotification = ({ - pushNotificationSettings, - pushNotificationType, -}: { - pushNotificationSettings: PushNotificationSettings - pushNotificationType: PushNotificationType -}): boolean => { - if (pushNotificationSettings.pushNotificationsEnabled) { - return !pushNotificationSettings.disabledPushNotificationTypes.includes( - pushNotificationType, - ) - } - - return false -} diff --git a/src/services/notifications/push-notifications.ts b/src/services/notifications/push-notifications.ts index 71d3f0669db..d66323f494e 100644 --- a/src/services/notifications/push-notifications.ts +++ b/src/services/notifications/push-notifications.ts @@ -3,6 +3,7 @@ import * as admin from "firebase-admin" import { DeviceTokensNotRegisteredNotificationsServiceError, InvalidDeviceNotificationsServiceError, + NotificationChannel, NotificationsServiceError, NotificationsServiceUnreachableServerError, UnknownNotificationsServiceError, @@ -18,7 +19,7 @@ import { Messaging } from "firebase-admin/lib/messaging/messaging" import { GOOGLE_APPLICATION_CREDENTIALS } from "@config" -import { shouldSendPushNotification } from "./push-notification-filtering" +import { shouldSendNotification } from "./notification-filtering" const logger = baseLogger.child({ module: "notifications" }) @@ -116,17 +117,14 @@ export const PushNotificationsService = (): IPushNotificationsService => { } const sendFilteredNotification = async (args: SendFilteredPushNotificationArgs) => { - const { - pushNotificationSettings, - pushNotificationType, - data, - ...sendNotificationArgs - } = args + const { notificationSettings, notificationCategory, data, ...sendNotificationArgs } = + args if ( - !shouldSendPushNotification({ - pushNotificationSettings, - pushNotificationType, + !shouldSendNotification({ + notificationCategory, + notificationSettings, + notificationChannel: NotificationChannel.Push, }) ) { return { @@ -138,7 +136,7 @@ export const PushNotificationsService = (): IPushNotificationsService => { ...sendNotificationArgs, data: { ...data, - PushNotificationType: pushNotificationType, + NotificationCategory: notificationCategory, }, }) diff --git a/src/services/notifications/push-notifications.types.d.ts b/src/services/notifications/push-notifications.types.d.ts index e3514a37164..c8a8dd928f5 100644 --- a/src/services/notifications/push-notifications.types.d.ts +++ b/src/services/notifications/push-notifications.types.d.ts @@ -10,8 +10,8 @@ type SendFilteredPushNotificationArgs = { title: string body: string data?: { [key: string]: string } - pushNotificationSettings: PushNotificationSettings - pushNotificationType: PushNotificationType + notificationSettings: NotificationSettings + notificationCategory: NotificationCategory } type SendFilteredPushNotificationStatus = diff --git a/test/legacy-integration/notifications/notification.spec.ts b/test/legacy-integration/notifications/notification.spec.ts index c03d239718e..c6b599734c5 100644 --- a/test/legacy-integration/notifications/notification.spec.ts +++ b/test/legacy-integration/notifications/notification.spec.ts @@ -18,7 +18,7 @@ import { getCurrentPriceAsDisplayPriceRatio, } from "@app/prices" import { WalletCurrency } from "@domain/shared" -import { GaloyPushNotifications } from "@domain/notifications" +import { GaloyNotificationCategories } from "@domain/notifications" let spy let displayPriceRatios: Record> @@ -44,9 +44,11 @@ const crcDisplayPaymentAmount = { displayInMajor: "3500.50", } -const unfilteredPushNotificationSettings: PushNotificationSettings = { - pushNotificationsEnabled: true, - disabledPushNotificationTypes: [], +const unfilteredNotificationSettings: NotificationSettings = { + push: { + enabled: true, + disabledCategories: [], + } } beforeAll(async () => { @@ -221,7 +223,7 @@ describe("notification", () => { displayPaymentAmount: crcDisplayPaymentAmount, paymentHash, recipientDeviceTokens: deviceTokens, - recipientPushNotificationSettings: unfilteredPushNotificationSettings, + recipientNotificationSettings: unfilteredNotificationSettings, recipientLanguage: language, }) @@ -229,7 +231,7 @@ describe("notification", () => { expect(sendFilteredNotification.mock.calls[0][0].title).toBe(title) expect(sendFilteredNotification.mock.calls[0][0].body).toBe(body) expect(sendFilteredNotification.mock.calls[0][0].pushNotificationType).toBe( - GaloyPushNotifications.Payments, + GaloyNotificationCategories.Payments, ) }), ) @@ -268,7 +270,7 @@ describe("notification", () => { recipientWalletId: walletId, displayPaymentAmount: crcDisplayPaymentAmount, recipientDeviceTokens: deviceTokens, - recipientPushNotificationSettings: unfilteredPushNotificationSettings, + recipientNotificationSettings: unfilteredNotificationSettings, recipientLanguage: language, }) @@ -276,7 +278,7 @@ describe("notification", () => { expect(sendFilteredNotification.mock.calls[0][0].title).toBe(title) expect(sendFilteredNotification.mock.calls[0][0].body).toBe(body) expect(sendFilteredNotification.mock.calls[0][0].pushNotificationType).toBe( - GaloyPushNotifications.Payments, + GaloyNotificationCategories.Payments, ) }), ) @@ -316,7 +318,7 @@ describe("notification", () => { displayPaymentAmount: crcDisplayPaymentAmount, txHash, recipientDeviceTokens: deviceTokens, - recipientPushNotificationSettings: unfilteredPushNotificationSettings, + recipientNotificationSettings: unfilteredNotificationSettings, recipientLanguage: language, }) @@ -324,7 +326,7 @@ describe("notification", () => { expect(sendFilteredNotification.mock.calls[0][0].title).toBe(title) expect(sendFilteredNotification.mock.calls[0][0].body).toBe(body) expect(sendFilteredNotification.mock.calls[0][0].pushNotificationType).toBe( - GaloyPushNotifications.Payments, + GaloyNotificationCategories.Payments, ) }), ) @@ -363,7 +365,7 @@ describe("notification", () => { txHash, displayPaymentAmount: crcDisplayPaymentAmount, recipientDeviceTokens: deviceTokens, - recipientPushNotificationSettings: unfilteredPushNotificationSettings, + recipientNotificationSettings: unfilteredNotificationSettings, recipientLanguage: language, }) @@ -371,7 +373,7 @@ describe("notification", () => { expect(sendFilteredNotification.mock.calls[0][0].title).toBe(title) expect(sendFilteredNotification.mock.calls[0][0].body).toBe(body) expect(sendFilteredNotification.mock.calls[0][0].pushNotificationType).toBe( - GaloyPushNotifications.Payments, + GaloyNotificationCategories.Payments, ) }), ) @@ -410,7 +412,7 @@ describe("notification", () => { txHash, displayPaymentAmount: crcDisplayPaymentAmount, senderDeviceTokens: deviceTokens, - senderPushNotificationSettings: unfilteredPushNotificationSettings, + senderNotificationSettings: unfilteredNotificationSettings, senderLanguage: language, }) @@ -418,7 +420,7 @@ describe("notification", () => { expect(sendFilteredNotification.mock.calls[0][0].title).toBe(title) expect(sendFilteredNotification.mock.calls[0][0].body).toBe(body) expect(sendFilteredNotification.mock.calls[0][0].pushNotificationType).toBe( - GaloyPushNotifications.Payments, + GaloyNotificationCategories.Payments, ) }), ) diff --git a/test/legacy-integration/notifications/push-notification.spec.ts b/test/legacy-integration/notifications/push-notification.spec.ts index dd8ba46f439..551306e512a 100644 --- a/test/legacy-integration/notifications/push-notification.spec.ts +++ b/test/legacy-integration/notifications/push-notification.spec.ts @@ -5,18 +5,20 @@ import { describe("push notification", () => { it("should filter a notification", async () => { - const pushNotificationType = "transaction" as PushNotificationType - const pushNotificationSettings = { - pushNotificationsEnabled: true, - disabledPushNotificationTypes: [pushNotificationType], + const notificationCategory = "transaction" as NotificationCategory + const notificationSettings = { + push: { + enabled: true, + disabledCategories: [notificationCategory], + } } const result = await PushNotificationsService().sendFilteredNotification({ body: "body", title: "title", deviceTokens: ["deviceToken" as DeviceToken], - pushNotificationType, - pushNotificationSettings, + notificationCategory, + notificationSettings, }) expect(result).toEqual({ diff --git a/test/unit/domain/wallets/payment-input-validator.spec.ts b/test/unit/domain/wallets/payment-input-validator.spec.ts index a2aeae7ed07..098fb9c81a8 100644 --- a/test/unit/domain/wallets/payment-input-validator.spec.ts +++ b/test/unit/domain/wallets/payment-input-validator.spec.ts @@ -22,9 +22,11 @@ describe("PaymentInputValidator", () => { latitude: 0, longitude: 0, }, - pushNotificationSettings: { - pushNotificationsEnabled: true, - disabledPushNotificationTypes: [], + notificationSettings: { + push: { + enabled: true, + disabledCategories: [], + } }, contactEnabled: true, contacts: [], diff --git a/test/unit/services/notifications/push-notification-filtering.spec.ts b/test/unit/services/notifications/push-notification-filtering.spec.ts index d0d31468dad..2c7ab4cbbc7 100644 --- a/test/unit/services/notifications/push-notification-filtering.spec.ts +++ b/test/unit/services/notifications/push-notification-filtering.spec.ts @@ -1,4 +1,4 @@ -import { shouldSendPushNotification } from "@services/notifications/push-notification-filtering" +import { shouldSendNotification } from "@services/notifications/notification-filtering" describe("Notifications - push notification filtering", () => { describe("shouldSendPushNotification", () => { @@ -8,10 +8,10 @@ describe("Notifications - push notification filtering", () => { disabledPushNotificationTypes: [], } - const pushNotificationType = "transaction" as PushNotificationType + const pushNotificationType = "transaction" as NotificationCategory expect( - shouldSendPushNotification({ + shouldSendNotification({ pushNotificationSettings, pushNotificationType, }), @@ -25,10 +25,10 @@ describe("Notifications - push notification filtering", () => { disabledPushNotificationTypes: [], } - const pushNotificationType = "transaction" as PushNotificationType + const pushNotificationType = "transaction" as NotificationCategory expect( - shouldSendPushNotification({ + shouldSendNotification({ pushNotificationSettings, pushNotificationType, }), @@ -38,13 +38,13 @@ describe("Notifications - push notification filtering", () => { it("returns false when a notification is disabled", () => { const pushNotificationSettings = { pushNotificationsEnabled: true, - disabledPushNotificationTypes: ["transaction" as PushNotificationType], + disabledPushNotificationTypes: ["transaction" as NotificationCategory], } - const pushNotificationType = "transaction" as PushNotificationType + const pushNotificationType = "transaction" as NotificationCategory expect( - shouldSendPushNotification({ + shouldSendNotification({ pushNotificationSettings, pushNotificationType, }),