Impove Intl formatting (#5372)
* Improve Intl formatting * Additional fixes * Fixed formatters were not updated on locale change * Fixed formatNumber was not updated on locale change * Additional formatting and fixes after merge * Run prepr:frontend * Remove `'` in icon map * Run `pnpm install` * fix: lint + import * Additional fixes --------- Co-authored-by: Calum H. <calum@modrinth.com> Co-authored-by: Calum H. (IMB11) <contact@cal.engineer>
This commit is contained in:
@@ -178,12 +178,7 @@
|
||||
class="categories"
|
||||
/>
|
||||
{{ $formatVersion(notif.extra_data.version.game_versions) }}
|
||||
<span
|
||||
v-tooltip="
|
||||
$dayjs(notif.extra_data.version.date_published).format('MMMM D, YYYY [at] h:mm A')
|
||||
"
|
||||
class="date"
|
||||
>
|
||||
<span v-tooltip="formatDateTime(notif.extra_data.version.date_published)" class="date">
|
||||
{{ formatRelativeTime(notif.extra_data.version.date_published) }}
|
||||
</span>
|
||||
</span>
|
||||
@@ -197,10 +192,7 @@
|
||||
<span v-if="notification.read" class="read-badge inline-flex">
|
||||
<CheckCircleIcon /> Read
|
||||
</span>
|
||||
<span
|
||||
v-tooltip="$dayjs(notification.created).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="inline-flex"
|
||||
>
|
||||
<span v-tooltip="formatDateTime(notification.created)" class="inline-flex">
|
||||
<CalendarIcon class="mr-1" /> Received
|
||||
{{ formatRelativeTime(notification.created) }}
|
||||
</span>
|
||||
@@ -338,6 +330,7 @@ import {
|
||||
DoubleIcon,
|
||||
injectNotificationManager,
|
||||
ProjectStatusBadge,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
} from '@modrinth/ui'
|
||||
import { getUserLink, renderString } from '@modrinth/utils'
|
||||
@@ -351,6 +344,10 @@ import ThreadSummary from './thread/ThreadSummary.vue'
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const emit = defineEmits(['update:notifications'])
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
notification: {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<script setup lang="ts">
|
||||
import { FileTextIcon } from '@modrinth/assets'
|
||||
import { ButtonStyled, defineMessages, PagewideBanner, useVIntl } from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import {
|
||||
ButtonStyled,
|
||||
defineMessages,
|
||||
PagewideBanner,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { getTaxThreshold } from '@/providers/creator-withdraw.ts'
|
||||
@@ -9,6 +14,7 @@ import CreatorTaxFormModal from '~/components/ui/dashboard/CreatorTaxFormModal.v
|
||||
import { useGeneratedState } from '~/composables/generated'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
|
||||
const generatedState = useGeneratedState()
|
||||
const taxThreshold = computed(() => getTaxThreshold(generatedState.value?.taxComplianceThresholds))
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup>
|
||||
import { formatMoney, formatNumber } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { useFormatDateTime, useFormatMoney, useFormatNumber } from '@modrinth/ui'
|
||||
import VueApexCharts from 'vue3-apexcharts'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -18,7 +17,6 @@ const props = defineProps({
|
||||
},
|
||||
formatLabels: {
|
||||
type: Function,
|
||||
default: (label) => dayjs(label).format('MMM D'),
|
||||
},
|
||||
colors: {
|
||||
type: Array,
|
||||
@@ -78,8 +76,15 @@ const props = defineProps({
|
||||
},
|
||||
})
|
||||
|
||||
const formatNumber = useFormatNumber()
|
||||
const formatMoney = useFormatMoney()
|
||||
const formatDate = useFormatDateTime({
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})
|
||||
|
||||
function formatTooltipValue(value, props) {
|
||||
return props.isMoney ? formatMoney(value, false) : formatNumber(value, false)
|
||||
return props.isMoney ? formatMoney(value) : formatNumber(value)
|
||||
}
|
||||
|
||||
function generateListEntry(value, index, _, w, props) {
|
||||
@@ -99,7 +104,7 @@ function generateListEntry(value, index, _, w, props) {
|
||||
function generateTooltip({ series, seriesIndex, dataPointIndex, w }, props) {
|
||||
const label = w.globals.lastXAxis.categories?.[dataPointIndex]
|
||||
|
||||
const formattedLabel = props.formatLabels(label)
|
||||
const formattedLabel = props.formatLabels ? props.formatLabels(label) : formatDate(label)
|
||||
|
||||
let tooltip = `<div class="bar-tooltip">
|
||||
<div class="seperated-entry title">
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
ref="tinyDownloadChart"
|
||||
:title="`Downloads`"
|
||||
color="var(--color-brand)"
|
||||
:value="formatNumber(analytics.formattedData.value.downloads.sum, false)"
|
||||
:value="formatNumber(analytics.formattedData.value.downloads.sum)"
|
||||
:data="analytics.formattedData.value.downloads.chart.sumData"
|
||||
:labels="analytics.formattedData.value.downloads.chart.labels"
|
||||
suffix="<svg xmlns='http://www.w3.org/2000/svg' class='h-6 w-6' fill='none' viewBox='0 0 24 24' stroke='currentColor' stroke-width='2'><path stroke-linecap='round' stroke-linejoin='round' d='M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4' /></svg>"
|
||||
@@ -40,7 +40,7 @@
|
||||
ref="tinyViewChart"
|
||||
:title="`Views`"
|
||||
color="var(--color-blue)"
|
||||
:value="formatNumber(analytics.formattedData.value.views.sum, false)"
|
||||
:value="formatNumber(analytics.formattedData.value.views.sum)"
|
||||
:data="analytics.formattedData.value.views.chart.sumData"
|
||||
:labels="analytics.formattedData.value.views.chart.labels"
|
||||
suffix="<svg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'><path d='M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z'/><circle cx='12' cy='12' r='3'/></svg>"
|
||||
@@ -57,7 +57,7 @@
|
||||
ref="tinyRevenueChart"
|
||||
:title="`Revenue`"
|
||||
color="var(--color-purple)"
|
||||
:value="formatMoney(analytics.formattedData.value.revenue.sum, false)"
|
||||
:value="formatMoney(analytics.formattedData.value.revenue.sum)"
|
||||
:data="analytics.formattedData.value.revenue.chart.sumData"
|
||||
:labels="analytics.formattedData.value.revenue.chart.labels"
|
||||
is-money
|
||||
@@ -221,7 +221,7 @@
|
||||
><template v-if="name.toLowerCase() === 'xx' || !name">Other</template>
|
||||
<template v-else>{{ countryCodeToName(name) }}</template>
|
||||
</strong>
|
||||
<span class="data-point">{{ formatNumber(count) }}</span>
|
||||
<span class="data-point">{{ formatCompactNumber(count) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="
|
||||
@@ -280,7 +280,7 @@
|
||||
<template v-if="name.toLowerCase() === 'xx' || !name">Other</template>
|
||||
<template v-else>{{ countryCodeToName(name) }}</template>
|
||||
</strong>
|
||||
<span class="data-point">{{ formatNumber(count) }}</span>
|
||||
<span class="data-point">{{ formatCompactNumber(count) }}</span>
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="
|
||||
@@ -310,8 +310,15 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DownloadIcon, PaletteIcon, UpdatedIcon } from '@modrinth/assets'
|
||||
import { Button, Card, DropdownSelect } from '@modrinth/ui'
|
||||
import { capitalizeString, formatMoney, formatNumber } from '@modrinth/utils'
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
DropdownSelect,
|
||||
useCompactNumber,
|
||||
useFormatMoney,
|
||||
useFormatNumber,
|
||||
} from '@modrinth/ui'
|
||||
import { capitalizeString } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -325,6 +332,10 @@ import {
|
||||
intToRgba,
|
||||
} from '~/utils/analytics.js'
|
||||
|
||||
const formatNumber = useFormatNumber()
|
||||
const { formatCompactNumber } = useCompactNumber()
|
||||
const formatMoney = useFormatMoney()
|
||||
|
||||
const router = useNativeRouter()
|
||||
const theme = useTheme()
|
||||
|
||||
|
||||
@@ -55,9 +55,9 @@ import {
|
||||
commonMessages,
|
||||
formFieldPlaceholders,
|
||||
StyledInput,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -82,6 +82,7 @@ const emit = defineEmits<{
|
||||
}>()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const amountInput = ref<InstanceType<typeof StyledInput> | null>(null)
|
||||
|
||||
const safeMaxAmount = computed(() => {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
>{{ formatTransactionStatus(transaction.status) }} <BulletDivider
|
||||
/></span>
|
||||
</template>
|
||||
{{ dayjs(transaction.created).format('MMM DD YYYY') }}
|
||||
{{ formatDate(transaction.created) }}
|
||||
<template v-if="transaction.type === 'withdrawal' && transaction.fee">
|
||||
<BulletDivider /> Fee {{ formatMoney(transaction.fee) }}
|
||||
</template>
|
||||
@@ -79,10 +79,11 @@ import {
|
||||
ButtonStyled,
|
||||
getCurrencyIcon,
|
||||
injectNotificationManager,
|
||||
useFormatDateTime,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { capitalizeString, formatMoney } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { capitalizeString } from '@modrinth/utils'
|
||||
import { Tooltip } from 'floating-vue'
|
||||
|
||||
import { useGeneratedState } from '~/composables/generated'
|
||||
@@ -188,6 +189,8 @@ function formatTransactionStatus(status: string): string {
|
||||
}
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const formatDate = useFormatDateTime({ dateStyle: 'medium' })
|
||||
|
||||
function formatMethodName(method: string | undefined, method_id: string | undefined): string {
|
||||
if (!method) return 'Unknown'
|
||||
|
||||
@@ -57,8 +57,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { LoaderCircleIcon } from '@modrinth/assets'
|
||||
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { defineMessages, useFormatMoney, useVIntl } from '@modrinth/ui'
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
@@ -78,6 +77,7 @@ const props = withDefaults(
|
||||
)
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
|
||||
const amountInUsd = computed(() => {
|
||||
if (props.isGiftCard && shouldShowExchangeRate.value) {
|
||||
@@ -119,31 +119,13 @@ const formattedLocalCurrency = computed(() => {
|
||||
if (!shouldShowExchangeRate.value || !netAmountInLocalCurrency.value || !props.localCurrency)
|
||||
return ''
|
||||
|
||||
try {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: props.localCurrency,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(netAmountInLocalCurrency.value)
|
||||
} catch {
|
||||
return `${props.localCurrency} ${netAmountInLocalCurrency.value.toFixed(2)}`
|
||||
}
|
||||
return formatMoney(netAmountInLocalCurrency.value, props.localCurrency)
|
||||
})
|
||||
|
||||
const formattedLocalCurrencyAmount = computed(() => {
|
||||
if (!shouldShowExchangeRate.value || !localCurrencyAmount.value || !props.localCurrency) return ''
|
||||
|
||||
try {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: props.localCurrency,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(localCurrencyAmount.value)
|
||||
} catch {
|
||||
return `${props.localCurrency} ${localCurrencyAmount.value.toFixed(2)}`
|
||||
}
|
||||
return formatMoney(localCurrencyAmount.value, props.localCurrency)
|
||||
})
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -124,9 +124,14 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { defineMessages, IntlFormatted, normalizeChildren, useVIntl } from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import {
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
normalizeChildren,
|
||||
useFormatDateTime,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import ConfettiExplosion from 'vue-confetti-explosion'
|
||||
|
||||
@@ -135,6 +140,8 @@ import { getRailConfig } from '@/utils/muralpay-rails'
|
||||
|
||||
const { withdrawData } = useWithdrawContext()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const formatDate = useFormatDateTime({ dateStyle: 'long' })
|
||||
|
||||
const result = computed(() => withdrawData.value.result)
|
||||
|
||||
@@ -149,7 +156,7 @@ onMounted(() => {
|
||||
|
||||
const formattedDate = computed(() => {
|
||||
if (!result.value?.created) return 'N/A'
|
||||
return dayjs(result.value.created).format('MMMM D, YYYY')
|
||||
return formatDate(result.value.created)
|
||||
})
|
||||
|
||||
const selectedRail = computed(() => {
|
||||
@@ -185,16 +192,7 @@ const formattedLocalCurrency = computed(() => {
|
||||
if (!shouldShowExchangeRate.value || !netAmountInLocalCurrency.value || !localCurrency.value)
|
||||
return ''
|
||||
|
||||
try {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: localCurrency.value,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(netAmountInLocalCurrency.value)
|
||||
} catch {
|
||||
return `${localCurrency.value} ${netAmountInLocalCurrency.value.toFixed(2)}`
|
||||
}
|
||||
return formatMoney(netAmountInLocalCurrency.value, localCurrency.value)
|
||||
})
|
||||
|
||||
const isMuralPayWithdrawal = computed(() => {
|
||||
|
||||
@@ -88,9 +88,9 @@ import {
|
||||
IntlFormatted,
|
||||
normalizeChildren,
|
||||
useDebugLogger,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { useGeolocation } from '@vueuse/core'
|
||||
|
||||
import { useCountries, useFormattedCountries, useUserCountry } from '@/composables/country.ts'
|
||||
@@ -115,6 +115,7 @@ const userCountry = useUserCountry()
|
||||
const allCountries = useCountries()
|
||||
const { coords } = useGeolocation()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const auth = await useAuth()
|
||||
|
||||
|
||||
@@ -80,9 +80,9 @@ import {
|
||||
defineMessages,
|
||||
IntlFormatted,
|
||||
normalizeChildren,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { getTaxThreshold, getTaxThresholdActual } from '@/providers/creator-withdraw.ts'
|
||||
@@ -94,6 +94,7 @@ const props = defineProps<{
|
||||
}>()
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
|
||||
const generatedState = useGeneratedState()
|
||||
const taxThreshold = computed(() => getTaxThreshold(generatedState.value.taxComplianceThresholds))
|
||||
|
||||
@@ -350,9 +350,9 @@ import {
|
||||
paymentMethodMessages,
|
||||
StyledInput,
|
||||
useDebugLogger,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
|
||||
@@ -365,6 +365,7 @@ const debug = useDebugLogger('TremendousDetailsStage')
|
||||
const { withdrawData, maxWithdrawAmount, availableMethods, paymentOptions, calculateFees } =
|
||||
useWithdrawContext()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const auth = await useAuth()
|
||||
|
||||
const userEmail = computed(() => {
|
||||
@@ -587,16 +588,7 @@ function formatAmountForDisplay(
|
||||
if (!currencyCode || currencyCode === 'USD' || !rate) {
|
||||
return formatMoney(localAmount)
|
||||
}
|
||||
try {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: currencyCode,
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 2,
|
||||
}).format(localAmount)
|
||||
} catch {
|
||||
return `${currencyCode} ${localAmount.toFixed(2)}`
|
||||
}
|
||||
return formatMoney(localAmount, currencyCode)
|
||||
}
|
||||
|
||||
const useFixedDenominations = computed(() => {
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<span
|
||||
v-tooltip="`Since ${queuedDate.toLocaleString()}`"
|
||||
v-tooltip="`Since ${formatDateTimeFull(queuedDate.toDate())}`"
|
||||
class="text-base text-secondary"
|
||||
:class="{
|
||||
'text-red': daysInQueue > 4,
|
||||
@@ -120,6 +120,7 @@ import {
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
type OverflowMenuOption,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
} from '@modrinth/ui'
|
||||
import { formatProjectType } from '@modrinth/utils'
|
||||
@@ -130,6 +131,17 @@ import type { ModerationProject } from '~/helpers/moderation'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTimeFull = useFormatDateTime({
|
||||
weekday: 'short',
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
timeZoneName: 'short',
|
||||
timeZone: 'UTC',
|
||||
})
|
||||
|
||||
const baseId = useId()
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<div class="flex flex-row items-center gap-2 self-end sm:self-auto">
|
||||
<span
|
||||
v-tooltip="formatExactDate(report.created)"
|
||||
v-tooltip="formatDateTime(report.created)"
|
||||
class="cursor-help whitespace-nowrap text-sm text-secondary"
|
||||
>
|
||||
{{ formatRelativeTime(report.created) }}
|
||||
@@ -80,7 +80,7 @@
|
||||
|
||||
<span
|
||||
v-if="report.user?.created"
|
||||
v-tooltip="formatExactDate(report.user.created)"
|
||||
v-tooltip="formatDateTime(report.user.created)"
|
||||
class="cursor-help text-sm text-secondary"
|
||||
>
|
||||
Joined {{ formatRelativeTime(report.user.created) }}
|
||||
@@ -190,7 +190,7 @@ import {
|
||||
LinkIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { type ExtendedReport, reportQuickReplies } from '@modrinth/moderation'
|
||||
import type { OverflowMenuOption } from '@modrinth/ui'
|
||||
import { type OverflowMenuOption, useFormatDateTime } from '@modrinth/ui'
|
||||
import {
|
||||
Avatar,
|
||||
ButtonStyled,
|
||||
@@ -201,7 +201,6 @@ import {
|
||||
useRelativeTime,
|
||||
} from '@modrinth/ui'
|
||||
import { formatProjectType } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { isStaff } from '~/helpers/users.js'
|
||||
@@ -305,10 +304,10 @@ async function reopenReport() {
|
||||
}
|
||||
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
|
||||
function formatExactDate(date: string): string {
|
||||
return dayjs(date).format('MMMM D, YYYY [at] h:mm A')
|
||||
}
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
function updateThread(newThread: any) {
|
||||
if (props.report.thread) {
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
injectNotificationManager,
|
||||
OverflowMenu,
|
||||
type OverflowMenuOption,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import {
|
||||
capitalizeString,
|
||||
@@ -46,6 +47,16 @@ import ThreadView from '~/components/ui/thread/ThreadView.vue'
|
||||
const auth = await useAuth()
|
||||
const featureFlags = useFeatureFlags()
|
||||
|
||||
const formatDateTimeUtc = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: '2-digit',
|
||||
timeZoneName: 'short',
|
||||
timeZone: 'UTC',
|
||||
})
|
||||
|
||||
type FlattenedFileReport = Labrinth.TechReview.Internal.FileReport & {
|
||||
id: string
|
||||
version_id: string
|
||||
@@ -763,7 +774,7 @@ const reviewSummaryPreview = computed(() => {
|
||||
const totalDecisions = totalSafe + totalUnsafe
|
||||
if (totalDecisions === 0) return ''
|
||||
|
||||
const timestamp = dayjs().utc().format('MMMM D, YYYY [at] h:mm A [UTC]')
|
||||
const timestamp = formatDateTimeUtc(dayjs().toDate())
|
||||
let markdown = `## Tech Review Summary\n*${timestamp}*\n\n`
|
||||
markdown += `<details>\n<summary>File Details (${totalSafe} safe, ${totalUnsafe} unsafe)</summary>\n\n`
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
<span>{{ report.reporterUser.username }}</span>
|
||||
</nuxt-link>
|
||||
<span> </span>
|
||||
<span v-tooltip="$dayjs(report.created).format('MMMM D, YYYY [at] h:mm A')">{{
|
||||
<span v-tooltip="formatDateTime(report.created)">{{
|
||||
formatRelativeTime(report.created)
|
||||
}}</span>
|
||||
<CopyCode v-if="flags.developerMode" :text="report.id" class="report-id" />
|
||||
@@ -104,13 +104,17 @@
|
||||
|
||||
<script setup>
|
||||
import { ReportIcon, UnknownIcon, VersionIcon } from '@modrinth/assets'
|
||||
import { Avatar, Badge, CopyCode, useRelativeTime } from '@modrinth/ui'
|
||||
import { Avatar, Badge, CopyCode, useFormatDateTime, useRelativeTime } from '@modrinth/ui'
|
||||
import { formatProjectType, renderHighlightedString } from '@modrinth/utils'
|
||||
|
||||
import ThreadSummary from '~/components/ui/thread/ThreadSummary.vue'
|
||||
import { getProjectTypeForUrl } from '~/helpers/projects.js'
|
||||
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
defineProps({
|
||||
report: {
|
||||
|
||||
@@ -79,6 +79,7 @@ import {
|
||||
getFileExtensionIcon,
|
||||
isEditableFile as isEditableFileExt,
|
||||
isImageFile,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import { computed, h, ref, shallowRef } from 'vue'
|
||||
import { renderToString } from 'vue/server-renderer'
|
||||
@@ -121,6 +122,14 @@ const units = Object.freeze(['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB'])
|
||||
const route = shallowRef(useRoute())
|
||||
const router = useRouter()
|
||||
|
||||
const formatDateTime = useFormatDateTime({
|
||||
year: '2-digit',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
})
|
||||
|
||||
const containerClasses = computed(() => [
|
||||
'group m-0 flex w-full select-none items-center justify-between overflow-hidden border-0 border-t border-solid border-surface-3 px-4 py-3 focus:!outline-none',
|
||||
props.index % 2 === 0 ? 'bg-surface-2' : 'bg-surface-3',
|
||||
@@ -177,28 +186,12 @@ const iconComponent = computed(() => {
|
||||
|
||||
const formattedModifiedDate = computed(() => {
|
||||
const date = new Date(props.modified * 1000)
|
||||
return `${date.toLocaleDateString('en-US', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
year: '2-digit',
|
||||
})}, ${date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
hour12: true,
|
||||
})}`
|
||||
return formatDateTime(date)
|
||||
})
|
||||
|
||||
const formattedCreationDate = computed(() => {
|
||||
const date = new Date(props.created * 1000)
|
||||
return `${date.toLocaleDateString('en-US', {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
year: '2-digit',
|
||||
})}, ${date.toLocaleTimeString('en-US', {
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
hour12: true,
|
||||
})}`
|
||||
return formatDateTime(date)
|
||||
})
|
||||
|
||||
const isEditableFile = computed(() => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
import type { MessageDescriptor } from '@modrinth/ui'
|
||||
import { type MessageDescriptor, useFormatPrice } from '@modrinth/ui'
|
||||
import { ButtonStyled, defineMessage, defineMessages, ServersSpecs, useVIntl } from '@modrinth/ui'
|
||||
import { formatPrice } from '@modrinth/utils'
|
||||
|
||||
const { formatMessage, locale } = useVIntl()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatPrice = useFormatPrice()
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'select' | 'scroll-to-faq'): void
|
||||
@@ -132,7 +132,7 @@ const billingMonths = computed(() => {
|
||||
</div>
|
||||
</div>
|
||||
<span class="m-0 text-2xl font-bold text-contrast">
|
||||
{{ formatPrice(locale, price / billingMonths, currency, true) }}
|
||||
{{ formatPrice(price / billingMonths, currency, true) }}
|
||||
<span class="text-lg font-semibold text-secondary">
|
||||
/ month<template v-if="interval !== 'monthly'">, billed {{ interval }}</template>
|
||||
</span>
|
||||
|
||||
@@ -6,14 +6,22 @@ import {
|
||||
NOTICE_LEVELS,
|
||||
ServerNotice,
|
||||
TagItem,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import type { ServerNotice as ServerNoticeType } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const formatDateTimeShortMonth = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'medium',
|
||||
})
|
||||
|
||||
defineProps<{
|
||||
notice: ServerNoticeType
|
||||
@@ -27,17 +35,14 @@ defineProps<{
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<span v-if="notice.announce_at">
|
||||
{{ dayjs(notice.announce_at).format('MMM D, YYYY [at] h:mm A') }} ({{
|
||||
{{ formatDateTimeShortMonth(notice.announce_at) }} ({{
|
||||
formatRelativeTime(notice.announce_at)
|
||||
}})
|
||||
</span>
|
||||
<template v-else> Never begins </template>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<span
|
||||
v-if="notice.expires"
|
||||
v-tooltip="dayjs(notice.expires).format('MMMM D, YYYY [at] h:mm A')"
|
||||
>
|
||||
<span v-if="notice.expires" v-tooltip="formatDateTime(notice.expires)">
|
||||
{{ formatRelativeTime(notice.expires) }}
|
||||
</span>
|
||||
<template v-else> Never expires </template>
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
</span>
|
||||
</div>
|
||||
<span class="message__date">
|
||||
<span v-tooltip="$dayjs(message.created).format('MMMM D, YYYY [at] h:mm A')">
|
||||
<span v-tooltip="formatDateTime(message.created)">
|
||||
{{ timeSincePosted }}
|
||||
</span>
|
||||
</span>
|
||||
@@ -131,7 +131,14 @@ import {
|
||||
ScaleIcon,
|
||||
TrashIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { AutoLink, Avatar, Badge, OverflowMenu, useRelativeTime } from '@modrinth/ui'
|
||||
import {
|
||||
AutoLink,
|
||||
Avatar,
|
||||
Badge,
|
||||
OverflowMenu,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
} from '@modrinth/ui'
|
||||
import { renderString } from '@modrinth/utils'
|
||||
|
||||
import { isStaff } from '~/helpers/users.js'
|
||||
@@ -186,6 +193,11 @@ const formattedMessage = computed(() => {
|
||||
})
|
||||
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const timeSincePosted = ref(formatRelativeTime(props.message.created))
|
||||
|
||||
const isPrivateMessage = computed(() => {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
const formatters = new WeakMap<object, Intl.NumberFormat>()
|
||||
|
||||
export function useCompactNumber(truncate = false, fractionDigits = 2, locale?: string) {
|
||||
const context = {}
|
||||
|
||||
let formatter = formatters.get(context)
|
||||
|
||||
if (!formatter) {
|
||||
formatter = new Intl.NumberFormat(locale, {
|
||||
notation: 'compact',
|
||||
maximumFractionDigits: fractionDigits,
|
||||
})
|
||||
formatters.set(context, formatter)
|
||||
}
|
||||
|
||||
function format(value: number): string {
|
||||
let formattedValue = value
|
||||
if (truncate) {
|
||||
const scale = Math.pow(10, fractionDigits)
|
||||
formattedValue = Math.floor(value * scale) / scale
|
||||
}
|
||||
return formatter!.format(formattedValue)
|
||||
}
|
||||
|
||||
return format
|
||||
}
|
||||
@@ -393,7 +393,7 @@
|
||||
"message": "No projects in collection yet"
|
||||
},
|
||||
"collection.label.projects-count": {
|
||||
"message": "{count, plural, =0 {No projects yet} one {<stat>{count}</stat> project} other {<stat>{count}</stat> {type}}}"
|
||||
"message": "{count, plural, =0 {No projects yet} other {<stat>{count}</stat> {type}}}"
|
||||
},
|
||||
"collection.label.updated-at": {
|
||||
"message": "Updated {ago}"
|
||||
@@ -591,7 +591,7 @@
|
||||
"message": "Try adjusting your filters or search terms."
|
||||
},
|
||||
"dashboard.collections.label.projects-count": {
|
||||
"message": "{count, plural, one {{count} project} other {{count} projects}}"
|
||||
"message": "{count} {countPlural, plural, one {project} other {projects}}"
|
||||
},
|
||||
"dashboard.collections.label.search-input": {
|
||||
"message": "Search your collections"
|
||||
@@ -1437,7 +1437,7 @@
|
||||
"message": "For Players"
|
||||
},
|
||||
"landing.section.for-players.tagline": {
|
||||
"message": "Discover over {count} creations"
|
||||
"message": "Discover over {count, number} creations"
|
||||
},
|
||||
"landing.subheading": {
|
||||
"message": "Discover, play, and share Minecraft content through our open-source platform built for the community."
|
||||
@@ -2040,7 +2040,7 @@
|
||||
"message": "Collection"
|
||||
},
|
||||
"profile.label.downloads": {
|
||||
"message": "{count} {count, plural, one {download} other {downloads}}"
|
||||
"message": "{count} {countPlural, plural, one {download} other {downloads}}"
|
||||
},
|
||||
"profile.label.joined": {
|
||||
"message": "Joined"
|
||||
@@ -2061,7 +2061,7 @@
|
||||
"message": "Organizations"
|
||||
},
|
||||
"profile.label.projects": {
|
||||
"message": "{count} {count, plural, one {project} other {projects}}"
|
||||
"message": "{count} {countPlural, plural, one {project} other {projects}}"
|
||||
},
|
||||
"profile.label.saving": {
|
||||
"message": "Saving..."
|
||||
|
||||
@@ -613,7 +613,7 @@
|
||||
<IntlFormatted
|
||||
:message-id="messages.serversPromoPricing"
|
||||
:values="{
|
||||
price: formatPrice(locale, 500, 'USD', true),
|
||||
price: formatPrice(500, 'USD', true),
|
||||
}"
|
||||
>
|
||||
<template #small="{ children }">
|
||||
@@ -945,7 +945,7 @@
|
||||
{{
|
||||
capitalizeString(
|
||||
formatMessage(commonMessages.projectFollowers, {
|
||||
count: formatNumber(project.followers, false),
|
||||
count: project.followers,
|
||||
}),
|
||||
)
|
||||
}}
|
||||
@@ -953,7 +953,7 @@
|
||||
</div>
|
||||
<div
|
||||
v-if="project.approved"
|
||||
v-tooltip="$dayjs(project.approved).format('MMMM D, YYYY [at] h:mm A')"
|
||||
v-tooltip="formatDateTime(project.approved)"
|
||||
class="details-list__item"
|
||||
>
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
@@ -968,11 +968,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
v-tooltip="$dayjs(project.published).format('MMMM D, YYYY [at] h:mm A')"
|
||||
class="details-list__item"
|
||||
>
|
||||
<div v-else v-tooltip="formatDateTime(project.published)" class="details-list__item">
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
<div>
|
||||
{{
|
||||
@@ -983,7 +979,7 @@
|
||||
|
||||
<div
|
||||
v-if="project.status === 'processing' && project.queued"
|
||||
v-tooltip="$dayjs(project.queued).format('MMMM D, YYYY [at] h:mm A')"
|
||||
v-tooltip="formatDateTime(project.queued)"
|
||||
class="details-list__item"
|
||||
>
|
||||
<ScaleIcon aria-hidden="true" />
|
||||
@@ -1000,7 +996,7 @@
|
||||
|
||||
<div
|
||||
v-if="versions.length > 0 && project.updated"
|
||||
v-tooltip="$dayjs(project.updated).format('MMMM D, YYYY [at] h:mm A')"
|
||||
v-tooltip="formatDateTime(project.updated)"
|
||||
class="details-list__item"
|
||||
>
|
||||
<VersionIcon aria-hidden="true" />
|
||||
@@ -1099,17 +1095,13 @@ import {
|
||||
StyledInput,
|
||||
TagItem,
|
||||
useDebugLogger,
|
||||
useFormatDateTime,
|
||||
useFormatPrice,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import VersionSummary from '@modrinth/ui/src/components/version/VersionSummary.vue'
|
||||
import {
|
||||
capitalizeString,
|
||||
formatNumber,
|
||||
formatPrice,
|
||||
formatProjectType,
|
||||
renderString,
|
||||
} from '@modrinth/utils'
|
||||
import { capitalizeString, formatProjectType, renderString } from '@modrinth/utils'
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
|
||||
import { useLocalStorage } from '@vueuse/core'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -1150,7 +1142,12 @@ const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const cosmetics = useCosmetics()
|
||||
|
||||
const { locale, formatMessage } = useVIntl()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatPrice = useFormatPrice()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const debug = useDebugLogger('DownloadModal')
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
</span>
|
||||
<span>
|
||||
on
|
||||
{{ $dayjs(version.date_published).format('MMM D, YYYY') }}</span
|
||||
{{ formatDate(version.date_published) }}</span
|
||||
>
|
||||
</div>
|
||||
<a
|
||||
@@ -86,12 +86,23 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { DownloadIcon, SpinnerIcon } from '@modrinth/assets'
|
||||
import { injectModrinthClient, injectProjectPageContext, Pagination } from '@modrinth/ui'
|
||||
import {
|
||||
injectModrinthClient,
|
||||
injectProjectPageContext,
|
||||
Pagination,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import VersionFilterControl from '@modrinth/ui/src/components/version/VersionFilterControl.vue'
|
||||
import { renderHighlightedString } from '@modrinth/utils'
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
const formatDate = useFormatDateTime({
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const { projectV2, versions, versionsLoading, loadVersions } = injectProjectPageContext()
|
||||
|
||||
// Load versions on mount (client-side)
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
<div class="gallery-bottom">
|
||||
<div class="gallery-created">
|
||||
<CalendarIcon aria-hidden="true" aria-label="Date created" />
|
||||
{{ $dayjs(item.created).format('MMMM D, YYYY') }}
|
||||
{{ formatDate(item.created) }}
|
||||
</div>
|
||||
<div v-if="currentMember" class="gallery-buttons input-group">
|
||||
<button
|
||||
@@ -341,11 +341,18 @@ import {
|
||||
injectProjectPageContext,
|
||||
NewModal as Modal,
|
||||
StyledInput,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import { useEventListener, useLocalStorage } from '@vueuse/core'
|
||||
|
||||
import { isPermission } from '~/utils/permissions.ts'
|
||||
|
||||
const formatDate = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
|
||||
// Router
|
||||
const router = useRouter()
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
<div class="gallery-bottom">
|
||||
<div class="gallery-created">
|
||||
<CalendarIcon aria-hidden="true" aria-label="Date created" />
|
||||
{{ $dayjs(item.created).format('MMMM D, YYYY') }}
|
||||
{{ formatDate(item.created) }}
|
||||
</div>
|
||||
<div v-if="currentMember" class="gallery-buttons input-group">
|
||||
<button
|
||||
@@ -300,10 +300,17 @@ import {
|
||||
injectProjectPageContext,
|
||||
NewModal as Modal,
|
||||
StyledInput,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import { isPermission } from '~/utils/permissions.ts'
|
||||
|
||||
const formatDate = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
|
||||
const {
|
||||
projectV2: project,
|
||||
currentMember,
|
||||
|
||||
@@ -368,7 +368,7 @@
|
||||
<div v-if="!isEditing">
|
||||
<h4>Publication date</h4>
|
||||
<span>
|
||||
{{ $dayjs(version.date_published).format('MMMM D, YYYY [at] h:mm A') }}
|
||||
{{ formatDateTime(version.date_published) }}
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="!isEditing && version.author">
|
||||
@@ -437,6 +437,7 @@ import {
|
||||
injectNotificationManager,
|
||||
injectProjectPageContext,
|
||||
StyledInput,
|
||||
useFormatDateTime,
|
||||
} from '@modrinth/ui'
|
||||
import { formatBytes, renderHighlightedString } from '@modrinth/utils'
|
||||
import { Multiselect } from 'vue-multiselect'
|
||||
@@ -460,6 +461,11 @@ const auth = await useAuth()
|
||||
const tags = useGeneratedState()
|
||||
const flags = useFeatureFlags()
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const formatDate = useFormatDateTime({ dateStyle: 'medium' })
|
||||
|
||||
// Helper for accessing nuxt app $formatVersion
|
||||
const formatVersionDisplay = (versions: string[]) => (data as any).$formatVersion(versions)
|
||||
@@ -697,9 +703,9 @@ const description = computed(
|
||||
version.value.loaders ?? []
|
||||
)
|
||||
.map((x: string) => x.charAt(0).toUpperCase() + x.slice(1))
|
||||
.join(' & ')}. Published on ${data
|
||||
.$dayjs(version.value.date_published)
|
||||
.format('MMM D, YYYY')}. ${version.value.downloads} downloads.`,
|
||||
.join(
|
||||
' & ',
|
||||
)}. Published on ${formatDate(version.value.date_published)}. ${version.value.downloads} downloads.`,
|
||||
)
|
||||
|
||||
const usesFeaturedVersions = computed(() =>
|
||||
|
||||
@@ -169,7 +169,7 @@
|
||||
</span>
|
||||
<div class="mb-4 mt-2 flex w-full items-center gap-1 text-sm text-secondary">
|
||||
{{ capitalizeString(subscription.interval) }} ⋅ {{ subscription.status }} ⋅
|
||||
{{ dayjs(subscription.created).format('MMMM D, YYYY [at] h:mma') }} ({{
|
||||
{{ formatDateTime(subscription.created) }} ({{
|
||||
formatRelativeTime(subscription.created)
|
||||
}})
|
||||
</div>
|
||||
@@ -239,7 +239,7 @@
|
||||
</span>
|
||||
<template v-if="charge.status !== 'cancelled'">
|
||||
⋅
|
||||
{{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}
|
||||
{{ formatPrice(charge.amount, charge.currency_code) }}
|
||||
</template>
|
||||
</span>
|
||||
<span class="text-sm text-secondary">
|
||||
@@ -252,13 +252,13 @@
|
||||
<span v-else-if="charge.status === 'cancelled'" class="font-bold">Ends:</span>
|
||||
<span v-else-if="charge.type === 'refund'" class="font-bold">Issued:</span>
|
||||
<span v-else class="font-bold">Due:</span>
|
||||
{{ dayjs(charge.due).format('MMMM D, YYYY [at] h:mma') }}
|
||||
{{ formatDateTime(charge.due) }}
|
||||
<span class="text-secondary">({{ formatRelativeTime(charge.due) }}) </span>
|
||||
</span>
|
||||
<span v-if="charge.last_attempt != null" class="text-sm text-secondary">
|
||||
<span v-if="charge.status === 'failed'" class="font-bold">Last attempt:</span>
|
||||
<span v-else class="font-bold">Charged:</span>
|
||||
{{ dayjs(charge.last_attempt).format('MMMM D, YYYY [at] h:mma') }}
|
||||
{{ formatDateTime(charge.last_attempt) }}
|
||||
<span class="text-secondary"
|
||||
>({{ formatRelativeTime(charge.last_attempt) }})
|
||||
</span>
|
||||
@@ -268,9 +268,9 @@
|
||||
⋅
|
||||
{{ charge.type }}
|
||||
⋅
|
||||
{{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}
|
||||
{{ formatPrice(charge.amount, charge.currency_code) }}
|
||||
⋅
|
||||
{{ dayjs(charge.due).format('YYYY-MM-DD h:mma') }}
|
||||
{{ formatDateTimeShort(charge.due) }}
|
||||
<template v-if="charge.subscription_interval">
|
||||
⋅ {{ charge.subscription_interval }}
|
||||
</template>
|
||||
@@ -332,16 +332,30 @@ import {
|
||||
NewModal,
|
||||
StyledInput,
|
||||
Toggle,
|
||||
useFormatDateTime,
|
||||
useFormatPrice,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { capitalizeString, formatPrice } from '@modrinth/utils'
|
||||
import { capitalizeString } from '@modrinth/utils'
|
||||
import { DEFAULT_CREDIT_EMAIL_MESSAGE } from '@modrinth/utils/utils.ts'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import ModrinthServersIcon from '~/components/ui/servers/ModrinthServersIcon.vue'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const formatPrice = useFormatPrice()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const formatDateTimeShort = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
const vintl = useVIntl()
|
||||
|
||||
@@ -159,16 +159,13 @@
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<span v-if="notice.announce_at">
|
||||
{{ dayjs(notice.announce_at).format('MMM D, YYYY [at] h:mm A') }}
|
||||
{{ formatDateTimeShortMonth(notice.announce_at) }}
|
||||
({{ formatRelativeTime(notice.announce_at) }})
|
||||
</span>
|
||||
<template v-else> Never begins </template>
|
||||
</div>
|
||||
<div class="text-sm">
|
||||
<span
|
||||
v-if="notice.expires"
|
||||
v-tooltip="dayjs(notice.expires).format('MMMM D, YYYY [at] h:mm A')"
|
||||
>
|
||||
<span v-if="notice.expires" v-tooltip="formatDateTime(notice.expires)">
|
||||
{{ formatRelativeTime(notice.expires) }}
|
||||
</span>
|
||||
<template v-else> Never expires </template>
|
||||
@@ -276,6 +273,7 @@ import {
|
||||
StyledInput,
|
||||
TagItem,
|
||||
Toggle,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -290,6 +288,14 @@ import { useServersFetch } from '~/composables/servers/servers-fetch.ts'
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
const formatDateTimeShortMonth = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'medium',
|
||||
})
|
||||
|
||||
const notices = ref<ServerNoticeType[]>([])
|
||||
const createNoticeModal = ref<InstanceType<typeof NewModal>>()
|
||||
|
||||
@@ -76,11 +76,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1 text-sm text-secondary">
|
||||
<span v-tooltip="dayjs(batch.created_at).format('MMMM D, YYYY [at] h:mm A')">
|
||||
<span v-tooltip="formatDateTime(batch.created_at)">
|
||||
Created {{ formatRelativeTime(batch.created_at) }}
|
||||
</span>
|
||||
<span>•</span>
|
||||
<span v-tooltip="dayjs(batch.scheduled_at).format('MMMM D, YYYY [at] h:mm A')">
|
||||
<span v-tooltip="formatDateTime(batch.scheduled_at)">
|
||||
Scheduled {{ formatRelativeTime(batch.scheduled_at) }}
|
||||
</span>
|
||||
<template v-if="batch.provision_options?.region">
|
||||
@@ -116,6 +116,7 @@ import {
|
||||
injectNotificationManager,
|
||||
Pagination,
|
||||
TagItem,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
} from '@modrinth/ui'
|
||||
import type { User } from '@modrinth/utils'
|
||||
@@ -127,6 +128,10 @@ import { useServersFetch } from '~/composables/servers/servers-fetch.ts'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
// Types
|
||||
interface ProvisionOptions {
|
||||
|
||||
@@ -180,9 +180,11 @@
|
||||
count: formatCompactNumber(projects?.length || 0),
|
||||
type: formatMessage(
|
||||
commonProjectTypeSentenceMessages[
|
||||
projectTypes.length === 1 ? projectTypes[0] : 'project'
|
||||
projectTypes.length === 1 && projects?.length > 1
|
||||
? projectTypes[0]
|
||||
: 'project'
|
||||
],
|
||||
{ count: projects?.length || 0 },
|
||||
{ count: formatCompactNumberPlural(projects?.length || 0) },
|
||||
),
|
||||
}"
|
||||
>
|
||||
@@ -247,7 +249,7 @@
|
||||
>
|
||||
<div class="flex flex-col gap-2">
|
||||
<span
|
||||
v-tooltip="dayjs(collection.created).format('MMMM D, YYYY [at] h:mm A')"
|
||||
v-tooltip="formatDateTime(collection.created)"
|
||||
class="flex w-fit items-center gap-2"
|
||||
>
|
||||
<CalendarIcon aria-hidden="true" />
|
||||
@@ -259,7 +261,7 @@
|
||||
</span>
|
||||
<span
|
||||
v-if="showUpdatedDate"
|
||||
v-tooltip="dayjs(collection.updated).format('MMMM D, YYYY [at] h:mm A')"
|
||||
v-tooltip="formatDateTime(collection.updated)"
|
||||
class="flex w-fit items-center gap-2"
|
||||
>
|
||||
<UpdatedIcon aria-hidden="true" />
|
||||
@@ -410,6 +412,8 @@ import {
|
||||
RadioButtons,
|
||||
SidebarCard,
|
||||
StyledInput,
|
||||
useCompactNumber,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
useSavable,
|
||||
useVIntl,
|
||||
@@ -425,7 +429,11 @@ const { handleError } = injectNotificationManager()
|
||||
const api = injectModrinthClient()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatCompactNumber = useCompactNumber()
|
||||
const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const route = useNativeRoute()
|
||||
const router = useRouter()
|
||||
@@ -491,8 +499,7 @@ const messages = defineMessages({
|
||||
},
|
||||
projectsCountLabel: {
|
||||
id: 'collection.label.projects-count',
|
||||
defaultMessage:
|
||||
'{count, plural, =0 {No projects yet} one {<stat>{count}</stat> project} other {<stat>{count}</stat> {type}}}',
|
||||
defaultMessage: '{count, plural, =0 {No projects yet} other {<stat>{count}</stat> {type}}}',
|
||||
},
|
||||
removeProjectButton: {
|
||||
id: 'collection.button.remove-project',
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
{{
|
||||
formatMessage(messages.projectsCountLabel, {
|
||||
count: formatCompactNumber(user ? user.follows.length : 0),
|
||||
countPlural: formatCompactNumberPlural(user ? user.follows.length : 0),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
@@ -91,6 +92,7 @@
|
||||
{{
|
||||
formatMessage(messages.projectsCountLabel, {
|
||||
count: formatCompactNumber(collection.projects?.length || 0),
|
||||
countPlural: formatCompactNumberPlural(collection.projects?.length || 0),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
@@ -154,13 +156,14 @@ import {
|
||||
defineMessages,
|
||||
DropdownSelect,
|
||||
StyledInput,
|
||||
useCompactNumber,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
import CollectionCreateModal from '~/components/ui/create/CollectionCreateModal.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatCompactNumber = useCompactNumber()
|
||||
const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber()
|
||||
|
||||
const messages = defineMessages({
|
||||
createNewButton: {
|
||||
@@ -177,7 +180,7 @@ const messages = defineMessages({
|
||||
},
|
||||
projectsCountLabel: {
|
||||
id: 'dashboard.collections.label.projects-count',
|
||||
defaultMessage: '{count, plural, one {{count} project} other {{count} projects}}',
|
||||
defaultMessage: '{count} {countPlural, plural, one {project} other {projects}}',
|
||||
},
|
||||
searchInputLabel: {
|
||||
id: 'dashboard.collections.label.search-input',
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
></span>
|
||||
{{
|
||||
formatMessage(messages.estimatedWithDate, {
|
||||
date: date.date ? dayjs(date.date).format('MMM D, YYYY') : '',
|
||||
date: date.date ? formatDate(date.date) : '',
|
||||
})
|
||||
}}
|
||||
<Tooltip theme="dismissable-prompt" :triggers="['hover', 'focus']" no-auto-focus>
|
||||
@@ -260,8 +260,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ArrowUpRightIcon, InProgressIcon, UnknownIcon } from '@modrinth/assets'
|
||||
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import { defineMessages, useFormatDateTime, useFormatMoney, useVIntl } from '@modrinth/ui'
|
||||
import dayjs from 'dayjs'
|
||||
import { Tooltip } from 'floating-vue'
|
||||
|
||||
@@ -271,6 +270,8 @@ import CreatorWithdrawModal from '~/components/ui/dashboard/CreatorWithdrawModal
|
||||
import RevenueTransaction from '~/components/ui/dashboard/RevenueTransaction.vue'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const formatDate = useFormatDateTime({ dateStyle: 'medium' })
|
||||
|
||||
await useAuth()
|
||||
|
||||
|
||||
@@ -90,8 +90,15 @@ import {
|
||||
GenericListIcon,
|
||||
SpinnerIcon,
|
||||
} from '@modrinth/assets'
|
||||
import { ButtonStyled, Combobox, defineMessages, useVIntl } from '@modrinth/ui'
|
||||
import { formatMoney } from '@modrinth/utils'
|
||||
import {
|
||||
ButtonStyled,
|
||||
Combobox,
|
||||
defineMessages,
|
||||
useFormatDateTime,
|
||||
useFormatMoney,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { capitalizeString } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import RevenueTransaction from '~/components/ui/dashboard/RevenueTransaction.vue'
|
||||
@@ -99,6 +106,12 @@ import { useGeneratedState } from '~/composables/generated'
|
||||
import { findRail } from '~/utils/muralpay-rails'
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatMoney = useFormatMoney()
|
||||
const formatMonth = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
})
|
||||
|
||||
const generatedState = useGeneratedState()
|
||||
|
||||
useHead({
|
||||
@@ -152,7 +165,7 @@ function getPeriodLabel(date) {
|
||||
} else if (txnDate.isSame(now.subtract(1, 'month'), 'month')) {
|
||||
return 'Last month'
|
||||
} else {
|
||||
return txnDate.format('MMMM YYYY')
|
||||
return capitalizeString(formatMonth(txnDate.toDate()))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -616,7 +616,7 @@
|
||||
<p v-if="lowestPrice" class="m-0 text-sm">
|
||||
{{
|
||||
formatMessage(messages.startingAtPrice, {
|
||||
price: formatPrice(locale, lowestPrice, selectedCurrency, true),
|
||||
price: formatPrice(lowestPrice, selectedCurrency, true),
|
||||
})
|
||||
}}
|
||||
</p>
|
||||
@@ -644,10 +644,10 @@ import {
|
||||
injectNotificationManager,
|
||||
IntlFormatted,
|
||||
ModrinthServersPurchaseModal,
|
||||
useFormatPrice,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { monthsInInterval } from '@modrinth/ui/src/utils/billing.ts'
|
||||
import { formatPrice } from '@modrinth/utils'
|
||||
import { computed } from 'vue'
|
||||
|
||||
import { useBaseFetch } from '@/composables/fetch.js'
|
||||
@@ -678,7 +678,8 @@ if (affiliateCode.value) {
|
||||
}
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { locale, formatMessage } = useVIntl()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatPrice = useFormatPrice()
|
||||
const flags = useFeatureFlags()
|
||||
|
||||
const messages = defineMessages({
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<div class="section-header">
|
||||
<div class="section-label green">{{ formatMessage(messages.forPlayersLabel) }}</div>
|
||||
<h2 class="section-tagline">
|
||||
{{ formatMessage(messages.discoverCreationsTagline, { count: formattedProjectCount }) }}
|
||||
{{ formatMessage(messages.discoverCreationsTagline, { count: PROJECT_COUNT }) }}
|
||||
</h2>
|
||||
<p class="section-description">
|
||||
{{ formatMessage(messages.playersDescription) }}
|
||||
@@ -466,8 +466,6 @@ const searchQuery = ref('leave')
|
||||
const sortType = ref('relevance')
|
||||
|
||||
const PROJECT_COUNT = 100000
|
||||
const formatNumber = new Intl.NumberFormat().format
|
||||
const formattedProjectCount = computed(() => formatNumber(PROJECT_COUNT))
|
||||
|
||||
const auth = await useAuth()
|
||||
|
||||
@@ -526,7 +524,7 @@ const messages = defineMessages({
|
||||
},
|
||||
discoverCreationsTagline: {
|
||||
id: 'landing.section.for-players.tagline',
|
||||
defaultMessage: 'Discover over {count} creations',
|
||||
defaultMessage: 'Discover over {count, number} creations',
|
||||
},
|
||||
shareContentTagline: {
|
||||
id: 'landing.section.for-creators.tagline',
|
||||
|
||||
@@ -111,7 +111,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>End of the month</td>
|
||||
<td>{{ formatDate(endOfMonthDate) }}</td>
|
||||
<td>{{ formatDate(endOfMonthDate.toDate()) }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>NET 60 policy applied</td>
|
||||
@@ -119,7 +119,7 @@
|
||||
</tr>
|
||||
<tr class="final-result">
|
||||
<td>Available for withdrawal</td>
|
||||
<td>{{ formatDate(withdrawalDate) }}</td>
|
||||
<td>{{ formatDate(withdrawalDate.toDate()) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -162,11 +162,17 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { StyledInput } from '@modrinth/ui'
|
||||
import { formatDate, formatMoney } from '@modrinth/utils'
|
||||
import { StyledInput, useFormatDateTime, useFormatMoney } from '@modrinth/ui'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
const formatMoney = useFormatMoney()
|
||||
const formatDate = useFormatDateTime({
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
year: 'numeric',
|
||||
})
|
||||
|
||||
const description =
|
||||
'Information about the Rewards Program of Modrinth, an open source modding platform focused on Minecraft.'
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { GitGraphIcon, RssIcon } from '@modrinth/assets'
|
||||
import { articles as rawArticles } from '@modrinth/blog'
|
||||
import { Avatar, ButtonStyled } from '@modrinth/ui'
|
||||
import { Avatar, ButtonStyled, useFormatDateTime } from '@modrinth/ui'
|
||||
import type { User } from '@modrinth/utils'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, onMounted } from 'vue'
|
||||
@@ -12,6 +12,8 @@ import ShareArticleButtons from '~/components/ui/ShareArticleButtons.vue'
|
||||
const config = useRuntimeConfig()
|
||||
const route = useRoute()
|
||||
|
||||
const formatDate = useFormatDateTime({ dateStyle: 'long' })
|
||||
|
||||
const rawArticle = rawArticles.find((article) => article.slug === route.params.slug)
|
||||
|
||||
if (!rawArticle) {
|
||||
@@ -157,10 +159,10 @@ onMounted(() => {
|
||||
</nuxt-link>
|
||||
</template>
|
||||
<span class="hidden md:block">•</span>
|
||||
<span class="hidden md:block"> {{ dayjsDate.format('MMMM D, YYYY') }}</span>
|
||||
<span class="hidden md:block"> {{ formatDate(dayjsDate.toDate()) }}</span>
|
||||
</div>
|
||||
<span class="text-sm text-secondary sm:text-base md:hidden">
|
||||
Posted on {{ dayjsDate.format('MMMM D, YYYY') }}</span
|
||||
Posted on {{ formatDate(dayjsDate.toDate()) }}</span
|
||||
>
|
||||
<ShareArticleButtons :title="article.title" :url="articleUrl" />
|
||||
<img
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { ChevronRightIcon, GitGraphIcon, RssIcon } from '@modrinth/assets'
|
||||
import { articles as rawArticles } from '@modrinth/blog'
|
||||
import { ButtonStyled, NewsArticleCard } from '@modrinth/ui'
|
||||
import dayjs from 'dayjs'
|
||||
import { ButtonStyled, NewsArticleCard, useFormatDateTime } from '@modrinth/ui'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import NewsletterButton from '~/components/ui/NewsletterButton.vue'
|
||||
|
||||
const formatDate = useFormatDateTime({ dateStyle: 'long' })
|
||||
|
||||
const articles = ref(
|
||||
rawArticles
|
||||
.map((article) => ({
|
||||
@@ -83,7 +84,7 @@ useSeoMeta({
|
||||
</h3>
|
||||
<p class="m-0 text-lg leading-tight">{{ featuredArticle?.summary }}</p>
|
||||
<div class="mt-auto text-secondary">
|
||||
{{ dayjs(featuredArticle?.date).format('MMMM D, YYYY') }}
|
||||
{{ formatDate(featuredArticle?.date) }}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
</nuxt-link>
|
||||
</h2>
|
||||
<span>
|
||||
{{ formatNumber(acceptedMembers?.length || 0) }}
|
||||
{{ formatCompactNumber(acceptedMembers?.length || 0) }}
|
||||
member<template v-if="acceptedMembers?.length !== 1">s</template>
|
||||
</span>
|
||||
</div>
|
||||
@@ -89,7 +89,7 @@
|
||||
projects
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="sumDownloads.toLocaleString()"
|
||||
v-tooltip="formatNumber(sumDownloads)"
|
||||
class="flex items-center gap-2 font-semibold"
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 text-secondary" />
|
||||
@@ -293,10 +293,11 @@ import {
|
||||
OverflowMenu,
|
||||
ProjectCard,
|
||||
ProjectCardList,
|
||||
useCompactNumber,
|
||||
useFormatNumber,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import type { Organization, ProjectStatus, ProjectType } from '@modrinth/utils'
|
||||
import { formatNumber } from '@modrinth/utils'
|
||||
|
||||
import UpToDate from '~/assets/images/illustrations/up_to_date.svg?component'
|
||||
import AdPlaceholder from '~/components/ui/AdPlaceholder.vue'
|
||||
@@ -318,7 +319,8 @@ type ProjectV3 = Labrinth.Projects.v3.Project & {
|
||||
const vintl = useVIntl()
|
||||
const { formatMessage } = vintl
|
||||
|
||||
const formatCompactNumber = useCompactNumber(true)
|
||||
const formatNumber = useFormatNumber()
|
||||
const { formatCompactNumber } = useCompactNumber()
|
||||
|
||||
const auth: { user: any } & any = await useAuth()
|
||||
const user = await useUser()
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
anytime.
|
||||
</p>
|
||||
<p class="m-0 text-[2rem] font-bold text-purple">
|
||||
{{ formatPrice(vintl.locale, price.prices.intervals.monthly, price.currency_code) }}/mo
|
||||
{{ formatPrice(price.prices.intervals.monthly, price.currency_code) }}/mo
|
||||
</p>
|
||||
<p class="m-0 mb-4 text-secondary">
|
||||
or save
|
||||
@@ -86,14 +86,15 @@
|
||||
</template>
|
||||
<script setup>
|
||||
import { HeartIcon, ModrinthPlusIcon, SettingsIcon, SparklesIcon, StarIcon } from '@modrinth/assets'
|
||||
import { injectNotificationManager, PurchaseModal, useVIntl } from '@modrinth/ui'
|
||||
import { calculateSavings, formatPrice, getCurrency } from '@modrinth/utils'
|
||||
import { injectNotificationManager, PurchaseModal, useFormatPrice } from '@modrinth/ui'
|
||||
import { calculateSavings, getCurrency } from '@modrinth/utils'
|
||||
|
||||
import { useBaseFetch } from '@/composables/fetch.js'
|
||||
import { isPermission } from '@/utils/permissions.ts'
|
||||
import { products } from '~/generated/state.json'
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const formatPrice = useFormatPrice()
|
||||
|
||||
const title = 'Subscribe to Modrinth Plus!'
|
||||
const description =
|
||||
@@ -116,8 +117,6 @@ useHead({
|
||||
],
|
||||
})
|
||||
|
||||
const vintl = useVIntl()
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const auth = await useAuth()
|
||||
|
||||
@@ -182,7 +182,7 @@
|
||||
<div>
|
||||
{{
|
||||
formatMessage(messages.createdOn, {
|
||||
date: new Date(app.created).toLocaleDateString(),
|
||||
date: formatDate(new Date(app.created)),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
@@ -257,6 +257,7 @@ import {
|
||||
IntlFormatted,
|
||||
normalizeChildren,
|
||||
StyledInput,
|
||||
useFormatDateTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
|
||||
@@ -272,6 +273,7 @@ import {
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatDate = useFormatDateTime()
|
||||
|
||||
definePageMeta({
|
||||
middleware: 'auth',
|
||||
|
||||
@@ -25,12 +25,12 @@
|
||||
</template>
|
||||
</span>
|
||||
⋅
|
||||
<span>{{ formatPrice(vintl.locale, charge.amount, charge.currency_code) }}</span>
|
||||
<span>{{ formatPrice(charge.amount, charge.currency_code) }}</span>
|
||||
</div>
|
||||
<div class="flex items-center gap-1">
|
||||
<Badge :color="charge.status === 'succeeded' ? 'green' : 'red'" :type="charge.status" />
|
||||
⋅
|
||||
{{ $dayjs(charge.due).format('YYYY-MM-DD') }}
|
||||
{{ formatDate(charge.due) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -38,8 +38,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { Badge, Breadcrumbs, useVIntl } from '@modrinth/ui'
|
||||
import { formatPrice } from '@modrinth/utils'
|
||||
import { Badge, Breadcrumbs, useFormatDateTime, useFormatPrice } from '@modrinth/ui'
|
||||
|
||||
import { products } from '~/generated/state.json'
|
||||
|
||||
@@ -47,7 +46,12 @@ definePageMeta({
|
||||
middleware: 'auth',
|
||||
})
|
||||
|
||||
const vintl = useVIntl()
|
||||
const formatPrice = useFormatPrice()
|
||||
const formatDate = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
})
|
||||
|
||||
const { data: charges } = await useAsyncData(
|
||||
'billing/payments',
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
<template v-if="midasCharge">
|
||||
{{
|
||||
formatPrice(
|
||||
vintl.locale,
|
||||
midasSubscriptionPrice.prices.intervals[midasSubscription.interval],
|
||||
midasSubscriptionPrice.currency_code,
|
||||
)
|
||||
@@ -60,7 +59,7 @@
|
||||
{{ midasSubscription.interval }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ formatPrice(vintl.locale, price.prices.intervals.monthly, price.currency_code) }}
|
||||
{{ formatPrice(price.prices.intervals.monthly, price.currency_code) }}
|
||||
/ month
|
||||
</template>
|
||||
</span>
|
||||
@@ -77,7 +76,7 @@
|
||||
>
|
||||
<span class="opacity-70">Next:</span>
|
||||
<span class="font-semibold text-contrast">
|
||||
{{ formatPrice(vintl.locale, midasCharge.amount, midasCharge.currency_code) }}
|
||||
{{ formatPrice(midasCharge.amount, midasCharge.currency_code) }}
|
||||
</span>
|
||||
<span>/{{ midasCharge.subscription_interval.replace('ly', '') }}</span>
|
||||
</div>
|
||||
@@ -90,21 +89,17 @@
|
||||
>
|
||||
Save
|
||||
{{
|
||||
formatPrice(
|
||||
vintl.locale,
|
||||
midasCharge.amount * 12 - oppositePrice,
|
||||
midasCharge.currency_code,
|
||||
)
|
||||
formatPrice(midasCharge.amount * 12 - oppositePrice, midasCharge.currency_code)
|
||||
}}/year by switching to yearly billing!
|
||||
</span>
|
||||
<span class="text-sm text-secondary">
|
||||
Since {{ $dayjs(midasSubscription.created).format('MMMM D, YYYY') }}
|
||||
Since {{ formatDate(midasSubscription.created) }}
|
||||
</span>
|
||||
<span v-if="midasCharge.status === 'open'" class="text-sm text-secondary">
|
||||
Renews {{ $dayjs(midasCharge.due).format('MMMM D, YYYY') }}
|
||||
Renews {{ formatDate(midasCharge.due) }}
|
||||
</span>
|
||||
<span v-else-if="midasCharge.status === 'cancelled'" class="text-sm text-secondary">
|
||||
Expires {{ $dayjs(midasCharge.due).format('MMMM D, YYYY') }}
|
||||
Expires {{ formatDate(midasCharge.due) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="
|
||||
@@ -116,14 +111,13 @@
|
||||
class="text-sm text-secondary"
|
||||
>
|
||||
Switches to {{ midasCharge.subscription_interval }} billing on
|
||||
{{ $dayjs(midasCharge.due).format('MMMM D, YYYY') }}
|
||||
{{ formatDate(midasCharge.due) }}
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<span v-else class="text-sm text-secondary">
|
||||
Or
|
||||
{{ formatPrice(vintl.locale, price.prices.intervals.yearly, price.currency_code) }} /
|
||||
year (save
|
||||
{{ formatPrice(price.prices.intervals.yearly, price.currency_code) }} / year (save
|
||||
{{
|
||||
calculateSavings(price.prices.intervals.monthly, price.prices.intervals.yearly)
|
||||
}}%)!
|
||||
@@ -188,7 +182,6 @@
|
||||
v-tooltip="
|
||||
midasCharge.subscription_interval === 'yearly'
|
||||
? `Monthly billing will cost you an additional ${formatPrice(
|
||||
vintl.locale,
|
||||
oppositePrice * 12 - midasCharge.amount,
|
||||
midasCharge.currency_code,
|
||||
)} per year`
|
||||
@@ -307,7 +300,6 @@
|
||||
<span class="text-contrast">
|
||||
{{
|
||||
formatPrice(
|
||||
vintl.locale,
|
||||
getProductPrice(getPyroProduct(subscription), subscription.interval)
|
||||
.prices.intervals[subscription.interval],
|
||||
getProductPrice(getPyroProduct(subscription), subscription.interval)
|
||||
@@ -333,7 +325,6 @@
|
||||
<span class="font-semibold text-contrast">
|
||||
{{
|
||||
formatPrice(
|
||||
vintl.locale,
|
||||
getPyroCharge(subscription).amount,
|
||||
getPyroCharge(subscription).currency_code,
|
||||
)
|
||||
@@ -351,13 +342,13 @@
|
||||
</div>
|
||||
<div v-if="getPyroCharge(subscription)" class="mb-4 flex flex-col items-end">
|
||||
<span class="text-sm text-secondary">
|
||||
Since {{ $dayjs(subscription.created).format('MMMM D, YYYY') }}
|
||||
Since {{ formatDate(subscription.created) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="getPyroCharge(subscription).status === 'open'"
|
||||
class="text-sm text-secondary"
|
||||
>
|
||||
Renews {{ $dayjs(getPyroCharge(subscription).due).format('MMMM D, YYYY') }}
|
||||
Renews {{ formatDate(getPyroCharge(subscription).due) }}
|
||||
</span>
|
||||
<span
|
||||
v-if="
|
||||
@@ -371,7 +362,7 @@
|
||||
Switches to
|
||||
{{ getPyroCharge(subscription).subscription_interval }}
|
||||
billing on
|
||||
{{ $dayjs(getPyroCharge(subscription).due).format('MMMM D, YYYY') }}
|
||||
{{ formatDate(getPyroCharge(subscription).due) }}
|
||||
</span>
|
||||
<span
|
||||
v-else-if="getPyroCharge(subscription).status === 'processing'"
|
||||
@@ -384,7 +375,7 @@
|
||||
v-else-if="getPyroCharge(subscription).status === 'cancelled'"
|
||||
class="text-sm text-secondary"
|
||||
>
|
||||
Expires {{ $dayjs(getPyroCharge(subscription).due).format('MMMM D, YYYY') }}
|
||||
Expires {{ formatDate(getPyroCharge(subscription).due) }}
|
||||
</span>
|
||||
<span
|
||||
v-else-if="getPyroCharge(subscription).status === 'failed'"
|
||||
@@ -624,9 +615,11 @@ import {
|
||||
paymentMethodMessages,
|
||||
PurchaseModal,
|
||||
ServerListing,
|
||||
useFormatDateTime,
|
||||
useFormatPrice,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
import { calculateSavings, formatPrice, getCurrency } from '@modrinth/utils'
|
||||
import { calculateSavings, getCurrency } from '@modrinth/utils'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
import { useBaseFetch } from '@/composables/fetch.js'
|
||||
@@ -655,8 +648,13 @@ useHead({
|
||||
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const vintl = useVIntl()
|
||||
const { formatMessage } = vintl
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatPrice = useFormatPrice()
|
||||
const formatDate = useFormatDateTime({
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric',
|
||||
})
|
||||
|
||||
const deleteModalMessages = defineMessages({
|
||||
title: {
|
||||
|
||||
@@ -119,16 +119,7 @@
|
||||
<CopyCode :text="pat.access_token" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span
|
||||
v-tooltip="
|
||||
pat.last_used
|
||||
? formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||
date: new Date(pat.last_used),
|
||||
time: new Date(pat.last_used),
|
||||
})
|
||||
: null
|
||||
"
|
||||
>
|
||||
<span v-tooltip="pat.last_used ? formatDateTime(pat.last_used) : null">
|
||||
<template v-if="pat.last_used">
|
||||
{{
|
||||
formatMessage(tokenMessages.lastUsed, {
|
||||
@@ -139,14 +130,7 @@
|
||||
<template v-else>{{ formatMessage(tokenMessages.neverUsed) }}</template>
|
||||
</span>
|
||||
⋅
|
||||
<span
|
||||
v-tooltip="
|
||||
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||
date: new Date(pat.expires),
|
||||
time: new Date(pat.expires),
|
||||
})
|
||||
"
|
||||
>
|
||||
<span v-tooltip="formatDateTime(pat.expires)">
|
||||
<template v-if="new Date(pat.expires) > new Date()">
|
||||
{{
|
||||
formatMessage(tokenMessages.expiresIn, {
|
||||
@@ -163,14 +147,7 @@
|
||||
</template>
|
||||
</span>
|
||||
⋅
|
||||
<span
|
||||
v-tooltip="
|
||||
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||
date: new Date(pat.created),
|
||||
time: new Date(pat.created),
|
||||
})
|
||||
"
|
||||
>
|
||||
<span v-tooltip="formatDateTime(pat.created)">
|
||||
{{
|
||||
formatMessage(commonMessages.createdAgoLabel, {
|
||||
ago: formatRelativeTime(pat.created),
|
||||
@@ -222,6 +199,7 @@ import {
|
||||
injectNotificationManager,
|
||||
IntlFormatted,
|
||||
StyledInput,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -240,6 +218,10 @@ const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const createModalMessages = defineMessages({
|
||||
createTitle: {
|
||||
|
||||
@@ -15,14 +15,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<template v-if="session.city">{{ session.city }}, {{ session.country }} ⋅ </template>
|
||||
<span
|
||||
v-tooltip="
|
||||
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||
date: new Date(session.last_login),
|
||||
time: new Date(session.last_login),
|
||||
})
|
||||
"
|
||||
>
|
||||
<span v-tooltip="formatDateTime(session.last_login)">
|
||||
{{
|
||||
formatMessage(messages.lastAccessedAgoLabel, {
|
||||
ago: formatRelativeTime(session.last_login),
|
||||
@@ -30,14 +23,7 @@
|
||||
}}
|
||||
</span>
|
||||
⋅
|
||||
<span
|
||||
v-tooltip="
|
||||
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||
date: new Date(session.created),
|
||||
time: new Date(session.created),
|
||||
})
|
||||
"
|
||||
>
|
||||
<span v-tooltip="formatDateTime(session.created)">
|
||||
{{
|
||||
formatMessage(messages.createdAgoLabel, {
|
||||
ago: formatRelativeTime(session.created),
|
||||
@@ -62,6 +48,7 @@ import {
|
||||
commonSettingsMessages,
|
||||
defineMessages,
|
||||
injectNotificationManager,
|
||||
useFormatDateTime,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -73,6 +60,10 @@ definePageMeta({
|
||||
const { addNotification } = injectNotificationManager()
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const messages = defineMessages({
|
||||
currentSessionLabel: {
|
||||
|
||||
@@ -155,27 +155,24 @@
|
||||
{{
|
||||
formatMessage(messages.profileProjectsLabel, {
|
||||
count: formatCompactNumber(projects?.length || 0),
|
||||
countPlural: formatCompactNumberPlural(projects?.length || 0),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="sumDownloads.toLocaleString()"
|
||||
v-tooltip="formatNumber(sumDownloads)"
|
||||
class="flex items-center gap-2 border-0 border-r border-solid border-divider pr-4 font-semibold"
|
||||
>
|
||||
<DownloadIcon class="h-6 w-6 text-secondary" />
|
||||
{{
|
||||
formatMessage(messages.profileDownloadsLabel, {
|
||||
count: formatCompactNumber(sumDownloads),
|
||||
countPlural: formatCompactNumberPlural(sumDownloads),
|
||||
})
|
||||
}}
|
||||
</div>
|
||||
<div
|
||||
v-tooltip="
|
||||
formatMessage(commonMessages.dateAtTimeTooltip, {
|
||||
date: new Date(user.created),
|
||||
time: new Date(user.created),
|
||||
})
|
||||
"
|
||||
v-tooltip="formatDateTime(user.created)"
|
||||
class="flex items-center gap-2 font-semibold"
|
||||
>
|
||||
<CalendarIcon class="h-6 w-6 text-secondary" />
|
||||
@@ -502,6 +499,9 @@ import {
|
||||
ProjectCard,
|
||||
ProjectCardList,
|
||||
TagItem,
|
||||
useCompactNumber,
|
||||
useFormatDateTime,
|
||||
useFormatNumber,
|
||||
useRelativeTime,
|
||||
useVIntl,
|
||||
} from '@modrinth/ui'
|
||||
@@ -528,12 +528,14 @@ const cosmetics = useCosmetics()
|
||||
const tags = useGeneratedState()
|
||||
const config = useRuntimeConfig()
|
||||
|
||||
const vintl = useVIntl()
|
||||
const { formatMessage } = vintl
|
||||
|
||||
const formatCompactNumber = useCompactNumber(true)
|
||||
|
||||
const { formatMessage } = useVIntl()
|
||||
const formatNumber = useFormatNumber()
|
||||
const { formatCompactNumber, formatCompactNumberPlural } = useCompactNumber()
|
||||
const formatRelativeTime = useRelativeTime()
|
||||
const formatDateTime = useFormatDateTime({
|
||||
timeStyle: 'short',
|
||||
dateStyle: 'long',
|
||||
})
|
||||
|
||||
const { addNotification } = injectNotificationManager()
|
||||
|
||||
@@ -542,11 +544,11 @@ const baseId = useId()
|
||||
const messages = defineMessages({
|
||||
profileProjectsLabel: {
|
||||
id: 'profile.label.projects',
|
||||
defaultMessage: '{count} {count, plural, one {project} other {projects}}',
|
||||
defaultMessage: '{count} {countPlural, plural, one {project} other {projects}}',
|
||||
},
|
||||
profileDownloadsLabel: {
|
||||
id: 'profile.label.downloads',
|
||||
defaultMessage: '{count} {count, plural, one {download} other {downloads}}',
|
||||
defaultMessage: '{count} {countPlural, plural, one {download} other {downloads}}',
|
||||
},
|
||||
profileJoinedLabel: {
|
||||
id: 'profile.label.joined',
|
||||
|
||||
Reference in New Issue
Block a user