summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzyoshoka <107108195+zyoshoka@users.noreply.github.com>2024-01-12 15:29:06 +0900
committerGitHub <noreply@github.com>2024-01-12 15:29:06 +0900
commit1aeede97f5d56ee4d98a2069de6d4aab7979a6e3 (patch)
tree590733b31d2f196b30106d0e12a30c94475b1363
parentenhance(drop-and-fusion): add new mode, some tweaks (diff)
downloadmisskey-1aeede97f5d56ee4d98a2069de6d4aab7979a6e3.tar.gz
misskey-1aeede97f5d56ee4d98a2069de6d4aab7979a6e3.tar.bz2
misskey-1aeede97f5d56ee4d98a2069de6d4aab7979a6e3.zip
refactor(frontend): `activity.heatmap.vue`をコンポーネントに置換 (#12967)
-rw-r--r--packages/frontend/src/components/MkHeatmap.vue30
-rw-r--r--packages/frontend/src/components/MkInstanceStats.vue6
-rw-r--r--packages/frontend/src/pages/user/activity.heatmap.vue219
-rw-r--r--packages/frontend/src/pages/user/activity.vue4
4 files changed, 28 insertions, 231 deletions
diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue
index f47b680f83..a77f3627f9 100644
--- a/packages/frontend/src/components/MkHeatmap.vue
+++ b/packages/frontend/src/components/MkHeatmap.vue
@@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
import { Chart } from 'chart.js';
+import * as Misskey from 'misskey-js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { defaultStore } from '@/store.js';
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
@@ -23,9 +24,16 @@ import { initChart } from '@/scripts/init-chart.js';
initChart();
-const props = defineProps<{
- src: string;
-}>();
+export type HeatmapSource = 'active-users' | 'notes' | 'ap-requests-inbox-received' | 'ap-requests-deliver-succeeded' | 'ap-requests-deliver-failed';
+
+const props = withDefaults(defineProps<{
+ src: HeatmapSource;
+ user?: Misskey.entities.User;
+ label?: string;
+}>(), {
+ user: undefined,
+ label: '',
+});
const rootEl = shallowRef<HTMLDivElement>(null);
const chartEl = shallowRef<HTMLCanvasElement>(null);
@@ -75,8 +83,13 @@ async function renderChart() {
const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
values = raw.readWrite;
} else if (props.src === 'notes') {
- const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' });
- values = raw.local.inc;
+ if (props.user) {
+ const raw = await misskeyApi('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' });
+ values = raw.inc;
+ } else {
+ const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' });
+ values = raw.local.inc;
+ }
} else if (props.src === 'ap-requests-inbox-received') {
const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
values = raw.inboxReceived;
@@ -105,7 +118,7 @@ async function renderChart() {
type: 'matrix',
data: {
datasets: [{
- label: 'Read & Write',
+ label: props.label,
data: format(values),
pointRadius: 0,
borderWidth: 0,
@@ -128,6 +141,9 @@ async function renderChart() {
const a = c.chart.chartArea ?? {};
return (a.bottom - a.top) / 7 - marginEachCell;
},
+ /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
+ }] satisfies ChartData[],
+ */
}],
},
options: {
@@ -195,7 +211,7 @@ async function renderChart() {
},
label(context) {
const v = context.dataset.data[context.dataIndex];
- return ['Active: ' + v.v];
+ return [v.v];
},
},
//mode: 'index',
diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue
index 1576089657..00f5e96286 100644
--- a/packages/frontend/src/components/MkInstanceStats.vue
+++ b/packages/frontend/src/components/MkInstanceStats.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option>
</MkSelect>
<div class="_panel" :class="$style.heatmap">
- <MkHeatmap :src="heatmapSrc"/>
+ <MkHeatmap :src="heatmapSrc" :label="'Read & Write'"/>
</div>
</MkFoldableSection>
@@ -92,7 +92,7 @@ import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
-import MkHeatmap from '@/components/MkHeatmap.vue';
+import MkHeatmap, { type HeatmapSource } from '@/components/MkHeatmap.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue';
import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue';
@@ -103,7 +103,7 @@ initChart();
const chartLimit = 500;
const chartSpan = ref<'hour' | 'day'>('hour');
const chartSrc = ref('active-users');
-const heatmapSrc = ref('active-users');
+const heatmapSrc = ref<HeatmapSource>('active-users');
const subDoughnutEl = shallowRef<HTMLCanvasElement>();
const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue
deleted file mode 100644
index ea3276a890..0000000000
--- a/packages/frontend/src/pages/user/activity.heatmap.vue
+++ /dev/null
@@ -1,219 +0,0 @@
-<!--
-SPDX-FileCopyrightText: syuilo and other misskey contributors
-SPDX-License-Identifier: AGPL-3.0-only
--->
-
-<template>
-<div ref="rootEl">
- <MkLoading v-if="fetching"/>
- <div v-else :class="$style.root" class="_panel">
- <canvas ref="chartEl"></canvas>
- </div>
-</div>
-</template>
-
-<script lang="ts" setup>
-import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
-import { Chart } from 'chart.js';
-import * as Misskey from 'misskey-js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
-import { defaultStore } from '@/store.js';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
-import { alpha } from '@/scripts/color.js';
-import { initChart } from '@/scripts/init-chart.js';
-
-initChart();
-
-const props = defineProps<{
- src: string;
- user: Misskey.entities.User;
-}>();
-
-const rootEl = shallowRef<HTMLDivElement>(null);
-const chartEl = shallowRef<HTMLCanvasElement>(null);
-const now = new Date();
-let chartInstance: Chart = null;
-const fetching = ref(true);
-
-const { handler: externalTooltipHandler } = useChartTooltip({
- position: 'middle',
-});
-
-async function renderChart() {
- if (chartInstance) {
- chartInstance.destroy();
- }
-
- const wide = rootEl.value.offsetWidth > 700;
- const narrow = rootEl.value.offsetWidth < 400;
-
- const weeks = wide ? 50 : narrow ? 10 : 25;
- const chartLimit = 7 * weeks;
-
- 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) => {
- const dt = getDate(i);
- const iso = `${dt.getFullYear()}-${(dt.getMonth() + 1).toString().padStart(2, '0')}-${dt.getDate().toString().padStart(2, '0')}`;
- return {
- x: iso,
- y: dt.getDay(),
- d: iso,
- v,
- };
- });
- };
-
- let values;
-
- if (props.src === 'notes') {
- const raw = await misskeyApi('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' });
- values = raw.inc;
- }
-
- fetching.value = false;
-
- await nextTick();
-
- const color = defaultStore.state.darkMode ? '#b4e900' : '#86b300';
-
- // 視覚上の分かりやすさのため上から最も大きい3つの値の平均を最大値とする
- const max = values.slice().sort((a, b) => b - a).slice(0, 3).reduce((a, b) => a + b, 0) / 3;
-
- const min = Math.max(0, Math.min(...values) - 1);
-
- const marginEachCell = 4;
-
- chartInstance = new Chart(chartEl.value, {
- type: 'matrix',
- data: {
- datasets: [{
- label: '',
- data: format(values),
- pointRadius: 0,
- borderWidth: 0,
- borderJoinStyle: 'round',
- borderRadius: 3,
- backgroundColor(c) {
- const value = c.dataset.data[c.dataIndex].v;
- let a = (value - min) / max;
- if (value !== 0) { // 0でない限りは完全に不可視にはしない
- a = Math.max(a, 0.05);
- }
- return alpha(color, a);
- },
- fill: true,
- width(c) {
- const a = c.chart.chartArea ?? {};
- return (a.right - a.left) / weeks - marginEachCell;
- },
- height(c) {
- const a = c.chart.chartArea ?? {};
- return (a.bottom - a.top) / 7 - marginEachCell;
- },
- /* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
- }] satisfies ChartData[],
- */
- }],
- },
- options: {
- aspectRatio: wide ? 6 : narrow ? 1.8 : 3.2,
- layout: {
- padding: {
- left: 8,
- right: 0,
- top: 0,
- bottom: 0,
- },
- },
- scales: {
- x: {
- type: 'time',
- offset: true,
- position: 'bottom',
- time: {
- unit: 'week',
- round: 'week',
- isoWeekday: 0,
- displayFormats: {
- day: 'M/d',
- month: 'Y/M',
- week: 'M/d',
- },
- },
- grid: {
- display: false,
- },
- ticks: {
- display: true,
- maxRotation: 0,
- autoSkipPadding: 8,
- },
- },
- y: {
- offset: true,
- reverse: true,
- position: 'right',
- grid: {
- display: false,
- },
- ticks: {
- maxRotation: 0,
- autoSkip: true,
- padding: 1,
- font: {
- size: 9,
- },
- callback: (value, index, values) => ['', 'Mon', '', 'Wed', '', 'Fri', ''][value],
- },
- },
- },
- plugins: {
- legend: {
- display: false,
- },
- tooltip: {
- enabled: false,
- callbacks: {
- title(context) {
- const v = context[0].dataset.data[context[0].dataIndex];
- return v.d;
- },
- label(context) {
- const v = context.dataset.data[context.dataIndex];
- return [v.v];
- },
- },
- //mode: 'index',
- animation: {
- duration: 0,
- },
- external: externalTooltipHandler,
- },
- },
- },
- });
-}
-
-watch(() => props.src, () => {
- fetching.value = true;
- renderChart();
-});
-
-onMounted(async () => {
- renderChart();
-});
-</script>
-
-<style lang="scss" module>
-.root {
- padding: 20px;
-}
-</style>
diff --git a/packages/frontend/src/pages/user/activity.vue b/packages/frontend/src/pages/user/activity.vue
index 6703890893..3c7635a312 100644
--- a/packages/frontend/src/pages/user/activity.vue
+++ b/packages/frontend/src/pages/user/activity.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps">
<MkFoldableSection class="item">
<template #header><i class="ti ti-activity"></i> Heatmap</template>
- <XHeatmap :user="user" :src="'notes'"/>
+ <MkHeatmap :user="user" :src="'notes'"/>
</MkFoldableSection>
<MkFoldableSection class="item">
<template #header><i class="ti ti-pencil"></i> Notes</template>
@@ -28,11 +28,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
-import XHeatmap from './activity.heatmap.vue';
import XPv from './activity.pv.vue';
import XNotes from './activity.notes.vue';
import XFollowing from './activity.following.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
+import MkHeatmap from '@/components/MkHeatmap.vue';
const props = defineProps<{
user: Misskey.entities.User;