fix: poll for updates to timeago displays (#1232)

* fix: poll for updates to timeago displays

* code cleanup

* avoid some recomputes

* avoid costly recomputes
This commit is contained in:
Nolan Lawson 2019-05-26 16:01:14 -07:00 committed by GitHub
parent bf640b9b0f
commit dac4b493c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 88 additions and 32 deletions

View File

@ -13,11 +13,11 @@
<StatusAuthorName {...params} /> <StatusAuthorName {...params} />
<StatusAuthorHandle {...params} /> <StatusAuthorHandle {...params} />
{#if !isStatusInOwnThread} {#if !isStatusInOwnThread}
<StatusRelativeDate {...params} /> <StatusRelativeDate {...params} {...timestampParams} />
{/if} {/if}
<StatusSidebar {...params} /> <StatusSidebar {...params} />
{#if spoilerText} {#if spoilerText}
<StatusSpoiler {...params} on:recalculateHeight /> <StatusSpoiler {...params} {spoilerShown} on:recalculateHeight />
{/if} {/if}
{#if !showContent} {#if !showContent}
<StatusMentions {...params} /> <StatusMentions {...params} />
@ -35,9 +35,9 @@
<StatusPoll {...params} /> <StatusPoll {...params} />
{/if} {/if}
{#if isStatusInOwnThread} {#if isStatusInOwnThread}
<StatusDetails {...params} /> <StatusDetails {...params} {...timestampParams} />
{/if} {/if}
<StatusToolbar {...params} on:recalculateHeight /> <StatusToolbar {...params} {replyShown} on:recalculateHeight />
{#if replyShown} {#if replyShown}
<StatusComposeBox {...params} on:recalculateHeight /> <StatusComposeBox {...params} on:recalculateHeight />
{/if} {/if}
@ -277,13 +277,15 @@
originalStatus.media_attachments.length originalStatus.media_attachments.length
), ),
originalAccountEmojis: ({ originalAccount }) => (originalAccount.emojis || []), originalAccountEmojis: ({ originalAccount }) => (originalAccount.emojis || []),
originalStatusEmojis: ({ originalStatus }) => (originalStatus.emojis || []),
originalAccountDisplayName: ({ originalAccount }) => (originalAccount.display_name || originalAccount.username), originalAccountDisplayName: ({ originalAccount }) => (originalAccount.display_name || originalAccount.username),
originalAccountAccessibleName: ({ originalAccount, $omitEmojiInDisplayNames }) => { originalAccountAccessibleName: ({ originalAccount, $omitEmojiInDisplayNames }) => {
return getAccountAccessibleName(originalAccount, $omitEmojiInDisplayNames) return getAccountAccessibleName(originalAccount, $omitEmojiInDisplayNames)
}, },
createdAtDate: ({ originalStatus }) => originalStatus.created_at, createdAtDate: ({ originalStatus }) => originalStatus.created_at,
absoluteFormattedDate: ({ createdAtDate }) => absoluteDateFormatter.format(new Date(createdAtDate)), createdAtDateTS: ({ createdAtDate }) => new Date(createdAtDate).getTime(),
timeagoFormattedDate: ({ createdAtDate }) => formatTimeagoDate(createdAtDate), absoluteFormattedDate: ({ createdAtDateTS }) => absoluteDateFormatter.format(createdAtDateTS),
timeagoFormattedDate: ({ createdAtDateTS, $now }) => formatTimeagoDate(createdAtDateTS, $now),
reblog: ({ status }) => status.reblog, reblog: ({ status }) => status.reblog,
ariaLabel: ({ originalAccount, account, plainTextContent, timeagoFormattedDate, spoilerText, ariaLabel: ({ originalAccount, account, plainTextContent, timeagoFormattedDate, spoilerText,
showContent, reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels }) => ( showContent, reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels }) => (
@ -307,11 +309,22 @@
)), )),
content: ({ originalStatus }) => originalStatus.content || '', content: ({ originalStatus }) => originalStatus.content || '',
showContent: ({ spoilerText, spoilerShown }) => !spoilerText || spoilerShown, showContent: ({ spoilerText, spoilerShown }) => !spoilerText || spoilerShown,
// These timestamp params may change every 10 seconds due to now() polling, so keep them
// separate from the generic `params` list to avoid costly recomputes.
timestampParams: ({ createdAtDate, createdAtDateTS, timeagoFormattedDate, absoluteFormattedDate }) => ({
createdAtDate,
createdAtDateTS,
timeagoFormattedDate,
absoluteFormattedDate
}),
// This params list deliberately does *not* include `spoilersShown` or `replyShown`, because these
// change frequently and would therefore cause costly recomputes if included here.
// The main goal here is to avoid typing by passing as many params as possible to child components.
params: ({ notification, notificationId, status, statusId, timelineType, params: ({ notification, notificationId, status, statusId, timelineType,
account, accountId, uuid, isStatusInNotification, isStatusInOwnThread, account, accountId, uuid, isStatusInNotification, isStatusInOwnThread,
originalAccount, originalAccountId, spoilerShown, visibility, replyShown, originalAccount, originalAccountId, visibility,
replyVisibility, spoilerText, originalStatus, originalStatusId, inReplyToId, replyVisibility, spoilerText, originalStatus, originalStatusId, inReplyToId,
createdAtDate, timeagoFormattedDate, enableShortcuts, absoluteFormattedDate, shortcutScope }) => ({ enableShortcuts, shortcutScope, originalStatusEmojis }) => ({
notification, notification,
notificationId, notificationId,
status, status,
@ -324,19 +337,15 @@
isStatusInOwnThread, isStatusInOwnThread,
originalAccount, originalAccount,
originalAccountId, originalAccountId,
spoilerShown,
visibility, visibility,
replyShown,
replyVisibility, replyVisibility,
spoilerText, spoilerText,
originalStatus, originalStatus,
originalStatusId, originalStatusId,
inReplyToId, inReplyToId,
createdAtDate,
timeagoFormattedDate,
enableShortcuts, enableShortcuts,
absoluteFormattedDate, shortcutScope,
shortcutScope originalStatusEmojis
}) })
}, },
events: { events: {

View File

@ -76,8 +76,9 @@
) )
}, },
content: ({ originalStatus }) => (originalStatus.content || ''), content: ({ originalStatus }) => (originalStatus.content || ''),
emojis: ({ originalStatus }) => originalStatus.emojis, massagedContent: ({ content, originalStatusEmojis, $autoplayGifs }) => (
massagedContent: ({ content, emojis, $autoplayGifs }) => massageUserText(content, emojis, $autoplayGifs) massageUserText(content, originalStatusEmojis, $autoplayGifs)
)
}, },
methods: { methods: {
hydrateContent () { hydrateContent () {

View File

@ -158,7 +158,6 @@
application: ({ originalStatus }) => originalStatus.application, application: ({ originalStatus }) => originalStatus.application,
applicationName: ({ application }) => (application && application.name), applicationName: ({ application }) => (application && application.name),
applicationWebsite: ({ application }) => (application && application.website), applicationWebsite: ({ application }) => (application && application.website),
createdAtDate: ({ originalStatus }) => originalStatus.created_at,
numReblogs: ({ overrideNumReblogs, originalStatus }) => { numReblogs: ({ overrideNumReblogs, originalStatus }) => {
if (typeof overrideNumReblogs === 'number') { if (typeof overrideNumReblogs === 'number') {
return overrideNumReblogs return overrideNumReblogs
@ -171,8 +170,8 @@
} }
return originalStatus.favourites_count || 0 return originalStatus.favourites_count || 0
}, },
displayAbsoluteFormattedDate: ({ createdAtDate, $isMobileSize }) => ( displayAbsoluteFormattedDate: ({ createdAtDateTS, $isMobileSize }) => (
$isMobileSize ? shortAbsoluteDateFormatter : absoluteDateFormatter).format(new Date(createdAtDate) ($isMobileSize ? shortAbsoluteDateFormatter : absoluteDateFormatter).format(createdAtDateTS)
), ),
reblogsLabel: ({ numReblogs }) => { reblogsLabel: ({ numReblogs }) => {
// TODO: intl // TODO: intl

View File

@ -64,10 +64,9 @@
Shortcut Shortcut
}, },
computed: { computed: {
emojis: ({ originalStatus }) => originalStatus.emojis, massagedSpoilerText: ({ spoilerText, originalStatusEmojis, $autoplayGifs }) => {
massagedSpoilerText: ({ spoilerText, emojis, $autoplayGifs }) => {
spoilerText = escapeHtml(spoilerText) spoilerText = escapeHtml(spoilerText)
return emojifyText(spoilerText, emojis, $autoplayGifs) return emojifyText(spoilerText, originalStatusEmojis, $autoplayGifs)
}, },
elementId: ({ uuid }) => `spoiler-${uuid}` elementId: ({ uuid }) => `spoiler-${uuid}`
}, },

View File

@ -1,9 +1,10 @@
import { format } from '../_thirdparty/timeago/timeago' import { format } from '../_thirdparty/timeago/timeago'
import { mark, stop } from '../_utils/marks' import { mark, stop } from '../_utils/marks'
export function formatTimeagoDate (date) { export function formatTimeagoDate (date, now) {
mark('formatTimeagoDate') mark('formatTimeagoDate')
let res = format(date) // use Math.max() to avoid things like "in 10 seconds" when the timestamps are slightly off
let res = format(date, Math.max(now, date))
stop('formatTimeagoDate') stop('formatTimeagoDate')
return res return res
} }

View File

@ -0,0 +1,47 @@
// For convenience, periodically re-compute the current time. This ensures freshness of
// displays like "x minutes ago" without having to jump through a lot of hoops.
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
import lifecycle from 'page-lifecycle/dist/lifecycle.mjs'
const POLL_INTERVAL = 10000
export function nowObservers (store) {
let interval
function updateNow () {
store.set({ now: Date.now() })
}
function startPolling () {
interval = setInterval(() => scheduleIdleTask(updateNow), POLL_INTERVAL)
}
function stopPolling () {
if (interval) {
clearInterval(interval)
interval = null
}
}
function restartPolling () {
stopPolling()
scheduleIdleTask(updateNow)
startPolling()
}
updateNow()
if (process.browser) {
startPolling()
lifecycle.addEventListener('statechange', e => {
if (e.newState === 'passive') {
console.log('stopping Date.now() observer...')
stopPolling()
} else if (e.newState === 'active') {
console.log('restarting Date.now() observer...')
restartPolling()
}
})
}
}

View File

@ -1,4 +1,5 @@
import { onlineObservers } from './onlineObservers' import { onlineObservers } from './onlineObservers'
import { nowObservers } from './nowObservers'
import { navObservers } from './navObservers' import { navObservers } from './navObservers'
import { pageVisibilityObservers } from './pageVisibilityObservers' import { pageVisibilityObservers } from './pageVisibilityObservers'
import { resizeObservers } from './resizeObservers' import { resizeObservers } from './resizeObservers'
@ -8,6 +9,7 @@ import { touchObservers } from './touchObservers'
export function observers (store) { export function observers (store) {
onlineObservers(store) onlineObservers(store)
nowObservers(store)
navObservers(store) navObservers(store)
pageVisibilityObservers(store) pageVisibilityObservers(store)
resizeObservers(store) resizeObservers(store)

View File

@ -4,7 +4,7 @@
* Contract: i@hust.cc * Contract: i@hust.cc
*/ */
var IndexMapEn = 'second_minute_hour_day_week_month_year'.split('_') var IndexMapEn = ['second', 'minute', 'hour', 'day', 'week', 'month', 'year']
var SEC_ARRAY = [60, 60, 24, 7, 365 / 7 / 12, 12] var SEC_ARRAY = [60, 60, 24, 7, 365 / 7 / 12, 12]
/** /**
@ -63,16 +63,14 @@ function formatDiff (diff) {
* @param nowDate * @param nowDate
* @returns {number} * @returns {number}
*/ */
function diffSec (date) { function diffSec (date, now) {
var nowDate = new Date() return (now - date) / 1000
var otherDate = new Date(date)
return (nowDate - otherDate) / 1000
} }
/** /**
* Created by hustcc on 18/5/20. * Created by hustcc on 18/5/20.
* Contract: i@hust.cc * Contract: i@hust.cc
*/ */
export function format (date) { export function format (date, now) {
return formatDiff(diffSec(date)) return formatDiff(diffSec(date, now))
} }