import { reactive } from "vue" import { Track } from "./track" import { Tick } from './ticks' import { error } from "./error" import { getCookie } from "./util"; import router from './router' enum State { Unfetched, Fetching, Fetched, } interface LoggedInUser { name: string } class AppState { tracks: Array state: State user?: LoggedInUser source?: EventSource constructor() { this.tracks = new Array this.state = State.Unfetched const name = getCookie("name") if (name) this.user = { name } } streamUpdatesFromServer() { const source = new EventSource("/api/v1/updates") source.addEventListener("open", () => console.debug("opened event source")) source.addEventListener('message', event => console.log(event)) source.addEventListener('TickAdded', event => { const tick: Tick = Tick.fromJSON(JSON.parse(event.data)) const tracks = this.tracks.map(track => { if (track.id === tick.track_id) { const ticks = track.ticks ?? [] ticks.push(tick) track.ticks = ticks } return track }) this.tracks = tracks }) source.addEventListener('TrackAdded', ({ data }) => { const track: Track = Track.fromJSON(JSON.parse(data)) this.tracks = [track, ...this.tracks] }) source.addEventListener('TickDropped', event => { const tick: Tick = Tick.fromJSON(JSON.parse(event.data)) const tracks = this.tracks.map(track => { if (track.id === tick.track_id) { track.ticks = track.ticks?.filter($tick => $tick.id !== tick.id) } return track }) this.tracks = tracks }) source.addEventListener('TrackDropped', ({ data }) => { const track: Track = Track.fromJSON(JSON.parse(data)) this.tracks = this.tracks.filter($track => $track.id !== track.id) }) source.addEventListener('TrackChanged', ({ data }) => { const track: Track = Track.fromJSON(JSON.parse(data)) this.tracks = this.tracks.map($track => $track.id === track.id ? track : $track) }) source.addEventListener('Lagged', event => { console.log(event) // Refresh the page, refetching the list of tracks and ticks window.location = window.location }) source.addEventListener('error', event => { error(event) window.location = window.location }) window.addEventListener('beforeunload', () => source.close()) this.source = source } async repopulate() { if (!this.user) { this.tracks = [] return } this.state = State.Fetching this.tracks = await Track.fetchAll() this.source?.close() this.streamUpdatesFromServer() this.state = State.Fetched } async populate() { if (this.state != State.Unfetched) return await this.repopulate() } async logOut() { const result = await fetch('/api/v1/auth', {method: 'DELETE'}) if(!result.ok) return error('failed to log out') this.user = undefined router.push('/login') } } export const state = reactive(new AppState)