Notif ux improvements (#471)
* Save resolved values in notifs * Clean up * Various ux improvements
This commit is contained in:
parent
8634af702a
commit
699bddcb2a
|
@ -20,7 +20,6 @@ import { getValue } from 'web/lib/firebase/utils'
|
||||||
import Custom404 from 'web/pages/404'
|
import Custom404 from 'web/pages/404'
|
||||||
import { UserLink } from 'web/components/user-page'
|
import { UserLink } from 'web/components/user-page'
|
||||||
import { notification_subscribe_types, PrivateUser } from 'common/user'
|
import { notification_subscribe_types, PrivateUser } from 'common/user'
|
||||||
import { useContract } from 'web/hooks/use-contract'
|
|
||||||
import { Contract } from 'common/contract'
|
import { Contract } from 'common/contract'
|
||||||
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
import { ChoicesToggleGroup } from 'web/components/choices-toggle-group'
|
||||||
import { listenForPrivateUser, updatePrivateUser } from 'web/lib/firebase/users'
|
import { listenForPrivateUser, updatePrivateUser } from 'web/lib/firebase/users'
|
||||||
|
@ -185,14 +184,40 @@ function NotificationGroupItem(props: {
|
||||||
}) {
|
}) {
|
||||||
const { notificationGroup, className } = props
|
const { notificationGroup, className } = props
|
||||||
const { sourceContractId, notifications, timePeriod } = notificationGroup
|
const { sourceContractId, notifications, timePeriod } = notificationGroup
|
||||||
const contract = useContract(sourceContractId ?? '')
|
const {
|
||||||
|
sourceContractTitle,
|
||||||
|
sourceContractSlug,
|
||||||
|
sourceContractCreatorUsername,
|
||||||
|
} = notifications[0]
|
||||||
const numSummaryLines = 3
|
const numSummaryLines = 3
|
||||||
|
|
||||||
const [expanded, setExpanded] = useState(false)
|
const [expanded, setExpanded] = useState(false)
|
||||||
|
const [contract, setContract] = useState<Contract | undefined>(undefined)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
sourceContractTitle &&
|
||||||
|
sourceContractSlug &&
|
||||||
|
sourceContractCreatorUsername
|
||||||
|
)
|
||||||
|
return
|
||||||
|
if (sourceContractId) {
|
||||||
|
getContractFromId(sourceContractId)
|
||||||
|
.then((contract) => {
|
||||||
|
if (contract) setContract(contract)
|
||||||
|
})
|
||||||
|
.catch((e) => console.log(e))
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
sourceContractCreatorUsername,
|
||||||
|
sourceContractId,
|
||||||
|
sourceContractSlug,
|
||||||
|
sourceContractTitle,
|
||||||
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!contract) return
|
|
||||||
setNotificationsAsSeen(notifications)
|
setNotificationsAsSeen(notifications)
|
||||||
}, [contract, notifications])
|
}, [notifications])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
@ -218,10 +243,21 @@ function NotificationGroupItem(props: {
|
||||||
onClick={() => setExpanded(!expanded)}
|
onClick={() => setExpanded(!expanded)}
|
||||||
className={'line-clamp-1 cursor-pointer pl-1 sm:pl-0'}
|
className={'line-clamp-1 cursor-pointer pl-1 sm:pl-0'}
|
||||||
>
|
>
|
||||||
{contract ? (
|
{sourceContractTitle || contract ? (
|
||||||
<span>
|
<span>
|
||||||
{'Activity on '}
|
{'Activity on '}
|
||||||
<span className={'mx-1 font-bold'}>{contract?.question}</span>
|
<a
|
||||||
|
href={
|
||||||
|
sourceContractCreatorUsername
|
||||||
|
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
||||||
|
: `/${contract?.creatorUsername}/${contract?.slug}`
|
||||||
|
}
|
||||||
|
className={
|
||||||
|
'font-bold hover:underline hover:decoration-indigo-400 hover:decoration-2'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{sourceContractTitle || contract?.question}
|
||||||
|
</a>
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
'Other activity'
|
'Other activity'
|
||||||
|
@ -233,7 +269,7 @@ 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 gap-1 whitespace-pre-line'}>
|
<div className={'line-clamp-4 mt-1 ml-1 gap-1 whitespace-pre-line'}>
|
||||||
{!expanded ? (
|
{!expanded ? (
|
||||||
<>
|
<>
|
||||||
{notifications.slice(0, numSummaryLines).map((notification) => {
|
{notifications.slice(0, numSummaryLines).map((notification) => {
|
||||||
|
@ -496,7 +532,9 @@ function NotificationItem(props: {
|
||||||
sourceContractCreatorUsername,
|
sourceContractCreatorUsername,
|
||||||
sourceContractSlug,
|
sourceContractSlug,
|
||||||
} = notification
|
} = notification
|
||||||
const [notificationText, setNotificationText] = useState<string>('')
|
|
||||||
|
const [defaultNotificationText, setDefaultNotificationText] =
|
||||||
|
useState<string>('')
|
||||||
const [contract, setContract] = useState<Contract | null>(null)
|
const [contract, setContract] = useState<Contract | null>(null)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -518,13 +556,8 @@ function NotificationItem(props: {
|
||||||
])
|
])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (sourceText) {
|
||||||
sourceText &&
|
setDefaultNotificationText(sourceText)
|
||||||
(sourceType === 'comment' ||
|
|
||||||
sourceType === 'answer' ||
|
|
||||||
(sourceType === 'contract' && sourceUpdateType === 'updated'))
|
|
||||||
) {
|
|
||||||
setNotificationText(sourceText)
|
|
||||||
} else if (!contract || !sourceContractId || !sourceId) return
|
} else if (!contract || !sourceContractId || !sourceId) return
|
||||||
else if (
|
else if (
|
||||||
sourceType === 'answer' ||
|
sourceType === 'answer' ||
|
||||||
|
@ -532,12 +565,12 @@ function NotificationItem(props: {
|
||||||
sourceType === 'contract'
|
sourceType === 'contract'
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
getNotificationText(
|
parseNotificationText(
|
||||||
sourceId,
|
sourceId,
|
||||||
sourceContractId,
|
sourceContractId,
|
||||||
sourceType,
|
sourceType,
|
||||||
sourceUpdateType,
|
sourceUpdateType,
|
||||||
setNotificationText,
|
setDefaultNotificationText,
|
||||||
contract
|
contract
|
||||||
)
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -545,7 +578,7 @@ function NotificationItem(props: {
|
||||||
}
|
}
|
||||||
} else if (reasonText) {
|
} else if (reasonText) {
|
||||||
// Handle arbitrary notifications with reason text here.
|
// Handle arbitrary notifications with reason text here.
|
||||||
setNotificationText(reasonText)
|
setDefaultNotificationText(reasonText)
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
contract,
|
contract,
|
||||||
|
@ -586,7 +619,7 @@ function NotificationItem(props: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getNotificationText(
|
async function parseNotificationText(
|
||||||
sourceId: string,
|
sourceId: string,
|
||||||
sourceContractId: string,
|
sourceContractId: string,
|
||||||
sourceType: 'answer' | 'comment' | 'contract',
|
sourceType: 'answer' | 'comment' | 'contract',
|
||||||
|
@ -643,9 +676,10 @@ function NotificationItem(props: {
|
||||||
<div className={'ml-1 text-black'}>
|
<div className={'ml-1 text-black'}>
|
||||||
<NotificationTextLabel
|
<NotificationTextLabel
|
||||||
contract={contract}
|
contract={contract}
|
||||||
defaultText={notificationText}
|
defaultText={defaultNotificationText}
|
||||||
className={'line-clamp-1'}
|
className={'line-clamp-1'}
|
||||||
notification={notification}
|
notification={notification}
|
||||||
|
justSummary={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -685,25 +719,23 @@ function NotificationItem(props: {
|
||||||
sourceUpdateType,
|
sourceUpdateType,
|
||||||
contract
|
contract
|
||||||
)}
|
)}
|
||||||
<span className={'mx-1 font-bold'}>
|
<a
|
||||||
|
href={
|
||||||
|
sourceContractCreatorUsername
|
||||||
|
? `/${sourceContractCreatorUsername}/${sourceContractSlug}`
|
||||||
|
: `/${contract?.creatorUsername}/${contract?.slug}`
|
||||||
|
}
|
||||||
|
className={
|
||||||
|
'ml-1 font-bold hover:underline hover:decoration-indigo-400 hover:decoration-2'
|
||||||
|
}
|
||||||
|
>
|
||||||
{contract?.question || sourceContractTitle}
|
{contract?.question || sourceContractTitle}
|
||||||
</span>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{sourceId && contract && (
|
{sourceId && sourceContractSlug && sourceContractCreatorUsername ? (
|
||||||
<CopyLinkDateTimeComponent
|
|
||||||
contractCreatorUsername={contract.creatorUsername}
|
|
||||||
contractSlug={contract.slug}
|
|
||||||
createdTime={createdTime}
|
|
||||||
elementId={getSourceIdForLinkComponent(sourceId)}
|
|
||||||
className={'-mx-1 inline-flex sm:inline-block'}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{sourceId &&
|
|
||||||
sourceContractSlug &&
|
|
||||||
sourceContractCreatorUsername && (
|
|
||||||
<CopyLinkDateTimeComponent
|
<CopyLinkDateTimeComponent
|
||||||
contractCreatorUsername={sourceContractCreatorUsername}
|
contractCreatorUsername={sourceContractCreatorUsername}
|
||||||
contractSlug={sourceContractSlug}
|
contractSlug={sourceContractSlug}
|
||||||
|
@ -711,13 +743,15 @@ function NotificationItem(props: {
|
||||||
elementId={getSourceIdForLinkComponent(sourceId)}
|
elementId={getSourceIdForLinkComponent(sourceId)}
|
||||||
className={'-mx-1 inline-flex sm:inline-block'}
|
className={'-mx-1 inline-flex sm:inline-block'}
|
||||||
/>
|
/>
|
||||||
|
) : (
|
||||||
|
<RelativeTimestamp time={createdTime} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Row>
|
</Row>
|
||||||
<div className={'mt-1 md:text-base'}>
|
<div className={'mt-1 ml-1 md:text-base'}>
|
||||||
<NotificationTextLabel
|
<NotificationTextLabel
|
||||||
contract={contract}
|
contract={contract}
|
||||||
defaultText={notificationText}
|
defaultText={defaultNotificationText}
|
||||||
notification={notification}
|
notification={notification}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -733,16 +767,12 @@ function NotificationTextLabel(props: {
|
||||||
contract?: Contract | null
|
contract?: Contract | null
|
||||||
notification: Notification
|
notification: Notification
|
||||||
className?: string
|
className?: string
|
||||||
|
justSummary?: boolean
|
||||||
}) {
|
}) {
|
||||||
const { contract, className, defaultText, notification } = props
|
const { contract, className, defaultText, notification, justSummary } = props
|
||||||
const { sourceUpdateType, sourceType, sourceText, sourceContractTitle } =
|
const { sourceUpdateType, sourceType, sourceText, sourceContractTitle } =
|
||||||
notification
|
notification
|
||||||
if (
|
if (!contract && !sourceText && sourceType !== 'follow')
|
||||||
!contract &&
|
|
||||||
!sourceContractTitle &&
|
|
||||||
!sourceText &&
|
|
||||||
sourceType !== 'follow'
|
|
||||||
)
|
|
||||||
return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
|
return <LoadingIndicator spinnerClassName={'border-gray-500 h-4 w-4'} />
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -784,11 +814,10 @@ function NotificationTextLabel(props: {
|
||||||
)
|
)
|
||||||
} else return <div />
|
} else return <div />
|
||||||
} else if (sourceType === 'contract') {
|
} else if (sourceType === 'contract') {
|
||||||
return (
|
if (justSummary)
|
||||||
<div className={clsx('text-indigo-700 hover:underline', className)}>
|
return <span>{contract?.question || sourceContractTitle}</span>
|
||||||
{defaultText}
|
// Ignore contract update source text until we improve them
|
||||||
</div>
|
return <div />
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
Loading…
Reference in New Issue
Block a user