Create my first Microsub reader
All client side at the moment, but it will probably need a bit of backend code, to circumvent CORS problems in the browser.
This commit is contained in:
parent
03a6ebf8a6
commit
ddad588d90
|
|
@ -8,6 +8,7 @@
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"node-sass": "^4.9.3",
|
||||||
"vue": "^2.5.17",
|
"vue": "^2.5.17",
|
||||||
"vue-router": "^3.0.1",
|
"vue-router": "^3.0.1",
|
||||||
"vuex": "^3.0.1"
|
"vuex": "^3.0.1"
|
||||||
|
|
|
||||||
52
src/App.vue
52
src/App.vue
|
|
@ -1,20 +1,48 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<div id="nav">
|
<nav id="nav" class="navbar is-primary">
|
||||||
<router-link to="/">Home</router-link> |
|
<div class="container">
|
||||||
<router-link to="/about">About</router-link>
|
<div class="navbar-brand">
|
||||||
|
<router-link to="/" class="navbar-item">Ekster</router-link>
|
||||||
|
|
||||||
|
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
<span aria-hidden="true"></span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="navbar-menu">
|
||||||
|
<!--<router-link to="/about">About</router-link>-->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<div class="container">
|
||||||
|
<router-view/>
|
||||||
|
|
||||||
|
<LoginModal :active="!this.$store.state.logged_in"></LoginModal>
|
||||||
</div>
|
</div>
|
||||||
<router-view/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import LoginModal from '@/components/LoginModal'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {LoginModal},
|
||||||
|
mounted() {
|
||||||
|
let loginData = JSON.parse(window.localStorage.getItem('login_data'))
|
||||||
|
this.$store.dispatch('isLoggedIn', loginData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
#app {
|
#app {
|
||||||
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
font-family: 'Avenir', Helvetica, Arial, sans-serif;
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
text-align: center;
|
color: #2c3e50;
|
||||||
color: #2c3e50;
|
}
|
||||||
margin-top: 60px;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
31
src/components/Channel.vue
Normal file
31
src/components/Channel.vue
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
<template>
|
||||||
|
<div class="channel--name" @click="select(channel)">
|
||||||
|
{{ channel.name }} <span :class="{ tag: true, 'is-success': channel.unread > 0}">{{ channel.unread }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Channel",
|
||||||
|
|
||||||
|
props: ['channel'],
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
select(channel) {
|
||||||
|
this.$emit('channel-selected', channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.channel--name {
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
.channel--name:hover {
|
||||||
|
background: aliceblue;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
17
src/components/Channels.vue
Normal file
17
src/components/Channels.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<template>
|
||||||
|
<div :class="this.className">
|
||||||
|
<div class="channels--channel" v-for="channel in channels" :key="channel.uid">
|
||||||
|
<slot :channel="channel"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Channels",
|
||||||
|
props: ['className', 'channels']
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
||||||
99
src/components/Entry.vue
Normal file
99
src/components/Entry.vue
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
<template>
|
||||||
|
<div class="entry card">
|
||||||
|
<div class="card-image" v-if="photo_first">
|
||||||
|
<figure class="image is-4by3">
|
||||||
|
<img :src="photo_first"/>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="media">
|
||||||
|
<div class="media-left">
|
||||||
|
<figure class="image is-48x48">
|
||||||
|
<img :src="item.author.photo"/>
|
||||||
|
</figure>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="content">
|
||||||
|
<div><a :href="author_url">{{ author_name }}</a></div>
|
||||||
|
<h3 class="title is-6" v-if="item.name" v-text="item.name"></h3>
|
||||||
|
<div class="content" v-html="main_content"></div>
|
||||||
|
|
||||||
|
<div class="photos">
|
||||||
|
<div class="photo" v-for="photo in photo_rest" :key="photo">
|
||||||
|
<img :src="photo" class="image"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="debug" v-text="item" v-if="this.$store.state.debug"></div>
|
||||||
|
|
||||||
|
<a :href="item.url">
|
||||||
|
<span class="published" v-html="item.published"></span>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "Entry",
|
||||||
|
props: ['item'],
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
main_content() {
|
||||||
|
let content = this.item.content
|
||||||
|
if (content) {
|
||||||
|
if (content.html) {
|
||||||
|
return content.html
|
||||||
|
} else if (content.text) {
|
||||||
|
return content.text
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
content = this.item.summary
|
||||||
|
if (content) {
|
||||||
|
if (content.html) {
|
||||||
|
return content.html
|
||||||
|
} else if (content.text) {
|
||||||
|
return content.text
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
},
|
||||||
|
photo_first() {
|
||||||
|
if (this.item.photo && this.item.photo.length >= 1) {
|
||||||
|
return this.item.photo[0]
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
photo_rest() {
|
||||||
|
if (this.item.photo && this.item.photo.length > 1) {
|
||||||
|
return this.item.photo.slice(1)
|
||||||
|
}
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
author_name() {
|
||||||
|
if (!this.item.author.name) {
|
||||||
|
return new URL(this.item.author.url).hostname
|
||||||
|
}
|
||||||
|
return this.item.author.name
|
||||||
|
},
|
||||||
|
author_url() {
|
||||||
|
return this.item.author.url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.entry {
|
||||||
|
}
|
||||||
|
|
||||||
|
.photos {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
grid-gap: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="hello">
|
|
||||||
<h1>{{ msg }}</h1>
|
|
||||||
<p>
|
|
||||||
For guide and recipes on how to configure / customize this project,<br>
|
|
||||||
check out the
|
|
||||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
|
||||||
</p>
|
|
||||||
<h3>Installed CLI Plugins</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Essential Links</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
|
||||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
|
||||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
|
||||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
|
||||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
|
||||||
</ul>
|
|
||||||
<h3>Ecosystem</h3>
|
|
||||||
<ul>
|
|
||||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
|
||||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
|
||||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
|
||||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
export default {
|
|
||||||
name: 'HelloWorld',
|
|
||||||
props: {
|
|
||||||
msg: String
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
|
||||||
<style scoped>
|
|
||||||
h3 {
|
|
||||||
margin: 40px 0 0;
|
|
||||||
}
|
|
||||||
ul {
|
|
||||||
list-style-type: none;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 10px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
color: #42b983;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
96
src/components/LoginModal.vue
Normal file
96
src/components/LoginModal.vue
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
<template>
|
||||||
|
<div :class="classes">
|
||||||
|
<div class="modal-background"></div>
|
||||||
|
<div class="modal-card">
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Sign in with your website</p>
|
||||||
|
<button class="delete" aria-label="close" @click="close"></button>
|
||||||
|
</header>
|
||||||
|
<section class="modal-card-body">
|
||||||
|
<div class="field">
|
||||||
|
<label class="label">Web Sign in</label>
|
||||||
|
<div class="control">
|
||||||
|
<input class="input" type="text" placeholder="https://example.com/" v-model="url">
|
||||||
|
</div>
|
||||||
|
<p class="help">Sign in with your website address</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<button class="button is-success" @click="login">Login</button>
|
||||||
|
<button class="button" @click="close">Cancel</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<button class="modal-close is-large" aria-label="close" @click="close"></button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import qs from 'qs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "LoginModal",
|
||||||
|
|
||||||
|
props: ['active'],
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
show: false,
|
||||||
|
url: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
classes() {
|
||||||
|
return {'modal': true, 'is-active': this.show}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
close() {
|
||||||
|
this.show = false
|
||||||
|
},
|
||||||
|
login() {
|
||||||
|
// try to login with
|
||||||
|
window.location = 'https://p83.nl/auth?response_type=code&me=https://p83.nl/&redirect_uri=http://192.168.178.21:8080/callback&scope=channels+timeline&state=1234&client_id=https://p83.nl/';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.show = this.active
|
||||||
|
|
||||||
|
if (!this.$route.query.hasOwnProperty('code')) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let code = this.$route.query['code']
|
||||||
|
if (!code.length) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let tokenurl = 'https://p83.nl/authtoken'
|
||||||
|
let params = {}
|
||||||
|
params['grant_type'] = 'authorization_code'
|
||||||
|
params['code'] = code
|
||||||
|
params['client_id'] = 'https://p83.nl/'
|
||||||
|
params['redirect_uri'] = 'http://192.168.178.21:8080/callback'
|
||||||
|
params['me'] = 'https://p83.nl/'
|
||||||
|
|
||||||
|
fetch(tokenurl, {
|
||||||
|
method: 'POST',
|
||||||
|
body: qs.stringify(params, {arrayFormat: 'brackets'}),
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
}).then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
this.$store.dispatch('tokenResponse', response)
|
||||||
|
this.active = false
|
||||||
|
this.$router.push('/')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
||||||
49
src/components/Timeline.vue
Normal file
49
src/components/Timeline.vue
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
<template>
|
||||||
|
<div :class="this.className">
|
||||||
|
<div class="timeline--item" v-for="item in items" :key="item.id">
|
||||||
|
<TimelineEntry :item="item"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="level">
|
||||||
|
<div class="level-item">
|
||||||
|
<button class="button" @click="prevPage" v-if="timeline.paging.before">Prev Page</button>
|
||||||
|
</div>
|
||||||
|
<div class="level-item">
|
||||||
|
<button class="button" @click="nextPage" v-if="timeline.paging.after">Next Page</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import TimelineEntry from '@/components/Entry'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "Timeline",
|
||||||
|
props: ['className', 'channel', 'timeline'],
|
||||||
|
components: {TimelineEntry},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
items () {
|
||||||
|
return this.timeline.items.filter((item) => {
|
||||||
|
return item.type === 'entry';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
nextPage() {
|
||||||
|
this.$emit('getPage', {uid: this.channel.uid, after: this.timeline.paging.after})
|
||||||
|
},
|
||||||
|
prevPage() {
|
||||||
|
this.$emit('getPage', {uid: this.channel.uid, before: this.timeline.paging.before})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.timeline--item {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -2,6 +2,8 @@ import Vue from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
|
import 'bulma'
|
||||||
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,16 @@ export default new Router({
|
||||||
name: 'home',
|
name: 'home',
|
||||||
component: Home
|
component: Home
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/channel/:uid',
|
||||||
|
name: 'channel',
|
||||||
|
component: Home
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/callback',
|
||||||
|
name: 'callback',
|
||||||
|
component: Home
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/about',
|
path: '/about',
|
||||||
name: 'about',
|
name: 'about',
|
||||||
|
|
|
||||||
68
src/store.js
68
src/store.js
|
|
@ -1,16 +1,76 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
|
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
state: {
|
state() {
|
||||||
|
let loginData = JSON.parse(window.localStorage.getItem('login_data'))
|
||||||
|
return {
|
||||||
|
...loginData,
|
||||||
|
channels: [],
|
||||||
|
timeline: {items: [], paging: {}},
|
||||||
|
channel: {},
|
||||||
|
debug: false,
|
||||||
|
logged_in: loginData.access_token && loginData.access_token.length > 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mutations: {
|
mutations: {
|
||||||
|
newChannels(state, channels) {
|
||||||
|
state.channels = channels
|
||||||
|
},
|
||||||
|
newTimeline(state, {channel, timeline}) {
|
||||||
|
state.timeline = timeline
|
||||||
|
state.channel = channel
|
||||||
|
},
|
||||||
|
newAccessToken(state, {me, access_token, scope}) {
|
||||||
|
state.logged_in = true
|
||||||
|
state.scope = scope
|
||||||
|
state.me = me
|
||||||
|
state.access_token = access_token
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
fetchChannels({commit}) {
|
||||||
|
fetch('https://microsub.stuifzandapp.com/microsub?action=channels', {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + this.state.access_token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
commit('newChannels', response.channels)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
fetchTimeline({commit}, channel) {
|
||||||
|
let url = 'https://microsub.stuifzandapp.com/microsub?action=timeline&channel=' + channel.uid
|
||||||
|
if (channel.after) {
|
||||||
|
url += '&after=' + channel.after;
|
||||||
|
}
|
||||||
|
if (channel.before) {
|
||||||
|
url += '&before=' + channel.before;
|
||||||
|
}
|
||||||
|
fetch(url, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + this.state.access_token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(response => {
|
||||||
|
commit('newTimeline', {channel: channel, timeline: response})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
tokenResponse({commit}, response) {
|
||||||
|
window.localStorage.setItem('login_data', JSON.stringify(response))
|
||||||
|
commit('newAccessToken', response)
|
||||||
|
},
|
||||||
|
isLoggedIn({commit}, response) {
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.log(response)
|
||||||
|
commit('newAccessToken', response)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,71 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="home">
|
<div class="home">
|
||||||
<img alt="Vue logo" src="../assets/logo.png">
|
<div class="columns">
|
||||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
<div class="column is-2">
|
||||||
|
<Channels class="channels" :channels="this.$store.state.channels">
|
||||||
|
<div slot-scope="{ channel }">
|
||||||
|
<Channel :channel="channel" @channel-selected="selectChannel"></Channel>
|
||||||
|
</div>
|
||||||
|
</Channels>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
<Timeline class="timeline" :timeline="this.$store.state.timeline" :channel="this.$store.state.channel"
|
||||||
|
@getPage="getPage"></Timeline>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// @ is an alias to /src
|
// @ is an alias to /src
|
||||||
import HelloWorld from '@/components/HelloWorld.vue'
|
import Timeline from '@/components/Timeline.vue'
|
||||||
|
import Channels from '@/components/Channels.vue'
|
||||||
|
import Channel from '@/components/Channel.vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'home',
|
name: 'home',
|
||||||
components: {
|
components: {
|
||||||
HelloWorld
|
Timeline,
|
||||||
|
Channels,
|
||||||
|
Channel
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showChannels: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
uid() {
|
||||||
|
return this.$route.params.uid || 'home';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
selectChannel(channel) {
|
||||||
|
this.$store.dispatch('fetchTimeline', channel)
|
||||||
|
this.showChannels = false
|
||||||
|
window.scrollTo({top: 0})
|
||||||
|
},
|
||||||
|
getPage(next) {
|
||||||
|
this.$store.dispatch('fetchTimeline', next)
|
||||||
|
window.scrollTo({top: 0})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
if (this.$store.state.logged_in) {
|
||||||
|
this.$store.dispatch('fetchChannels')
|
||||||
|
this.$store.dispatch('fetchTimeline', {uid: this.uid})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.timeline {
|
||||||
|
margin-top: 20px;
|
||||||
|
width: 600px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
Reference in New Issue
Block a user