From 9dddb1eb6db020a5d4ce04beccad6051215396f7 Mon Sep 17 00:00:00 2001 From: Norm Date: Tue, 4 Oct 2022 18:05:41 +0000 Subject: [PATCH] client: Use named constants for time calculations (#183) Constants were borrowed from `const.ts` from the backend but also includes `WEEK`, `MONTH`, and `YEAR` constants as well. Co-authored-by: Francis Dinh Reviewed-on: https://akkoma.dev/FoundKeyGang/FoundKey/pulls/183 --- .../client/src/components/birthday-date.vue | 3 +- .../client/src/components/global/time.vue | 31 ++++++++++--------- .../client/src/components/poll-editor.vue | 22 ++++++------- packages/client/src/components/signin.vue | 3 +- packages/client/src/const.ts | 15 +++++++-- packages/client/src/pages/settings/2fa.vue | 3 +- packages/client/src/scripts/get-user-menu.ts | 25 ++++++++------- packages/client/src/scripts/theme.ts | 3 +- packages/client/src/scripts/time.ts | 6 ++-- packages/client/src/widgets/memo.vue | 3 +- 10 files changed, 65 insertions(+), 49 deletions(-) diff --git a/packages/client/src/components/birthday-date.vue b/packages/client/src/components/birthday-date.vue index 675f3bca6..a8da9f4ac 100644 --- a/packages/client/src/components/birthday-date.vue +++ b/packages/client/src/components/birthday-date.vue @@ -4,6 +4,7 @@ diff --git a/packages/client/src/components/global/time.vue b/packages/client/src/components/global/time.vue index e7aa0fa4d..cc872096c 100644 --- a/packages/client/src/components/global/time.vue +++ b/packages/client/src/components/global/time.vue @@ -10,6 +10,7 @@ import { onUnmounted } from 'vue'; import { i18n } from '@/i18n'; import { lang } from '@/config'; +import { DAY, WEEK, MONTH, YEAR, HOUR, MINUTE, SECOND } from '@/const'; const props = withDefaults(defineProps<{ time: Date | string; @@ -34,26 +35,26 @@ const absolute = ((): string => { let now = $ref(new Date()); const relative = $computed(() => { - const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; + const ago = now.getTime() - _time.getTime(); - if (ago >= 31536000) { - return i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }); - } else if (ago >= 2592000) { - return i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }); - } else if (ago >= 604800) { - return i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }); - } else if (ago >= 86400) { - return i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }); + if (ago >= YEAR) { + return i18n.t('_ago.yearsAgo', { n: Math.round(ago / YEAR).toString() }); + } else if (ago >= MONTH) { + return i18n.t('_ago.monthsAgo', { n: Math.round(ago / MONTH).toString() }); + } else if (ago >= WEEK) { + return i18n.t('_ago.weeksAgo', { n: Math.round(ago / WEEK).toString() }); + } else if (ago >= DAY) { + return i18n.t('_ago.daysAgo', { n: Math.round(ago / DAY).toString() }); } // if the format is 'date', the relative date precision is no more than days ago if (props.format !== 'date') { - if (ago >= 3600) { - return i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }); - } else if (ago >= 60) { - return i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }); - } else if (ago >= 10) { - return i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }); + if (ago >= HOUR) { + return i18n.t('_ago.hoursAgo', { n: Math.round(ago / HOUR).toString() }); + } else if (ago >= MINUTE) { + return i18n.t('_ago.minutesAgo', { n: (~~(ago / MINUTE)).toString() }); + } else if (ago >= 10 * SECOND) { + return i18n.t('_ago.secondsAgo', { n: (~~(ago % MINUTE)).toString() }); } } diff --git a/packages/client/src/components/poll-editor.vue b/packages/client/src/components/poll-editor.vue index b2a98b7ee..e4247f57c 100644 --- a/packages/client/src/components/poll-editor.vue +++ b/packages/client/src/components/poll-editor.vue @@ -56,6 +56,7 @@ import MkButton from './ui/button.vue'; import { formatDateTimeString } from '@/scripts/format-time-string'; import { addTime } from '@/scripts/time'; import { i18n } from '@/i18n'; +import { DAY, HOUR, MINUTE, SECOND } from '@/const'; const props = defineProps<{ modelValue: { @@ -92,11 +93,11 @@ if (props.modelValue.expiresAt) { expiration.value = 'infinite'; } -function onInput(i, value) { +function onInput(i: number, value: string): void { choices.value[i] = value; } -function add() { +function add(): void { choices.value.push(''); // TODO // nextTick(() => { @@ -104,25 +105,22 @@ function add() { // }); } -function remove(i) { +function remove(i: number): void { choices.value = choices.value.filter((_, _i) => _i !== i); } function get() { - const calcAt = () => { + const calcAt = (): number => { return new Date(`${atDate.value} ${atTime.value}`).getTime(); }; - const calcAfter = () => { + const calcAfter = (): number | null => { let base = parseInt(after.value); switch (unit.value) { - case 'day': base *= 24; - // fallthrough - case 'hour': base *= 60; - // fallthrough - case 'minute': base *= 60; - // fallthrough - case 'second': return base *= 1000; + case 'day': return base * DAY; + case 'hour': return base * HOUR; + case 'minute': return base * MINUTE; + case 'second': return base * SECOND; default: return null; } }; diff --git a/packages/client/src/components/signin.vue b/packages/client/src/components/signin.vue index 6fb37f4fd..112e13bd3 100644 --- a/packages/client/src/components/signin.vue +++ b/packages/client/src/components/signin.vue @@ -61,6 +61,7 @@ import * as os from '@/os'; import { login } from '@/account'; import { instance } from '@/instance'; import { i18n } from '@/i18n'; +import { MINUTE } from '@/const'; let signing = $ref(false); let user = $ref(null); @@ -124,7 +125,7 @@ function queryKey() { type: 'public-key', transports: ['usb', 'nfc', 'ble', 'internal'], })), - timeout: 60 * 1000, + timeout: MINUTE, }, }).catch(() => { queryingKey = false; diff --git a/packages/client/src/const.ts b/packages/client/src/const.ts index 505cf2748..51b69f86b 100644 --- a/packages/client/src/const.ts +++ b/packages/client/src/const.ts @@ -1,6 +1,15 @@ -// ブラウザで直接表示することを許可するファイルの種類のリスト -// ここに含まれないものは application/octet-stream としてレスポンスされる -// SVGはXSSを生むので許可しない +// Time constants +export const SECOND = 1000; +export const MINUTE = 60 * SECOND; +export const HOUR = 60 * MINUTE; +export const DAY = 24 * HOUR; +export const WEEK = 7 * DAY; +export const MONTH = 30 * DAY; +export const YEAR = 365 * DAY; + +// List of file types allowed to be viewed directly in the browser. +// Anything not included here will be reported as application/octet-stream +// SVG is not allowed because it can lead to XSS export const FILE_TYPE_BROWSERSAFE = [ // Images 'image/png', diff --git a/packages/client/src/pages/settings/2fa.vue b/packages/client/src/pages/settings/2fa.vue index 4f3730aa6..412122863 100644 --- a/packages/client/src/pages/settings/2fa.vue +++ b/packages/client/src/pages/settings/2fa.vue @@ -75,6 +75,7 @@ import MkSwitch from '@/components/form/switch.vue'; import * as os from '@/os'; import { $i } from '@/account'; import { i18n } from '@/i18n'; +import { MINUTE } from '@/const'; const twoFactorData = ref(null); const supportsCredentials = ref(!!navigator.credentials); @@ -188,7 +189,7 @@ function addSecurityKey() { displayName: $i!.name, }, pubKeyCredParams: [{ alg: -7, type: 'public-key' }], - timeout: 60000, + timeout: MINUTE, attestation: 'direct', }, saving: true, diff --git a/packages/client/src/scripts/get-user-menu.ts b/packages/client/src/scripts/get-user-menu.ts index a6fa61b22..3721c5711 100644 --- a/packages/client/src/scripts/get-user-menu.ts +++ b/packages/client/src/scripts/get-user-menu.ts @@ -7,11 +7,12 @@ import * as os from '@/os'; import { userActions } from '@/store'; import { $i, iAmModerator } from '@/account'; import { mainRouter } from '@/router'; +import { DAY, HOUR, MINUTE, WEEK } from '@/const'; export function getUserMenu(user) { const meId = $i ? $i.id : null; - async function pushList() { + async function pushList(): Promise { const t = i18n.ts.selectList; // なぜか後で参照すると null になるので最初にメモリに確保しておく const lists = await os.api('users/lists/list'); if (lists.length === 0) { @@ -34,7 +35,7 @@ export function getUserMenu(user) { }); } - async function inviteGroup() { + async function inviteGroup(): Promise { const groups = await os.api('users/groups/owned'); if (groups.length === 0) { os.alert({ @@ -56,7 +57,7 @@ export function getUserMenu(user) { }); } - async function toggleMute() { + async function toggleMute(): Promise { if (user.isMuted) { os.apiWithDialog('mute/delete', { userId: user.id, @@ -82,10 +83,10 @@ export function getUserMenu(user) { if (canceled) return; const expiresAt = period === 'indefinitely' ? null - : period === 'tenMinutes' ? Date.now() + (1000 * 60 * 10) - : period === 'oneHour' ? Date.now() + (1000 * 60 * 60) - : period === 'oneDay' ? Date.now() + (1000 * 60 * 60 * 24) - : period === 'oneWeek' ? Date.now() + (1000 * 60 * 60 * 24 * 7) + : period === 'tenMinutes' ? Date.now() + (MINUTE * 10) + : period === 'oneHour' ? Date.now() + HOUR + : period === 'oneDay' ? Date.now() + DAY + : period === 'oneWeek' ? Date.now() + WEEK : null; os.apiWithDialog('mute/create', { @@ -97,7 +98,7 @@ export function getUserMenu(user) { } } - async function toggleBlock() { + async function toggleBlock(): Promise { if (!await getConfirmed(user.isBlocking ? i18n.ts.unblockConfirm : i18n.ts.blockConfirm)) return; os.apiWithDialog(user.isBlocking ? 'blocking/delete' : 'blocking/create', { @@ -107,7 +108,7 @@ export function getUserMenu(user) { }); } - async function toggleSilence() { + async function toggleSilence(): Promise { if (!await getConfirmed(i18n.t(user.isSilenced ? 'unsilenceConfirm' : 'silenceConfirm'))) return; os.apiWithDialog(user.isSilenced ? 'admin/unsilence-user' : 'admin/silence-user', { @@ -117,7 +118,7 @@ export function getUserMenu(user) { }); } - async function toggleSuspend() { + async function toggleSuspend(): Promise { if (!await getConfirmed(i18n.t(user.isSuspended ? 'unsuspendConfirm' : 'suspendConfirm'))) return; os.apiWithDialog(user.isSuspended ? 'admin/unsuspend-user' : 'admin/suspend-user', { @@ -127,7 +128,7 @@ export function getUserMenu(user) { }); } - function reportAbuse() { + function reportAbuse(): void { os.popup(defineAsyncComponent(() => import('@/components/abuse-report-window.vue')), { user, }, {}, 'closed'); @@ -143,7 +144,7 @@ export function getUserMenu(user) { return !confirm.canceled; } - async function invalidateFollow() { + async function invalidateFollow(): Promise { os.apiWithDialog('following/invalidate', { userId: user.id, }).then(() => { diff --git a/packages/client/src/scripts/theme.ts b/packages/client/src/scripts/theme.ts index 147f5d383..0025d26e2 100644 --- a/packages/client/src/scripts/theme.ts +++ b/packages/client/src/scripts/theme.ts @@ -13,6 +13,7 @@ export type Theme = { import lightTheme from '@/themes/_light.json5'; import darkTheme from '@/themes/_dark.json5'; +import { SECOND } from '@/const'; export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X')); @@ -53,7 +54,7 @@ export function applyTheme(theme: Theme, persist = true) { timeout = window.setTimeout(() => { document.documentElement.classList.remove('_themeChanging_'); - }, 1000); + }, SECOND); // Deep copy const _theme = JSON.parse(JSON.stringify(theme)); diff --git a/packages/client/src/scripts/time.ts b/packages/client/src/scripts/time.ts index e720a7aa9..2e578000c 100644 --- a/packages/client/src/scripts/time.ts +++ b/packages/client/src/scripts/time.ts @@ -1,6 +1,8 @@ +import { DAY, HOUR } from '@/const'; + const dateTimeIntervals = { - 'day': 86400000, - 'hour': 3600000, + 'day': DAY, + 'hour': HOUR, 'ms': 1, }; diff --git a/packages/client/src/widgets/memo.vue b/packages/client/src/widgets/memo.vue index 7ecc2e78d..130583fe4 100644 --- a/packages/client/src/widgets/memo.vue +++ b/packages/client/src/widgets/memo.vue @@ -16,6 +16,7 @@ import { GetFormResultType } from '@/scripts/form'; import MkContainer from '@/components/ui/container.vue'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; +import { SECOND } from '@/const'; const name = 'memo'; @@ -54,7 +55,7 @@ const saveMemo = (): void => { const onChange = (): void => { changed.value = true; window.clearTimeout(timeoutId); - timeoutId = window.setTimeout(saveMemo, 1000); + timeoutId = window.setTimeout(saveMemo, SECOND); }; watch(() => defaultStore.reactiveState.memo, newText => {