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:
parent
bf640b9b0f
commit
dac4b493c8
|
@ -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: {
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}`
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
47
src/routes/_store/observers/nowObservers.js
Normal file
47
src/routes/_store/observers/nowObservers.js
Normal 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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
12
src/routes/_thirdparty/timeago/timeago.js
vendored
12
src/routes/_thirdparty/timeago/timeago.js
vendored
|
@ -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))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue