From c79f5a0c5c64b6273e8f6a3b239bc7e2f77a1625 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Sun, 8 Apr 2018 21:27:35 +0900 Subject: [PATCH 1/7] refs #167 Create lists menu in side menu --- .../components/TimelineSpace/SideMenu.vue | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/renderer/components/TimelineSpace/SideMenu.vue b/src/renderer/components/TimelineSpace/SideMenu.vue index b7cfdca3..c93d12b5 100644 --- a/src/renderer/components/TimelineSpace/SideMenu.vue +++ b/src/renderer/components/TimelineSpace/SideMenu.vue @@ -37,6 +37,13 @@ PublicTimeline + + + #List1 + @@ -93,6 +100,22 @@ export default { border: none; margin-left: 4px; } + + .menu-item-title { + color: rgb(144, 147, 153); + cursor: default; + } + + .menu-item-title:hover { + background-color: inherit; + } + + .sub-menu { + padding-left: 45px !important; + height: 32px; + line-height: 32px; + font-size: 14px; + } } } From 99eac1d958f699ca822abec27f71927c6c848aec Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Sun, 8 Apr 2018 22:09:28 +0900 Subject: [PATCH 2/7] refs #167 Fetch user's lists --- src/renderer/components/TimelineSpace.vue | 51 ++++++++++---------- src/renderer/store/TimelineSpace/SideMenu.js | 26 +++++++++- 2 files changed, 49 insertions(+), 28 deletions(-) diff --git a/src/renderer/components/TimelineSpace.vue b/src/renderer/components/TimelineSpace.vue index 312b0634..28eacd6f 100644 --- a/src/renderer/components/TimelineSpace.vue +++ b/src/renderer/components/TimelineSpace.vue @@ -50,37 +50,36 @@ export default { await this.clear() this.$store.dispatch('TimelineSpace/watchShortcutEvents') - try { - const account = await this.$store.dispatch('TimelineSpace/localAccount', this.$route.params.id) - try { - await this.$store.dispatch('TimelineSpace/fetchHomeTimeline', account) - } catch (err) { - this.$message({ - message: 'Could not fetch timeline', - type: 'error' - }) - } - try { - await this.$store.dispatch('TimelineSpace/fetchNotifications', account) - } catch (err) { - this.$message({ - message: 'Could not fetch notification', - type: 'error' - }) - } - this.$store.dispatch('TimelineSpace/startUserStreaming', account) - .catch(() => { - this.$message({ - message: 'Failed to start streaming', - type: 'error' - }) - }) - } catch (err) { + const account = await this.$store.dispatch('TimelineSpace/localAccount', this.$route.params.id).catch(() => { this.$message({ message: 'Could not find account', type: 'error' }) + }) + try { + await this.$store.dispatch('TimelineSpace/fetchHomeTimeline', account) + } catch (err) { + this.$message({ + message: 'Could not fetch timeline', + type: 'error' + }) } + try { + await this.$store.dispatch('TimelineSpace/fetchNotifications', account) + } catch (err) { + this.$message({ + message: 'Could not fetch notification', + type: 'error' + }) + } + this.$store.dispatch('TimelineSpace/SideMenu/fetchLists', account) + this.$store.dispatch('TimelineSpace/startUserStreaming', account) + .catch(() => { + this.$message({ + message: 'Failed to start streaming', + type: 'error' + }) + }) } } } diff --git a/src/renderer/store/TimelineSpace/SideMenu.js b/src/renderer/store/TimelineSpace/SideMenu.js index 4a278f5b..b9471b4c 100644 --- a/src/renderer/store/TimelineSpace/SideMenu.js +++ b/src/renderer/store/TimelineSpace/SideMenu.js @@ -1,8 +1,11 @@ +import Mastodon from 'mastodon-api' + const SideMenu = { namespaced: true, state: { unreadHomeTimeline: false, - unreadNotifications: false + unreadNotifications: false, + lists: [] }, mutations: { changeUnreadHomeTimeline (state, value) { @@ -10,9 +13,28 @@ const SideMenu = { }, changeUnreadNotifications (state, value) { state.unreadNotifications = value + }, + updateLists (state, lists) { + state.lists = lists } }, - actions: {} + actions: { + fetchLists ({ commit }, account) { + return new Promise((resolve, reject) => { + const client = new Mastodon( + { + access_token: account.accessToken, + api_url: account.baseURL + '/api/v1' + } + ) + client.get('/lists', {}, (err, data, res) => { + if (err) return reject(err) + commit('updateLists', data) + resolve(res) + }) + }) + } + } } export default SideMenu From 5254c115025d741372f15f3f7ab51b851c055466 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Mon, 9 Apr 2018 08:43:11 +0900 Subject: [PATCH 3/7] refs #167 Update header menu in user's list timeline --- .../TimelineSpace/Contents/Lists.vue | 14 +++++++ .../components/TimelineSpace/HeaderMenu.vue | 38 +++++++++++++++---- .../components/TimelineSpace/SideMenu.vue | 11 ++++-- src/renderer/router/index.js | 5 +++ src/renderer/store/TimelineSpace.js | 2 + .../store/TimelineSpace/HeaderMenu.js | 32 ++++++++++++++++ 6 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 src/renderer/components/TimelineSpace/Contents/Lists.vue create mode 100644 src/renderer/store/TimelineSpace/HeaderMenu.js diff --git a/src/renderer/components/TimelineSpace/Contents/Lists.vue b/src/renderer/components/TimelineSpace/Contents/Lists.vue new file mode 100644 index 00000000..e8b4dbae --- /dev/null +++ b/src/renderer/components/TimelineSpace/Contents/Lists.vue @@ -0,0 +1,14 @@ + + + + + diff --git a/src/renderer/components/TimelineSpace/HeaderMenu.vue b/src/renderer/components/TimelineSpace/HeaderMenu.vue index 52060b7b..0fe43850 100644 --- a/src/renderer/components/TimelineSpace/HeaderMenu.vue +++ b/src/renderer/components/TimelineSpace/HeaderMenu.vue @@ -1,27 +1,51 @@ diff --git a/src/renderer/components/TimelineSpace/Modals/NewToot.vue b/src/renderer/components/TimelineSpace/Modals/NewToot.vue index 7f296a1d..6989b94a 100644 --- a/src/renderer/components/TimelineSpace/Modals/NewToot.vue +++ b/src/renderer/components/TimelineSpace/Modals/NewToot.vue @@ -10,7 +10,7 @@
-
+
diff --git a/src/renderer/components/TimelineSpace/SideMenu.vue b/src/renderer/components/TimelineSpace/SideMenu.vue index 5bb57cd5..82051618 100644 --- a/src/renderer/components/TimelineSpace/SideMenu.vue +++ b/src/renderer/components/TimelineSpace/SideMenu.vue @@ -42,7 +42,7 @@ Lists diff --git a/src/renderer/router/index.js b/src/renderer/router/index.js index 21d342b7..4598e4a3 100644 --- a/src/renderer/router/index.js +++ b/src/renderer/router/index.js @@ -70,7 +70,8 @@ export default new Router({ { path: 'lists/:list_id', name: 'lists', - component: require('@/components/TimelineSpace/Contents/Lists').default + component: require('@/components/TimelineSpace/Contents/Lists').default, + props: true } ] } diff --git a/src/renderer/store/TimelineSpace/Contents.js b/src/renderer/store/TimelineSpace/Contents.js index f50a0abb..4c0acb8b 100644 --- a/src/renderer/store/TimelineSpace/Contents.js +++ b/src/renderer/store/TimelineSpace/Contents.js @@ -4,6 +4,7 @@ import Notifications from './Contents/Notifications' import Favourites from './Contents/Favourites' import Local from './Contents/Local' import Public from './Contents/Public' +import Lists from './Contents/Lists' import Cards from './Contents/Cards' const Contents = { @@ -15,6 +16,7 @@ const Contents = { Favourites, Local, Public, + Lists, Cards } } diff --git a/src/renderer/store/TimelineSpace/Contents/Lists.js b/src/renderer/store/TimelineSpace/Contents/Lists.js new file mode 100644 index 00000000..5bf52c78 --- /dev/null +++ b/src/renderer/store/TimelineSpace/Contents/Lists.js @@ -0,0 +1,31 @@ +import Mastodon from 'mastodon-api' + +const Lists = { + namespaced: true, + state: { + timeline: [] + }, + mutations: { + updateTimeline (state, timeline) { + state.timeline = timeline + } + }, + actions: { + fetchTimeline ({ state, commit, rootState }, listID) { + return new Promise((resolve, reject) => { + const client = new Mastodon( + { + access_token: rootState.TimelineSpace.account.accessToken, + api_url: rootState.TimelineSpace.account.baseURL + '/api/v1' + }) + client.get(`/timelines/list/${listID}`, { limit: 40 }, (err, data, res) => { + if (err) return reject(err) + commit('updateTimeline', data) + resolve(res) + }) + }) + } + } +} + +export default Lists From b860ccca86266fd3e80adfc5fcb2ce4ac5b16709 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Mon, 9 Apr 2018 22:05:15 +0900 Subject: [PATCH 5/7] refs #167 Update toot when user favourite/reblog a toot in list timeline --- .../components/TimelineSpace/Contents/Lists.vue | 5 ++++- .../store/TimelineSpace/Contents/Lists.js | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/renderer/components/TimelineSpace/Contents/Lists.vue b/src/renderer/components/TimelineSpace/Contents/Lists.vue index 00e725ad..84ddab59 100644 --- a/src/renderer/components/TimelineSpace/Contents/Lists.vue +++ b/src/renderer/components/TimelineSpace/Contents/Lists.vue @@ -1,7 +1,7 @@ @@ -46,6 +46,9 @@ export default { type: 'error' }) }) + }, + updateToot (message) { + this.$store.commit('TimelineSpace/Contents/Lists/updateToot', message) } } } diff --git a/src/renderer/store/TimelineSpace/Contents/Lists.js b/src/renderer/store/TimelineSpace/Contents/Lists.js index 5bf52c78..7cb6e9c0 100644 --- a/src/renderer/store/TimelineSpace/Contents/Lists.js +++ b/src/renderer/store/TimelineSpace/Contents/Lists.js @@ -8,6 +8,22 @@ const Lists = { mutations: { updateTimeline (state, timeline) { state.timeline = timeline + }, + 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 + } + }) } }, actions: { From abcef61a714d54c987c7cda808a0ca760c91a7b7 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Mon, 9 Apr 2018 23:08:52 +0900 Subject: [PATCH 6/7] refs #167 Add lazyLoading in list timeline --- .../TimelineSpace/Contents/Lists.vue | 28 +++++++++++++++++-- .../store/TimelineSpace/Contents/Lists.js | 27 +++++++++++++++++- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/src/renderer/components/TimelineSpace/Contents/Lists.vue b/src/renderer/components/TimelineSpace/Contents/Lists.vue index 84ddab59..b6a54b8c 100644 --- a/src/renderer/components/TimelineSpace/Contents/Lists.vue +++ b/src/renderer/components/TimelineSpace/Contents/Lists.vue @@ -1,8 +1,9 @@ @@ -16,17 +17,24 @@ export default { components: { Toot }, computed: { ...mapState({ - timeline: state => state.TimelineSpace.Contents.Lists.timeline + timeline: state => state.TimelineSpace.Contents.Lists.timeline, + lazyLoading: state => state.TimelineSpace.Contents.Lists.lazyLoading }) }, created () { this.load() + document.getElementById('scrollable').addEventListener('scroll', this.onScroll) }, watch: { list_id: function () { this.load() } }, + destroyed () { + if (document.getElementById('scrollable') !== undefined && document.getElementById('scrollable') !== null) { + document.getElementById('scrollable').removeEventListener('scroll', this.onScroll) + } + }, methods: { load () { const loading = this.$loading({ @@ -49,10 +57,26 @@ export default { }, updateToot (message) { this.$store.commit('TimelineSpace/Contents/Lists/updateToot', message) + }, + onScroll (event) { + console.log(document.getElementsByName('lists')) + if (((event.target.clientHeight + event.target.scrollTop) >= document.getElementsByName('lists')[0].clientHeight - 10) && !this.lazyloading) { + this.$store.dispatch('TimelineSpace/Contents/Lists/lazyFetchTimeline', { + list_id: this.list_id, + last: this.timeline[this.timeline.length - 1] + }) + } } } } diff --git a/src/renderer/store/TimelineSpace/Contents/Lists.js b/src/renderer/store/TimelineSpace/Contents/Lists.js index 7cb6e9c0..4f813c7b 100644 --- a/src/renderer/store/TimelineSpace/Contents/Lists.js +++ b/src/renderer/store/TimelineSpace/Contents/Lists.js @@ -3,12 +3,16 @@ import Mastodon from 'mastodon-api' const Lists = { namespaced: true, state: { - timeline: [] + timeline: [], + lazyLoading: false }, mutations: { updateTimeline (state, timeline) { state.timeline = timeline }, + insertTimeline (state, messages) { + state.timeline = state.timeline.concat(messages) + }, updateToot (state, message) { state.timeline = state.timeline.map((toot) => { if (toot.id === message.id) { @@ -24,6 +28,9 @@ const Lists = { return toot } }) + }, + changeLazyLoading (state, value) { + state.lazyLoading = value } }, actions: { @@ -40,6 +47,24 @@ const Lists = { resolve(res) }) }) + }, + 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/list/${obj.list_id}`, { max_id: obj.last.id, limit: 40 }, (err, data, res) => { + if (err) return reject(err) + commit('insertTimeline', data) + commit('changeLazyLoading', false) + }) + }) } } } From 32b8ab9190b216ab4c7ee06045a6df63af945a77 Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Mon, 9 Apr 2018 23:43:36 +0900 Subject: [PATCH 7/7] refs #167 Streaming update in list timeline --- src/main/index.js | 30 ++++++++++++ .../TimelineSpace/Contents/Lists.vue | 46 +++++++++++++------ .../store/TimelineSpace/Contents/Lists.js | 26 +++++++++++ 3 files changed, 89 insertions(+), 13 deletions(-) diff --git a/src/main/index.js b/src/main/index.js index c3a560e2..b3f0da66 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -452,6 +452,36 @@ ipcMain.on('stop-public-streaming', (event, _) => { publicStreaming = null }) +let listStreaming = null + +ipcMain.on('start-list-streaming', (event, obj) => { + const account = new Account(accountDB) + account.getAccount(obj.account._id) + .catch((err) => { + log.error(err) + event.sender.send('error-start-list-streaming', err) + }) + .then((account) => { + // Stop old list streaming + if (listStreaming !== null) { + listStreaming.stop() + listStreaming = null + } + + listStreaming = new Streaming(account) + listStreaming.start( + `/streaming/list?list=${obj.list_id}`, + (update) => { + event.sender.send('update-start-list-streaming', update) + }, + (err) => { + log.error(err) + event.sendeer.send('error-start-list-streaming', err) + } + ) + }) +}) + // sounds ipcMain.on('fav-rt-action-sound', (event, _) => { const preferences = new Preferences(preferencesDBPath) diff --git a/src/renderer/components/TimelineSpace/Contents/Lists.vue b/src/renderer/components/TimelineSpace/Contents/Lists.vue index b6a54b8c..57522ccb 100644 --- a/src/renderer/components/TimelineSpace/Contents/Lists.vue +++ b/src/renderer/components/TimelineSpace/Contents/Lists.vue @@ -22,44 +22,64 @@ export default { }) }, created () { + const loading = this.$loading({ + lock: true, + text: 'Loading', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }) this.load() + .then(() => { + loading.close() + }) document.getElementById('scrollable').addEventListener('scroll', this.onScroll) }, watch: { list_id: function () { + const loading = this.$loading({ + lock: true, + text: 'Loading', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }) this.load() + .then(() => { + loading.close() + }) } }, + beforeDestroy () { + this.$store.dispatch('TimelineSpace/Contents/Lists/stopStreaming') + }, destroyed () { if (document.getElementById('scrollable') !== undefined && document.getElementById('scrollable') !== null) { document.getElementById('scrollable').removeEventListener('scroll', this.onScroll) } }, methods: { - load () { - const loading = this.$loading({ - lock: true, - text: 'Loading', - spinner: 'el-icon-loading', - background: 'rgba(0, 0, 0, 0.7)' - }) - this.$store.dispatch('TimelineSpace/Contents/Lists/fetchTimeline', this.list_id) - .then(() => { - loading.close() + async load () { + await this.$store.dispatch('TimelineSpace/Contents/Lists/stopStreaming') + try { + await this.$store.dispatch('TimelineSpace/Contents/Lists/fetchTimeline', this.list_id) + } catch (err) { + this.$message({ + message: 'Failed to get timeline', + type: 'error' }) + } + this.$store.dispatch('TimelineSpace/Contents/Lists/startStreaming', this.list_id) .catch(() => { - loading.close() this.$message({ - message: 'Failed to get timeline', + message: 'Failed to start streaming', type: 'error' }) }) + return 'started' }, updateToot (message) { this.$store.commit('TimelineSpace/Contents/Lists/updateToot', message) }, onScroll (event) { - console.log(document.getElementsByName('lists')) if (((event.target.clientHeight + event.target.scrollTop) >= document.getElementsByName('lists')[0].clientHeight - 10) && !this.lazyloading) { this.$store.dispatch('TimelineSpace/Contents/Lists/lazyFetchTimeline', { list_id: this.list_id, diff --git a/src/renderer/store/TimelineSpace/Contents/Lists.js b/src/renderer/store/TimelineSpace/Contents/Lists.js index 4f813c7b..21cd93a5 100644 --- a/src/renderer/store/TimelineSpace/Contents/Lists.js +++ b/src/renderer/store/TimelineSpace/Contents/Lists.js @@ -1,3 +1,4 @@ +import { ipcRenderer } from 'electron' import Mastodon from 'mastodon-api' const Lists = { @@ -7,6 +8,9 @@ const Lists = { lazyLoading: false }, mutations: { + appendTimeline (state, update) { + state.timeline = [update].concat(state.timeline) + }, updateTimeline (state, timeline) { state.timeline = timeline }, @@ -48,6 +52,28 @@ const Lists = { }) }) }, + startStreaming ({ state, commit, rootState }, listID) { + ipcRenderer.on('update-start-list-streaming', (event, update) => { + commit('appendTimeline', update) + }) + return new Promise((resolve, reject) => { + ipcRenderer.send('start-list-streaming', { + list_id: listID, + account: rootState.TimelineSpace.account + }) + ipcRenderer.once('error-start-list-streaming', (event, err) => { + reject(err) + }) + }) + }, + stopStreaming ({ commit }) { + return new Promise((resolve, reject) => { + ipcRenderer.removeAllListeners('error-start-list-streaming') + ipcRenderer.removeAllListeners('update-start-list-streaming') + ipcRenderer.send('stop-list-streaming') + resolve() + }) + }, lazyFetchTimeline ({ state, commit, rootState }, obj) { return new Promise((resolve, reject) => { if (state.lazyLoading) {