Replace maxComments in state w/loadingMoreComments

maxComments is now only the user-preferred setting, and has nothing to
do with displayed state. loadingMoreComments is used to trigger loading
more comments, and temporarily contains the desired comment count to
load. This should make future changes easier. Also fix two minor bugs:

 * the 'Reload' was sometimes hidden after clicking 'load more comments'
 * 'loaded x more comments' incorrectly appeared after clicking 'Reload'
This commit is contained in:
Christopher Gurnee 2022-03-09 16:15:41 +00:00
parent 09e6f9d11d
commit 0081278e3c
5 changed files with 47 additions and 43 deletions

View file

@ -75,7 +75,7 @@ export const getPost = async threadID => {
// retrieved. It should return as quickly as possible (scheduling time-taking work
// later), and may return false to cause getComments to exit early, or true otherwise.
export const getComments = async (callback, threadID, maxComments, after) => {
let chunks = Math.ceil(maxComments / chunkSize), comments, lastCreatedUtc = 1
let chunks = Math.floor(maxComments / chunkSize), comments, lastCreatedUtc = 1
while (true) {
let delay = 0

View file

@ -1,14 +1,14 @@
import React from 'react'
import {connect, getMaxComments, maxCommentsDefault} from '../../state'
import {connect, maxCommentsDefault} from '../../state'
let lastTotal
let loadingStarted = false, showLoadedCount = false, lastTotal, newComments
const loadMoreComments = (props, maxComments) => {
lastTotal = props.total
loadingStarted = true
props.global.loadMoreComments(maxComments)
}
const loadMore = (props) => {
const maxCommentsPreferred = getMaxComments()
const maxCommentsPreferred = props.global.maxComments
let loadElements
if (props.reloadingComments)
@ -24,8 +24,16 @@ const loadMore = (props) => {
if (maxCommentsPreferred >= maxCommentsDefault * 2)
loadElements.push(<a key='pref' onClick={() => loadMoreComments(props, maxCommentsPreferred)}>load {maxCommentsPreferred} more comments</a>)
}
if (lastTotal !== undefined) {
const newComments = props.total - lastTotal
if (loadingStarted) {
showLoadedCount = true
newComments = props.total - lastTotal
lastTotal = props.total
loadingStarted = false
} else if (lastTotal !== props.total) {
showLoadedCount = false
lastTotal = props.total
}
if (showLoadedCount) {
loadElements.push(<span key='loaded' className='fade'>
{newComments > 0 ? `loaded ${newComments} more comments` : 'no new comments found'}
</span>)

View file

@ -1,9 +1,9 @@
import React, { useState } from 'react'
import {connect, sort, filter, getMaxComments, saveMaxComments, minCommentsLimit, maxCommentsLimit} from '../../state'
import {connect, sort, filter, minCommentsLimit, maxCommentsLimit, constrainMaxComments} from '../../state'
const sortBy = props => {
const [maxCommentsField, setMaxCommentsField] = useState(props.global.state.maxComments)
const initSavedMaxComments = getMaxComments()
// The current value of the field; it'll be later saved after an onBlur event
const [maxCommentsField, setMaxCommentsField] = useState(props.global.maxComments)
const isFirefox = typeof InstallTrigger !== 'undefined'
let usedMouse;
@ -41,16 +41,16 @@ const sortBy = props => {
<input id='maxComments'
onKeyDown={e => e.key == "Enter" && e.target.blur()}
onChange= {e => setMaxCommentsField(parseInt(e.target.value))}
onBlur= {e => e.target.value = saveMaxComments(e.target.value)}
onBlur= {e => e.target.value = props.global.setMaxComments(e.target.value)}
{ ...(isFirefox ? {
onClick: e => e.target.focus() } : {}) }
defaultValue={initSavedMaxComments} type='number' maxLength='5' required
defaultValue={props.global.maxComments} type='number' maxLength='5' required
min={minCommentsLimit} max={maxCommentsLimit} step={minCommentsLimit} />
</span>
{ maxCommentsField > props.global.state.maxComments && props.global.state.maxComments < maxCommentsLimit && !props.reloadingComments &&
{ !props.loadedAllComments && !props.reloadingComments && constrainMaxComments(maxCommentsField) - minCommentsLimit >= props.total &&
<span className='nowrap'>
<span className='space' />
<input onClick={() => {props.global.loadMaxComments()}} type='button' value='Reload' />
<input onClick={() => props.global.loadMoreComments(props.global.maxComments - props.total)} type='button' value='Reload' />
</span> }
</div>
)

View file

@ -59,12 +59,6 @@ class Thread extends React.Component {
componentDidMount () {
const { subreddit, threadID } = this.props.match.params
let maxCommentsQuery = parseInt((new URLSearchParams(this.props.location.search)).get('max_comments'))
if (maxCommentsQuery > this.props.global.state.maxComments)
// Directly mutating the state is not recommended, but it's only done once and is probably OK here
this.props.global.state.maxComments = this.curMaxComments = constrainMaxComments(maxCommentsQuery)
else
this.curMaxComments = this.props.global.state.maxComments
this.props.global.setLoading('Loading post...')
// Get post from reddit
@ -111,16 +105,18 @@ class Thread extends React.Component {
this.props.global.setLoading('Loading comments from Pushshift...')
})
this.getComments(this.props.global.state.maxComments, 0)
const maxCommentsQuery = constrainMaxComments(
parseInt((new URLSearchParams(this.props.location.search)).get('max_comments')))
this.getComments(Math.max(this.props.global.maxComments, maxCommentsQuery), 0)
}
componentDidUpdate () {
const newCommentCount = this.props.global.state.maxComments - this.curMaxComments
if (newCommentCount > 0) {
this.curMaxComments = this.props.global.state.maxComments
const { loadingMoreComments } = this.props.global.state
if (loadingMoreComments) {
this.props.global.state.loadingMoreComments = 0
this.setState({reloadingComments: true})
this.props.global.setLoading('Loading more comments from Pushshift...')
this.getComments(newCommentCount, this.lastCreatedUtc - 1)
this.getComments(loadingMoreComments, this.lastCreatedUtc - 1)
}
}
@ -234,6 +230,7 @@ class Thread extends React.Component {
render () {
const { subreddit, id, author } = this.state.post
const { commentID } = this.props.match.params
const reloadingComments = this.state.reloadingComments || this.props.global.state.loadingMoreComments
const linkToRestOfComments = `/r/${subreddit}/comments/${id}/_/`
const isSingleComment = commentID !== undefined
@ -251,7 +248,9 @@ class Thread extends React.Component {
deleted={this.state.deleted.length}
/>
<SortBy
reloadingComments={this.state.reloadingComments}
loadedAllComments={this.state.loadedAllComments}
reloadingComments={reloadingComments}
total={this.state.pushshiftCommentLookup.size}
/>
{isSingleComment &&
<div className='view-rest-of-comment'>
@ -267,12 +266,12 @@ class Thread extends React.Component {
postAuthor={isDeleted(author) ? null : author}
commentFilter={this.props.global.state.commentFilter} // need to explicitly
commentSort={this.props.global.state.commentSort} // pass in these props
reloadingComments={this.state.reloadingComments} // to ensure React.memo
reloadingComments={reloadingComments} // to ensure React.memo
total={this.state.pushshiftCommentLookup.size} // works correctly
/>
<LoadMore
loadedAllComments={this.state.loadedAllComments}
reloadingComments={this.state.reloadingComments}
reloadingComments={reloadingComments}
total={this.state.pushshiftCommentLookup.size}
/>
</>

View file

@ -31,16 +31,6 @@ export const constrainMaxComments = maxComments => {
return maxComments
}
// Contrains, saves, and returns it, but does not change the state (does not load more comments)
export const saveMaxComments = maxComments => {
maxComments = constrainMaxComments(maxComments)
put(maxCommentsKey, maxComments)
return maxComments
}
// Gets the saved setting, regardless of the current state
export const getMaxComments = () => get(maxCommentsKey, maxCommentsDefault)
// Keys for localStorage
const sortKey = 'commentSort'
const filterKey = 'commentFilter'
@ -50,12 +40,15 @@ class GlobalState extends Container {
state = {
commentSort: get(sortKey, sort.top),
commentFilter: get(filterKey, filter.removedDeleted),
maxComments: get(maxCommentsKey, maxCommentsDefault),
loadingMoreComments: 0, // max # of comments to attempt to load next
statusText: '',
statusHelpUrl: undefined,
statusImage: undefined
}
// Preferred max # of comments to get during (re-)loads
maxComments = get(maxCommentsKey, maxCommentsDefault)
setCommentSort (sortType) {
put(sortKey, sortType)
this.setState({commentSort: sortType})
@ -66,11 +59,15 @@ class GlobalState extends Container {
this.setState({commentFilter: filterType})
}
// Sets the current state based on the saved setting (loads more comments)
loadMaxComments = () => this.setState({maxComments: getMaxComments()})
// Contrains, saves, and returns it (does not load more comments)
setMaxComments (maxComments) {
this.maxComments = constrainMaxComments(maxComments)
put(maxCommentsKey, this.maxComments)
return this.maxComments
}
// Sets the current state loading moreComments, ignoring the saved setting
loadMoreComments = moreComments => this.setState({maxComments: this.state.maxComments + moreComments})
// Loads more comments
loadMoreComments = loadingMoreComments => this.setState({loadingMoreComments})
setSuccess = () => {
this.setState({statusText: '', statusHelpUrl: undefined, statusImage: '/images/success.png'})