summaryrefslogtreecommitdiff
path: root/packages/frontend/src/utility/timeline-date-separate.ts
blob: 33ddea048b1b7011be994636a918858c1396ee39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
 * SPDX-FileCopyrightText: syuilo and misskey-project
 * SPDX-License-Identifier: AGPL-3.0-only
 */

import { computed } from 'vue';
import type { Ref, ShallowRef } from 'vue';

export function getDateText(dateInstance: Date) {
	const date = dateInstance.getDate();
	const month = dateInstance.getMonth() + 1;
	return `${month.toString()}/${date.toString()}`;
}

// TODO: いちいちDateインスタンス作成するのは無駄感あるから文字列のまま解析したい
export function isSeparatorNeeded(
	prev: string | null,
	next: string | null,
) {
	if (prev == null || next == null) return false;
	const prevDate = new Date(prev);
	const nextDate = new Date(next);
	return (
		prevDate.getFullYear() !== nextDate.getFullYear() ||
		prevDate.getMonth() !== nextDate.getMonth() ||
		prevDate.getDate() !== nextDate.getDate()
	);
}

// TODO: いちいちDateインスタンス作成するのは無駄感あるから文字列のまま解析したい
export function getSeparatorInfo(
	prev: string | null,
	next: string | null,
) {
	if (prev == null || next == null) return null;
	const prevDate = new Date(prev);
	const nextDate = new Date(next);
	return {
		prevDate,
		prevText: getDateText(prevDate),
		nextDate,
		nextText: getDateText(nextDate),
	};
}

export type DateSeparetedTimelineItem<T> = {
	id: string;
	type: 'item';
	data: T;
} | {
	id: string;
	type: 'date';
	prev: Date;
	prevText: string;
	next: Date;
	nextText: string;
};

export function makeDateSeparatedTimelineComputedRef<T extends { id: string; createdAt: string; }>(items: Ref<T[]> | ShallowRef<T[]>) {
	return computed<DateSeparetedTimelineItem<T>[]>(() => {
		const tl: DateSeparetedTimelineItem<T>[] = [];
		for (let i = 0; i < items.value.length; i++) {
			const item = items.value[i];

			const date = new Date(item.createdAt);
			const nextDate = items.value[i + 1] ? new Date(items.value[i + 1].createdAt) : null;

			tl.push({
				id: item.id,
				type: 'item',
				data: item,
			});

			if (
				i !== items.value.length - 1 &&
					nextDate != null && (
					date.getFullYear() !== nextDate.getFullYear() ||
						date.getMonth() !== nextDate.getMonth() ||
						date.getDate() !== nextDate.getDate()
				)
			) {
				tl.push({
					id: `date-${item.id}`,
					type: 'date',
					prev: date,
					prevText: getDateText(date),
					next: nextDate,
					nextText: getDateText(nextDate),
				});
			}
		}
		return tl;
	});
}

export type DateGroupedTimelineItem<T> = {
	date: Date;
	items: T[];
};

export function makeDateGroupedTimelineComputedRef<T extends { id: string; createdAt: string; }>(items: Ref<T[]> | ShallowRef<T[]>, span: 'day' | 'month' = 'day') {
	return computed<DateGroupedTimelineItem<T>[]>(() => {
		const tl: DateGroupedTimelineItem<T>[] = [];
		for (let i = 0; i < items.value.length; i++) {
			const item = items.value[i];
			const date = new Date(item.createdAt);
			const nextDate = items.value[i + 1] ? new Date(items.value[i + 1].createdAt) : null;

			if (tl.length === 0 || (
				span === 'day' && tl[tl.length - 1].date.getTime() !== date.getTime()
			) || (
				span === 'month' && (
					tl[tl.length - 1].date.getFullYear() !== date.getFullYear() ||
					tl[tl.length - 1].date.getMonth() !== date.getMonth()
				)
			)) {
				tl.push({
					date,
					items: [],
				});
			}
			tl[tl.length - 1].items.push(item);
		}
		return tl;
	});
}