fix: remove charts from client (close #76)
This commit is contained in:
parent
3e663f975e
commit
670e750257
22 changed files with 8 additions and 1755 deletions
|
@ -365,235 +365,6 @@ const exportData = () => {
|
|||
// TODO
|
||||
};
|
||||
|
||||
const fetchFederationChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/federation", {
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Received",
|
||||
type: "area",
|
||||
data: format(raw.inboxInstances),
|
||||
color: colors.blue,
|
||||
},
|
||||
{
|
||||
name: "Delivered",
|
||||
type: "area",
|
||||
data: format(raw.deliveredInstances),
|
||||
color: colors.green,
|
||||
},
|
||||
{
|
||||
name: "Stalled",
|
||||
type: "area",
|
||||
data: format(raw.stalled),
|
||||
color: colors.red,
|
||||
},
|
||||
{
|
||||
name: "Pub Active",
|
||||
type: "line",
|
||||
data: format(raw.pubActive),
|
||||
color: colors.purple,
|
||||
},
|
||||
{
|
||||
name: "Sub Active",
|
||||
type: "line",
|
||||
data: format(raw.subActive),
|
||||
color: colors.orange,
|
||||
},
|
||||
{
|
||||
name: "Pub & Sub",
|
||||
type: "line",
|
||||
data: format(raw.pubsub),
|
||||
dashed: true,
|
||||
color: colors.cyan,
|
||||
},
|
||||
{
|
||||
name: "Pub",
|
||||
type: "line",
|
||||
data: format(raw.pub),
|
||||
dashed: true,
|
||||
color: colors.purple,
|
||||
},
|
||||
{
|
||||
name: "Sub",
|
||||
type: "line",
|
||||
data: format(raw.sub),
|
||||
dashed: true,
|
||||
color: colors.orange,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchApRequestChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/ap-request", {
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "In",
|
||||
type: "area",
|
||||
color: "#31748f",
|
||||
data: format(raw.inboxReceived),
|
||||
},
|
||||
{
|
||||
name: "Out (succ)",
|
||||
type: "area",
|
||||
color: "#c4a7e7",
|
||||
data: format(raw.deliverSucceeded),
|
||||
},
|
||||
{
|
||||
name: "Out (fail)",
|
||||
type: "area",
|
||||
color: "#f6c177",
|
||||
data: format(raw.deliverFailed),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/notes", {
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "All",
|
||||
type: "line",
|
||||
data: format(
|
||||
type === "combined"
|
||||
? sum(
|
||||
raw.local.inc,
|
||||
negate(raw.local.dec),
|
||||
raw.remote.inc,
|
||||
negate(raw.remote.dec),
|
||||
)
|
||||
: sum(raw[type].inc, negate(raw[type].dec)),
|
||||
),
|
||||
color: "#888888",
|
||||
},
|
||||
{
|
||||
name: "Renotes",
|
||||
type: "area",
|
||||
data: format(
|
||||
type === "combined"
|
||||
? sum(raw.local.diffs.renote, raw.remote.diffs.renote)
|
||||
: raw[type].diffs.renote,
|
||||
),
|
||||
color: colors.green,
|
||||
},
|
||||
{
|
||||
name: "Replies",
|
||||
type: "area",
|
||||
data: format(
|
||||
type === "combined"
|
||||
? sum(raw.local.diffs.reply, raw.remote.diffs.reply)
|
||||
: raw[type].diffs.reply,
|
||||
),
|
||||
color: colors.yellow,
|
||||
},
|
||||
{
|
||||
name: "Normal",
|
||||
type: "area",
|
||||
data: format(
|
||||
type === "combined"
|
||||
? sum(raw.local.diffs.normal, raw.remote.diffs.normal)
|
||||
: raw[type].diffs.normal,
|
||||
),
|
||||
color: colors.blue,
|
||||
},
|
||||
{
|
||||
name: "With file",
|
||||
type: "area",
|
||||
data: format(
|
||||
type === "combined"
|
||||
? sum(
|
||||
raw.local.diffs.withFile,
|
||||
raw.remote.diffs.withFile,
|
||||
)
|
||||
: raw[type].diffs.withFile,
|
||||
),
|
||||
color: colors.purple,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchNotesTotalChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/notes", {
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Combined",
|
||||
type: "line",
|
||||
data: format(sum(raw.local.total, raw.remote.total)),
|
||||
},
|
||||
{
|
||||
name: "Local",
|
||||
type: "area",
|
||||
data: format(raw.local.total),
|
||||
},
|
||||
{
|
||||
name: "Remote",
|
||||
type: "area",
|
||||
data: format(raw.remote.total),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/users", {
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Combined",
|
||||
type: "line",
|
||||
data: format(
|
||||
total
|
||||
? sum(raw.local.total, raw.remote.total)
|
||||
: sum(
|
||||
raw.local.inc,
|
||||
negate(raw.local.dec),
|
||||
raw.remote.inc,
|
||||
negate(raw.remote.dec),
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Local",
|
||||
type: "area",
|
||||
data: format(
|
||||
total
|
||||
? raw.local.total
|
||||
: sum(raw.local.inc, negate(raw.local.dec)),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Remote",
|
||||
type: "area",
|
||||
data: format(
|
||||
total
|
||||
? raw.remote.total
|
||||
: sum(raw.remote.inc, negate(raw.remote.dec)),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/active-users", {
|
||||
limit: props.limit,
|
||||
|
@ -659,424 +430,13 @@ const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
|
|||
};
|
||||
};
|
||||
|
||||
const fetchDriveChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/drive", {
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
bytes: true,
|
||||
series: [
|
||||
{
|
||||
name: "All",
|
||||
type: "line",
|
||||
dashed: true,
|
||||
data: format(
|
||||
sum(
|
||||
raw.local.incSize,
|
||||
negate(raw.local.decSize),
|
||||
raw.remote.incSize,
|
||||
negate(raw.remote.decSize),
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Local +",
|
||||
type: "area",
|
||||
data: format(raw.local.incSize),
|
||||
},
|
||||
{
|
||||
name: "Local -",
|
||||
type: "area",
|
||||
data: format(negate(raw.local.decSize)),
|
||||
},
|
||||
{
|
||||
name: "Remote +",
|
||||
type: "area",
|
||||
data: format(raw.remote.incSize),
|
||||
},
|
||||
{
|
||||
name: "Remote -",
|
||||
type: "area",
|
||||
data: format(negate(raw.remote.decSize)),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/drive", {
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "All",
|
||||
type: "line",
|
||||
dashed: true,
|
||||
data: format(
|
||||
sum(
|
||||
raw.local.incCount,
|
||||
negate(raw.local.decCount),
|
||||
raw.remote.incCount,
|
||||
negate(raw.remote.decCount),
|
||||
),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Local +",
|
||||
type: "area",
|
||||
data: format(raw.local.incCount),
|
||||
},
|
||||
{
|
||||
name: "Local -",
|
||||
type: "area",
|
||||
data: format(negate(raw.local.decCount)),
|
||||
},
|
||||
{
|
||||
name: "Remote +",
|
||||
type: "area",
|
||||
data: format(raw.remote.incCount),
|
||||
},
|
||||
{
|
||||
name: "Remote -",
|
||||
type: "area",
|
||||
data: format(negate(raw.remote.decCount)),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/instance", {
|
||||
host: props.args.host,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "In",
|
||||
type: "area",
|
||||
color: "#31748f",
|
||||
data: format(raw.requests.received),
|
||||
},
|
||||
{
|
||||
name: "Out (succ)",
|
||||
type: "area",
|
||||
color: "#c4a7e7",
|
||||
data: format(raw.requests.succeeded),
|
||||
},
|
||||
{
|
||||
name: "Out (fail)",
|
||||
type: "area",
|
||||
color: "#f6c177",
|
||||
data: format(raw.requests.failed),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchInstanceUsersChart = async (
|
||||
total: boolean,
|
||||
): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/instance", {
|
||||
host: props.args.host,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Users",
|
||||
type: "area",
|
||||
color: "#31748f",
|
||||
data: format(
|
||||
total
|
||||
? raw.users.total
|
||||
: sum(raw.users.inc, negate(raw.users.dec)),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchInstanceNotesChart = async (
|
||||
total: boolean,
|
||||
): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/instance", {
|
||||
host: props.args.host,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Posts",
|
||||
type: "area",
|
||||
color: "#31748f",
|
||||
data: format(
|
||||
total
|
||||
? raw.notes.total
|
||||
: sum(raw.notes.inc, negate(raw.notes.dec)),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchInstanceFfChart = async (
|
||||
total: boolean,
|
||||
): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/instance", {
|
||||
host: props.args.host,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Following",
|
||||
type: "area",
|
||||
color: "#31748f",
|
||||
data: format(
|
||||
total
|
||||
? raw.following.total
|
||||
: sum(raw.following.inc, negate(raw.following.dec)),
|
||||
),
|
||||
},
|
||||
{
|
||||
name: "Followers",
|
||||
type: "area",
|
||||
color: "#c4a7e7",
|
||||
data: format(
|
||||
total
|
||||
? raw.followers.total
|
||||
: sum(raw.followers.inc, negate(raw.followers.dec)),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchInstanceDriveUsageChart = async (
|
||||
total: boolean,
|
||||
): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/instance", {
|
||||
host: props.args.host,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
bytes: true,
|
||||
series: [
|
||||
{
|
||||
name: "Drive usage",
|
||||
type: "area",
|
||||
color: "#31748f",
|
||||
data: format(
|
||||
total
|
||||
? raw.drive.totalUsage
|
||||
: sum(raw.drive.incUsage, negate(raw.drive.decUsage)),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchInstanceDriveFilesChart = async (
|
||||
total: boolean,
|
||||
): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/instance", {
|
||||
host: props.args.host,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Drive files",
|
||||
type: "area",
|
||||
color: "#31748f",
|
||||
data: format(
|
||||
total
|
||||
? raw.drive.totalFiles
|
||||
: sum(raw.drive.incFiles, negate(raw.drive.decFiles)),
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/user/notes", {
|
||||
userId: props.args.user.id,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
...(props.args.withoutAll
|
||||
? []
|
||||
: [
|
||||
{
|
||||
name: "All",
|
||||
type: "line",
|
||||
data: format(sum(raw.inc, negate(raw.dec))),
|
||||
color: "#888888",
|
||||
},
|
||||
]),
|
||||
{
|
||||
name: "With file",
|
||||
type: "area",
|
||||
data: format(raw.diffs.withFile),
|
||||
color: colors.purple,
|
||||
},
|
||||
{
|
||||
name: "Renotes",
|
||||
type: "area",
|
||||
data: format(raw.diffs.renote),
|
||||
color: colors.green,
|
||||
},
|
||||
{
|
||||
name: "Replies",
|
||||
type: "area",
|
||||
data: format(raw.diffs.reply),
|
||||
color: colors.yellow,
|
||||
},
|
||||
{
|
||||
name: "Normal",
|
||||
type: "area",
|
||||
data: format(raw.diffs.normal),
|
||||
color: colors.blue,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/user/following", {
|
||||
userId: props.args.user.id,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Local",
|
||||
type: "area",
|
||||
data: format(raw.local.followings.total),
|
||||
},
|
||||
{
|
||||
name: "Remote",
|
||||
type: "area",
|
||||
data: format(raw.remote.followings.total),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/user/following", {
|
||||
userId: props.args.user.id,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Local",
|
||||
type: "area",
|
||||
data: format(raw.local.followers.total),
|
||||
},
|
||||
{
|
||||
name: "Remote",
|
||||
type: "area",
|
||||
data: format(raw.remote.followers.total),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
|
||||
const raw = await os.apiGet("charts/user/drive", {
|
||||
userId: props.args.user.id,
|
||||
limit: props.limit,
|
||||
span: props.span,
|
||||
});
|
||||
return {
|
||||
series: [
|
||||
{
|
||||
name: "Inc",
|
||||
type: "area",
|
||||
data: format(raw.incSize),
|
||||
},
|
||||
{
|
||||
name: "Dec",
|
||||
type: "area",
|
||||
data: format(raw.decSize),
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
const fetchAndRender = async () => {
|
||||
const fetchData = () => {
|
||||
switch (props.src) {
|
||||
case "federation":
|
||||
return fetchFederationChart();
|
||||
case "ap-request":
|
||||
return fetchApRequestChart();
|
||||
case "users":
|
||||
return fetchUsersChart(false);
|
||||
case "users-total":
|
||||
return fetchUsersChart(true);
|
||||
case "active-users":
|
||||
return fetchActiveUsersChart();
|
||||
case "notes":
|
||||
return fetchNotesChart("combined");
|
||||
case "local-notes":
|
||||
return fetchNotesChart("local");
|
||||
case "remote-notes":
|
||||
return fetchNotesChart("remote");
|
||||
case "notes-total":
|
||||
return fetchNotesTotalChart();
|
||||
case "drive":
|
||||
return fetchDriveChart();
|
||||
case "drive-files":
|
||||
return fetchDriveFilesChart();
|
||||
case "instance-requests":
|
||||
return fetchInstanceRequestsChart();
|
||||
case "instance-users":
|
||||
return fetchInstanceUsersChart(false);
|
||||
case "instance-users-total":
|
||||
return fetchInstanceUsersChart(true);
|
||||
case "instance-notes":
|
||||
return fetchInstanceNotesChart(false);
|
||||
case "instance-notes-total":
|
||||
return fetchInstanceNotesChart(true);
|
||||
case "instance-ff":
|
||||
return fetchInstanceFfChart(false);
|
||||
case "instance-ff-total":
|
||||
return fetchInstanceFfChart(true);
|
||||
case "instance-drive-usage":
|
||||
return fetchInstanceDriveUsageChart(false);
|
||||
case "instance-drive-usage-total":
|
||||
return fetchInstanceDriveUsageChart(true);
|
||||
case "instance-drive-files":
|
||||
return fetchInstanceDriveFilesChart(false);
|
||||
case "instance-drive-files-total":
|
||||
return fetchInstanceDriveFilesChart(true);
|
||||
|
||||
case "per-user-notes":
|
||||
return fetchPerUserNotesChart();
|
||||
case "per-user-following":
|
||||
return fetchPerUserFollowingChart();
|
||||
case "per-user-followers":
|
||||
return fetchPerUserFollowersChart();
|
||||
case "per-user-drive":
|
||||
return fetchPerUserDriveChart();
|
||||
default:
|
||||
console.error(`${props.src} chart is disabled.`);
|
||||
}
|
||||
};
|
||||
fetching.value = true;
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
{{ instance.softwareVersion }}</span
|
||||
>
|
||||
</div>
|
||||
<MkMiniChart v-if="chartValues" class="chart" :src="chartValues" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -27,7 +26,6 @@
|
|||
import { ref } from "vue";
|
||||
|
||||
import type * as firefish from "firefish-js";
|
||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||
import * as os from "@/os";
|
||||
import { getProxiedImageUrlNullable } from "@/scripts/media-proxy";
|
||||
|
||||
|
@ -35,18 +33,6 @@ const props = defineProps<{
|
|||
instance: firefish.entities.Instance;
|
||||
}>();
|
||||
|
||||
const chartValues = ref<number[] | null>(null);
|
||||
|
||||
os.apiGet("charts/instance", {
|
||||
host: props.instance.host,
|
||||
limit: 16 + 1,
|
||||
span: "day",
|
||||
}).then((res) => {
|
||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||
res.requests.received.splice(0, 1);
|
||||
chartValues.value = res.requests.received;
|
||||
});
|
||||
|
||||
function getInstanceIcon(instance): string {
|
||||
return (
|
||||
getProxiedImageUrlNullable(instance.iconUrl, "preview") ??
|
||||
|
@ -104,10 +90,6 @@ function getInstanceIcon(instance): string {
|
|||
}
|
||||
}
|
||||
|
||||
> :global(.chart) {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
&:global(.yellow) {
|
||||
--c: rgb(255 196 0 / 15%);
|
||||
background-image: linear-gradient(
|
||||
|
|
|
@ -1,52 +1,9 @@
|
|||
<template>
|
||||
<div :class="$style.root">
|
||||
<MkFolder class="item">
|
||||
<template #header>Chart</template>
|
||||
<template #header>Active Users</template>
|
||||
<div :class="$style.chart">
|
||||
<div class="selects">
|
||||
<MkSelect v-model="chartSrc" style="margin: 0; flex: 1">
|
||||
<optgroup :label="i18n.ts.federation">
|
||||
<option value="federation">
|
||||
{{ i18n.ts._charts.federation }}
|
||||
</option>
|
||||
<option value="ap-request">
|
||||
{{ i18n.ts._charts.apRequest }}
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup :label="i18n.ts.users">
|
||||
<option value="users">
|
||||
{{ i18n.ts._charts.usersIncDec }}
|
||||
</option>
|
||||
<option value="users-total">
|
||||
{{ i18n.ts._charts.usersTotal }}
|
||||
</option>
|
||||
<option value="active-users">
|
||||
{{ i18n.ts._charts.activeUsers }}
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup :label="i18n.ts.notes">
|
||||
<option value="notes">
|
||||
{{ i18n.ts._charts.notesIncDec }}
|
||||
</option>
|
||||
<option value="local-notes">
|
||||
{{ i18n.ts._charts.localNotesIncDec }}
|
||||
</option>
|
||||
<option value="remote-notes">
|
||||
{{ i18n.ts._charts.remoteNotesIncDec }}
|
||||
</option>
|
||||
<option value="notes-total">
|
||||
{{ i18n.ts._charts.notesTotal }}
|
||||
</option>
|
||||
</optgroup>
|
||||
<optgroup :label="i18n.ts.drive">
|
||||
<option value="drive-files">
|
||||
{{ i18n.ts._charts.filesIncDec }}
|
||||
</option>
|
||||
<option value="drive">
|
||||
{{ i18n.ts._charts.storageUsageIncDec }}
|
||||
</option>
|
||||
</optgroup>
|
||||
</MkSelect>
|
||||
<MkSelect v-model="chartSpan" style="margin: 0 0 0 10px">
|
||||
<option value="hour">{{ i18n.ts.perHour }}</option>
|
||||
<option value="day">{{ i18n.ts.perDay }}</option>
|
||||
|
@ -54,32 +11,15 @@
|
|||
</div>
|
||||
<div class="chart _panel">
|
||||
<MkChart
|
||||
:src="chartSrc"
|
||||
src="active-users"
|
||||
:span="chartSpan"
|
||||
:limit="chartLimit"
|
||||
:detailed="true"
|
||||
></MkChart>
|
||||
</div>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder class="item">
|
||||
<template #header>Active users heatmap</template>
|
||||
<MkSelect v-model="heatmapSrc" style="margin: 0 0 12px 0">
|
||||
<option value="active-users">Active users</option>
|
||||
<option value="notes">Posts</option>
|
||||
<option value="ap-requests-inbox-received">
|
||||
Fediverse Requests: inboxReceived
|
||||
</option>
|
||||
<option value="ap-requests-deliver-succeeded">
|
||||
Fediverse Requests: deliverSucceeded
|
||||
</option>
|
||||
<option value="ap-requests-deliver-failed">
|
||||
Fediverse Requests: deliverFailed
|
||||
</option>
|
||||
</MkSelect>
|
||||
<div class="_panel" :class="$style.heatmap">
|
||||
<MkHeatmap :src="heatmapSrc" />
|
||||
<MkHeatmap src="active-users" />
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
|
@ -117,8 +57,6 @@ initChart();
|
|||
|
||||
const chartLimit = 500;
|
||||
const chartSpan = ref<"hour" | "day">("hour");
|
||||
const chartSrc = ref("active-users");
|
||||
const heatmapSrc = ref("active-users");
|
||||
const subDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||
const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
|
||||
|
||||
|
|
|
@ -98,7 +98,6 @@
|
|||
v-if="tab === 'renotes' && renotes"
|
||||
:key="item.user.id"
|
||||
:user="item.user"
|
||||
:with-chart="false"
|
||||
/>
|
||||
<!-- </MkPagination> -->
|
||||
<MkLoading v-else-if="tab === 'renotes' && note.renoteCount > 0" />
|
||||
|
|
|
@ -23,12 +23,7 @@
|
|||
}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<MkUserCardMini
|
||||
v-for="user in users"
|
||||
:key="user.id"
|
||||
:user="user"
|
||||
:with-chart="false"
|
||||
/>
|
||||
<MkUserCardMini v-for="user in users" :key="user.id" :user="user" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<MkLoading />
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
><span class="acct _monospace">@{{ acct(user) }}</span></span
|
||||
>
|
||||
</div>
|
||||
<MkMiniChart v-if="chartValues" class="chart" :src="chartValues" />
|
||||
</MkA>
|
||||
</template>
|
||||
|
||||
|
@ -27,35 +26,17 @@
|
|||
import { ref } from "vue";
|
||||
|
||||
import type * as misskey from "firefish-js";
|
||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||
import * as os from "@/os";
|
||||
import { acct, userPage } from "@/filters/user";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
user: misskey.entities.User;
|
||||
withChart?: boolean;
|
||||
showAboutPage?: boolean;
|
||||
}>(),
|
||||
{
|
||||
withChart: true,
|
||||
showAboutPage: false,
|
||||
},
|
||||
);
|
||||
|
||||
const chartValues = ref<number[] | null>(null);
|
||||
|
||||
if (props.withChart) {
|
||||
os.apiGet("charts/user/notes", {
|
||||
userId: props.user.id,
|
||||
limit: 16 + 1,
|
||||
span: "day",
|
||||
}).then((res) => {
|
||||
// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
|
||||
res.inc.splice(0, 1);
|
||||
chartValues.value = res.inc;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
|
|
@ -1,289 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<MkLoading v-if="fetching" />
|
||||
<div v-show="!fetching" :class="$style.root">
|
||||
<div class="charts _panel">
|
||||
<div class="chart">
|
||||
<canvas ref="chartEl2"></canvas>
|
||||
</div>
|
||||
<div class="chart">
|
||||
<canvas ref="chartEl"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref, shallowRef } from "vue";
|
||||
import { Chart } from "chart.js";
|
||||
import gradient from "chartjs-plugin-gradient";
|
||||
import * as os from "@/os";
|
||||
import { useChartTooltip } from "@/scripts/use-chart-tooltip";
|
||||
import { chartVLine } from "@/scripts/chart-vline";
|
||||
import { defaultStore } from "@/store";
|
||||
import { alpha } from "@/scripts/color";
|
||||
import { initChart } from "@/scripts/init-chart";
|
||||
|
||||
initChart();
|
||||
|
||||
const chartLimit = 50;
|
||||
const chartEl = shallowRef<HTMLCanvasElement>();
|
||||
const chartEl2 = shallowRef<HTMLCanvasElement>();
|
||||
const fetching = ref(true);
|
||||
|
||||
const { handler: externalTooltipHandler } = useChartTooltip();
|
||||
const { handler: externalTooltipHandler2 } = useChartTooltip();
|
||||
|
||||
onMounted(async () => {
|
||||
const now = new Date();
|
||||
|
||||
const getDate = (ago: number) => {
|
||||
const y = now.getFullYear();
|
||||
const m = now.getMonth();
|
||||
const d = now.getDate();
|
||||
|
||||
return new Date(y, m, d - ago);
|
||||
};
|
||||
|
||||
const format = (arr) => {
|
||||
return arr.map((v, i) => ({
|
||||
x: getDate(i).getTime(),
|
||||
y: v,
|
||||
}));
|
||||
};
|
||||
|
||||
const formatMinus = (arr) => {
|
||||
return arr.map((v, i) => ({
|
||||
x: getDate(i).getTime(),
|
||||
y: -v,
|
||||
}));
|
||||
};
|
||||
|
||||
const raw = await os.api("charts/ap-request", {
|
||||
limit: chartLimit,
|
||||
span: "day",
|
||||
});
|
||||
|
||||
const vLineColor = defaultStore.state.darkMode
|
||||
? "rgba(255, 255, 255, 0.2)"
|
||||
: "rgba(0, 0, 0, 0.2)";
|
||||
const succColor = "#9ccfd8";
|
||||
const failColor = "#f6c177";
|
||||
|
||||
new Chart(chartEl.value, {
|
||||
type: "line",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
stack: "a",
|
||||
parsing: false,
|
||||
label: "Out: Succ",
|
||||
data: format(raw.deliverSucceeded).slice().reverse(),
|
||||
tension: 0.3,
|
||||
pointRadius: 0,
|
||||
borderWidth: 2,
|
||||
borderColor: succColor,
|
||||
borderJoinStyle: "round",
|
||||
borderRadius: 4,
|
||||
backgroundColor: alpha(succColor, 0.35),
|
||||
fill: true,
|
||||
clip: 8,
|
||||
},
|
||||
{
|
||||
stack: "a",
|
||||
parsing: false,
|
||||
label: "Out: Fail",
|
||||
data: formatMinus(raw.deliverFailed).slice().reverse(),
|
||||
tension: 0.3,
|
||||
pointRadius: 0,
|
||||
borderWidth: 2,
|
||||
borderColor: failColor,
|
||||
borderJoinStyle: "round",
|
||||
borderRadius: 4,
|
||||
backgroundColor: alpha(failColor, 0.35),
|
||||
fill: true,
|
||||
clip: 8,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
aspectRatio: 2.5,
|
||||
layout: {
|
||||
padding: {
|
||||
left: 0,
|
||||
right: 8,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
stacked: true,
|
||||
offset: false,
|
||||
time: {
|
||||
stepSize: 1,
|
||||
unit: "day",
|
||||
},
|
||||
grid: {
|
||||
display: true,
|
||||
},
|
||||
ticks: {
|
||||
display: true,
|
||||
maxRotation: 0,
|
||||
autoSkipPadding: 16,
|
||||
},
|
||||
min: getDate(chartLimit).getTime(),
|
||||
},
|
||||
y: {
|
||||
stacked: true,
|
||||
position: "left",
|
||||
suggestedMax: 10,
|
||||
grid: {
|
||||
display: true,
|
||||
},
|
||||
ticks: {
|
||||
display: true,
|
||||
// mirror: true,
|
||||
callback: (value, index, values) =>
|
||||
value < 0 ? -value : value,
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: "index",
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
hoverRadius: 5,
|
||||
hoverBorderWidth: 2,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
mode: "index",
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
external: externalTooltipHandler,
|
||||
},
|
||||
gradient,
|
||||
},
|
||||
},
|
||||
plugins: [chartVLine(vLineColor)],
|
||||
});
|
||||
|
||||
new Chart(chartEl2.value, {
|
||||
type: "bar",
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
parsing: false,
|
||||
label: "In",
|
||||
data: format(raw.inboxReceived).slice().reverse(),
|
||||
tension: 0.3,
|
||||
pointRadius: 0,
|
||||
borderWidth: 0,
|
||||
borderJoinStyle: "round",
|
||||
borderRadius: 4,
|
||||
backgroundColor: "#c4a7e7",
|
||||
barPercentage: 0.8,
|
||||
categoryPercentage: 0.9,
|
||||
fill: true,
|
||||
clip: 8,
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
aspectRatio: 5,
|
||||
layout: {
|
||||
padding: {
|
||||
left: 0,
|
||||
right: 8,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: "time",
|
||||
offset: false,
|
||||
time: {
|
||||
stepSize: 1,
|
||||
unit: "day",
|
||||
displayFormats: {
|
||||
day: "M/d",
|
||||
month: "Y/M",
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
display: false,
|
||||
},
|
||||
ticks: {
|
||||
display: false,
|
||||
maxRotation: 0,
|
||||
autoSkipPadding: 16,
|
||||
},
|
||||
min: getDate(chartLimit).getTime(),
|
||||
},
|
||||
y: {
|
||||
position: "left",
|
||||
suggestedMax: 10,
|
||||
grid: {
|
||||
display: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
interaction: {
|
||||
intersect: false,
|
||||
mode: "index",
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
hoverRadius: 5,
|
||||
hoverBorderWidth: 2,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
mode: "index",
|
||||
animation: {
|
||||
duration: 0,
|
||||
},
|
||||
external: externalTooltipHandler2,
|
||||
},
|
||||
gradient,
|
||||
},
|
||||
},
|
||||
plugins: [chartVLine(vLineColor)],
|
||||
});
|
||||
|
||||
fetching.value = false;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
.root {
|
||||
&:global {
|
||||
> .charts {
|
||||
> .chart {
|
||||
padding: 16px;
|
||||
|
||||
&:first-child {
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -17,40 +17,6 @@
|
|||
<div class="subTitle">Top 10</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!fetching" class="items">
|
||||
<div class="item _panel sub">
|
||||
<div class="icon">
|
||||
<i class="ph-download ph-bold ph-xl"></i>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="value">
|
||||
{{ number(federationSubActive) }}
|
||||
<MkNumberDiff
|
||||
v-tooltip="i18n.ts.dayOverDayChanges"
|
||||
class="diff"
|
||||
:value="federationSubActiveDiff"
|
||||
></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">Sub</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item _panel pub">
|
||||
<div class="icon">
|
||||
<i class="ph-upload ph-bold ph-xl"></i>
|
||||
</div>
|
||||
<div class="body">
|
||||
<div class="value">
|
||||
{{ number(federationPubActive) }}
|
||||
<MkNumberDiff
|
||||
v-tooltip="i18n.ts.dayOverDayChanges"
|
||||
class="diff"
|
||||
:value="federationPubActiveDiff"
|
||||
></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">Pub</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,29 +1,11 @@
|
|||
<template>
|
||||
<div class="_panel" :class="$style.root">
|
||||
<MkSelect v-model="src" style="margin: 0 0 12px 0" small>
|
||||
<option value="notes">Posts</option>
|
||||
<option value="active-users">Active users</option>
|
||||
<option value="ap-requests-inbox-received">
|
||||
Fediverse Requests: inboxReceived
|
||||
</option>
|
||||
<option value="ap-requests-deliver-succeeded">
|
||||
Fediverse Requests: deliverSucceeded
|
||||
</option>
|
||||
<option value="ap-requests-deliver-failed">
|
||||
Fediverse Requests: deliverFailed
|
||||
</option>
|
||||
</MkSelect>
|
||||
<MkHeatmap :src="src" />
|
||||
<MkHeatmap src="active-users" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import MkHeatmap from "@/components/MkHeatmap.vue";
|
||||
import MkSelect from "@/components/form/select.vue";
|
||||
|
||||
const src = ref("notes");
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
|
|
@ -16,11 +16,6 @@
|
|||
:value="stats.originalUsersCount"
|
||||
style="margin-right: 0.5em"
|
||||
/>
|
||||
<MkNumberDiff
|
||||
v-tooltip="i18n.ts.dayOverDayChanges"
|
||||
class="diff"
|
||||
:value="usersComparedToThePrevDay"
|
||||
></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">{{ i18n.ts.users }}</div>
|
||||
</div>
|
||||
|
@ -35,11 +30,6 @@
|
|||
:value="stats.originalNotesCount"
|
||||
style="margin-right: 0.5em"
|
||||
/>
|
||||
<MkNumberDiff
|
||||
v-tooltip="i18n.ts.dayOverDayChanges"
|
||||
class="diff"
|
||||
:value="notesComparedToThePrevDay"
|
||||
></MkNumberDiff>
|
||||
</div>
|
||||
<div class="label">{{ i18n.ts.notes }}</div>
|
||||
</div>
|
||||
|
@ -96,7 +86,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import * as os from "@/os";
|
||||
import MkNumberDiff from "@/components/MkNumberDiff.vue";
|
||||
import MkNumber from "@/components/MkNumber.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
><span class="acct _monospace">@{{ acct(user) }}</span></span
|
||||
>
|
||||
</div>
|
||||
<MkMiniChart v-if="chart" class="chart" :src="chart.inc" />
|
||||
</MkA>
|
||||
</template>
|
||||
|
||||
|
@ -20,23 +19,12 @@
|
|||
import { ref } from "vue";
|
||||
|
||||
import type * as misskey from "firefish-js";
|
||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||
import * as os from "@/os";
|
||||
import { acct } from "@/filters/user";
|
||||
|
||||
const props = defineProps<{
|
||||
user: misskey.entities.User;
|
||||
}>();
|
||||
|
||||
const chart = ref(null);
|
||||
|
||||
os.apiGet("charts/user/notes", {
|
||||
userId: props.user.id,
|
||||
limit: 16,
|
||||
span: "day",
|
||||
}).then((res) => {
|
||||
chart.value = res;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" module>
|
||||
|
@ -81,9 +69,5 @@ os.apiGet("charts/user/notes", {
|
|||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
> :global(.chart) {
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -9,10 +9,6 @@
|
|||
<MkFolder class="item">
|
||||
<template #header>Active users</template>
|
||||
<XActiveUsers />
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder class="item">
|
||||
<template #header>Heatmap</template>
|
||||
<XHeatmap />
|
||||
</MkFolder>
|
||||
|
||||
|
@ -31,11 +27,6 @@
|
|||
<XInstances />
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder class="item">
|
||||
<template #header>Fediverse Requests</template>
|
||||
<XApRequests />
|
||||
</MkFolder>
|
||||
|
||||
<MkFolder class="item">
|
||||
<template #header>New users</template>
|
||||
<XUsers />
|
||||
|
@ -71,7 +62,6 @@ import {
|
|||
import XFederation from "./overview.federation.vue";
|
||||
import XInstances from "./overview.instances.vue";
|
||||
import XQueue from "./overview.queue.vue";
|
||||
import XApRequests from "./overview.ap-requests.vue";
|
||||
import XUsers from "./overview.users.vue";
|
||||
import XActiveUsers from "./overview.active-users.vue";
|
||||
import XStats from "./overview.stats.vue";
|
||||
|
|
|
@ -223,79 +223,6 @@
|
|||
</FormSection>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="_formRoot">
|
||||
<div class="cmhjzshl">
|
||||
<div class="selects">
|
||||
<MkSelect
|
||||
v-model="chartSrc"
|
||||
style="margin: 0 10px 0 0; flex: 1"
|
||||
>
|
||||
<option value="instance-requests">
|
||||
{{ i18n.ts._instanceCharts.requests }}
|
||||
</option>
|
||||
<option value="instance-users">
|
||||
{{ i18n.ts._instanceCharts.users }}
|
||||
</option>
|
||||
<option value="instance-users-total">
|
||||
{{ i18n.ts._instanceCharts.usersTotal }}
|
||||
</option>
|
||||
<option value="instance-notes">
|
||||
{{ i18n.ts._instanceCharts.notes }}
|
||||
</option>
|
||||
<option value="instance-notes-total">
|
||||
{{ i18n.ts._instanceCharts.notesTotal }}
|
||||
</option>
|
||||
<option value="instance-ff">
|
||||
{{ i18n.ts._instanceCharts.ff }}
|
||||
</option>
|
||||
<option value="instance-ff-total">
|
||||
{{ i18n.ts._instanceCharts.ffTotal }}
|
||||
</option>
|
||||
<option value="instance-drive-usage">
|
||||
{{ i18n.ts._instanceCharts.cacheSize }}
|
||||
</option>
|
||||
<option value="instance-drive-usage-total">
|
||||
{{
|
||||
i18n.ts._instanceCharts
|
||||
.cacheSizeTotal
|
||||
}}
|
||||
</option>
|
||||
<option value="instance-drive-files">
|
||||
{{ i18n.ts._instanceCharts.files }}
|
||||
</option>
|
||||
<option value="instance-drive-files-total">
|
||||
{{ i18n.ts._instanceCharts.filesTotal }}
|
||||
</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
<div class="charts">
|
||||
<div class="label">
|
||||
{{ i18n.t("recentNHours", { n: 90 }) }}
|
||||
</div>
|
||||
<MkChart
|
||||
class="chart"
|
||||
:src="chartSrc"
|
||||
span="hour"
|
||||
:limit="90"
|
||||
:args="{ host: host }"
|
||||
:detailed="true"
|
||||
></MkChart>
|
||||
<div class="label">
|
||||
{{ i18n.t("recentNDays", { n: 90 }) }}
|
||||
</div>
|
||||
<MkChart
|
||||
class="chart"
|
||||
:src="chartSrc"
|
||||
span="day"
|
||||
:limit="90"
|
||||
:args="{ host: host }"
|
||||
:detailed="true"
|
||||
></MkChart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</swiper-slide>
|
||||
<swiper-slide>
|
||||
<div class="_formRoot">
|
||||
<MkPagination
|
||||
|
@ -341,7 +268,6 @@ import { computed, ref, watch } from "vue";
|
|||
import { Virtual } from "swiper/modules";
|
||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||
import type * as firefish from "firefish-js";
|
||||
import MkChart from "@/components/MkChart.vue";
|
||||
import MkObjectView from "@/components/MkObjectView.vue";
|
||||
import FormLink from "@/components/form/link.vue";
|
||||
import MkLink from "@/components/MkLink.vue";
|
||||
|
@ -377,11 +303,10 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const tabs = ["overview"];
|
||||
if (iAmAdmin) tabs.push("chart", "users", "raw");
|
||||
if (iAmAdmin) tabs.push("users", "raw");
|
||||
const tab = ref(tabs[0]);
|
||||
watch(tab, () => syncSlide(tabs.indexOf(tab.value)));
|
||||
|
||||
const chartSrc = ref("instance-requests");
|
||||
const meta = ref<AugmentedInstanceMetadata | null>(null);
|
||||
const instance = ref<AugmentedInstance | null>(null);
|
||||
const suspended = ref(false);
|
||||
|
@ -488,11 +413,6 @@ const theTabs = [
|
|||
|
||||
if (iAmAdmin) {
|
||||
theTabs.push(
|
||||
{
|
||||
key: "chart",
|
||||
title: i18n.ts.charts,
|
||||
icon: "ph-chart-bar ph-bold ph-lg",
|
||||
},
|
||||
{
|
||||
key: "users",
|
||||
title: i18n.ts.users,
|
||||
|
@ -551,12 +471,5 @@ function syncSlide(index) {
|
|||
display: flex;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
> .charts {
|
||||
> .label {
|
||||
margin-bottom: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -17,20 +17,6 @@
|
|||
</FormSplit>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
<template #label>{{ i18n.ts.statistics }}</template>
|
||||
<MkChart
|
||||
src="per-user-drive"
|
||||
:args="{ user: $i }"
|
||||
span="day"
|
||||
:limit="7 * 5"
|
||||
:bar="true"
|
||||
:stacked="true"
|
||||
:detailed="false"
|
||||
:aspect-ratio="6"
|
||||
/>
|
||||
</FormSection>
|
||||
|
||||
<FormSection>
|
||||
<FormButton @click="chooseUploadFolder()">
|
||||
{{ i18n.ts.uploadFolder }}
|
||||
|
@ -82,7 +68,6 @@ import FormSplit from "@/components/form/split.vue";
|
|||
import * as os from "@/os";
|
||||
import bytes from "@/filters/bytes";
|
||||
import { defaultStore } from "@/store";
|
||||
import MkChart from "@/components/MkChart.vue";
|
||||
import { i18n } from "@/i18n";
|
||||
import { definePageMetadata } from "@/scripts/page-metadata";
|
||||
import { $i } from "@/account";
|
||||
|
|
|
@ -335,44 +335,6 @@
|
|||
</FormInput>
|
||||
</FormSection>
|
||||
</div>
|
||||
<div v-else-if="tab === 'chart'" class="_formRoot">
|
||||
<div class="cmhjzshm">
|
||||
<div class="selects">
|
||||
<MkSelect
|
||||
v-model="chartSrc"
|
||||
style="margin: 0 10px 0 0; flex: 1"
|
||||
>
|
||||
<option value="per-user-notes">
|
||||
{{ i18n.ts.notes }}
|
||||
</option>
|
||||
</MkSelect>
|
||||
</div>
|
||||
<div class="charts">
|
||||
<div class="label">
|
||||
{{ i18n.t("recentNHours", { n: 90 }) }}
|
||||
</div>
|
||||
<MkChart
|
||||
class="chart"
|
||||
:src="chartSrc"
|
||||
span="hour"
|
||||
:limit="90"
|
||||
:args="{ user, withoutAll: true }"
|
||||
:detailed="true"
|
||||
></MkChart>
|
||||
<div class="label">
|
||||
{{ i18n.t("recentNDays", { n: 90 }) }}
|
||||
</div>
|
||||
<MkChart
|
||||
class="chart"
|
||||
:src="chartSrc"
|
||||
span="day"
|
||||
:limit="90"
|
||||
:args="{ user, withoutAll: true }"
|
||||
:detailed="true"
|
||||
></MkChart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="tab === 'raw'" class="_formRoot">
|
||||
<MkObjectView v-if="info && $i.isAdmin" tall :value="info">
|
||||
</MkObjectView>
|
||||
|
@ -387,7 +349,6 @@
|
|||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from "vue";
|
||||
import type * as misskey from "firefish-js";
|
||||
import MkChart from "@/components/MkChart.vue";
|
||||
import MkObjectView from "@/components/MkObjectView.vue";
|
||||
import FormTextarea from "@/components/form/textarea.vue";
|
||||
import FormSwitch from "@/components/form/switch.vue";
|
||||
|
@ -415,7 +376,6 @@ const props = defineProps<{
|
|||
}>();
|
||||
|
||||
const tab = ref("overview");
|
||||
const chartSrc = ref("per-user-notes");
|
||||
const user = ref<null | misskey.entities.UserDetailed>();
|
||||
const init = ref<ReturnType<typeof createFetcher>>();
|
||||
const info = ref();
|
||||
|
@ -685,11 +645,6 @@ const headerTabs = computed(() =>
|
|||
icon: "ph-shield ph-bold ph-lg",
|
||||
}
|
||||
: null,
|
||||
{
|
||||
key: "chart",
|
||||
title: i18n.ts.charts,
|
||||
icon: "ph-chart-bar ph-bold ph-lg",
|
||||
},
|
||||
{
|
||||
key: "raw",
|
||||
title: "Raw",
|
||||
|
@ -783,13 +738,6 @@ definePageMetadata(
|
|||
display: flex;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
> .charts {
|
||||
> .label {
|
||||
margin-bottom: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
@ -362,11 +362,6 @@
|
|||
>
|
||||
<template v-if="narrow">
|
||||
<XPhotos :key="user.id" :user="user" />
|
||||
<!-- <XActivity
|
||||
:key="user.id"
|
||||
:user="user"
|
||||
style="margin-top: var(--margin)"
|
||||
/> -->
|
||||
</template>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -375,11 +370,6 @@
|
|||
</div>
|
||||
<div v-if="!narrow" class="sub">
|
||||
<XPhotos :key="user.id" :user="user" />
|
||||
<XActivity
|
||||
:key="user.id"
|
||||
:user="user"
|
||||
style="margin-top: var(--margin)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</MkSpacer>
|
||||
|
@ -413,7 +403,6 @@ import { $i } from "@/account";
|
|||
import { host } from "@/config";
|
||||
|
||||
const XPhotos = defineAsyncComponent(() => import("./index.photos.vue"));
|
||||
const XActivity = defineAsyncComponent(() => import("./index.activity.vue"));
|
||||
|
||||
const hideFollowButton = defaultStore.state.hideFollowButtons;
|
||||
const emphasizeFollowed = defaultStore.state.emphasizeFollowed;
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
<template>
|
||||
<MkContainer>
|
||||
<template #header
|
||||
><i
|
||||
class="ph-chart-bar ph-bold ph-lg"
|
||||
style="margin-right: 0.5em"
|
||||
></i
|
||||
>{{ i18n.ts.activity }}</template
|
||||
>
|
||||
<template #func>
|
||||
<button class="_button" @click="showMenu">
|
||||
<i class="ph-dots-three-outline ph-bold ph-lg"></i>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<div style="padding: 8px">
|
||||
<MkChart
|
||||
:src="chartSrc"
|
||||
:args="{ user, withoutAll: true }"
|
||||
span="day"
|
||||
:limit="limit"
|
||||
:bar="true"
|
||||
:stacked="true"
|
||||
:detailed="false"
|
||||
:aspect-ratio="5"
|
||||
/>
|
||||
</div>
|
||||
</MkContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
import type * as misskey from "firefish-js";
|
||||
import MkContainer from "@/components/MkContainer.vue";
|
||||
import MkChart from "@/components/MkChart.vue";
|
||||
import * as os from "@/os";
|
||||
import { i18n } from "@/i18n";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
user: misskey.entities.User;
|
||||
limit?: number;
|
||||
}>(),
|
||||
{
|
||||
limit: 50,
|
||||
},
|
||||
);
|
||||
|
||||
const chartSrc = ref("per-user-notes");
|
||||
|
||||
function showMenu(ev: MouseEvent) {
|
||||
os.popupMenu(
|
||||
[
|
||||
{
|
||||
text: i18n.ts.notes,
|
||||
active: true,
|
||||
action: () => {
|
||||
chartSrc.value = "per-user-notes";
|
||||
},
|
||||
} /*, {
|
||||
text: i18n.ts.following,
|
||||
action: () => {
|
||||
chartSrc = 'per-user-following';
|
||||
}
|
||||
}, {
|
||||
text: i18n.ts.followers,
|
||||
action: () => {
|
||||
chartSrc = 'per-user-followers';
|
||||
}
|
||||
} */,
|
||||
],
|
||||
ev.currentTarget ?? ev.target,
|
||||
);
|
||||
}
|
||||
</script>
|
|
@ -1,102 +0,0 @@
|
|||
<template>
|
||||
<svg viewBox="0 0 21 7">
|
||||
<rect
|
||||
v-for="record in activity"
|
||||
class="day"
|
||||
width="1"
|
||||
height="1"
|
||||
:x="record.x"
|
||||
:y="record.date.weekday"
|
||||
rx="1"
|
||||
ry="1"
|
||||
fill="transparent"
|
||||
>
|
||||
<title>
|
||||
{{ record.date.year }}/{{ record.date.month + 1 }}/{{
|
||||
record.date.day
|
||||
}}
|
||||
</title>
|
||||
</rect>
|
||||
<rect
|
||||
v-for="record in activity"
|
||||
class="day"
|
||||
:width="record.v"
|
||||
:height="record.v"
|
||||
:x="record.x + (1 - record.v) / 2"
|
||||
:y="record.date.weekday + (1 - record.v) / 2"
|
||||
rx="1"
|
||||
ry="1"
|
||||
:fill="record.color"
|
||||
style="pointer-events: none"
|
||||
/>
|
||||
<rect
|
||||
class="today"
|
||||
width="1"
|
||||
height="1"
|
||||
:x="activity[0].x"
|
||||
:y="activity[0].date.weekday"
|
||||
rx="1"
|
||||
ry="1"
|
||||
fill="none"
|
||||
stroke-width="0.1"
|
||||
stroke="#eb6f92"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
const props = defineProps<{
|
||||
activity: any[];
|
||||
}>();
|
||||
|
||||
for (const d of props.activity) {
|
||||
d.total = d.notes + d.replies + d.renotes;
|
||||
}
|
||||
const peak = Math.max(...props.activity.map((d) => d.total));
|
||||
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth();
|
||||
const day = now.getDate();
|
||||
|
||||
let x = 20;
|
||||
props.activity.slice().forEach((d, i) => {
|
||||
d.x = x;
|
||||
|
||||
const date = new Date(year, month, day - i);
|
||||
d.date = {
|
||||
year: date.getFullYear(),
|
||||
month: date.getMonth(),
|
||||
day: date.getDate(),
|
||||
weekday: date.getDay(),
|
||||
};
|
||||
|
||||
d.v = peak === 0 ? 0 : d.total / (peak / 2);
|
||||
if (d.v > 1) d.v = 1;
|
||||
const ch = d.date.weekday === 0 || d.date.weekday === 6 ? 275 : 170;
|
||||
const cs = d.v * 100;
|
||||
const cl = 15 + (1 - d.v) * 80;
|
||||
d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
|
||||
|
||||
if (d.date.weekday === 0) x--;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
svg {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
> rect {
|
||||
transform-origin: center;
|
||||
|
||||
&.day {
|
||||
&:hover {
|
||||
fill: rgba(#000, 0.05);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,136 +0,0 @@
|
|||
<template>
|
||||
<svg
|
||||
:viewBox="`0 0 ${viewBoxX} ${viewBoxY}`"
|
||||
@mousedown.prevent="onMousedown"
|
||||
>
|
||||
<polyline
|
||||
:points="pointsNote"
|
||||
fill="none"
|
||||
stroke-width="1"
|
||||
stroke="#c4a7e7"
|
||||
/>
|
||||
<polyline
|
||||
:points="pointsReply"
|
||||
fill="none"
|
||||
stroke-width="1"
|
||||
stroke="#eb6f92"
|
||||
/>
|
||||
<polyline
|
||||
:points="pointsRenote"
|
||||
fill="none"
|
||||
stroke-width="1"
|
||||
stroke="#ebbcba"
|
||||
/>
|
||||
<polyline
|
||||
:points="pointsTotal"
|
||||
fill="none"
|
||||
stroke-width="1"
|
||||
stroke="#6e6a86"
|
||||
stroke-dasharray="2 2"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const props = defineProps<{
|
||||
activity: any[];
|
||||
}>();
|
||||
|
||||
const viewBoxX: number = ref(147);
|
||||
const viewBoxY: number = ref(60);
|
||||
const zoom: number = ref(1);
|
||||
const pos: number = ref(0);
|
||||
const pointsNote: any = ref(null);
|
||||
const pointsReply: any = ref(null);
|
||||
const pointsRenote: any = ref(null);
|
||||
const pointsTotal: any = ref(null);
|
||||
|
||||
function dragListen(fn) {
|
||||
window.addEventListener("mousemove", fn);
|
||||
window.addEventListener("mouseleave", dragClear.bind(null, fn));
|
||||
window.addEventListener("mouseup", dragClear.bind(null, fn));
|
||||
}
|
||||
|
||||
function dragClear(fn) {
|
||||
window.removeEventListener("mousemove", fn);
|
||||
window.removeEventListener("mouseleave", dragClear);
|
||||
window.removeEventListener("mouseup", dragClear);
|
||||
}
|
||||
|
||||
function onMousedown(ev) {
|
||||
const clickX = ev.clientX;
|
||||
const clickY = ev.clientY;
|
||||
const baseZoom = zoom.value;
|
||||
const basePos = pos.value;
|
||||
|
||||
// 動かした時
|
||||
dragListen((me) => {
|
||||
const moveLeft = me.clientX - clickX;
|
||||
const moveTop = me.clientY - clickY;
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
function render() {
|
||||
const peak = Math.max(...props.activity.map((d) => d.total));
|
||||
if (peak !== 0) {
|
||||
const activity = props.activity.slice().reverse();
|
||||
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>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
svg {
|
||||
display: block;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
cursor: all-scroll;
|
||||
}
|
||||
</style>
|
|
@ -1,124 +0,0 @@
|
|||
<template>
|
||||
<MkContainer
|
||||
:show-header="widgetProps.showHeader"
|
||||
:naked="widgetProps.transparent"
|
||||
class="mkw-activity"
|
||||
>
|
||||
<template #header
|
||||
><i class="ph-chart-bar ph-bold ph-lg"></i
|
||||
>{{ i18n.ts._widgets.activity }}</template
|
||||
>
|
||||
<template #func
|
||||
><button
|
||||
v-if="!widgetProps.newStyle"
|
||||
class="_button"
|
||||
@click="toggleView()"
|
||||
>
|
||||
<i class="ph-sort-ascending ph-bold ph-lg"></i></button
|
||||
></template>
|
||||
|
||||
<div v-if="widgetProps.newStyle">
|
||||
<MkHeatmap src="my-notes" />
|
||||
</div>
|
||||
<div v-else>
|
||||
<MkLoading v-if="fetching" />
|
||||
<template v-else>
|
||||
<XCalendar
|
||||
v-show="widgetProps.view === 0"
|
||||
:activity="[].concat(activity)"
|
||||
/>
|
||||
<XChart
|
||||
v-show="widgetProps.view === 1"
|
||||
:activity="[].concat(activity)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</MkContainer>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, onUnmounted, reactive, ref, watch } from "vue";
|
||||
import type { Widget, WidgetComponentExpose } from "./widget";
|
||||
import {
|
||||
WidgetComponentEmits,
|
||||
WidgetComponentProps,
|
||||
useWidgetPropsManager,
|
||||
} from "./widget";
|
||||
import XCalendar from "./activity.calendar.vue";
|
||||
import XChart from "./activity.chart.vue";
|
||||
import MkHeatmap from "@/components/MkHeatmap.vue";
|
||||
import type { GetFormResultType } from "@/scripts/form";
|
||||
import * as os from "@/os";
|
||||
import MkContainer from "@/components/MkContainer.vue";
|
||||
import { $i } from "@/account";
|
||||
import { i18n } from "@/i18n";
|
||||
|
||||
const name = "activity";
|
||||
|
||||
const widgetPropsDef = {
|
||||
newStyle: {
|
||||
type: "boolean" as const,
|
||||
default: true,
|
||||
},
|
||||
showHeader: {
|
||||
type: "boolean" as const,
|
||||
default: true,
|
||||
},
|
||||
transparent: {
|
||||
type: "boolean" as const,
|
||||
default: false,
|
||||
},
|
||||
view: {
|
||||
type: "number" as const,
|
||||
default: 0,
|
||||
hidden: true,
|
||||
},
|
||||
};
|
||||
|
||||
type WidgetProps = GetFormResultType<typeof widgetPropsDef>;
|
||||
|
||||
// 現時点ではvueの制限によりimportしたtypeをジェネリックに渡せない
|
||||
// const props = defineProps<WidgetComponentProps<WidgetProps>>();
|
||||
// const emit = defineEmits<WidgetComponentEmits<WidgetProps>>();
|
||||
const props = defineProps<{ widget?: Widget<WidgetProps> }>();
|
||||
const emit = defineEmits<{ (ev: "updateProps", props: WidgetProps) }>();
|
||||
|
||||
const { widgetProps, configure, save } = useWidgetPropsManager(
|
||||
name,
|
||||
widgetPropsDef,
|
||||
props,
|
||||
emit,
|
||||
);
|
||||
|
||||
const activity = ref(null);
|
||||
const fetching = ref(true);
|
||||
|
||||
const toggleView = () => {
|
||||
if (widgetProps.view === 1) {
|
||||
widgetProps.view = 0;
|
||||
} else {
|
||||
widgetProps.view++;
|
||||
}
|
||||
save();
|
||||
};
|
||||
|
||||
os.apiGet("charts/user/notes", {
|
||||
userId: $i.id,
|
||||
span: "day",
|
||||
limit: 7 * 21,
|
||||
}).then((res) => {
|
||||
activity.value = res.diffs.normal.map((_, i) => ({
|
||||
total: res.diffs.normal[i] + res.diffs.reply[i] + res.diffs.renote[i],
|
||||
notes: res.diffs.normal[i],
|
||||
replies: res.diffs.reply[i],
|
||||
renotes: res.diffs.renote[i],
|
||||
}));
|
||||
fetching.value = false;
|
||||
});
|
||||
|
||||
defineExpose<WidgetComponentExpose>({
|
||||
name,
|
||||
configure,
|
||||
id: props.widget ? props.widget.id : null,
|
||||
});
|
||||
</script>
|
|
@ -37,10 +37,6 @@
|
|||
{{ instance.softwareVersion }}
|
||||
</p>
|
||||
</div>
|
||||
<MkMiniChart
|
||||
class="chart"
|
||||
:src="charts[i].requests.received"
|
||||
/>
|
||||
</div>
|
||||
</transition-group>
|
||||
</div>
|
||||
|
@ -57,7 +53,6 @@ import {
|
|||
} from "./widget";
|
||||
import type { GetFormResultType } from "@/scripts/form";
|
||||
import MkContainer from "@/components/MkContainer.vue";
|
||||
import MkMiniChart from "@/components/MkMiniChart.vue";
|
||||
import * as os from "@/os";
|
||||
import { useInterval } from "@/scripts/use-interval";
|
||||
import { i18n } from "@/i18n";
|
||||
|
@ -92,7 +87,6 @@ const { widgetProps, configure } = useWidgetPropsManager(
|
|||
);
|
||||
|
||||
const instances = ref([]);
|
||||
const charts = ref([]);
|
||||
const fetching = ref(true);
|
||||
|
||||
const fetch = async () => {
|
||||
|
@ -100,17 +94,7 @@ const fetch = async () => {
|
|||
sort: "+lastCommunicatedAt",
|
||||
limit: 5,
|
||||
});
|
||||
const fetchedCharts = await Promise.all(
|
||||
fetchedInstances.map((i) =>
|
||||
os.apiGet("charts/instance", {
|
||||
host: i.host,
|
||||
limit: 16,
|
||||
span: "hour",
|
||||
}),
|
||||
),
|
||||
);
|
||||
instances.value = fetchedInstances;
|
||||
charts.value = fetchedCharts;
|
||||
fetching.value = false;
|
||||
};
|
||||
|
||||
|
|
|
@ -34,10 +34,6 @@ export default function (app: App) {
|
|||
"MkwClock",
|
||||
defineAsyncComponent(() => import("./clock.vue")),
|
||||
);
|
||||
app.component(
|
||||
"MkwActivity",
|
||||
defineAsyncComponent(() => import("./activity.vue")),
|
||||
);
|
||||
app.component(
|
||||
"MkwPhotos",
|
||||
defineAsyncComponent(() => import("./photos.vue")),
|
||||
|
@ -110,7 +106,6 @@ export const widgets = [
|
|||
"rssTicker",
|
||||
"trends",
|
||||
"clock",
|
||||
"activity",
|
||||
"photos",
|
||||
"digitalClock",
|
||||
"unixClock",
|
||||
|
|
Loading…
Reference in a new issue