Speed up notification loading by prepopulating relevant info (#453)
* Populate notification with relevant info * eslint * Remove duplicated code * Unused ? * Add new q notification, other small fixes
This commit is contained in:
parent
7e37fc776c
commit
936cabe353
|
@ -14,6 +14,10 @@ export type Notification = {
|
||||||
sourceUserName?: string
|
sourceUserName?: string
|
||||||
sourceUserUsername?: string
|
sourceUserUsername?: string
|
||||||
sourceUserAvatarUrl?: string
|
sourceUserAvatarUrl?: string
|
||||||
|
sourceText?: string
|
||||||
|
sourceContractTitle?: string
|
||||||
|
sourceContractCreatorUsername?: string
|
||||||
|
sourceContractSlug?: string
|
||||||
}
|
}
|
||||||
export type notification_source_types =
|
export type notification_source_types =
|
||||||
| 'contract'
|
| 'contract'
|
||||||
|
@ -42,3 +46,4 @@ export type notification_reason_types =
|
||||||
| 'reply_to_users_answer'
|
| 'reply_to_users_answer'
|
||||||
| 'reply_to_users_comment'
|
| 'reply_to_users_comment'
|
||||||
| 'on_new_follow'
|
| 'on_new_follow'
|
||||||
|
| 'you_follow_user'
|
||||||
|
|
|
@ -26,10 +26,10 @@ export const createNotification = async (
|
||||||
sourceUpdateType: notification_source_update_types,
|
sourceUpdateType: notification_source_update_types,
|
||||||
sourceUser: User,
|
sourceUser: User,
|
||||||
idempotencyKey: string,
|
idempotencyKey: string,
|
||||||
|
sourceText: string,
|
||||||
sourceContract?: Contract,
|
sourceContract?: Contract,
|
||||||
relatedSourceType?: notification_source_types,
|
relatedSourceType?: notification_source_types,
|
||||||
relatedUserId?: string,
|
relatedUserId?: string
|
||||||
sourceText?: string
|
|
||||||
) => {
|
) => {
|
||||||
const shouldGetNotification = (
|
const shouldGetNotification = (
|
||||||
userId: string,
|
userId: string,
|
||||||
|
@ -62,12 +62,37 @@ export const createNotification = async (
|
||||||
sourceUserName: sourceUser.name,
|
sourceUserName: sourceUser.name,
|
||||||
sourceUserUsername: sourceUser.username,
|
sourceUserUsername: sourceUser.username,
|
||||||
sourceUserAvatarUrl: sourceUser.avatarUrl,
|
sourceUserAvatarUrl: sourceUser.avatarUrl,
|
||||||
|
sourceText,
|
||||||
|
sourceContractTitle: sourceContract?.question,
|
||||||
|
sourceContractCreatorUsername: sourceContract?.creatorUsername,
|
||||||
|
sourceContractSlug: sourceContract?.slug,
|
||||||
}
|
}
|
||||||
await notificationRef.set(removeUndefinedProps(notification))
|
await notificationRef.set(removeUndefinedProps(notification))
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const notifyUsersFollowers = async (
|
||||||
|
userToReasonTexts: user_to_reason_texts
|
||||||
|
) => {
|
||||||
|
const followers = await firestore
|
||||||
|
.collectionGroup('follows')
|
||||||
|
.where('userId', '==', sourceUser.id)
|
||||||
|
.get()
|
||||||
|
|
||||||
|
followers.docs.forEach((doc) => {
|
||||||
|
const followerUserId = doc.ref.parent.parent?.id
|
||||||
|
if (
|
||||||
|
followerUserId &&
|
||||||
|
shouldGetNotification(followerUserId, userToReasonTexts)
|
||||||
|
) {
|
||||||
|
userToReasonTexts[followerUserId] = {
|
||||||
|
reason: 'you_follow_user',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const notifyRepliedUsers = async (
|
const notifyRepliedUsers = async (
|
||||||
userToReasonTexts: user_to_reason_texts
|
userToReasonTexts: user_to_reason_texts
|
||||||
) => {
|
) => {
|
||||||
|
@ -200,7 +225,8 @@ export const createNotification = async (
|
||||||
sourceContract &&
|
sourceContract &&
|
||||||
(sourceType === 'comment' ||
|
(sourceType === 'comment' ||
|
||||||
sourceType === 'answer' ||
|
sourceType === 'answer' ||
|
||||||
sourceType === 'contract')
|
(sourceType === 'contract' &&
|
||||||
|
(sourceUpdateType === 'updated' || sourceUpdateType === 'resolved')))
|
||||||
) {
|
) {
|
||||||
if (sourceType === 'comment') {
|
if (sourceType === 'comment') {
|
||||||
await notifyRepliedUsers(userToReasonTexts)
|
await notifyRepliedUsers(userToReasonTexts)
|
||||||
|
@ -212,6 +238,8 @@ export const createNotification = async (
|
||||||
await notifyOtherCommentersOnContract(userToReasonTexts, sourceContract)
|
await notifyOtherCommentersOnContract(userToReasonTexts, sourceContract)
|
||||||
} else if (sourceType === 'follow' && relatedUserId) {
|
} else if (sourceType === 'follow' && relatedUserId) {
|
||||||
await notifyFollowedUser(userToReasonTexts, relatedUserId)
|
await notifyFollowedUser(userToReasonTexts, relatedUserId)
|
||||||
|
} else if (sourceType === 'contract' && sourceUpdateType === 'created') {
|
||||||
|
await notifyUsersFollowers(userToReasonTexts)
|
||||||
}
|
}
|
||||||
return userToReasonTexts
|
return userToReasonTexts
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ export * from './market-close-emails'
|
||||||
export * from './add-liquidity'
|
export * from './add-liquidity'
|
||||||
export * from './on-create-answer'
|
export * from './on-create-answer'
|
||||||
export * from './on-update-contract'
|
export * from './on-update-contract'
|
||||||
|
export * from './on-create-contract'
|
||||||
export * from './on-follow-user'
|
export * from './on-follow-user'
|
||||||
|
|
||||||
// v2
|
// v2
|
||||||
|
|
|
@ -27,6 +27,7 @@ export const onCreateAnswer = functions.firestore
|
||||||
'created',
|
'created',
|
||||||
answerCreator,
|
answerCreator,
|
||||||
eventId,
|
eventId,
|
||||||
|
answer.text,
|
||||||
contract
|
contract
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -78,10 +78,10 @@ export const onCreateComment = functions
|
||||||
'created',
|
'created',
|
||||||
commentCreator,
|
commentCreator,
|
||||||
eventId,
|
eventId,
|
||||||
|
comment.text,
|
||||||
contract,
|
contract,
|
||||||
relatedSourceType,
|
relatedSourceType,
|
||||||
relatedUser,
|
relatedUser
|
||||||
comment.text
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const recipientUserIds = uniq([
|
const recipientUserIds = uniq([
|
||||||
|
|
24
functions/src/on-create-contract.ts
Normal file
24
functions/src/on-create-contract.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import * as functions from 'firebase-functions'
|
||||||
|
import { getUser } from './utils'
|
||||||
|
import { createNotification } from './create-notification'
|
||||||
|
import { Contract } from '../../common/contract'
|
||||||
|
|
||||||
|
export const onCreateContract = functions.firestore
|
||||||
|
.document('contracts/{contractId}')
|
||||||
|
.onCreate(async (snapshot, context) => {
|
||||||
|
const contract = snapshot.data() as Contract
|
||||||
|
const { eventId } = context
|
||||||
|
|
||||||
|
const contractCreator = await getUser(contract.creatorId)
|
||||||
|
if (!contractCreator) throw new Error('Could not find contract creator')
|
||||||
|
|
||||||
|
await createNotification(
|
||||||
|
contract.id,
|
||||||
|
'contract',
|
||||||
|
'created',
|
||||||
|
contractCreator,
|
||||||
|
eventId,
|
||||||
|
contract.question,
|
||||||
|
contract
|
||||||
|
)
|
||||||
|
})
|
|
@ -21,6 +21,7 @@ export const onFollowUser = functions.firestore
|
||||||
'created',
|
'created',
|
||||||
followingUser,
|
followingUser,
|
||||||
eventId,
|
eventId,
|
||||||
|
'',
|
||||||
undefined,
|
undefined,
|
||||||
undefined,
|
undefined,
|
||||||
follow.userId
|
follow.userId
|
||||||
|
|
|
@ -20,6 +20,7 @@ export const onUpdateContract = functions.firestore
|
||||||
'resolved',
|
'resolved',
|
||||||
contractUpdater,
|
contractUpdater,
|
||||||
eventId,
|
eventId,
|
||||||
|
contract.question,
|
||||||
contract
|
contract
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
|
@ -32,6 +33,7 @@ export const onUpdateContract = functions.firestore
|
||||||
'updated',
|
'updated',
|
||||||
contractUpdater,
|
contractUpdater,
|
||||||
eventId,
|
eventId,
|
||||||
|
contract.question,
|
||||||
contract
|
contract
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import { Contract } from 'common/contract'
|
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { ENV_CONFIG } from 'common/envs/constants'
|
import { ENV_CONFIG } from 'common/envs/constants'
|
||||||
import { contractPath } from 'web/lib/firebase/contracts'
|
|
||||||
import { copyToClipboard } from 'web/lib/util/copy'
|
import { copyToClipboard } from 'web/lib/util/copy'
|
||||||
import { DateTimeTooltip } from 'web/components/datetime-tooltip'
|
import { DateTimeTooltip } from 'web/components/datetime-tooltip'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
|
@ -11,20 +9,26 @@ import { LinkIcon } from '@heroicons/react/outline'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
export function CopyLinkDateTimeComponent(props: {
|
export function CopyLinkDateTimeComponent(props: {
|
||||||
contract: Contract
|
contractCreatorUsername: string
|
||||||
|
contractSlug: string
|
||||||
createdTime: number
|
createdTime: number
|
||||||
elementId: string
|
elementId: string
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { contract, elementId, createdTime, className } = props
|
const {
|
||||||
|
contractCreatorUsername,
|
||||||
|
contractSlug,
|
||||||
|
elementId,
|
||||||
|
createdTime,
|
||||||
|
className,
|
||||||
|
} = props
|
||||||
const [showToast, setShowToast] = useState(false)
|
const [showToast, setShowToast] = useState(false)
|
||||||
|
|
||||||
function copyLinkToComment(
|
function copyLinkToComment(
|
||||||
event: React.MouseEvent<HTMLAnchorElement, MouseEvent>
|
event: React.MouseEvent<HTMLAnchorElement, MouseEvent>
|
||||||
) {
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
const elementLocation = `https://${ENV_CONFIG.domain}${contractPath(
|
const elementLocation = `https://${ENV_CONFIG.domain}/${contractCreatorUsername}/${contractSlug}
|
||||||
contract
|
|
||||||
)}#${elementId}`
|
)}#${elementId}`
|
||||||
|
|
||||||
copyToClipboard(elementLocation)
|
copyToClipboard(elementLocation)
|
||||||
|
@ -35,7 +39,7 @@ export function CopyLinkDateTimeComponent(props: {
|
||||||
<div className={clsx('inline', className)}>
|
<div className={clsx('inline', className)}>
|
||||||
<DateTimeTooltip time={createdTime}>
|
<DateTimeTooltip time={createdTime}>
|
||||||
<Link
|
<Link
|
||||||
href={`/${contract.creatorUsername}/${contract.slug}#${elementId}`}
|
href={`/${contractCreatorUsername}/${contractCreatorUsername}#${elementId}`}
|
||||||
passHref={true}
|
passHref={true}
|
||||||
>
|
>
|
||||||
<a
|
<a
|
||||||
|
|
|
@ -127,7 +127,8 @@ export function FeedAnswerCommentGroup(props: {
|
||||||
<div className="text-sm text-gray-500">
|
<div className="text-sm text-gray-500">
|
||||||
<UserLink username={username} name={name} /> answered
|
<UserLink username={username} name={name} /> answered
|
||||||
<CopyLinkDateTimeComponent
|
<CopyLinkDateTimeComponent
|
||||||
contract={contract}
|
contractCreatorUsername={contract.creatorUsername}
|
||||||
|
contractSlug={contract.slug}
|
||||||
createdTime={answer.createdTime}
|
createdTime={answer.createdTime}
|
||||||
elementId={answerElementId}
|
elementId={answerElementId}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -245,7 +245,8 @@ export function FeedComment(props: {
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
<CopyLinkDateTimeComponent
|
<CopyLinkDateTimeComponent
|
||||||
contract={contract}
|
contractCreatorUsername={contract.creatorUsername}
|
||||||
|
contractSlug={contract.slug}
|
||||||
createdTime={createdTime}
|
createdTime={createdTime}
|
||||||
elementId={comment.id}
|
elementId={comment.id}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -17,8 +17,8 @@ export function usePreferredGroupedNotifications(
|
||||||
options: { unseenOnly: boolean }
|
options: { unseenOnly: boolean }
|
||||||
) {
|
) {
|
||||||
const [notificationGroups, setNotificationGroups] = useState<
|
const [notificationGroups, setNotificationGroups] = useState<
|
||||||
NotificationGroup[]
|
NotificationGroup[] | undefined
|
||||||
>([])
|
>(undefined)
|
||||||
|
|
||||||
const notifications = usePreferredNotifications(userId, options)
|
const notifications = usePreferredNotifications(userId, options)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -45,16 +45,17 @@ import toast from 'react-hot-toast'
|
||||||
export default function Notifications() {
|
export default function Notifications() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const [unseenNotificationGroups, setUnseenNotificationGroups] = useState<
|
const [unseenNotificationGroups, setUnseenNotificationGroups] = useState<
|
||||||
NotificationGroup[]
|
NotificationGroup[] | undefined
|
||||||
>([])
|
>(undefined)
|
||||||
const allNotificationGroups = usePreferredGroupedNotifications(user?.id, {
|
const allNotificationGroups = usePreferredGroupedNotifications(user?.id, {
|
||||||
unseenOnly: false,
|
unseenOnly: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!allNotificationGroups) return
|
||||||
// Don't re-add notifications that are visible right now or have been seen already.
|
// Don't re-add notifications that are visible right now or have been seen already.
|
||||||
const currentlyVisibleUnseenNotificationIds = Object.values(
|
const currentlyVisibleUnseenNotificationIds = Object.values(
|
||||||
unseenNotificationGroups
|
unseenNotificationGroups ?? []
|
||||||
)
|
)
|
||||||
.map((n) => n.notifications.map((n) => n.id))
|
.map((n) => n.notifications.map((n) => n.id))
|
||||||
.flat()
|
.flat()
|
||||||
|
@ -92,7 +93,7 @@ export default function Notifications() {
|
||||||
tabs={[
|
tabs={[
|
||||||
{
|
{
|
||||||
title: 'New Notifications',
|
title: 'New Notifications',
|
||||||
content: (
|
content: unseenNotificationGroups ? (
|
||||||
<div className={''}>
|
<div className={''}>
|
||||||
{unseenNotificationGroups.length === 0 &&
|
{unseenNotificationGroups.length === 0 &&
|
||||||
"You don't have any new notifications."}
|
"You don't have any new notifications."}
|
||||||
|
@ -113,11 +114,13 @@ export default function Notifications() {
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<LoadingIndicator />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'All Notifications',
|
title: 'All Notifications',
|
||||||
content: (
|
content: allNotificationGroups ? (
|
||||||
<div className={''}>
|
<div className={''}>
|
||||||
{allNotificationGroups.length === 0 &&
|
{allNotificationGroups.length === 0 &&
|
||||||
"You don't have any notifications. Try changing your settings to see more."}
|
"You don't have any notifications. Try changing your settings to see more."}
|
||||||
|
@ -138,6 +141,8 @@ export default function Notifications() {
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
) : (
|
||||||
|
<LoadingIndicator />
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -469,10 +474,8 @@ function isNotificationAboutContractResolution(
|
||||||
contract: Contract | null | undefined
|
contract: Contract | null | undefined
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
(sourceType === 'contract' && !sourceUpdateType && contract?.resolution) ||
|
(sourceType === 'contract' && sourceUpdateType === 'resolved') ||
|
||||||
(sourceType === 'contract' &&
|
(sourceType === 'contract' && !sourceUpdateType && contract?.resolution)
|
||||||
sourceUpdateType === 'resolved' &&
|
|
||||||
contract?.resolution)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -492,32 +495,58 @@ function NotificationItem(props: {
|
||||||
reason,
|
reason,
|
||||||
sourceUserUsername,
|
sourceUserUsername,
|
||||||
createdTime,
|
createdTime,
|
||||||
|
sourceText,
|
||||||
|
sourceContractTitle,
|
||||||
|
sourceContractCreatorUsername,
|
||||||
|
sourceContractSlug,
|
||||||
} = notification
|
} = notification
|
||||||
const [notificationText, setNotificationText] = useState<string>('')
|
const [notificationText, setNotificationText] = useState<string>('')
|
||||||
const [contract, setContract] = useState<Contract | null>(null)
|
const [contract, setContract] = useState<Contract | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!sourceContractId) return
|
if (
|
||||||
getContractFromId(sourceContractId).then((contract) => {
|
!sourceContractId ||
|
||||||
if (contract) setContract(contract)
|
(sourceContractSlug && sourceContractCreatorUsername)
|
||||||
})
|
)
|
||||||
}, [sourceContractId])
|
return
|
||||||
|
getContractFromId(sourceContractId)
|
||||||
|
.then((contract) => {
|
||||||
|
if (contract) setContract(contract)
|
||||||
|
})
|
||||||
|
.catch((e) => console.log(e))
|
||||||
|
}, [
|
||||||
|
sourceContractCreatorUsername,
|
||||||
|
sourceContractId,
|
||||||
|
sourceContractSlug,
|
||||||
|
sourceContractTitle,
|
||||||
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!contract || !sourceContractId || !sourceId) return
|
|
||||||
if (
|
if (
|
||||||
|
sourceText &&
|
||||||
|
(sourceType === 'comment' ||
|
||||||
|
sourceType === 'answer' ||
|
||||||
|
(sourceType === 'contract' && sourceUpdateType === 'updated'))
|
||||||
|
) {
|
||||||
|
setNotificationText(sourceText)
|
||||||
|
} else if (!contract || !sourceContractId || !sourceId) return
|
||||||
|
else if (
|
||||||
sourceType === 'answer' ||
|
sourceType === 'answer' ||
|
||||||
sourceType === 'comment' ||
|
sourceType === 'comment' ||
|
||||||
sourceType === 'contract'
|
sourceType === 'contract'
|
||||||
) {
|
) {
|
||||||
getNotificationText(
|
try {
|
||||||
sourceId,
|
getNotificationText(
|
||||||
sourceContractId,
|
sourceId,
|
||||||
sourceType,
|
sourceContractId,
|
||||||
sourceUpdateType,
|
sourceType,
|
||||||
setNotificationText,
|
sourceUpdateType,
|
||||||
contract
|
setNotificationText,
|
||||||
)
|
contract
|
||||||
|
)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
} else if (reasonText) {
|
} else if (reasonText) {
|
||||||
// Handle arbitrary notifications with reason text here.
|
// Handle arbitrary notifications with reason text here.
|
||||||
setNotificationText(reasonText)
|
setNotificationText(reasonText)
|
||||||
|
@ -527,6 +556,7 @@ function NotificationItem(props: {
|
||||||
reasonText,
|
reasonText,
|
||||||
sourceContractId,
|
sourceContractId,
|
||||||
sourceId,
|
sourceId,
|
||||||
|
sourceText,
|
||||||
sourceType,
|
sourceType,
|
||||||
sourceUpdateType,
|
sourceUpdateType,
|
||||||
])
|
])
|
||||||
|
@ -537,6 +567,10 @@ function NotificationItem(props: {
|
||||||
|
|
||||||
function getSourceUrl() {
|
function getSourceUrl() {
|
||||||
if (sourceType === 'follow') return `/${sourceUserUsername}`
|
if (sourceType === 'follow') return `/${sourceUserUsername}`
|
||||||
|
if (sourceContractCreatorUsername && sourceContractSlug)
|
||||||
|
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${getSourceIdForLinkComponent(
|
||||||
|
sourceId ?? ''
|
||||||
|
)}`
|
||||||
if (!contract) return ''
|
if (!contract) return ''
|
||||||
return `/${contract.creatorUsername}/${
|
return `/${contract.creatorUsername}/${
|
||||||
contract.slug
|
contract.slug
|
||||||
|
@ -562,19 +596,16 @@ function NotificationItem(props: {
|
||||||
sourceType: 'answer' | 'comment' | 'contract',
|
sourceType: 'answer' | 'comment' | 'contract',
|
||||||
sourceUpdateType: notification_source_update_types | undefined,
|
sourceUpdateType: notification_source_update_types | undefined,
|
||||||
setText: (text: string) => void,
|
setText: (text: string) => void,
|
||||||
contract?: Contract
|
contract: Contract
|
||||||
) {
|
) {
|
||||||
if (sourceType === 'contract' && !contract)
|
if (sourceType === 'contract') {
|
||||||
contract = await getContractFromId(sourceContractId)
|
|
||||||
|
|
||||||
if (sourceType === 'contract' && contract) {
|
|
||||||
if (
|
if (
|
||||||
isNotificationAboutContractResolution(
|
isNotificationAboutContractResolution(
|
||||||
sourceType,
|
sourceType,
|
||||||
sourceUpdateType,
|
sourceUpdateType,
|
||||||
contract
|
contract
|
||||||
) &&
|
) &&
|
||||||
contract?.resolution
|
contract.resolution
|
||||||
)
|
)
|
||||||
setText(contract.resolution)
|
setText(contract.resolution)
|
||||||
else setText(contract.question)
|
else setText(contract.question)
|
||||||
|
@ -602,31 +633,24 @@ function NotificationItem(props: {
|
||||||
className={'mr-0 flex-shrink-0'}
|
className={'mr-0 flex-shrink-0'}
|
||||||
/>
|
/>
|
||||||
<div className={'inline-flex overflow-hidden text-ellipsis pl-1'}>
|
<div className={'inline-flex overflow-hidden text-ellipsis pl-1'}>
|
||||||
{sourceType &&
|
<span className={'flex-shrink-0'}>
|
||||||
reason &&
|
{sourceType &&
|
||||||
getReasonForShowingNotification(
|
reason &&
|
||||||
sourceType,
|
getReasonForShowingNotification(
|
||||||
reason,
|
sourceType,
|
||||||
sourceUpdateType,
|
reason,
|
||||||
contract,
|
sourceUpdateType,
|
||||||
true
|
contract,
|
||||||
).replace(' on', '')}
|
true
|
||||||
|
).replace(' on', '')}
|
||||||
|
</span>
|
||||||
<div className={'ml-1 text-black'}>
|
<div className={'ml-1 text-black'}>
|
||||||
{contract ? (
|
<NotificationTextLabel
|
||||||
<NotificationTextLabel
|
contract={contract}
|
||||||
contract={contract}
|
defaultText={notificationText}
|
||||||
defaultText={notificationText}
|
className={'line-clamp-1'}
|
||||||
className={'line-clamp-1'}
|
notification={notification}
|
||||||
sourceUpdateType={sourceUpdateType}
|
/>
|
||||||
sourceType={sourceType}
|
|
||||||
/>
|
|
||||||
) : sourceType != 'follow' ? (
|
|
||||||
<LoadingIndicator
|
|
||||||
spinnerClassName={'border-gray-500 h-4 w-4'}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -666,35 +690,40 @@ function NotificationItem(props: {
|
||||||
contract
|
contract
|
||||||
)}
|
)}
|
||||||
<span className={'mx-1 font-bold'}>
|
<span className={'mx-1 font-bold'}>
|
||||||
{contract?.question}
|
{contract?.question || sourceContractTitle}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{contract && sourceId && (
|
{sourceId && contract && (
|
||||||
<CopyLinkDateTimeComponent
|
<CopyLinkDateTimeComponent
|
||||||
contract={contract}
|
contractCreatorUsername={contract.creatorUsername}
|
||||||
|
contractSlug={contract.slug}
|
||||||
createdTime={createdTime}
|
createdTime={createdTime}
|
||||||
elementId={getSourceIdForLinkComponent(sourceId)}
|
elementId={getSourceIdForLinkComponent(sourceId)}
|
||||||
className={'-mx-1 inline-flex sm:inline-block'}
|
className={'-mx-1 inline-flex sm:inline-block'}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{sourceId &&
|
||||||
|
sourceContractSlug &&
|
||||||
|
sourceContractCreatorUsername && (
|
||||||
|
<CopyLinkDateTimeComponent
|
||||||
|
contractCreatorUsername={sourceContractCreatorUsername}
|
||||||
|
contractSlug={sourceContractSlug}
|
||||||
|
createdTime={createdTime}
|
||||||
|
elementId={getSourceIdForLinkComponent(sourceId)}
|
||||||
|
className={'-mx-1 inline-flex sm:inline-block'}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
<div className={'mt-1 md:text-base'}>
|
<div className={'mt-1 md:text-base'}>
|
||||||
{contract ? (
|
<NotificationTextLabel
|
||||||
<NotificationTextLabel
|
contract={contract}
|
||||||
contract={contract}
|
defaultText={notificationText}
|
||||||
defaultText={notificationText}
|
notification={notification}
|
||||||
sourceType={sourceType}
|
/>
|
||||||
sourceUpdateType={sourceUpdateType}
|
|
||||||
/>
|
|
||||||
) : sourceType != 'follow' ? (
|
|
||||||
<LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
|
|
||||||
) : (
|
|
||||||
<div />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={'mt-6 border-b border-gray-300'} />
|
<div className={'mt-6 border-b border-gray-300'} />
|
||||||
|
@ -704,14 +733,21 @@ function NotificationItem(props: {
|
||||||
}
|
}
|
||||||
|
|
||||||
function NotificationTextLabel(props: {
|
function NotificationTextLabel(props: {
|
||||||
contract: Contract
|
|
||||||
defaultText: string
|
defaultText: string
|
||||||
sourceType?: notification_source_types
|
contract?: Contract | null
|
||||||
sourceUpdateType?: notification_source_update_types
|
notification: Notification
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { contract, className, sourceUpdateType, sourceType, defaultText } =
|
const { contract, className, defaultText, notification } = props
|
||||||
props
|
const { sourceUpdateType, sourceType, sourceText, sourceContractTitle } =
|
||||||
|
notification
|
||||||
|
if (
|
||||||
|
!contract &&
|
||||||
|
!sourceContractTitle &&
|
||||||
|
!sourceText &&
|
||||||
|
sourceType !== 'follow'
|
||||||
|
)
|
||||||
|
return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
|
||||||
|
|
||||||
if (
|
if (
|
||||||
isNotificationAboutContractResolution(
|
isNotificationAboutContractResolution(
|
||||||
|
@ -719,7 +755,7 @@ function NotificationTextLabel(props: {
|
||||||
sourceUpdateType,
|
sourceUpdateType,
|
||||||
contract
|
contract
|
||||||
) &&
|
) &&
|
||||||
contract.resolution
|
contract?.resolution
|
||||||
) {
|
) {
|
||||||
if (contract.outcomeType === 'FREE_RESPONSE') {
|
if (contract.outcomeType === 'FREE_RESPONSE') {
|
||||||
return (
|
return (
|
||||||
|
@ -782,7 +818,8 @@ function getReasonForShowingNotification(
|
||||||
else reasonText = `commented on`
|
else reasonText = `commented on`
|
||||||
break
|
break
|
||||||
case 'contract':
|
case 'contract':
|
||||||
if (
|
if (reason === 'you_follow_user') reasonText = 'created a new question'
|
||||||
|
else if (
|
||||||
isNotificationAboutContractResolution(
|
isNotificationAboutContractResolution(
|
||||||
source,
|
source,
|
||||||
sourceUpdateType,
|
sourceUpdateType,
|
||||||
|
@ -794,7 +831,8 @@ function getReasonForShowingNotification(
|
||||||
break
|
break
|
||||||
case 'answer':
|
case 'answer':
|
||||||
if (reason === 'on_users_contract') reasonText = `answered your question `
|
if (reason === 'on_users_contract') reasonText = `answered your question `
|
||||||
if (reason === 'on_contract_with_users_comment') reasonText = `answered`
|
else if (reason === 'on_contract_with_users_comment')
|
||||||
|
reasonText = `answered`
|
||||||
else if (reason === 'on_contract_with_users_answer')
|
else if (reason === 'on_contract_with_users_answer')
|
||||||
reasonText = `answered`
|
reasonText = `answered`
|
||||||
else if (reason === 'on_contract_with_users_shares_in')
|
else if (reason === 'on_contract_with_users_shares_in')
|
||||||
|
|
Loading…
Reference in New Issue
Block a user