Notify mentioned users on market publish (#683)

* Add function to parse at mentions

* Notify mentioned users on market create

- refactor createNotification to accept list of recipients' ids
This commit is contained in:
Sinclair Chen 2022-08-04 15:35:55 -07:00 committed by GitHub
parent 912ccad530
commit edae709f5f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 68 additions and 52 deletions

View File

@ -22,6 +22,7 @@ import { Image } from '@tiptap/extension-image'
import { Link } from '@tiptap/extension-link'
import { Mention } from '@tiptap/extension-mention'
import Iframe from './tiptap-iframe'
import { uniq } from 'lodash'
export function parseTags(text: string) {
const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
@ -61,6 +62,15 @@ const checkAgainstQuery = (query: string, corpus: string) =>
export const searchInAny = (query: string, ...fields: string[]) =>
fields.some((field) => checkAgainstQuery(query, field))
/** @return user ids of all \@mentions */
export function parseMentions(data: JSONContent): string[] {
const mentions = data.content?.flatMap(parseMentions) ?? [] //dfs
if (data.type === 'mention' && data.attrs) {
mentions.push(data.attrs.id as string)
}
return uniq(mentions)
}
// can't just do [StarterKit, Image...] because it doesn't work with cjs imports
export const exhibitExts = [
Blockquote,

View File

@ -33,7 +33,7 @@ export const createNotification = async (
miscData?: {
contract?: Contract
relatedSourceType?: notification_source_types
relatedUserId?: string
recipients?: string[]
slug?: string
title?: string
}
@ -41,7 +41,7 @@ export const createNotification = async (
const {
contract: sourceContract,
relatedSourceType,
relatedUserId,
recipients,
slug,
title,
} = miscData ?? {}
@ -128,7 +128,7 @@ export const createNotification = async (
})
}
const notifyRepliedUsers = async (
const notifyRepliedUser = (
userToReasonTexts: user_to_reason_texts,
relatedUserId: string,
relatedSourceType: notification_source_types
@ -145,7 +145,7 @@ export const createNotification = async (
}
}
const notifyFollowedUser = async (
const notifyFollowedUser = (
userToReasonTexts: user_to_reason_texts,
followedUserId: string
) => {
@ -155,21 +155,24 @@ export const createNotification = async (
}
}
const notifyTaggedUsers = async (
userToReasonTexts: user_to_reason_texts,
sourceText: string
) => {
const taggedUsers = sourceText.match(/@\w+/g)
if (!taggedUsers) return
// await all get tagged users:
const users = await Promise.all(
taggedUsers.map(async (username) => {
return await getUserByUsername(username.slice(1))
})
/** @deprecated parse from rich text instead */
const parseMentions = async (source: string) => {
const mentions = source.match(/@\w+/g)
if (!mentions) return []
return Promise.all(
mentions.map(
async (username) => (await getUserByUsername(username.slice(1)))?.id
)
)
users.forEach((taggedUser) => {
if (taggedUser && shouldGetNotification(taggedUser.id, userToReasonTexts))
userToReasonTexts[taggedUser.id] = {
}
const notifyTaggedUsers = (
userToReasonTexts: user_to_reason_texts,
userIds: (string | undefined)[]
) => {
userIds.forEach((id) => {
if (id && shouldGetNotification(id, userToReasonTexts))
userToReasonTexts[id] = {
reason: 'tagged_user',
}
})
@ -254,7 +257,7 @@ export const createNotification = async (
})
}
const notifyUserAddedToGroup = async (
const notifyUserAddedToGroup = (
userToReasonTexts: user_to_reason_texts,
relatedUserId: string
) => {
@ -276,11 +279,14 @@ export const createNotification = async (
const getUsersToNotify = async () => {
const userToReasonTexts: user_to_reason_texts = {}
// The following functions modify the userToReasonTexts object in place.
if (sourceType === 'follow' && relatedUserId) {
await notifyFollowedUser(userToReasonTexts, relatedUserId)
} else if (sourceType === 'group' && relatedUserId) {
if (sourceUpdateType === 'created')
await notifyUserAddedToGroup(userToReasonTexts, relatedUserId)
if (sourceType === 'follow' && recipients?.[0]) {
notifyFollowedUser(userToReasonTexts, recipients[0])
} else if (
sourceType === 'group' &&
sourceUpdateType === 'created' &&
recipients
) {
recipients.forEach((r) => notifyUserAddedToGroup(userToReasonTexts, r))
}
// The following functions need sourceContract to be defined.
@ -293,13 +299,10 @@ export const createNotification = async (
(sourceUpdateType === 'updated' || sourceUpdateType === 'resolved'))
) {
if (sourceType === 'comment') {
if (relatedUserId && relatedSourceType)
await notifyRepliedUsers(
userToReasonTexts,
relatedUserId,
relatedSourceType
)
if (sourceText) await notifyTaggedUsers(userToReasonTexts, sourceText)
if (recipients?.[0] && relatedSourceType)
notifyRepliedUser(userToReasonTexts, recipients[0], relatedSourceType)
if (sourceText)
notifyTaggedUsers(userToReasonTexts, await parseMentions(sourceText))
}
await notifyContractCreator(userToReasonTexts, sourceContract)
await notifyOtherAnswerersOnContract(userToReasonTexts, sourceContract)
@ -308,6 +311,7 @@ export const createNotification = async (
await notifyOtherCommentersOnContract(userToReasonTexts, sourceContract)
} else if (sourceType === 'contract' && sourceUpdateType === 'created') {
await notifyUsersFollowers(userToReasonTexts)
notifyTaggedUsers(userToReasonTexts, recipients ?? [])
} else if (sourceType === 'contract' && sourceUpdateType === 'closed') {
await notifyContractCreator(userToReasonTexts, sourceContract, {
force: true,

View File

@ -68,9 +68,10 @@ export const onCreateCommentOnContract = functions
? 'answer'
: undefined
const relatedUserId = comment.replyToCommentId
const repliedUserId = comment.replyToCommentId
? comments.find((c) => c.id === comment.replyToCommentId)?.userId
: answer?.userId
const recipients = repliedUserId ? [repliedUserId] : []
await createNotification(
comment.id,
@ -79,7 +80,7 @@ export const onCreateCommentOnContract = functions
commentCreator,
eventId,
comment.text,
{ contract, relatedSourceType, relatedUserId }
{ contract, relatedSourceType, recipients }
)
const recipientUserIds = uniq([

View File

@ -2,7 +2,7 @@ import * as functions from 'firebase-functions'
import { getUser } from './utils'
import { createNotification } from './create-notification'
import { Contract } from '../../common/contract'
import { richTextToString } from '../../common/util/parse'
import { parseMentions, richTextToString } from '../../common/util/parse'
import { JSONContent } from '@tiptap/core'
export const onCreateContract = functions.firestore
@ -14,13 +14,16 @@ export const onCreateContract = functions.firestore
const contractCreator = await getUser(contract.creatorId)
if (!contractCreator) throw new Error('Could not find contract creator')
const desc = contract.description as JSONContent
const mentioned = parseMentions(desc)
await createNotification(
contract.id,
'contract',
'created',
contractCreator,
eventId,
richTextToString(contract.description as JSONContent),
{ contract }
richTextToString(desc),
{ contract, recipients: mentioned }
)
})

View File

@ -12,19 +12,17 @@ export const onCreateGroup = functions.firestore
const groupCreator = await getUser(group.creatorId)
if (!groupCreator) throw new Error('Could not find group creator')
// create notifications for all members of the group
for (const memberId of group.memberIds) {
await createNotification(
group.id,
'group',
'created',
groupCreator,
eventId,
group.about,
{
relatedUserId: memberId,
slug: group.slug,
title: group.name,
}
)
}
await createNotification(
group.id,
'group',
'created',
groupCreator,
eventId,
group.about,
{
recipients: group.memberIds,
slug: group.slug,
title: group.name,
}
)
})

View File

@ -30,7 +30,7 @@ export const onFollowUser = functions.firestore
followingUser,
eventId,
'',
{ relatedUserId: follow.userId }
{ recipients: [follow.userId] }
)
})