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:
parent
2d1e76eae8
commit
de20ee9fb9
|
@ -15,6 +15,7 @@ import { ENV_CONFIG } from './envs/constants'
|
||||||
export const FIXED_ANTE = ENV_CONFIG.fixedAnte ?? 100
|
export const FIXED_ANTE = ENV_CONFIG.fixedAnte ?? 100
|
||||||
|
|
||||||
export const HOUSE_LIQUIDITY_PROVIDER_ID = 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // @ManifoldMarkets' id
|
export const HOUSE_LIQUIDITY_PROVIDER_ID = 'IPTOzEqrpkWmEzh6hwvAyY9PqFb2' // @ManifoldMarkets' id
|
||||||
|
export const DEV_HOUSE_LIQUIDITY_PROVIDER_ID = '94YYTk1AFWfbWMpfYcvnnwI1veP2' // @ManifoldMarkets' id
|
||||||
|
|
||||||
export function getCpmmInitialLiquidity(
|
export function getCpmmInitialLiquidity(
|
||||||
providerId: string,
|
providerId: string,
|
||||||
|
|
|
@ -61,3 +61,4 @@ export type notification_reason_types =
|
||||||
| 'user_joined_to_bet_on_your_market'
|
| 'user_joined_to_bet_on_your_market'
|
||||||
| 'unique_bettors_on_your_contract'
|
| 'unique_bettors_on_your_contract'
|
||||||
| 'on_group_you_are_member_of'
|
| 'on_group_you_are_member_of'
|
||||||
|
| 'tip_received'
|
||||||
|
|
|
@ -66,9 +66,7 @@ export const createNotification = async (
|
||||||
sourceUserAvatarUrl: sourceUser.avatarUrl,
|
sourceUserAvatarUrl: sourceUser.avatarUrl,
|
||||||
sourceText,
|
sourceText,
|
||||||
sourceContractCreatorUsername: sourceContract?.creatorUsername,
|
sourceContractCreatorUsername: sourceContract?.creatorUsername,
|
||||||
// TODO: move away from sourceContractTitle to sourceTitle
|
|
||||||
sourceContractTitle: sourceContract?.question,
|
sourceContractTitle: sourceContract?.question,
|
||||||
// TODO: move away from sourceContractSlug to sourceSlug
|
|
||||||
sourceContractSlug: sourceContract?.slug,
|
sourceContractSlug: sourceContract?.slug,
|
||||||
sourceSlug: sourceSlug ? sourceSlug : sourceContract?.slug,
|
sourceSlug: sourceSlug ? sourceSlug : sourceContract?.slug,
|
||||||
sourceTitle: sourceTitle ? sourceTitle : sourceContract?.question,
|
sourceTitle: sourceTitle ? sourceTitle : sourceContract?.question,
|
||||||
|
@ -278,13 +276,22 @@ export const createNotification = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
const notifyOtherGroupMembersOfComment = 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,
|
userToReasonTexts: user_to_reason_texts,
|
||||||
userId: string
|
userId: string
|
||||||
) => {
|
) => {
|
||||||
if (shouldGetNotification(userId, userToReasonTexts))
|
if (shouldGetNotification(userId, userToReasonTexts))
|
||||||
userToReasonTexts[userId] = {
|
userToReasonTexts[userId] = {
|
||||||
reason: 'on_group_you_are_member_of',
|
reason: 'tip_received',
|
||||||
isSeeOnHref: sourceSlug,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,6 +311,7 @@ export const createNotification = async (
|
||||||
|
|
||||||
// The following functions need sourceContract to be defined.
|
// The following functions need sourceContract to be defined.
|
||||||
if (!sourceContract) return userToReasonTexts
|
if (!sourceContract) return userToReasonTexts
|
||||||
|
|
||||||
if (
|
if (
|
||||||
sourceType === 'comment' ||
|
sourceType === 'comment' ||
|
||||||
sourceType === 'answer' ||
|
sourceType === 'answer' ||
|
||||||
|
@ -338,6 +346,8 @@ export const createNotification = async (
|
||||||
userToReasonTexts,
|
userToReasonTexts,
|
||||||
sourceContract.creatorId
|
sourceContract.creatorId
|
||||||
)
|
)
|
||||||
|
} else if (sourceType === 'tip' && relatedUserId) {
|
||||||
|
await notifyTippedUserOfNewTip(userToReasonTexts, relatedUserId)
|
||||||
}
|
}
|
||||||
return userToReasonTexts
|
return userToReasonTexts
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,14 @@
|
||||||
import { APIError, newEndpoint } from './api'
|
import { APIError, newEndpoint } from './api'
|
||||||
import { log } from './utils'
|
import { isProd, log } from './utils'
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import { PrivateUser } from '../../common/lib/user'
|
import { PrivateUser } from '../../common/lib/user'
|
||||||
import { uniq } from 'lodash'
|
import { uniq } from 'lodash'
|
||||||
import { Bet } from '../../common/lib/bet'
|
import { Bet } from '../../common/lib/bet'
|
||||||
const firestore = admin.firestore()
|
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 { runTxn, TxnData } from './transact'
|
||||||
import { createNotification } from './create-notification'
|
import { createNotification } from './create-notification'
|
||||||
import { User } from '../../common/lib/user'
|
import { User } from '../../common/lib/user'
|
||||||
|
@ -38,9 +41,9 @@ export const getdailybonuses = newEndpoint({}, async (req, auth) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// TODO: switch to prod id
|
const fromUserId = isProd()
|
||||||
// const fromUserId = '94YYTk1AFWfbWMpfYcvnnwI1veP2' // dev manifold account
|
? HOUSE_LIQUIDITY_PROVIDER_ID
|
||||||
const fromUserId = HOUSE_LIQUIDITY_PROVIDER_ID // prod manifold account
|
: DEV_HOUSE_LIQUIDITY_PROVIDER_ID
|
||||||
const fromSnap = await firestore.doc(`users/${fromUserId}`).get()
|
const fromSnap = await firestore.doc(`users/${fromUserId}`).get()
|
||||||
if (!fromSnap.exists) throw new APIError(400, 'From user not found.')
|
if (!fromSnap.exists) throw new APIError(400, 'From user not found.')
|
||||||
const fromUser = fromSnap.data() as User
|
const fromUser = fromSnap.data() as User
|
||||||
|
|
|
@ -29,6 +29,7 @@ export * from './on-update-group'
|
||||||
export * from './on-create-group'
|
export * from './on-create-group'
|
||||||
export * from './on-update-user'
|
export * from './on-update-user'
|
||||||
export * from './on-create-comment-on-group'
|
export * from './on-create-comment-on-group'
|
||||||
|
export * from './on-create-txn'
|
||||||
|
|
||||||
// v2
|
// v2
|
||||||
export * from './health'
|
export * from './health'
|
||||||
|
|
68
functions/src/on-create-txn.ts
Normal file
68
functions/src/on-create-txn.ts
Normal 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
|
||||||
|
)
|
||||||
|
}
|
|
@ -53,7 +53,7 @@ export function EmptyAvatar(props: { size?: number; multi?: boolean }) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<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 />
|
<Icon className={`h-${insize} w-${insize} text-gray-500`} aria-hidden />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -224,7 +224,7 @@ export function FeedComment(props: {
|
||||||
return (
|
return (
|
||||||
<Row
|
<Row
|
||||||
className={clsx(
|
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` : ''
|
highlighted ? `-m-1 rounded bg-indigo-500/[0.2] p-2` : ''
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -39,17 +39,19 @@ export function groupNotifications(notifications: Notification[]) {
|
||||||
)
|
)
|
||||||
Object.keys(notificationGroupsByDay).forEach((day) => {
|
Object.keys(notificationGroupsByDay).forEach((day) => {
|
||||||
const notificationsGroupedByDay = notificationGroupsByDay[day]
|
const notificationsGroupedByDay = notificationGroupsByDay[day]
|
||||||
const bonusNotifications = notificationsGroupedByDay.filter(
|
const incomeNotifications = notificationsGroupedByDay.filter(
|
||||||
(notification) => notification.sourceType === 'bonus'
|
(notification) =>
|
||||||
|
notification.sourceType === 'bonus' || notification.sourceType === 'tip'
|
||||||
)
|
)
|
||||||
const normalNotificationsGroupedByDay = notificationsGroupedByDay.filter(
|
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({
|
notificationGroups = notificationGroups.concat({
|
||||||
notifications: bonusNotifications,
|
notifications: incomeNotifications,
|
||||||
groupedById: 'income' + day,
|
groupedById: 'income' + day,
|
||||||
isSeen: bonusNotifications[0].isSeen,
|
isSeen: incomeNotifications[0].isSeen,
|
||||||
timePeriod: day,
|
timePeriod: day,
|
||||||
type: 'income',
|
type: 'income',
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Tabs } from 'web/components/layout/tabs'
|
import { Tabs } from 'web/components/layout/tabs'
|
||||||
import { useUser } from 'web/hooks/use-user'
|
import { useUser } from 'web/hooks/use-user'
|
||||||
import React, { useEffect, useState } from 'react'
|
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 { Avatar, EmptyAvatar } from 'web/components/avatar'
|
||||||
import { Row } from 'web/components/layout/row'
|
import { Row } from 'web/components/layout/row'
|
||||||
import { Page } from 'web/components/page'
|
import { Page } from 'web/components/page'
|
||||||
|
@ -34,10 +34,10 @@ import toast from 'react-hot-toast'
|
||||||
import { formatMoney } from 'common/util/format'
|
import { formatMoney } from 'common/util/format'
|
||||||
import { groupPath } from 'web/lib/firebase/groups'
|
import { groupPath } from 'web/lib/firebase/groups'
|
||||||
import { UNIQUE_BETTOR_BONUS_AMOUNT } from 'common/numeric-constants'
|
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 NOTIFICATIONS_PER_PAGE = 30
|
||||||
export const HIGHLIGHT_DURATION = 30 * 1000
|
const MULTIPLE_USERS_KEY = 'multipleUsers'
|
||||||
|
|
||||||
export default function Notifications() {
|
export default function Notifications() {
|
||||||
const user = useUser()
|
const user = useUser()
|
||||||
|
@ -187,16 +187,12 @@ function IncomeNotificationGroupItem(props: {
|
||||||
const { notificationGroup, className } = props
|
const { notificationGroup, className } = props
|
||||||
const { notifications } = notificationGroup
|
const { notifications } = notificationGroup
|
||||||
const numSummaryLines = 3
|
const numSummaryLines = 3
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false)
|
||||||
const [highlighted, setHighlighted] = useState(false)
|
const [highlighted, setHighlighted] = useState(
|
||||||
|
notifications.some((n) => !n.isSeen)
|
||||||
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (notifications.some((n) => !n.isSeen)) {
|
|
||||||
setHighlighted(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
setHighlighted(false)
|
|
||||||
}, HIGHLIGHT_DURATION)
|
|
||||||
}
|
|
||||||
setNotificationsAsSeen(notifications)
|
setNotificationsAsSeen(notifications)
|
||||||
}, [notifications])
|
}, [notifications])
|
||||||
|
|
||||||
|
@ -204,51 +200,62 @@ function IncomeNotificationGroupItem(props: {
|
||||||
if (expanded) setHighlighted(false)
|
if (expanded) setHighlighted(false)
|
||||||
}, [expanded])
|
}, [expanded])
|
||||||
|
|
||||||
const totalIncome = notifications.reduce(
|
const totalIncome = sum(
|
||||||
(acc, notification) =>
|
notifications.map((notification) =>
|
||||||
acc +
|
notification.sourceText ? parseInt(notification.sourceText) : 0
|
||||||
(notification.sourceType &&
|
)
|
||||||
notification.sourceText &&
|
|
||||||
notification.sourceType === 'bonus'
|
|
||||||
? parseInt(notification.sourceText)
|
|
||||||
: 0),
|
|
||||||
0
|
|
||||||
)
|
)
|
||||||
// loop through the contracts and combine the notification items into one
|
// Loop through the contracts and combine the notification items into one
|
||||||
function combineNotificationsByAddingSourceTextsAndReturningTheRest(
|
function combineNotificationsByAddingNumericSourceTexts(
|
||||||
notifications: Notification[]
|
notifications: Notification[]
|
||||||
) {
|
) {
|
||||||
const newNotifications = []
|
const newNotifications = []
|
||||||
const groupedNotificationsByContractId = groupBy(
|
const groupedNotificationsBySourceType = groupBy(
|
||||||
notifications,
|
notifications,
|
||||||
(notification) => {
|
(n) => n.sourceType
|
||||||
return notification.sourceContractId
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
for (const contractId in groupedNotificationsByContractId) {
|
for (const sourceType in groupedNotificationsBySourceType) {
|
||||||
const notificationsForContractId =
|
const groupedNotificationsByContractId = groupBy(
|
||||||
groupedNotificationsByContractId[contractId]
|
groupedNotificationsBySourceType[sourceType],
|
||||||
let sum = 0
|
(notification) => {
|
||||||
notificationsForContractId.forEach(
|
return notification.sourceContractId
|
||||||
(notification) =>
|
}
|
||||||
notification.sourceText &&
|
|
||||||
(sum = parseInt(notification.sourceText) + sum)
|
|
||||||
)
|
)
|
||||||
|
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 =
|
const newNotification = {
|
||||||
notificationsForContractId.length === 1
|
...notificationsForContractId[0],
|
||||||
? notificationsForContractId[0]
|
sourceText: sum.toString(),
|
||||||
: {
|
sourceUserUsername:
|
||||||
...notificationsForContractId[0],
|
uniqueUsers.length > 1
|
||||||
sourceText: sum.toString(),
|
? MULTIPLE_USERS_KEY
|
||||||
}
|
: notificationsForContractId[0].sourceType,
|
||||||
newNotifications.push(newNotification)
|
}
|
||||||
|
newNotifications.push(newNotification)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return newNotifications
|
return newNotifications
|
||||||
}
|
}
|
||||||
|
|
||||||
const combinedNotifs =
|
const combinedNotifs =
|
||||||
combineNotificationsByAddingSourceTextsAndReturningTheRest(notifications)
|
combineNotificationsByAddingNumericSourceTexts(notifications)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -286,53 +293,23 @@ function IncomeNotificationGroupItem(props: {
|
||||||
<div>
|
<div>
|
||||||
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
|
<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 ? (
|
{!expanded ? (
|
||||||
<>
|
<>
|
||||||
{combinedNotifs
|
{combinedNotifs
|
||||||
.slice(0, numSummaryLines)
|
.slice(0, numSummaryLines)
|
||||||
.map((notification) => {
|
.map((notification) => (
|
||||||
return (
|
<IncomeNotificationItem
|
||||||
<Row
|
notification={notification}
|
||||||
className={
|
justSummary={true}
|
||||||
'items-center text-sm text-gray-500 sm:justify-start'
|
key={notification.id}
|
||||||
}
|
/>
|
||||||
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'}
|
|
||||||
notification={notification}
|
|
||||||
justSummary={true}
|
|
||||||
/>
|
|
||||||
</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 '}>
|
<div className={'text-sm text-gray-500 hover:underline '}>
|
||||||
{combinedNotifs.length - numSummaryLines > 0
|
{combinedNotifs.length - numSummaryLines > 0
|
||||||
? 'And ' +
|
? 'And ' +
|
||||||
|
@ -344,7 +321,7 @@ function IncomeNotificationGroupItem(props: {
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{combinedNotifs.map((notification) => (
|
{combinedNotifs.map((notification) => (
|
||||||
<NotificationItem
|
<IncomeNotificationItem
|
||||||
notification={notification}
|
notification={notification}
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
justSummary={false}
|
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: {
|
function NotificationGroupItem(props: {
|
||||||
notificationGroup: NotificationGroup
|
notificationGroup: NotificationGroup
|
||||||
className?: string
|
className?: string
|
||||||
}) {
|
}) {
|
||||||
const { notificationGroup, className } = props
|
const { notificationGroup, className } = props
|
||||||
const { notifications } = notificationGroup
|
const { notifications } = notificationGroup
|
||||||
const {
|
const { sourceContractTitle } = notifications[0]
|
||||||
sourceContractTitle,
|
|
||||||
sourceContractSlug,
|
|
||||||
sourceContractCreatorUsername,
|
|
||||||
} = notifications[0]
|
|
||||||
const numSummaryLines = 3
|
const numSummaryLines = 3
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false)
|
||||||
const [highlighted, setHighlighted] = useState(false)
|
const [highlighted, setHighlighted] = useState(
|
||||||
|
notifications.some((n) => !n.isSeen)
|
||||||
|
)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (notifications.some((n) => !n.isSeen)) {
|
|
||||||
setHighlighted(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
setHighlighted(false)
|
|
||||||
}, HIGHLIGHT_DURATION)
|
|
||||||
}
|
|
||||||
setNotificationsAsSeen(notifications)
|
setNotificationsAsSeen(notifications)
|
||||||
}, [notifications])
|
}, [notifications])
|
||||||
|
|
||||||
|
@ -408,27 +487,18 @@ function NotificationGroupItem(props: {
|
||||||
)}
|
)}
|
||||||
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
||||||
<EmptyAvatar multi />
|
<EmptyAvatar multi />
|
||||||
<div className={'flex-1 overflow-hidden pl-2 sm:flex'}>
|
<div className={'flex truncate pl-2'}>
|
||||||
<div
|
<div
|
||||||
onClick={() => setExpanded(!expanded)}
|
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 ? (
|
{sourceContractTitle ? (
|
||||||
<span>
|
<>
|
||||||
{'Activity on '}
|
<span className={'flex-shrink-0'}>{'Activity on '}</span>
|
||||||
<a
|
<span className={'truncate'}>
|
||||||
href={
|
<NotificationLink notification={notifications[0]} />
|
||||||
sourceContractCreatorUsername
|
</span>
|
||||||
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
</>
|
||||||
: ''
|
|
||||||
}
|
|
||||||
className={
|
|
||||||
'font-bold hover:underline hover:decoration-indigo-400 hover:decoration-2'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{sourceContractTitle}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
) : (
|
) : (
|
||||||
'Other activity'
|
'Other activity'
|
||||||
)}
|
)}
|
||||||
|
@ -439,7 +509,13 @@ function NotificationGroupItem(props: {
|
||||||
<div>
|
<div>
|
||||||
<div className={clsx('mt-1 md:text-base', expanded ? 'pl-4' : '')}>
|
<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 ? (
|
{!expanded ? (
|
||||||
<>
|
<>
|
||||||
{notifications.slice(0, numSummaryLines).map((notification) => {
|
{notifications.slice(0, numSummaryLines).map((notification) => {
|
||||||
|
@ -466,6 +542,7 @@ function NotificationGroupItem(props: {
|
||||||
notification={notification}
|
notification={notification}
|
||||||
key={notification.id}
|
key={notification.id}
|
||||||
justSummary={false}
|
justSummary={false}
|
||||||
|
hideTitle={true}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
@ -695,7 +772,7 @@ function NotificationLink(props: { notification: Notification }) {
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
className={
|
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}
|
{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: {
|
function NotificationItem(props: {
|
||||||
notification: Notification
|
notification: Notification
|
||||||
justSummary?: boolean
|
justSummary?: boolean
|
||||||
|
hideTitle?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { notification, justSummary } = props
|
const { notification, justSummary, hideTitle } = props
|
||||||
const {
|
const {
|
||||||
sourceType,
|
sourceType,
|
||||||
sourceId,
|
sourceId,
|
||||||
|
@ -721,7 +841,6 @@ function NotificationItem(props: {
|
||||||
sourceText,
|
sourceText,
|
||||||
sourceContractCreatorUsername,
|
sourceContractCreatorUsername,
|
||||||
sourceContractSlug,
|
sourceContractSlug,
|
||||||
sourceSlug,
|
|
||||||
} = notification
|
} = notification
|
||||||
|
|
||||||
const [defaultNotificationText, setDefaultNotificationText] =
|
const [defaultNotificationText, setDefaultNotificationText] =
|
||||||
|
@ -736,48 +855,12 @@ function NotificationItem(props: {
|
||||||
}
|
}
|
||||||
}, [reasonText, sourceText])
|
}, [reasonText, sourceText])
|
||||||
|
|
||||||
const [highlighted, setHighlighted] = useState(false)
|
const [highlighted] = useState(!notification.isSeen)
|
||||||
useEffect(() => {
|
|
||||||
if (!notification.isSeen) {
|
|
||||||
setHighlighted(true)
|
|
||||||
setTimeout(() => {
|
|
||||||
setHighlighted(false)
|
|
||||||
}, HIGHLIGHT_DURATION)
|
|
||||||
}
|
|
||||||
}, [notification.isSeen])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setNotificationsAsSeen([notification])
|
setNotificationsAsSeen([notification])
|
||||||
}, [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) {
|
if (justSummary) {
|
||||||
return (
|
return (
|
||||||
<Row className={'items-center text-sm text-gray-500 sm:justify-start'}>
|
<Row className={'items-center text-sm text-gray-500 sm:justify-start'}>
|
||||||
|
@ -793,10 +876,7 @@ function NotificationItem(props: {
|
||||||
<span className={'flex-shrink-0'}>
|
<span className={'flex-shrink-0'}>
|
||||||
{sourceType &&
|
{sourceType &&
|
||||||
reason &&
|
reason &&
|
||||||
getReasonForShowingNotification(notification, true).replace(
|
getReasonForShowingNotification(notification, true, true)}
|
||||||
' on',
|
|
||||||
''
|
|
||||||
)}
|
|
||||||
</span>
|
</span>
|
||||||
<div className={'ml-1 text-black'}>
|
<div className={'ml-1 text-black'}>
|
||||||
<NotificationTextLabel
|
<NotificationTextLabel
|
||||||
|
@ -821,25 +901,21 @@ function NotificationItem(props: {
|
||||||
highlighted && 'bg-indigo-200 hover:bg-indigo-100'
|
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'}>
|
<Row className={'items-center text-gray-500 sm:justify-start'}>
|
||||||
{sourceType != 'bonus' ? (
|
<Avatar
|
||||||
<Avatar
|
avatarUrl={sourceUserAvatarUrl}
|
||||||
avatarUrl={sourceUserAvatarUrl}
|
size={'sm'}
|
||||||
size={'sm'}
|
className={'mr-2'}
|
||||||
className={'mr-2'}
|
username={sourceUserName}
|
||||||
username={sourceUserName}
|
/>
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<TrendingUpIcon className={'text-primary h-7 w-7'} />
|
|
||||||
)}
|
|
||||||
<div className={'flex-1 overflow-hidden sm:flex'}>
|
<div className={'flex-1 overflow-hidden sm:flex'}>
|
||||||
<div
|
<div
|
||||||
className={
|
className={
|
||||||
'flex max-w-xl shrink overflow-hidden text-ellipsis pl-1 sm:pl-0'
|
'flex max-w-xl shrink overflow-hidden text-ellipsis pl-1 sm:pl-0'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{sourceType != 'bonus' && sourceUpdateType != 'closed' && (
|
{sourceUpdateType != 'closed' && (
|
||||||
<UserLink
|
<UserLink
|
||||||
name={sourceUserName || ''}
|
name={sourceUserName || ''}
|
||||||
username={sourceUserUsername || ''}
|
username={sourceUserUsername || ''}
|
||||||
|
@ -847,26 +923,30 @@ function NotificationItem(props: {
|
||||||
justFirstName={true}
|
justFirstName={true}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className={'inline-flex overflow-hidden text-ellipsis pl-1'}>
|
{sourceType && reason && (
|
||||||
{sourceType && reason && (
|
<div className={'inline flex truncate'}>
|
||||||
<div className={'inline truncate'}>
|
<span className={'ml-1 flex-shrink-0'}>
|
||||||
{getReasonForShowingNotification(notification, false)}
|
{getReasonForShowingNotification(notification, false, true)}
|
||||||
|
</span>
|
||||||
|
{!hideTitle && (
|
||||||
<NotificationLink notification={notification} />
|
<NotificationLink notification={notification} />
|
||||||
</div>
|
)}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
{sourceId &&
|
||||||
|
sourceContractSlug &&
|
||||||
|
sourceContractCreatorUsername ? (
|
||||||
|
<CopyLinkDateTimeComponent
|
||||||
|
prefix={sourceContractCreatorUsername}
|
||||||
|
slug={sourceContractSlug}
|
||||||
|
createdTime={createdTime}
|
||||||
|
elementId={getSourceIdForLinkComponent(sourceId)}
|
||||||
|
className={'-mx-1 inline-flex sm:inline-block'}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<RelativeTimestamp time={createdTime} />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{sourceId && sourceContractSlug && sourceContractCreatorUsername ? (
|
|
||||||
<CopyLinkDateTimeComponent
|
|
||||||
prefix={sourceContractCreatorUsername}
|
|
||||||
slug={sourceContractSlug}
|
|
||||||
createdTime={createdTime}
|
|
||||||
elementId={getSourceIdForLinkComponent(sourceId)}
|
|
||||||
className={'-mx-1 inline-flex sm:inline-block'}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<RelativeTimestamp time={createdTime} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
<div className={'mt-1 ml-1 md:text-base'}>
|
<div className={'mt-1 ml-1 md:text-base'}>
|
||||||
|
@ -938,9 +1018,11 @@ function NotificationTextLabel(props: {
|
||||||
return (
|
return (
|
||||||
<span className="text-blue-400">{formatMoney(parseInt(sourceText))}</span>
|
<span className="text-blue-400">{formatMoney(parseInt(sourceText))}</span>
|
||||||
)
|
)
|
||||||
} else if (sourceType === 'bonus' && sourceText) {
|
} else if ((sourceType === 'bonus' || sourceType === 'tip') && sourceText) {
|
||||||
return (
|
return (
|
||||||
<span className="text-primary">{formatMoney(parseInt(sourceText))}</span>
|
<span className="text-primary">
|
||||||
|
{'+' + formatMoney(parseInt(sourceText))}
|
||||||
|
</span>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// return default text
|
// return default text
|
||||||
|
@ -953,19 +1035,19 @@ function NotificationTextLabel(props: {
|
||||||
|
|
||||||
function getReasonForShowingNotification(
|
function getReasonForShowingNotification(
|
||||||
notification: Notification,
|
notification: Notification,
|
||||||
simple?: boolean
|
simple?: boolean,
|
||||||
|
replaceOn?: boolean
|
||||||
) {
|
) {
|
||||||
const { sourceType, sourceUpdateType, sourceText, reason, sourceSlug } =
|
const { sourceType, sourceUpdateType, reason, sourceSlug } = notification
|
||||||
notification
|
|
||||||
let reasonText: string
|
let reasonText: string
|
||||||
switch (sourceType) {
|
switch (sourceType) {
|
||||||
case 'comment':
|
case 'comment':
|
||||||
if (reason === 'reply_to_users_answer')
|
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')
|
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')
|
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')
|
else if (reason === 'on_users_contract')
|
||||||
reasonText = !simple ? `commented on your question` : 'commented'
|
reasonText = !simple ? `commented on your question` : 'commented'
|
||||||
else if (reason === 'on_contract_with_users_comment')
|
else if (reason === 'on_contract_with_users_comment')
|
||||||
|
@ -973,7 +1055,7 @@ function getReasonForShowingNotification(
|
||||||
else if (reason === 'on_contract_with_users_answer')
|
else if (reason === 'on_contract_with_users_answer')
|
||||||
reasonText = `commented on`
|
reasonText = `commented on`
|
||||||
else if (reason === 'on_contract_with_users_shares_in')
|
else if (reason === 'on_contract_with_users_shares_in')
|
||||||
reasonText = `commented`
|
reasonText = `commented on`
|
||||||
else reasonText = `commented on`
|
else reasonText = `commented on`
|
||||||
break
|
break
|
||||||
case 'contract':
|
case 'contract':
|
||||||
|
@ -1008,17 +1090,13 @@ function getReasonForShowingNotification(
|
||||||
else if (sourceSlug) reasonText = 'joined because you shared'
|
else if (sourceSlug) reasonText = 'joined because you shared'
|
||||||
else reasonText = 'joined because of you'
|
else reasonText = 'joined because of you'
|
||||||
break
|
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:
|
default:
|
||||||
reasonText = ''
|
reasonText = ''
|
||||||
}
|
}
|
||||||
return reasonText
|
|
||||||
|
return (
|
||||||
|
<span className={'flex-shrink-0'}>
|
||||||
|
{replaceOn ? reasonText.replace(' on', '') : reasonText}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user