chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
import type { ActionFunction } from '@sdk/types'
|
||||
import { leon } from '@sdk/leon'
|
||||
|
||||
import { deleteAllTimersMemory } from '../lib/memory'
|
||||
|
||||
export const run: ActionFunction = async function () {
|
||||
await deleteAllTimersMemory()
|
||||
|
||||
await leon.answer({ key: 'timer_canceled' })
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { ActionFunction } from '@sdk/types'
|
||||
import { leon } from '@sdk/leon'
|
||||
|
||||
import { TimerWidget } from '../widgets/timer-widget'
|
||||
import { getTimerMemoryByWidgetId, getNewestTimerMemory } from '../lib/memory'
|
||||
|
||||
export const run: ActionFunction = async function (_params, paramsHelper) {
|
||||
const widgetId = paramsHelper.getWidgetId()
|
||||
const timerMemory = widgetId
|
||||
? await getTimerMemoryByWidgetId(widgetId)
|
||||
: await getNewestTimerMemory()
|
||||
|
||||
if (!timerMemory) {
|
||||
await leon.answer({ key: 'no_timer_set' })
|
||||
return
|
||||
}
|
||||
|
||||
const { interval, finishedAt, duration } = timerMemory
|
||||
let remainingTime = finishedAt - Math.floor(Date.now() / 1_000)
|
||||
if (remainingTime <= 0) {
|
||||
remainingTime = 0
|
||||
}
|
||||
const initialProgress = 100 - (remainingTime / duration) * 100
|
||||
|
||||
const timerWidget = new TimerWidget({
|
||||
params: {
|
||||
seconds: remainingTime,
|
||||
initialProgress,
|
||||
initialDuration: duration,
|
||||
interval
|
||||
},
|
||||
onFetch: {
|
||||
widgetId: widgetId ?? timerMemory.widgetId,
|
||||
actionName: 'check_timer'
|
||||
}
|
||||
})
|
||||
|
||||
await leon.answer({
|
||||
widget: timerWidget,
|
||||
key: 'timer_status',
|
||||
data: {
|
||||
remaining_time: remainingTime
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import type { ActionFunction } from '@sdk/types'
|
||||
import { leon } from '@sdk/leon'
|
||||
|
||||
import { TimerWidget } from '../widgets/timer-widget'
|
||||
import { createTimerMemory } from '../lib/memory'
|
||||
|
||||
interface TimerDuration {
|
||||
value?: number
|
||||
unit?: string
|
||||
}
|
||||
|
||||
export const run: ActionFunction = async function (params) {
|
||||
const supportedUnits = ['hours', 'minutes', 'seconds']
|
||||
const duration = (params.action_arguments['duration'] as TimerDuration) || null
|
||||
|
||||
if (
|
||||
!duration ||
|
||||
typeof duration.value !== 'number' ||
|
||||
typeof duration.unit !== 'string'
|
||||
) {
|
||||
await leon.answer({ key: 'cannot_get_duration' })
|
||||
return
|
||||
}
|
||||
|
||||
const normalizedUnit = duration.unit.toLowerCase()
|
||||
if (!supportedUnits.includes(normalizedUnit)) {
|
||||
await leon.answer({ key: 'unit_not_supported' })
|
||||
return
|
||||
}
|
||||
|
||||
const { value: durationValue } = duration
|
||||
const seconds =
|
||||
normalizedUnit === 'hours'
|
||||
? Number(durationValue) * 3_600
|
||||
: normalizedUnit === 'minutes'
|
||||
? Number(durationValue) * 60
|
||||
: Number(durationValue)
|
||||
const interval = 1_000
|
||||
const timerWidget = new TimerWidget({
|
||||
params: {
|
||||
seconds,
|
||||
initialProgress: 0,
|
||||
interval
|
||||
},
|
||||
onFetch: {
|
||||
actionName: 'check_timer'
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all([
|
||||
createTimerMemory(timerWidget.id, seconds, interval),
|
||||
leon.answer({
|
||||
widget: timerWidget,
|
||||
key: 'timer_set'
|
||||
})
|
||||
])
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Memory } from '@sdk/memory'
|
||||
|
||||
export interface TimerMemory {
|
||||
widgetId: string
|
||||
duration: number
|
||||
interval: number
|
||||
createdAt: number
|
||||
finishedAt: number
|
||||
}
|
||||
|
||||
const TIMERS_MEMORY = new Memory<TimerMemory[]>({
|
||||
name: 'timers',
|
||||
defaultMemory: []
|
||||
})
|
||||
|
||||
export async function createTimerMemory(
|
||||
widgetId: string,
|
||||
duration: number,
|
||||
interval: number
|
||||
): Promise<TimerMemory> {
|
||||
const createdAt = Math.floor(Date.now() / 1_000)
|
||||
const newTimerMemory: TimerMemory = {
|
||||
duration,
|
||||
widgetId,
|
||||
interval,
|
||||
createdAt,
|
||||
finishedAt: createdAt + duration
|
||||
}
|
||||
|
||||
const timersMemory = await TIMERS_MEMORY.read()
|
||||
await TIMERS_MEMORY.write([...timersMemory, newTimerMemory])
|
||||
|
||||
return newTimerMemory
|
||||
}
|
||||
|
||||
export async function getTimerMemoryByWidgetId(
|
||||
widgetId: string
|
||||
): Promise<TimerMemory | null> {
|
||||
const timersMemory = await TIMERS_MEMORY.read()
|
||||
|
||||
return (
|
||||
timersMemory.find((timerMemory) => timerMemory.widgetId === widgetId) ||
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
export async function getNewestTimerMemory(): Promise<TimerMemory | null> {
|
||||
const timersMemory = await TIMERS_MEMORY.read()
|
||||
|
||||
return timersMemory[timersMemory.length - 1] || null
|
||||
}
|
||||
|
||||
export function deleteAllTimersMemory(): Promise<TimerMemory[]> {
|
||||
return TIMERS_MEMORY.write([])
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{}
|
||||
@@ -0,0 +1,16 @@
|
||||
import type { WidgetEventMethod } from '@sdk/widget'
|
||||
import { WidgetComponent } from '@sdk/widget-component'
|
||||
|
||||
interface TimerProps {
|
||||
initialTime: number
|
||||
initialProgress: number
|
||||
interval: number
|
||||
totalTimeContent: string
|
||||
onEnd?: () => WidgetEventMethod
|
||||
}
|
||||
|
||||
export class Timer extends WidgetComponent<TimerProps> {
|
||||
constructor(props: TimerProps) {
|
||||
super(props)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { WidgetComponent } from '@sdk/widget-component'
|
||||
import { Widget, type WidgetEventMethod, type WidgetOptions } from '@sdk/widget'
|
||||
|
||||
import { Timer } from './components/timer'
|
||||
|
||||
interface Params {
|
||||
seconds: number
|
||||
interval: number
|
||||
initialProgress: number
|
||||
initialDuration?: number
|
||||
}
|
||||
|
||||
export class TimerWidget extends Widget<Params> {
|
||||
constructor(options: WidgetOptions<Params>) {
|
||||
super(options)
|
||||
}
|
||||
|
||||
public render(): WidgetComponent {
|
||||
const { seconds, interval, initialDuration, initialProgress } = this.params
|
||||
const secondUnitContent = this.content('second_unit')
|
||||
const secondsUnitContent = this.content('seconds_unit')
|
||||
const minuteUnitContent = this.content('minute_unit')
|
||||
const minutesUnitContent = this.content('minutes_unit')
|
||||
const totalTime = initialDuration || seconds
|
||||
let totalTimeContent = ''
|
||||
|
||||
if (totalTime >= 60) {
|
||||
const minutes = totalTime / 60
|
||||
|
||||
totalTimeContent = this.content('total_time', {
|
||||
value: minutes % 1 === 0 ? minutes : minutes.toFixed(2),
|
||||
unit: minutes > 1 ? minutesUnitContent : minuteUnitContent
|
||||
})
|
||||
} else {
|
||||
totalTimeContent = this.content('total_time', {
|
||||
value: totalTime,
|
||||
unit: totalTime > 1 ? secondsUnitContent : secondUnitContent
|
||||
})
|
||||
}
|
||||
|
||||
return new Timer({
|
||||
initialTime: seconds,
|
||||
initialProgress,
|
||||
interval,
|
||||
totalTimeContent,
|
||||
onEnd: (): WidgetEventMethod => {
|
||||
return this.sendUtterance('times_up', {
|
||||
from: 'leon'
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user