Move private user out of getDestinationsForUser

This commit is contained in:
Ian Philips 2022-09-12 08:34:16 -06:00
parent deb0d4a2d2
commit f18d5825c2
4 changed files with 103 additions and 80 deletions

View File

@ -1,4 +1,5 @@
import { notification_subscription_types } from 'common/user'
import { notification_subscription_types, PrivateUser } from './user'
import { DOMAIN } from './envs/constants'
export type Notification = {
id: string
@ -53,6 +54,7 @@ export type notification_source_update_types =
| 'deleted'
| 'closed'
/* Optional - if possible use a keyof notification_subscription_types */
export type notification_reason_types =
| 'tagged_user'
| 'on_new_follow'
@ -90,7 +92,11 @@ export type notification_reason_types =
| 'your_contract_closed'
| 'subsidized_your_market'
// Adding a new key:value here is optional, you can also just use a key of notification_subscription_types
// Adding a new key:value here is optional, you can just use a key of notification_subscription_types
// You might want to add a key:value here if there will be multiple notification reasons that map to the same
// subscription type, i.e. 'comment_on_contract_you_follow' and 'comment_on_contract_with_users_answer' both map to
// 'all_comments_on_watched_markets' subscription type
// TODO: perhaps better would be to map notification_subscription_types to arrays of notification_reason_types
export const notificationReasonToSubscriptionType: Partial<
Record<notification_reason_types, keyof notification_subscription_types>
> = {
@ -127,3 +133,27 @@ export const notificationReasonToSubscriptionType: Partial<
reply_to_users_answer: 'all_replies_to_my_answers_on_watched_markets',
reply_to_users_comment: 'all_replies_to_my_comments_on_watched_markets',
}
export const getDestinationsForUser = async (
privateUser: PrivateUser,
reason: notification_reason_types | keyof notification_subscription_types
) => {
const notificationSettings = privateUser.notificationSubscriptionTypes
let destinations
let subscriptionType: keyof notification_subscription_types | undefined
if (Object.keys(notificationSettings).includes(reason)) {
subscriptionType = reason as keyof notification_subscription_types
destinations = notificationSettings[subscriptionType]
} else {
const key = reason as notification_reason_types
subscriptionType = notificationReasonToSubscriptionType[key]
destinations = subscriptionType
? notificationSettings[subscriptionType]
: []
}
return {
sendToEmail: destinations.includes('email'),
sendToBrowser: destinations.includes('browser'),
urlToManageThisNotification: `${DOMAIN}/notifications?section=${subscriptionType}`,
}
}

View File

@ -1,10 +1,10 @@
import * as admin from 'firebase-admin'
import {
getDestinationsForUser,
Notification,
notification_reason_types,
notificationReasonToSubscriptionType,
} from '../../common/notification'
import { notification_subscription_types, User } from '../../common/user'
import { User } from '../../common/user'
import { Contract } from '../../common/contract'
import { getPrivateUser, getValues } from './utils'
import { Comment } from '../../common/comment'
@ -23,7 +23,6 @@ import {
sendNewAnswerEmail,
sendNewCommentEmail,
} from './emails'
import { DOMAIN } from 'common/lib/envs/constants'
const firestore = admin.firestore()
type recipients_to_reason_texts = {
@ -61,8 +60,12 @@ export const createNotification = async (
) => {
for (const userId in userToReasonTexts) {
const { reason } = userToReasonTexts[userId]
const { sendToBrowser, sendToEmail, privateUser } =
await getDestinationsForUser(userId, reason)
const privateUser = await getPrivateUser(userId)
if (!privateUser) continue
const { sendToBrowser, sendToEmail } = await getDestinationsForUser(
privateUser,
reason
)
if (sendToBrowser) {
const notificationRef = firestore
.collection(`/users/${userId}/notifications`)
@ -93,7 +96,12 @@ export const createNotification = async (
if (!sendToEmail) continue
if (reason === 'your_contract_closed' && privateUser && sourceContract) {
await sendMarketCloseEmail(reason, sourceUser, sourceContract)
await sendMarketCloseEmail(
reason,
sourceUser,
privateUser,
sourceContract
)
} else if (reason === 'tagged_user') {
// TODO: send email to tagged user in new contract
} else if (reason === 'subsidized_your_market') {
@ -197,35 +205,6 @@ export const createNotification = async (
await sendNotificationsIfSettingsPermit(userToReasonTexts)
}
export const getDestinationsForUser = async (
userId: string,
reason: notification_reason_types | keyof notification_subscription_types
) => {
const privateUser = await getPrivateUser(userId)
if (!privateUser)
return { sendToEmail: false, sendToBrowser: false, privateUser: null }
const notificationSettings = privateUser.notificationSubscriptionTypes
let destinations
let subscriptionType: keyof notification_subscription_types | undefined
if (Object.keys(notificationSettings).includes(reason)) {
subscriptionType = reason as keyof notification_subscription_types
destinations = notificationSettings[subscriptionType]
} else {
const key = reason as notification_reason_types
subscriptionType = notificationReasonToSubscriptionType[key]
destinations = subscriptionType
? notificationSettings[subscriptionType]
: []
}
return {
sendToEmail: destinations.includes('email'),
sendToBrowser: destinations.includes('browser'),
privateUser,
urlToManageThisNotification: `${DOMAIN}/notifications?section=${subscriptionType}`,
}
}
export const createCommentOrAnswerOrUpdatedContractNotification = async (
sourceId: string,
sourceType: 'comment' | 'answer' | 'contract',
@ -314,9 +293,10 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async (
recipientIdsList.includes(userId)
)
return
const privateUser = await getPrivateUser(userId)
if (!privateUser) return
const { sendToBrowser, sendToEmail } = await getDestinationsForUser(
userId,
privateUser,
reason
)
@ -328,7 +308,7 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async (
if (sourceType === 'comment') {
await sendNewCommentEmail(
reason,
userId,
privateUser,
sourceUser,
sourceContract,
sourceText,
@ -341,7 +321,7 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async (
} else if (sourceType === 'answer')
await sendNewAnswerEmail(
reason,
userId,
privateUser,
sourceUser.name,
sourceText,
sourceContract,
@ -354,7 +334,7 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async (
)
await sendMarketResolutionEmail(
reason,
userId,
privateUser,
resolutionData.userInvestments[userId],
resolutionData.userPayouts[userId],
sourceUser,
@ -534,8 +514,10 @@ export const createTipNotification = async (
contract?: Contract,
group?: Group
) => {
const privateUser = await getPrivateUser(toUser.id)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
toUser.id,
privateUser,
'tip_received'
)
if (!sendToBrowser) return
@ -565,6 +547,7 @@ export const createTipNotification = async (
}
return await notificationRef.set(removeUndefinedProps(notification))
// TODO: send notification to users that are watching the contract and want highly tipped comments only
// maybe TODO: send email notification to bet creator
}
@ -576,7 +559,12 @@ export const createBetFillNotification = async (
contract: Contract,
idempotencyKey: string
) => {
const { sendToBrowser } = await getDestinationsForUser(toUser.id, 'bet_fill')
const privateUser = await getPrivateUser(toUser.id)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
privateUser,
'bet_fill'
)
if (!sendToBrowser) return
const fill = userBet.fills.find((fill) => fill.matchedBetId === bet.id)
@ -616,8 +604,10 @@ export const createReferralNotification = async (
referredByContract?: Contract,
referredByGroup?: Group
) => {
const privateUser = await getPrivateUser(toUser.id)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
toUser.id,
privateUser,
'you_referred_user'
)
if (!sendToBrowser) return
@ -668,8 +658,10 @@ export const createLoanIncomeNotification = async (
idempotencyKey: string,
income: number
) => {
const privateUser = await getPrivateUser(toUser.id)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
toUser.id,
privateUser,
'loan_income'
)
if (!sendToBrowser) return
@ -704,8 +696,10 @@ export const createChallengeAcceptedNotification = async (
acceptedAmount: number,
contract: Contract
) => {
const privateUser = await getPrivateUser(challengeCreator.id)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
challengeCreator.id,
privateUser,
'challenge_accepted'
)
if (!sendToBrowser) return
@ -743,8 +737,10 @@ export const createBettingStreakBonusNotification = async (
amount: number,
idempotencyKey: string
) => {
const privateUser = await getPrivateUser(user.id)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
user.id,
privateUser,
'betting_streak_incremented'
)
if (!sendToBrowser) return
@ -784,8 +780,10 @@ export const createLikeNotification = async (
contract: Contract,
tip?: TipTxn
) => {
const privateUser = await getPrivateUser(toUser.id)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
toUser.id,
privateUser,
'liked_and_tipped_your_contract'
)
if (!sendToBrowser) return
@ -828,8 +826,11 @@ export const createUniqueBettorBonusNotification = async (
amount: number,
idempotencyKey: string
) => {
console.log('createUniqueBettorBonusNotification')
const privateUser = await getPrivateUser(contractCreatorId)
if (!privateUser) return
const { sendToBrowser } = await getDestinationsForUser(
contractCreatorId,
privateUser,
'unique_bettors_on_your_contract'
)
if (!sendToBrowser) return

View File

@ -18,12 +18,14 @@ import { formatNumericProbability } from '../../common/pseudo-numeric'
import { sendTemplateEmail, sendTextEmail } from './send-email'
import { getUser } from './utils'
import { buildCardUrl, getOpenGraphProps } from '../../common/contract-details'
import { notification_reason_types } from '../../common/notification'
import { getDestinationsForUser } from './create-notification'
import {
notification_reason_types,
getDestinationsForUser,
} from '../../common/notification'
export const sendMarketResolutionEmail = async (
reason: notification_reason_types,
userId: string,
privateUser: PrivateUser,
investment: number,
payout: number,
creator: User,
@ -33,14 +35,11 @@ export const sendMarketResolutionEmail = async (
resolutionProbability?: number,
resolutions?: { [outcome: string]: number }
) => {
const {
privateUser,
sendToEmail,
urlToManageThisNotification: unsubscribeUrl,
} = await getDestinationsForUser(userId, reason)
const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
await getDestinationsForUser(privateUser, reason)
if (!privateUser || !privateUser.email || !sendToEmail) return
const user = await getUser(userId)
const user = await getUser(privateUser.id)
if (!user) return
const outcome = toDisplayResolution(
@ -53,7 +52,7 @@ export const sendMarketResolutionEmail = async (
const subject = `Resolved ${outcome}: ${contract.question}`
const creatorPayoutText =
creatorPayout >= 1 && userId === creator.id
creatorPayout >= 1 && privateUser.id === creator.id
? ` (plus ${formatMoney(creatorPayout)} in commissions)`
: ''
@ -310,15 +309,13 @@ export const sendThankYouEmail = async (
export const sendMarketCloseEmail = async (
reason: notification_reason_types,
user: User,
privateUser: PrivateUser,
contract: Contract
) => {
const {
privateUser,
sendToEmail,
urlToManageThisNotification: unsubscribeUrl,
} = await getDestinationsForUser(user.id, reason)
const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
await getDestinationsForUser(privateUser, reason)
if (!privateUser || !privateUser.email || !sendToEmail) return
if (!privateUser.email || !sendToEmail) return
const { username, name, id: userId } = user
const firstName = name.split(' ')[0]
@ -344,7 +341,7 @@ export const sendMarketCloseEmail = async (
export const sendNewCommentEmail = async (
reason: notification_reason_types,
userId: string,
privateUser: PrivateUser,
commentCreator: User,
contract: Contract,
commentText: string,
@ -353,11 +350,8 @@ export const sendNewCommentEmail = async (
answerText?: string,
answerId?: string
) => {
const {
privateUser,
sendToEmail,
urlToManageThisNotification: unsubscribeUrl,
} = await getDestinationsForUser(userId, reason)
const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
await getDestinationsForUser(privateUser, reason)
if (!privateUser || !privateUser.email || !sendToEmail) return
const { question } = contract
@ -421,7 +415,7 @@ export const sendNewCommentEmail = async (
export const sendNewAnswerEmail = async (
reason: notification_reason_types,
userId: string,
privateUser: PrivateUser,
name: string,
text: string,
contract: Contract,
@ -429,14 +423,11 @@ export const sendNewAnswerEmail = async (
) => {
const { creatorId } = contract
// Don't send the creator's own answers.
if (userId === creatorId) return
if (privateUser.id === creatorId) return
const {
privateUser,
sendToEmail,
urlToManageThisNotification: unsubscribeUrl,
} = await getDestinationsForUser(userId, reason)
if (!privateUser || !privateUser.email || !sendToEmail) return
const { sendToEmail, urlToManageThisNotification: unsubscribeUrl } =
await getDestinationsForUser(privateUser, reason)
if (!privateUser.email || !sendToEmail) return
const { question, creatorUsername, slug } = contract

View File

@ -1006,6 +1006,7 @@ function getReasonForShowingNotification(
) {
const { sourceType, sourceUpdateType, reason, sourceSlug } = notification
let reasonText: string
// TODO: we could leave out this switch and just use the reason field now that they have more information
switch (sourceType) {
case 'comment':
if (reason === 'reply_to_users_answer')