Skip to content

Commit 7a7775c

Browse files
committed
+ <RelativeTime> that extracted from <PostBadgeTimeView> to only invoking expensive DateTime.toRelative() moment/luxon#959 for components in viewport
* now will return the ref `timer[unit]` being bound * replace the null forgive operator for unmatched units with fallbacking to year unit * remove param `options` * renamed from `registerRelative()` @ `registerTimerDep()` @ `store/relativeTime.ts` - scoped style for `span` that being unused since 4d82a0a @ `<PostBadgeTimeView>` @ fe
1 parent ca674de commit 7a7775c

File tree

3 files changed

+46
-26
lines changed

3 files changed

+46
-26
lines changed

fe/src/components/RelativeTime.vue

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<template>
2+
<span :ref="el => relativeEl = (el as HTMLElement)">
3+
<template v-if="hydrationStore.isHydratingOrSSR || !relativeElIsVisible">
4+
{{ dateTimeInChina.toLocaleString({
5+
year: 'numeric',
6+
...keysWithSameValue(['month', 'day', 'hour', 'minute', 'second'], '2-digit')
7+
}) }}
8+
</template>
9+
<template v-else>
10+
{{ current.toRelative({ base: relativeTo, round: false }) }}
11+
</template>
12+
</span>
13+
</template>
14+
15+
<script setup lang="ts">
16+
import type { DateTime } from 'luxon';
17+
18+
const props = defineProps<{
19+
dateTime: DateTime<true>,
20+
relativeTo?: DateTime<true>
21+
}>();
22+
const hydrationStore = useHydrationStore();
23+
const relativeEl = ref<HTMLElement>();
24+
const relativeElIsVisible = ref(false);
25+
const dateTimeInChina = computed(() => setDateTimeZoneAndLocale()(props.dateTime));
26+
27+
const { pause, resume } = useIntersectionObserver(
28+
relativeEl,
29+
([{ isIntersecting }]) => {
30+
relativeElIsVisible.value = isIntersecting;
31+
}
32+
);
33+
watchEffect(() => {
34+
if (props.relativeTo === undefined)
35+
pause();
36+
else
37+
resume();
38+
});
39+
</script>

fe/src/components/post/badge/TimeView.vue

+1-19
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,7 @@
33
v-tippy="tippyContent" :datetime="currentInChina.toISO() ?? undefined"
44
class="ms-1 fw-normal badge rounded-pill user-select-all">
55
<component :is="$slots.default" />
6-
<template v-if="hydrationStore.isHydratingOrSSR">
7-
{{ currentInChina.toLocaleString({
8-
year: 'numeric',
9-
...keysWithSameValue(['month', 'day', 'hour', 'minute', 'second'], '2-digit')
10-
}) }}
11-
</template>
12-
<template v-else>
13-
{{ relativeTo === undefined
14-
? relativeTimeStore.registerRelative(current)
15-
: current.toRelative({ base: relativeTo, round: false }) }}
16-
</template>
6+
<RelativeTime :dateTime="current" :relativeTo="relativeTo" />
177
</time>
188
</template>
199

@@ -32,7 +22,6 @@ const props = defineProps<{
3222
: 'postedAt' extends TPostTimeKey ? '发帖时间' : never
3323
}>();
3424
const hydrationStore = useHydrationStore();
35-
const relativeTimeStore = useRelativeTimeStore();
3625
3726
const currentInChina = computed(() => setDateTimeZoneAndLocale()(props.current));
3827
const tippyContentRelativeTo = computed(() => {
@@ -72,10 +61,3 @@ const tippyContent = () => {
7261
${tippyContentRelativeTo.value}`;
7362
};
7463
</script>
75-
76-
<style scoped>
77-
span {
78-
padding-inline-start: .75rem;
79-
padding-inline-end: .75rem;
80-
}
81-
</style>

fe/src/stores/relativeTime.ts

+6-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ToRelativeOptions, ToRelativeUnit } from 'luxon';
1+
import type { ToRelativeUnit } from 'luxon';
22
import { DateTime, Duration } from 'luxon';
33

44
export const useRelativeTimeStore = defineStore('relativeTime', () => {
@@ -12,18 +12,17 @@ export const useRelativeTimeStore = defineStore('relativeTime', () => {
1212
);
1313
});
1414
}
15-
const registerRelative = (dateTime: DateTime, options?: ToRelativeOptions) => computed(() => {
15+
const registerTimerDep = (dateTime: DateTime) => computed(() => {
1616
const relativeDuration = dateTime
1717
.diff(DateTime.now(), undefined, { conversionAccuracy: 'longterm' })
1818
.shiftTo(...units);
19-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
2019
const { unit } = units
2120
.map(unit => ({ unit, value: relativeDuration.get(unit) }))
22-
.find(unit => unit.value !== 0)!;
23-
void timers[unit]; // track this computed ref as dependency of reactive timers[unit]
21+
.find(unit => unit.value !== 0)
22+
?? { unit: 'years' };
2423

25-
return dateTime.toRelative({ ...options, round: false });
24+
return timers[unit];
2625
});
2726

28-
return { timers, registerRelative };
27+
return { timers, registerTimerDep };
2928
});

0 commit comments

Comments
 (0)