<template lang="pug">
.client(@scroll.passive="onScroll")
  .statues
    .header(v-if="hasNotifications") Accueil
    success-loadable.list(:loadable="statues")
      template(v-for="status in statues.get()")
        status(v-if="showStatus(status)" :key="status.id" :status="status" :showMedia="options.showMedia" @mark="onStatusMark")
      .status(v-show="statues.loadingMore")
        .service-loader
  .notifications(v-if="hasNotifications")
    .header
      | Notifications
      span.date(@click.stop.prevent="onNotificationsClear") ❌
    .list
      notification(v-for="notification in notifications.get()" :key="notification.id" :notification="notification"
        :showMedia="options.showMedia" @dismiss="onNotificationDismiss" @mark="onStatusMark")
</template>

<script lang="ts">
import axios, { AxiosResponse } from 'axios'
import { Component, Mixins } from 'vue-property-decorator'

import ServiceClient from '@/components/ServiceClient'
import Lists from '@/helpers/lists/Lists'
import AxiosLodable from '@/helpers/loadable/AxiosLoadable'
import AxiosLodableMore from '@/helpers/loadable/AxiosLoadableMore'
import { AUTH, getHeaders, getRest } from './Mastodon.vue'
import Notification from './Notification.vue'
import Status from './Status.vue'
import { MarkMessage, Notification as INotification, Options, Status as IStatus } from './Types'

@Component({ components: { Status, Notification } })
export default class Client extends Mixins<ServiceClient<Options>>(ServiceClient) {

  rest = getRest(this.auth, this.options.timeout)

  statues = new AxiosLodableMore<IStatus[], object>()
  notifications = new AxiosLodable<INotification[], object>()

  get hasNotifications() {
    if(!this.notifications.isSuccess) {
      return false
    }

    const not = this.notifications.get()
    if(not){
      return not.length > 0
    } else {
      return false
    }
  }

  created() {
    this.statues.load(this.getTimeline())
    this.notifications.load(this.get('/notifications'))
    this.setupStream()
  }

  get(path: string, options = {}) {
    return this.catchEmit(this.rest.get(path, { params: { limit: this.options.buffer, ...options } }))
  }

  post(path: string, options = {}) {
    return this.catchEmit(this.rest.post(path, options))
  }

  getTimeline(options = {}) {
    return this.get('/timelines/home', options)
  }

  onScroll(event: any) {
    this.statues.handleScroll(event.target,
      st => this.getTimeline({ max_id: Lists.last(st).id }),
      (res, st) => Lists.pushAll(st, res.data),
      st => Lists.removeFrom(st, this.options.buffer)
    )
  }

  showStatus(status: IStatus) {
    return (!status.in_reply_to_id || this.options.reply) && (!status.reblog || this.options.reblog)
  }

  onStatusMark(action: MarkMessage) {
    this.post(`/statuses/${action.id}/${action.type}`)
      .then(() => action.callback())
  }

  onNotificationDismiss(id: number) {
    this.post('/notifications/dismiss', { id })
      .then(() => this.notifications.with(
        ns => Lists.removeFirstBy(ns, n => n.id, id)))
  }

  onNotificationsClear() {
    this.post('/notifications/clear')
      .then(() => this.notifications.with(
        ns => Lists.clear(ns)))
  }

  setupStream() {
    this.get('/instance').then(res => {
      const oldAuth = res.data.version < '2.8.4' ? `access_token=${this.auth.get(AUTH.TOKEN)}&` : ''
      const ws = new WebSocket(
        `wss://${this.auth.get(AUTH.SERVER)}/api/v1/streaming?${oldAuth}stream=user`,
        this.auth.get(AUTH.TOKEN)
      )
      ws.onmessage = event => {
        const data = JSON.parse(event.data)
        const payload = JSON.parse(data.payload)
        switch (data.event) {
          case 'update':
            this.statues.with(s => s.unshift(payload))
            break

          case 'notification':
            this.notifications.with(n => n.unshift(payload))
            break

          case 'delete':
            this.statues.with(st => Lists.removeFirstBy(st, s => s.id, payload.id))
            break
        }
      }
      ws.onerror = ev => this.emitError(ev.type)
      ws.onclose = () => {
        this.emitError(
          'Mastodon stream disconnected !' +
            (this.options.reconnect ? ' Reconnecting...' : '')
        )
        if (this.options.reconnect) {
          setTimeout(() => this.setupStream(), this.options.timeout)
        }
      }
    })
  }
}
</script>


<style lang="sass">
@import '@/common.sass'

.mastodon
  .client
    display: flex
    height: 100%
    overflow-y: auto
    .header
      @include main-tile
    .list
      @include group-tile
    .statues
      flex: 1
    .notifications
      max-width: 33%

    .account
      .name
        margin: 0 $borderRadius
        color: $foreColor
      .avatar
        float: left
        @include rounded
        width: $avatarSize
        height: $avatarSize
        background-size: $avatarSize $avatarSize
      div
        display: inline-block

    .status, .notification
      min-height: $avatarSize
      .content
        margin: .5em .5em .5em 1em
        &.avatared
          margin-left: .5em + $avatarSize
        .reblog
          font-size: .8em
        .spoiler
          margin-bottom: .5em
        .media
          margin: .5em
          position: relative
          display: inline-block
          & > *
            max-height: 10em
            max-width: 100%
          .gif
            position: absolute
            top: 0
            bottom: 0
            left: 0
            right: 0
            height: 100%
            width: 100%
            background-color: #00000044
            color: white
            padding: .5em
        .card
          @include tile
          padding: .2em
          display: block
          .provider
            float: right
      .meta
        margin-left: 1em + $avatarSize
        font-size: .8em
        a
          margin: 0 .5em

    .date, .dismiss
      float: right
</style>
