Show avatars of tippers and unique bettors (#837)
* Show avatars of tippers and unique bettors * Make transparent the avatar bg * fix import
This commit is contained in:
parent
7508d86c73
commit
00ba3b0c48
|
@ -15,6 +15,7 @@ export type Notification = {
|
||||||
sourceUserUsername?: string
|
sourceUserUsername?: string
|
||||||
sourceUserAvatarUrl?: string
|
sourceUserAvatarUrl?: string
|
||||||
sourceText?: string
|
sourceText?: string
|
||||||
|
data?: string
|
||||||
|
|
||||||
sourceContractTitle?: string
|
sourceContractTitle?: string
|
||||||
sourceContractCreatorUsername?: string
|
sourceContractCreatorUsername?: string
|
||||||
|
|
|
@ -151,15 +151,6 @@ export const createNotification = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyContractCreatorOfUniqueBettorsBonus = async (
|
|
||||||
userToReasonTexts: user_to_reason_texts,
|
|
||||||
userId: string
|
|
||||||
) => {
|
|
||||||
userToReasonTexts[userId] = {
|
|
||||||
reason: 'unique_bettors_on_your_contract',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
@ -192,16 +183,6 @@ export const createNotification = async (
|
||||||
sourceContract
|
sourceContract
|
||||||
) {
|
) {
|
||||||
await notifyContractCreator(userToReasonTexts, sourceContract)
|
await notifyContractCreator(userToReasonTexts, sourceContract)
|
||||||
} else if (
|
|
||||||
sourceType === 'bonus' &&
|
|
||||||
sourceUpdateType === 'created' &&
|
|
||||||
sourceContract
|
|
||||||
) {
|
|
||||||
// Note: the daily bonus won't have a contract attached to it
|
|
||||||
await notifyContractCreatorOfUniqueBettorsBonus(
|
|
||||||
userToReasonTexts,
|
|
||||||
sourceContract.creatorId
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await createUsersNotifications(userToReasonTexts)
|
await createUsersNotifications(userToReasonTexts)
|
||||||
|
@ -737,3 +718,38 @@ export async function filterUserIdsForOnlyFollowerIds(
|
||||||
)
|
)
|
||||||
return userIds.filter((id) => contractFollowersIds.includes(id))
|
return userIds.filter((id) => contractFollowersIds.includes(id))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const createUniqueBettorBonusNotification = async (
|
||||||
|
contractCreatorId: string,
|
||||||
|
bettor: User,
|
||||||
|
txnId: string,
|
||||||
|
contract: Contract,
|
||||||
|
amount: number,
|
||||||
|
idempotencyKey: string
|
||||||
|
) => {
|
||||||
|
const notificationRef = firestore
|
||||||
|
.collection(`/users/${contractCreatorId}/notifications`)
|
||||||
|
.doc(idempotencyKey)
|
||||||
|
const notification: Notification = {
|
||||||
|
id: idempotencyKey,
|
||||||
|
userId: contractCreatorId,
|
||||||
|
reason: 'unique_bettors_on_your_contract',
|
||||||
|
createdTime: Date.now(),
|
||||||
|
isSeen: false,
|
||||||
|
sourceId: txnId,
|
||||||
|
sourceType: 'bonus',
|
||||||
|
sourceUpdateType: 'created',
|
||||||
|
sourceUserName: bettor.name,
|
||||||
|
sourceUserUsername: bettor.username,
|
||||||
|
sourceUserAvatarUrl: bettor.avatarUrl,
|
||||||
|
sourceText: amount.toString(),
|
||||||
|
sourceSlug: contract.slug,
|
||||||
|
sourceTitle: contract.question,
|
||||||
|
// Perhaps not necessary, but just in case
|
||||||
|
sourceContractSlug: contract.slug,
|
||||||
|
sourceContractId: contract.id,
|
||||||
|
sourceContractTitle: contract.question,
|
||||||
|
sourceContractCreatorUsername: contract.creatorUsername,
|
||||||
|
}
|
||||||
|
return await notificationRef.set(removeUndefinedProps(notification))
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { getUser, getValues, isProd, log } from './utils'
|
||||||
import {
|
import {
|
||||||
createBetFillNotification,
|
createBetFillNotification,
|
||||||
createBettingStreakBonusNotification,
|
createBettingStreakBonusNotification,
|
||||||
createNotification,
|
createUniqueBettorBonusNotification,
|
||||||
} from './create-notification'
|
} from './create-notification'
|
||||||
import { filterDefined } from '../../common/util/array'
|
import { filterDefined } from '../../common/util/array'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
|
@ -54,11 +54,11 @@ export const onCreateBet = functions.firestore
|
||||||
log(`Could not find contract ${contractId}`)
|
log(`Could not find contract ${contractId}`)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
await updateUniqueBettorsAndGiveCreatorBonus(contract, eventId, bet.userId)
|
|
||||||
|
|
||||||
const bettor = await getUser(bet.userId)
|
const bettor = await getUser(bet.userId)
|
||||||
if (!bettor) return
|
if (!bettor) return
|
||||||
|
|
||||||
|
await updateUniqueBettorsAndGiveCreatorBonus(contract, eventId, bettor)
|
||||||
await notifyFills(bet, contract, eventId, bettor)
|
await notifyFills(bet, contract, eventId, bettor)
|
||||||
await updateBettingStreak(bettor, bet, contract, eventId)
|
await updateBettingStreak(bettor, bet, contract, eventId)
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ const updateBettingStreak = async (
|
||||||
const updateUniqueBettorsAndGiveCreatorBonus = async (
|
const updateUniqueBettorsAndGiveCreatorBonus = async (
|
||||||
contract: Contract,
|
contract: Contract,
|
||||||
eventId: string,
|
eventId: string,
|
||||||
bettorId: string
|
bettor: User
|
||||||
) => {
|
) => {
|
||||||
let previousUniqueBettorIds = contract.uniqueBettorIds
|
let previousUniqueBettorIds = contract.uniqueBettorIds
|
||||||
|
|
||||||
|
@ -147,13 +147,13 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isNewUniqueBettor = !previousUniqueBettorIds.includes(bettorId)
|
const isNewUniqueBettor = !previousUniqueBettorIds.includes(bettor.id)
|
||||||
|
|
||||||
const newUniqueBettorIds = uniq([...previousUniqueBettorIds, bettorId])
|
const newUniqueBettorIds = uniq([...previousUniqueBettorIds, bettor.id])
|
||||||
// Update contract unique bettors
|
// Update contract unique bettors
|
||||||
if (!contract.uniqueBettorIds || isNewUniqueBettor) {
|
if (!contract.uniqueBettorIds || isNewUniqueBettor) {
|
||||||
log(`Got ${previousUniqueBettorIds} unique bettors`)
|
log(`Got ${previousUniqueBettorIds} unique bettors`)
|
||||||
isNewUniqueBettor && log(`And a new unique bettor ${bettorId}`)
|
isNewUniqueBettor && log(`And a new unique bettor ${bettor.id}`)
|
||||||
await firestore.collection(`contracts`).doc(contract.id).update({
|
await firestore.collection(`contracts`).doc(contract.id).update({
|
||||||
uniqueBettorIds: newUniqueBettorIds,
|
uniqueBettorIds: newUniqueBettorIds,
|
||||||
uniqueBettorCount: newUniqueBettorIds.length,
|
uniqueBettorCount: newUniqueBettorIds.length,
|
||||||
|
@ -161,7 +161,7 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to give a bonus for the creator's bet
|
// No need to give a bonus for the creator's bet
|
||||||
if (!isNewUniqueBettor || bettorId == contract.creatorId) return
|
if (!isNewUniqueBettor || bettor.id == contract.creatorId) return
|
||||||
|
|
||||||
// Create combined txn for all new unique bettors
|
// Create combined txn for all new unique bettors
|
||||||
const bonusTxnDetails = {
|
const bonusTxnDetails = {
|
||||||
|
@ -192,18 +192,13 @@ const updateUniqueBettorsAndGiveCreatorBonus = async (
|
||||||
log(`No bonus for user: ${contract.creatorId} - reason:`, result.status)
|
log(`No bonus for user: ${contract.creatorId} - reason:`, result.status)
|
||||||
} else {
|
} else {
|
||||||
log(`Bonus txn for user: ${contract.creatorId} completed:`, result.txn?.id)
|
log(`Bonus txn for user: ${contract.creatorId} completed:`, result.txn?.id)
|
||||||
await createNotification(
|
await createUniqueBettorBonusNotification(
|
||||||
|
contract.creatorId,
|
||||||
|
bettor,
|
||||||
result.txn.id,
|
result.txn.id,
|
||||||
'bonus',
|
contract,
|
||||||
'created',
|
result.txn.amount,
|
||||||
fromUser,
|
eventId + '-unique-bettor-bonus'
|
||||||
eventId + '-bonus',
|
|
||||||
result.txn.amount + '',
|
|
||||||
{
|
|
||||||
contract,
|
|
||||||
slug: contract.slug,
|
|
||||||
title: contract.question,
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { ReactNode } from 'react'
|
import { MouseEventHandler, ReactNode } from 'react'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
|
||||||
export type SizeType = '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
export type SizeType = '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
|
||||||
|
@ -14,7 +14,7 @@ export type ColorType =
|
||||||
|
|
||||||
export function Button(props: {
|
export function Button(props: {
|
||||||
className?: string
|
className?: string
|
||||||
onClick?: () => void
|
onClick?: MouseEventHandler<any> | undefined
|
||||||
children?: ReactNode
|
children?: ReactNode
|
||||||
size?: SizeType
|
size?: SizeType
|
||||||
color?: ColorType
|
color?: ColorType
|
||||||
|
|
74
web/components/multi-user-transaction-link.tsx
Normal file
74
web/components/multi-user-transaction-link.tsx
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
import { useState } from 'react'
|
||||||
|
import { Row } from 'web/components/layout/row'
|
||||||
|
import { Modal } from 'web/components/layout/modal'
|
||||||
|
import { Col } from 'web/components/layout/col'
|
||||||
|
import { formatMoney } from 'common/util/format'
|
||||||
|
import { Avatar } from 'web/components/avatar'
|
||||||
|
import { UserLink } from 'web/components/user-link'
|
||||||
|
import { Button } from 'web/components/button'
|
||||||
|
|
||||||
|
export type MultiUserLinkInfo = {
|
||||||
|
name: string
|
||||||
|
username: string
|
||||||
|
avatarUrl: string | undefined
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export function MultiUserTransactionLink(props: {
|
||||||
|
userInfos: MultiUserLinkInfo[]
|
||||||
|
modalLabel: string
|
||||||
|
}) {
|
||||||
|
const { userInfos, modalLabel } = props
|
||||||
|
const [open, setOpen] = useState(false)
|
||||||
|
const maxShowCount = 5
|
||||||
|
return (
|
||||||
|
<Row>
|
||||||
|
<Button
|
||||||
|
size={'xs'}
|
||||||
|
color={'gray-white'}
|
||||||
|
className={'z-10 mr-1 gap-1 bg-transparent'}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
setOpen(true)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Row className={'gap-1'}>
|
||||||
|
{userInfos.map((userInfo, index) =>
|
||||||
|
index < maxShowCount ? (
|
||||||
|
<Row key={userInfo.username + 'shortened'}>
|
||||||
|
<Avatar
|
||||||
|
username={userInfo.username}
|
||||||
|
size={'sm'}
|
||||||
|
avatarUrl={userInfo.avatarUrl}
|
||||||
|
noLink={userInfos.length > 1}
|
||||||
|
/>
|
||||||
|
</Row>
|
||||||
|
) : (
|
||||||
|
<span>& {userInfos.length - maxShowCount} more</span>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</Row>
|
||||||
|
</Button>
|
||||||
|
<Modal open={open} setOpen={setOpen} size={'sm'}>
|
||||||
|
<Col className="items-start gap-4 rounded-md bg-white p-6">
|
||||||
|
<span className={'text-xl'}>{modalLabel}</span>
|
||||||
|
{userInfos.map((userInfo) => (
|
||||||
|
<Row
|
||||||
|
key={userInfo.username + 'list'}
|
||||||
|
className="w-full items-center gap-2"
|
||||||
|
>
|
||||||
|
<span className="text-primary min-w-[3.5rem]">
|
||||||
|
+{formatMoney(userInfo.amount)}
|
||||||
|
</span>
|
||||||
|
<Avatar
|
||||||
|
username={userInfo.username}
|
||||||
|
avatarUrl={userInfo.avatarUrl}
|
||||||
|
/>
|
||||||
|
<UserLink name={userInfo.name} username={userInfo.username} />
|
||||||
|
</Row>
|
||||||
|
))}
|
||||||
|
</Col>
|
||||||
|
</Modal>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,13 +1,7 @@
|
||||||
import { linkClass, SiteLink } from 'web/components/site-link'
|
import { SiteLink } from 'web/components/site-link'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { Row } from 'web/components/layout/row'
|
|
||||||
import { Modal } from 'web/components/layout/modal'
|
|
||||||
import { Col } from 'web/components/layout/col'
|
|
||||||
import { useState } from 'react'
|
|
||||||
import { Avatar } from 'web/components/avatar'
|
|
||||||
import { formatMoney } from 'common/util/format'
|
|
||||||
|
|
||||||
function shortenName(name: string) {
|
export function shortenName(name: string) {
|
||||||
const firstName = name.split(' ')[0]
|
const firstName = name.split(' ')[0]
|
||||||
const maxLength = 11
|
const maxLength = 11
|
||||||
const shortName =
|
const shortName =
|
||||||
|
@ -38,63 +32,3 @@ export function UserLink(props: {
|
||||||
</SiteLink>
|
</SiteLink>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MultiUserLinkInfo = {
|
|
||||||
name: string
|
|
||||||
username: string
|
|
||||||
avatarUrl: string | undefined
|
|
||||||
amountTipped: number
|
|
||||||
}
|
|
||||||
|
|
||||||
export function MultiUserTipLink(props: {
|
|
||||||
userInfos: MultiUserLinkInfo[]
|
|
||||||
className?: string
|
|
||||||
}) {
|
|
||||||
const { userInfos, className } = props
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
const maxShowCount = 2
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Row
|
|
||||||
className={clsx('mr-1 inline-flex gap-1', linkClass, className)}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
setOpen(true)
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{userInfos.map((userInfo, index) =>
|
|
||||||
index < maxShowCount ? (
|
|
||||||
<span key={userInfo.username + 'shortened'} className={linkClass}>
|
|
||||||
{shortenName(userInfo.name) +
|
|
||||||
(index < maxShowCount - 1 ? ', ' : '')}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span className={linkClass}>
|
|
||||||
& {userInfos.length - maxShowCount} more
|
|
||||||
</span>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</Row>
|
|
||||||
<Modal open={open} setOpen={setOpen} size={'sm'}>
|
|
||||||
<Col className="items-start gap-4 rounded-md bg-white p-6">
|
|
||||||
<span className={'text-xl'}>Who tipped you</span>
|
|
||||||
{userInfos.map((userInfo) => (
|
|
||||||
<Row
|
|
||||||
key={userInfo.username + 'list'}
|
|
||||||
className="w-full items-center gap-2"
|
|
||||||
>
|
|
||||||
<span className="text-primary min-w-[3.5rem]">
|
|
||||||
+{formatMoney(userInfo.amountTipped)}
|
|
||||||
</span>
|
|
||||||
<Avatar
|
|
||||||
username={userInfo.username}
|
|
||||||
avatarUrl={userInfo.avatarUrl}
|
|
||||||
/>
|
|
||||||
<UserLink name={userInfo.name} username={userInfo.username} />
|
|
||||||
</Row>
|
|
||||||
))}
|
|
||||||
</Col>
|
|
||||||
</Modal>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
|
@ -43,12 +43,13 @@ import { SiteLink } from 'web/components/site-link'
|
||||||
import { NotificationSettings } from 'web/components/NotificationSettings'
|
import { NotificationSettings } from 'web/components/NotificationSettings'
|
||||||
import { SEO } from 'web/components/SEO'
|
import { SEO } from 'web/components/SEO'
|
||||||
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
import { usePrivateUser, useUser } from 'web/hooks/use-user'
|
||||||
import {
|
import { UserLink } from 'web/components/user-link'
|
||||||
MultiUserTipLink,
|
|
||||||
MultiUserLinkInfo,
|
|
||||||
UserLink,
|
|
||||||
} from 'web/components/user-link'
|
|
||||||
import { LoadingIndicator } from 'web/components/loading-indicator'
|
import { LoadingIndicator } from 'web/components/loading-indicator'
|
||||||
|
import {
|
||||||
|
MultiUserLinkInfo,
|
||||||
|
MultiUserTransactionLink,
|
||||||
|
} from 'web/components/multi-user-transaction-link'
|
||||||
|
import { Col } from 'web/components/layout/col'
|
||||||
|
|
||||||
export const NOTIFICATIONS_PER_PAGE = 30
|
export const NOTIFICATIONS_PER_PAGE = 30
|
||||||
const HIGHLIGHT_CLASS = 'bg-indigo-50'
|
const HIGHLIGHT_CLASS = 'bg-indigo-50'
|
||||||
|
@ -212,7 +213,7 @@ function IncomeNotificationGroupItem(props: {
|
||||||
function combineNotificationsByAddingNumericSourceTexts(
|
function combineNotificationsByAddingNumericSourceTexts(
|
||||||
notifications: Notification[]
|
notifications: Notification[]
|
||||||
) {
|
) {
|
||||||
const newNotifications = []
|
const newNotifications: Notification[] = []
|
||||||
const groupedNotificationsBySourceType = groupBy(
|
const groupedNotificationsBySourceType = groupBy(
|
||||||
notifications,
|
notifications,
|
||||||
(n) => n.sourceType
|
(n) => n.sourceType
|
||||||
|
@ -228,10 +229,7 @@ function IncomeNotificationGroupItem(props: {
|
||||||
for (const sourceTitle in groupedNotificationsBySourceTitle) {
|
for (const sourceTitle in groupedNotificationsBySourceTitle) {
|
||||||
const notificationsForSourceTitle =
|
const notificationsForSourceTitle =
|
||||||
groupedNotificationsBySourceTitle[sourceTitle]
|
groupedNotificationsBySourceTitle[sourceTitle]
|
||||||
if (notificationsForSourceTitle.length === 1) {
|
|
||||||
newNotifications.push(notificationsForSourceTitle[0])
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
let sum = 0
|
let sum = 0
|
||||||
notificationsForSourceTitle.forEach(
|
notificationsForSourceTitle.forEach(
|
||||||
(notification) =>
|
(notification) =>
|
||||||
|
@ -251,7 +249,7 @@ function IncomeNotificationGroupItem(props: {
|
||||||
username: notification.sourceUserUsername,
|
username: notification.sourceUserUsername,
|
||||||
name: notification.sourceUserName,
|
name: notification.sourceUserName,
|
||||||
avatarUrl: notification.sourceUserAvatarUrl,
|
avatarUrl: notification.sourceUserAvatarUrl,
|
||||||
amountTipped: thisSum,
|
amount: thisSum,
|
||||||
} as MultiUserLinkInfo
|
} as MultiUserLinkInfo
|
||||||
}),
|
}),
|
||||||
(n) => n.username
|
(n) => n.username
|
||||||
|
@ -260,10 +258,8 @@ function IncomeNotificationGroupItem(props: {
|
||||||
const newNotification = {
|
const newNotification = {
|
||||||
...notificationsForSourceTitle[0],
|
...notificationsForSourceTitle[0],
|
||||||
sourceText: sum.toString(),
|
sourceText: sum.toString(),
|
||||||
sourceUserUsername:
|
sourceUserUsername: notificationsForSourceTitle[0].sourceUserUsername,
|
||||||
uniqueUsers.length > 1
|
data: JSON.stringify(uniqueUsers),
|
||||||
? JSON.stringify(uniqueUsers)
|
|
||||||
: notificationsForSourceTitle[0].sourceType,
|
|
||||||
}
|
}
|
||||||
newNotifications.push(newNotification)
|
newNotifications.push(newNotification)
|
||||||
}
|
}
|
||||||
|
@ -372,12 +368,15 @@ function IncomeNotificationItem(props: {
|
||||||
justSummary?: boolean
|
justSummary?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { notification, justSummary } = props
|
const { notification, justSummary } = props
|
||||||
const { sourceType, sourceUserName, sourceUserUsername, sourceText } =
|
const { sourceType, sourceUserUsername, sourceText, data } = notification
|
||||||
notification
|
|
||||||
const [highlighted] = useState(!notification.isSeen)
|
const [highlighted] = useState(!notification.isSeen)
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
const isMobile = (width && width < 768) || false
|
const isMobile = (width && width < 768) || false
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
const isTip = sourceType === 'tip' || sourceType === 'tip_and_like'
|
||||||
|
const isUniqueBettorBonus = sourceType === 'bonus'
|
||||||
|
const userLinks: MultiUserLinkInfo[] =
|
||||||
|
isTip || isUniqueBettorBonus ? JSON.parse(data ?? '{}') : []
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNotificationsAsSeen([notification])
|
setNotificationsAsSeen([notification])
|
||||||
|
@ -505,29 +504,26 @@ function IncomeNotificationItem(props: {
|
||||||
href={getIncomeSourceUrl() ?? ''}
|
href={getIncomeSourceUrl() ?? ''}
|
||||||
className={'absolute left-0 right-0 top-0 bottom-0 z-0'}
|
className={'absolute left-0 right-0 top-0 bottom-0 z-0'}
|
||||||
/>
|
/>
|
||||||
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
<Col className={'justify-start text-gray-500'}>
|
||||||
<div className={'line-clamp-2 flex max-w-xl shrink '}>
|
{(isTip || isUniqueBettorBonus) && (
|
||||||
<div className={'inline'}>
|
<MultiUserTransactionLink
|
||||||
<span className={'mr-1'}>{incomeNotificationLabel()}</span>
|
userInfos={userLinks}
|
||||||
</div>
|
modalLabel={isTip ? 'Who tipped you' : 'Unique bettors'}
|
||||||
<span>
|
/>
|
||||||
{(sourceType === 'tip' || sourceType === 'tip_and_like') &&
|
)}
|
||||||
(sourceUserUsername?.includes(',') ? (
|
<Row className={'line-clamp-2 flex max-w-xl'}>
|
||||||
<MultiUserTipLink
|
<span>{incomeNotificationLabel()}</span>
|
||||||
userInfos={JSON.parse(sourceUserUsername)}
|
<span className={'mx-1'}>
|
||||||
/>
|
{isTip &&
|
||||||
) : (
|
(userLinks.length > 1
|
||||||
<UserLink
|
? 'Multiple users'
|
||||||
name={sourceUserName || ''}
|
: userLinks.length > 0
|
||||||
username={sourceUserUsername || ''}
|
? userLinks[0].name
|
||||||
className={'mr-1 flex-shrink-0'}
|
: '')}
|
||||||
short={true}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
{reasonAndLink(false)}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
<span>{reasonAndLink(false)}</span>
|
||||||
</Row>
|
</Row>
|
||||||
|
</Col>
|
||||||
<div className={'border-b border-gray-300 pt-4'} />
|
<div className={'border-b border-gray-300 pt-4'} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user