diff --git a/common/notification.ts b/common/notification.ts
index d6615514..845a859e 100644
--- a/common/notification.ts
+++ b/common/notification.ts
@@ -18,6 +18,7 @@ export type Notification = {
sourceContractTitle?: string
sourceContractCreatorUsername?: string
sourceContractSlug?: string
+ sourceContractTags?: string[]
}
export type notification_source_types =
| 'contract'
diff --git a/functions/src/create-notification.ts b/functions/src/create-notification.ts
index 892b42eb..83ba9f24 100644
--- a/functions/src/create-notification.ts
+++ b/functions/src/create-notification.ts
@@ -66,12 +66,31 @@ export const createNotification = async (
sourceContractTitle: sourceContract?.question,
sourceContractCreatorUsername: sourceContract?.creatorUsername,
sourceContractSlug: sourceContract?.slug,
+ sourceContractTags: sourceContract?.tags,
}
await notificationRef.set(removeUndefinedProps(notification))
})
)
}
+ const notifyLiquidityProviders = async (
+ userToReasonTexts: user_to_reason_texts,
+ contract: Contract
+ ) => {
+ const liquidityProviders = await firestore
+ .collection(`contracts/${contract.id}/liquidity`)
+ .get()
+ const liquidityProvidersIds = uniq(
+ liquidityProviders.docs.map((doc) => doc.data().userId)
+ )
+ liquidityProvidersIds.forEach((userId) => {
+ if (!shouldGetNotification(userId, userToReasonTexts)) return
+ userToReasonTexts[userId] = {
+ reason: 'on_contract_with_users_shares_in',
+ }
+ })
+ }
+
const notifyUsersFollowers = async (
userToReasonTexts: user_to_reason_texts
) => {
@@ -94,14 +113,11 @@ export const createNotification = async (
}
const notifyRepliedUsers = async (
- userToReasonTexts: user_to_reason_texts
+ userToReasonTexts: user_to_reason_texts,
+ relatedUserId: string,
+ relatedSourceType: notification_source_types
) => {
- if (
- !relatedSourceType ||
- !relatedUserId ||
- !shouldGetNotification(relatedUserId, userToReasonTexts)
- )
- return
+ if (!shouldGetNotification(relatedUserId, userToReasonTexts)) return
if (relatedSourceType === 'comment') {
userToReasonTexts[relatedUserId] = {
reason: 'reply_to_users_comment',
@@ -123,8 +139,10 @@ export const createNotification = async (
}
}
- const notifyTaggedUsers = async (userToReasonTexts: user_to_reason_texts) => {
- if (!sourceText) return
+ const notifyTaggedUsers = async (
+ userToReasonTexts: user_to_reason_texts,
+ sourceText: string
+ ) => {
const taggedUsers = sourceText.match(/@\w+/g)
if (!taggedUsers) return
// await all get tagged users:
@@ -143,9 +161,13 @@ export const createNotification = async (
const notifyContractCreator = async (
userToReasonTexts: user_to_reason_texts,
- sourceContract: Contract
+ sourceContract: Contract,
+ options?: { force: boolean }
) => {
- if (shouldGetNotification(sourceContract.creatorId, userToReasonTexts))
+ if (
+ options?.force ||
+ shouldGetNotification(sourceContract.creatorId, userToReasonTexts)
+ )
userToReasonTexts[sourceContract.creatorId] = {
reason: 'on_users_contract',
}
@@ -189,7 +211,7 @@ export const createNotification = async (
})
}
- const notifyOtherBettorsOnContract = async (
+ const notifyBettorsOnContract = async (
userToReasonTexts: user_to_reason_texts,
sourceContract: Contract
) => {
@@ -216,30 +238,41 @@ export const createNotification = async (
})
}
- // TODO: Update for liquidity.
- // TODO: Notify users of their own closed but not resolved contracts.
const getUsersToNotify = async () => {
const userToReasonTexts: user_to_reason_texts = {}
// The following functions modify the userToReasonTexts object in place.
- if (
- sourceContract &&
- (sourceType === 'comment' ||
+ if (sourceContract) {
+ if (
+ sourceType === 'comment' ||
sourceType === 'answer' ||
(sourceType === 'contract' &&
- (sourceUpdateType === 'updated' || sourceUpdateType === 'resolved')))
- ) {
- if (sourceType === 'comment') {
- await notifyRepliedUsers(userToReasonTexts)
- await notifyTaggedUsers(userToReasonTexts)
+ (sourceUpdateType === 'updated' || sourceUpdateType === 'resolved'))
+ ) {
+ if (sourceType === 'comment') {
+ if (relatedUserId && relatedSourceType)
+ await notifyRepliedUsers(
+ userToReasonTexts,
+ relatedUserId,
+ relatedSourceType
+ )
+ if (sourceText) await notifyTaggedUsers(userToReasonTexts, sourceText)
+ }
+ await notifyContractCreator(userToReasonTexts, sourceContract)
+ await notifyOtherAnswerersOnContract(userToReasonTexts, sourceContract)
+ await notifyLiquidityProviders(userToReasonTexts, sourceContract)
+ await notifyBettorsOnContract(userToReasonTexts, sourceContract)
+ await notifyOtherCommentersOnContract(userToReasonTexts, sourceContract)
+ } else if (sourceType === 'contract' && sourceUpdateType === 'created') {
+ await notifyUsersFollowers(userToReasonTexts)
+ } else if (sourceType === 'contract' && sourceUpdateType === 'closed') {
+ await notifyContractCreator(userToReasonTexts, sourceContract, {
+ force: true,
+ })
+ } else if (sourceType === 'liquidity' && sourceUpdateType === 'created') {
+ await notifyContractCreator(userToReasonTexts, sourceContract)
}
- await notifyContractCreator(userToReasonTexts, sourceContract)
- await notifyOtherAnswerersOnContract(userToReasonTexts, sourceContract)
- await notifyOtherBettorsOnContract(userToReasonTexts, sourceContract)
- await notifyOtherCommentersOnContract(userToReasonTexts, sourceContract)
} else if (sourceType === 'follow' && relatedUserId) {
await notifyFollowedUser(userToReasonTexts, relatedUserId)
- } else if (sourceType === 'contract' && sourceUpdateType === 'created') {
- await notifyUsersFollowers(userToReasonTexts)
}
return userToReasonTexts
}
diff --git a/functions/src/index.ts b/functions/src/index.ts
index ecc17133..88a13759 100644
--- a/functions/src/index.ts
+++ b/functions/src/index.ts
@@ -22,12 +22,13 @@ export * from './update-recommendations'
export * from './update-feed'
export * from './backup-db'
export * from './change-user-info'
-export * from './market-close-emails'
+export * from './market-close-notifications'
export * from './add-liquidity'
export * from './on-create-answer'
export * from './on-update-contract'
export * from './on-create-contract'
export * from './on-follow-user'
+export * from './on-create-liquidity-provision'
// v2
export * from './health'
@@ -35,4 +36,4 @@ export * from './place-bet'
export * from './sell-bet'
export * from './sell-shares'
export * from './create-contract'
-export * from './withdraw-liquidity'
\ No newline at end of file
+export * from './withdraw-liquidity'
diff --git a/functions/src/market-close-emails.ts b/functions/src/market-close-notifications.ts
similarity index 81%
rename from functions/src/market-close-emails.ts
rename to functions/src/market-close-notifications.ts
index 17700613..ee9952bf 100644
--- a/functions/src/market-close-emails.ts
+++ b/functions/src/market-close-notifications.ts
@@ -4,8 +4,9 @@ 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 marketCloseEmails = functions
+export const marketCloseNotifications = functions
.runWith({ secrets: ['MAILGUN_KEY'] })
.pubsub.schedule('every 1 hours')
.onRun(async () => {
@@ -56,5 +57,14 @@ async function sendMarketCloseEmails() {
if (!privateUser) continue
await sendMarketCloseEmail(user, privateUser, contract)
+ await createNotification(
+ contract.id,
+ 'contract',
+ 'closed',
+ user,
+ 'closed' + contract.id.slice(6, contract.id.length),
+ contract.closeTime?.toString() ?? new Date().toString(),
+ contract
+ )
}
}
diff --git a/functions/src/on-create-contract.ts b/functions/src/on-create-contract.ts
index 040ba378..20c7ceba 100644
--- a/functions/src/on-create-contract.ts
+++ b/functions/src/on-create-contract.ts
@@ -18,7 +18,7 @@ export const onCreateContract = functions.firestore
'created',
contractCreator,
eventId,
- contract.question,
+ contract.description,
contract
)
})
diff --git a/functions/src/on-create-liquidity-provision.ts b/functions/src/on-create-liquidity-provision.ts
new file mode 100644
index 00000000..189b4693
--- /dev/null
+++ b/functions/src/on-create-liquidity-provision.ts
@@ -0,0 +1,28 @@
+import * as functions from 'firebase-functions'
+import { getContract, getUser } from './utils'
+import { createNotification } from './create-notification'
+import { LiquidityProvision } from 'common/liquidity-provision'
+
+export const onCreateLiquidityProvision = functions.firestore
+ .document('contracts/{contractId}/liquidity/{liquidityId}')
+ .onCreate(async (change, context) => {
+ const liquidity = change.data() as LiquidityProvision
+ const { eventId } = context
+ const contract = await getContract(liquidity.contractId)
+
+ if (!contract)
+ throw new Error('Could not find contract corresponding with liquidity')
+
+ const liquidityProvider = await getUser(liquidity.userId)
+ if (!liquidityProvider) throw new Error('Could not find liquidity provider')
+
+ await createNotification(
+ contract.id,
+ 'liquidity',
+ 'created',
+ liquidityProvider,
+ eventId,
+ liquidity.amount.toString(),
+ contract
+ )
+ })
diff --git a/functions/src/on-update-contract.ts b/functions/src/on-update-contract.ts
index 8b16b3d6..f47c019c 100644
--- a/functions/src/on-update-contract.ts
+++ b/functions/src/on-update-contract.ts
@@ -39,13 +39,26 @@ export const onUpdateContract = functions.firestore
previousValue.closeTime !== contract.closeTime ||
previousValue.description !== contract.description
) {
+ let sourceText = ''
+ if (previousValue.closeTime !== contract.closeTime && contract.closeTime)
+ sourceText = contract.closeTime.toString()
+ else {
+ const oldTrimmedDescription = previousValue.description.trim()
+ const newTrimmedDescription = contract.description.trim()
+ if (oldTrimmedDescription === '') sourceText = newTrimmedDescription
+ else
+ sourceText = newTrimmedDescription
+ .split(oldTrimmedDescription)[1]
+ .trim()
+ }
+
await createNotification(
contract.id,
'contract',
'updated',
contractUpdater,
eventId,
- contract.question,
+ sourceText,
contract
)
}
diff --git a/web/pages/notifications.tsx b/web/pages/notifications.tsx
index e4a118e4..8130ed22 100644
--- a/web/pages/notifications.tsx
+++ b/web/pages/notifications.tsx
@@ -44,6 +44,7 @@ import {
import { getContractFromId } from 'web/lib/firebase/contracts'
import { CheckIcon, XIcon } from '@heroicons/react/outline'
import toast from 'react-hot-toast'
+import { formatMoney, formatPercent } from 'common/util/format'
export default function Notifications() {
const user = useUser()
@@ -565,7 +566,7 @@ function NotificationItem(props: {
sourceType === 'contract'
) {
try {
- parseNotificationText(
+ parseOldStyleNotificationText(
sourceId,
sourceContractId,
sourceType,
@@ -619,7 +620,7 @@ function NotificationItem(props: {
}
}
- async function parseNotificationText(
+ async function parseOldStyleNotificationText(
sourceId: string,
sourceContractId: string,
sourceType: 'answer' | 'comment' | 'contract',
@@ -772,61 +773,54 @@ function NotificationTextLabel(props: {
const { contract, className, defaultText, notification, justSummary } = props
const { sourceUpdateType, sourceType, sourceText, sourceContractTitle } =
notification
- if (!contract && !sourceText && sourceType !== 'follow')
- return