Notif ux improvements (#471)

* Save resolved values in notifs

* Clean up

* Various ux improvements
This commit is contained in:
Ian Philips 2022-06-09 11:39:23 -06:00 committed by GitHub
parent 8634af702a
commit 699bddcb2a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -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