fix(frontend): auto-refresh relative dates as time passes
Relative dates ("5 minutes ago", "in 2 hours") were computed once via
dayjs().fromNow() and never recomputed, so a view left open kept showing
the value from the moment it was rendered.
Compute the relative string against the shared, ticking `now` from
useGlobalNow() instead. This makes every reactive caller — <TimeDisplay>,
direct formatDateSince() calls, and formatDisplayDate() when the user's
date display is set to relative — re-render on the existing 60s tick.
Absolute date formats don't read `now`, so they never needlessly
re-render.
useGlobalNow can now be initialised from a plain helper rather than only
from a component, so its route-update hook is guarded with
getCurrentInstance().
This commit is contained in:
parent
55ca06ca3d
commit
40156d0b60
|
|
@ -1,4 +1,4 @@
|
||||||
import { ref } from 'vue'
|
import { getCurrentInstance, ref } from 'vue'
|
||||||
import { createGlobalState, useIntervalFn } from '@vueuse/core'
|
import { createGlobalState, useIntervalFn } from '@vueuse/core'
|
||||||
import { onBeforeRouteUpdate } from 'vue-router'
|
import { onBeforeRouteUpdate } from 'vue-router'
|
||||||
|
|
||||||
|
|
@ -18,10 +18,14 @@ export const useGlobalNow = createGlobalState(() => {
|
||||||
|
|
||||||
useIntervalFn(update, GLOBAL_NOW_INTERVAL, { immediate: true })
|
useIntervalFn(update, GLOBAL_NOW_INTERVAL, { immediate: true })
|
||||||
|
|
||||||
// ensure the now value is refreshed when the route changes
|
// Now that this state can be initialised from a plain helper (formatDateSince), the
|
||||||
onBeforeRouteUpdate(() => {
|
// first caller is not guaranteed to be a component — guard the route hook accordingly.
|
||||||
update()
|
if (getCurrentInstance()) {
|
||||||
})
|
// ensure the now value is refreshed when the route changes
|
||||||
|
onBeforeRouteUpdate(() => {
|
||||||
|
update()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
now,
|
now,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {i18n} from '@/i18n'
|
||||||
import {createSharedComposable} from '@vueuse/core'
|
import {createSharedComposable} from '@vueuse/core'
|
||||||
import {computed, toValue, type MaybeRefOrGetter} from 'vue'
|
import {computed, toValue, type MaybeRefOrGetter} from 'vue'
|
||||||
import {useDateDisplay} from '@/composables/useDateDisplay'
|
import {useDateDisplay} from '@/composables/useDateDisplay'
|
||||||
|
import {useGlobalNow} from '@/composables/useGlobalNow'
|
||||||
import {useTimeFormat} from '@/composables/useTimeFormat'
|
import {useTimeFormat} from '@/composables/useTimeFormat'
|
||||||
import {DATE_DISPLAY, type DateDisplay} from '@/constants/dateDisplay'
|
import {DATE_DISPLAY, type DateDisplay} from '@/constants/dateDisplay'
|
||||||
import {TIME_FORMAT, type TimeFormat} from '@/constants/timeFormat'
|
import {TIME_FORMAT, type TimeFormat} from '@/constants/timeFormat'
|
||||||
|
|
@ -49,8 +50,13 @@ export const formatDateSince = (date: Date | string | null) => {
|
||||||
|
|
||||||
const locale = DAYJS_LOCALE_MAPPING[i18n.global.locale.value.toLowerCase()] ?? 'en'
|
const locale = DAYJS_LOCALE_MAPPING[i18n.global.locale.value.toLowerCase()] ?? 'en'
|
||||||
|
|
||||||
|
// Computing the relative string against the shared, ticking `now` (instead of fromNow's
|
||||||
|
// internal Date.now()) makes every reactive caller re-render on the 60s tick, so open views
|
||||||
|
// don't keep showing a stale "x minutes ago".
|
||||||
|
const {now} = useGlobalNow()
|
||||||
|
|
||||||
return date
|
return date
|
||||||
? dayjs(date).locale(locale).fromNow()
|
? dayjs(date).locale(locale).from(now.value)
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue