Enrich limit order notification

This commit is contained in:
Ian Philips 2022-09-15 13:39:46 -06:00
parent 69c2570ff9
commit 8c6a40bab7
4 changed files with 190 additions and 75 deletions

View File

@ -92,11 +92,6 @@ export type notification_reason_types =
| 'your_contract_closed' | 'your_contract_closed'
| 'subsidized_your_market' | 'subsidized_your_market'
export type BettingStreakData = {
streak: number
bonusAmount: number
}
type notification_descriptions = { type notification_descriptions = {
[key in notification_preference]: { [key in notification_preference]: {
simple: string simple: string
@ -241,3 +236,15 @@ export const NOTIFICATION_DESCRIPTIONS: notification_descriptions = {
detailed: `Answers on markets that you're watching and that you're invested in`, detailed: `Answers on markets that you're watching and that you're invested in`,
}, },
} }
export type BettingStreakData = {
streak: number
bonusAmount: number
}
export type BetFillData = {
betOutcome: string
creatorOutcome: string
probability: number
fillAmount: number
}

View File

@ -1,5 +1,6 @@
import * as admin from 'firebase-admin' import * as admin from 'firebase-admin'
import { import {
BetFillData,
BettingStreakData, BettingStreakData,
Notification, Notification,
notification_reason_types, notification_reason_types,
@ -542,6 +543,12 @@ export const createBetFillNotification = async (
sourceContractTitle: contract.question, sourceContractTitle: contract.question,
sourceContractSlug: contract.slug, sourceContractSlug: contract.slug,
sourceContractId: contract.id, sourceContractId: contract.id,
data: {
betOutcome: bet.outcome,
creatorOutcome: userBet.outcome,
fillAmount,
probability: userBet.limitProb,
} as BetFillData,
} }
return await notificationRef.set(removeUndefinedProps(notification)) return await notificationRef.set(removeUndefinedProps(notification))

View File

@ -4,14 +4,14 @@ import { initAdmin } from './script-init'
initAdmin() initAdmin()
import { getValues } from '../utils' import { getValues } from '../utils'
import { Contract } from 'common/lib/contract' import { Contract } from 'common/contract'
import { Comment } from 'common/lib/comment' import { Comment } from 'common/comment'
import { uniq } from 'lodash' import { uniq } from 'lodash'
import { Bet } from 'common/lib/bet' import { Bet } from 'common/bet'
import { import {
DEV_HOUSE_LIQUIDITY_PROVIDER_ID, DEV_HOUSE_LIQUIDITY_PROVIDER_ID,
HOUSE_LIQUIDITY_PROVIDER_ID, HOUSE_LIQUIDITY_PROVIDER_ID,
} from 'common/lib/antes' } from 'common/antes'
const firestore = admin.firestore() const firestore = admin.firestore()

View File

@ -1,7 +1,11 @@
import { ControlledTabs } from 'web/components/layout/tabs' import { ControlledTabs } from 'web/components/layout/tabs'
import React, { useEffect, useMemo, useState } from 'react' import React, { useEffect, useMemo, useState } from 'react'
import Router, { useRouter } from 'next/router' import Router, { useRouter } from 'next/router'
import { Notification, notification_source_types } from 'common/notification' import {
BetFillData,
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'
@ -141,6 +145,7 @@ function RenderNotificationGroups(props: {
<NotificationItem <NotificationItem
notification={notification.notifications[0]} notification={notification.notifications[0]}
key={notification.notifications[0].id} key={notification.notifications[0].id}
justSummary={false}
/> />
) : ( ) : (
<NotificationGroupItem <NotificationGroupItem
@ -697,20 +702,11 @@ function NotificationGroupItem(props: {
function NotificationItem(props: { function NotificationItem(props: {
notification: Notification notification: Notification
justSummary?: boolean justSummary: boolean
isChildOfGroup?: boolean isChildOfGroup?: boolean
}) { }) {
const { notification, justSummary, isChildOfGroup } = props const { notification, justSummary, isChildOfGroup } = props
const { const { sourceType, reason } = notification
sourceType,
sourceUserName,
sourceUserAvatarUrl,
sourceUpdateType,
reasonText,
reason,
sourceUserUsername,
sourceText,
} = notification
const [highlighted] = useState(!notification.isSeen) const [highlighted] = useState(!notification.isSeen)
@ -718,9 +714,62 @@ function NotificationItem(props: {
setNotificationsAsSeen([notification]) setNotificationsAsSeen([notification])
}, [notification]) }, [notification])
const questionNeedsResolution = sourceUpdateType == 'closed' // TODO Any new notification should be its own component
if (reason === 'bet_fill') {
return (
<BetFillNotification
notification={notification}
isChildOfGroup={isChildOfGroup}
highlighted={highlighted}
justSummary={justSummary}
/>
)
}
// TODO Add new notification components here
if (justSummary) { if (justSummary) {
return (
<NotificationSummaryFrame
notification={notification}
subtitle={
(sourceType &&
reason &&
getReasonForShowingNotification(notification, true)) ??
''
}
>
<NotificationTextLabel
className={'line-clamp-1'}
notification={notification}
justSummary={true}
/>
</NotificationSummaryFrame>
)
}
return (
<NotificationFrame
notification={notification}
subtitle={getReasonForShowingNotification(
notification,
isChildOfGroup ?? false
)}
highlighted={highlighted}
>
<div className={'mt-1 ml-1 md:text-base'}>
<NotificationTextLabel notification={notification} />
</div>
</NotificationFrame>
)
}
function NotificationSummaryFrame(props: {
notification: Notification
subtitle: string
children: React.ReactNode
}) {
const { notification, subtitle, children } = props
const { sourceUserName, sourceUserUsername } = notification
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'}>
<div className={'line-clamp-1 flex-1 overflow-hidden sm:flex'}> <div className={'line-clamp-1 flex-1 overflow-hidden sm:flex'}>
@ -732,24 +781,35 @@ function NotificationItem(props: {
short={true} short={true}
/> />
<div className={'inline-flex overflow-hidden text-ellipsis pl-1'}> <div className={'inline-flex overflow-hidden text-ellipsis pl-1'}>
<span className={'flex-shrink-0'}> <span className={'flex-shrink-0'}>{subtitle}</span>
{sourceType && <div className={'line-clamp-1 ml-1 text-black'}>{children}</div>
reason &&
getReasonForShowingNotification(notification, true)}
</span>
<div className={'ml-1 text-black'}>
<NotificationTextLabel
className={'line-clamp-1'}
notification={notification}
justSummary={true}
/>
</div>
</div> </div>
</div> </div>
</div> </div>
</Row> </Row>
) )
} }
function NotificationFrame(props: {
notification: Notification
highlighted: boolean
subtitle: string
children: React.ReactNode
isChildOfGroup?: boolean
}) {
const { notification, isChildOfGroup, highlighted, subtitle, children } =
props
const {
sourceType,
sourceUserName,
sourceUserAvatarUrl,
sourceUpdateType,
reason,
reasonText,
sourceUserUsername,
sourceText,
} = notification
const questionNeedsResolution = sourceUpdateType == 'closed'
return ( return (
<div <div
@ -796,18 +856,13 @@ function NotificationItem(props: {
} }
> >
<div> <div>
{!questionNeedsResolution && (
<UserLink <UserLink
name={sourceUserName || ''} name={sourceUserName || ''}
username={sourceUserUsername || ''} username={sourceUserUsername || ''}
className={'relative mr-1 flex-shrink-0'} className={'relative mr-1 flex-shrink-0'}
short={true} short={true}
/> />
)} {subtitle}
{getReasonForShowingNotification(
notification,
isChildOfGroup ?? false
)}
{isChildOfGroup ? ( {isChildOfGroup ? (
<RelativeTimestamp time={notification.createdTime} /> <RelativeTimestamp time={notification.createdTime} />
) : ( ) : (
@ -822,9 +877,7 @@ function NotificationItem(props: {
)} )}
</div> </div>
</Row> </Row>
<div className={'mt-1 ml-1 md:text-base'}> <div className={'mt-1 ml-1 md:text-base'}>{children}</div>
<NotificationTextLabel notification={notification} />
</div>
<div className={'mt-6 border-b border-gray-300'} /> <div className={'mt-6 border-b border-gray-300'} />
</div> </div>
@ -832,6 +885,66 @@ function NotificationItem(props: {
) )
} }
function BetFillNotification(props: {
notification: Notification
highlighted: boolean
justSummary: boolean
isChildOfGroup?: boolean
}) {
const { notification, isChildOfGroup, highlighted, justSummary } = props
const { sourceText, data } = notification
const { creatorOutcome, probability } = (data as BetFillData) ?? {}
const subtitle = 'bet against you'
const amount = formatMoney(parseInt(sourceText ?? '0'))
const description =
creatorOutcome && probability ? (
<span>
of your{' '}
<span
className={
creatorOutcome === 'YES'
? 'text-primary'
: creatorOutcome === 'NO'
? 'text-red-500'
: 'text-blue-500'
}
>
{creatorOutcome}{' '}
</span>
limit order at {Math.round(probability * 100)}% was filled
</span>
) : (
<span>of your limit order was filled</span>
)
if (justSummary) {
return (
<NotificationSummaryFrame notification={notification} subtitle={subtitle}>
<Row className={'line-clamp-1'}>
<span className={'text-primary mr-1'}>{amount}</span>
<span>{description}</span>
</Row>
</NotificationSummaryFrame>
)
}
return (
<NotificationFrame
notification={notification}
isChildOfGroup={isChildOfGroup}
highlighted={highlighted}
subtitle={subtitle}
>
<Row>
<span>
<span className="text-primary mr-1">{amount}</span>
{description}
</span>
</Row>
</NotificationFrame>
)
}
export const setNotificationsAsSeen = async (notifications: Notification[]) => { export const setNotificationsAsSeen = async (notifications: Notification[]) => {
const unseenNotifications = notifications.filter((n) => !n.isSeen) const unseenNotifications = notifications.filter((n) => !n.isSeen)
return await Promise.all( return await Promise.all(
@ -1002,15 +1115,6 @@ 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 === 'bet' && sourceText) {
return (
<>
<span className="text-primary">
{formatMoney(parseInt(sourceText))}
</span>{' '}
<span>of your limit order was filled</span>
</>
)
} else if (sourceType === 'challenge' && sourceText) { } else if (sourceType === 'challenge' && sourceText) {
return ( return (
<> <>
@ -1074,9 +1178,6 @@ 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 'bet':
reasonText = 'bet against you'
break
case 'challenge': case 'challenge':
reasonText = 'accepted your challenge' reasonText = 'accepted your challenge'
break break