113 lines
3.2 KiB
TypeScript
113 lines
3.2 KiB
TypeScript
export type HomeTimeBucket = 'late-night' | 'dawn' | 'morning' | 'afternoon' | 'evening' | 'night'
|
|
export type PlaytimeView = 'month' | 'year'
|
|
|
|
export type HeatmapDay = {
|
|
date: Date
|
|
dateKey: string
|
|
inPeriod: boolean
|
|
}
|
|
|
|
export type MinecraftAccountLike = {
|
|
account_type?: string
|
|
profile?: {
|
|
id?: string
|
|
name?: string
|
|
}
|
|
}
|
|
|
|
export function getTimeBucket(date: Date): HomeTimeBucket {
|
|
const hour = date.getHours()
|
|
if (hour < 5) return 'late-night'
|
|
if (hour < 8) return 'dawn'
|
|
if (hour < 12) return 'morning'
|
|
if (hour < 17) return 'afternoon'
|
|
if (hour < 21) return 'evening'
|
|
return 'night'
|
|
}
|
|
|
|
export function stableGreetingIndex(seed: string, count: number): number {
|
|
if (count <= 0) return 0
|
|
|
|
let hash = 0
|
|
for (const character of seed) {
|
|
hash = (hash * 31 + character.charCodeAt(0)) | 0
|
|
}
|
|
return Math.abs(hash) % count
|
|
}
|
|
|
|
export function toDateKey(date: Date): string {
|
|
const year = date.getFullYear()
|
|
const month = String(date.getMonth() + 1).padStart(2, '0')
|
|
const day = String(date.getDate()).padStart(2, '0')
|
|
return `${year}-${month}-${day}`
|
|
}
|
|
|
|
export function dateFromKey(dateKey: string): Date {
|
|
const [year, month, day] = dateKey.split('-').map(Number)
|
|
return new Date(year, month - 1, day, 12)
|
|
}
|
|
|
|
export function startOfPeriod(anchor: Date, view: PlaytimeView): Date {
|
|
return view === 'month'
|
|
? new Date(anchor.getFullYear(), anchor.getMonth(), 1, 12)
|
|
: new Date(anchor.getFullYear(), 0, 1, 12)
|
|
}
|
|
|
|
export function endOfPeriod(anchor: Date, view: PlaytimeView): Date {
|
|
return view === 'month'
|
|
? new Date(anchor.getFullYear(), anchor.getMonth() + 1, 0, 12)
|
|
: new Date(anchor.getFullYear(), 11, 31, 12)
|
|
}
|
|
|
|
export function shiftPeriod(anchor: Date, view: PlaytimeView, amount: number): Date {
|
|
return view === 'month'
|
|
? new Date(anchor.getFullYear(), anchor.getMonth() + amount, 1, 12)
|
|
: new Date(anchor.getFullYear() + amount, 0, 1, 12)
|
|
}
|
|
|
|
export function buildHeatmapDays(anchor: Date, view: PlaytimeView): HeatmapDay[] {
|
|
const periodStart = startOfPeriod(anchor, view)
|
|
const periodEnd = endOfPeriod(anchor, view)
|
|
const periodStartKey = toDateKey(periodStart)
|
|
const periodEndKey = toDateKey(periodEnd)
|
|
const gridStart = new Date(periodStart)
|
|
gridStart.setDate(periodStart.getDate() - ((periodStart.getDay() + 6) % 7))
|
|
const gridEnd = new Date(periodEnd)
|
|
gridEnd.setDate(periodEnd.getDate() + ((7 - gridEnd.getDay()) % 7))
|
|
|
|
const days: HeatmapDay[] = []
|
|
const cursor = new Date(gridStart)
|
|
while (cursor <= gridEnd) {
|
|
const date = new Date(cursor)
|
|
const dateKey = toDateKey(date)
|
|
days.push({
|
|
date,
|
|
dateKey,
|
|
inPeriod: dateKey >= periodStartKey && dateKey <= periodEndKey,
|
|
})
|
|
cursor.setDate(cursor.getDate() + 1)
|
|
}
|
|
return days
|
|
}
|
|
|
|
export function getPlaytimeLevel(seconds: number): number {
|
|
if (seconds <= 0) return 0
|
|
if (seconds <= 30 * 60) return 1
|
|
if (seconds <= 90 * 60) return 2
|
|
if (seconds <= 180 * 60) return 3
|
|
return 4
|
|
}
|
|
|
|
export function getActivePlayerName(
|
|
selectedUser: string | null | undefined,
|
|
accounts: readonly MinecraftAccountLike[],
|
|
): string | null {
|
|
if (!selectedUser) return null
|
|
const account = accounts.find(
|
|
(candidate) =>
|
|
candidate.profile?.id === selectedUser &&
|
|
(candidate.account_type === 'microsoft' || candidate.account_type === 'yggdrasil'),
|
|
)
|
|
return account?.profile?.name ?? null
|
|
}
|