Newer
Older
import { cloneDeep, isEqual } from 'lodash'
import { computed, ComputedGetter, ComputedRef, readonly, Ref, ref, watch, watchEffect } from 'vue'
import { formatISO, parseISO } from 'date-fns'
import DOMPurify from 'dompurify'
import { useStore } from 'vuex'
import { Show } from '@/types'
export function computedIter<T>(fn: ComputedGetter<Iterable<T>>): ComputedRef<T[]> {
return computed(() => Array.from(fn()))
}
export const useId = (() => {
let _id = 0
return function useId(prefix = 'component') {
return readonly(ref(`${prefix}-${_id++}`))
}
})()
export function useAsyncFunction<F extends (...args: never[]) => Promise<unknown>>(fn: F) {
const isProcessing = ref(false)
async function wrapper(...args: Parameters<F>): Promise<Awaited<ReturnType<F>>> {
return (await fn(...args)) as Awaited<ReturnType<F>>
return { fn: wrapper, isProcessing }
}
export function useUpdatableState<T>(
externalStateRef: Ref<T>,
onUpdate: (value: T) => void,
clone: (value: T) => T = cloneDeep,
): Ref<T> {
const localRef = ref()
watchEffect(() => {
localRef.value = clone(externalStateRef.value)
})
watch(
localRef,
(newValue) => {
if (!isEqual(newValue, externalStateRef.value)) {
onUpdate(newValue)
}
},
{ deep: true },
)
return localRef
}
export function useFormattedISODate(date: Ref<Date>) {
return computed({
get() {
return formatISO(date.value, { representation: 'date' })
},
set(dateValue: string) {
date.value = parseISO(dateValue)
},
})
}

Konrad Mohrfeldt
committed
export function getClosestSlot(slotDurationMinutes: number, date?: Date): Date {
date = date ?? new Date()
const slotDurationMillis = slotDurationMinutes * 60 * 1000
const slots = Math.floor(date.getTime() / slotDurationMillis)
const closestSlotTimestamp = slots * slotDurationMillis
return new Date(closestSlotTimestamp)
}
export function getNextAvailableSlot(slotDurationMinutes: number, date?: Date): Date {
date = date ?? new Date()
const slotDurationMillis = slotDurationMinutes * 60 * 1000
const slots = Math.ceil(date.getTime() / slotDurationMillis)
const nextSlotTimestamp = slots * slotDurationMillis
return new Date(nextSlotTimestamp)
}
export function calculateDurationSeconds(start: Date, end: Date): number {
return Math.abs(end.getTime() - start.getTime()) / 1000
}
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
export function sanitizeHTML(
html: string,
preset?: 'inline-noninteractive' | 'safe-html' | 'strip',
): string {
const inlineElements = [
'b',
'big',
'i',
'small',
'tt',
'abbr',
'acronym',
'cite',
'code',
'dfn',
'em',
'kbd',
'strong',
'samp',
'var',
'br',
'q',
's',
'span',
'sub',
'sup',
]
if (preset === 'inline-noninteractive') {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: inlineElements,
ALLOWED_ATTR: ['style', 'title'],
})
}
if (preset === 'safe-html') {
return DOMPurify.sanitize(html, { USE_PROFILES: { html: true } })
}
return DOMPurify.sanitize(html, { ALLOWED_TAGS: [] })
}
export function useSelectedShow() {
const store = useStore()
return computed<Show>({
get() {
return store.state.shows.shows[store.state.shows.selected.index]
},
set(show) {
store.commit('shows/switchShowById', show.id)
},
})
}
export function secondsToDurationString(seconds: number): string {
const h = Math.floor(seconds / 3600)
const m = Math.floor((seconds % 3600) / 60)
const s = Math.round(seconds % 60)
return `${h ? h + 'h ' : ''}${m ? m + 'min ' : ''}${s ? s + 's' : ''}`
}