kalkutago/client/src/track.ts

163 lines
5.5 KiB
TypeScript

import { error } from "./error"
import { Tick, ITick } from './ticks'
import { dateQuery } from "./util"
export interface ITrack {
id?: number
name: String
description: String
icon: String
enabled: number
multiple_entries_per_day?: number
color?: number
order?: number
ticks?: Array<Tick>
}
export class Track implements ITrack {
id?: number
name: String
description: String
icon: String
enabled: number
multiple_entries_per_day?: number
color?: number
order?: number
ticks?: Array<Tick>
constructor(
id: number | undefined,
name: String,
description: String,
icon: String,
enabled: number,
multiple_entries_per_day?: number,
color?: number,
order?: number,
ticks?: Array<ITick>
) {
this.id = id
this.name = name
this.description = description
this.icon = icon
this.enabled = enabled
this.multiple_entries_per_day = multiple_entries_per_day
this.color = color
this.order = order
this.ticks = ticks?.map(tick => Tick.fromJSON(tick))
this.isSetOn = this.isSetOn.bind(this)
this.fetchTicks = this.fetchTicks.bind(this)
}
/**
* Add this track to the database. A `TrackAdded` event should have been
* received from the server on the event stream by the time this returns.
*
* @returns whether or not the query succeeded
*/
async create(): Promise<boolean> {
// note that this.id is expected to be `undefined` here.
const response = await fetch('/api/v1/tracks', {
method: "POST",
body: JSON.stringify(this),
headers: { "Content-Type": "application/json" }
})
if (!response.ok)
error(`error submitting track ${this.name}: ${response.statusText} (${response.status})`)
return response.ok
}
async delete() {
const id = this.id
if (id) await Track.deleteById(id)
}
static async deleteById(id: number) {
const response = await fetch(`/api/v1/tracks/${id}`, { method: "DELETE" })
if (!response.ok) error(`error deleting track with ID ${id}: ${response.statusText} (${response.status})`)
}
static fromJSON(track: ITrack): Track {
return new Track(track.id, track.name, track.description, track.icon, track.enabled, track.multiple_entries_per_day, track.color, track.order)
}
isSetOn(date: Date): boolean {
for (var tick of (this.ticks ?? [])) {
if (
date.getUTCFullYear() == tick.year &&
(date.getUTCMonth() + 1) == tick.month &&
// Javascript Date ^^^ uses 0-index for dates of months 🤦
date.getDate() == tick.day
) return true
}
return false
}
async fetchTicks(): Promise<Track> {
const response = await fetch(`/api/v1/tracks/${this.id}/ticks`)
if (response.ok) {
this.ticks = await response.json()
} else {
throw new Error(`error fetching ticks: ${response.statusText} (${response.status})`)
}
return this
}
static async fetchAll(): Promise<Array<Track>> {
const result = await fetch('/api/v1/tracks')
if (result.ok) {
try {
const body = await result.text();
try {
const tracks = Array.prototype.map.call(JSON.parse(body), Track.fromJSON) as Array<Track>
return Promise.all(tracks.map((track: Track) => track.fetchTicks()))
} catch (e) {
console.error('error parsing body from JSON')
console.error(e)
console.debug(body)
}
} catch (err) {
console.error(err)
result.text()
.then(console.debug)
.catch(console.error)
}
} else {
error(`error fetching tracks: ${result.statusText} (${result.status})`)
}
return []
}
/**
* Mark this track as being completed on the given date. A `TickAdded` event
* should have been received from the server on the event stream by the time
* this returns.
*
* @param date the date the task was completed
* @returns the decoded server API response
*/
async markComplete(date: Date) {
const query = dateQuery(date)
const response: Response = await fetch(`/api/v1/tracks/${this.id}/ticked?${query.toString()}`, { method: "PATCH" })
const body = await response.text()
if (!response.ok) {
error(body)
throw new Error(`error setting tick for track ${this.id} ("${this.name}"): ${response.status} ${response.statusText}`)
}
return JSON.parse(body)
}
/**
* Mark this track as being incomplete on the given date. A `TickAdded` event
* should have been received from the server on the event stream by the time
* this returns.
*
* @param date the date the task was completed
* @returns the decoded server API response
*/
async markIncomplete(date: Date) {
const query = dateQuery(date)
const { ok, status, statusText } = await fetch(`/api/v1/tracks/${this.id}/all-ticks?${query.toString()}`, { method: 'DELETE' })
if (!ok)
error(`error deleting ticks for ${this.id}: ${statusText} (${status})`)
}
}