Enable tipping on group chats w/ notif (#629)
This commit is contained in:
parent
d6136a9937
commit
b1b016f9e0
|
@ -36,8 +36,9 @@ type Tip = {
|
||||||
toType: 'USER'
|
toType: 'USER'
|
||||||
category: 'TIP'
|
category: 'TIP'
|
||||||
data: {
|
data: {
|
||||||
contractId: string
|
|
||||||
commentId: string
|
commentId: string
|
||||||
|
contractId?: string
|
||||||
|
groupId?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ import { Bet } from '../../common/bet'
|
||||||
import { Answer } from '../../common/answer'
|
import { Answer } from '../../common/answer'
|
||||||
import { getContractBetMetrics } from '../../common/calculate'
|
import { getContractBetMetrics } from '../../common/calculate'
|
||||||
import { removeUndefinedProps } from '../../common/util/object'
|
import { removeUndefinedProps } from '../../common/util/object'
|
||||||
|
import { TipTxn } from '../../common/txn'
|
||||||
|
import { Group } from '../../common/group'
|
||||||
const firestore = admin.firestore()
|
const firestore = admin.firestore()
|
||||||
|
|
||||||
type user_to_reason_texts = {
|
type user_to_reason_texts = {
|
||||||
|
@ -285,15 +287,6 @@ export const createNotification = async (
|
||||||
isSeeOnHref: sourceSlug,
|
isSeeOnHref: sourceSlug,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const notifyTippedUserOfNewTip = async (
|
|
||||||
userToReasonTexts: user_to_reason_texts,
|
|
||||||
userId: string
|
|
||||||
) => {
|
|
||||||
if (shouldGetNotification(userId, userToReasonTexts))
|
|
||||||
userToReasonTexts[userId] = {
|
|
||||||
reason: 'tip_received',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const getUsersToNotify = async () => {
|
const getUsersToNotify = async () => {
|
||||||
const userToReasonTexts: user_to_reason_texts = {}
|
const userToReasonTexts: user_to_reason_texts = {}
|
||||||
|
@ -346,8 +339,6 @@ export const createNotification = async (
|
||||||
userToReasonTexts,
|
userToReasonTexts,
|
||||||
sourceContract.creatorId
|
sourceContract.creatorId
|
||||||
)
|
)
|
||||||
} else if (sourceType === 'tip' && relatedUserId) {
|
|
||||||
await notifyTippedUserOfNewTip(userToReasonTexts, relatedUserId)
|
|
||||||
}
|
}
|
||||||
return userToReasonTexts
|
return userToReasonTexts
|
||||||
}
|
}
|
||||||
|
@ -355,3 +346,39 @@ export const createNotification = async (
|
||||||
const userToReasonTexts = await getUsersToNotify()
|
const userToReasonTexts = await getUsersToNotify()
|
||||||
await createUsersNotifications(userToReasonTexts)
|
await createUsersNotifications(userToReasonTexts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createTipNotification = async (
|
||||||
|
fromUser: User,
|
||||||
|
toUser: User,
|
||||||
|
tip: TipTxn,
|
||||||
|
idempotencyKey: string,
|
||||||
|
commentId: string,
|
||||||
|
contract?: Contract,
|
||||||
|
group?: Group
|
||||||
|
) => {
|
||||||
|
const slug = group ? group.slug + `#${commentId}` : commentId
|
||||||
|
|
||||||
|
const notificationRef = firestore
|
||||||
|
.collection(`/users/${toUser.id}/notifications`)
|
||||||
|
.doc(idempotencyKey)
|
||||||
|
const notification: Notification = {
|
||||||
|
id: idempotencyKey,
|
||||||
|
userId: toUser.id,
|
||||||
|
reason: 'tip_received',
|
||||||
|
createdTime: Date.now(),
|
||||||
|
isSeen: false,
|
||||||
|
sourceId: tip.id,
|
||||||
|
sourceType: 'tip',
|
||||||
|
sourceUpdateType: 'created',
|
||||||
|
sourceUserName: fromUser.name,
|
||||||
|
sourceUserUsername: fromUser.username,
|
||||||
|
sourceUserAvatarUrl: fromUser.avatarUrl,
|
||||||
|
sourceText: tip.amount.toString(),
|
||||||
|
sourceContractCreatorUsername: contract?.creatorUsername,
|
||||||
|
sourceContractTitle: contract?.question,
|
||||||
|
sourceContractSlug: contract?.slug,
|
||||||
|
sourceSlug: slug,
|
||||||
|
sourceTitle: group?.name,
|
||||||
|
}
|
||||||
|
return await notificationRef.set(removeUndefinedProps(notification))
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import * as functions from 'firebase-functions'
|
import * as functions from 'firebase-functions'
|
||||||
import { Txn } from 'common/txn'
|
import { TipTxn, Txn } from 'common/txn'
|
||||||
import { getContract, getUser, log } from './utils'
|
import { getContract, getGroup, getUser, log } from './utils'
|
||||||
import { createNotification } from './create-notification'
|
import { createTipNotification } from './create-notification'
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import { Comment } from 'common/comment'
|
import { Comment } from 'common/comment'
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ export const onCreateTxn = functions.firestore
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
async function handleTipTxn(txn: Txn, eventId: string) {
|
async function handleTipTxn(txn: TipTxn, eventId: string) {
|
||||||
// get user sending and receiving tip
|
// get user sending and receiving tip
|
||||||
const [sender, receiver] = await Promise.all([
|
const [sender, receiver] = await Promise.all([
|
||||||
getUser(txn.fromId),
|
getUser(txn.fromId),
|
||||||
|
@ -29,40 +29,53 @@ async function handleTipTxn(txn: Txn, eventId: string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!txn.data?.contractId || !txn.data?.commentId) {
|
if (!txn.data?.commentId) {
|
||||||
log('No contractId or comment id in tip txn.data')
|
log('No comment id in tip txn.data')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let contract = undefined
|
||||||
|
let group = undefined
|
||||||
|
let commentSnapshot = undefined
|
||||||
|
|
||||||
const contract = await getContract(txn.data.contractId)
|
if (txn.data.contractId) {
|
||||||
if (!contract) {
|
contract = await getContract(txn.data.contractId)
|
||||||
log('Could not find contract')
|
if (!contract) {
|
||||||
return
|
log('Could not find contract')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commentSnapshot = await firestore
|
||||||
|
.collection('contracts')
|
||||||
|
.doc(contract.id)
|
||||||
|
.collection('comments')
|
||||||
|
.doc(txn.data.commentId)
|
||||||
|
.get()
|
||||||
|
} else if (txn.data.groupId) {
|
||||||
|
group = await getGroup(txn.data.groupId)
|
||||||
|
if (!group) {
|
||||||
|
log('Could not find group')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commentSnapshot = await firestore
|
||||||
|
.collection('groups')
|
||||||
|
.doc(group.id)
|
||||||
|
.collection('comments')
|
||||||
|
.doc(txn.data.commentId)
|
||||||
|
.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
const commentSnapshot = await firestore
|
if (!commentSnapshot || !commentSnapshot.exists) {
|
||||||
.collection('contracts')
|
|
||||||
.doc(contract.id)
|
|
||||||
.collection('comments')
|
|
||||||
.doc(txn.data.commentId)
|
|
||||||
.get()
|
|
||||||
if (!commentSnapshot.exists) {
|
|
||||||
log('Could not find comment')
|
log('Could not find comment')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const comment = commentSnapshot.data() as Comment
|
const comment = commentSnapshot.data() as Comment
|
||||||
|
|
||||||
await createNotification(
|
await createTipNotification(
|
||||||
txn.id,
|
|
||||||
'tip',
|
|
||||||
'created',
|
|
||||||
sender,
|
sender,
|
||||||
|
receiver,
|
||||||
|
txn,
|
||||||
eventId,
|
eventId,
|
||||||
txn.amount.toString(),
|
comment.id,
|
||||||
contract,
|
contract,
|
||||||
'comment',
|
group
|
||||||
receiver.id,
|
|
||||||
txn.data?.commentId,
|
|
||||||
comment.text
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as admin from 'firebase-admin'
|
||||||
import { chunk } from 'lodash'
|
import { chunk } from 'lodash'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
import { PrivateUser, User } from '../../common/user'
|
import { PrivateUser, User } from '../../common/user'
|
||||||
|
import { Group } from '../../common/group'
|
||||||
|
|
||||||
export const log = (...args: unknown[]) => {
|
export const log = (...args: unknown[]) => {
|
||||||
console.log(`[${new Date().toISOString()}]`, ...args)
|
console.log(`[${new Date().toISOString()}]`, ...args)
|
||||||
|
@ -66,6 +67,10 @@ export const getContract = (contractId: string) => {
|
||||||
return getDoc<Contract>('contracts', contractId)
|
return getDoc<Contract>('contracts', contractId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getGroup = (groupId: string) => {
|
||||||
|
return getDoc<Group>('groups', groupId)
|
||||||
|
}
|
||||||
|
|
||||||
export const getUser = (userId: string) => {
|
export const getUser = (userId: string) => {
|
||||||
return getDoc<User>('users', userId)
|
return getDoc<User>('users', userId)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,13 +18,18 @@ import { UserLink } from 'web/components/user-page'
|
||||||
|
|
||||||
import { groupPath } from 'web/lib/firebase/groups'
|
import { groupPath } from 'web/lib/firebase/groups'
|
||||||
import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time'
|
import { CopyLinkDateTimeComponent } from 'web/components/feed/copy-link-date-time'
|
||||||
|
import { CommentTipMap, CommentTips } from 'web/hooks/use-tip-txns'
|
||||||
|
import { Tipper } from 'web/components/tipper'
|
||||||
|
import { sum } from 'lodash'
|
||||||
|
import { formatMoney } from 'common/util/format'
|
||||||
|
|
||||||
export function GroupChat(props: {
|
export function GroupChat(props: {
|
||||||
messages: Comment[]
|
messages: Comment[]
|
||||||
user: User | null | undefined
|
user: User | null | undefined
|
||||||
group: Group
|
group: Group
|
||||||
|
tips: CommentTipMap
|
||||||
}) {
|
}) {
|
||||||
const { messages, user, group } = props
|
const { messages, user, group, tips } = props
|
||||||
const [messageText, setMessageText] = useState('')
|
const [messageText, setMessageText] = useState('')
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false)
|
const [isSubmitting, setIsSubmitting] = useState(false)
|
||||||
const [scrollToBottomRef, setScrollToBottomRef] =
|
const [scrollToBottomRef, setScrollToBottomRef] =
|
||||||
|
@ -117,6 +122,7 @@ export function GroupChat(props: {
|
||||||
? setScrollToMessageRef
|
? setScrollToMessageRef
|
||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
|
tips={tips[message.id] ?? {}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{messages.length === 0 && (
|
{messages.length === 0 && (
|
||||||
|
@ -166,8 +172,9 @@ const GroupMessage = memo(function GroupMessage_(props: {
|
||||||
onReplyClick?: (comment: Comment) => void
|
onReplyClick?: (comment: Comment) => void
|
||||||
setRef?: (ref: HTMLDivElement) => void
|
setRef?: (ref: HTMLDivElement) => void
|
||||||
highlight?: boolean
|
highlight?: boolean
|
||||||
|
tips: CommentTips
|
||||||
}) {
|
}) {
|
||||||
const { comment, onReplyClick, group, setRef, highlight, user } = props
|
const { comment, onReplyClick, group, setRef, highlight, user, tips } = props
|
||||||
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
|
const { text, userUsername, userName, userAvatarUrl, createdTime } = comment
|
||||||
const isCreatorsComment = user && comment.userId === user.id
|
const isCreatorsComment = user && comment.userId === user.id
|
||||||
return (
|
return (
|
||||||
|
@ -209,16 +216,24 @@ const GroupMessage = memo(function GroupMessage_(props: {
|
||||||
shouldTruncate={false}
|
shouldTruncate={false}
|
||||||
/>
|
/>
|
||||||
</Row>
|
</Row>
|
||||||
{!isCreatorsComment && onReplyClick && (
|
<Row>
|
||||||
<button
|
{!isCreatorsComment && onReplyClick && (
|
||||||
className={
|
<button
|
||||||
'self-start py-1 text-xs font-bold text-gray-500 hover:underline'
|
className={
|
||||||
}
|
'self-start py-1 text-xs font-bold text-gray-500 hover:underline'
|
||||||
onClick={() => onReplyClick(comment)}
|
}
|
||||||
>
|
onClick={() => onReplyClick(comment)}
|
||||||
Reply
|
>
|
||||||
</button>
|
Reply
|
||||||
)}
|
</button>
|
||||||
|
)}
|
||||||
|
{isCreatorsComment && sum(Object.values(tips)) > 0 && (
|
||||||
|
<span className={'text-primary'}>
|
||||||
|
{formatMoney(sum(Object.values(tips)))}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{!isCreatorsComment && <Tipper comment={comment} tips={tips} />}
|
||||||
|
</Row>
|
||||||
</Col>
|
</Col>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -53,6 +53,7 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) {
|
||||||
data: {
|
data: {
|
||||||
contractId: comment.contractId,
|
contractId: comment.contractId,
|
||||||
commentId: comment.id,
|
commentId: comment.id,
|
||||||
|
groupId: comment.groupId,
|
||||||
},
|
},
|
||||||
description: `${user.name} tipped M$ ${change} to ${comment.userName} for a comment`,
|
description: `${user.name} tipped M$ ${change} to ${comment.userName} for a comment`,
|
||||||
})
|
})
|
||||||
|
@ -60,6 +61,7 @@ export function Tipper(prop: { comment: Comment; tips: CommentTips }) {
|
||||||
track('send comment tip', {
|
track('send comment tip', {
|
||||||
contractId: comment.contractId,
|
contractId: comment.contractId,
|
||||||
commentId: comment.id,
|
commentId: comment.id,
|
||||||
|
groupId: comment.groupId,
|
||||||
amount: change,
|
amount: change,
|
||||||
fromId: user.id,
|
fromId: user.id,
|
||||||
toId: comment.userId,
|
toId: comment.userId,
|
||||||
|
|
|
@ -1,17 +1,25 @@
|
||||||
import { TipTxn } from 'common/txn'
|
import { TipTxn } from 'common/txn'
|
||||||
import { groupBy, mapValues, sumBy } from 'lodash'
|
import { groupBy, mapValues, sumBy } from 'lodash'
|
||||||
import { useEffect, useMemo, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import { listenForTipTxns } from 'web/lib/firebase/txns'
|
import {
|
||||||
|
listenForTipTxns,
|
||||||
|
listenForTipTxnsOnGroup,
|
||||||
|
} from 'web/lib/firebase/txns'
|
||||||
|
|
||||||
export type CommentTips = { [userId: string]: number }
|
export type CommentTips = { [userId: string]: number }
|
||||||
export type CommentTipMap = { [commentId: string]: CommentTips }
|
export type CommentTipMap = { [commentId: string]: CommentTips }
|
||||||
|
|
||||||
export function useTipTxns(contractId: string): CommentTipMap {
|
export function useTipTxns(on: {
|
||||||
|
contractId?: string
|
||||||
|
groupId?: string
|
||||||
|
}): CommentTipMap {
|
||||||
const [txns, setTxns] = useState<TipTxn[]>([])
|
const [txns, setTxns] = useState<TipTxn[]>([])
|
||||||
|
const { contractId, groupId } = on
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return listenForTipTxns(contractId, setTxns)
|
if (contractId) return listenForTipTxns(contractId, setTxns)
|
||||||
}, [contractId, setTxns])
|
if (groupId) return listenForTipTxnsOnGroup(groupId, setTxns)
|
||||||
|
}, [contractId, groupId, setTxns])
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const byComment = groupBy(txns, 'data.commentId')
|
const byComment = groupBy(txns, 'data.commentId')
|
||||||
|
|
|
@ -27,18 +27,31 @@ export function getAllCharityTxns() {
|
||||||
return getValues<DonationTxn>(charitiesQuery)
|
return getValues<DonationTxn>(charitiesQuery)
|
||||||
}
|
}
|
||||||
|
|
||||||
const getTipsQuery = (contractId: string) =>
|
const getTipsOnContractQuery = (contractId: string) =>
|
||||||
query(
|
query(
|
||||||
txns,
|
txns,
|
||||||
where('category', '==', 'TIP'),
|
where('category', '==', 'TIP'),
|
||||||
where('data.contractId', '==', contractId)
|
where('data.contractId', '==', contractId)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const getTipsOnGroupQuery = (groupId: string) =>
|
||||||
|
query(
|
||||||
|
txns,
|
||||||
|
where('category', '==', 'TIP'),
|
||||||
|
where('data.groupId', '==', groupId)
|
||||||
|
)
|
||||||
|
|
||||||
export function listenForTipTxns(
|
export function listenForTipTxns(
|
||||||
contractId: string,
|
contractId: string,
|
||||||
setTxns: (txns: TipTxn[]) => void
|
setTxns: (txns: TipTxn[]) => void
|
||||||
) {
|
) {
|
||||||
return listenForValues<TipTxn>(getTipsQuery(contractId), setTxns)
|
return listenForValues<TipTxn>(getTipsOnContractQuery(contractId), setTxns)
|
||||||
|
}
|
||||||
|
export function listenForTipTxnsOnGroup(
|
||||||
|
groupId: string,
|
||||||
|
setTxns: (txns: TipTxn[]) => void
|
||||||
|
) {
|
||||||
|
return listenForValues<TipTxn>(getTipsOnGroupQuery(groupId), setTxns)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all manalink Txns that are from or to this user
|
// Find all manalink Txns that are from or to this user
|
||||||
|
|
|
@ -124,7 +124,7 @@ export function ContractPageContent(
|
||||||
// Sort for now to see if bug is fixed.
|
// Sort for now to see if bug is fixed.
|
||||||
comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
|
comments.sort((c1, c2) => c1.createdTime - c2.createdTime)
|
||||||
|
|
||||||
const tips = useTipTxns(contract.id)
|
const tips = useTipTxns({ contractId: contract.id })
|
||||||
|
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
const { width, height } = useWindowSize()
|
const { width, height } = useWindowSize()
|
||||||
|
|
|
@ -53,6 +53,7 @@ import { ContractSearch } from 'web/components/contract-search'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { FollowList } from 'web/components/follow-list'
|
import { FollowList } from 'web/components/follow-list'
|
||||||
import { SearchIcon } from '@heroicons/react/outline'
|
import { SearchIcon } from '@heroicons/react/outline'
|
||||||
|
import { useTipTxns } from 'web/hooks/use-tip-txns'
|
||||||
|
|
||||||
export const getStaticProps = fromPropz(getStaticPropz)
|
export const getStaticProps = fromPropz(getStaticPropz)
|
||||||
export async function getStaticPropz(props: { params: { slugs: string[] } }) {
|
export async function getStaticPropz(props: { params: { slugs: string[] } }) {
|
||||||
|
@ -149,6 +150,7 @@ export default function GroupPage(props: {
|
||||||
const group = useGroup(props.group?.id) ?? props.group
|
const group = useGroup(props.group?.id) ?? props.group
|
||||||
const [contracts, setContracts] = useState<Contract[] | undefined>(undefined)
|
const [contracts, setContracts] = useState<Contract[] | undefined>(undefined)
|
||||||
const [query, setQuery] = useState('')
|
const [query, setQuery] = useState('')
|
||||||
|
const tips = useTipTxns({ groupId: group?.id })
|
||||||
|
|
||||||
const messages = useCommentsOnGroup(group?.id)
|
const messages = useCommentsOnGroup(group?.id)
|
||||||
const debouncedQuery = debounce(setQuery, 50)
|
const debouncedQuery = debounce(setQuery, 50)
|
||||||
|
@ -263,7 +265,12 @@ export default function GroupPage(props: {
|
||||||
{
|
{
|
||||||
title: 'Chat',
|
title: 'Chat',
|
||||||
content: messages ? (
|
content: messages ? (
|
||||||
<GroupChat messages={messages} user={user} group={group} />
|
<GroupChat
|
||||||
|
messages={messages}
|
||||||
|
user={user}
|
||||||
|
group={group}
|
||||||
|
tips={tips}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<LoadingIndicator />
|
<LoadingIndicator />
|
||||||
),
|
),
|
||||||
|
|
|
@ -384,7 +384,10 @@ function IncomeNotificationItem(props: {
|
||||||
</div>
|
</div>
|
||||||
<span className={'flex truncate'}>
|
<span className={'flex truncate'}>
|
||||||
{getReasonForShowingIncomeNotification(true)}
|
{getReasonForShowingIncomeNotification(true)}
|
||||||
<QuestionLink notification={notification} ignoreClick={true} />
|
<QuestionOrGroupLink
|
||||||
|
notification={notification}
|
||||||
|
ignoreClick={true}
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -425,7 +428,7 @@ function IncomeNotificationItem(props: {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
{getReasonForShowingIncomeNotification(false)} {' on'}
|
{getReasonForShowingIncomeNotification(false)} {' on'}
|
||||||
<QuestionLink notification={notification} />
|
<QuestionOrGroupLink notification={notification} />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
|
@ -481,7 +484,7 @@ function NotificationGroupItem(props: {
|
||||||
<div className={'flex w-full flex-row justify-between'}>
|
<div className={'flex w-full flex-row justify-between'}>
|
||||||
<div className={'ml-2'}>
|
<div className={'ml-2'}>
|
||||||
Activity on
|
Activity on
|
||||||
<QuestionLink notification={notifications[0]} />
|
<QuestionOrGroupLink notification={notifications[0]} />
|
||||||
</div>
|
</div>
|
||||||
<div className={'hidden sm:inline-block'}>
|
<div className={'hidden sm:inline-block'}>
|
||||||
<RelativeTimestamp time={notifications[0].createdTime} />
|
<RelativeTimestamp time={notifications[0].createdTime} />
|
||||||
|
@ -666,7 +669,7 @@ function NotificationItem(props: {
|
||||||
{isChildOfGroup ? (
|
{isChildOfGroup ? (
|
||||||
<RelativeTimestamp time={notification.createdTime} />
|
<RelativeTimestamp time={notification.createdTime} />
|
||||||
) : (
|
) : (
|
||||||
<QuestionLink notification={notification} />
|
<QuestionOrGroupLink notification={notification} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -705,7 +708,7 @@ export const setNotificationsAsSeen = (notifications: Notification[]) => {
|
||||||
return notifications
|
return notifications
|
||||||
}
|
}
|
||||||
|
|
||||||
function QuestionLink(props: {
|
function QuestionOrGroupLink(props: {
|
||||||
notification: Notification
|
notification: Notification
|
||||||
ignoreClick?: boolean
|
ignoreClick?: boolean
|
||||||
}) {
|
}) {
|
||||||
|
@ -733,7 +736,7 @@ function QuestionLink(props: {
|
||||||
href={
|
href={
|
||||||
sourceContractCreatorUsername
|
sourceContractCreatorUsername
|
||||||
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
||||||
: sourceType === 'group' && sourceSlug
|
: (sourceType === 'group' || sourceType === 'tip') && sourceSlug
|
||||||
? `${groupPath(sourceSlug)}`
|
? `${groupPath(sourceSlug)}`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
@ -771,8 +774,9 @@ function getSourceUrl(notification: Notification) {
|
||||||
sourceType === 'user'
|
sourceType === 'user'
|
||||||
)
|
)
|
||||||
return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
||||||
if (sourceType === 'tip')
|
if (sourceType === 'tip' && sourceContractSlug)
|
||||||
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${sourceSlug}`
|
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${sourceSlug}`
|
||||||
|
if (sourceType === 'tip' && sourceSlug) return `${groupPath(sourceSlug)}`
|
||||||
if (sourceContractCreatorUsername && sourceContractSlug)
|
if (sourceContractCreatorUsername && sourceContractSlug)
|
||||||
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${getSourceIdForLinkComponent(
|
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${getSourceIdForLinkComponent(
|
||||||
sourceId ?? '',
|
sourceId ?? '',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user