2022-06-01 13:11:25 +00:00
|
|
|
import * as admin from 'firebase-admin'
|
|
|
|
import {
|
|
|
|
Notification,
|
|
|
|
notification_reason_types,
|
|
|
|
notification_source_types,
|
|
|
|
} from '../../common/notification'
|
|
|
|
import { User } from '../../common/user'
|
|
|
|
import { Contract } from '../../common/contract'
|
2022-06-06 16:54:25 +00:00
|
|
|
import { getValues } from './utils'
|
2022-06-01 13:11:25 +00:00
|
|
|
import { Comment } from '../../common/comment'
|
|
|
|
import { uniq } from 'lodash'
|
|
|
|
import { Bet } from '../../common/bet'
|
|
|
|
import { Answer } from '../../common/answer'
|
|
|
|
const firestore = admin.firestore()
|
|
|
|
|
|
|
|
type user_to_reason_texts = {
|
2022-06-06 16:54:25 +00:00
|
|
|
[userId: string]: { text: string; reason: notification_reason_types }
|
2022-06-01 13:11:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export const createNotification = async (
|
|
|
|
sourceId: string,
|
|
|
|
sourceType: notification_source_types,
|
2022-06-06 16:54:25 +00:00
|
|
|
reason: notification_reason_types,
|
|
|
|
sourceContract: Contract,
|
2022-06-01 13:11:25 +00:00
|
|
|
sourceUser: User,
|
2022-06-06 16:54:25 +00:00
|
|
|
idempotencyKey: string
|
2022-06-01 13:11:25 +00:00
|
|
|
) => {
|
|
|
|
const shouldGetNotification = (
|
|
|
|
userId: string,
|
|
|
|
userToReasonTexts: user_to_reason_texts
|
|
|
|
) => {
|
|
|
|
return (
|
|
|
|
sourceUser.id != userId &&
|
|
|
|
!Object.keys(userToReasonTexts).includes(userId)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
const createUsersNotifications = async (
|
|
|
|
userToReasonTexts: user_to_reason_texts
|
|
|
|
) => {
|
|
|
|
await Promise.all(
|
|
|
|
Object.keys(userToReasonTexts).map(async (userId) => {
|
|
|
|
const notificationRef = firestore
|
|
|
|
.collection(`/users/${userId}/notifications`)
|
|
|
|
.doc(idempotencyKey)
|
|
|
|
const notification: Notification = {
|
|
|
|
id: idempotencyKey,
|
|
|
|
userId,
|
2022-06-06 16:54:25 +00:00
|
|
|
reasonText: userToReasonTexts[userId].text,
|
2022-06-01 13:11:25 +00:00
|
|
|
reason: userToReasonTexts[userId].reason,
|
|
|
|
createdTime: Date.now(),
|
|
|
|
isSeen: false,
|
|
|
|
sourceId,
|
|
|
|
sourceType,
|
2022-06-06 16:54:25 +00:00
|
|
|
sourceContractId: sourceContract.id,
|
2022-06-01 13:11:25 +00:00
|
|
|
sourceUserName: sourceUser.name,
|
|
|
|
sourceUserUsername: sourceUser.username,
|
|
|
|
sourceUserAvatarUrl: sourceUser.avatarUrl,
|
|
|
|
}
|
2022-06-06 16:54:25 +00:00
|
|
|
await notificationRef.set(notification)
|
2022-06-01 13:11:25 +00:00
|
|
|
})
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
// TODO: Update for liquidity.
|
|
|
|
// TODO: Find tagged users.
|
|
|
|
// TODO: Find replies to comments.
|
|
|
|
// TODO: Filter bets for only open bets.
|
|
|
|
// TODO: Notify users of their own closed but not resolved contracts.
|
|
|
|
if (
|
|
|
|
sourceType === 'comment' ||
|
|
|
|
sourceType === 'answer' ||
|
|
|
|
sourceType === 'contract'
|
|
|
|
) {
|
|
|
|
const reasonTextPretext = getReasonTextFromReason(sourceType, reason)
|
2022-06-01 13:11:25 +00:00
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
const notifyContractCreator = async (
|
|
|
|
userToReasonTexts: user_to_reason_texts
|
|
|
|
) => {
|
|
|
|
if (shouldGetNotification(sourceContract.creatorId, userToReasonTexts))
|
|
|
|
userToReasonTexts[sourceContract.creatorId] = {
|
|
|
|
text: `${reasonTextPretext} your question`,
|
|
|
|
reason,
|
|
|
|
}
|
|
|
|
}
|
2022-06-01 13:11:25 +00:00
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
const notifyOtherAnswerersOnContract = async (
|
|
|
|
userToReasonTexts: user_to_reason_texts
|
|
|
|
) => {
|
|
|
|
const answers = await getValues<Answer>(
|
|
|
|
firestore
|
|
|
|
.collection('contracts')
|
|
|
|
.doc(sourceContract.id)
|
|
|
|
.collection('answers')
|
|
|
|
)
|
|
|
|
const recipientUserIds = uniq(answers.map((answer) => answer.userId))
|
|
|
|
recipientUserIds.forEach((userId) => {
|
|
|
|
if (shouldGetNotification(userId, userToReasonTexts))
|
|
|
|
userToReasonTexts[userId] = {
|
|
|
|
text: `${reasonTextPretext} a question you submitted an answer to`,
|
|
|
|
reason,
|
|
|
|
}
|
2022-06-01 13:11:25 +00:00
|
|
|
})
|
2022-06-06 16:54:25 +00:00
|
|
|
}
|
2022-06-01 13:11:25 +00:00
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
const notifyOtherCommentersOnContract = async (
|
|
|
|
userToReasonTexts: user_to_reason_texts
|
|
|
|
) => {
|
|
|
|
const comments = await getValues<Comment>(
|
|
|
|
firestore
|
|
|
|
.collection('contracts')
|
|
|
|
.doc(sourceContract.id)
|
|
|
|
.collection('comments')
|
|
|
|
)
|
|
|
|
const recipientUserIds = uniq(comments.map((comment) => comment.userId))
|
|
|
|
recipientUserIds.forEach((userId) => {
|
|
|
|
if (shouldGetNotification(userId, userToReasonTexts))
|
|
|
|
userToReasonTexts[userId] = {
|
|
|
|
text: `${reasonTextPretext} a question you commented on`,
|
|
|
|
reason,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2022-06-01 13:11:25 +00:00
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
const notifyOtherBettorsOnContract = async (
|
|
|
|
userToReasonTexts: user_to_reason_texts
|
|
|
|
) => {
|
|
|
|
const betsSnap = await firestore
|
|
|
|
.collection(`contracts/${sourceContract.id}/bets`)
|
|
|
|
.get()
|
|
|
|
const bets = betsSnap.docs.map((doc) => doc.data() as Bet)
|
|
|
|
const recipientUserIds = uniq(bets.map((bet) => bet.userId))
|
|
|
|
recipientUserIds.forEach((userId) => {
|
|
|
|
if (shouldGetNotification(userId, userToReasonTexts))
|
|
|
|
userToReasonTexts[userId] = {
|
|
|
|
text: `${reasonTextPretext} a question you bet on`,
|
|
|
|
reason,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
2022-06-01 13:11:25 +00:00
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
const getUsersToNotify = async () => {
|
|
|
|
const userToReasonTexts: user_to_reason_texts = {}
|
|
|
|
// The following functions modify the userToReasonTexts object in place.
|
|
|
|
await notifyContractCreator(userToReasonTexts)
|
|
|
|
await notifyOtherAnswerersOnContract(userToReasonTexts)
|
|
|
|
await notifyOtherCommentersOnContract(userToReasonTexts)
|
|
|
|
await notifyOtherBettorsOnContract(userToReasonTexts)
|
|
|
|
return userToReasonTexts
|
|
|
|
}
|
2022-06-01 13:11:25 +00:00
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
const userToReasonTexts = await getUsersToNotify()
|
|
|
|
await createUsersNotifications(userToReasonTexts)
|
2022-06-01 13:11:25 +00:00
|
|
|
}
|
2022-06-06 16:54:25 +00:00
|
|
|
}
|
2022-06-06 16:52:11 +00:00
|
|
|
|
2022-06-06 16:54:25 +00:00
|
|
|
function getReasonTextFromReason(
|
|
|
|
source: notification_source_types,
|
|
|
|
reason: notification_reason_types
|
|
|
|
) {
|
|
|
|
// TODO: Find tagged users.
|
|
|
|
// TODO: Find replies to comments.
|
|
|
|
switch (source) {
|
|
|
|
case 'comment':
|
|
|
|
return 'commented on'
|
|
|
|
case 'contract':
|
|
|
|
return reason
|
|
|
|
case 'answer':
|
|
|
|
return 'answered'
|
|
|
|
default:
|
|
|
|
throw new Error('Invalid notification reason')
|
2022-06-06 16:52:11 +00:00
|
|
|
}
|
2022-06-01 13:11:25 +00:00
|
|
|
}
|