fix: améliorer la gestion des activités de codage et simplifier le formatage des données

This commit is contained in:
2025-12-18 10:36:07 +01:00
parent c8d6894a51
commit 7c799b7206

View File

@@ -3,58 +3,67 @@ import type { UseTimeAgoMessages } from '@vueuse/core'
import type { Activity } from '~~/types'
import { activityMessages, IDEs } from '~~/types'
const { locale, locales, t } = useI18n({
useScope: 'local',
})
const { locale, locales, t } = useI18n({ useScope: 'local' })
const { data: activity, refresh } = await useAsyncData<Activity>('activity', () => $fetch('/api/activity'))
useIntervalFn(async () => await refresh(), 5000)
const codingActivity = computed(() => {
const activities = activity.value!.data.activities.filter(activity => IDEs.some(ide => ide.name === activity.name)).map(activity => ({
...activity,
name: activity.assets?.small_text === 'Cursor' ? 'Cursor' : activity.name,
}))
useIntervalFn(refresh, 5000)
return activities.length > 1
? activities[Math.floor(Math.random() * activities.length)]
: activities[0]
const codingActivities = computed(() => {
const list = activity.value?.data.activities ?? []
return list
.filter(a => IDEs.some(ide => ide.name === a.name))
.map(a => ({ ...a, name: a.assets?.small_text === 'Cursor' ? 'Cursor' : a.name }))
})
const currentLocale = computed(() => locales.value.find((l: { code: string }) => l.code === locale.value))
const codingActivity = computed(() => {
if (!codingActivities.value.length) return null
return codingActivities.value.length > 1
? codingActivities.value[Math.floor(Math.random() * codingActivities.value.length)]
: codingActivities.value[0]
})
const currentLocale = computed(() => locales.value.find((l: { code: string }) => l.code === locale.value)?.code ?? 'en')
const isActive = computed(() => {
if (!codingActivity.value)
return
const act = codingActivity.value
if (!act) return false
const { name, details, state } = codingActivity.value
const { name, details = '', state = '' } = act
return name === 'Visual Studio Code' || name === 'Cursor'
? !details.includes('Idling')
: state.toLowerCase().includes('editing')
if (name === 'Visual Studio Code' || name === 'Cursor')
return !details.includes('Idling')
return state.toLowerCase().includes('editing')
})
const getActivity = computed(() => {
if (!codingActivity.value)
return
type FormattedActivity = {
name: string
project: string
state: string
start: {
ago: string
formatted: { date: string; time: string }
}
} | null
const { name, details, state, timestamps } = codingActivity.value
const formattedActivity = computed<FormattedActivity>(() => {
const act = codingActivity.value
if (!act) return null
const { name, details = '', state = '', timestamps } = act
const project = details
? details
.charAt(0)
.toUpperCase()
+ details
.slice(1)
.replace('Workspace:', '')
.trim()
? (details.charAt(0).toUpperCase() + details.slice(1).replace('Workspace:', '').trim())
: ''
const stateWord = state && state.split(' ').length >= 2 ? state.split(' ')[1] : t('secret')
const stateWord = (state && state.split(' ').length >= 2 ? state.split(' ')[1] : t('secret')) as string
const ago = useTimeAgo(timestamps.start, {
messages: activityMessages[locale.value as keyof typeof activityMessages] as UseTimeAgoMessages,
}).value
const formatDate = (date: number, format: string) => useDateFormat(date, format, { locales: currentLocale.value?.code ?? 'en' }).value
const formatDate = (date: number, format: string) =>
useDateFormat(date, format, { locales: currentLocale.value }).value
return {
name,
@@ -62,96 +71,70 @@ const getActivity = computed(() => {
state: stateWord,
start: {
ago,
formated: {
formatted: {
date: formatDate(timestamps.start, 'DD MMM YYYY'),
time: formatDate(timestamps.start, 'HH:mm'),
},
},
}
})
const editorIcon = computed(() => {
const name = formattedActivity.value?.name ?? codingActivity.value?.name
return IDEs.find(ide => ide.name === name)?.icon ?? 'file'
})
</script>
<template>
<ClientOnly>
<div
v-if="getActivity"
class="flex items-start gap-2 mt-4"
>
<div v-if="formattedActivity" class="flex items-start gap-2 mt-4">
<UTooltip :text="isActive ? t('tooltip.online') : t('tooltip.idling')">
<div class="relative flex h-3 w-3 mt-2">
<div
v-if="isActive"
class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-500 opacity-75"
/>
<div
:class="isActive ? 'bg-green-500' : 'bg-amber-500'"
class="relative inline-flex rounded-full h-3 w-3"
/>
<div v-if="isActive" class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-500 opacity-75" />
<div :class="isActive ? 'bg-green-500' : 'bg-amber-500'" class="relative inline-flex rounded-full h-3 w-3" />
</div>
</UTooltip>
<i18n-t
v-if="isActive"
keypath="working"
tag="div"
>
<i18n-t v-if="isActive" keypath="working" tag="div">
<template #state>
<strong>{{ getActivity.state!.split(' ').map((word: string) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(' ') }}</strong>
<strong>{{ formattedActivity.state.split(' ').map(w => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ') }}</strong>
</template>
<template #project>
<i>{{ getActivity.project.replaceAll('Editing', '') }}</i>
<i>{{ formattedActivity.project.replace('Editing', '') }}</i>
</template>
<template #editor>
<span class="space-x-1">
<UIcon
:name="IDEs.find(ide => ide.name === getActivity!.name)!.icon"
size="16"
/>
<strong>{{ getActivity.name }}</strong></span>
<UIcon :name="editorIcon" size="16" />
<strong>{{ formattedActivity.name }}</strong>
</span>
</template>
<template #start>
<strong>{{ getActivity.start.ago }}</strong>
<strong>{{ formattedActivity.start.ago }}</strong>
</template>
<template #format>
<strong>{{ getActivity.start.formated.date }}</strong> {{ t('separator') }}
<strong>{{ getActivity.start.formated.time }}</strong>
<strong>{{ formattedActivity.start.formatted.date }}</strong> {{ t('separator') }}
<strong>{{ formattedActivity.start.formatted.time }}</strong>
</template>
</i18n-t>
<i18n-t
v-else
keypath="idling"
tag="div"
class="space-x-1"
>
<i18n-t v-else keypath="idling" tag="div" class="space-x-1">
<template #editor>
<span class="space-x-1">
<UIcon
:name="IDEs.find(ide => ide.name === getActivity!.name)!.icon"
size="16"
/>
<strong>{{ getActivity.name }}</strong>
<UIcon :name="editorIcon" size="16" />
<strong>{{ formattedActivity.name }}</strong>
</span>
</template>
</i18n-t>
</div>
<div
v-else
class="my-5 flex md:items-start gap-2"
>
<div v-else class="my-5 flex md:items-start gap-2">
<UTooltip :text="t('tooltip.offline')">
<div class="relative flex h-3 w-3 mt-2">
<div
class="relative cursor-not-allowed inline-flex rounded-full h-3 w-3 bg-red-500"
/>
<div class="relative cursor-not-allowed inline-flex rounded-full h-3 w-3 bg-red-500" />
</div>
</UTooltip>
<i18n-t
keypath="offline"
tag="p"
class="not-prose"
>
<template #maths>
<i>{{ t('maths') }}</i>
</template>
<i18n-t keypath="offline" tag="p" class="not-prose">
<template #maths><i>{{ t('maths') }}</i></template>
</i18n-t>
</div>
</ClientOnly>