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 { Link } from '@tiptap/extension-link'
|
||||||
import { Mention } from '@tiptap/extension-mention'
|
import { Mention } from '@tiptap/extension-mention'
|
||||||
import Iframe from './tiptap-iframe'
|
import Iframe from './tiptap-iframe'
|
||||||
|
import { uniq } from 'lodash'
|
||||||
|
|
||||||
export function parseTags(text: string) {
|
export function parseTags(text: string) {
|
||||||
const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
|
const regex = /(?:^|\s)(?:[#][a-z0-9_]+)/gi
|
||||||
|
@ -61,6 +62,15 @@ const checkAgainstQuery = (query: string, corpus: string) =>
|
||||||
export const searchInAny = (query: string, ...fields: string[]) =>
|
export const searchInAny = (query: string, ...fields: string[]) =>
|
||||||
fields.some((field) => checkAgainstQuery(query, field))
|
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
|
// can't just do [StarterKit, Image...] because it doesn't work with cjs imports
|
||||||
export const exhibitExts = [
|
export const exhibitExts = [
|
||||||
Blockquote,
|
Blockquote,
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const createNotification = async (
|
||||||
miscData?: {
|
miscData?: {
|
||||||
contract?: Contract
|
contract?: Contract
|
||||||
relatedSourceType?: notification_source_types
|
relatedSourceType?: notification_source_types
|
||||||
relatedUserId?: string
|
recipients?: string[]
|
||||||
slug?: string
|
slug?: string
|
||||||
title?: string
|
title?: string
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ export const createNotification = async (
|
||||||
const {
|
const {
|
||||||
contract: sourceContract,
|
contract: sourceContract,
|
||||||
relatedSourceType,
|
relatedSourceType,
|
||||||
relatedUserId,
|
recipients,
|
||||||
slug,
|
slug,
|
||||||
title,
|
title,
|
||||||
} = miscData ?? {}
|
} = miscData ?? {}
|
||||||
|
@ -128,7 +128,7 @@ export const createNotification = async (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyRepliedUsers = async (
|
const notifyRepliedUser = (
|
||||||
userToReasonTexts: user_to_reason_texts,
|
userToReasonTexts: user_to_reason_texts,
|
||||||
relatedUserId: string,
|
relatedUserId: string,
|
||||||
relatedSourceType: notification_source_types
|
relatedSourceType: notification_source_types
|
||||||
|
@ -145,7 +145,7 @@ export const createNotification = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyFollowedUser = async (
|
const notifyFollowedUser = (
|
||||||
userToReasonTexts: user_to_reason_texts,
|
userToReasonTexts: user_to_reason_texts,
|
||||||
followedUserId: string
|
followedUserId: string
|
||||||
) => {
|
) => {
|
||||||
|
@ -155,21 +155,24 @@ export const createNotification = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyTaggedUsers = async (
|
/** @deprecated parse from rich text instead */
|
||||||
userToReasonTexts: user_to_reason_texts,
|
const parseMentions = async (source: string) => {
|
||||||
sourceText: string
|
const mentions = source.match(/@\w+/g)
|
||||||
) => {
|
if (!mentions) return []
|
||||||
const taggedUsers = sourceText.match(/@\w+/g)
|
return Promise.all(
|
||||||
if (!taggedUsers) return
|
mentions.map(
|
||||||
// await all get tagged users:
|
async (username) => (await getUserByUsername(username.slice(1)))?.id
|
||||||
const users = await Promise.all(
|
)
|
||||||
taggedUsers.map(async (username) => {
|
|
||||||
return await getUserByUsername(username.slice(1))
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
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',
|
reason: 'tagged_user',
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -254,7 +257,7 @@ export const createNotification = async (
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyUserAddedToGroup = async (
|
const notifyUserAddedToGroup = (
|
||||||
userToReasonTexts: user_to_reason_texts,
|
userToReasonTexts: user_to_reason_texts,
|
||||||
relatedUserId: string
|
relatedUserId: string
|
||||||
) => {
|
) => {
|
||||||
|
@ -276,11 +279,14 @@ export const createNotification = async (
|
||||||
const getUsersToNotify = async () => {
|
const getUsersToNotify = async () => {
|
||||||
const userToReasonTexts: user_to_reason_texts = {}
|
const userToReasonTexts: user_to_reason_texts = {}
|
||||||
// The following functions modify the userToReasonTexts object in place.
|
// The following functions modify the userToReasonTexts object in place.
|
||||||
if (sourceType === 'follow' && relatedUserId) {
|
if (sourceType === 'follow' && recipients?.[0]) {
|
||||||
await notifyFollowedUser(userToReasonTexts, relatedUserId)
|
notifyFollowedUser(userToReasonTexts, recipients[0])
|
||||||
} else if (sourceType === 'group' && relatedUserId) {
|
} else if (
|
||||||
if (sourceUpdateType === 'created')
|
sourceType === 'group' &&
|
||||||
await notifyUserAddedToGroup(userToReasonTexts, relatedUserId)
|
sourceUpdateType === 'created' &&
|
||||||
|
recipients
|
||||||
|
) {
|
||||||
|
recipients.forEach((r) => notifyUserAddedToGroup(userToReasonTexts, r))
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following functions need sourceContract to be defined.
|
// The following functions need sourceContract to be defined.
|
||||||
|
@ -293,13 +299,10 @@ export const createNotification = async (
|
||||||
(sourceUpdateType === 'updated' || sourceUpdateType === 'resolved'))
|
(sourceUpdateType === 'updated' || sourceUpdateType === 'resolved'))
|
||||||
) {
|
) {
|
||||||
if (sourceType === 'comment') {
|
if (sourceType === 'comment') {
|
||||||
if (relatedUserId && relatedSourceType)
|
if (recipients?.[0] && relatedSourceType)
|
||||||
await notifyRepliedUsers(
|
notifyRepliedUser(userToReasonTexts, recipients[0], relatedSourceType)
|
||||||
userToReasonTexts,
|
if (sourceText)
|
||||||
relatedUserId,
|
notifyTaggedUsers(userToReasonTexts, await parseMentions(sourceText))
|
||||||
relatedSourceType
|
|
||||||
)
|
|
||||||
if (sourceText) await notifyTaggedUsers(userToReasonTexts, sourceText)
|
|
||||||
}
|
}
|
||||||
await notifyContractCreator(userToReasonTexts, sourceContract)
|
await notifyContractCreator(userToReasonTexts, sourceContract)
|
||||||
await notifyOtherAnswerersOnContract(userToReasonTexts, sourceContract)
|
await notifyOtherAnswerersOnContract(userToReasonTexts, sourceContract)
|
||||||
|
@ -308,6 +311,7 @@ export const createNotification = async (
|
||||||
await notifyOtherCommentersOnContract(userToReasonTexts, sourceContract)
|
await notifyOtherCommentersOnContract(userToReasonTexts, sourceContract)
|
||||||
} else if (sourceType === 'contract' && sourceUpdateType === 'created') {
|
} else if (sourceType === 'contract' && sourceUpdateType === 'created') {
|
||||||
await notifyUsersFollowers(userToReasonTexts)
|
await notifyUsersFollowers(userToReasonTexts)
|
||||||
|
notifyTaggedUsers(userToReasonTexts, recipients ?? [])
|
||||||
} else if (sourceType === 'contract' && sourceUpdateType === 'closed') {
|
} else if (sourceType === 'contract' && sourceUpdateType === 'closed') {
|
||||||
await notifyContractCreator(userToReasonTexts, sourceContract, {
|
await notifyContractCreator(userToReasonTexts, sourceContract, {
|
||||||
force: true,
|
force: true,
|
||||||
|
|
|
@ -68,9 +68,10 @@ export const onCreateCommentOnContract = functions
|
||||||
? 'answer'
|
? 'answer'
|
||||||
: undefined
|
: undefined
|
||||||
|
|
||||||
const relatedUserId = comment.replyToCommentId
|
const repliedUserId = comment.replyToCommentId
|
||||||
? comments.find((c) => c.id === comment.replyToCommentId)?.userId
|
? comments.find((c) => c.id === comment.replyToCommentId)?.userId
|
||||||
: answer?.userId
|
: answer?.userId
|
||||||
|
const recipients = repliedUserId ? [repliedUserId] : []
|
||||||
|
|
||||||
await createNotification(
|
await createNotification(
|
||||||
comment.id,
|
comment.id,
|
||||||
|
@ -79,7 +80,7 @@ export const onCreateCommentOnContract = functions
|
||||||
commentCreator,
|
commentCreator,
|
||||||
eventId,
|
eventId,
|
||||||
comment.text,
|
comment.text,
|
||||||
{ contract, relatedSourceType, relatedUserId }
|
{ contract, relatedSourceType, recipients }
|
||||||
)
|
)
|
||||||
|
|
||||||
const recipientUserIds = uniq([
|
const recipientUserIds = uniq([
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as functions from 'firebase-functions'
|
||||||
import { getUser } from './utils'
|
import { getUser } from './utils'
|
||||||
import { createNotification } from './create-notification'
|
import { createNotification } from './create-notification'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
import { richTextToString } from '../../common/util/parse'
|
import { parseMentions, richTextToString } from '../../common/util/parse'
|
||||||
import { JSONContent } from '@tiptap/core'
|
import { JSONContent } from '@tiptap/core'
|
||||||
|
|
||||||
export const onCreateContract = functions.firestore
|
export const onCreateContract = functions.firestore
|
||||||
|
@ -14,13 +14,16 @@ export const onCreateContract = functions.firestore
|
||||||
const contractCreator = await getUser(contract.creatorId)
|
const contractCreator = await getUser(contract.creatorId)
|
||||||
if (!contractCreator) throw new Error('Could not find contract creator')
|
if (!contractCreator) throw new Error('Could not find contract creator')
|
||||||
|
|
||||||
|
const desc = contract.description as JSONContent
|
||||||
|
const mentioned = parseMentions(desc)
|
||||||
|
|
||||||
await createNotification(
|
await createNotification(
|
||||||
contract.id,
|
contract.id,
|
||||||
'contract',
|
'contract',
|
||||||
'created',
|
'created',
|
||||||
contractCreator,
|
contractCreator,
|
||||||
eventId,
|
eventId,
|
||||||
richTextToString(contract.description as JSONContent),
|
richTextToString(desc),
|
||||||
{ contract }
|
{ contract, recipients: mentioned }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,19 +12,17 @@ export const onCreateGroup = functions.firestore
|
||||||
const groupCreator = await getUser(group.creatorId)
|
const groupCreator = await getUser(group.creatorId)
|
||||||
if (!groupCreator) throw new Error('Could not find group creator')
|
if (!groupCreator) throw new Error('Could not find group creator')
|
||||||
// create notifications for all members of the group
|
// create notifications for all members of the group
|
||||||
for (const memberId of group.memberIds) {
|
await createNotification(
|
||||||
await createNotification(
|
group.id,
|
||||||
group.id,
|
'group',
|
||||||
'group',
|
'created',
|
||||||
'created',
|
groupCreator,
|
||||||
groupCreator,
|
eventId,
|
||||||
eventId,
|
group.about,
|
||||||
group.about,
|
{
|
||||||
{
|
recipients: group.memberIds,
|
||||||
relatedUserId: memberId,
|
slug: group.slug,
|
||||||
slug: group.slug,
|
title: group.name,
|
||||||
title: group.name,
|
}
|
||||||
}
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -30,7 +30,7 @@ export const onFollowUser = functions.firestore
|
||||||
followingUser,
|
followingUser,
|
||||||
eventId,
|
eventId,
|
||||||
'',
|
'',
|
||||||
{ relatedUserId: follow.userId }
|
{ recipients: [follow.userId] }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user