packages feed

kurita-0: client/src/KuritaClient.ts

import sortBy from 'lodash/sortBy'

type ServerCompetitorState = {
  competitor: string,
  score: number,
}

type ServerGameState = {
  extra: {
    commentary: Array<string>,
    end_time: Date,
  },
  game: [ServerCompetitorState, ServerCompetitorState],
}

export type ServerBracketState = {
  upcoming: Array<[string, string]>,
  current: ServerGameState | null,
  played: Array<ServerGameState>,
}

type ServerStartEvent = {
  event: 'start',
  bracket: ServerBracketState,
}

type ServerScoreEvent = {
  event: 'score',
  scores: [ServerCompetitorState, ServerCompetitorState],
}

export type ServerEvent = (
  ServerStartEvent | ServerScoreEvent
)

type ServerAction = {
  event: 'vote',
  for: string,
}

type ClientOptions = {
  debug: boolean,
  endpoint: string,
}

type ServerEventHandler = (event: ServerEvent) => void

class KuritaClient {
  private ws: WebSocket
  private options: ClientOptions
  private listeners: Set<ServerEventHandler>
  private reconnectTimeout: number

  constructor(options: ClientOptions) {
    this.options = options
    this.listeners = new Set()
  }

  connect() {
    this.ws = new WebSocket(this.options.endpoint)
    this.ws.addEventListener('message', this.handleMessage)
    this.ws.addEventListener('close', this.handledisconnect)
  }

  disconnect() {
    this.ws.close()
  }

  private handledisconnect = () => {
    if (this.options.debug) {
      console.log('disconnected, reconnecting...')
    }
    clearTimeout(this.reconnectTimeout)
    setTimeout(() => { this.connect() }, Math.random() * 2500 + 2500)
  }

  subscribe(handler: ServerEventHandler) {
    this.listeners.add(handler)
    return () => {
      this.listeners.delete(handler)
    }
  }

  send(data: ServerAction) {
    const dataText = JSON.stringify(data)
    this.ws.send(dataText)
    if (this.options.debug) {
      console.log('sent', data)
    }
  }

  private _reviver(key: string, value: any) {
    if (key.endsWith('_time')) {
      return new Date(value)
    }
    if (key === 'game' || key === 'scores') {
      return sortBy(value, state => state.competitor)
    }
    return value
  }

  private handleMessage = (event: MessageEvent) => {
    const dataText = event.data
    const data = JSON.parse(dataText, this._reviver) as ServerEvent

    if (this.options.debug) {
      console.log('received', data)
    }

    for (const listener of this.listeners) {
      listener(data)
    }
  }
}

export default KuritaClient