Show tip notifications (#623)

* Show tip notifications

* Optimizing notifications for mobile

* Unused vars

* Move income reason logic to income notif

* Remove unnecessary icons

* Unused vars
This commit is contained in:
Ian Philips 2022-07-06 13:30:51 -06:00 committed by GitHub
parent 2d1e76eae8
commit de20ee9fb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 391 additions and 227 deletions

View File

@ -15,6 +15,7 @@ import { ENV_CONFIG } from './envs/constants'
export const FIXED_ANTE = ENV_CONFIG.fixedAnte ?? 100
export const HOUSE_LIQUIDITY_PROVIDER_ID = 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // @ManifoldMarkets' id
export const DEV_HOUSE_LIQUIDITY_PROVIDER_ID = '94YYTk1AFWfbWMpfYcvnnwI1veP2' // @ManifoldMarkets' id
export function getCpmmInitialLiquidity(
providerId: string,

View File

@ -61,3 +61,4 @@ export type notification_reason_types =
| 'user_joined_to_bet_on_your_market'
| 'unique_bettors_on_your_contract'
| 'on_group_you_are_member_of'
| 'tip_received'

View File

@ -66,9 +66,7 @@ export const createNotification = async (
sourceUserAvatarUrl: sourceUser.avatarUrl,
sourceText,
sourceContractCreatorUsername: sourceContract?.creatorUsername,
// TODO: move away from sourceContractTitle to sourceTitle
sourceContractTitle: sourceContract?.question,
// TODO: move away from sourceContractSlug to sourceSlug
sourceContractSlug: sourceContract?.slug,
sourceSlug: sourceSlug ? sourceSlug : sourceContract?.slug,
sourceTitle: sourceTitle ? sourceTitle : sourceContract?.question,
@ -278,13 +276,22 @@ export const createNotification = async (
}
const notifyOtherGroupMembersOfComment = async (
userToReasons: user_to_reason_texts,
userId: string
) => {
if (shouldGetNotification(userId, userToReasons))
userToReasons[userId] = {
reason: 'on_group_you_are_member_of',
isSeeOnHref: sourceSlug,
}
}
const notifyTippedUserOfNewTip = async (
userToReasonTexts: user_to_reason_texts,
userId: string
) => {
if (shouldGetNotification(userId, userToReasonTexts))
userToReasonTexts[userId] = {
reason: 'on_group_you_are_member_of',
isSeeOnHref: sourceSlug,
reason: 'tip_received',
}
}
@ -304,6 +311,7 @@ export const createNotification = async (
// The following functions need sourceContract to be defined.
if (!sourceContract) return userToReasonTexts
if (
sourceType === 'comment' ||
sourceType === 'answer' ||
@ -338,6 +346,8 @@ export const createNotification = async (
userToReasonTexts,
sourceContract.creatorId
)
} else if (sourceType === 'tip' && relatedUserId) {
await notifyTippedUserOfNewTip(userToReasonTexts, relatedUserId)
}
return userToReasonTexts
}

View File

@ -1,11 +1,14 @@
import { APIError, newEndpoint } from './api'
import { log } from './utils'
import { isProd, log } from './utils'
import * as admin from 'firebase-admin'
import { PrivateUser } from '../../common/lib/user'
import { uniq } from 'lodash'
import { Bet } from '../../common/lib/bet'
const firestore = admin.firestore()
import { HOUSE_LIQUIDITY_PROVIDER_ID } from '../../common/antes'
import {
DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
HOUSE_LIQUIDITY_PROVIDER_ID,
} from '../../common/antes'
import { runTxn, TxnData } from './transact'
import { createNotification } from './create-notification'
import { User } from '../../common/lib/user'
@ -38,9 +41,9 @@ export const getdailybonuses = newEndpoint({}, async (req, auth) => {
}
}
)
// TODO: switch to prod id
// const fromUserId = '94YYTk1AFWfbWMpfYcvnnwI1veP2' // dev manifold account
const fromUserId = HOUSE_LIQUIDITY_PROVIDER_ID // prod manifold account
const fromUserId = isProd()
? HOUSE_LIQUIDITY_PROVIDER_ID
: DEV_HOUSE_LIQUIDITY_PROVIDER_ID
const fromSnap = await firestore.doc(`users/${fromUserId}`).get()
if (!fromSnap.exists) throw new APIError(400, 'From user not found.')
const fromUser = fromSnap.data() as User

View File

@ -29,6 +29,7 @@ export * from './on-update-group'
export * from './on-create-group'
export * from './on-update-user'
export * from './on-create-comment-on-group'
export * from './on-create-txn'
// v2
export * from './health'

View File

@ -0,0 +1,68 @@
import * as functions from 'firebase-functions'
import { Txn } from 'common/txn'
import { getContract, getUser, log } from './utils'
import { createNotification } from './create-notification'
import * as admin from 'firebase-admin'
import { Comment } from 'common/comment'
const firestore = admin.firestore()
export const onCreateTxn = functions.firestore
.document('txns/{txnId}')
.onCreate(async (change, context) => {
const txn = change.data() as Txn
const { eventId } = context
if (txn.category === 'TIP') {
await handleTipTxn(txn, eventId)
}
})
async function handleTipTxn(txn: Txn, eventId: string) {
// get user sending and receiving tip
const [sender, receiver] = await Promise.all([
getUser(txn.fromId),
getUser(txn.toId),
])
if (!sender || !receiver) {
log('Could not find corresponding users')
return
}
if (!txn.data?.contractId || !txn.data?.commentId) {
log('No contractId or comment id in tip txn.data')
return
}
const contract = await getContract(txn.data.contractId)
if (!contract) {
log('Could not find contract')
return
}
const commentSnapshot = await firestore
.collection('contracts')
.doc(contract.id)
.collection('comments')
.doc(txn.data.commentId)
.get()
if (!commentSnapshot.exists) {
log('Could not find comment')
return
}
const comment = commentSnapshot.data() as Comment
await createNotification(
txn.id,
'tip',
'created',
sender,
eventId,
txn.amount.toString(),
contract,
'comment',
receiver.id,
txn.data?.commentId,
comment.text
)
}

View File

@ -53,7 +53,7 @@ export function EmptyAvatar(props: { size?: number; multi?: boolean }) {
return (
<div
className={`flex h-${size} w-${size} items-center justify-center rounded-full bg-gray-200`}
className={`flex flex-shrink-0 h-${size} w-${size} items-center justify-center rounded-full bg-gray-200`}
>
<Icon className={`h-${insize} w-${insize} text-gray-500`} aria-hidden />
</div>

View File

@ -224,7 +224,7 @@ export function FeedComment(props: {
return (
<Row
className={clsx(
'flex space-x-1.5 transition-all duration-1000 sm:space-x-3',
'flex space-x-1.5 sm:space-x-3',
highlighted ? `-m-1 rounded bg-indigo-500/[0.2] p-2` : ''
)}
>

View File

@ -39,17 +39,19 @@ export function groupNotifications(notifications: Notification[]) {
)
Object.keys(notificationGroupsByDay).forEach((day) => {
const notificationsGroupedByDay = notificationGroupsByDay[day]
const bonusNotifications = notificationsGroupedByDay.filter(
(notification) => notification.sourceType === 'bonus'
const incomeNotifications = notificationsGroupedByDay.filter(
(notification) =>
notification.sourceType === 'bonus' || notification.sourceType === 'tip'
)
const normalNotificationsGroupedByDay = notificationsGroupedByDay.filter(
(notification) => notification.sourceType !== 'bonus'
(notification) =>
notification.sourceType !== 'bonus' && notification.sourceType !== 'tip'
)
if (bonusNotifications.length > 0) {
if (incomeNotifications.length > 0) {
notificationGroups = notificationGroups.concat({
notifications: bonusNotifications,
notifications: incomeNotifications,
groupedById: 'income' + day,
isSeen: bonusNotifications[0].isSeen,
isSeen: incomeNotifications[0].isSeen,
timePeriod: day,
type: 'income',
})

View File

@ -1,7 +1,7 @@
import { Tabs } from 'web/components/layout/tabs'
import { useUser } from 'web/hooks/use-user'
import React, { useEffect, useState } from 'react'
import { Notification } from 'common/notification'
import { Notification, notification_source_types } from 'common/notification'
import { Avatar, EmptyAvatar } from 'web/components/avatar'
import { Row } from 'web/components/layout/row'
import { Page } from 'web/components/page'
@ -34,10 +34,10 @@ import toast from 'react-hot-toast'
import { formatMoney } from 'common/util/format'
import { groupPath } from 'web/lib/firebase/groups'
import { UNIQUE_BETTOR_BONUS_AMOUNT } from 'common/numeric-constants'
import { groupBy } from 'lodash'
import { groupBy, sum, uniq } from 'lodash'
export const NOTIFICATIONS_PER_PAGE = 30
export const HIGHLIGHT_DURATION = 30 * 1000
const MULTIPLE_USERS_KEY = 'multipleUsers'
export default function Notifications() {
const user = useUser()
@ -187,16 +187,12 @@ function IncomeNotificationGroupItem(props: {
const { notificationGroup, className } = props
const { notifications } = notificationGroup
const numSummaryLines = 3
const [expanded, setExpanded] = useState(false)
const [highlighted, setHighlighted] = useState(false)
const [highlighted, setHighlighted] = useState(
notifications.some((n) => !n.isSeen)
)
useEffect(() => {
if (notifications.some((n) => !n.isSeen)) {
setHighlighted(true)
setTimeout(() => {
setHighlighted(false)
}, HIGHLIGHT_DURATION)
}
setNotificationsAsSeen(notifications)
}, [notifications])
@ -204,23 +200,23 @@ function IncomeNotificationGroupItem(props: {
if (expanded) setHighlighted(false)
}, [expanded])
const totalIncome = notifications.reduce(
(acc, notification) =>
acc +
(notification.sourceType &&
notification.sourceText &&
notification.sourceType === 'bonus'
? parseInt(notification.sourceText)
: 0),
0
const totalIncome = sum(
notifications.map((notification) =>
notification.sourceText ? parseInt(notification.sourceText) : 0
)
// loop through the contracts and combine the notification items into one
function combineNotificationsByAddingSourceTextsAndReturningTheRest(
)
// Loop through the contracts and combine the notification items into one
function combineNotificationsByAddingNumericSourceTexts(
notifications: Notification[]
) {
const newNotifications = []
const groupedNotificationsByContractId = groupBy(
const groupedNotificationsBySourceType = groupBy(
notifications,
(n) => n.sourceType
)
for (const sourceType in groupedNotificationsBySourceType) {
const groupedNotificationsByContractId = groupBy(
groupedNotificationsBySourceType[sourceType],
(notification) => {
return notification.sourceContractId
}
@ -228,27 +224,38 @@ function IncomeNotificationGroupItem(props: {
for (const contractId in groupedNotificationsByContractId) {
const notificationsForContractId =
groupedNotificationsByContractId[contractId]
if (notificationsForContractId.length === 1) {
newNotifications.push(notificationsForContractId[0])
continue
}
let sum = 0
notificationsForContractId.forEach(
(notification) =>
notification.sourceText &&
(sum = parseInt(notification.sourceText) + sum)
)
const uniqueUsers = uniq(
notificationsForContractId.map((notification) => {
return notification.sourceUserUsername
})
)
const newNotification =
notificationsForContractId.length === 1
? notificationsForContractId[0]
: {
const newNotification = {
...notificationsForContractId[0],
sourceText: sum.toString(),
sourceUserUsername:
uniqueUsers.length > 1
? MULTIPLE_USERS_KEY
: notificationsForContractId[0].sourceType,
}
newNotifications.push(newNotification)
}
}
return newNotifications
}
const combinedNotifs =
combineNotificationsByAddingSourceTextsAndReturningTheRest(notifications)
combineNotificationsByAddingNumericSourceTexts(notifications)
return (
<div
@ -286,53 +293,23 @@ function IncomeNotificationGroupItem(props: {
<div>
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
{' '}
<div className={'line-clamp-4 mt-1 ml-1 gap-1 whitespace-pre-line'}>
<div
className={clsx(
'mt-1 ml-1 gap-1 whitespace-pre-line',
!expanded ? 'line-clamp-4' : ''
)}
>
{!expanded ? (
<>
{combinedNotifs
.slice(0, numSummaryLines)
.map((notification) => {
return (
<Row
className={
'items-center text-sm text-gray-500 sm:justify-start'
}
key={notification.id}
>
<div
className={
'line-clamp-1 flex-1 overflow-hidden sm:flex'
}
>
<div className={'flex pl-1 sm:pl-0'}>
<div
className={
'inline-flex overflow-hidden text-ellipsis pl-1'
}
>
<div className={'mr-1 text-black'}>
<NotificationTextLabel
contract={null}
defaultText={notification.sourceText ?? ''}
className={'line-clamp-1'}
.map((notification) => (
<IncomeNotificationItem
notification={notification}
justSummary={true}
key={notification.id}
/>
</div>
<span className={'flex-shrink-0'}>
{getReasonForShowingNotification(
notification,
true
)}
{` on`}
<NotificationLink notification={notification} />
</span>
</div>
</div>
</div>
</Row>
)
})}
))}
<div className={'text-sm text-gray-500 hover:underline '}>
{combinedNotifs.length - numSummaryLines > 0
? 'And ' +
@ -344,7 +321,7 @@ function IncomeNotificationGroupItem(props: {
) : (
<>
{combinedNotifs.map((notification) => (
<NotificationItem
<IncomeNotificationItem
notification={notification}
key={notification.id}
justSummary={false}
@ -361,28 +338,130 @@ function IncomeNotificationGroupItem(props: {
)
}
function IncomeNotificationItem(props: {
notification: Notification
justSummary?: boolean
}) {
const { notification, justSummary } = props
const {
sourceType,
sourceUserName,
reason,
sourceUserUsername,
createdTime,
} = notification
const [highlighted] = useState(!notification.isSeen)
useEffect(() => {
setNotificationsAsSeen([notification])
}, [notification])
function getReasonForShowingIncomeNotification(simple: boolean) {
const { sourceText } = notification
let reasonText = ''
if (sourceType === 'bonus' && sourceText) {
reasonText = !simple
? `bonus for ${
parseInt(sourceText) / UNIQUE_BETTOR_BONUS_AMOUNT
} unique bettors`
: ' bonus for unique bettors on'
} else if (sourceType === 'tip') {
reasonText = !simple ? `tipped you` : `in tips on`
}
return <span className={'flex-shrink-0'}>{reasonText}</span>
}
if (justSummary) {
return (
<Row className={'items-center text-sm text-gray-500 sm:justify-start'}>
<div className={'line-clamp-1 flex-1 overflow-hidden sm:flex'}>
<div className={'flex pl-1 sm:pl-0'}>
<div className={'inline-flex overflow-hidden text-ellipsis pl-1'}>
<div className={'mr-1 text-black'}>
<NotificationTextLabel
contract={null}
defaultText={notification.sourceText ?? ''}
className={'line-clamp-1'}
notification={notification}
justSummary={true}
/>
</div>
<span className={'flex truncate'}>
{getReasonForShowingIncomeNotification(true)}
<NotificationLink notification={notification} />
</span>
</div>
</div>
</div>
</Row>
)
}
return (
<div
className={clsx(
'bg-white px-2 pt-6 text-sm sm:px-4',
highlighted && 'bg-indigo-200 hover:bg-indigo-100'
)}
>
<a href={getSourceUrl(notification)}>
<Row className={'items-center text-gray-500 sm:justify-start'}>
<div className={'flex max-w-xl shrink '}>
{sourceType && reason && (
<div className={'inline'}>
<span className={'mr-1'}>
<NotificationTextLabel
contract={null}
defaultText={notification.sourceText ?? ''}
notification={notification}
/>
</span>
{sourceType != 'bonus' &&
(sourceUserUsername === MULTIPLE_USERS_KEY ? (
<span className={'mr-1 truncate'}>Multiple users</span>
) : (
<UserLink
name={sourceUserName || ''}
username={sourceUserUsername || ''}
className={'mr-1 flex-shrink-0'}
justFirstName={true}
/>
))}
</div>
)}
{getReasonForShowingIncomeNotification(false)}
<span className={'ml-1 flex hidden sm:inline-block'}>
on
<NotificationLink notification={notification} />
</span>
<RelativeTimestamp time={createdTime} />
</div>
</Row>
<span className={'flex truncate text-gray-500 sm:hidden'}>
on
<NotificationLink notification={notification} />
</span>
<div className={'mt-4 border-b border-gray-300'} />
</a>
</div>
)
}
function NotificationGroupItem(props: {
notificationGroup: NotificationGroup
className?: string
}) {
const { notificationGroup, className } = props
const { notifications } = notificationGroup
const {
sourceContractTitle,
sourceContractSlug,
sourceContractCreatorUsername,
} = notifications[0]
const { sourceContractTitle } = notifications[0]
const numSummaryLines = 3
const [expanded, setExpanded] = useState(false)
const [highlighted, setHighlighted] = useState(false)
const [highlighted, setHighlighted] = useState(
notifications.some((n) => !n.isSeen)
)
useEffect(() => {
if (notifications.some((n) => !n.isSeen)) {
setHighlighted(true)
setTimeout(() => {
setHighlighted(false)
}, HIGHLIGHT_DURATION)
}
setNotificationsAsSeen(notifications)
}, [notifications])
@ -408,27 +487,18 @@ function NotificationGroupItem(props: {
)}
<Row className={'items-center text-gray-500 sm:justify-start'}>
<EmptyAvatar multi />
<div className={'flex-1 overflow-hidden pl-2 sm:flex'}>
<div className={'flex truncate pl-2'}>
<div
onClick={() => setExpanded(!expanded)}
className={'line-clamp-1 cursor-pointer pl-1 sm:pl-0'}
className={' flex cursor-pointer truncate pl-1 sm:pl-0'}
>
{sourceContractTitle ? (
<span>
{'Activity on '}
<a
href={
sourceContractCreatorUsername
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
: ''
}
className={
'font-bold hover:underline hover:decoration-indigo-400 hover:decoration-2'
}
>
{sourceContractTitle}
</a>
<>
<span className={'flex-shrink-0'}>{'Activity on '}</span>
<span className={'truncate'}>
<NotificationLink notification={notifications[0]} />
</span>
</>
) : (
'Other activity'
)}
@ -439,7 +509,13 @@ function NotificationGroupItem(props: {
<div>
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
{' '}
<div className={'line-clamp-4 mt-1 ml-1 gap-1 whitespace-pre-line'}>
<div
className={clsx(
'mt-1 ml-1 gap-1 whitespace-pre-line',
!expanded ? 'line-clamp-4' : ''
)}
>
{' '}
{!expanded ? (
<>
{notifications.slice(0, numSummaryLines).map((notification) => {
@ -466,6 +542,7 @@ function NotificationGroupItem(props: {
notification={notification}
key={notification.id}
justSummary={false}
hideTitle={true}
/>
))}
</>
@ -695,7 +772,7 @@ function NotificationLink(props: { notification: Notification }) {
: ''
}
className={
'ml-1 font-bold hover:underline hover:decoration-indigo-400 hover:decoration-2'
'ml-1 inline max-w-xs truncate font-bold text-gray-500 hover:underline hover:decoration-indigo-400 hover:decoration-2 sm:max-w-sm'
}
>
{sourceContractTitle || sourceTitle}
@ -703,11 +780,54 @@ function NotificationLink(props: { notification: Notification }) {
)
}
function getSourceUrl(notification: Notification) {
const {
sourceType,
sourceId,
sourceUserUsername,
sourceContractCreatorUsername,
sourceContractSlug,
sourceSlug,
} = notification
if (sourceType === 'follow') return `/${sourceUserUsername}`
if (sourceType === 'group' && sourceSlug) return `${groupPath(sourceSlug)}`
if (
sourceContractCreatorUsername &&
sourceContractSlug &&
sourceType === 'user'
)
return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
if (sourceType === 'tip')
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${sourceSlug}`
if (sourceContractCreatorUsername && sourceContractSlug)
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${getSourceIdForLinkComponent(
sourceId ?? '',
sourceType
)}`
}
function getSourceIdForLinkComponent(
sourceId: string,
sourceType?: notification_source_types
) {
switch (sourceType) {
case 'answer':
return `answer-${sourceId}`
case 'comment':
return sourceId
case 'contract':
return ''
default:
return sourceId
}
}
function NotificationItem(props: {
notification: Notification
justSummary?: boolean
hideTitle?: boolean
}) {
const { notification, justSummary } = props
const { notification, justSummary, hideTitle } = props
const {
sourceType,
sourceId,
@ -721,7 +841,6 @@ function NotificationItem(props: {
sourceText,
sourceContractCreatorUsername,
sourceContractSlug,
sourceSlug,
} = notification
const [defaultNotificationText, setDefaultNotificationText] =
@ -736,48 +855,12 @@ function NotificationItem(props: {
}
}, [reasonText, sourceText])
const [highlighted, setHighlighted] = useState(false)
useEffect(() => {
if (!notification.isSeen) {
setHighlighted(true)
setTimeout(() => {
setHighlighted(false)
}, HIGHLIGHT_DURATION)
}
}, [notification.isSeen])
const [highlighted] = useState(!notification.isSeen)
useEffect(() => {
setNotificationsAsSeen([notification])
}, [notification])
function getSourceUrl() {
if (sourceType === 'follow') return `/${sourceUserUsername}`
if (sourceType === 'group' && sourceSlug) return `${groupPath(sourceSlug)}`
if (
sourceContractCreatorUsername &&
sourceContractSlug &&
sourceType === 'user'
)
return `/${sourceContractCreatorUsername}/${sourceContractSlug}`
if (sourceContractCreatorUsername && sourceContractSlug)
return `/${sourceContractCreatorUsername}/${sourceContractSlug}#${getSourceIdForLinkComponent(
sourceId ?? ''
)}`
}
function getSourceIdForLinkComponent(sourceId: string) {
switch (sourceType) {
case 'answer':
return `answer-${sourceId}`
case 'comment':
return sourceId
case 'contract':
return ''
default:
return sourceId
}
}
if (justSummary) {
return (
<Row className={'items-center text-sm text-gray-500 sm:justify-start'}>
@ -793,10 +876,7 @@ function NotificationItem(props: {
<span className={'flex-shrink-0'}>
{sourceType &&
reason &&
getReasonForShowingNotification(notification, true).replace(
' on',
''
)}
getReasonForShowingNotification(notification, true, true)}
</span>
<div className={'ml-1 text-black'}>
<NotificationTextLabel
@ -821,25 +901,21 @@ function NotificationItem(props: {
highlighted && 'bg-indigo-200 hover:bg-indigo-100'
)}
>
<a href={getSourceUrl()}>
<a href={getSourceUrl(notification)}>
<Row className={'items-center text-gray-500 sm:justify-start'}>
{sourceType != 'bonus' ? (
<Avatar
avatarUrl={sourceUserAvatarUrl}
size={'sm'}
className={'mr-2'}
username={sourceUserName}
/>
) : (
<TrendingUpIcon className={'text-primary h-7 w-7'} />
)}
<div className={'flex-1 overflow-hidden sm:flex'}>
<div
className={
'flex max-w-xl shrink overflow-hidden text-ellipsis pl-1 sm:pl-0'
}
>
{sourceType != 'bonus' && sourceUpdateType != 'closed' && (
{sourceUpdateType != 'closed' && (
<UserLink
name={sourceUserName || ''}
username={sourceUserUsername || ''}
@ -847,16 +923,19 @@ function NotificationItem(props: {
justFirstName={true}
/>
)}
<div className={'inline-flex overflow-hidden text-ellipsis pl-1'}>
{sourceType && reason && (
<div className={'inline truncate'}>
{getReasonForShowingNotification(notification, false)}
<div className={'inline flex truncate'}>
<span className={'ml-1 flex-shrink-0'}>
{getReasonForShowingNotification(notification, false, true)}
</span>
{!hideTitle && (
<NotificationLink notification={notification} />
</div>
)}
</div>
</div>
{sourceId && sourceContractSlug && sourceContractCreatorUsername ? (
)}
{sourceId &&
sourceContractSlug &&
sourceContractCreatorUsername ? (
<CopyLinkDateTimeComponent
prefix={sourceContractCreatorUsername}
slug={sourceContractSlug}
@ -868,6 +947,7 @@ function NotificationItem(props: {
<RelativeTimestamp time={createdTime} />
)}
</div>
</div>
</Row>
<div className={'mt-1 ml-1 md:text-base'}>
<NotificationTextLabel
@ -938,9 +1018,11 @@ function NotificationTextLabel(props: {
return (
<span className="text-blue-400">{formatMoney(parseInt(sourceText))}</span>
)
} else if (sourceType === 'bonus' && sourceText) {
} else if ((sourceType === 'bonus' || sourceType === 'tip') && sourceText) {
return (
<span className="text-primary">{formatMoney(parseInt(sourceText))}</span>
<span className="text-primary">
{'+' + formatMoney(parseInt(sourceText))}
</span>
)
}
// return default text
@ -953,19 +1035,19 @@ function NotificationTextLabel(props: {
function getReasonForShowingNotification(
notification: Notification,
simple?: boolean
simple?: boolean,
replaceOn?: boolean
) {
const { sourceType, sourceUpdateType, sourceText, reason, sourceSlug } =
notification
const { sourceType, sourceUpdateType, reason, sourceSlug } = notification
let reasonText: string
switch (sourceType) {
case 'comment':
if (reason === 'reply_to_users_answer')
reasonText = !simple ? 'replied to your answer on' : 'replied'
reasonText = !simple ? 'replied to you on' : 'replied'
else if (reason === 'tagged_user')
reasonText = !simple ? 'tagged you in a comment on' : 'tagged you'
reasonText = !simple ? 'tagged you on' : 'tagged you'
else if (reason === 'reply_to_users_comment')
reasonText = !simple ? 'replied to your comment on' : 'replied'
reasonText = !simple ? 'replied to you on' : 'replied'
else if (reason === 'on_users_contract')
reasonText = !simple ? `commented on your question` : 'commented'
else if (reason === 'on_contract_with_users_comment')
@ -973,7 +1055,7 @@ function getReasonForShowingNotification(
else if (reason === 'on_contract_with_users_answer')
reasonText = `commented on`
else if (reason === 'on_contract_with_users_shares_in')
reasonText = `commented`
reasonText = `commented on`
else reasonText = `commented on`
break
case 'contract':
@ -1008,17 +1090,13 @@ function getReasonForShowingNotification(
else if (sourceSlug) reasonText = 'joined because you shared'
else reasonText = 'joined because of you'
break
case 'bonus':
if (reason === 'unique_bettors_on_your_contract' && sourceText)
reasonText = !simple
? `You had ${
parseInt(sourceText) / UNIQUE_BETTOR_BONUS_AMOUNT
} unique bettors on`
: ' for unique bettors'
else reasonText = 'You earned your daily manna'
break
default:
reasonText = ''
}
return reasonText
return (
<span className={'flex-shrink-0'}>
{replaceOn ? reasonText.replace(' on', '') : reasonText}
</span>
)
}