mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-22 01:41:02 +00:00
Unify date/time dependencies (#2891)
Remove all date/time-related dependencies from the ui except `dayjs` and use `dayjs` for all tasks. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
6c9ff24ba6
commit
8c6738e2bb
12 changed files with 181 additions and 146 deletions
2
web/.gitignore
vendored
2
web/.gitignore
vendored
|
@ -3,4 +3,4 @@ node_modules
|
|||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
src/assets/timeAgoLocales
|
||||
src/assets/dayjsLocales
|
||||
|
|
|
@ -23,8 +23,6 @@
|
|||
"ansi_up": "^6.0.0",
|
||||
"dayjs": "^1.11.9",
|
||||
"fuse.js": "^7.0.0",
|
||||
"humanize-duration": "^3.28.0",
|
||||
"javascript-time-ago": "^2.5.9",
|
||||
"lodash": "^4.17.21",
|
||||
"node-emoji": "^2.0.0",
|
||||
"pinia": "^2.1.4",
|
||||
|
@ -35,8 +33,6 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@iconify/json": "^2.2.131",
|
||||
"@types/humanize-duration": "^3.27.1",
|
||||
"@types/javascript-time-ago": "^2.0.3",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/node": "^20.0.0",
|
||||
"@types/node-emoji": "^1.8.2",
|
||||
|
@ -56,6 +52,7 @@
|
|||
"eslint-plugin-vue": "^9.17.0",
|
||||
"eslint-plugin-vue-scoped-css": "^2.5.0",
|
||||
"prettier": "^3.0.0",
|
||||
"replace-in-file": "^7.0.2",
|
||||
"tinycolor2": "^1.6.0",
|
||||
"typescript": "5.3.2",
|
||||
"unplugin-icons": "^0.18.0",
|
||||
|
|
|
@ -26,12 +26,6 @@ dependencies:
|
|||
fuse.js:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
humanize-duration:
|
||||
specifier: ^3.28.0
|
||||
version: 3.31.0
|
||||
javascript-time-ago:
|
||||
specifier: ^2.5.9
|
||||
version: 2.5.9
|
||||
lodash:
|
||||
specifier: ^4.17.21
|
||||
version: 4.17.21
|
||||
|
@ -58,12 +52,6 @@ devDependencies:
|
|||
'@iconify/json':
|
||||
specifier: ^2.2.131
|
||||
version: 2.2.143
|
||||
'@types/humanize-duration':
|
||||
specifier: ^3.27.1
|
||||
version: 3.27.3
|
||||
'@types/javascript-time-ago':
|
||||
specifier: ^2.0.3
|
||||
version: 2.0.7
|
||||
'@types/lodash':
|
||||
specifier: ^4.14.195
|
||||
version: 4.14.201
|
||||
|
@ -121,6 +109,9 @@ devDependencies:
|
|||
prettier:
|
||||
specifier: ^3.0.0
|
||||
version: 3.1.0
|
||||
replace-in-file:
|
||||
specifier: ^7.0.2
|
||||
version: 7.0.2
|
||||
tinycolor2:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.0
|
||||
|
@ -922,14 +913,6 @@ packages:
|
|||
/@types/estree@1.0.5:
|
||||
resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
|
||||
|
||||
/@types/humanize-duration@3.27.3:
|
||||
resolution: {integrity: sha512-wiiiFYjnrYDJE/ujU7wS/NShqp12IKrejozjDtcejP0zYi+cjyjVcfZHwcFUDKVJ7tHGsmgeW2ED92ABIIjfpg==}
|
||||
dev: true
|
||||
|
||||
/@types/javascript-time-ago@2.0.7:
|
||||
resolution: {integrity: sha512-+sZQnKxkGeDHtX7jJ/iVucZ8Gg8CTnJLpNwynHX+V/G6Z9n6V93+6DO/sv8QU6STAcq04xPw+btFf/OKjCkT0A==}
|
||||
dev: true
|
||||
|
||||
/@types/json-schema@7.0.15:
|
||||
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
|
||||
dev: true
|
||||
|
@ -1528,6 +1511,15 @@ packages:
|
|||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
/cliui@8.0.1:
|
||||
resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
wrap-ansi: 7.0.0
|
||||
dev: true
|
||||
|
||||
/color-convert@1.9.3:
|
||||
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||
dependencies:
|
||||
|
@ -1766,6 +1758,10 @@ packages:
|
|||
resolution: {integrity: sha512-soytjxwbgcCu7nh5Pf4S2/4wa6UIu+A3p03U2yVr53qGxi1/VTR3ENI+p50v+UxqqZAfl48j3z55ud7VHIOr9w==}
|
||||
dev: true
|
||||
|
||||
/emoji-regex@8.0.0:
|
||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||
dev: true
|
||||
|
||||
/emojilib@2.4.0:
|
||||
resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==}
|
||||
dev: false
|
||||
|
@ -2367,6 +2363,11 @@ packages:
|
|||
engines: {node: '>=6.9.0'}
|
||||
dev: true
|
||||
|
||||
/get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
dev: true
|
||||
|
||||
/get-intrinsic@1.2.2:
|
||||
resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
|
||||
dependencies:
|
||||
|
@ -2418,6 +2419,17 @@ packages:
|
|||
path-is-absolute: 1.0.1
|
||||
dev: true
|
||||
|
||||
/glob@8.1.0:
|
||||
resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
fs.realpath: 1.0.0
|
||||
inflight: 1.0.6
|
||||
inherits: 2.0.4
|
||||
minimatch: 5.1.6
|
||||
once: 1.4.0
|
||||
dev: true
|
||||
|
||||
/globals@11.12.0:
|
||||
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
|
||||
engines: {node: '>=4'}
|
||||
|
@ -2523,10 +2535,6 @@ packages:
|
|||
engines: {node: '>=16.17.0'}
|
||||
dev: true
|
||||
|
||||
/humanize-duration@3.31.0:
|
||||
resolution: {integrity: sha512-fRrehgBG26NNZysRlTq1S+HPtDpp3u+Jzdc/d5A4cEzOD86YLAkDaJyJg8krSdCi7CJ+s7ht3fwRj8Dl+Btd0w==}
|
||||
dev: false
|
||||
|
||||
/ignore@5.3.0:
|
||||
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
|
||||
engines: {node: '>= 4'}
|
||||
|
@ -2627,6 +2635,11 @@ packages:
|
|||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
/is-fullwidth-code-point@3.0.0:
|
||||
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
|
||||
engines: {node: '>=8'}
|
||||
dev: true
|
||||
|
||||
/is-glob@4.0.3:
|
||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -2728,12 +2741,6 @@ packages:
|
|||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
dev: true
|
||||
|
||||
/javascript-time-ago@2.5.9:
|
||||
resolution: {integrity: sha512-pQ8mNco/9g9TqWXWWjP0EWl6i/lAQScOyEeXy5AB+f7MfLSdgyV9BJhiOD1zrIac/lrxPYOWNbyl/IW8CW5n0A==}
|
||||
dependencies:
|
||||
relative-time-format: 1.1.6
|
||||
dev: false
|
||||
|
||||
/jiti@1.21.0:
|
||||
resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==}
|
||||
hasBin: true
|
||||
|
@ -2905,6 +2912,13 @@ packages:
|
|||
brace-expansion: 1.1.11
|
||||
dev: true
|
||||
|
||||
/minimatch@5.1.6:
|
||||
resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
brace-expansion: 2.0.1
|
||||
dev: true
|
||||
|
||||
/minimatch@9.0.3:
|
||||
resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
|
||||
engines: {node: '>=16 || 14 >=14.17'}
|
||||
|
@ -3279,9 +3293,20 @@ packages:
|
|||
set-function-name: 2.0.1
|
||||
dev: true
|
||||
|
||||
/relative-time-format@1.1.6:
|
||||
resolution: {integrity: sha512-aCv3juQw4hT1/P/OrVltKWLlp15eW1GRcwP1XdxHrPdZE9MtgqFpegjnTjLhi2m2WI9MT/hQQtE+tjEWG1hgkQ==}
|
||||
dev: false
|
||||
/replace-in-file@7.0.2:
|
||||
resolution: {integrity: sha512-tPG+Qmqf+x2Rf1WVdb/9B5tFIf6KJ5hs3fgxh1OTzPRUugPPvyAva7NvCJtnSpmyq6r+ABYcuUOqZkm6yzGSUw==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
glob: 8.1.0
|
||||
yargs: 17.7.2
|
||||
dev: true
|
||||
|
||||
/require-directory@2.1.1:
|
||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/resolve-from@4.0.0:
|
||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||
|
@ -3454,6 +3479,15 @@ packages:
|
|||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/string-width@4.2.3:
|
||||
resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
emoji-regex: 8.0.0
|
||||
is-fullwidth-code-point: 3.0.0
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/string.prototype.trim@1.2.8:
|
||||
resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
@ -3982,6 +4016,15 @@ packages:
|
|||
hasBin: true
|
||||
dev: true
|
||||
|
||||
/wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
string-width: 4.2.3
|
||||
strip-ansi: 6.0.1
|
||||
dev: true
|
||||
|
||||
/wrappy@1.0.2:
|
||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||
dev: true
|
||||
|
@ -3991,6 +4034,11 @@ packages:
|
|||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/y18n@5.0.8:
|
||||
resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
/yallist@3.1.1:
|
||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||
dev: true
|
||||
|
@ -4012,6 +4060,24 @@ packages:
|
|||
engines: {node: '>= 14'}
|
||||
dev: false
|
||||
|
||||
/yargs-parser@21.1.1:
|
||||
resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/yargs@17.7.2:
|
||||
resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
cliui: 8.0.1
|
||||
escalade: 3.1.1
|
||||
get-caller-file: 2.0.5
|
||||
require-directory: 2.1.1
|
||||
string-width: 4.2.3
|
||||
y18n: 5.0.8
|
||||
yargs-parser: 21.1.1
|
||||
dev: true
|
||||
|
||||
/yocto-queue@0.1.0:
|
||||
resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<Badge v-if="agent.capacity" :label="$t('admin.settings.agents.capacity.badge')" :value="agent.capacity" />
|
||||
</span>
|
||||
<span class="ml-2">{{
|
||||
agent.last_contact ? timeAgo.format(agent.last_contact * 1000) : $t('admin.settings.agents.never')
|
||||
agent.last_contact ? date.timeAgo(agent.last_contact * 1000) : $t('admin.settings.agents.never')
|
||||
}}</span>
|
||||
</span>
|
||||
<IconButton
|
||||
|
@ -98,7 +98,7 @@
|
|||
<TextField
|
||||
:model-value="
|
||||
selectedAgent.last_contact
|
||||
? timeAgo.format(selectedAgent.last_contact * 1000)
|
||||
? date.timeAgo(selectedAgent.last_contact * 1000)
|
||||
: $t('admin.settings.agents.never')
|
||||
"
|
||||
disabled
|
||||
|
@ -135,14 +135,14 @@ import TextField from '~/components/form/TextField.vue';
|
|||
import Settings from '~/components/layout/Settings.vue';
|
||||
import useApiClient from '~/compositions/useApiClient';
|
||||
import { useAsyncAction } from '~/compositions/useAsyncAction';
|
||||
import { useDate } from '~/compositions/useDate';
|
||||
import useNotifications from '~/compositions/useNotifications';
|
||||
import { usePagination } from '~/compositions/usePaginate';
|
||||
import useTimeAgo from '~/compositions/useTimeAgo';
|
||||
import { Agent } from '~/lib/api/types';
|
||||
|
||||
const apiClient = useApiClient();
|
||||
const notifications = useNotifications();
|
||||
const timeAgo = useTimeAgo();
|
||||
const date = useDate();
|
||||
const { t } = useI18n();
|
||||
|
||||
const selectedAgent = ref<Partial<Agent>>();
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, toRef } from 'vue';
|
||||
|
||||
import { useDate } from '~/compositions/useDate';
|
||||
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
||||
import { PipelineStep, PipelineWorkflow } from '~/lib/api/types';
|
||||
import { durationAsNumber } from '~/utils/duration';
|
||||
|
||||
const props = defineProps<{
|
||||
step?: PipelineStep;
|
||||
|
@ -16,6 +16,7 @@ const props = defineProps<{
|
|||
|
||||
const step = toRef(props, 'step');
|
||||
const workflow = toRef(props, 'workflow');
|
||||
const { durationAsNumber } = useDate();
|
||||
|
||||
const durationRaw = computed(() => {
|
||||
const start = (step.value ? step.value?.start_time : workflow.value?.start_time) || 0;
|
||||
|
|
|
@ -18,8 +18,6 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { useLocalStorage } from '@vueuse/core';
|
||||
import dayjs from 'dayjs';
|
||||
import TimeAgo from 'javascript-time-ago';
|
||||
import { SUPPORTED_LOCALES } from 'virtual:vue-i18n-supported-locales';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
@ -44,8 +42,6 @@ const selectedLocale = computed<string>({
|
|||
async set(_selectedLocale) {
|
||||
await setI18nLanguage(_selectedLocale);
|
||||
storedLocale.value = _selectedLocale;
|
||||
dayjs.locale(_selectedLocale);
|
||||
TimeAgo.setDefaultLocale(_selectedLocale);
|
||||
},
|
||||
get() {
|
||||
return storedLocale.value;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import dayjs from 'dayjs';
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat';
|
||||
import duration from 'dayjs/plugin/duration';
|
||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||
import timezone from 'dayjs/plugin/timezone';
|
||||
import utc from 'dayjs/plugin/utc';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
@ -7,13 +9,43 @@ import { useI18n } from 'vue-i18n';
|
|||
dayjs.extend(timezone);
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(advancedFormat);
|
||||
dayjs.extend(relativeTime);
|
||||
dayjs.extend(duration);
|
||||
|
||||
export function useDate() {
|
||||
function toLocaleString(date: Date) {
|
||||
return dayjs(date).format(useI18n().t('time.tmpl'));
|
||||
}
|
||||
|
||||
function timeAgo(date: Date | string | number) {
|
||||
return dayjs().to(dayjs(date));
|
||||
}
|
||||
|
||||
function prettyDuration(durationMs: number) {
|
||||
return dayjs.duration(durationMs).humanize();
|
||||
}
|
||||
|
||||
const addedLocales = ['en'];
|
||||
|
||||
async function setDayjsLocale(locale: string) {
|
||||
if (!addedLocales.includes(locale)) {
|
||||
const l = await import(`~/assets/dayjsLocales/${locale}.js`);
|
||||
dayjs.locale(l.default);
|
||||
} else {
|
||||
dayjs.locale(locale);
|
||||
}
|
||||
}
|
||||
|
||||
function durationAsNumber(durationMs: number): string {
|
||||
const dur = dayjs.duration(durationMs);
|
||||
return dur.format(dur.hours() > 1 ? 'HH:mm:ss' : 'mm:ss');
|
||||
}
|
||||
|
||||
return {
|
||||
toLocaleString,
|
||||
timeAgo,
|
||||
prettyDuration,
|
||||
setDayjsLocale,
|
||||
durationAsNumber,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,8 +3,9 @@ import { createI18n } from 'vue-i18n';
|
|||
|
||||
import { getUserLanguage } from '~/utils/locale';
|
||||
|
||||
import { loadTimeAgoLocale } from './useTimeAgo';
|
||||
import { useDate } from './useDate';
|
||||
|
||||
const { setDayjsLocale } = useDate();
|
||||
const userLanguage = getUserLanguage();
|
||||
const fallbackLocale = 'en';
|
||||
export const i18n = createI18n({
|
||||
|
@ -14,13 +15,11 @@ export const i18n = createI18n({
|
|||
fallbackLocale,
|
||||
});
|
||||
|
||||
export const loadLocaleMessages = async (locale: string) => {
|
||||
const loadLocaleMessages = async (locale: string) => {
|
||||
const { default: messages } = await import(`~/assets/locales/${locale}.json`);
|
||||
|
||||
i18n.global.setLocaleMessage(locale, messages);
|
||||
|
||||
loadTimeAgoLocale(locale);
|
||||
|
||||
return nextTick();
|
||||
};
|
||||
|
||||
|
@ -29,7 +28,9 @@ export const setI18nLanguage = async (lang: string): Promise<void> => {
|
|||
await loadLocaleMessages(lang);
|
||||
}
|
||||
i18n.global.locale.value = lang;
|
||||
await setDayjsLocale(lang);
|
||||
};
|
||||
|
||||
loadLocaleMessages(fallbackLocale);
|
||||
loadLocaleMessages(userLanguage);
|
||||
setDayjsLocale(userLanguage);
|
||||
|
|
|
@ -4,12 +4,9 @@ import { useI18n } from 'vue-i18n';
|
|||
import { useDate } from '~/compositions/useDate';
|
||||
import { useElapsedTime } from '~/compositions/useElapsedTime';
|
||||
import { Pipeline } from '~/lib/api/types';
|
||||
import { prettyDuration } from '~/utils/duration';
|
||||
import { convertEmojis } from '~/utils/emoji';
|
||||
|
||||
import useTimeAgo from './useTimeAgo';
|
||||
|
||||
const { toLocaleString } = useDate();
|
||||
const { toLocaleString, timeAgo, prettyDuration } = useDate();
|
||||
|
||||
export default (pipeline: Ref<Pipeline | undefined>) => {
|
||||
const sinceRaw = computed(() => {
|
||||
|
@ -28,7 +25,6 @@ export default (pipeline: Ref<Pipeline | undefined>) => {
|
|||
const { time: sinceElapsed } = useElapsedTime(sinceUnderOneHour, sinceRaw);
|
||||
|
||||
const i18n = useI18n();
|
||||
const timeAgo = useTimeAgo();
|
||||
const since = computed(() => {
|
||||
if (sinceRaw.value === 0) {
|
||||
return i18n.t('time.not_started');
|
||||
|
@ -38,7 +34,8 @@ export default (pipeline: Ref<Pipeline | undefined>) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
return timeAgo.format(sinceElapsed.value);
|
||||
// TODO check whetehr elapsed works
|
||||
return timeAgo(sinceElapsed.value);
|
||||
});
|
||||
|
||||
const durationRaw = computed(() => {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
import TimeAgo from 'javascript-time-ago';
|
||||
import en from 'javascript-time-ago/locale/en.json';
|
||||
|
||||
import { getUserLanguage } from '~/utils/locale';
|
||||
|
||||
TimeAgo.addDefaultLocale(en);
|
||||
|
||||
const addedLocales = ['en'];
|
||||
|
||||
export default () => new TimeAgo(getUserLanguage());
|
||||
export async function loadTimeAgoLocale(locale: string) {
|
||||
if (!addedLocales.includes(locale)) {
|
||||
const { default: timeAgoLocale } = await import(`~/assets/timeAgoLocales/${locale}.js`);
|
||||
TimeAgo.addLocale(timeAgoLocale);
|
||||
addedLocales.push(locale);
|
||||
}
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
import humanizeDuration from 'humanize-duration';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
export function prettyDuration(durationMs: number): string {
|
||||
const i18n = useI18n();
|
||||
const short = {
|
||||
w: () => i18n.t('time.weeks_short'),
|
||||
d: () => i18n.t('time.days_short'),
|
||||
h: () => i18n.t('time.hours_short'),
|
||||
m: () => i18n.t('time.min_short'),
|
||||
s: () => i18n.t('time.sec_short'),
|
||||
};
|
||||
const durationOptions: humanizeDuration.HumanizerOptions = {
|
||||
round: true,
|
||||
languages: { short },
|
||||
language: 'short',
|
||||
};
|
||||
|
||||
if (durationMs < 1000 * 60 * 60) {
|
||||
return humanizeDuration(durationMs, durationOptions);
|
||||
}
|
||||
return humanizeDuration(durationMs, { ...durationOptions, units: ['y', 'mo', 'd', 'h', 'm'] });
|
||||
}
|
||||
|
||||
function leadingZeros(n: number, length: number): string {
|
||||
let res = n.toString();
|
||||
while (res.length < length) {
|
||||
res = `0${res}`;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function durationAsNumber(durationMs: number): string {
|
||||
const durationSeconds = durationMs / 1000;
|
||||
const seconds = leadingZeros(Math.floor(durationSeconds % 60), 2);
|
||||
const minutes = leadingZeros(Math.floor(durationSeconds / 60) % 60, 2);
|
||||
const hours = Math.floor(durationSeconds / 3600);
|
||||
|
||||
if (hours !== 0) {
|
||||
return `${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
return `${minutes}:${seconds}`;
|
||||
}
|
|
@ -3,6 +3,7 @@ import VueI18nPlugin from '@intlify/unplugin-vue-i18n/vite';
|
|||
import vue from '@vitejs/plugin-vue';
|
||||
import { copyFile, existsSync, mkdirSync, readdirSync } from 'fs';
|
||||
import path from 'path';
|
||||
import replace from 'replace-in-file';
|
||||
import IconsResolver from 'unplugin-icons/resolver';
|
||||
import Icons from 'unplugin-icons/vite';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
|
@ -56,37 +57,42 @@ export default defineConfig({
|
|||
|
||||
const filenames = readdirSync('src/assets/locales/').map((filename) => filename.replace('.json', ''));
|
||||
|
||||
if (!existsSync('src/assets/timeAgoLocales')) {
|
||||
mkdirSync('src/assets/timeAgoLocales');
|
||||
if (!existsSync('src/assets/dayjsLocales')) {
|
||||
mkdirSync('src/assets/dayjsLocales');
|
||||
}
|
||||
|
||||
filenames.forEach((name) => {
|
||||
// copy timeAgo language
|
||||
if (name === 'zh-Hans') {
|
||||
// zh-Hans is called zh in javascript-time-ago, so we need to rename this
|
||||
copyFile(
|
||||
'node_modules/javascript-time-ago/locale/zh.json.js',
|
||||
'src/assets/timeAgoLocales/zh-Hans.js',
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
(err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
);
|
||||
} else if (name !== 'en') {
|
||||
// English is always directly loaded (compiled by Vite) and thus not copied
|
||||
copyFile(
|
||||
`node_modules/javascript-time-ago/locale/${name}.json.js`,
|
||||
`src/assets/timeAgoLocales/${name}.js`,
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
(err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
);
|
||||
filenames.forEach(async (name) => {
|
||||
// English is always directly loaded (compiled by Vite) and thus not copied
|
||||
if (name === 'en') {
|
||||
return;
|
||||
}
|
||||
let langName = name;
|
||||
|
||||
// copy dayjs language
|
||||
if (name === 'zh-Hans') {
|
||||
// zh-Hans is called zh in dayjs
|
||||
langName = 'zh';
|
||||
} else if (name === 'zh-Hant') {
|
||||
// zh-Hant is called zh-cn in dayjs
|
||||
langName = 'zh-cn';
|
||||
}
|
||||
|
||||
copyFile(
|
||||
`node_modules/dayjs/esm/locale/${langName}.js`,
|
||||
`src/assets/dayjsLocales/${name}.js`,
|
||||
// eslint-disable-next-line promise/prefer-await-to-callbacks
|
||||
(err) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
replace.sync({
|
||||
files: 'src/assets/dayjsLocales/*.js',
|
||||
// remove any dayjs import and any dayjs.locale call
|
||||
from: /(?:import dayjs.*'|dayjs\.locale.*);/g,
|
||||
to: '',
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
Loading…
Reference in a new issue