diff --git a/src/routes/_components/profile/AccountDisplayName.html b/src/routes/_components/profile/AccountDisplayName.html
index 96165aa1..3dff1ff8 100644
--- a/src/routes/_components/profile/AccountDisplayName.html
+++ b/src/routes/_components/profile/AccountDisplayName.html
@@ -29,4 +29,4 @@
}
}
}
-
\ No newline at end of file
+
diff --git a/src/routes/_components/status/Status.html b/src/routes/_components/status/Status.html
index 10e53f42..d8929120 100644
--- a/src/routes/_components/status/Status.html
+++ b/src/routes/_components/status/Status.html
@@ -13,11 +13,11 @@
{#if !isStatusInOwnThread}
-
+
{/if}
{#if spoilerText}
-
+
{/if}
{#if !showContent}
@@ -35,9 +35,9 @@
{/if}
{#if isStatusInOwnThread}
-
+
{/if}
-
+
{#if replyShown}
{/if}
@@ -277,13 +277,15 @@
originalStatus.media_attachments.length
),
originalAccountEmojis: ({ originalAccount }) => (originalAccount.emojis || []),
+ originalStatusEmojis: ({ originalStatus }) => (originalStatus.emojis || []),
originalAccountDisplayName: ({ originalAccount }) => (originalAccount.display_name || originalAccount.username),
originalAccountAccessibleName: ({ originalAccount, $omitEmojiInDisplayNames }) => {
return getAccountAccessibleName(originalAccount, $omitEmojiInDisplayNames)
},
createdAtDate: ({ originalStatus }) => originalStatus.created_at,
- absoluteFormattedDate: ({ createdAtDate }) => absoluteDateFormatter.format(new Date(createdAtDate)),
- timeagoFormattedDate: ({ createdAtDate }) => formatTimeagoDate(createdAtDate),
+ createdAtDateTS: ({ createdAtDate }) => new Date(createdAtDate).getTime(),
+ absoluteFormattedDate: ({ createdAtDateTS }) => absoluteDateFormatter.format(createdAtDateTS),
+ timeagoFormattedDate: ({ createdAtDateTS, $now }) => formatTimeagoDate(createdAtDateTS, $now),
reblog: ({ status }) => status.reblog,
ariaLabel: ({ originalAccount, account, plainTextContent, timeagoFormattedDate, spoilerText,
showContent, reblog, notification, visibility, $omitEmojiInDisplayNames, $disableLongAriaLabels }) => (
@@ -307,11 +309,22 @@
)),
content: ({ originalStatus }) => originalStatus.content || '',
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,
account, accountId, uuid, isStatusInNotification, isStatusInOwnThread,
- originalAccount, originalAccountId, spoilerShown, visibility, replyShown,
+ originalAccount, originalAccountId, visibility,
replyVisibility, spoilerText, originalStatus, originalStatusId, inReplyToId,
- createdAtDate, timeagoFormattedDate, enableShortcuts, absoluteFormattedDate, shortcutScope }) => ({
+ enableShortcuts, shortcutScope, originalStatusEmojis }) => ({
notification,
notificationId,
status,
@@ -324,19 +337,15 @@
isStatusInOwnThread,
originalAccount,
originalAccountId,
- spoilerShown,
visibility,
- replyShown,
replyVisibility,
spoilerText,
originalStatus,
originalStatusId,
inReplyToId,
- createdAtDate,
- timeagoFormattedDate,
enableShortcuts,
- absoluteFormattedDate,
- shortcutScope
+ shortcutScope,
+ originalStatusEmojis
})
},
events: {
diff --git a/src/routes/_components/status/StatusContent.html b/src/routes/_components/status/StatusContent.html
index 1f2b1215..043c6da8 100644
--- a/src/routes/_components/status/StatusContent.html
+++ b/src/routes/_components/status/StatusContent.html
@@ -76,8 +76,9 @@
)
},
content: ({ originalStatus }) => (originalStatus.content || ''),
- emojis: ({ originalStatus }) => originalStatus.emojis,
- massagedContent: ({ content, emojis, $autoplayGifs }) => massageUserText(content, emojis, $autoplayGifs)
+ massagedContent: ({ content, originalStatusEmojis, $autoplayGifs }) => (
+ massageUserText(content, originalStatusEmojis, $autoplayGifs)
+ )
},
methods: {
hydrateContent () {
diff --git a/src/routes/_components/status/StatusDetails.html b/src/routes/_components/status/StatusDetails.html
index cc9cf953..d3c4b2a2 100644
--- a/src/routes/_components/status/StatusDetails.html
+++ b/src/routes/_components/status/StatusDetails.html
@@ -158,7 +158,6 @@
application: ({ originalStatus }) => originalStatus.application,
applicationName: ({ application }) => (application && application.name),
applicationWebsite: ({ application }) => (application && application.website),
- createdAtDate: ({ originalStatus }) => originalStatus.created_at,
numReblogs: ({ overrideNumReblogs, originalStatus }) => {
if (typeof overrideNumReblogs === 'number') {
return overrideNumReblogs
@@ -171,8 +170,8 @@
}
return originalStatus.favourites_count || 0
},
- displayAbsoluteFormattedDate: ({ createdAtDate, $isMobileSize }) => (
- $isMobileSize ? shortAbsoluteDateFormatter : absoluteDateFormatter).format(new Date(createdAtDate)
+ displayAbsoluteFormattedDate: ({ createdAtDateTS, $isMobileSize }) => (
+ ($isMobileSize ? shortAbsoluteDateFormatter : absoluteDateFormatter).format(createdAtDateTS)
),
reblogsLabel: ({ numReblogs }) => {
// TODO: intl
diff --git a/src/routes/_components/status/StatusSpoiler.html b/src/routes/_components/status/StatusSpoiler.html
index 3d54621d..acabf18c 100644
--- a/src/routes/_components/status/StatusSpoiler.html
+++ b/src/routes/_components/status/StatusSpoiler.html
@@ -64,10 +64,9 @@
Shortcut
},
computed: {
- emojis: ({ originalStatus }) => originalStatus.emojis,
- massagedSpoilerText: ({ spoilerText, emojis, $autoplayGifs }) => {
+ massagedSpoilerText: ({ spoilerText, originalStatusEmojis, $autoplayGifs }) => {
spoilerText = escapeHtml(spoilerText)
- return emojifyText(spoilerText, emojis, $autoplayGifs)
+ return emojifyText(spoilerText, originalStatusEmojis, $autoplayGifs)
},
elementId: ({ uuid }) => `spoiler-${uuid}`
},
diff --git a/src/routes/_intl/formatTimeagoDate.js b/src/routes/_intl/formatTimeagoDate.js
index ec63b7c3..0756e4b5 100644
--- a/src/routes/_intl/formatTimeagoDate.js
+++ b/src/routes/_intl/formatTimeagoDate.js
@@ -1,9 +1,10 @@
import { format } from '../_thirdparty/timeago/timeago'
import { mark, stop } from '../_utils/marks'
-export function formatTimeagoDate (date) {
+export function formatTimeagoDate (date, now) {
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')
return res
}
diff --git a/src/routes/_store/observers/nowObservers.js b/src/routes/_store/observers/nowObservers.js
new file mode 100644
index 00000000..df055e62
--- /dev/null
+++ b/src/routes/_store/observers/nowObservers.js
@@ -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()
+ }
+ })
+ }
+}
diff --git a/src/routes/_store/observers/observers.js b/src/routes/_store/observers/observers.js
index fe278cc4..ff8f6b1f 100644
--- a/src/routes/_store/observers/observers.js
+++ b/src/routes/_store/observers/observers.js
@@ -1,4 +1,5 @@
import { onlineObservers } from './onlineObservers'
+import { nowObservers } from './nowObservers'
import { navObservers } from './navObservers'
import { pageVisibilityObservers } from './pageVisibilityObservers'
import { resizeObservers } from './resizeObservers'
@@ -8,6 +9,7 @@ import { touchObservers } from './touchObservers'
export function observers (store) {
onlineObservers(store)
+ nowObservers(store)
navObservers(store)
pageVisibilityObservers(store)
resizeObservers(store)
diff --git a/src/routes/_thirdparty/timeago/timeago.js b/src/routes/_thirdparty/timeago/timeago.js
index db8fddd1..6a1ff1ef 100644
--- a/src/routes/_thirdparty/timeago/timeago.js
+++ b/src/routes/_thirdparty/timeago/timeago.js
@@ -4,7 +4,7 @@
* 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]
/**
@@ -63,16 +63,14 @@ function formatDiff (diff) {
* @param nowDate
* @returns {number}
*/
-function diffSec (date) {
- var nowDate = new Date()
- var otherDate = new Date(date)
- return (nowDate - otherDate) / 1000
+function diffSec (date, now) {
+ return (now - date) / 1000
}
/**
* Created by hustcc on 18/5/20.
* Contract: i@hust.cc
*/
-export function format (date) {
- return formatDiff(diffSec(date))
+export function format (date, now) {
+ return formatDiff(diffSec(date, now))
}