Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add push notification filtering settings #3207

Merged
merged 12 commits into from
Sep 20, 2023
61 changes: 61 additions & 0 deletions dev/apollo-federation/supergraph.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ interface Account
id: ID!
level: AccountLevel!
limits: AccountLimits!
notificationSettings: NotificationSettings!
realtimePrice: RealtimePrice!
transactions(
"""Returns the items in the list that come after the specified cursor."""
Expand All @@ -56,6 +57,32 @@ type AccountDeletePayload
success: Boolean!
}

input AccountDisableNotificationCategoryInput
@join__type(graph: PUBLIC)
{
category: NotificationCategory!
channel: NotificationChannel
}

input AccountDisableNotificationChannelInput
@join__type(graph: PUBLIC)
{
channel: NotificationChannel!
}

input AccountEnableNotificationCategoryInput
@join__type(graph: PUBLIC)
{
category: NotificationCategory!
channel: NotificationChannel
}

input AccountEnableNotificationChannelInput
@join__type(graph: PUBLIC)
{
channel: NotificationChannel!
}

enum AccountLevel
@join__type(graph: PUBLIC)
{
Expand Down Expand Up @@ -120,6 +147,13 @@ type AccountUpdateDisplayCurrencyPayload
errors: [Error!]!
}

type AccountUpdateNotificationSettingsPayload
@join__type(graph: PUBLIC)
{
account: ConsumerAccount
errors: [Error!]!
}

"""An Opaque Bearer token"""
scalar AuthToken
@join__type(graph: PUBLIC)
Expand Down Expand Up @@ -267,6 +301,7 @@ type ConsumerAccount implements Account
id: ID!
level: AccountLevel!
limits: AccountLimits!
notificationSettings: NotificationSettings!

"""List the quiz questions of the consumer account"""
quiz: [Quiz!]!
Expand Down Expand Up @@ -816,6 +851,10 @@ type Mutation
@join__type(graph: PUBLIC)
{
accountDelete: AccountDeletePayload!
accountDisableNotificationCategory(input: AccountDisableNotificationCategoryInput!): AccountUpdateNotificationSettingsPayload!
accountDisableNotificationChannel(input: AccountDisableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload!
accountEnableNotificationCategory(input: AccountEnableNotificationCategoryInput!): AccountUpdateNotificationSettingsPayload!
accountEnableNotificationChannel(input: AccountEnableNotificationChannelInput!): AccountUpdateNotificationSettingsPayload!
accountUpdateDefaultWalletId(input: AccountUpdateDefaultWalletIdInput!): AccountUpdateDefaultWalletIdPayload!
accountUpdateDisplayCurrency(input: AccountUpdateDisplayCurrencyInput!): AccountUpdateDisplayCurrencyPayload!
callbackEndpointAdd(input: CallbackEndpointAddInput!): CallbackEndpointAddPayload!
Expand Down Expand Up @@ -949,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)
Expand Down
31 changes: 31 additions & 0 deletions src/app/accounts/disable-notification-category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
checkedToNotificationCategory,
disableNotificationCategory as domainDisableNotificationCategory,
} from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose"

export const disableNotificationCategory = async ({
accountId,
notificationChannel,
notificationCategory,
}: {
accountId: AccountId
notificationChannel?: NotificationChannel
notificationCategory: string
}): Promise<Account | ApplicationError> => {
const checkedNotificationCategory = checkedToNotificationCategory(notificationCategory)
if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory

const account = await AccountsRepository().findById(accountId)
if (account instanceof Error) return account

const newNotificationSettings = domainDisableNotificationCategory({
notificationSettings: account.notificationSettings,
notificationChannel,
notificationCategory: checkedNotificationCategory,
})

account.notificationSettings = newNotificationSettings

return AccountsRepository().update(account)
}
23 changes: 23 additions & 0 deletions src/app/accounts/disable-notification-channel.ts
Original file line number Diff line number Diff line change
@@ -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<Account | ApplicationError> => {
const account = await AccountsRepository().findById(accountId)
if (account instanceof Error) return account

const newNotificationSettings = setNotificationChannelIsEnabled({
UncleSamtoshi marked this conversation as resolved.
Show resolved Hide resolved
notificationSettings: account.notificationSettings,
notificationChannel,
enabled: false,
})

account.notificationSettings = newNotificationSettings

return AccountsRepository().update(account)
}
31 changes: 31 additions & 0 deletions src/app/accounts/enable-notification-category.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
checkedToNotificationCategory,
enableNotificationCategory as domainEnableNotificationCategory,
} from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose"

export const enableNotificationCategory = async ({
accountId,
notificationChannel,
notificationCategory,
}: {
accountId: AccountId
notificationChannel?: NotificationChannel
notificationCategory: string
}): Promise<Account | ApplicationError> => {
const checkedNotificationCategory = checkedToNotificationCategory(notificationCategory)
if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory

const account = await AccountsRepository().findById(accountId)
if (account instanceof Error) return account

const newNotificationSettings = domainEnableNotificationCategory({
notificationSettings: account.notificationSettings,
notificationChannel,
notificationCategory: checkedNotificationCategory,
})

account.notificationSettings = newNotificationSettings

return AccountsRepository().update(account)
}
23 changes: 23 additions & 0 deletions src/app/accounts/enable-notification-channel.ts
Original file line number Diff line number Diff line change
@@ -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<Account | ApplicationError> => {
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)
}
4 changes: 4 additions & 0 deletions src/app/accounts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export * from "./update-display-currency"
export * from "./username-available"
export * from "./delete-business-map-info"
export * from "./upgrade-device-account"
export * from "./disable-notification-category"
export * from "./enable-notification-category"
export * from "./enable-notification-channel"
export * from "./disable-notification-channel"

const accounts = AccountsRepository()

Expand Down
1 change: 1 addition & 0 deletions src/app/accounts/send-default-wallet-balance-to-users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const sendDefaultWalletBalanceToAccounts = async () => {
deviceTokens: user.deviceTokens,
displayBalanceAmount: displayAmount,
recipientLanguage: user.language,
notificationSettings: account.notificationSettings,
})

if (result instanceof DeviceTokensNotRegisteredNotificationsServiceError) {
Expand Down
18 changes: 15 additions & 3 deletions src/app/admin/send-admin-push-notification.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { checkedToAccountUuid } from "@domain/accounts"
import {
GaloyNotificationCategories,
nicolasburtey marked this conversation as resolved.
Show resolved Hide resolved
checkedToNotificationCategory,
} from "@domain/notifications"
import { AccountsRepository } from "@services/mongoose/accounts"
import { UsersRepository } from "@services/mongoose/users"
import { NotificationsService } from "@services/notifications"
Expand All @@ -8,12 +12,20 @@ export const sendAdminPushNotification = async ({
title,
body,
data,
notificationCategory,
}: {
accountId: string
title: string
body: string
data?: { [key: string]: string }
notificationCategory?: string
}): Promise<true | ApplicationError> => {
const checkedNotificationCategory = notificationCategory
? checkedToNotificationCategory(notificationCategory)
: GaloyNotificationCategories.AdminPushNotification

if (checkedNotificationCategory instanceof Error) return checkedNotificationCategory

const accountId = checkedToAccountUuid(accountIdRaw)
if (accountId instanceof Error) return accountId

Expand All @@ -26,14 +38,14 @@ export const sendAdminPushNotification = async ({
const user = await usersRepo.findById(kratosUserId)
if (user instanceof Error) return user

const success = await NotificationsService().adminPushNotificationSend({
const success = await NotificationsService().adminPushNotificationFilteredSend({
deviceTokens: user.deviceTokens,
title,
body,
data,
notificationCategory: checkedNotificationCategory,
notificationSettings: account.notificationSettings,
})

if (success instanceof Error) return success

return success
}
1 change: 1 addition & 0 deletions src/app/payments/send-intraledger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ const executePaymentViaIntraledger = async <
recipientDeviceTokens: recipientUser.deviceTokens,
recipientLanguage: recipientUser.language,
paymentAmount: { amount, currency: recipientWallet.currency },
recipientNotificationSettings: recipientAccount.notificationSettings,
displayPaymentAmount: recipientDisplayAmount,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/payments/send-lightning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,7 @@ const executePaymentViaIntraledger = async <
displayPaymentAmount: recipientDisplayAmount,
paymentHash,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: recipientAccount.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/add-pending-on-chain-transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ export const addPendingTransaction = async ({
displayPaymentAmount: settlementDisplayAmount,
txHash: txId,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: account.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/add-settled-on-chain-transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ const addSettledTransactionBeforeFinally = async ({
displayPaymentAmount: displayAmount,
txHash,
recipientDeviceTokens: user.deviceTokens,
recipientNotificationSettings: account.notificationSettings,
recipientLanguage: user.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/send-on-chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ const executePaymentViaIntraledger = async <
paymentAmount: { amount, currency: recipientWalletCurrency },
displayPaymentAmount: recipientDisplayAmount,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: recipientAccount.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/settle-payout-txn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const settlePayout = async (
displayPaymentAmount,
txHash,
senderDeviceTokens: user.deviceTokens,
senderNotificationSettings: account.notificationSettings,
senderLanguage: user.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/app/wallets/update-pending-invoices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ const updatePendingInvoiceBeforeFinally = async ({
displayPaymentAmount,
paymentHash,
recipientDeviceTokens: recipientUser.deviceTokens,
recipientNotificationSettings: recipientAccount.notificationSettings,
recipientLanguage: recipientUser.language,
})

Expand Down
1 change: 1 addition & 0 deletions src/domain/accounts/index.types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type Account = {
readonly isEditor: boolean
readonly quizQuestions: UserQuizQuestion[] // deprecated
readonly quiz: Quiz[]
notificationSettings: NotificationSettings
kratosUserId: UserId
displayCurrency: DisplayCurrency
// temp
Expand Down
2 changes: 2 additions & 0 deletions src/domain/notifications/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ export class NotificationsServiceUnreachableServerError extends NotificationsSer
export class UnknownNotificationsServiceError extends NotificationsError {
level = ErrorLevel.Critical
}

export class InvalidPushNotificationSettingError extends NotificationsError {}
Loading
Loading