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