import {SparklesIcon} from '@heroicons/react/24/solid'
import clsx from 'clsx'
import {ENV_CONFIG} from 'common/envs/constants'
import {Notification} from 'common/notifications'
import {sortBy} from 'lodash'
import Link from 'next/link'
import {ReactNode, useState} from 'react'
import {useIsMobile} from 'web/hooks/use-is-mobile'
import {useT} from 'web/lib/locale'
import {Col} from './layout/col'
import {Row} from './layout/row'
import {MultiUserReactionModal} from './multi-user-reaction-link'
import {RelativeTimestampNoTooltip} from './relative-timestamp'
import {Avatar} from './widgets/avatar'
import {Linkify} from './widgets/linkify'
import {UserLink} from './widgets/user-link'
export function NotificationItem(props: {notification: Notification}) {
const {notification} = props
const {sourceType, reason} = notification
const [highlighted, setHighlighted] = useState(!notification.isSeen)
const params = {
notification,
highlighted,
setHighlighted,
}
if (sourceType === 'comment_on_profile') {
return
} else if (sourceType === 'new_match') {
return
} else if (reason === 'new_profile_like') {
return
} else if (reason === 'new_profile_ship') {
return
} else if (reason === 'connection_interest_match') {
return
} else {
return
}
}
export function BaseNotification(props: {
notification: Notification
highlighted: boolean
setHighlighted: (highlighted: boolean) => void
}) {
const {notification, highlighted, setHighlighted} = props
return (
}
subtitle={
}
link={notification.sourceSlug}
>
{notification.title}
)
}
export function CommentOnProfileNotification(props: {
notification: Notification
highlighted: boolean
setHighlighted: (highlighted: boolean) => void
isChildOfGroup?: boolean
}) {
const {notification, isChildOfGroup, highlighted, setHighlighted} = props
const {sourceUserName, sourceUserUsername, sourceText} = notification
const t = useT()
const reasonText = t('notifications.comment.commented', `commented `)
return (
}
subtitle={
}
link={notification.sourceSlug}
>
{reasonText}
{!isChildOfGroup && (
{t('notifications.comment.on_your_profile', 'on your profile')}
)}
)
}
export function NewMatchNotification(props: {
notification: Notification
highlighted: boolean
setHighlighted: (highlighted: boolean) => void
isChildOfGroup?: boolean
}) {
const {notification, isChildOfGroup, highlighted, setHighlighted} = props
const {sourceContractTitle, sourceText, sourceUserName, sourceUserUsername} = notification
const t = useT()
return (
}
link={getSourceUrl(notification)}
subtitle={
}
>
{' '}
{t('notifications.match.proposed_new_match', 'proposed a new match:')}
)
}
function ProfileLikeNotification(props: {
notification: Notification
highlighted: boolean
setHighlighted: (highlighted: boolean) => void
isChildOfGroup?: boolean
}) {
const {notification, highlighted, setHighlighted, isChildOfGroup} = props
const [open, setOpen] = useState(false)
const t = useT()
const {sourceUserName, sourceUserUsername} = notification
const relatedNotifications: Notification[] = notification.data?.relatedNotifications ?? [
notification,
]
const reactorsText =
relatedNotifications.length > 1
? `${sourceUserName} & ${relatedNotifications.length - 1} other${
relatedNotifications.length > 2 ? 's' : ''
}`
: sourceUserName
return (
}
link={`https://${ENV_CONFIG.domain}/${sourceUserUsername}`}
subtitle={<>>}
>
{reactorsText && }{' '}
{t('notifications.profile.liked_you', 'liked you!')}
)
}
function ProfileShipNotification(props: {
notification: Notification
highlighted: boolean
setHighlighted: (highlighted: boolean) => void
isChildOfGroup?: boolean
}) {
const {notification, highlighted, setHighlighted, isChildOfGroup} = props
const [open, setOpen] = useState(false)
const t = useT()
const {sourceUserName, sourceUserUsername} = notification
const relatedNotifications: Notification[] = notification.data?.relatedNotifications ?? [
notification,
]
const reactorsText =
relatedNotifications.length > 1
? `${sourceUserName} & ${relatedNotifications.length - 1} other${
relatedNotifications.length > 2 ? 's' : ''
}`
: sourceUserName
const {creatorId, creatorName, creatorUsername} = notification.data ?? {}
return (
}
link={`https://${ENV_CONFIG.domain}/${sourceUserUsername}`}
subtitle={<>>}
>
You and {reactorsText && }{' '}
{t('notifications.profile.are_being_shipped_by', 'are being shipped by')}{' '}
!
)
}
export function ConnectionInterestMatchNotification(props: {
notification: Notification
highlighted: boolean
setHighlighted: (highlighted: boolean) => void
isChildOfGroup?: boolean
}) {
const {notification, highlighted, setHighlighted, isChildOfGroup} = props
const {sourceUserName, sourceUserUsername, sourceText} = notification
const t = useT()
const connectionType = notification.data?.connectionType || sourceText
const type = t(`profile.relationship.${connectionType}`, connectionType).toLowerCase()
return (
}
link={`/${sourceUserUsername}`}
subtitle={<>>}
>
{' '}
{t('notifications.connection.interested_in_you', 'is interested in a {type} with you', {
type,
})}
!
)
}
const getSourceUrl = (notification: Notification) => {
const {sourceSlug, sourceId} = notification
if (sourceSlug) {
return `${sourceSlug.startsWith('/') ? sourceSlug : '/' + sourceSlug}#${sourceId}`
}
return ''
}
// TODO: fix badges (id based)
export function NotificationUserLink(props: {
userId?: string
name?: string
username?: string
className?: string
hideBadge?: boolean
}) {
const {userId, name, username, className, hideBadge} = props
return (
)
}
export function AvatarNotificationIcon(props: {
notification: Notification
symbol?: string | ReactNode
}) {
const {notification, symbol} = props
const {sourceUserName, sourceUserAvatarUrl, sourceUserUsername, sourceSlug} = notification
const href = sourceUserUsername ? `/${sourceUserUsername}` : (sourceSlug ?? '/')
return (
) => e.stopPropagation}
>
{symbol}
)
}
export function MultipleAvatarIcons(props: {
notification: Notification
symbol: string
setOpen: (open: boolean) => void
}) {
const {notification, symbol, setOpen} = props
const relatedNotifications: Notification[] = sortBy(
notification.data?.relatedNotifications ?? [notification],
(n) => n.createdTime,
)
const combineAvatars = (notifications: Notification[]) => {
const totalAvatars = notifications.length
const maxToShow = Math.min(totalAvatars, 3)
const avatarsToCombine = notifications.slice(totalAvatars - maxToShow, totalAvatars)
const max = avatarsToCombine.length
const startLeft = -0.35 * (max - 1)
return avatarsToCombine.map((n, index) => (
))
}
return (
{
if (relatedNotifications.length === 1) return
event.preventDefault()
setOpen(true)
}}
>
{relatedNotifications.length > 1 ? (
{/* placeholder avatar to set the proper size*/}
{combineAvatars(relatedNotifications)}
) : (
)}
)
}
export function PrimaryNotificationLink(props: {text: string | undefined}) {
const {text} = props
if (!text) {
return <>>
}
return {text}
}
// the primary skeleton for notifications
export function NotificationFrame(props: {
notification: Notification
highlighted: boolean
setHighlighted: (highlighted: boolean) => void
children: React.ReactNode
icon: ReactNode
link?: string
onClick?: () => void
subtitle?: string | ReactNode
isChildOfGroup?: boolean
customBackground?: ReactNode
}) {
const {
notification,
highlighted,
setHighlighted,
children,
icon,
subtitle,
onClick,
link,
customBackground,
} = props
const isMobile = useIsMobile()
const frameObject = (
{icon}
{children}
{subtitle}
{highlighted && !isMobile && }
)
return (
{customBackground}
{link && (
{
if (highlighted) {
setHighlighted(false)
}
}}
>
{frameObject}
)}
{!link && (
{
if (highlighted) {
setHighlighted(false)
}
if (onClick) {
onClick()
}
}}
>
{frameObject}
)}
)
}