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:
parent
912ccad530
commit
edae709f5f
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -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 }
|
||||
)
|
||||
})
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
)
|
||||
})
|
||||
|
|
|
@ -30,7 +30,7 @@ export const onFollowUser = functions.firestore
|
|||
followingUser,
|
||||
eventId,
|
||||
'',
|
||||
{ relatedUserId: follow.userId }
|
||||
{ recipients: [follow.userId] }
|
||||
)
|
||||
})
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user