mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 15:34:13 +00:00 
			
		
		
		
	refactor(frontend): activity.heatmap.vueをコンポーネントに置換 (#12967)
				
					
				
			This commit is contained in:
		
							parent
							
								
									d2063df78d
								
							
						
					
					
						commit
						1aeede97f5
					
				
					 4 changed files with 28 additions and 231 deletions
				
			
		| 
						 | 
					@ -15,6 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
 | 
					import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
 | 
				
			||||||
import { Chart } from 'chart.js';
 | 
					import { Chart } from 'chart.js';
 | 
				
			||||||
 | 
					import * as Misskey from 'misskey-js';
 | 
				
			||||||
import { misskeyApi } from '@/scripts/misskey-api.js';
 | 
					import { misskeyApi } from '@/scripts/misskey-api.js';
 | 
				
			||||||
import { defaultStore } from '@/store.js';
 | 
					import { defaultStore } from '@/store.js';
 | 
				
			||||||
import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 | 
					import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 | 
				
			||||||
| 
						 | 
					@ -23,9 +24,16 @@ import { initChart } from '@/scripts/init-chart.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
initChart();
 | 
					initChart();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					export type HeatmapSource = 'active-users' | 'notes' | 'ap-requests-inbox-received' | 'ap-requests-deliver-succeeded' | 'ap-requests-deliver-failed';
 | 
				
			||||||
	src: string;
 | 
					
 | 
				
			||||||
}>();
 | 
					const props = withDefaults(defineProps<{
 | 
				
			||||||
 | 
						src: HeatmapSource;
 | 
				
			||||||
 | 
						user?: Misskey.entities.User;
 | 
				
			||||||
 | 
						label?: string;
 | 
				
			||||||
 | 
					}>(), {
 | 
				
			||||||
 | 
						user: undefined,
 | 
				
			||||||
 | 
						label: '',
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const rootEl = shallowRef<HTMLDivElement>(null);
 | 
					const rootEl = shallowRef<HTMLDivElement>(null);
 | 
				
			||||||
const chartEl = shallowRef<HTMLCanvasElement>(null);
 | 
					const chartEl = shallowRef<HTMLCanvasElement>(null);
 | 
				
			||||||
| 
						 | 
					@ -75,8 +83,13 @@ async function renderChart() {
 | 
				
			||||||
		const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
 | 
							const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
 | 
				
			||||||
		values = raw.readWrite;
 | 
							values = raw.readWrite;
 | 
				
			||||||
	} else if (props.src === 'notes') {
 | 
						} else if (props.src === 'notes') {
 | 
				
			||||||
		const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' });
 | 
							if (props.user) {
 | 
				
			||||||
		values = raw.local.inc;
 | 
								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') {
 | 
						} else if (props.src === 'ap-requests-inbox-received') {
 | 
				
			||||||
		const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
 | 
							const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
 | 
				
			||||||
		values = raw.inboxReceived;
 | 
							values = raw.inboxReceived;
 | 
				
			||||||
| 
						 | 
					@ -105,7 +118,7 @@ async function renderChart() {
 | 
				
			||||||
		type: 'matrix',
 | 
							type: 'matrix',
 | 
				
			||||||
		data: {
 | 
							data: {
 | 
				
			||||||
			datasets: [{
 | 
								datasets: [{
 | 
				
			||||||
				label: 'Read & Write',
 | 
									label: props.label,
 | 
				
			||||||
				data: format(values),
 | 
									data: format(values),
 | 
				
			||||||
				pointRadius: 0,
 | 
									pointRadius: 0,
 | 
				
			||||||
				borderWidth: 0,
 | 
									borderWidth: 0,
 | 
				
			||||||
| 
						 | 
					@ -128,6 +141,9 @@ async function renderChart() {
 | 
				
			||||||
					const a = c.chart.chartArea ?? {};
 | 
										const a = c.chart.chartArea ?? {};
 | 
				
			||||||
					return (a.bottom - a.top) / 7 - marginEachCell;
 | 
										return (a.bottom - a.top) / 7 - marginEachCell;
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
 | 
								/* @see <https://github.com/misskey-dev/misskey/pull/10365#discussion_r1155511107>
 | 
				
			||||||
 | 
								}] satisfies ChartData[],
 | 
				
			||||||
 | 
								 */
 | 
				
			||||||
			}],
 | 
								}],
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		options: {
 | 
							options: {
 | 
				
			||||||
| 
						 | 
					@ -195,7 +211,7 @@ async function renderChart() {
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
						label(context) {
 | 
											label(context) {
 | 
				
			||||||
							const v = context.dataset.data[context.dataIndex];
 | 
												const v = context.dataset.data[context.dataIndex];
 | 
				
			||||||
							return ['Active: ' + v.v];
 | 
												return [v.v];
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
					},
 | 
										},
 | 
				
			||||||
					//mode: 'index',
 | 
										//mode: 'index',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
			<option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option>
 | 
								<option value="ap-requests-deliver-failed">AP Requests: deliverFailed</option>
 | 
				
			||||||
		</MkSelect>
 | 
							</MkSelect>
 | 
				
			||||||
		<div class="_panel" :class="$style.heatmap">
 | 
							<div class="_panel" :class="$style.heatmap">
 | 
				
			||||||
			<MkHeatmap :src="heatmapSrc"/>
 | 
								<MkHeatmap :src="heatmapSrc" :label="'Read & Write'"/>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</MkFoldableSection>
 | 
						</MkFoldableSection>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -92,7 +92,7 @@ import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 | 
				
			||||||
import * as os from '@/os.js';
 | 
					import * as os from '@/os.js';
 | 
				
			||||||
import { misskeyApiGet } from '@/scripts/misskey-api.js';
 | 
					import { misskeyApiGet } from '@/scripts/misskey-api.js';
 | 
				
			||||||
import { i18n } from '@/i18n.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 MkFoldableSection from '@/components/MkFoldableSection.vue';
 | 
				
			||||||
import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue';
 | 
					import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue';
 | 
				
			||||||
import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue';
 | 
					import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue';
 | 
				
			||||||
| 
						 | 
					@ -103,7 +103,7 @@ initChart();
 | 
				
			||||||
const chartLimit = 500;
 | 
					const chartLimit = 500;
 | 
				
			||||||
const chartSpan = ref<'hour' | 'day'>('hour');
 | 
					const chartSpan = ref<'hour' | 'day'>('hour');
 | 
				
			||||||
const chartSrc = ref('active-users');
 | 
					const chartSrc = ref('active-users');
 | 
				
			||||||
const heatmapSrc = ref('active-users');
 | 
					const heatmapSrc = ref<HeatmapSource>('active-users');
 | 
				
			||||||
const subDoughnutEl = shallowRef<HTMLCanvasElement>();
 | 
					const subDoughnutEl = shallowRef<HTMLCanvasElement>();
 | 
				
			||||||
const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
 | 
					const pubDoughnutEl = shallowRef<HTMLCanvasElement>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
	<div class="_gaps">
 | 
						<div class="_gaps">
 | 
				
			||||||
		<MkFoldableSection class="item">
 | 
							<MkFoldableSection class="item">
 | 
				
			||||||
			<template #header><i class="ti ti-activity"></i> Heatmap</template>
 | 
								<template #header><i class="ti ti-activity"></i> Heatmap</template>
 | 
				
			||||||
			<XHeatmap :user="user" :src="'notes'"/>
 | 
								<MkHeatmap :user="user" :src="'notes'"/>
 | 
				
			||||||
		</MkFoldableSection>
 | 
							</MkFoldableSection>
 | 
				
			||||||
		<MkFoldableSection class="item">
 | 
							<MkFoldableSection class="item">
 | 
				
			||||||
			<template #header><i class="ti ti-pencil"></i> Notes</template>
 | 
								<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>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import * as Misskey from 'misskey-js';
 | 
					import * as Misskey from 'misskey-js';
 | 
				
			||||||
import XHeatmap from './activity.heatmap.vue';
 | 
					 | 
				
			||||||
import XPv from './activity.pv.vue';
 | 
					import XPv from './activity.pv.vue';
 | 
				
			||||||
import XNotes from './activity.notes.vue';
 | 
					import XNotes from './activity.notes.vue';
 | 
				
			||||||
import XFollowing from './activity.following.vue';
 | 
					import XFollowing from './activity.following.vue';
 | 
				
			||||||
import MkFoldableSection from '@/components/MkFoldableSection.vue';
 | 
					import MkFoldableSection from '@/components/MkFoldableSection.vue';
 | 
				
			||||||
 | 
					import MkHeatmap from '@/components/MkHeatmap.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const props = defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
	user: Misskey.entities.User;
 | 
						user: Misskey.entities.User;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue