subtle highlight of new comments
This commit is contained in:
parent
ea1e31c6ee
commit
1efc17fcc2
|
@ -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
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue