summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Nedbal <github-bf215181b5140522137b3d4f6b73544a@desu.email>2022-05-14 14:20:41 +0200
committerGitHub <noreply@github.com>2022-05-14 21:20:41 +0900
commit88307327e6e9999f4e8e2be48749b5c91c1a10dd (patch)
tree17c6554fcbf96ae35f226c16223e4a3f6ce7454c
parentUpdate extensions.json (diff)
downloadsharkey-88307327e6e9999f4e8e2be48749b5c91c1a10dd.tar.gz
sharkey-88307327e6e9999f4e8e2be48749b5c91c1a10dd.tar.bz2
sharkey-88307327e6e9999f4e8e2be48749b5c91c1a10dd.zip
Refactor Chart component (#8622)
* refactor(client): refactor Chart component * Apply review suggestions from @Johann150 Co-authored-by: Johann150 <johann@qwertqwefsday.eu> * fix(client): don't expose values from Chart Co-authored-by: Johann150 <johann@qwertqwefsday.eu>
-rw-r--r--packages/client/src/components/chart.vue1536
1 files changed, 766 insertions, 770 deletions
diff --git a/packages/client/src/components/chart.vue b/packages/client/src/components/chart.vue
index a7d5206c71..4e9c4e587a 100644
--- a/packages/client/src/components/chart.vue
+++ b/packages/client/src/components/chart.vue
@@ -7,8 +7,13 @@
</div>
</template>
-<script lang="ts">
-import { defineComponent, onMounted, ref, watch, PropType, onUnmounted, shallowRef } from 'vue';
+<script lang="ts" setup>
+/* eslint-disable id-denylist --
+ Chart.js has a `data` attribute in most chart definitions, which triggers the
+ id-denylist violation when setting it. This is causing about 60+ lint issues.
+ As this is part of Chart.js's API it makes sense to disable the check here.
+*/
+import { defineProps, onMounted, ref, watch, PropType, onUnmounted } from 'vue';
import {
Chart,
ArcElement,
@@ -36,6 +41,46 @@ import * as os from '@/os';
import { defaultStore } from '@/store';
import MkChartTooltip from '@/components/chart-tooltip.vue';
+const props = defineProps({
+ src: {
+ type: String,
+ required: true,
+ },
+ args: {
+ type: Object,
+ required: false,
+ },
+ limit: {
+ type: Number,
+ required: false,
+ default: 90
+ },
+ span: {
+ type: String as PropType<'hour' | 'day'>,
+ required: true,
+ },
+ detailed: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ stacked: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ bar: {
+ type: Boolean,
+ required: false,
+ default: false
+ },
+ aspectRatio: {
+ type: Number,
+ required: false,
+ default: null
+ },
+});
+
Chart.register(
ArcElement,
LineElement,
@@ -80,826 +125,777 @@ const getColor = (i) => {
return colorSets[i % colorSets.length];
};
-export default defineComponent({
- props: {
- src: {
- type: String,
- required: true,
- },
- args: {
- type: Object,
- required: false,
- },
- limit: {
- type: Number,
- required: false,
- default: 90
- },
- span: {
- type: String as PropType<'hour' | 'day'>,
- required: true,
- },
- detailed: {
- type: Boolean,
- required: false,
- default: false
- },
- stacked: {
- type: Boolean,
- required: false,
- default: false
- },
- bar: {
- type: Boolean,
- required: false,
- default: false
- },
- aspectRatio: {
- type: Number,
- required: false,
- default: null
- },
- },
+const now = new Date();
+let chartInstance: Chart = null;
+let chartData: {
+ series: {
+ name: string;
+ type: 'line' | 'area';
+ color?: string;
+ dashed?: boolean;
+ hidden?: boolean;
+ data: {
+ x: number;
+ y: number;
+ }[];
+ }[];
+} = null;
- setup(props) {
- const now = new Date();
- let chartInstance: Chart = null;
- let data: {
- series: {
- name: string;
- type: 'line' | 'area';
- color?: string;
- dashed?: boolean;
- hidden?: boolean;
- data: {
- x: number;
- y: number;
- }[];
- }[];
- } = null;
+const chartEl = ref<HTMLCanvasElement>(null);
+const fetching = ref(true);
- const chartEl = ref<HTMLCanvasElement>(null);
- const fetching = ref(true);
+const getDate = (ago: number) => {
+ const y = now.getFullYear();
+ const m = now.getMonth();
+ const d = now.getDate();
+ const h = now.getHours();
- const getDate = (ago: number) => {
- const y = now.getFullYear();
- const m = now.getMonth();
- const d = now.getDate();
- const h = now.getHours();
-
- return props.span === 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago);
- };
+ return props.span === 'day' ? new Date(y, m, d - ago) : new Date(y, m, d, h - ago);
+};
- const format = (arr) => {
- return arr.map((v, i) => ({
- x: getDate(i).getTime(),
- y: v
- }));
- };
+const format = (arr) => {
+ return arr.map((v, i) => ({
+ x: getDate(i).getTime(),
+ y: v
+ }));
+};
- const tooltipShowing = ref(false);
- const tooltipX = ref(0);
- const tooltipY = ref(0);
- const tooltipTitle = ref(null);
- const tooltipSeries = ref(null);
- let disposeTooltipComponent;
+const tooltipShowing = ref(false);
+const tooltipX = ref(0);
+const tooltipY = ref(0);
+const tooltipTitle = ref(null);
+const tooltipSeries = ref(null);
+let disposeTooltipComponent;
- os.popup(MkChartTooltip, {
- showing: tooltipShowing,
- x: tooltipX,
- y: tooltipY,
- title: tooltipTitle,
- series: tooltipSeries,
- }, {}).then(({ dispose }) => {
- disposeTooltipComponent = dispose;
- });
+os.popup(MkChartTooltip, {
+ showing: tooltipShowing,
+ x: tooltipX,
+ y: tooltipY,
+ title: tooltipTitle,
+ series: tooltipSeries,
+}, {}).then(({ dispose }) => {
+ disposeTooltipComponent = dispose;
+});
- function externalTooltipHandler(context) {
- if (context.tooltip.opacity === 0) {
- tooltipShowing.value = false;
- return;
- }
+function externalTooltipHandler(context) {
+ if (context.tooltip.opacity === 0) {
+ tooltipShowing.value = false;
+ return;
+ }
- tooltipTitle.value = context.tooltip.title[0];
- tooltipSeries.value = context.tooltip.body.map((b, i) => ({
- backgroundColor: context.tooltip.labelColors[i].backgroundColor,
- borderColor: context.tooltip.labelColors[i].borderColor,
- text: b.lines[0],
- }));
+ tooltipTitle.value = context.tooltip.title[0];
+ tooltipSeries.value = context.tooltip.body.map((b, i) => ({
+ backgroundColor: context.tooltip.labelColors[i].backgroundColor,
+ borderColor: context.tooltip.labelColors[i].borderColor,
+ text: b.lines[0],
+ }));
- const rect = context.chart.canvas.getBoundingClientRect();
+ const rect = context.chart.canvas.getBoundingClientRect();
- tooltipShowing.value = true;
- tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
- tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
- }
+ tooltipShowing.value = true;
+ tooltipX.value = rect.left + window.pageXOffset + context.tooltip.caretX;
+ tooltipY.value = rect.top + window.pageYOffset + context.tooltip.caretY;
+}
- const render = () => {
- if (chartInstance) {
- chartInstance.destroy();
- }
+const render = () => {
+ if (chartInstance) {
+ chartInstance.destroy();
+ }
- const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
- const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
+ const gridColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)';
+ const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
- // フォントカラー
- Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
+ // フォントカラー
+ Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--fg');
- const maxes = data.series.map((x, i) => Math.max(...x.data.map(d => d.y)));
+ const maxes = chartData.series.map((x, i) => Math.max(...x.data.map(d => d.y)));
- chartInstance = new Chart(chartEl.value, {
- type: props.bar ? 'bar' : 'line',
- data: {
- labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
- datasets: data.series.map((x, i) => ({
- parsing: false,
- label: x.name,
- data: x.data.slice().reverse(),
- tension: 0.3,
- pointRadius: 0,
- borderWidth: props.bar ? 0 : 2,
- borderColor: x.color ? x.color : getColor(i),
- borderDash: x.dashed ? [5, 5] : [],
- borderJoinStyle: 'round',
- borderRadius: props.bar ? 3 : undefined,
- backgroundColor: props.bar ? (x.color ? x.color : getColor(i)) : alpha(x.color ? x.color : getColor(i), 0.1),
- /*gradient: props.bar ? undefined : {
- backgroundColor: {
- axis: 'y',
- colors: {
- 0: alpha(x.color ? x.color : getColor(i), 0),
- [maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.2),
- },
- },
- },*/
- barPercentage: 0.9,
- categoryPercentage: 0.9,
- fill: x.type === 'area',
- clip: 8,
- hidden: !!x.hidden,
- })),
- },
- options: {
- aspectRatio: props.aspectRatio || 2.5,
- layout: {
- padding: {
- left: 0,
- right: 8,
- top: 0,
- bottom: 0,
+ chartInstance = new Chart(chartEl.value, {
+ type: props.bar ? 'bar' : 'line',
+ data: {
+ labels: new Array(props.limit).fill(0).map((_, i) => getDate(i).toLocaleString()).slice().reverse(),
+ datasets: chartData.series.map((x, i) => ({
+ parsing: false,
+ label: x.name,
+ data: x.data.slice().reverse(),
+ tension: 0.3,
+ pointRadius: 0,
+ borderWidth: props.bar ? 0 : 2,
+ borderColor: x.color ? x.color : getColor(i),
+ borderDash: x.dashed ? [5, 5] : [],
+ borderJoinStyle: 'round',
+ borderRadius: props.bar ? 3 : undefined,
+ backgroundColor: props.bar ? (x.color ? x.color : getColor(i)) : alpha(x.color ? x.color : getColor(i), 0.1),
+ /*gradient: props.bar ? undefined : {
+ backgroundColor: {
+ axis: 'y',
+ colors: {
+ 0: alpha(x.color ? x.color : getColor(i), 0),
+ [maxes[i]]: alpha(x.color ? x.color : getColor(i), 0.2),
},
},
- scales: {
- x: {
- type: 'time',
- stacked: props.stacked,
- offset: false,
- time: {
- stepSize: 1,
- unit: props.span === 'day' ? 'month' : 'day',
- },
- grid: {
- color: gridColor,
- borderColor: 'rgb(0, 0, 0, 0)',
- },
- ticks: {
- display: props.detailed,
- maxRotation: 0,
- autoSkipPadding: 16,
- },
- adapters: {
- date: {
- locale: enUS,
- },
- },
- min: getDate(props.limit).getTime(),
- },
- y: {
- position: 'left',
- stacked: props.stacked,
- suggestedMax: 50,
- grid: {
- color: gridColor,
- borderColor: 'rgb(0, 0, 0, 0)',
- },
- ticks: {
- display: props.detailed,
- //mirror: true,
- },
- },
+ },*/
+ barPercentage: 0.9,
+ categoryPercentage: 0.9,
+ fill: x.type === 'area',
+ clip: 8,
+ hidden: !!x.hidden,
+ })),
+ },
+ options: {
+ aspectRatio: props.aspectRatio || 2.5,
+ layout: {
+ padding: {
+ left: 0,
+ right: 8,
+ top: 0,
+ bottom: 0,
+ },
+ },
+ scales: {
+ x: {
+ type: 'time',
+ stacked: props.stacked,
+ offset: false,
+ time: {
+ stepSize: 1,
+ unit: props.span === 'day' ? 'month' : 'day',
},
- interaction: {
- intersect: false,
- mode: 'index',
+ grid: {
+ color: gridColor,
+ borderColor: 'rgb(0, 0, 0, 0)',
},
- elements: {
- point: {
- hoverRadius: 5,
- hoverBorderWidth: 2,
+ ticks: {
+ display: props.detailed,
+ maxRotation: 0,
+ autoSkipPadding: 16,
+ },
+ adapters: {
+ date: {
+ locale: enUS,
},
},
- animation: false,
- plugins: {
- legend: {
- display: props.detailed,
- position: 'bottom',
- labels: {
- boxWidth: 16,
- },
+ min: getDate(props.limit).getTime(),
+ },
+ y: {
+ position: 'left',
+ stacked: props.stacked,
+ suggestedMax: 50,
+ grid: {
+ color: gridColor,
+ borderColor: 'rgb(0, 0, 0, 0)',
+ },
+ ticks: {
+ display: props.detailed,
+ //mirror: true,
+ },
+ },
+ },
+ interaction: {
+ intersect: false,
+ mode: 'index',
+ },
+ elements: {
+ point: {
+ hoverRadius: 5,
+ hoverBorderWidth: 2,
+ },
+ },
+ animation: false,
+ plugins: {
+ legend: {
+ display: props.detailed,
+ position: 'bottom',
+ labels: {
+ boxWidth: 16,
+ },
+ },
+ tooltip: {
+ enabled: false,
+ mode: 'index',
+ animation: {
+ duration: 0,
+ },
+ external: externalTooltipHandler,
+ },
+ zoom: props.detailed ? {
+ pan: {
+ enabled: true,
+ },
+ zoom: {
+ wheel: {
+ enabled: true,
},
- tooltip: {
+ pinch: {
+ enabled: true,
+ },
+ drag: {
enabled: false,
- mode: 'index',
- animation: {
- duration: 0,
- },
- external: externalTooltipHandler,
},
- zoom: props.detailed ? {
- pan: {
- enabled: true,
- },
- zoom: {
- wheel: {
- enabled: true,
- },
- pinch: {
- enabled: true,
- },
- drag: {
- enabled: false,
- },
- mode: 'x',
- },
- limits: {
- x: {
- min: 'original',
- max: 'original',
- },
- y: {
- min: 'original',
- max: 'original',
- },
- }
- } : undefined,
- //gradient,
+ mode: 'x',
},
- },
- plugins: [{
- id: 'vLine',
- beforeDraw(chart, args, options) {
- if (chart.tooltip._active && chart.tooltip._active.length) {
- const activePoint = chart.tooltip._active[0];
- const ctx = chart.ctx;
- const x = activePoint.element.x;
- const topY = chart.scales.y.top;
- const bottomY = chart.scales.y.bottom;
-
- ctx.save();
- ctx.beginPath();
- ctx.moveTo(x, bottomY);
- ctx.lineTo(x, topY);
- ctx.lineWidth = 1;
- ctx.strokeStyle = vLineColor;
- ctx.stroke();
- ctx.restore();
- }
+ limits: {
+ x: {
+ min: 'original',
+ max: 'original',
+ },
+ y: {
+ min: 'original',
+ max: 'original',
+ },
}
- }]
- });
- };
+ } : undefined,
+ //gradient,
+ },
+ },
+ plugins: [{
+ id: 'vLine',
+ beforeDraw(chart, args, options) {
+ if (chart.tooltip._active && chart.tooltip._active.length) {
+ const activePoint = chart.tooltip._active[0];
+ const ctx = chart.ctx;
+ const x = activePoint.element.x;
+ const topY = chart.scales.y.top;
+ const bottomY = chart.scales.y.bottom;
- const exportData = () => {
- // TODO
- };
+ ctx.save();
+ ctx.beginPath();
+ ctx.moveTo(x, bottomY);
+ ctx.lineTo(x, topY);
+ ctx.lineWidth = 1;
+ ctx.strokeStyle = vLineColor;
+ ctx.stroke();
+ ctx.restore();
+ }
+ }
+ }]
+ });
+};
- const fetchFederationChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 exportData = () => {
+ // TODO
+};
- const fetchApRequestChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'In',
- type: 'area',
- color: '#008FFB',
- data: format(raw.inboxReceived)
- }, {
- name: 'Out (succ)',
- type: 'area',
- color: '#00E396',
- data: format(raw.deliverSucceeded)
- }, {
- name: 'Out (fail)',
- type: 'area',
- color: '#FEB019',
- data: format(raw.deliverFailed)
- }]
- };
- };
+const fetchFederationChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchNotesChart = async (type: string): Promise<typeof data> => {
- const raw = await os.api('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 fetchApRequestChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/ap-request', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'In',
+ type: 'area',
+ color: '#008FFB',
+ data: format(raw.inboxReceived)
+ }, {
+ name: 'Out (succ)',
+ type: 'area',
+ color: '#00E396',
+ data: format(raw.deliverSucceeded)
+ }, {
+ name: 'Out (fail)',
+ type: 'area',
+ color: '#FEB019',
+ data: format(raw.deliverFailed)
+ }]
+ };
+};
- const fetchNotesTotalChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 fetchNotesChart = async (type: string): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchUsersChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('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 fetchNotesTotalChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchActiveUsersChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Read & Write',
- type: 'area',
- data: format(raw.readWrite),
- color: colors.orange,
- }, {
- name: 'Write',
- type: 'area',
- data: format(raw.write),
- color: colors.lime,
- }, {
- name: 'Read',
- type: 'area',
- data: format(raw.read),
- color: colors.blue,
- }, {
- name: '< Week',
- type: 'area',
- data: format(raw.registeredWithinWeek),
- color: colors.green,
- }, {
- name: '< Month',
- type: 'area',
- data: format(raw.registeredWithinMonth),
- color: colors.yellow,
- }, {
- name: '< Year',
- type: 'area',
- data: format(raw.registeredWithinYear),
- color: colors.red,
- }, {
- name: '> Week',
- type: 'area',
- data: format(raw.registeredOutsideWeek),
- color: colors.yellow,
- }, {
- name: '> Month',
- type: 'area',
- data: format(raw.registeredOutsideMonth),
- color: colors.red,
- }, {
- name: '> Year',
- type: 'area',
- data: format(raw.registeredOutsideYear),
- color: colors.purple,
- }],
- };
- };
+const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchDriveChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 fetchActiveUsersChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/active-users', { limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Read & Write',
+ type: 'area',
+ data: format(raw.readWrite),
+ color: colors.orange,
+ }, {
+ name: 'Write',
+ type: 'area',
+ data: format(raw.write),
+ color: colors.lime,
+ }, {
+ name: 'Read',
+ type: 'area',
+ data: format(raw.read),
+ color: colors.blue,
+ }, {
+ name: '< Week',
+ type: 'area',
+ data: format(raw.registeredWithinWeek),
+ color: colors.green,
+ }, {
+ name: '< Month',
+ type: 'area',
+ data: format(raw.registeredWithinMonth),
+ color: colors.yellow,
+ }, {
+ name: '< Year',
+ type: 'area',
+ data: format(raw.registeredWithinYear),
+ color: colors.red,
+ }, {
+ name: '> Week',
+ type: 'area',
+ data: format(raw.registeredOutsideWeek),
+ color: colors.yellow,
+ }, {
+ name: '> Month',
+ type: 'area',
+ data: format(raw.registeredOutsideMonth),
+ color: colors.red,
+ }, {
+ name: '> Year',
+ type: 'area',
+ data: format(raw.registeredOutsideYear),
+ color: colors.purple,
+ }],
+ };
+};
- const fetchDriveFilesChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 fetchDriveChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchInstanceRequestsChart = async (): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'In',
- type: 'area',
- color: '#008FFB',
- data: format(raw.requests.received)
- }, {
- name: 'Out (succ)',
- type: 'area',
- color: '#00E396',
- data: format(raw.requests.succeeded)
- }, {
- name: 'Out (fail)',
- type: 'area',
- color: '#FEB019',
- data: format(raw.requests.failed)
- }]
- };
- };
+const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchInstanceUsersChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Users',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.users.total
- : sum(raw.users.inc, negate(raw.users.dec))
- )
- }]
- };
- };
+const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'In',
+ type: 'area',
+ color: '#008FFB',
+ data: format(raw.requests.received)
+ }, {
+ name: 'Out (succ)',
+ type: 'area',
+ color: '#00E396',
+ data: format(raw.requests.succeeded)
+ }, {
+ name: 'Out (fail)',
+ type: 'area',
+ color: '#FEB019',
+ data: format(raw.requests.failed)
+ }]
+ };
+};
- const fetchInstanceNotesChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Notes',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.notes.total
- : sum(raw.notes.inc, negate(raw.notes.dec))
- )
- }]
- };
- };
+const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Users',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.users.total
+ : sum(raw.users.inc, negate(raw.users.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceFfChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Following',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.following.total
- : sum(raw.following.inc, negate(raw.following.dec))
- )
- }, {
- name: 'Followers',
- type: 'area',
- color: '#00E396',
- data: format(total
- ? raw.followers.total
- : sum(raw.followers.inc, negate(raw.followers.dec))
- )
- }]
- };
- };
+const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Notes',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.notes.total
+ : sum(raw.notes.inc, negate(raw.notes.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- bytes: true,
- series: [{
- name: 'Drive usage',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.drive.totalUsage
- : sum(raw.drive.incUsage, negate(raw.drive.decUsage))
- )
- }]
- };
- };
+const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Following',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.following.total
+ : sum(raw.following.inc, negate(raw.following.dec))
+ )
+ }, {
+ name: 'Followers',
+ type: 'area',
+ color: '#00E396',
+ data: format(total
+ ? raw.followers.total
+ : sum(raw.followers.inc, negate(raw.followers.dec))
+ )
+ }]
+ };
+};
- const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof data> => {
- const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
- return {
- series: [{
- name: 'Drive files',
- type: 'area',
- color: '#008FFB',
- data: format(total
- ? raw.drive.totalFiles
- : sum(raw.drive.incFiles, negate(raw.drive.decFiles))
- )
- }]
- };
- };
+const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ bytes: true,
+ series: [{
+ name: 'Drive usage',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.drive.totalUsage
+ : sum(raw.drive.incUsage, negate(raw.drive.decUsage))
+ )
+ }]
+ };
+};
- const fetchPerUserNotesChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
+ const raw = await os.api('charts/instance', { host: props.args.host, limit: props.limit, span: props.span });
+ return {
+ series: [{
+ name: 'Drive files',
+ type: 'area',
+ color: '#008FFB',
+ data: format(total
+ ? raw.drive.totalFiles
+ : sum(raw.drive.incFiles, negate(raw.drive.decFiles))
+ )
+ }]
+ };
+};
- const fetchPerUserFollowingChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchPerUserFollowersChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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 fetchPerUserDriveChart = async (): Promise<typeof data> => {
- const raw = await os.api('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 fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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 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);
+const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
+ const raw = await os.api('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),
+ }],
+ };
+};
- case 'per-user-notes': return fetchPerUserNotesChart();
- case 'per-user-following': return fetchPerUserFollowingChart();
- case 'per-user-followers': return fetchPerUserFollowersChart();
- case 'per-user-drive': return fetchPerUserDriveChart();
- }
- };
- fetching.value = true;
- data = await fetchData();
- fetching.value = false;
- render();
- };
+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);
- watch(() => [props.src, props.span], fetchAndRender);
+ case 'per-user-notes': return fetchPerUserNotesChart();
+ case 'per-user-following': return fetchPerUserFollowingChart();
+ case 'per-user-followers': return fetchPerUserFollowersChart();
+ case 'per-user-drive': return fetchPerUserDriveChart();
+ }
+ };
+ fetching.value = true;
+ chartData = await fetchData();
+ fetching.value = false;
+ render();
+};
- onMounted(() => {
- fetchAndRender();
- });
+watch(() => [props.src, props.span], fetchAndRender);
- onUnmounted(() => {
- if (disposeTooltipComponent) disposeTooltipComponent();
- });
+onMounted(() => {
+ fetchAndRender();
+});
- return {
- chartEl,
- fetching,
- };
- },
+onUnmounted(() => {
+ if (disposeTooltipComponent) disposeTooltipComponent();
});
+/* eslint-enable id-denylist */
</script>
<style lang="scss" scoped>