You've already forked AstralRinth
forked from didirus/AstralRinth
fix: standardize relative timestamping (#3612)
* fix(frontend): relative timestamps are incorrectly rounded. Closes: #1371 * fix(all): remove legacy fromNow for proper relative timestamp creation Closes: #1395
This commit is contained in:
@@ -184,7 +184,7 @@
|
||||
"
|
||||
class="date"
|
||||
>
|
||||
{{ fromNow(notif.extra_data.version.date_published) }}
|
||||
{{ formatRelativeTime(notif.extra_data.version.date_published) }}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -201,7 +201,7 @@
|
||||
v-tooltip="$dayjs(notification.created).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="inline-flex"
|
||||
>
|
||||
<CalendarIcon class="mr-1" /> Received {{ fromNow(notification.created) }}
|
||||
<CalendarIcon class="mr-1" /> Received {{ formatRelativeTime(notification.created) }}
|
||||
</span>
|
||||
</span>
|
||||
<div v-if="compact" class="notification__actions">
|
||||
@@ -331,6 +331,7 @@ import {
|
||||
XIcon,
|
||||
ExternalIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { useRelativeTime } from "@modrinth/ui";
|
||||
import ThreadSummary from "~/components/ui/thread/ThreadSummary.vue";
|
||||
import { getProjectLink, getVersionLink } from "~/helpers/projects.js";
|
||||
import { getUserLink } from "~/helpers/users.js";
|
||||
@@ -345,6 +346,8 @@ import Categories from "~/components/ui/search/Categories.vue";
|
||||
const app = useNuxtApp();
|
||||
const emit = defineEmits(["update:notifications"]);
|
||||
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
const props = defineProps({
|
||||
notification: {
|
||||
type: Object,
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
class="stat date"
|
||||
>
|
||||
<UpdatedIcon aria-hidden="true" />
|
||||
<span class="date-label">Updated </span>{{ fromNow(updatedAt) }}
|
||||
<span class="date-label">Updated </span>{{ formatRelativeTime(updatedAt) }}
|
||||
</div>
|
||||
<div
|
||||
v-else-if="showCreatedDate"
|
||||
@@ -83,7 +83,7 @@
|
||||
class="stat date"
|
||||
>
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<span class="date-label">Published </span>{{ fromNow(createdAt) }}
|
||||
<span class="date-label">Published </span>{{ formatRelativeTime(createdAt) }}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
@@ -95,6 +95,7 @@ import Categories from "~/components/ui/search/Categories.vue";
|
||||
import Badge from "~/components/ui/Badge.vue";
|
||||
import EnvironmentIndicator from "~/components/ui/EnvironmentIndicator.vue";
|
||||
import Avatar from "~/components/ui/Avatar.vue";
|
||||
import { useRelativeTime } from "@modrinth/ui";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -213,8 +214,9 @@ export default {
|
||||
},
|
||||
setup() {
|
||||
const tags = useTags();
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
return { tags };
|
||||
return { tags, formatRelativeTime };
|
||||
},
|
||||
computed: {
|
||||
projectTypeDisplay() {
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
</nuxt-link>
|
||||
<span> </span>
|
||||
<span v-tooltip="$dayjs(report.created).format('MMMM D, YYYY [at] h:mm A')">{{
|
||||
fromNow(report.created)
|
||||
formatRelativeTime(report.created)
|
||||
}}</span>
|
||||
<CopyCode v-if="flags.developerMode" :text="report.id" class="report-id" />
|
||||
</div>
|
||||
@@ -105,11 +105,14 @@
|
||||
<script setup>
|
||||
import { ReportIcon, UnknownIcon, VersionIcon } from "@modrinth/assets";
|
||||
import { renderHighlightedString } from "~/helpers/highlight.js";
|
||||
import { useRelativeTime } from "@modrinth/ui";
|
||||
import Avatar from "~/components/ui/Avatar.vue";
|
||||
import Badge from "~/components/ui/Badge.vue";
|
||||
import ThreadSummary from "~/components/ui/thread/ThreadSummary.vue";
|
||||
import CopyCode from "~/components/ui/CopyCode.vue";
|
||||
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
defineProps({
|
||||
report: {
|
||||
type: Object,
|
||||
|
||||
@@ -3,6 +3,7 @@ import dayjs from "dayjs";
|
||||
import { ButtonStyled, commonMessages, CopyCode, ServerNotice, TagItem } from "@modrinth/ui";
|
||||
import { EditIcon, SettingsIcon, TrashIcon } from "@modrinth/assets";
|
||||
import { ServerNotice as ServerNoticeType } from "@modrinth/utils";
|
||||
import { useRelativeTime } from "@modrinth/ui";
|
||||
import {
|
||||
DISMISSABLE,
|
||||
getDismissableMetadata,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
import { useVIntl } from "@vintl/vintl";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
const props = defineProps<{
|
||||
notice: ServerNoticeType;
|
||||
@@ -25,7 +27,7 @@ const props = defineProps<{
|
||||
<div class="text-sm">
|
||||
<span v-if="notice.announce_at">
|
||||
{{ dayjs(notice.announce_at).format("MMM D, YYYY [at] h:mm A") }} ({{
|
||||
dayjs(notice.announce_at).fromNow()
|
||||
formatRelativeTime(notice.announce_at)
|
||||
}})
|
||||
</span>
|
||||
<template v-else> Never begins </template>
|
||||
@@ -35,7 +37,7 @@ const props = defineProps<{
|
||||
v-if="notice.expires"
|
||||
v-tooltip="dayjs(notice.expires).format('MMMM D, YYYY [at] h:mm A')"
|
||||
>
|
||||
{{ dayjs(notice.expires).fromNow() }}
|
||||
{{ formatRelativeTime(notice.expires) }}
|
||||
</span>
|
||||
<template v-else> Never expires </template>
|
||||
</div>
|
||||
|
||||
@@ -103,7 +103,7 @@ import {
|
||||
ModrinthIcon,
|
||||
ScaleIcon,
|
||||
} from "@modrinth/assets";
|
||||
import { AutoLink, OverflowMenu } from "@modrinth/ui";
|
||||
import { AutoLink, OverflowMenu, useRelativeTime } from "@modrinth/ui";
|
||||
import { renderString } from "@modrinth/utils";
|
||||
import Avatar from "~/components/ui/Avatar.vue";
|
||||
import Badge from "~/components/ui/Badge.vue";
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import dayjs from "dayjs";
|
||||
import relativeTime from "dayjs/plugin/relativeTime";
|
||||
|
||||
dayjs.extend(relativeTime); // eslint-disable-line import/no-named-as-default-member
|
||||
|
||||
export const useCurrentDate = () => useState("currentDate", () => Date.now());
|
||||
|
||||
export const updateCurrentDate = () => {
|
||||
const currentDate = useCurrentDate();
|
||||
|
||||
currentDate.value = Date.now();
|
||||
};
|
||||
|
||||
export const fromNow = (date) => {
|
||||
const currentDate = useCurrentDate();
|
||||
return dayjs(date).from(currentDate.value);
|
||||
};
|
||||
@@ -1,18 +0,0 @@
|
||||
import { createFormatter, type Formatter } from "@vintl/how-ago";
|
||||
import type { IntlController } from "@vintl/vintl/controller";
|
||||
|
||||
const formatters = new WeakMap<IntlController<any>, Formatter>();
|
||||
|
||||
export function useRelativeTime(): Formatter {
|
||||
const vintl = useVIntl();
|
||||
|
||||
let formatter = formatters.get(vintl);
|
||||
|
||||
if (formatter == null) {
|
||||
const formatterRef = computed(() => createFormatter(vintl.intl));
|
||||
formatter = (value, options) => formatterRef.value(value, options);
|
||||
formatters.set(vintl, formatter);
|
||||
}
|
||||
|
||||
return formatter;
|
||||
}
|
||||
@@ -871,6 +871,7 @@ import {
|
||||
ProjectSidebarDetails,
|
||||
ProjectSidebarLinks,
|
||||
ScrollablePanel,
|
||||
useRelativeTime,
|
||||
} from "@modrinth/ui";
|
||||
import VersionSummary from "@modrinth/ui/src/components/version/VersionSummary.vue";
|
||||
import { formatCategory, isRejected, isStaff, isUnderReview, renderString } from "@modrinth/utils";
|
||||
|
||||
@@ -92,7 +92,7 @@
|
||||
<div class="mb-4 mt-2 flex w-full items-center gap-1 text-sm text-secondary">
|
||||
{{ formatCategory(subscription.interval) }} ⋅ {{ subscription.status }} ⋅
|
||||
{{ dayjs(subscription.created).format("MMMM D, YYYY [at] h:mma") }} ({{
|
||||
dayjs(subscription.created).fromNow()
|
||||
formatRelativeTime(subscription.created)
|
||||
}})
|
||||
</div>
|
||||
</div>
|
||||
@@ -151,7 +151,7 @@
|
||||
</span>
|
||||
<span class="text-sm text-secondary">
|
||||
{{ dayjs(charge.due).format("MMMM D, YYYY [at] h:mma") }}
|
||||
<span class="text-secondary">({{ dayjs(charge.due).fromNow() }}) </span>
|
||||
<span class="text-secondary">({{ formatRelativeTime(charge.due) }}) </span>
|
||||
</span>
|
||||
<div
|
||||
v-if="flags.developerMode"
|
||||
@@ -196,7 +196,15 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Avatar, ButtonStyled, CopyCode, DropdownSelect, NewModal, Toggle } from "@modrinth/ui";
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
CopyCode,
|
||||
DropdownSelect,
|
||||
NewModal,
|
||||
Toggle,
|
||||
useRelativeTime,
|
||||
} from "@modrinth/ui";
|
||||
import { formatCategory, formatPrice } from "@modrinth/utils";
|
||||
import {
|
||||
CheckIcon,
|
||||
@@ -215,7 +223,9 @@ const flags = useFeatureFlags();
|
||||
const route = useRoute();
|
||||
const data = useNuxtApp();
|
||||
const vintl = useVIntl();
|
||||
|
||||
const { formatMessage } = vintl;
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
const messages = defineMessages({
|
||||
userNotFoundError: {
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<div class="text-sm">
|
||||
<span v-if="notice.announce_at">
|
||||
{{ dayjs(notice.announce_at).format("MMM D, YYYY [at] h:mm A") }} ({{
|
||||
dayjs(notice.announce_at).fromNow()
|
||||
formatRelativeTime(notice.announce_at)
|
||||
}})
|
||||
</span>
|
||||
<template v-else> Never begins </template>
|
||||
@@ -166,7 +166,7 @@
|
||||
v-if="notice.expires"
|
||||
v-tooltip="dayjs(notice.expires).format('MMMM D, YYYY [at] h:mm A')"
|
||||
>
|
||||
{{ dayjs(notice.expires).fromNow() }}
|
||||
{{ formatRelativeTime(notice.expires) }}
|
||||
</span>
|
||||
<template v-else> Never expires </template>
|
||||
</div>
|
||||
@@ -267,6 +267,7 @@ import {
|
||||
NewModal,
|
||||
TeleportDropdownMenu,
|
||||
Toggle,
|
||||
useRelativeTime,
|
||||
} from "@modrinth/ui";
|
||||
import { SettingsIcon, PlusIcon, SaveIcon, TrashIcon, EditIcon, XIcon } from "@modrinth/assets";
|
||||
import dayjs from "dayjs";
|
||||
@@ -278,6 +279,8 @@ import { usePyroFetch } from "~/composables/pyroFetch.ts";
|
||||
import AssignNoticeModal from "~/components/ui/servers/notice/AssignNoticeModal.vue";
|
||||
|
||||
const { formatMessage } = useVIntl();
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
const app = useNuxtApp() as unknown as { $notify: any };
|
||||
|
||||
const notices = ref<ServerNoticeType[]>([]);
|
||||
|
||||
@@ -391,6 +391,7 @@ import {
|
||||
DropdownSelect,
|
||||
FileInput,
|
||||
PopoutMenu,
|
||||
useRelativeTime,
|
||||
} from "@modrinth/ui";
|
||||
|
||||
import { isAdmin } from "@modrinth/utils";
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useRelativeTime } from "@modrinth/ui";
|
||||
|
||||
const vintl = useVIntl();
|
||||
const { formatMessage } = vintl;
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<span>
|
||||
Received
|
||||
{{ fromNow(notification.date_modified) }}
|
||||
{{ formatRelativeTime(notification.date_modified) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -527,7 +527,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { Multiselect } from "vue-multiselect";
|
||||
import { ButtonStyled } from "@modrinth/ui";
|
||||
import { ButtonStyled, useRelativeTime } from "@modrinth/ui";
|
||||
import {
|
||||
CompassIcon,
|
||||
LogInIcon,
|
||||
@@ -544,6 +544,8 @@ import ProjectCard from "~/components/ui/ProjectCard.vue";
|
||||
|
||||
import { homePageProjects, homePageSearch, homePageNotifs } from "~/generated/state.json";
|
||||
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
const searchQuery = ref("leave");
|
||||
const sortType = ref("relevance");
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<IssuesIcon v-if="project.age_warning" />
|
||||
Submitted
|
||||
<span v-tooltip="$dayjs(project.queued).format('MMMM D, YYYY [at] h:mm A')">{{
|
||||
fromNow(project.queued)
|
||||
formatRelativeTime(project.queued)
|
||||
}}</span>
|
||||
</span>
|
||||
<span v-else class="submitter-info"><UnknownIcon /> Unknown queue date</span>
|
||||
@@ -103,7 +103,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Chips } from "@modrinth/ui";
|
||||
import { Chips, useRelativeTime } from "@modrinth/ui";
|
||||
import {
|
||||
UnknownIcon,
|
||||
EyeIcon,
|
||||
@@ -128,6 +128,8 @@ const now = app.$dayjs();
|
||||
const TIME_24H = 86400000;
|
||||
const TIME_48H = TIME_24H * 2;
|
||||
|
||||
const formatRelativeTime = useRelativeTime();
|
||||
|
||||
const { data: projects } = await useAsyncData("moderation/projects?count=1000", () =>
|
||||
useBaseFetch("moderation/projects?count=1000", { internal: true }),
|
||||
);
|
||||
|
||||
@@ -203,7 +203,13 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { PlusIcon, XIcon, TrashIcon, EditIcon, SaveIcon } from "@modrinth/assets";
|
||||
import { Checkbox, ConfirmModal, commonSettingsMessages, commonMessages } from "@modrinth/ui";
|
||||
import {
|
||||
Checkbox,
|
||||
ConfirmModal,
|
||||
commonSettingsMessages,
|
||||
commonMessages,
|
||||
useRelativeTime,
|
||||
} from "@modrinth/ui";
|
||||
|
||||
import {
|
||||
hasScope,
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { XIcon } from "@modrinth/assets";
|
||||
import { commonMessages, commonSettingsMessages } from "@modrinth/ui";
|
||||
import { commonMessages, commonSettingsMessages, useRelativeTime } from "@modrinth/ui";
|
||||
|
||||
definePageMeta({
|
||||
middleware: "auth",
|
||||
|
||||
@@ -360,6 +360,7 @@ import {
|
||||
ContentPageHeader,
|
||||
commonMessages,
|
||||
NewModal,
|
||||
useRelativeTime,
|
||||
} from "@modrinth/ui";
|
||||
import { isStaff } from "~/helpers/users.js";
|
||||
import NavTabs from "~/components/ui/NavTabs.vue";
|
||||
|
||||
Reference in New Issue
Block a user