mirror of
https://iceshrimp.dev/Crimekillz/jointrashposs.git
synced 2024-11-25 02:09:05 +01:00
4c47eb20ee
* (add) サーバー統計ページ * fix * add jsdoc * Update CircGraph.vue
227 lines
8.0 KiB
Vue
227 lines
8.0 KiB
Vue
<template>
|
|
<div class="container mx-auto max-w-screen-lg px-6 space-y-6 lg:space-y-8">
|
|
<GLocalNav :items="[
|
|
{
|
|
name: $t('_servers._statistics.lang'),
|
|
anchor: '#lang',
|
|
},
|
|
{
|
|
name: $t('_servers._statistics.registerAcceptance'),
|
|
anchor: '#registerAcceptance',
|
|
},
|
|
{
|
|
name: $t('_servers._statistics.notes'),
|
|
anchor: '#notes',
|
|
},
|
|
{
|
|
name: $t('_servers._statistics.version'),
|
|
anchor: '#version'
|
|
}
|
|
]" />
|
|
<div id="lang">
|
|
<h2 class="text-2xl lg:text-3xl font-title font-bold mb-4">{{ $t(`_servers._statistics.lang`) }}</h2>
|
|
<div class="grid gap-4 md:grid-cols-2">
|
|
<div><ChartsCircGraph class="max-w-xs mx-auto" :data="langStats" :focusedIndex="langFocus" /></div>
|
|
<div>
|
|
<ul class="space-y-1">
|
|
<li v-for="value, key, index in trunc(langStats)" class="grid py-1 px-3 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800" :class="$style.kvRoot" @mouseenter.passive="langFocus = index" @mouseleave.passive="langFocus = undefined">
|
|
<div v-if="key === '__others'">{{ $t('other') }}</div>
|
|
<div v-else>{{ key }} {{ lang.find((v) => v.lang === key)?.label ? `(${lang.find((v) => v.lang === key)?.label})` : '' }}</div>
|
|
<div class="font-bold font-mono text-accent-600">{{ $n(value) }}</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="registerAcceptance">
|
|
<h2 class="text-2xl lg:text-3xl font-title font-bold mb-4">{{ $t(`_servers._statistics.registerAcceptance`) }}</h2>
|
|
<div class="grid gap-4 md:grid-cols-2">
|
|
<div><ChartsCircGraph class="max-w-xs mx-auto" :data="regAcceptanceStats" :focusedIndex="regAcceptanceFocus" :truncMinor="false" /></div>
|
|
<div>
|
|
<ul class="space-y-1">
|
|
<li v-for="value, key, index in regAcceptanceStats" class="grid py-1 px-3 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800" :class="$style.kvRoot" @mouseenter.passive="regAcceptanceFocus = index" @mouseleave.passive="regAcceptanceFocus = undefined">
|
|
<div>{{ $t(`_servers._registerAcceptance.${key}`) }}</div>
|
|
<div class="font-bold font-mono text-accent-600">{{ $n(value) }}</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="notes">
|
|
<h2 class="text-2xl lg:text-3xl font-title font-bold mb-4">{{ $t(`_servers._statistics.notes`) }}</h2>
|
|
<div class="grid gap-4 md:grid-cols-2">
|
|
<div><ChartsCircGraph class="max-w-xs mx-auto" :data="notesCountStats" :focusedIndex="notesCountFocus" /></div>
|
|
<div>
|
|
<ul class="space-y-1">
|
|
<li v-for="value, key, index in trunc(notesCountStats)" class="grid py-1 px-3 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800" :class="$style.kvRoot" @mouseenter.passive="notesCountFocus = index" @mouseleave.passive="notesCountFocus = undefined">
|
|
<div v-if="key === '__others'">{{ $t('other') }}</div>
|
|
<div v-else>{{ key }}</div>
|
|
<div class="font-bold font-mono text-accent-600">{{ $n(value) }}</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="version">
|
|
<h2 class="text-2xl lg:text-3xl font-title font-bold mb-4">{{ $t(`_servers._statistics.version`) }}</h2>
|
|
<div class="grid gap-4 md:grid-cols-2">
|
|
<div><ChartsCircGraph class="max-w-xs mx-auto" :data="avgVersionStats" :truncMinor="0.005" :focusedIndex="avgVersionFocus" /></div>
|
|
<div>
|
|
<ul class="space-y-1">
|
|
<li v-for="value, key, index in trunc(avgVersionStats, 0.005)" class="grid py-1 px-3 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800" :class="$style.kvRoot" @mouseenter.passive="avgVersionFocus = index" @mouseleave.passive="avgVersionFocus = undefined">
|
|
<div v-if="key === '__others'">{{ $t('other') }}</div>
|
|
<div v-else>{{ key }}</div>
|
|
<div class="font-bold font-mono text-accent-600">{{ $n(value) }}</div>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { InstanceInfo } from '@/types/instances-info';
|
|
import lang from '~/assets/data/lang';
|
|
|
|
const { t } = useI18n();
|
|
|
|
const { data } = await useFetch<InstanceInfo>('https://instanceapp.misskey.page/instances.json', {
|
|
onRequestError: () => {
|
|
alert(t('_servers._system.fetchError'));
|
|
}
|
|
});
|
|
|
|
const langStats = computed(() => {
|
|
const d = data.value?.instancesInfos;
|
|
if (!d || d.length === 0) {
|
|
return [];
|
|
}
|
|
const out: Record<string, number> = {};
|
|
d.forEach((v) => {
|
|
if (!out[v.langs[0]]) {
|
|
out[v.langs[0]] = 1;
|
|
} else {
|
|
out[v.langs[0]]++;
|
|
}
|
|
});
|
|
|
|
return Object.entries(out)
|
|
.sort(([, a], [, b]) => b - a)
|
|
.reduce(
|
|
(r, [k, v]) => ({
|
|
...r,
|
|
[k]: v
|
|
}),
|
|
{}
|
|
);
|
|
});
|
|
const langFocus = ref<number | undefined>();
|
|
|
|
const regAcceptanceStats = computed(() => {
|
|
const d = data.value?.instancesInfos;
|
|
if (!d || d.length === 0) {
|
|
return [];
|
|
}
|
|
const out = {
|
|
public: 0,
|
|
inviteOnly: 0,
|
|
};
|
|
d.forEach((v) => {
|
|
if (v.meta?.disableRegistration) {
|
|
out.inviteOnly++;
|
|
} else {
|
|
out.public++;
|
|
}
|
|
});
|
|
|
|
return Object.entries(out)
|
|
.sort(([, a], [, b]) => b - a)
|
|
.reduce(
|
|
(r, [k, v]) => ({
|
|
...r,
|
|
[k]: v
|
|
}),
|
|
{}
|
|
);
|
|
});
|
|
const regAcceptanceFocus = ref<number | undefined>();
|
|
|
|
const notesCountStats = computed(() => {
|
|
const d = data.value?.instancesInfos;
|
|
if (!d || d.length === 0) {
|
|
return [];
|
|
}
|
|
const out: Record<string, number> = {};
|
|
|
|
d.forEach((v) => {
|
|
out[v.name] = v.stats?.originalNotesCount ?? 0;
|
|
});
|
|
|
|
return Object.entries(out)
|
|
.sort(([, a], [, b]) => b - a)
|
|
.reduce(
|
|
(r, [k, v]) => ({
|
|
...r,
|
|
[k]: v
|
|
}),
|
|
{}
|
|
);
|
|
});
|
|
const notesCountFocus = ref<number | undefined>();
|
|
|
|
const avgVersionStats = computed(() => {
|
|
const d = data.value?.instancesInfos;
|
|
if (!d || d.length === 0) {
|
|
return [];
|
|
}
|
|
const out: Record<string, number> = {};
|
|
|
|
d.forEach((v) => {
|
|
const ver = v.meta?.version.replace(/[-\+].+$/g, '');
|
|
if (ver) {
|
|
if (!out[ver]) {
|
|
out[ver] = 1;
|
|
} else {
|
|
out[ver]++;
|
|
}
|
|
}
|
|
});
|
|
|
|
return Object.entries(out)
|
|
.sort(([, a], [, b]) => b - a)
|
|
.reduce(
|
|
(r, [k, v]) => ({
|
|
...r,
|
|
[k]: v
|
|
}),
|
|
{}
|
|
);
|
|
});
|
|
const avgVersionFocus = ref<number | undefined>();
|
|
|
|
function trunc(obj: Record<any, number>, ratio: number = 0.02) {
|
|
const sum = Object.values(obj).reduce((p, c) => p + c, 0);
|
|
const toBeTrunked = Object.values(obj).filter((v) => v / sum < ratio);
|
|
|
|
return {
|
|
...Object.entries(obj)
|
|
.slice(0, (Object.values(obj).length - toBeTrunked.length))
|
|
.reduce(
|
|
(r, [k, v]) => ({
|
|
...r,
|
|
[k]: v
|
|
}),
|
|
{}
|
|
),
|
|
__others: toBeTrunked.reduce((p, c) => p + c, 0),
|
|
};
|
|
}
|
|
|
|
</script>
|
|
|
|
<style module>
|
|
.kvRoot {
|
|
grid-template-columns: 1fr auto;
|
|
}
|
|
</style> |