inform user when there are new comments

This commit is contained in:
keyan 2022-09-02 11:53:44 -05:00
parent 0f5fc31803
commit 297270f34d
6 changed files with 64 additions and 4 deletions

View File

@ -129,6 +129,9 @@ export default function Header ({ sub }) {
} }
} }
const showJobIndicator = sub !== 'jobs' && (!me || me.noteJobIndicator) &&
(!lastCheckedJobs || lastCheckedJobs < subLatestPost?.subLatestPost)
const NavItems = ({ className }) => { const NavItems = ({ className }) => {
return ( return (
<> <>
@ -150,7 +153,7 @@ export default function Header ({ sub }) {
jobs jobs
</Nav.Link> </Nav.Link>
</Link> </Link>
{sub !== 'jobs' && (!me || me.noteJobIndicator) && (!lastCheckedJobs || lastCheckedJobs < subLatestPost?.subLatestPost) && {showJobIndicator &&
<span className={styles.jobIndicator}> <span className={styles.jobIndicator}>
<span className='invisible'>{' '}</span> <span className='invisible'>{' '}</span>
</span>} </span>}

View File

@ -11,8 +11,9 @@ import { Button } from 'react-bootstrap'
import { TwitterTweetEmbed } from 'react-twitter-embed' import { TwitterTweetEmbed } from 'react-twitter-embed'
import YouTube from 'react-youtube' import YouTube from 'react-youtube'
import useDarkMode from 'use-dark-mode' import useDarkMode from 'use-dark-mode'
import { useState } from 'react' import { useEffect, useState } from 'react'
import Poll from './poll' import Poll from './poll'
import { commentsViewed } from '../lib/new-comments'
function BioItem ({ item, handleClick }) { function BioItem ({ item, handleClick }) {
const me = useMe() const me = useMe()
@ -99,6 +100,10 @@ function ItemText ({ item }) {
} }
export default function ItemFull ({ item, bio, ...props }) { export default function ItemFull ({ item, bio, ...props }) {
useEffect(() => {
commentsViewed(item)
}, [item.lastCommentAt])
return ( return (
<> <>
{item.parentId {item.parentId

View File

@ -9,6 +9,8 @@ import Pin from '../svgs/pushpin-fill.svg'
import reactStringReplace from 'react-string-replace' import reactStringReplace from 'react-string-replace'
import Toc from './table-of-contents' import Toc from './table-of-contents'
import PollIcon from '../svgs/bar-chart-horizontal-fill.svg' import PollIcon from '../svgs/bar-chart-horizontal-fill.svg'
import { Badge } from 'react-bootstrap'
import { newComments } from '../lib/new-comments'
export function SearchTitle ({ title }) { export function SearchTitle ({ title }) {
return reactStringReplace(title, /:high\[([^\]]+)\]/g, (match, i) => { return reactStringReplace(title, /:high\[([^\]]+)\]/g, (match, i) => {
@ -34,6 +36,7 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
useState(mine && (Date.now() < editThreshold)) useState(mine && (Date.now() < editThreshold))
const [wrap, setWrap] = useState(false) const [wrap, setWrap] = useState(false)
const titleRef = useRef() const titleRef = useRef()
const [hasNewComments, setHasNewComments] = useState(false)
useEffect(() => { useEffect(() => {
setWrap( setWrap(
@ -41,6 +44,11 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
titleRef.current.clientHeight) titleRef.current.clientHeight)
}, []) }, [])
useEffect(() => {
// if we are showing toc, then this is a full item
setHasNewComments(!toc && newComments(item))
}, [item])
return ( return (
<> <>
{rank {rank
@ -82,7 +90,10 @@ export default function Item ({ item, rank, showFwdUser, toc, children }) {
<span> \ </span> <span> \ </span>
</>} </>}
<Link href={`/items/${item.id}`} passHref> <Link href={`/items/${item.id}`} passHref>
<a title={`${item.commentSats} sats`} className='text-reset'>{item.ncomments} comments</a> <a title={`${item.commentSats} sats`} className='text-reset'>
{item.ncomments} comments
{hasNewComments && <>{' '}<Badge className={styles.newComment} variant={null}>new</Badge></>}
</a>
</Link> </Link>
<span> \ </span> <span> \ </span>
<span> <span>

View File

@ -20,6 +20,11 @@ a.title:visited {
flex: 1 0 128px; flex: 1 0 128px;
} }
.newComment {
color: var(--theme-grey) !important;
background: var(--theme-clickToContextColor) !important;
}
.pin { .pin {
fill: #a5a5a5; fill: #a5a5a5;
margin-right: .2rem; margin-right: .2rem;

View File

@ -8,6 +8,7 @@ import TextareaAutosize from 'react-textarea-autosize'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import Link from 'next/link' import Link from 'next/link'
import FeeButton from './fee-button' import FeeButton from './fee-button'
import { commentsViewedAfterComment } from '../lib/new-comments'
export const CommentSchema = Yup.object({ export const CommentSchema = Yup.object({
text: Yup.string().required('required').trim() text: Yup.string().required('required').trim()
@ -57,8 +58,10 @@ export default function Reply ({ item, onSuccess, replyOpen }) {
} }
}) })
const ancestors = item.path.split('.')
// update all ancestors // update all ancestors
item.path.split('.').forEach(id => { ancestors.forEach(id => {
cache.modify({ cache.modify({
id: `Item:${id}`, id: `Item:${id}`,
fields: { fields: {
@ -68,6 +71,11 @@ export default function Reply ({ item, onSuccess, replyOpen }) {
} }
}) })
}) })
// so that we don't see indicator for our own comments, we record this comments as the latest time
// but we also have record num comments, in case someone else commented when we did
const root = ancestors[0]
commentsViewedAfterComment(root, createComment.createdAt)
} }
} }
) )

28
lib/new-comments.js Normal file
View File

@ -0,0 +1,28 @@
const COMMENTS_VIEW_PREFIX = 'commentsViewedAt'
const COMMENTS_NUM_PREFIX = 'commentsViewNum'
export function commentsViewed (item) {
if (!item.parentId && item.lastCommentAt) {
localStorage.setItem(`${COMMENTS_VIEW_PREFIX}:${item.id}`, new Date(item.lastCommentAt).getTime())
localStorage.setItem(`${COMMENTS_NUM_PREFIX}:${item.id}`, item.ncomments)
}
}
export function commentsViewedAfterComment (rootId, createdAt) {
localStorage.setItem(`${COMMENTS_VIEW_PREFIX}:${rootId}`, new Date(createdAt).getTime())
const existingRootComments = localStorage.getItem(`${COMMENTS_NUM_PREFIX}:${rootId}`) || 0
localStorage.setItem(`${COMMENTS_NUM_PREFIX}:${rootId}`, existingRootComments + 1)
}
export function newComments (item) {
if (!item.parentId) {
const commentsViewedAt = localStorage.getItem(`${COMMENTS_VIEW_PREFIX}:${item.id}`)
const commentsViewNum = localStorage.getItem(`${COMMENTS_NUM_PREFIX}:${item.id}`)
if (commentsViewedAt && commentsViewNum) {
return commentsViewedAt < new Date(item.lastCommentAt).getTime() || commentsViewNum < item.ncomments
}
}
return false
}