vuejs github timeline

by Victor Melnik

HTML

<div id="timeline">
  <h1>GitHub Timeline</h1>
  <div class="progress">
    <div class="line" :style="{ width: (Math.floor(time * 100 / timeMax) + '%') }"></div>
    <div class="background"></div>
  </div>
  <ul>
    <li v-for="event in events">
      <img class="avatar" :src="event.actor.avatar_url" />
      <div class="headline">
        <div class="time">{{ event.created_at|formatDate }}</div>
        <a class="user" :href="'https://github.com/' + event.actor.login">{{ event.actor.login }}</a>
      </div>
      <div class="clearfix"></div>
      <div v-if="event.type === 'CommitCommentEvent'">
        commented commit in <repo :name="event.repo.name"></repo><br/>
        <span class="small">{{ event.payload.comment.body }}</span>
      </div>
      <div v-else-if="event.type === 'CreateEvent'">
        created {{ event.payload.ref_type }} <repo :name="event.repo.name"></repo>
      </div>
      <div v-else-if="event.type === 'DeleteEvent'">
        deleted {{ event.payload.ref_type }} <repo :name="event.repo.name"></repo>
      </div>
      <div v-else-if="event.type === 'ForkEvent'">
        forked repository <repo :name="event.repo.name"></repo>
      </div>
      <div v-else-if="event.type === 'IssueCommentEvent'">
        commented issue {{ event.payload.issue.title }} on <repo :name="event.repo.name"></repo><br/>
        <span class="small">{{ event.payload.comment.body }}</span>
      </div>
      <div v-else-if="event.type === 'IssuesEvent'">
        {{ event.payload.action }} issue "{{ event.payload.issue.title }}" on <repo :name="event.repo.name"></repo>
      </div>
      <div v-else-if="event.type === 'PullRequestEvent'">
        {{ event.payload.action }} pull request #{{ event.payload.number }} "{{ event.payload.pull_request.title }}" on <repo :name="event.repo.name"></repo>
      </div>
      <div v-else-if="event.type === 'PushEvent'">
        pushed {{ event.payload.commits.length }} commits to <repo :name="event.repo.name"></repo>
     ...

CSS

body {
  font-family: Helvetica, Verdana, Arial, sans-serif;
  font-size: 16px;
  font-weight: 300;
}

#timeline h1 {
  text-align: center;
}

.progress {
  position: relative;
  display: block;
  height: 2px;
  margin: 0 auto;
  width: 70%;
}
.progress div {
  position: absolute;
  top: 0;
  display: block;
  height: 2px;
}
.progress .background {
  background-color: #c7c7c7;
  width: 100%;
}
.progress .line {
  background-color: #5282d2;
  z-index: 999;
}

#timeline {
  width: 90%;
  margin: 0 auto;
}

#timeline ul {
  list-style: none;
  margin: 10px 0;
  padding: 0;
}

#timeline li {
  display: block;
  position: relative;
  background-color: #FEFEFE;
  border: 2px solid #FEFEFE;
  margin: 0 auto 10px;
  padding: 6px;
  padding-bottom: 5px;
  box-shadow: 0px 1px 2px rgba(34, 25, 25, 0.4);
}

.avatar {
  float: left;
  max-width: 48px;
  max-height: 48px;
  margin-bottom: 5px;
}

.headline {
  float: left;
  margin-left: 8px;
}

.user {
  color: #000;
  font-size: 120%;
  font-weight: bold;
}

.small,
.time {
  font-size: 80%;
  color: #777;
}

.time {
  margin-bottom: 5px;
  font-size: small;
}

.clearfix {
  clear: both;
}

JavaScript

Vue.component('repo', {
  template: '<a :href="\'https://github.com/\' + name">{{ name }}</a>',
  props: ['name']
});

var vm = new Vue({
  el: '#timeline',
  data: {
    events: null,
    timeMax: 60,
    time: 0,
    timer: null
  },

  created: function() {
    this.fetchData();
  },

  filters: {
    formatDate: function(v) {
      return v.replace(/T|Z/g, ' ');
    }
  },

  methods: {
    fetchData: function() {
      var xhr = new XMLHttpRequest();
      var self = this;
      xhr.open('GET', 'https://api.github.com/events');
      xhr.onload = function() {
        self.events = JSON.parse(xhr.responseText);
        self.timeMax = xhr.getResponseHeader("X-Poll-Interval");
        self.startTimer();
      };
      xhr.send();
    },
    startTimer: function() {
      var self = this;
      this.time = 0;
      this.timer = setInterval(function() {
        self.time++;
        if (self.time >= self.timeMax) {
          clearInterval(self.timer);
          self.fetchData();
        }
      }, 1000);
    }
  }
});