diff options
| author | Marie <Marie@kaifa.ch> | 2023-12-23 02:09:23 +0100 |
|---|---|---|
| committer | Marie <Marie@kaifa.ch> | 2023-12-23 02:09:23 +0100 |
| commit | 5db583a3eb61d50de14d875ebf7ecef20490e313 (patch) | |
| tree | 783dd43d2ac660c32e745a4485d499e9ddc43324 /packages/frontend/src/widgets | |
| parent | add: Custom MOTDs (diff) | |
| parent | Update CHANGELOG.md (diff) | |
| download | sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.tar.gz sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.tar.bz2 sharkey-5db583a3eb61d50de14d875ebf7ecef20490e313.zip | |
merge: upstream
Diffstat (limited to 'packages/frontend/src/widgets')
36 files changed, 328 insertions, 186 deletions
diff --git a/packages/frontend/src/widgets/WidgetActivity.chart.vue b/packages/frontend/src/widgets/WidgetActivity.chart.vue index 9cfd845ace..a207071324 100644 --- a/packages/frontend/src/widgets/WidgetActivity.chart.vue +++ b/packages/frontend/src/widgets/WidgetActivity.chart.vue @@ -34,18 +34,19 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { ref } from 'vue'; const props = defineProps<{ activity: any[] }>(); -let viewBoxX: number = $ref(147); -let viewBoxY: number = $ref(60); -let zoom: number = $ref(1); -let pos: number = $ref(0); -let pointsNote: any = $ref(null); -let pointsReply: any = $ref(null); -let pointsRenote: any = $ref(null); -let pointsTotal: any = $ref(null); +const viewBoxX = ref(147); +const viewBoxY = ref(60); +const zoom = ref(1); +const pos = ref(0); +const pointsNote = ref<any>(null); +const pointsReply = ref<any>(null); +const pointsRenote = ref<any>(null); +const pointsTotal = ref<any>(null); function dragListen(fn) { window.addEventListener('mousemove', fn); @@ -62,17 +63,17 @@ function dragClear(fn) { function onMousedown(ev) { const clickX = ev.clientX; const clickY = ev.clientY; - const baseZoom = zoom; - const basePos = pos; + const baseZoom = zoom.value; + const basePos = pos.value; // 動かした時 dragListen(me => { let moveLeft = me.clientX - clickX; let moveTop = me.clientY - clickY; - zoom = Math.max(1, baseZoom + (-moveTop / 20)); - pos = Math.min(0, basePos + moveLeft); - if (pos < -(((props.activity.length - 1) * zoom) - viewBoxX)) pos = -(((props.activity.length - 1) * zoom) - viewBoxX); + zoom.value = Math.max(1, baseZoom + (-moveTop / 20)); + pos.value = Math.min(0, basePos + moveLeft); + if (pos.value < -(((props.activity.length - 1) * zoom.value) - viewBoxX.value)) pos.value = -(((props.activity.length - 1) * zoom.value) - viewBoxX.value); render(); }); @@ -82,10 +83,10 @@ function render() { const peak = Math.max(...props.activity.map(d => d.total)); if (peak !== 0) { const activity = props.activity.slice().reverse(); - pointsNote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.notes / peak)) * viewBoxY}`).join(' '); - pointsReply = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.replies / peak)) * viewBoxY}`).join(' '); - pointsRenote = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.renotes / peak)) * viewBoxY}`).join(' '); - pointsTotal = activity.map((d, i) => `${(i * zoom) + pos},${(1 - (d.total / peak)) * viewBoxY}`).join(' '); + pointsNote.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.notes / peak)) * viewBoxY.value}`).join(' '); + pointsReply.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.replies / peak)) * viewBoxY.value}`).join(' '); + pointsRenote.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.renotes / peak)) * viewBoxY.value}`).join(' '); + pointsTotal.value = activity.map((d, i) => `${(i * zoom.value) + pos.value},${(1 - (d.total / peak)) * viewBoxY.value}`).join(' '); } } </script> diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue index 788a9f2c7b..b107a47d8c 100644 --- a/packages/frontend/src/widgets/WidgetActivity.vue +++ b/packages/frontend/src/widgets/WidgetActivity.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import XCalendar from './WidgetActivity.calendar.vue'; import XChart from './WidgetActivity.chart.vue'; import { GetFormResultType } from '@/scripts/form.js'; diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue index 76b35f6fed..fef026244c 100644 --- a/packages/frontend/src/widgets/WidgetAichan.vue +++ b/packages/frontend/src/widgets/WidgetAichan.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, onUnmounted, shallowRef } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; const name = 'ai'; @@ -72,5 +72,6 @@ defineExpose<WidgetComponentExpose>({ height: 350px; border: none; pointer-events: none; + color-scheme: light; } </style> diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue index 8fb4ebe0f2..c17e9728a5 100644 --- a/packages/frontend/src/widgets/WidgetAiscript.vue +++ b/packages/frontend/src/widgets/WidgetAiscript.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import { Interpreter, Parser, utils } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import MkContainer from '@/components/MkContainer.vue'; diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue index 53b6020ffc..10248a840a 100644 --- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue +++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, Ref, ref, watch } from 'vue'; import { Interpreter, Parser } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; @@ -52,7 +52,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name, const parser = new Parser(); const root = ref<AsUiRoot>(); -const components: Ref<AsUiComponent>[] = $ref([]); +const components = ref<Ref<AsUiComponent>[]>([]); async function run() { const aiscript = new Interpreter({ @@ -60,7 +60,7 @@ async function run() { storageKey: 'widget', token: $i?.token, }), - ...registerAsUiLib(components, (_root) => { + ...registerAsUiLib(components.value, (_root) => { root.value = _root.value; }), }, { diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue new file mode 100644 index 0000000000..7c4455516d --- /dev/null +++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue @@ -0,0 +1,127 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkContainer :showHeader="widgetProps.showHeader" class="mkw-bdayfollowings"> + <template #icon><i class="ti ti-cake"></i></template> + <template #header>{{ i18n.ts._widgets.birthdayFollowings }}</template> + + <div :class="$style.bdayFRoot"> + <MkLoading v-if="fetching"/> + <div v-else-if="users.length > 0" :class="$style.bdayFGrid"> + <MkAvatar v-for="user in users" :key="user.id" :user="user.followee" link preview></MkAvatar> + </div> + <div v-else :class="$style.bdayFFallback"> + <img :src="infoImageUrl" class="_ghost" :class="$style.bdayFFallbackImage"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + </div> +</MkContainer> +</template> + +<script lang="ts" setup> +import { ref } from 'vue'; +import * as Misskey from 'misskey-js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { GetFormResultType } from '@/scripts/form.js'; +import MkContainer from '@/components/MkContainer.vue'; +import * as os from '@/os.js'; +import { useInterval } from '@/scripts/use-interval.js'; +import { i18n } from '@/i18n.js'; +import { infoImageUrl } from '@/instance.js'; +import { $i } from '@/account.js'; + +const name = i18n.ts._widgets.birthdayFollowings; + +const widgetPropsDef = { + showHeader: { + type: 'boolean' as const, + default: true, + }, +}; + +type WidgetProps = GetFormResultType<typeof widgetPropsDef>; + +const props = defineProps<WidgetComponentProps<WidgetProps>>(); +const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); + +const { widgetProps, configure } = useWidgetPropsManager(name, + widgetPropsDef, + props, + emit, +); + +const users = ref<Misskey.entities.FollowingFolloweePopulated[]>([]); +const fetching = ref(true); +let lastFetchedAt = '1970-01-01'; + +const fetch = () => { + if (!$i) { + users.value = []; + fetching.value = false; + return; + } + + const lfAtD = new Date(lastFetchedAt); + lfAtD.setHours(0, 0, 0, 0); + const now = new Date(); + now.setHours(0, 0, 0, 0); + + if (now > lfAtD) { + os.api('users/following', { + limit: 18, + birthday: now.toISOString(), + userId: $i.id, + }).then(res => { + users.value = res; + fetching.value = false; + }); + + lastFetchedAt = now.toISOString(); + } +}; + +useInterval(fetch, 1000 * 60, { + immediate: true, + afterMounted: true, +}); + +defineExpose<WidgetComponentExpose>({ + name, + configure, + id: props.widget ? props.widget.id : null, +}); +</script> + +<style lang="scss" module> +.bdayFRoot { + overflow: hidden; + min-height: calc(calc(calc(50px * 3) - 8px) + calc(var(--margin) * 2)); +} +.bdayFGrid { + display: grid; + grid-template-columns: repeat(6, 42px); + grid-template-rows: repeat(3, 42px); + place-content: center; + gap: 8px; + margin: var(--margin) auto; +} + +.bdayFFallback { + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} + +.bdayFFallbackImage { + height: 96px; + width: auto; + max-width: 90%; + margin-bottom: 8px; + border-radius: var(--radius); +} +</style> diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue index a7bdd4c49c..11082c1e3f 100644 --- a/packages/frontend/src/widgets/WidgetButton.vue +++ b/packages/frontend/src/widgets/WidgetButton.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { Interpreter, Parser } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue index 0ad166c6ba..b3f814a0a7 100644 --- a/packages/frontend/src/widgets/WidgetCalendar.vue +++ b/packages/frontend/src/widgets/WidgetCalendar.vue @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { i18n } from '@/i18n.js'; import { useInterval } from '@/scripts/use-interval.js'; diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index 2b3b391165..aa49269017 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkClickerGame from '@/components/MkClickerGame.vue'; diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue index e4ea2c97dd..22f053db59 100644 --- a/packages/frontend/src/widgets/WidgetClock.vue +++ b/packages/frontend/src/widgets/WidgetClock.vue @@ -29,8 +29,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { computed } from 'vue'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkAnalogClock from '@/components/MkAnalogClock.vue'; @@ -134,15 +134,15 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); -const tzAbbrev = $computed(() => (widgetProps.timezone === null +const tzAbbrev = computed(() => (widgetProps.timezone === null ? timezones.find((tz) => tz.name.toLowerCase() === Intl.DateTimeFormat().resolvedOptions().timeZone.toLowerCase())?.abbrev : timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.abbrev) ?? '?'); -const tzOffset = $computed(() => widgetProps.timezone === null +const tzOffset = computed(() => widgetProps.timezone === null ? 0 - new Date().getTimezoneOffset() : timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.offset ?? 0); -const tzOffsetLabel = $computed(() => (tzOffset >= 0 ? '+' : '-') + Math.floor(tzOffset / 60).toString().padStart(2, '0') + ':' + (tzOffset % 60).toString().padStart(2, '0')); +const tzOffsetLabel = computed(() => (tzOffset.value >= 0 ? '+' : '-') + Math.floor(tzOffset.value / 60).toString().padStart(2, '0') + ':' + (tzOffset.value % 60).toString().padStart(2, '0')); defineExpose<WidgetComponentExpose>({ name, diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue index 9ff5f8dcef..a4b90c49d3 100644 --- a/packages/frontend/src/widgets/WidgetDigitalClock.vue +++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue @@ -14,7 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { computed } from 'vue'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { timezones } from '@/scripts/timezones.js'; import MkDigitalClock from '@/components/MkDigitalClock.vue'; @@ -63,15 +64,15 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); -const tzAbbrev = $computed(() => (widgetProps.timezone === null +const tzAbbrev = computed(() => (widgetProps.timezone === null ? timezones.find((tz) => tz.name.toLowerCase() === Intl.DateTimeFormat().resolvedOptions().timeZone.toLowerCase())?.abbrev : timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.abbrev) ?? '?'); -const tzOffset = $computed(() => widgetProps.timezone === null +const tzOffset = computed(() => widgetProps.timezone === null ? 0 - new Date().getTimezoneOffset() : timezones.find((tz) => tz.name.toLowerCase() === widgetProps.timezone)?.offset ?? 0); -const tzOffsetLabel = $computed(() => (tzOffset >= 0 ? '+' : '-') + Math.floor(tzOffset / 60).toString().padStart(2, '0') + ':' + (tzOffset % 60).toString().padStart(2, '0')); +const tzOffsetLabel = computed(() => (tzOffset.value >= 0 ? '+' : '-') + Math.floor(tzOffset.value / 60).toString().padStart(2, '0') + ':' + (tzOffset.value % 60).toString().padStart(2, '0')); defineExpose<WidgetComponentExpose>({ name, diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue index e6ee590c15..605c24aaa3 100644 --- a/packages/frontend/src/widgets/WidgetFederation.vue +++ b/packages/frontend/src/widgets/WidgetFederation.vue @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue index 4ae77e86fc..0fc96c0d35 100644 --- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue +++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue @@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { shallowRef } from 'vue'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkTagCloud from '@/components/MkTagCloud.vue'; @@ -47,8 +47,8 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); -let cloud = $shallowRef<InstanceType<typeof MkTagCloud> | null>(); -let activeInstances = $shallowRef(null); +const cloud = shallowRef<InstanceType<typeof MkTagCloud> | null>(); +const activeInstances = shallowRef(null); function onInstanceClick(i) { os.pageWindow(`/instance-info/${i.host}`); @@ -59,8 +59,8 @@ useInterval(() => { sort: '+latestRequestReceivedAt', limit: 25, }).then(res => { - activeInstances = res; - if (cloud) cloud.update(); + activeInstances.value = res; + if (cloud.value) cloud.value.update(); }); }, 1000 * 60 * 3, { immediate: true, diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue index 3fa811fa13..2133deb363 100644 --- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue +++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { host } from '@/config.js'; import { instance } from '@/instance.js'; diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue index 09c27bfdec..c54682bb87 100644 --- a/packages/frontend/src/widgets/WidgetJobQueue.vue +++ b/packages/frontend/src/widgets/WidgetJobQueue.vue @@ -51,13 +51,14 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onUnmounted, reactive } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { onUnmounted, reactive, ref } from 'vue'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { useStream } from '@/stream.js'; import number from '@/filters/number.js'; import * as sound from '@/scripts/sound.js'; import { deepClone } from '@/scripts/clone.js'; +import { defaultStore } from '@/store.js'; const name = 'jobQueue'; @@ -99,7 +100,18 @@ const current = reactive({ }, }); const prev = reactive({} as typeof current); -const jammedSound = sound.setVolume(sound.getAudio('syuilo/queue-jammed'), 1); +const jammedAudioBuffer = ref<AudioBuffer | null>(null); +const jammedSoundNodePlaying = ref<boolean>(false); + +if (defaultStore.state.sound_masterVolume) { + sound.loadAudio({ + type: 'syuilo/queue-jammed', + volume: 1, + }).then(buf => { + if (!buf) throw new Error('[WidgetJobQueue] Failed to initialize AudioBuffer'); + jammedAudioBuffer.value = buf; + }); +} for (const domain of ['inbox', 'deliver']) { prev[domain] = deepClone(current[domain]); @@ -113,8 +125,13 @@ const onStats = (stats) => { current[domain].waiting = stats[domain].waiting; current[domain].delayed = stats[domain].delayed; - if (current[domain].waiting > 0 && widgetProps.sound && jammedSound.paused) { - jammedSound.play(); + if (current[domain].waiting > 0 && widgetProps.sound && jammedAudioBuffer.value && !jammedSoundNodePlaying.value) { + const soundNode = sound.createSourceNode(jammedAudioBuffer.value, 1); + if (soundNode) { + jammedSoundNodePlaying.value = true; + soundNode.onended = () => jammedSoundNodePlaying.value = false; + soundNode.start(); + } } } }; diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue index c2d5ef566c..8e9e67ade5 100644 --- a/packages/frontend/src/widgets/WidgetMemo.vue +++ b/packages/frontend/src/widgets/WidgetMemo.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import { defaultStore } from '@/store.js'; diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue index 6e38b8308c..e858741aa1 100644 --- a/packages/frontend/src/widgets/WidgetNotifications.vue +++ b/packages/frontend/src/widgets/WidgetNotifications.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import XNotifications from '@/components/MkNotifications.vue'; diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue index 46fe991f37..0a6fec7f2e 100644 --- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue +++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue index c07f2dd262..808f023174 100644 --- a/packages/frontend/src/widgets/WidgetPhotos.vue +++ b/packages/frontend/src/widgets/WidgetPhotos.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { useStream } from '@/stream.js'; import { getStaticImageUrl } from '@/scripts/media-proxy.js'; diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue index 320b47a4ff..9979ae256e 100644 --- a/packages/frontend/src/widgets/WidgetPostForm.vue +++ b/packages/frontend/src/widgets/WidgetPostForm.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkPostForm from '@/components/MkPostForm.vue'; diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue index fc54af2d71..3ff57bab86 100644 --- a/packages/frontend/src/widgets/WidgetProfile.vue +++ b/packages/frontend/src/widgets/WidgetProfile.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { $i } from '@/account.js'; import { userPage } from '@/filters/user.js'; diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue index 1ada9f4be8..a718548731 100644 --- a/packages/frontend/src/widgets/WidgetRss.vue +++ b/packages/frontend/src/widgets/WidgetRss.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, watch, computed } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import { url as base } from '@/config.js'; @@ -72,7 +72,7 @@ const fetchEndpoint = computed(() => { url.searchParams.set('url', widgetProps.url); return url; }); -let intervalClear = $ref<(() => void) | undefined>(); +const intervalClear = ref<(() => void) | undefined>(); const tick = () => { if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; @@ -87,10 +87,10 @@ const tick = () => { watch(() => fetchEndpoint, tick); watch(() => widgetProps.refreshIntervalSec, () => { - if (intervalClear) { - intervalClear(); + if (intervalClear.value) { + intervalClear.value(); } - intervalClear = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), { + intervalClear.value = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), { immediate: true, afterMounted: true, }); diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue index 790f94f7c3..607bb2f0ab 100644 --- a/packages/frontend/src/widgets/WidgetRssTicker.vue +++ b/packages/frontend/src/widgets/WidgetRssTicker.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, watch, computed } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import MarqueeText from '@/components/MkMarquee.vue'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; @@ -101,9 +101,9 @@ const fetchEndpoint = computed(() => { url.searchParams.set('url', widgetProps.url); return url; }); -let intervalClear = $ref<(() => void) | undefined>(); +const intervalClear = ref<(() => void) | undefined>(); -let key = $ref(0); +const key = ref(0); const tick = () => { if (document.visibilityState === 'hidden' && rawItems.value.length !== 0) return; @@ -113,16 +113,16 @@ const tick = () => { .then(feed => { rawItems.value = feed.items ?? []; fetching.value = false; - key++; + key.value++; }); }; watch(() => fetchEndpoint, tick); watch(() => widgetProps.refreshIntervalSec, () => { - if (intervalClear) { - intervalClear(); + if (intervalClear.value) { + intervalClear.value(); } - intervalClear = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), { + intervalClear.value = useInterval(tick, Math.max(10000, widgetProps.refreshIntervalSec * 1000), { immediate: true, afterMounted: true, }); diff --git a/packages/frontend/src/widgets/WidgetSearch.vue b/packages/frontend/src/widgets/WidgetSearch.vue index 979341e1ae..c114707b23 100644 --- a/packages/frontend/src/widgets/WidgetSearch.vue +++ b/packages/frontend/src/widgets/WidgetSearch.vue @@ -15,8 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, onMounted } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { defineAsyncComponent, ref } from 'vue'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import MkInput from '@/components/MkInput.vue'; import MkContainer from '@/components/MkContainer.vue'; import { i18n } from '@/i18n.js'; @@ -54,14 +54,12 @@ function onInputKeydown(evt: KeyboardEvent) { const router = useRouter(); -let key = $ref(0); -let searchQuery = $ref(''); -let notePagination = $ref(); -let searchOrigin = $ref('combined'); -let user = $ref(null); -let isLocalOnly = $ref(false); -let order = $ref(true); -let filetype = $ref(null); +let key = ref(0); +let searchQuery = ref(''); +let notePagination = ref(); +let isLocalOnly = ref(false); +let order = ref(true); +let filetype = ref<null | string>(null); function options(ev) { os.popupMenu([{ @@ -74,7 +72,7 @@ function options(ev) { icon: 'ph-image ph-bold ph-lg', text: 'With Images', action: () => { - filetype = 'image'; + filetype.value = 'image'; }, }, { @@ -82,7 +80,7 @@ function options(ev) { icon: 'ph-music-notes-simple ph-bold ph-lg', text: 'With Audios', action: () => { - filetype = 'audio'; + filetype.value = 'audio'; }, }, { @@ -90,20 +88,14 @@ function options(ev) { icon: 'ph-video ph-bold ph-lg', text: 'With Videos', action: () => { - filetype = 'video'; + filetype.value = 'video'; }, }], }], ev.currentTarget ?? ev.target); } -function selectUser() { - os.selectUser().then(_user => { - user = _user; - }); -} - async function search() { - const query = searchQuery.toString().trim(); + const query = searchQuery.value.toString().trim(); if (query == null || query === '') return; @@ -125,24 +117,24 @@ async function search() { return; } - notePagination = { + notePagination.value = { endpoint: 'notes/search', limit: 10, params: { query: searchQuery, - userId: user ? user.id : null, - order: order ? 'desc' : 'asc', - filetype: filetype, + userId: null, + order: order.value ? 'desc' : 'asc', + filetype: filetype.value, }, }; - if (isLocalOnly) notePagination.params.host = '.'; + if (isLocalOnly.value) notePagination.value.params.host = '.'; - key++; + key.value++; os.popup(defineAsyncComponent(() => import('@/components/SkSearchResultWindow.vue')), { - noteKey: key, - notePagination: notePagination, + noteKey: key.value, + notePagination: notePagination.value, }, { }, 'closed'); } diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue index 82b6246add..eccb9a00bf 100644 --- a/packages/frontend/src/widgets/WidgetSlideshow.vue +++ b/packages/frontend/src/widgets/WidgetSlideshow.vue @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, ref, shallowRef } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index 0ebffa105e..070466f476 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import MkContainer from '@/components/MkContainer.vue'; @@ -136,7 +136,7 @@ const choose = async (ev) => { text: i18n.ts._timelines.global, icon: 'ph-globe-hemisphere-west ph-bold ph-lg', action: () => { setSrc('global'); }, - }, antennaItems.length > 0 ? null : undefined, ...antennaItems, listItems.length > 0 ? null : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => { + }, antennaItems.length > 0 ? { type: 'divider' } : undefined, ...antennaItems, listItems.length > 0 ? { type: 'divider' } : undefined, ...listItems], ev.currentTarget ?? ev.target).then(() => { menuOpened.value = false; }); }; diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue index ea7b9078f3..738cd70b03 100644 --- a/packages/frontend/src/widgets/WidgetTrends.vue +++ b/packages/frontend/src/widgets/WidgetTrends.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue index 33585cd721..35f29b5e21 100644 --- a/packages/frontend/src/widgets/WidgetUnixClock.vue +++ b/packages/frontend/src/widgets/WidgetUnixClock.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onUnmounted, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; const name = 'unixClock'; diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue index 159318c2ca..f5c1d2d3a3 100644 --- a/packages/frontend/src/widgets/WidgetUserList.vue +++ b/packages/frontend/src/widgets/WidgetUserList.vue @@ -24,7 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; +import { ref } from 'vue'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import * as os from '@/os.js'; @@ -57,9 +58,9 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name, emit, ); -let list = $ref(); -let users = $ref([]); -let fetching = $ref(true); +const list = ref(); +const users = ref([]); +const fetching = ref(true); async function chooseList() { const lists = await os.api('users/lists/list'); @@ -79,19 +80,19 @@ async function chooseList() { const fetch = () => { if (widgetProps.listId == null) { - fetching = false; + fetching.value = false; return; } os.api('users/lists/show', { listId: widgetProps.listId, }).then(_list => { - list = _list; + list.value = _list; os.api('users/show', { - userIds: list.userIds, + userIds: list.value.userIds, }).then(_users => { - users = _users; - fetching = false; + users.value = _users; + fetching.value = false; }); }); }; diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index ae3bd09c86..b783d783bc 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -34,6 +34,7 @@ export default function(app: App) { app.component('WidgetUserList', defineAsyncComponent(() => import('./WidgetUserList.vue'))); app.component('WidgetClicker', defineAsyncComponent(() => import('./WidgetClicker.vue'))); app.component('WidgetSearch', defineAsyncComponent(() => import('./WidgetSearch.vue'))); + app.component('WidgetBirthdayFollowings', defineAsyncComponent(() => import('./WidgetBirthdayFollowings.vue'))); } export const widgets = [ @@ -65,4 +66,5 @@ export const widgets = [ 'userList', 'clicker', 'search', + 'birthdayFollowings', ]; diff --git a/packages/frontend/src/widgets/server-metric/cpu-mem.vue b/packages/frontend/src/widgets/server-metric/cpu-mem.vue index c656d75429..9196ae209f 100644 --- a/packages/frontend/src/widgets/server-metric/cpu-mem.vue +++ b/packages/frontend/src/widgets/server-metric/cpu-mem.vue @@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onBeforeUnmount } from 'vue'; +import { onMounted, onBeforeUnmount, ref } from 'vue'; import { v4 as uuid } from 'uuid'; const props = defineProps<{ @@ -83,23 +83,23 @@ const props = defineProps<{ meta: any }>(); -let viewBoxX: number = $ref(50); -let viewBoxY: number = $ref(30); -let stats: any[] = $ref([]); +const viewBoxX = ref<number>(50); +const viewBoxY = ref<number>(30); +const stats = ref<any[]>([]); const cpuGradientId = uuid(); const cpuMaskId = uuid(); const memGradientId = uuid(); const memMaskId = uuid(); -let cpuPolylinePoints: string = $ref(''); -let memPolylinePoints: string = $ref(''); -let cpuPolygonPoints: string = $ref(''); -let memPolygonPoints: string = $ref(''); -let cpuHeadX: any = $ref(null); -let cpuHeadY: any = $ref(null); -let memHeadX: any = $ref(null); -let memHeadY: any = $ref(null); -let cpuP: string = $ref(''); -let memP: string = $ref(''); +const cpuPolylinePoints = ref<string>(''); +const memPolylinePoints = ref<string>(''); +const cpuPolygonPoints = ref<string>(''); +const memPolygonPoints = ref<string>(''); +const cpuHeadX = ref<any>(null); +const cpuHeadY = ref<any>(null); +const memHeadX = ref<any>(null); +const memHeadY = ref<any>(null); +const cpuP = ref<string>(''); +const memP = ref<string>(''); onMounted(() => { props.connection.on('stats', onStats); @@ -115,24 +115,24 @@ onBeforeUnmount(() => { }); function onStats(connStats) { - stats.push(connStats); - if (stats.length > 50) stats.shift(); + stats.value.push(connStats); + if (stats.value.length > 50) stats.value.shift(); - let cpuPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - s.cpu) * viewBoxY]); - let memPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.mem.active / props.meta.mem.total)) * viewBoxY]); - cpuPolylinePoints = cpuPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); - memPolylinePoints = memPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); + let cpuPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - s.cpu) * viewBoxY.value]); + let memPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - (s.mem.active / props.meta.mem.total)) * viewBoxY.value]); + cpuPolylinePoints.value = cpuPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); + memPolylinePoints.value = memPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); - cpuPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${cpuPolylinePoints} ${viewBoxX},${viewBoxY}`; - memPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${memPolylinePoints} ${viewBoxX},${viewBoxY}`; + cpuPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${cpuPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`; + memPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${memPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`; - cpuHeadX = cpuPolylinePointsStats.at(-1)![0]; - cpuHeadY = cpuPolylinePointsStats.at(-1)![1]; - memHeadX = memPolylinePointsStats.at(-1)![0]; - memHeadY = memPolylinePointsStats.at(-1)![1]; + cpuHeadX.value = cpuPolylinePointsStats.at(-1)![0]; + cpuHeadY.value = cpuPolylinePointsStats.at(-1)![1]; + memHeadX.value = memPolylinePointsStats.at(-1)![0]; + memHeadY.value = memPolylinePointsStats.at(-1)![1]; - cpuP = (connStats.cpu * 100).toFixed(0); - memP = (connStats.mem.active / props.meta.mem.total * 100).toFixed(0); + cpuP.value = (connStats.cpu * 100).toFixed(0); + memP.value = (connStats.mem.active / props.meta.mem.total * 100).toFixed(0); } function onStatsLog(statsLog) { diff --git a/packages/frontend/src/widgets/server-metric/cpu.vue b/packages/frontend/src/widgets/server-metric/cpu.vue index 65da16a632..cffbdb27ce 100644 --- a/packages/frontend/src/widgets/server-metric/cpu.vue +++ b/packages/frontend/src/widgets/server-metric/cpu.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onBeforeUnmount } from 'vue'; +import { onMounted, onBeforeUnmount, ref } from 'vue'; import XPie from './pie.vue'; const props = defineProps<{ @@ -23,10 +23,10 @@ const props = defineProps<{ meta: any }>(); -let usage: number = $ref(0); +const usage = ref<number>(0); function onStats(stats) { - usage = stats.cpu; + usage.value = stats.cpu; } onMounted(() => { diff --git a/packages/frontend/src/widgets/server-metric/disk.vue b/packages/frontend/src/widgets/server-metric/disk.vue index b9774da0cf..18f8560265 100644 --- a/packages/frontend/src/widgets/server-metric/disk.vue +++ b/packages/frontend/src/widgets/server-metric/disk.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { computed } from 'vue'; import XPie from './pie.vue'; import bytes from '@/filters/bytes.js'; @@ -24,10 +24,10 @@ const props = defineProps<{ meta: any; // TODO }>(); -const usage = $computed(() => props.meta.fs.used / props.meta.fs.total); -const total = $computed(() => props.meta.fs.total); -const used = $computed(() => props.meta.fs.used); -const available = $computed(() => props.meta.fs.total - props.meta.fs.used); +const usage = computed(() => props.meta.fs.used / props.meta.fs.total); +const total = computed(() => props.meta.fs.total); +const used = computed(() => props.meta.fs.used); +const available = computed(() => props.meta.fs.total - props.meta.fs.used); </script> <style lang="scss" scoped> diff --git a/packages/frontend/src/widgets/server-metric/mem.vue b/packages/frontend/src/widgets/server-metric/mem.vue index 5a57ef6e1e..118fd68fe8 100644 --- a/packages/frontend/src/widgets/server-metric/mem.vue +++ b/packages/frontend/src/widgets/server-metric/mem.vue @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onBeforeUnmount } from 'vue'; +import { onMounted, onBeforeUnmount, ref } from 'vue'; import XPie from './pie.vue'; import bytes from '@/filters/bytes.js'; @@ -25,16 +25,16 @@ const props = defineProps<{ meta: any }>(); -let usage: number = $ref(0); -let total: number = $ref(0); -let used: number = $ref(0); -let free: number = $ref(0); +const usage = ref<number>(0); +const total = ref<number>(0); +const used = ref<number>(0); +const free = ref<number>(0); function onStats(stats) { - usage = stats.mem.active / props.meta.mem.total; - total = props.meta.mem.total; - used = stats.mem.active; - free = total - used; + usage.value = stats.mem.active / props.meta.mem.total; + total.value = props.meta.mem.total; + used.value = stats.mem.active; + free.value = total.value - used.value; } onMounted(() => { diff --git a/packages/frontend/src/widgets/server-metric/net.vue b/packages/frontend/src/widgets/server-metric/net.vue index 5593128660..e6a8bfc22a 100644 --- a/packages/frontend/src/widgets/server-metric/net.vue +++ b/packages/frontend/src/widgets/server-metric/net.vue @@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, onBeforeUnmount } from 'vue'; +import { onMounted, onBeforeUnmount, ref } from 'vue'; import bytes from '@/filters/bytes.js'; const props = defineProps<{ @@ -57,19 +57,19 @@ const props = defineProps<{ meta: any }>(); -let viewBoxX: number = $ref(50); -let viewBoxY: number = $ref(30); -let stats: any[] = $ref([]); -let inPolylinePoints: string = $ref(''); -let outPolylinePoints: string = $ref(''); -let inPolygonPoints: string = $ref(''); -let outPolygonPoints: string = $ref(''); -let inHeadX: any = $ref(null); -let inHeadY: any = $ref(null); -let outHeadX: any = $ref(null); -let outHeadY: any = $ref(null); -let inRecent: number = $ref(0); -let outRecent: number = $ref(0); +const viewBoxX = ref<number>(50); +const viewBoxY = ref<number>(30); +const stats = ref<any[]>([]); +const inPolylinePoints = ref<string>(''); +const outPolylinePoints = ref<string>(''); +const inPolygonPoints = ref<string>(''); +const outPolygonPoints = ref<string>(''); +const inHeadX = ref<any>(null); +const inHeadY = ref<any>(null); +const outHeadX = ref<any>(null); +const outHeadY = ref<any>(null); +const inRecent = ref<number>(0); +const outRecent = ref<number>(0); onMounted(() => { props.connection.on('stats', onStats); @@ -85,27 +85,27 @@ onBeforeUnmount(() => { }); function onStats(connStats) { - stats.push(connStats); - if (stats.length > 50) stats.shift(); + stats.value.push(connStats); + if (stats.value.length > 50) stats.value.shift(); - const inPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.rx))); - const outPeak = Math.max(1024 * 64, Math.max(...stats.map(s => s.net.tx))); + const inPeak = Math.max(1024 * 64, Math.max(...stats.value.map(s => s.net.rx))); + const outPeak = Math.max(1024 * 64, Math.max(...stats.value.map(s => s.net.tx))); - let inPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.rx / inPeak)) * viewBoxY]); - let outPolylinePointsStats = stats.map((s, i) => [viewBoxX - ((stats.length - 1) - i), (1 - (s.net.tx / outPeak)) * viewBoxY]); - inPolylinePoints = inPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); - outPolylinePoints = outPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); + let inPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - (s.net.rx / inPeak)) * viewBoxY.value]); + let outPolylinePointsStats = stats.value.map((s, i) => [viewBoxX.value - ((stats.value.length - 1) - i), (1 - (s.net.tx / outPeak)) * viewBoxY.value]); + inPolylinePoints.value = inPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); + outPolylinePoints.value = outPolylinePointsStats.map(xy => `${xy[0]},${xy[1]}`).join(' '); - inPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${inPolylinePoints} ${viewBoxX},${viewBoxY}`; - outPolygonPoints = `${viewBoxX - (stats.length - 1)},${viewBoxY} ${outPolylinePoints} ${viewBoxX},${viewBoxY}`; + inPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${inPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`; + outPolygonPoints.value = `${viewBoxX.value - (stats.value.length - 1)},${viewBoxY.value} ${outPolylinePoints.value} ${viewBoxX.value},${viewBoxY.value}`; - inHeadX = inPolylinePointsStats.at(-1)![0]; - inHeadY = inPolylinePointsStats.at(-1)![1]; - outHeadX = outPolylinePointsStats.at(-1)![0]; - outHeadY = outPolylinePointsStats.at(-1)![1]; + inHeadX.value = inPolylinePointsStats.at(-1)![0]; + inHeadY.value = inPolylinePointsStats.at(-1)![1]; + outHeadX.value = outPolylinePointsStats.at(-1)![0]; + outHeadY.value = outPolylinePointsStats.at(-1)![1]; - inRecent = connStats.net.rx; - outRecent = connStats.net.tx; + inRecent.value = connStats.net.rx; + outRecent.value = connStats.net.tx; } function onStatsLog(statsLog) { diff --git a/packages/frontend/src/widgets/server-metric/pie.vue b/packages/frontend/src/widgets/server-metric/pie.vue index c8a1496101..fd18a6a4f2 100644 --- a/packages/frontend/src/widgets/server-metric/pie.vue +++ b/packages/frontend/src/widgets/server-metric/pie.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { computed } from 'vue'; const props = defineProps<{ value: number; @@ -36,8 +36,8 @@ const props = defineProps<{ const r = 0.45; -const color = $computed(() => `hsl(${180 - (props.value * 180)}, 80%, 70%)`); -const strokeDashoffset = $computed(() => (1 - props.value) * (Math.PI * (r * 2))); +const color = computed(() => `hsl(${180 - (props.value * 180)}, 80%, 70%)`); +const strokeDashoffset = computed(() => (1 - props.value) * (Math.PI * (r * 2))); </script> <style lang="scss" module> |