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])
|
}, [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 bottomedOut = depth === COMMENT_DEPTH_LIMIT
|
||||||
const op = root.user.name === item.user.name
|
const op = root.user.name === item.user.name
|
||||||
const bountyPaid = root.bountyPaidTo?.includes(Number(item.id))
|
const bountyPaid = root.bountyPaidTo?.includes(Number(item.id))
|
||||||
@ -127,6 +135,7 @@ export default function Comment ({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref} className={includeParent ? '' : `${styles.comment} ${collapse === 'yep' ? styles.collapsed : ''}`}
|
ref={ref} className={includeParent ? '' : `${styles.comment} ${collapse === 'yep' ? styles.collapsed : ''}`}
|
||||||
|
onMouseEnter={() => ref.current.classList.remove('outline-new-comment')}
|
||||||
>
|
>
|
||||||
<div className={`${itemStyles.item} ${styles.item}`}>
|
<div className={`${itemStyles.item} ${styles.item}`}>
|
||||||
{item.meDontLike
|
{item.meDontLike
|
||||||
|
@ -5,7 +5,7 @@ import Badge from 'react-bootstrap/Badge'
|
|||||||
import Dropdown from 'react-bootstrap/Dropdown'
|
import Dropdown from 'react-bootstrap/Dropdown'
|
||||||
import Countdown from './countdown'
|
import Countdown from './countdown'
|
||||||
import { abbrNum } from '../lib/format'
|
import { abbrNum } from '../lib/format'
|
||||||
import { newComments } from '../lib/new-comments'
|
import { newComments, commentsViewedAt } from '../lib/new-comments'
|
||||||
import { timeSince } from '../lib/time'
|
import { timeSince } from '../lib/time'
|
||||||
import CowboyHat from './cowboy-hat'
|
import CowboyHat from './cowboy-hat'
|
||||||
import { DeleteDropdownItem } from './delete'
|
import { DeleteDropdownItem } from './delete'
|
||||||
@ -42,7 +42,17 @@ export default function ItemInfo ({ item, pendingSats, full, commentsText, class
|
|||||||
<span>{abbrNum(item.boost)} boost</span>
|
<span>{abbrNum(item.boost)} boost</span>
|
||||||
<span> \ </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'}
|
{item.ncomments} {commentsText || 'comments'}
|
||||||
{hasNewComments &&
|
{hasNewComments &&
|
||||||
<span className={styles.notification}>
|
<span className={styles.notification}>
|
||||||
|
@ -12,6 +12,8 @@ import Flag from '../svgs/flag-fill.svg'
|
|||||||
import ImageIcon from '../svgs/image-fill.svg'
|
import ImageIcon from '../svgs/image-fill.svg'
|
||||||
import { abbrNum } from '../lib/format'
|
import { abbrNum } from '../lib/format'
|
||||||
import ItemInfo from './item-info'
|
import ItemInfo from './item-info'
|
||||||
|
import { commentsViewedAt } from '../lib/new-comments'
|
||||||
|
import { useRouter } from 'next/router'
|
||||||
|
|
||||||
export function SearchTitle ({ title }) {
|
export function SearchTitle ({ title }) {
|
||||||
return reactStringReplace(title, /:high\[([^\]]+)\]/g, (match, i) => {
|
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 }) {
|
export default function Item ({ item, rank, belowTitle, right, full, children, siblingComments }) {
|
||||||
const titleRef = useRef()
|
const titleRef = useRef()
|
||||||
|
const router = useRouter()
|
||||||
const [pendingSats, setPendingSats] = useState(0)
|
const [pendingSats, setPendingSats] = useState(0)
|
||||||
|
|
||||||
const image = item.url && item.url.startsWith(process.env.NEXT_PUBLIC_IMGPROXY_URL)
|
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} />}
|
: 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.hunk}>
|
||||||
<div className={`${styles.main} flex-wrap`}>
|
<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.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.pollCost && <span className={styles.icon}> <PollIcon className='fill-grey ms-1' height={14} width={14} /></span>}
|
||||||
{item.bounty > 0 &&
|
{item.bounty > 0 &&
|
||||||
|
@ -14,13 +14,17 @@ export function commentsViewedAfterComment (rootId, createdAt) {
|
|||||||
window.localStorage.setItem(`${COMMENTS_NUM_PREFIX}:${rootId}`, existingRootComments + 1)
|
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) {
|
export function newComments (item) {
|
||||||
if (!item.parentId) {
|
if (!item.parentId) {
|
||||||
const commentsViewedAt = window.localStorage.getItem(`${COMMENTS_VIEW_PREFIX}:${item.id}`)
|
const viewedAt = commentsViewedAt(item)
|
||||||
const commentsViewNum = window.localStorage.getItem(`${COMMENTS_NUM_PREFIX}:${item.id}`)
|
const viewNum = window.localStorage.getItem(`${COMMENTS_NUM_PREFIX}:${item.id}`)
|
||||||
|
|
||||||
if (commentsViewedAt && commentsViewNum) {
|
if (viewedAt && viewNum) {
|
||||||
return commentsViewedAt < new Date(item.lastCommentAt).getTime() || commentsViewNum < item.ncomments
|
return viewedAt < new Date(item.lastCommentAt).getTime() || viewNum < item.ncomments
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,6 +688,10 @@ div[contenteditable]:focus,
|
|||||||
animation: outline 3s linear 1;
|
animation: outline 3s linear 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.outline-new-comment {
|
||||||
|
box-shadow: inset 0 0 1px 1px rgba(0, 123, 190, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user