subtle highlight of new comments

This commit is contained in:
keyan 2023-08-06 14:18:40 -05:00
parent ea1e31c6ee
commit 1efc17fcc2
5 changed files with 48 additions and 7 deletions

View File

@ -120,6 +120,14 @@ export default function Comment ({
}
}, [item.id, router.query.commentId])
useEffect(() => {
if (router.query.commentsViewedAt &&
me?.id !== item.user?.id &&
new Date(item.createdAt).getTime() > router.query.commentsViewedAt) {
ref.current.classList.add('outline-new-comment')
}
}, [item.id])
const bottomedOut = depth === COMMENT_DEPTH_LIMIT
const op = root.user.name === item.user.name
const bountyPaid = root.bountyPaidTo?.includes(Number(item.id))
@ -127,6 +135,7 @@ export default function Comment ({
return (
<div
ref={ref} className={includeParent ? '' : `${styles.comment} ${collapse === 'yep' ? styles.collapsed : ''}`}
onMouseEnter={() => ref.current.classList.remove('outline-new-comment')}
>
<div className={`${itemStyles.item} ${styles.item}`}>
{item.meDontLike

View File

@ -5,7 +5,7 @@ import Badge from 'react-bootstrap/Badge'
import Dropdown from 'react-bootstrap/Dropdown'
import Countdown from './countdown'
import { abbrNum } from '../lib/format'
import { newComments } from '../lib/new-comments'
import { newComments, commentsViewedAt } from '../lib/new-comments'
import { timeSince } from '../lib/time'
import CowboyHat from './cowboy-hat'
import { DeleteDropdownItem } from './delete'
@ -42,7 +42,17 @@ export default function ItemInfo ({ item, pendingSats, full, commentsText, class
<span>{abbrNum(item.boost)} boost</span>
<span> \ </span>
</>}
<Link href={`/items/${item.id}`} title={`${item.commentSats} sats`} className='text-reset position-relative'>
<Link
href={`/items/${item.id}`} onClick={(e) => {
const viewedAt = commentsViewedAt(item)
if (viewedAt) {
e.preventDefault()
router.push(
`/items/${item.id}?commentsViewedAt=${viewedAt}`,
`/items/${item.id}`)
}
}} title={`${item.commentSats} sats`} className='text-reset position-relative'
>
{item.ncomments} {commentsText || 'comments'}
{hasNewComments &&
<span className={styles.notification}>

View File

@ -12,6 +12,8 @@ import Flag from '../svgs/flag-fill.svg'
import ImageIcon from '../svgs/image-fill.svg'
import { abbrNum } from '../lib/format'
import ItemInfo from './item-info'
import { commentsViewedAt } from '../lib/new-comments'
import { useRouter } from 'next/router'
export function SearchTitle ({ title }) {
return reactStringReplace(title, /:high\[([^\]]+)\]/g, (match, i) => {
@ -21,6 +23,7 @@ export function SearchTitle ({ title }) {
export default function Item ({ item, rank, belowTitle, right, full, children, siblingComments }) {
const titleRef = useRef()
const router = useRouter()
const [pendingSats, setPendingSats] = useState(0)
const image = item.url && item.url.startsWith(process.env.NEXT_PUBLIC_IMGPROXY_URL)
@ -39,7 +42,18 @@ export default function Item ({ item, rank, belowTitle, right, full, children, s
: item.meDontLike ? <Flag width={24} height={24} className={styles.dontLike} /> : <UpVote item={item} className={styles.upvote} pendingSats={pendingSats} setPendingSats={setPendingSats} />}
<div className={styles.hunk}>
<div className={`${styles.main} flex-wrap`}>
<Link href={`/items/${item.id}`} ref={titleRef} className={`${styles.title} text-reset me-2`}>
<Link
href={`/items/${item.id}`}
onClick={(e) => {
const viewedAt = commentsViewedAt(item)
if (viewedAt) {
e.preventDefault()
router.push(
`/items/${item.id}?commentsViewedAt=${viewedAt}`,
`/items/${item.id}`)
}
}} ref={titleRef} className={`${styles.title} text-reset me-2`}
>
{item.searchTitle ? <SearchTitle title={item.searchTitle} /> : item.title}
{item.pollCost && <span className={styles.icon}> <PollIcon className='fill-grey ms-1' height={14} width={14} /></span>}
{item.bounty > 0 &&

View File

@ -14,13 +14,17 @@ export function commentsViewedAfterComment (rootId, createdAt) {
window.localStorage.setItem(`${COMMENTS_NUM_PREFIX}:${rootId}`, existingRootComments + 1)
}
export function commentsViewedAt (item) {
return window.localStorage.getItem(`${COMMENTS_VIEW_PREFIX}:${item.id}`)
}
export function newComments (item) {
if (!item.parentId) {
const commentsViewedAt = window.localStorage.getItem(`${COMMENTS_VIEW_PREFIX}:${item.id}`)
const commentsViewNum = window.localStorage.getItem(`${COMMENTS_NUM_PREFIX}:${item.id}`)
const viewedAt = commentsViewedAt(item)
const viewNum = window.localStorage.getItem(`${COMMENTS_NUM_PREFIX}:${item.id}`)
if (commentsViewedAt && commentsViewNum) {
return commentsViewedAt < new Date(item.lastCommentAt).getTime() || commentsViewNum < item.ncomments
if (viewedAt && viewNum) {
return viewedAt < new Date(item.lastCommentAt).getTime() || viewNum < item.ncomments
}
}

View File

@ -688,6 +688,10 @@ div[contenteditable]:focus,
animation: outline 3s linear 1;
}
.outline-new-comment {
box-shadow: inset 0 0 1px 1px rgba(0, 123, 190, 0.35);
}
@keyframes spin {
0% {
transform: rotate(0deg);