<template>
  <v-container fluid>
    <v-overlay absolute opacity="1" :value="!feed.updated">
      <v-progress-circular indeterminate size="64"></v-progress-circular>
    </v-overlay>
    <v-card>
      <v-card-title>
        {{feed.title}}
        <a :href="feed.link"><v-icon class="ml-2">mdi-link</v-icon></a>
      </v-card-title>
      <v-card-subtitle>Last updated: {{feed.updated}}</v-card-subtitle>
      <v-card-text>
        <v-timeline align-top dense>
          <v-timeline-item v-for="item in feed.items" small :key="item.id">
            <v-card :color="item.colour">
              <v-card-title primary-title>
                <div class="headline">{{item.title}}</div>
              </v-card-title>
              <v-card-subtitle>{{item.updated}} ({{item.author}})</v-card-subtitle>
              <v-card-text v-html="item.summary">
              </v-card-text>
              <v-card-actions>
              <v-btn target="_new" :href="item.link">Complete story</v-btn>
              </v-card-actions>
            </v-card>
          </v-timeline-item>
        </v-timeline>
      </v-card-text>
    </v-card>
  </v-container>
</template>

<script>
import { mapActions } from 'vuex';

export default {
  name: "FeedViewer",

  data () {
    return {
      timer: null,
      feed: {}
    }
  },

  methods: {
    parse (text) {
      const data = {items:[]};
      const parser = new DOMParser();
      const xml = parser.parseFromString(text, "application/xml");
      const feed = xml.getElementsByTagNameNS("http://www.w3.org/2005/Atom", "feed")[0];
      const entries = feed.getElementsByTagName("entry");

      data.title = feed.getElementsByTagName("title")[0].childNodes[0].textContent;
      data.updated = feed.getElementsByTagName("updated")[0].childNodes[0].textContent;
      data.link = [...feed.getElementsByTagName("link")].filter(i =>
         i.getAttribute("type") == "text/html"
      ).pop().getAttribute("href");

      data.items = [...entries].map(entry => {
        const item = {};
        const link = entry.getElementsByTagName("link")[0];
        if (link) {
          item.link = link.getAttribute("href");
        }
        const author = entry.getElementsByTagName("author")[0];
        if (author) {
          const name = author.getElementsByTagName("name")[0];
          item.author = name ? name.childNodes[0].textContent : author.innerHTML;
        }
        const summaries = entry.getElementsByTagName("summary");
        const summary = [...summaries].find(i => i.getAttribute("type") == "xhtml") || summaries[0];

        item.summary = summary.innerHTML;
        item.id = entry.getElementsByTagName("id")[0].childNodes[0].textContent;
        item.title = entry.getElementsByTagName("title")[0].childNodes[0].textContent;
        item.updated = entry.getElementsByTagName("updated")[0].childNodes[0].textContent;

        return item;
      });

      return data;
    },

    /** Try to fix idiosyncrasies and XML bugs in the source.
     */
    fixText (text) {
      // Of course this will fail if there happens to be an </hr>
      // element in the source.
      return text.replace(/(<hr( [^>]*)?>)/g, "$1</hr>")
    },

    async refresh () {
      const text = await this.api([`/rss/?remote=${atob(this.$route.params.source)}`, {format:"text"}]);
      try {
        this.feed = this.parse(text);
      } catch (err) {
        // If it failed to parse, try once again with some
        // tweaks to address known feed bugs.
        this.feed = this.parse(this.fixText(text));
      }
    },

    ...mapActions(["api"])
  },

  async mounted () {
    await this.refresh();
    this.timer = setInterval(this.refresh, 300000);
  },

  unmounted () {
    cancelInterval(this.timer);
    this.timer = null;
  }
}

</script>
