diff --git a/common/notification.ts b/common/notification.ts index 6efbda85..16fead5d 100644 --- a/common/notification.ts +++ b/common/notification.ts @@ -1,4 +1,4 @@ -import { exhaustive_notification_subscribe_types } from 'common/user' +import { notification_subscription_types } from 'common/user' export type Notification = { id: string @@ -55,26 +55,17 @@ export type notification_source_update_types = export type notification_reason_types = | 'tagged_user' - // | 'on_users_contract' - // | 'on_contract_with_users_shares_in' - // | 'on_contract_with_users_shares_out' - // | 'on_contract_with_users_answer' - // | 'on_contract_with_users_comment' | 'on_new_follow' | 'contract_from_followed_user' - | 'added_you_to_group' | 'you_referred_user' | 'user_joined_to_bet_on_your_market' | 'unique_bettors_on_your_contract' - // | 'on_group_you_are_member_of' | 'tip_received' | 'bet_fill' | 'user_joined_from_your_group_invite' | 'challenge_accepted' | 'betting_streak_incremented' | 'loan_income' - // | 'you_follow_contract' - | 'liked_your_contract' | 'liked_and_tipped_your_contract' | 'comment_on_your_contract' | 'answer_on_your_contract' @@ -97,25 +88,19 @@ export type notification_reason_types = | 'reply_to_users_answer' | 'reply_to_users_comment' | 'your_contract_closed' + | 'subsidized_your_market' -export const notificationReasonToSubscribeTypeMap: Record< - notification_reason_types, - keyof exhaustive_notification_subscribe_types +// Adding a new key:value here is optional, you can also just use a key of exhaustive_notification_subscribe_types +export const notificationReasonToSubscriptionType: Partial< + Record > = { - tagged_user: 'user_tagged_you', - on_new_follow: 'new_followers', - contract_from_followed_user: 'new_markets_by_followed_users', - added_you_to_group: 'group_adds', you_referred_user: 'referral_bonuses', user_joined_to_bet_on_your_market: 'referral_bonuses', - unique_bettors_on_your_contract: 'unique_bettor_bonuses', tip_received: 'tips_on_your_comments', bet_fill: 'limit_order_fills', user_joined_from_your_group_invite: 'referral_bonuses', challenge_accepted: 'limit_order_fills', betting_streak_incremented: 'betting_streaks', - loan_income: 'loan_income', - liked_your_contract: 'tips_on_your_markets', liked_and_tipped_your_contract: 'tips_on_your_markets', comment_on_your_contract: 'all_comments_on_my_markets', answer_on_your_contract: 'all_answers_on_my_markets', @@ -128,7 +113,7 @@ export const notificationReasonToSubscribeTypeMap: Record< answer_on_contract_with_users_shares_in: 'all_answers_on_contracts_with_shares_in_on_watched_markets', update_on_contract_with_users_shares_in: - 'market_updates_with_shares_in_on_watched_markets', + 'market_updates_on_watched_markets_with_shares_in', resolution_on_contract_with_users_shares_in: 'resolutions_on_watched_markets_with_shares_in', comment_on_contract_with_users_answer: 'all_comments_on_watched_markets', @@ -141,5 +126,4 @@ export const notificationReasonToSubscribeTypeMap: Record< resolution_on_contract_with_users_comment: 'resolutions_on_watched_markets', reply_to_users_answer: 'all_replies_to_my_answers_on_watched_markets', reply_to_users_comment: 'all_replies_to_my_comments_on_watched_markets', - your_contract_closed: 'my_markets_closed', } diff --git a/common/user.ts b/common/user.ts index 7fadd277..6df967e6 100644 --- a/common/user.ts +++ b/common/user.ts @@ -66,12 +66,12 @@ export type PrivateUser = { initialIpAddress?: string apiKey?: string notificationPreferences?: notification_subscribe_types - notificationSubscriptionTypes: exhaustive_notification_subscribe_types + notificationSubscriptionTypes: notification_subscription_types } export type notification_destination_types = 'email' | 'browser' -export type exhaustive_notification_subscribe_types = { +export type notification_subscription_types = { // Watched Markets all_comments_on_watched_markets: notification_destination_types[] // Email currently - seems bad all_answers_on_watched_markets: notification_destination_types[] // Email currently - seems bad @@ -89,31 +89,31 @@ export type exhaustive_notification_subscribe_types = { all_answers_on_contracts_with_shares_in_on_watched_markets: notification_destination_types[] // On users' markets - my_markets_closed: notification_destination_types[] // Email, Recommended + your_contract_closed: notification_destination_types[] // Email, Recommended all_comments_on_my_markets: notification_destination_types[] // Email all_answers_on_my_markets: notification_destination_types[] // Email + subsidized_your_market: notification_destination_types[] // Email // Market updates resolutions_on_watched_markets: notification_destination_types[] // Email resolutions_on_watched_markets_with_shares_in: notification_destination_types[] // Email market_updates_on_watched_markets: notification_destination_types[] - market_updates_with_shares_in_on_watched_markets: notification_destination_types[] + market_updates_on_watched_markets_with_shares_in: notification_destination_types[] probability_updates_on_watched_markets: notification_destination_types[] // Email - would want persistent changes only though // Balance Changes loan_income: notification_destination_types[] betting_streaks: notification_destination_types[] referral_bonuses: notification_destination_types[] - unique_bettor_bonuses: notification_destination_types[] + unique_bettors_on_your_contract: notification_destination_types[] tips_on_your_comments: notification_destination_types[] tips_on_your_markets: notification_destination_types[] limit_order_fills: notification_destination_types[] // General - user_tagged_you: notification_destination_types[] // Email - new_followers: notification_destination_types[] // Email - group_adds: notification_destination_types[] // Email - new_markets_by_followed_users: notification_destination_types[] // Email + tagged_user: notification_destination_types[] // Email + on_new_follow: notification_destination_types[] // Email + contract_from_followed_user: notification_destination_types[] // Email trending_markets: notification_destination_types[] // Email profit_loss_updates: notification_destination_types[] // Email } @@ -169,15 +169,15 @@ export const getDefaultNotificationSettings = ( comments_by_followed_users_on_watched_markets: constructPref( wantsAll, false - ), //wantsAll ? browserOnly : none, + ), all_replies_to_my_comments_on_watched_markets: constructPref( wantsAll || wantsLess, !unsubscribedFromCommentEmails - ), //wantsAll || wantsLess ? both : none, + ), all_replies_to_my_answers_on_watched_markets: constructPref( wantsAll || wantsLess, !unsubscribedFromCommentEmails - ), //wantsAll || wantsLess ? both : none, + ), all_comments_on_contracts_with_shares_in_on_watched_markets: constructPref( wantsAll, !unsubscribedFromCommentEmails @@ -187,29 +187,30 @@ export const getDefaultNotificationSettings = ( answers_by_followed_users_on_watched_markets: constructPref( wantsAll || wantsLess, !unsubscribedFromAnswerEmails - ), //wantsAll || wantsLess ? both : none, + ), answers_by_market_creator_on_watched_markets: constructPref( wantsAll || wantsLess, !unsubscribedFromAnswerEmails - ), //wantsAll || wantsLess ? both : none, + ), all_answers_on_contracts_with_shares_in_on_watched_markets: constructPref( wantsAll, !unsubscribedFromAnswerEmails ), // On users' markets - my_markets_closed: constructPref( + your_contract_closed: constructPref( wantsAll || wantsLess, !unsubscribedFromResolutionEmails - ), //wantsAll || wantsLess ? both : none, // High priority + ), // High priority all_comments_on_my_markets: constructPref( wantsAll || wantsLess, !unsubscribedFromCommentEmails - ), //wantsAll || wantsLess ? both : none, + ), all_answers_on_my_markets: constructPref( wantsAll || wantsLess, !unsubscribedFromAnswerEmails - ), //wantsAll || wantsLess ? both : none, + ), + subsidized_your_market: constructPref(wantsAll || wantsLess, true), // Market updates resolutions_on_watched_markets: constructPref( @@ -220,7 +221,7 @@ export const getDefaultNotificationSettings = ( wantsAll || wantsLess, false ), - market_updates_with_shares_in_on_watched_markets: constructPref( + market_updates_on_watched_markets_with_shares_in: constructPref( wantsAll || wantsLess, false ), @@ -233,7 +234,10 @@ export const getDefaultNotificationSettings = ( loan_income: constructPref(wantsAll || wantsLess, false), betting_streaks: constructPref(wantsAll || wantsLess, false), referral_bonuses: constructPref(wantsAll || wantsLess, true), - unique_bettor_bonuses: constructPref(wantsAll || wantsLess, false), + unique_bettors_on_your_contract: constructPref( + wantsAll || wantsLess, + false + ), tipped_comments_on_watched_markets: constructPref( wantsAll || wantsLess, !unsubscribedFromCommentEmails @@ -242,9 +246,9 @@ export const getDefaultNotificationSettings = ( limit_order_fills: constructPref(wantsAll || wantsLess, false), // General - user_tagged_you: constructPref(wantsAll || wantsLess, true), //wantsAll || wantsLess ? both : none, - new_followers: constructPref(wantsAll || wantsLess, true), - new_markets_by_followed_users: constructPref(wantsAll || wantsLess, true), //wantsAll || wantsLess ? both : none, + tagged_user: constructPref(wantsAll || wantsLess, true), + on_new_follow: constructPref(wantsAll || wantsLess, true), + contract_from_followed_user: constructPref(wantsAll || wantsLess, true), trending_markets: constructPref( false, !unsubscribedFromWeeklyTrendingEmails @@ -254,6 +258,5 @@ export const getDefaultNotificationSettings = ( wantsAll || wantsLess, false ), - group_adds: constructPref(wantsAll || wantsLess, true), - } as exhaustive_notification_subscribe_types + } as notification_subscription_types } diff --git a/functions/src/create-notification.ts b/functions/src/create-notification.ts index bbece366..600d6cbb 100644 --- a/functions/src/create-notification.ts +++ b/functions/src/create-notification.ts @@ -2,11 +2,9 @@ import * as admin from 'firebase-admin' import { Notification, notification_reason_types, - notification_source_update_types, - notification_source_types, - notificationReasonToSubscribeTypeMap, + notificationReasonToSubscriptionType, } from '../../common/notification' -import { User } from '../../common/user' +import { notification_subscription_types, User } from '../../common/user' import { Contract } from '../../common/contract' import { getPrivateUser, getValues } from './utils' import { Comment } from '../../common/comment' @@ -20,6 +18,7 @@ import { Group } from '../../common/group' import { Challenge } from '../../common/challenge' import { Like } from '../../common/like' import { + sendMarketCloseEmail, sendMarketResolutionEmail, sendNewAnswerEmail, sendNewCommentEmail, @@ -32,8 +31,8 @@ type recipients_to_reason_texts = { export const createNotification = async ( sourceId: string, - sourceType: notification_source_types, - sourceUpdateType: notification_source_update_types, + sourceType: 'contract' | 'liquidity' | 'follow', + sourceUpdateType: 'closed' | 'created', sourceUser: User, idempotencyKey: string, sourceText: string, @@ -56,15 +55,14 @@ export const createNotification = async ( ) } - const createUsersNotifications = async ( + const sendNotificationsIfSettingsPermit = async ( userToReasonTexts: recipients_to_reason_texts ) => { - await Promise.all( - Object.keys(userToReasonTexts).map(async (userId) => { - const { reason } = userToReasonTexts[userId] - const { sendToBrowser } = await getDestinationsForUser(userId, reason) - if (!sendToBrowser) return Promise.resolve() - + for (const userId in userToReasonTexts) { + const { reason } = userToReasonTexts[userId] + const { sendToBrowser, sendToEmail, privateUser } = + await getDestinationsForUser(userId, reason) + if (sendToBrowser) { const notificationRef = firestore .collection(`/users/${userId}/notifications`) .doc(idempotencyKey) @@ -89,8 +87,22 @@ export const createNotification = async ( sourceTitle: title ? title : sourceContract?.question, } await notificationRef.set(removeUndefinedProps(notification)) - }) - ) + } + + if (!sendToEmail) continue + + if (reason === 'your_contract_closed' && privateUser && sourceContract) { + await sendMarketCloseEmail(sourceUser, privateUser, sourceContract) + } else if (reason === 'tagged_user') { + // TODO: send email to tagged user in new contract + } else if (reason === 'subsidized_your_market') { + // TODO: send email to creator of market that was subsidized + } else if (reason === 'contract_from_followed_user') { + // TODO: send email to follower of user who created market + } else if (reason === 'on_new_follow') { + // TODO: send email to user who was followed + } + } } const notifyUsersFollowers = async ( @@ -146,31 +158,18 @@ export const createNotification = async ( shouldGetNotification(sourceContract.creatorId, userToReasonTexts) ) userToReasonTexts[sourceContract.creatorId] = { - reason: 'your_contract_closed', + reason: + sourceType === 'liquidity' + ? 'subsidized_your_market' + : 'your_contract_closed', } } - const notifyUserAddedToGroup = ( - userToReasonTexts: recipients_to_reason_texts, - relatedUserId: string - ) => { - if (shouldGetNotification(relatedUserId, userToReasonTexts)) - userToReasonTexts[relatedUserId] = { - reason: 'added_you_to_group', - } - } - - const userToReasonTexts: recipients_to_reason_texts = {} // The following functions modify the userToReasonTexts object in place. + const userToReasonTexts: recipients_to_reason_texts = {} if (sourceType === 'follow' && recipients?.[0]) { notifyFollowedUser(userToReasonTexts, recipients[0]) - } else if ( - sourceType === 'group' && - sourceUpdateType === 'created' && - recipients - ) { - recipients.forEach((r) => notifyUserAddedToGroup(userToReasonTexts, r)) } else if ( sourceType === 'contract' && sourceUpdateType === 'created' && @@ -194,27 +193,33 @@ export const createNotification = async ( await notifyContractCreator(userToReasonTexts, sourceContract) } - await createUsersNotifications(userToReasonTexts) + await sendNotificationsIfSettingsPermit(userToReasonTexts) } const getDestinationsForUser = async ( userId: string, - reason: notification_reason_types + reason: notification_reason_types | keyof notification_subscription_types ) => { const privateUser = await getPrivateUser(userId) - if (!privateUser) return { sendToEmail: false, sendToBrowser: false } + if (!privateUser) + return { sendToEmail: false, sendToBrowser: false, privateUser: null } + const notificationSettings = privateUser.notificationSubscriptionTypes - console.log('notificationSettings', notificationSettings) - console.log('reason', reason) - console.log('notif reason to type map', notificationReasonToSubscribeTypeMap) - const subscribeType = notificationReasonToSubscribeTypeMap[reason] - console.log('subscribeType', subscribeType) - const destinations = - notificationSettings[notificationReasonToSubscribeTypeMap[reason]] - console.log('destinations', destinations) + let destinations + if (Object.keys(notificationSettings).includes(reason)) { + const key = reason as keyof notification_subscription_types + destinations = notificationSettings[key] + } else { + const key = reason as notification_reason_types + const subscriptionType = notificationReasonToSubscriptionType[key] + destinations = subscriptionType + ? notificationSettings[subscriptionType] + : [] + } return { sendToEmail: destinations.includes('email'), sendToBrowser: destinations.includes('browser'), + privateUser, } } @@ -463,24 +468,23 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async ( ) } - const notifyRepliedUser = async ( - relatedUserId: string, - relatedSourceType: notification_source_types - ) => { - await sendNotificationsIfSettingsPermit( - relatedUserId, - relatedSourceType === 'answer' - ? 'reply_to_users_answer' - : 'reply_to_users_comment' - ) + const notifyRepliedUser = async () => { + if (sourceType === 'comment' && repliedUserId && repliedToType) + await sendNotificationsIfSettingsPermit( + repliedUserId, + repliedToType === 'answer' + ? 'reply_to_users_answer' + : 'reply_to_users_comment' + ) } - const notifyTaggedUsers = async (userIds: string[]) => { - await Promise.all( - userIds.map((userId) => - sendNotificationsIfSettingsPermit(userId, 'tagged_user') + const notifyTaggedUsers = async () => { + if (sourceType === 'comment' && taggedUserIds && taggedUserIds.length > 0) + await Promise.all( + taggedUserIds.map((userId) => + sendNotificationsIfSettingsPermit(userId, 'tagged_user') + ) ) - ) } const notifyLiquidityProviders = async () => { @@ -506,11 +510,8 @@ export const createCommentOrAnswerOrUpdatedContractNotification = async ( ) } - if (sourceType === 'comment') { - if (repliedUserId && repliedToType) - await notifyRepliedUser(repliedUserId, repliedToType) - await notifyTaggedUsers(taggedUserIds ?? []) - } + await notifyRepliedUser() + await notifyTaggedUsers() await notifyContractCreator() await notifyOtherAnswerersOnContract() await notifyLiquidityProviders() @@ -559,6 +560,8 @@ export const createTipNotification = async ( sourceTitle: group?.name, } return await notificationRef.set(removeUndefinedProps(notification)) + + // maybe TODO: send email notification to bet creator } export const createBetFillNotification = async ( @@ -597,6 +600,8 @@ export const createBetFillNotification = async ( sourceContractId: contract.id, } return await notificationRef.set(removeUndefinedProps(notification)) + + // maybe TODO: send email notification to bet creator } export const createReferralNotification = async ( @@ -650,6 +655,8 @@ export const createReferralNotification = async ( : referredByContract?.question, } await notificationRef.set(removeUndefinedProps(notification)) + + // TODO send email notification } export const createLoanIncomeNotification = async ( @@ -779,13 +786,16 @@ export const createLikeNotification = async ( ) if (!sendToBrowser) return + // not handling just likes, must include tip + if (!tip) return + const notificationRef = firestore .collection(`/users/${toUser.id}/notifications`) .doc(idempotencyKey) const notification: Notification = { id: idempotencyKey, userId: toUser.id, - reason: tip ? 'liked_and_tipped_your_contract' : 'liked_your_contract', + reason: 'liked_and_tipped_your_contract', createdTime: Date.now(), isSeen: false, sourceId: like.id, @@ -802,20 +812,8 @@ export const createLikeNotification = async ( sourceTitle: contract.question, } return await notificationRef.set(removeUndefinedProps(notification)) -} -export async function filterUserIdsForOnlyFollowerIds( - userIds: string[], - contractId: string -) { - // get contract follower documents and check here if they're a follower - const contractFollowersSnap = await firestore - .collection(`contracts/${contractId}/follows`) - .get() - const contractFollowersIds = contractFollowersSnap.docs.map( - (doc) => doc.data().id - ) - return userIds.filter((id) => contractFollowersIds.includes(id)) + // TODO send email notification } export const createUniqueBettorBonusNotification = async ( @@ -857,4 +855,6 @@ export const createUniqueBettorBonusNotification = async ( sourceContractCreatorUsername: contract.creatorUsername, } return await notificationRef.set(removeUndefinedProps(notification)) + + // TODO send email notification } diff --git a/functions/src/market-close-notifications.ts b/functions/src/market-close-notifications.ts index f31674a1..7878e410 100644 --- a/functions/src/market-close-notifications.ts +++ b/functions/src/market-close-notifications.ts @@ -3,7 +3,6 @@ import * as admin from 'firebase-admin' import { Contract } from '../../common/contract' import { getPrivateUser, getUserByUsername } from './utils' -import { sendMarketCloseEmail } from './emails' import { createNotification } from './create-notification' export const marketCloseNotifications = functions @@ -56,7 +55,6 @@ async function sendMarketCloseEmails() { const privateUser = await getPrivateUser(user.id) if (!privateUser) continue - await sendMarketCloseEmail(user, privateUser, contract) await createNotification( contract.id, 'contract', diff --git a/web/components/notification-settings.tsx b/web/components/notification-settings.tsx index 7c1ae93b..cb868160 100644 --- a/web/components/notification-settings.tsx +++ b/web/components/notification-settings.tsx @@ -4,7 +4,7 @@ import { LoadingIndicator } from 'web/components/loading-indicator' import { Row } from 'web/components/layout/row' import clsx from 'clsx' import { - exhaustive_notification_subscribe_types, + notification_subscription_types, notification_destination_types, } from 'common/user' import { updatePrivateUser } from 'web/lib/firebase/users' @@ -33,29 +33,42 @@ export function NotificationSettings() { return } - // should be keyof Partial[] but can't figure out how to make that work - // TODO: re-enable emails for renamed subscribe types - const emailsEnabled = [ - // 'all_comments', - // 'all_answers', - // 'resolutions', - 'all_replies_to_my_comments', - 'all_replies_to_my_answers', + const emailsEnabled: Array = [ + 'all_comments_on_watched_markets', + 'all_replies_to_my_comments_on_watched_markets', + 'all_comments_on_contracts_with_shares_in_on_watched_markets', + + 'all_answers_on_watched_markets', + 'all_replies_to_my_answers_on_watched_markets', + 'all_answers_on_contracts_with_shares_in_on_watched_markets', + + 'your_contract_closed', 'all_comments_on_my_markets', 'all_answers_on_my_markets', - 'my_markets_closed', - 'probability_updates', - 'user_tagged_you', - 'new_markets_by_followed_users', + + 'resolutions_on_watched_markets_with_shares_in', + 'resolutions_on_watched_markets', + + 'tagged_user', 'trending_markets', - 'profit_loss_updates', - 'all_comments_on_contracts_with_shares_in', - 'all_answers_on_contracts_with_shares_in', + + // TODO: add these + // 'contract_from_followed_user', + // 'referral_bonuses', + // 'unique_bettors_on_your_contract', + // 'tips_on_your_markets', + // 'tips_on_your_comments', + // 'subsidized_your_market', + // 'on_new_follow', + // maybe the following? + // 'profit_loss_updates', + // 'probability_updates_on_watched_markets', + // 'limit_order_fills', ] const browserDisabled = ['trending_markets', 'profit_loss_updates'] const watched_markets_explanations_comments: { - [key in keyof Partial]: string + [key in keyof Partial]: string } = { all_comments_on_watched_markets: 'All', all_replies_to_my_comments_on_watched_markets: 'Replies to your comments', @@ -64,7 +77,7 @@ export function NotificationSettings() { // comments_by_followed_users_on_watched_markets: 'By followed users', } const watched_markets_explanations_answers: { - [key in keyof Partial]: string + [key in keyof Partial]: string } = { all_answers_on_watched_markets: 'All', all_replies_to_my_answers_on_watched_markets: 'Replies to your answers', @@ -74,45 +87,49 @@ export function NotificationSettings() { // answers_by_market_creator_on_watched_markets: 'By market creator', } const watched_markets_explanations_your_markets: { - [key in keyof Partial]: string + [key in keyof Partial]: string } = { - my_markets_closed: 'Your market has closed (and needs resolution)', + your_contract_closed: 'Your market has closed (and needs resolution)', all_comments_on_my_markets: 'Comments on your markets', all_answers_on_my_markets: 'Answers on your markets', + subsidized_your_market: 'Your market was subsidized', } const watched_markets_explanations_market_updates: { - [key in keyof Partial]: string + [key in keyof Partial]: string } = { resolutions_on_watched_markets: 'Market resolutions', + resolutions_on_watched_markets_with_shares_in: + 'Market resolutions you have shares in', market_updates_on_watched_markets: 'Updates made by the creator', + market_updates_on_watched_markets_with_shares_in: + 'Updates made by the creator on markets you have shares in', // probability_updates_on_watched_markets: 'Probability updates', } const balance_change_explanations: { - [key in keyof Partial]: string + [key in keyof Partial]: string } = { loan_income: 'Automatic loans from your profitable bets', betting_streaks: 'Betting streak bonuses', referral_bonuses: 'Referral bonuses from referring users', - unique_bettor_bonuses: 'Unique bettor bonuses on your markets', + unique_bettors_on_your_contract: 'Unique bettor bonuses on your markets', tips_on_your_comments: 'Tips on your comments', limit_order_fills: 'Limit order fills', } const general_explanations: { - [key in keyof Partial]: string + [key in keyof Partial]: string } = { - user_tagged_you: 'A user tagged you', - new_markets_by_followed_users: 'New markets created by users you follow', + tagged_user: 'A user tagged you', + contract_from_followed_user: 'New markets created by users you follow', trending_markets: 'Weekly trending markets', - new_followers: 'New followers', - group_adds: 'When someone adds you to a group', + on_new_follow: 'New followers', // profit_loss_updates: 'Weekly profit/loss updates', } const NotificationSettingLine = ( description: string, - key: string, + key: keyof notification_subscription_types, value: notification_destination_types[] ) => { const previousInAppValue = value.includes('browser') @@ -217,22 +234,18 @@ export function NotificationSettings() { ) } - const getUsersSavedPreference = (key: string) => { - return Object.keys(privateUser.notificationSubscriptionTypes).includes(key) - ? // eslint-disable-next-line @typescript-eslint/ban-ts-comment - //@ts-ignore - privateUser.notificationSubscriptionTypes[ - Object.keys(privateUser.notificationSubscriptionTypes).filter( - (x) => x === key - )[0] - ] - : '' + const getUsersSavedPreference = ( + key: keyof notification_subscription_types + ) => { + return privateUser.notificationSubscriptionTypes[key] ?? [] } const Section = ( icon: ReactNode, label: string, - map: { [key: string]: string } + subscriptionTypeToDescription: { + [key in keyof Partial]: string + } ) => { const [expanded, setExpanded] = useState(false) return ( @@ -255,8 +268,14 @@ export function NotificationSettings() { )} - {Object.entries(map).map(([key, value]) => - NotificationSettingLine(value, key, getUsersSavedPreference(key)) + {Object.entries(subscriptionTypeToDescription).map(([key, value]) => + NotificationSettingLine( + value, + key as keyof notification_subscription_types, + getUsersSavedPreference( + key as keyof notification_subscription_types + ) + ) )}