refs #355 Update tag timeline with stream and lazy fetch past timeline

This commit is contained in:
AkiraFukushima 2018-05-31 18:13:38 +09:00
parent ad1f0aa662
commit 59e2fde554
3 changed files with 250 additions and 13 deletions

View File

@ -514,6 +514,46 @@ ipcMain.on('start-list-streaming', (event, obj) => {
})
})
ipcMain.on('stop-list-streaming', (event, _) => {
listStreaming.stop()
listStreaming = null
})
let tagStreaming = null
ipcMain.on('start-tag-streaming', (event, obj) => {
const account = new Account(accountDB)
account.getAccount(obj.account._id)
.catch((err) => {
log.error(err)
event.sender.send('error-start-tag-streaming', err)
})
.then((account) => {
// Stop old tag streaming
if (tagStreaming !== null) {
tagStreaming.stop()
tagStreaming = null
}
tagStreaming = new Streaming(account)
tagStreaming.start(
`/streaming/hashtag?tag=${obj.tag}`,
(update) => {
event.sender.send('update-start-tag-streaming', update)
},
(err) => {
log.error(err)
event.sender.send('error-start-tag-streaming', err)
}
)
})
})
ipcMain.on('stop-list-streaming', (event, _) => {
listStreaming.stop()
listStreaming = null
})
// sounds
ipcMain.on('fav-rt-action-sound', (event, _) => {
const preferences = new Preferences(preferencesDBPath)

View File

@ -1,11 +1,13 @@
<template>
<div id="tag">
<transition-group name="timeline" tag="div">
<div class="tag-timeline" v-for="message in timeline" v-bind:key="message.id">
<toot :message="message" v-on:update="updateToot" v-on:delete="deleteToot"></toot>
</div>
</transition-group>
</div>
<div name="tag">
<div class="unread">{{ unread.length > 0 ? unread.length : '' }}</div>
<transition-group name="timeline" tag="div">
<div class="tag-timeline" v-for="message in timeline" v-bind:key="message.id">
<toot :message="message" v-on:update="updateToot" v-on:delete="deleteToot"></toot>
</div>
</transition-group>
<div class="loading-card" v-loading="lazyLoading" :element-loading-background="backgroundColor"></div>
</div>
</template>
<script>
@ -17,29 +19,125 @@ export default {
components: { Toot },
computed: {
...mapState({
timeline: state => state.TimelineSpace.Contents.Hashtag.Tag.timeline
timeline: state => state.TimelineSpace.Contents.Hashtag.Tag.timeline,
lazyLoading: state => state.TimelineSpace.Contents.Hashtag.Tag.lazyLoading,
backgroundColor: state => state.App.theme.background_color,
heading: state => state.TimelineSpace.Contents.Hashtag.Tag.heading,
unread: state => state.TimelineSpace.Contents.Hashtag.Tag.unreadTimeline
})
},
mounted () {
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.load(this.$route.params.tag)
.then(() => {
loading.close()
})
.catch(() => {
loading.close()
})
document.getElementById('scrollable').addEventListener('scroll', this.onScroll)
},
watch: {
'$route': function () {
const loading = this.$loading({
lock: true,
text: 'Loading',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
})
this.reset()
this.load(this.$route.params.tag)
.then(() => {
loading.close()
})
.catch(() => {
loading.close()
})
}
},
beforeDestroy () {
this.$store.dispatch('TimelineSpace/Contents/Hashtag/Tag/stopStreaming')
this.reset()
},
methods: {
load (tag) {
this.$store.dispatch('TimelineSpace/Contents/Hashtag/Tag/fetch', tag)
async load (tag) {
await this.$store.dispatch('TimelineSpace/Contents/Hashtag/Tag/fetch', tag)
.catch(() => {
this.$message({
message: 'Could not fetch timeline',
type: 'error'
})
})
this.$store.dispatch('TimelineSpace/Contents/Hashtag/Tag/startStreaming', tag)
.catch(() => {
this.$message({
message: 'Could not start streaming',
type: 'error'
})
})
return true
},
updateToot (messag) {
reset () {
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/mergeTimeline')
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/archiveTimeline')
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/clearTimeline')
if (document.getElementById('scrollable') !== undefined && document.getElementById('scrollable') !== null) {
document.getElementById('scrollable').removeEventListener('scroll', this.onScroll)
document.getElementById('scrollable').scrollTop = 0
}
},
deleteToot (message) {
updateToot (toot) {
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/updateToot', toot)
},
deleteToot (toot) {
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/deleteToot', toot)
},
onScroll (event) {
if (((event.target.clientHeight + event.target.scrollTop) >= document.getElementsByName('tag')[0].clientHeight - 10) && !this.lazyloading) {
this.$store.dispatch('TimelineSpace/Contents/Hashtag/Tag/lazyFetchTimeline', {
tag: this.$route.params.tag,
last: this.timeline[this.timeline.length - 1]
})
}
// for unread control
if ((event.target.scrollTop > 10) && this.heading) {
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/changeHeading', false)
} else if ((event.target.scrollTop <= 10) && !this.heading) {
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/changeHeading', true)
this.$store.commit('TimelineSpace/Contents/Hashtag/Tag/mergeTimeline')
}
}
}
}
</script>
<style lang="scss" scoped>
.unread {
position: fixed;
right: 24px;
top: 48px;
background-color: rgba(0, 0, 0, 0.7);
color: #ffffff;
padding: 4px 8px;
border-radius: 0 0 2px 2px;
&:empty {
display: none;
}
}
.loading-card {
height: 60px;
}
.loading-card:empty {
height: 0;
}
</style>
<style src="@/assets/timeline-transition.scss"></style>

View File

@ -1,13 +1,69 @@
import { ipcRenderer } from 'electron'
import Mastodon from 'mastodon-api'
const Tag = {
namespaced: true,
state: {
timeline: []
timeline: [],
unreadTimeline: [],
lazyLoading: false,
heading: true
},
mutations: {
changeHeading (state, value) {
state.heading = value
},
appendTimeline (state, update) {
if (state.heading) {
state.timeline = [update].concat(state.timeline)
} else {
state.unreadTimeline = [update].concat(state.unreadTimeline)
}
},
updateTimeline (state, timeline) {
state.timeline = timeline
},
mergeTimeline (state) {
state.timeline = state.unreadTimeline.concat(state.timeline)
state.unreadTimeline = []
},
insertTimeline (state, messages) {
state.timeline = state.timeline.concat(messages)
},
archiveTimeline (state) {
state.timeline = state.timeline.slice(0, 40)
},
clearTimeline (state) {
state.timeline = []
state.unreadTimeline = []
},
updateToot (state, message) {
state.timeline = state.timeline.map((toot) => {
if (toot.id === message.id) {
return message
} else if (toot.reblog !== null && toot.reblog.id === message.id) {
// When user reblog/favourite a reblogged toot, target message is a original toot.
// So, a message which is received now is original toot.
const reblog = {
reblog: message
}
return Object.assign(toot, reblog)
} else {
return toot
}
})
},
deleteToot (state, message) {
state.timeline = state.timeline.filter((toot) => {
if (toot.reblog !== null && toot.reblog.id === message.id) {
return false
} else {
return toot.id !== message.id
}
})
},
changeLazyLoading (state, value) {
state.lazyLoading = value
}
},
actions: {
@ -25,6 +81,49 @@ const Tag = {
resolve(res)
})
})
},
startStreaming ({ state, commit, rootState }, tag) {
ipcRenderer.on('update-start-tag-streaming', (event, update) => {
commit('appendTimeline', update)
if (state.heading && Math.random() > 0.8) {
commit('archiveTimeline')
}
})
return new Promise((resolve, reject) => {
ipcRenderer.send('start-tag-streaming', {
tag: tag,
account: rootState.TimelineSpace.account
})
ipcRenderer.once('error-start-tag-streaming', (event, err) => {
reject(err)
})
})
},
stopStreaming ({ commit }) {
return new Promise((resolve, reject) => {
ipcRenderer.removeAllListeners('error-start-tag-streaming')
ipcRenderer.removeAllListeners('update-start-tag-streaming')
ipcRenderer.send('stop-tag-streaming')
resolve()
})
},
lazyFetchTimeline ({ state, commit, rootState }, obj) {
return new Promise((resolve, reject) => {
if (state.lazyLoading) {
return resolve()
}
commit('changeLazyLoading', true)
const client = new Mastodon(
{
access_token: rootState.TimelineSpace.account.accessToken,
api_url: rootState.TimelineSpace.account.baseURL + '/api/v1'
})
client.get(`/timelines/tag/${obj.tag}`, { max_id: obj.last.id, limit: 40 }, (err, data, res) => {
if (err) return reject(err)
commit('insertTimeline', data)
commit('changeLazyLoading', false)
})
})
}
}
}