diff --git a/src/config/locales/de/translation.json b/src/config/locales/de/translation.json index 8188c443..b62b8605 100644 --- a/src/config/locales/de/translation.json +++ b/src/config/locales/de/translation.json @@ -73,6 +73,7 @@ "reload": "Neu laden" }, "settings": { + "title": "Settings", "general": { "title": "Settings", "toot": { diff --git a/src/config/locales/de/translation.missing.json b/src/config/locales/de/translation.missing.json index d99b79c3..72622606 100644 --- a/src/config/locales/de/translation.missing.json +++ b/src/config/locales/de/translation.missing.json @@ -1,5 +1,17 @@ { "side_menu": { "direct": "Direct messages" + }, + "settings": { + "timeline": { + "title": "Timeline", + "unread_notification": { + "title": "Unread Notification", + "description": "Customize unread notifications for each timelines.", + "direct": "Direct Messages", + "local": "Local Timeline", + "public": "Public Timeline" + } + } } -} \ No newline at end of file +} diff --git a/src/config/locales/en/translation.json b/src/config/locales/en/translation.json index afb61b17..edc62f66 100644 --- a/src/config/locales/en/translation.json +++ b/src/config/locales/en/translation.json @@ -90,6 +90,16 @@ "description": "Mark medias as sensitive by default" } } + }, + "timeline": { + "title": "Timeline", + "unread_notification": { + "title": "Unread Notification", + "description": "Customize unread notifications for each timelines.", + "direct": "Direct Messages", + "local": "Local Timeline", + "public": "Public Timeline" + } } }, "preferences": { diff --git a/src/config/locales/fr/translation.missing.json b/src/config/locales/fr/translation.missing.json index d99b79c3..72622606 100644 --- a/src/config/locales/fr/translation.missing.json +++ b/src/config/locales/fr/translation.missing.json @@ -1,5 +1,17 @@ { "side_menu": { "direct": "Direct messages" + }, + "settings": { + "timeline": { + "title": "Timeline", + "unread_notification": { + "title": "Unread Notification", + "description": "Customize unread notifications for each timelines.", + "direct": "Direct Messages", + "local": "Local Timeline", + "public": "Public Timeline" + } + } } -} \ No newline at end of file +} diff --git a/src/config/locales/ja/translation.json b/src/config/locales/ja/translation.json index d2d1cf6f..8521bc5c 100644 --- a/src/config/locales/ja/translation.json +++ b/src/config/locales/ja/translation.json @@ -47,6 +47,7 @@ "expand": "拡大", "home": "ホーム", "notification": "通知", + "direct": "ダイレクトメッセージ", "favourite": "お気に入り", "local": "ローカル", "public": "連合", @@ -75,6 +76,7 @@ "settings": { "title": "設定", "general": { + "title": "一般", "toot": { "title": "トゥート", "visibility": { @@ -88,6 +90,16 @@ "description": "メディアを常に閲覧注意として投稿する" } } + }, + "timeline": { + "title": "タイムライン", + "unread_notification": { + "title": "未読管理", + "description": "未読管理をするタイムラインを設定", + "direct": "ダイレクトメッセージ", + "local": "ローカル", + "public": "連合" + } } }, "preferences": { diff --git a/src/config/locales/ko/translation.missing.json b/src/config/locales/ko/translation.missing.json index d99b79c3..72622606 100644 --- a/src/config/locales/ko/translation.missing.json +++ b/src/config/locales/ko/translation.missing.json @@ -1,5 +1,17 @@ { "side_menu": { "direct": "Direct messages" + }, + "settings": { + "timeline": { + "title": "Timeline", + "unread_notification": { + "title": "Unread Notification", + "description": "Customize unread notifications for each timelines.", + "direct": "Direct Messages", + "local": "Local Timeline", + "public": "Public Timeline" + } + } } -} \ No newline at end of file +} diff --git a/src/config/locales/pl/translation.missing.json b/src/config/locales/pl/translation.missing.json index d99b79c3..72622606 100644 --- a/src/config/locales/pl/translation.missing.json +++ b/src/config/locales/pl/translation.missing.json @@ -1,5 +1,17 @@ { "side_menu": { "direct": "Direct messages" + }, + "settings": { + "timeline": { + "title": "Timeline", + "unread_notification": { + "title": "Unread Notification", + "description": "Customize unread notifications for each timelines.", + "direct": "Direct Messages", + "local": "Local Timeline", + "public": "Public Timeline" + } + } } -} \ No newline at end of file +} diff --git a/src/constants/unreadNotification.js b/src/constants/unreadNotification.js new file mode 100644 index 00000000..929c744a --- /dev/null +++ b/src/constants/unreadNotification.js @@ -0,0 +1,11 @@ +export default { + Direct: { + default: false + }, + Local: { + default: true + }, + Public: { + default: false + } +} diff --git a/src/main/index.js b/src/main/index.js index d4f584fc..3e734c33 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -17,6 +17,7 @@ import StreamingManager from './streaming_manager' import Preferences from './preferences' import Fonts from './fonts' import Hashtags from './hashtags' +import UnreadNotification from './unread_notification' import i18n from '../config/i18n' import Language from '../constants/language' @@ -70,6 +71,13 @@ let hashtagsDB = new Datastore({ autoload: true }) +const unreadNotificationDBPath = process.env.NODE_ENV === 'production' + ? userData + '/db/unread_notification.db' + : 'unread_notification.db' +const unreadNotification = new UnreadNotification(unreadNotificationDBPath) +unreadNotification.initialize() + .catch(err => log.error(err)) + const preferencesDBPath = process.env.NODE_ENV === 'production' ? userData + './db/preferences.json' : 'preferences.json' @@ -762,6 +770,32 @@ ipcMain.on('list-fonts', (event, _) => { }) }) +// Unread notifications +ipcMain.on('get-unread-notification', (event, accountID) => { + unreadNotification.findOne({ + accountID: accountID + }) + .then(doc => { + event.sender.send('response-get-unread-notification', doc) + }) + .catch(err => { + console.warn(err) + event.sender.send('error-get-unread-notification', err) + }) +}) + +ipcMain.on('update-unread-notification', (event, obj) => { + const { accountID } = obj + unreadNotification.insertOrUpdate(accountID, obj) + .then(_ => { + event.sender.send('response-update-unread-notification', true) + }) + .catch(err => { + console.error(err) + event.sender.send('error-update-unread-notification', err) + }) +}) + // Application control ipcMain.on('relaunch', (event, _) => { app.relaunch() diff --git a/src/main/unread_notification.js b/src/main/unread_notification.js new file mode 100644 index 00000000..a8a0e3b1 --- /dev/null +++ b/src/main/unread_notification.js @@ -0,0 +1,63 @@ +import empty from 'is-empty' +import Datastore from 'nedb' + +export default class UnreadNotification { + constructor (path) { + this.db = new Datastore({ + filename: path, + autoload: true + }) + } + + async initialize () { + await this.updateUnique() + } + + updateUnique () { + return new Promise((resolve, reject) => { + // At first, remove old index. + this.db.removeIndex('accountID', (err) => { + if (err) reject(err) + // Add unique index. + this.db.ensureIndex({ fieldName: 'accountID', unique: true, sparse: true }, (err) => { + if (err) reject(err) + resolve(null) + }) + }) + }) + } + + insertOrUpdate (accountID, obj) { + return new Promise((resolve, reject) => { + this.db.update( + { + accountID: accountID + }, + obj, + { + upsert: true + }, + (err, num) => { + if (err) return reject(err) + resolve(num) + }) + }) + } + + findOne (obj) { + return new Promise((resolve, reject) => { + this.db.findOne(obj, (err, doc) => { + if (err) return reject(err) + if (empty(doc)) return reject(new EmptyRecordError('empty')) + resolve(doc) + }) + }) + } +} + +class EmptyRecordError extends Error { + constructor (msg) { + super(msg) + this.name = 'EmptyRecordError' + } +} diff --git a/src/renderer/components/Settings.vue b/src/renderer/components/Settings.vue index a5fd1b9b..b21a150b 100644 --- a/src/renderer/components/Settings.vue +++ b/src/renderer/components/Settings.vue @@ -23,6 +23,10 @@ {{ $t('settings.general.title') }} + + + {{ $t('settings.timeline.title') }} + @@ -44,6 +48,7 @@ export default { }) }, created () { + this.$store.commit('Settings/changeAccountID', this.id()) this.$router.push(`/${this.id()}/settings/general`) }, methods: { diff --git a/src/renderer/components/Settings/Timeline.vue b/src/renderer/components/Settings/Timeline.vue new file mode 100644 index 00000000..cfee03fb --- /dev/null +++ b/src/renderer/components/Settings/Timeline.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/src/renderer/components/TimelineSpace.vue b/src/renderer/components/TimelineSpace.vue index 03e42da9..1b21b5ce 100644 --- a/src/renderer/components/TimelineSpace.vue +++ b/src/renderer/components/TimelineSpace.vue @@ -72,20 +72,12 @@ export default { window.removeEventListener('dragleave', this.onDragLeave) window.removeEventListener('dragover', this.onDragOver) window.removeEventListener('drop', this.handleDrop) - this.$store.dispatch('TimelineSpace/stopUserStreaming') - this.$store.dispatch('TimelineSpace/unbindUserStreaming') - this.$store.dispatch('TimelineSpace/stopDirectMessagesStreaming') - this.$store.dispatch('TimelineSpace/unbindDirectMessagesStreaming') - this.$store.dispatch('TimelineSpace/stopLocalStreaming') - this.$store.dispatch('TimelineSpace/unbindLocalStreaming') + this.$store.dispatch('TimelineSpace/stopStreamings') }, methods: { async clear () { await this.$store.dispatch('TimelineSpace/clearAccount') - await this.$store.commit('TimelineSpace/Contents/Home/clearTimeline') - await this.$store.commit('TimelineSpace/Contents/Local/clearTimeline') - await this.$store.commit('TimelineSpace/Contents/DirectMessages/clearTimeline') - await this.$store.commit('TimelineSpace/Contents/Notifications/clearNotifications') + this.$store.dispatch('TimelineSpace/clearContentsTimelines') await this.$store.dispatch('TimelineSpace/removeShortcutEvents') await this.$store.dispatch('TimelineSpace/clearUnread') return 'clear' @@ -100,44 +92,23 @@ export default { type: 'error' }) }) - try { - await this.$store.dispatch('TimelineSpace/Contents/Home/fetchTimeline', account) - } catch (err) { - this.$message({ - message: this.$t('message.timeline_fetch_error'), - type: 'error' - }) - } - try { - await this.$store.dispatch('TimelineSpace/Contents/Notifications/fetchNotifications', account) - } catch (err) { - this.$message({ - message: this.$t('message.notification_fetch_error'), - type: 'error' - }) - } - try { - await this.$store.dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline', account) - await this.$store.dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', account) - } catch (err) { - this.$message({ - message: this.$t('message.timeline_fetch_error'), - type: 'error' - }) - } this.$store.dispatch('TimelineSpace/SideMenu/fetchLists', account) - this.$store.dispatch('TimelineSpace/bindUserStreaming', account) - this.$store.dispatch('TimelineSpace/startUserStreaming', account) - .catch(() => { + await this.$store.dispatch('TimelineSpace/loadUnreadNotification', this.$route.params.id) + + // Load timelines + await this.$store.dispatch('TimelineSpace/fetchContentsTimelines', account) + .catch(_ => { this.$message({ - message: this.$t('message.start_streaming_error'), + message: this.$t('message.timeline_fetch_error'), type: 'error' }) }) - this.$store.dispatch('TimelineSpace/bindLocalStreaming', account) - this.$store.dispatch('TimelineSpace/startLocalStreaming', account) - this.$store.dispatch('TimelineSpace/bindDirectMessagesStreaming', account) - this.$store.dispatch('TimelineSpace/startDirectMessagesStreaming', account) + + // Bind streamings + await this.$store.dispatch('TimelineSpace/bindStreamings', account) + // Start streamings + this.$store.dispatch('TimelineSpace/startStreamings', account) + this.$store.dispatch('TimelineSpace/fetchEmojis', account) this.$store.dispatch('TimelineSpace/fetchInstance', account) }, diff --git a/src/renderer/components/TimelineSpace/Contents/DirectMessages.vue b/src/renderer/components/TimelineSpace/Contents/DirectMessages.vue index 43df7ef3..4f3656d8 100644 --- a/src/renderer/components/TimelineSpace/Contents/DirectMessages.vue +++ b/src/renderer/components/TimelineSpace/Contents/DirectMessages.vue @@ -44,15 +44,18 @@ export default { } }, computed: { + ...mapState('TimelineSpace/Contents/DirectMessages', { + timeline: state => state.timeline, + lazyLoading: state => state.lazyLoading, + heading: state => state.heading, + unread: state => state.unreadTimeline, + filter: state => state.filter + }), ...mapState({ openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar, backgroundColor: state => state.App.theme.background_color, startReload: state => state.TimelineSpace.HeaderMenu.reload, - timeline: state => state.TimelineSpace.Contents.DirectMessages.timeline, - lazyLoading: state => state.TimelineSpace.Contents.DirectMessages.lazyLoading, - heading: state => state.TimelineSpace.Contents.DirectMessages.heading, - unread: state => state.TimelineSpace.Contents.DirectMessages.unreadTimeline, - filter: state => state.TimelineSpace.Contents.DirectMessages.filter + unreadNotification: state => state.TimelineSpace.unreadNotification }), ...mapGetters('TimelineSpace/Modals', [ 'modalOpened' @@ -69,19 +72,36 @@ export default { return currentIndex === -1 } }, - mounted () { + async mounted () { + this.$store.commit('TimelineSpace/changeLoading', true) this.$store.commit('TimelineSpace/SideMenu/changeUnreadDirectMessagesTimeline', false) document.getElementById('scrollable').addEventListener('scroll', this.onScroll) + if (!this.unreadNotification.direct) { + await this.initialize() + .catch(_ => { + this.$store.commit('TimelineSpace/changeLoading', false) + }) + } + this.$store.commit('TimelineSpace/changeLoading', false) }, beforeUpdate () { if (this.$store.state.TimelineSpace.SideMenu.unreadDirectMessagesTimeline && this.heading) { this.$store.commit('TimelineSpace/SideMenu/changeUnreadDirectMessagesTimeline', false) } }, + beforeDestroy () { + if (!this.unreadNotification.direct) { + this.$store.dispatch('TimelineSpace/stopDirectMessagesStreaming') + this.$store.dispatch('TimelineSpace/unbindDirectMessagesStreaming') + } + }, destroyed () { this.$store.commit('TimelineSpace/Contents/DirectMessages/changeHeading', true) this.$store.commit('TimelineSpace/Contents/DirectMessages/mergeTimeline') this.$store.commit('TimelineSpace/Contents/DirectMessages/archiveTimeline') + if (!this.unreadNotification.direct) { + this.$store.commit('TimelineSpace/Contents/DirectMessages/clearTimeline') + } if (document.getElementById('scrollable') !== undefined && document.getElementById('scrollable') !== null) { document.getElementById('scrollable').removeEventListener('scroll', this.onScroll) document.getElementById('scrollable').scrollTop = 0 @@ -106,6 +126,17 @@ export default { } }, methods: { + async initialize () { + await this.$store.dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline') + .catch(_ => { + this.$message({ + message: this.$t('message.timeline_fetch_error'), + type: 'error' + }) + }) + await this.$store.dispatch('TimelineSpace/bindDirectMessagesStreaming') + this.$store.dispatch('TimelineSpace/startDirectMessagesStreaming') + }, onScroll (event) { // for lazyLoading if (((event.target.clientHeight + event.target.scrollTop) >= document.getElementById('directmessages').clientHeight - 10) && !this.lazyloading) { diff --git a/src/renderer/components/TimelineSpace/Contents/Local.vue b/src/renderer/components/TimelineSpace/Contents/Local.vue index a1064d4c..057cbe49 100644 --- a/src/renderer/components/TimelineSpace/Contents/Local.vue +++ b/src/renderer/components/TimelineSpace/Contents/Local.vue @@ -44,15 +44,18 @@ export default { } }, computed: { + ...mapState('TimelineSpace/Contents/Local', { + timeline: state => state.timeline, + lazyLoading: state => state.lazyLoading, + heading: state => state.heading, + unread: state => state.unreadTimeline, + filter: state => state.filter + }), ...mapState({ openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar, backgroundColor: state => state.App.theme.background_color, startReload: state => state.TimelineSpace.HeaderMenu.reload, - timeline: state => state.TimelineSpace.Contents.Local.timeline, - lazyLoading: state => state.TimelineSpace.Contents.Local.lazyLoading, - heading: state => state.TimelineSpace.Contents.Local.heading, - unread: state => state.TimelineSpace.Contents.Local.unreadTimeline, - filter: state => state.TimelineSpace.Contents.Local.filter + unreadNotification: state => state.TimelineSpace.unreadNotification }), ...mapGetters('TimelineSpace/Modals', [ 'modalOpened' @@ -69,19 +72,36 @@ export default { return currentIndex === -1 } }, - mounted () { + async mounted () { + this.$store.commit('TimelineSpace/changeLoading', true) this.$store.commit('TimelineSpace/SideMenu/changeUnreadLocalTimeline', false) document.getElementById('scrollable').addEventListener('scroll', this.onScroll) + if (!this.unreadNotification.local) { + await this.initialize() + .finally(_ => { + this.$store.commit('TimelineSpace/changeLoading', false) + }) + } + this.$store.commit('TimelineSpace/changeLoading', false) }, beforeUpdate () { if (this.$store.state.TimelineSpace.SideMenu.unreadLocalTimeline && this.heading) { this.$store.commit('TimelineSpace/SideMenu/changeUnreadLocalTimeline', false) } }, + beforeDestroy () { + if (!this.unreadNotification.local) { + this.$store.dispatch('TimelineSpace/stopLocalStreaming') + this.$store.dispatch('TimelineSpace/unbindLocalStreaming') + } + }, destroyed () { this.$store.commit('TimelineSpace/Contents/Local/changeHeading', true) this.$store.commit('TimelineSpace/Contents/Local/mergeTimeline') this.$store.commit('TimelineSpace/Contents/Local/archiveTimeline') + if (!this.unreadNotification.local) { + this.$store.commit('TimelineSpace/Contents/Local/clearTimeline') + } if (document.getElementById('scrollable') !== undefined && document.getElementById('scrollable') !== null) { document.getElementById('scrollable').removeEventListener('scroll', this.onScroll) document.getElementById('scrollable').scrollTop = 0 @@ -106,6 +126,17 @@ export default { } }, methods: { + async initialize () { + await this.$store.dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline') + .catch(_ => { + this.$message({ + message: this.$t('message.timeline_fetch_error'), + type: 'error' + }) + }) + await this.$store.dispatch('TimelineSpace/bindLocalStreaming') + this.$store.dispatch('TimelineSpace/startLocalStreaming') + }, updateToot (message) { this.$store.commit('TimelineSpace/Contents/Local/updateToot', message) }, diff --git a/src/renderer/components/TimelineSpace/Contents/Notifications.vue b/src/renderer/components/TimelineSpace/Contents/Notifications.vue index b0bc0066..a6137f67 100644 --- a/src/renderer/components/TimelineSpace/Contents/Notifications.vue +++ b/src/renderer/components/TimelineSpace/Contents/Notifications.vue @@ -128,15 +128,7 @@ export default { async reload () { this.$store.commit('TimelineSpace/changeLoading', true) try { - const account = await this.reloadable() - await this.$store.dispatch('TimelineSpace/Contents/Notifications/fetchNotifications', account) - .catch(() => { - this.$message({ - message: this.$t('message.notification_fetch_error'), - type: 'error' - }) - }) - + await this.reloadable() this.$store.dispatch('TimelineSpace/Contents/Notifications/resetBadge') } finally { this.$store.commit('TimelineSpace/changeLoading', false) diff --git a/src/renderer/components/TimelineSpace/Contents/Public.vue b/src/renderer/components/TimelineSpace/Contents/Public.vue index d26f8aae..b9653dcd 100644 --- a/src/renderer/components/TimelineSpace/Contents/Public.vue +++ b/src/renderer/components/TimelineSpace/Contents/Public.vue @@ -44,15 +44,18 @@ export default { } }, computed: { + ...mapState('TimelineSpace/Contents/Public', { + timeline: state => state.timeline, + lazyLoading: state => state.lazyLoading, + heading: state => state.heading, + unread: state => state.unreadTimeline, + filter: state => state.filter + }), ...mapState({ openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar, backgroundColor: state => state.App.theme.background_color, startReload: state => state.TimelineSpace.HeaderMenu.reload, - timeline: state => state.TimelineSpace.Contents.Public.timeline, - lazyLoading: state => state.TimelineSpace.Contents.Public.lazyLoading, - heading: state => state.TimelineSpace.Contents.Public.heading, - unread: state => state.TimelineSpace.Contents.Public.unreadTimeline, - filter: state => state.TimelineSpace.Contents.Public.filter + unreadNotification: state => state.TimelineSpace.unreadNotification }), ...mapGetters('TimelineSpace/Modals', [ 'modalOpened' @@ -69,22 +72,36 @@ export default { return currentIndex === -1 } }, - created () { + async mounted () { this.$store.commit('TimelineSpace/changeLoading', true) - this.initialize() - .finally(() => { - this.$store.commit('TimelineSpace/changeLoading', false) - }) + this.$store.commit('TimelineSpace/SideMenu/changeUnreadPublicTimeline', false) document.getElementById('scrollable').addEventListener('scroll', this.onScroll) + if (!this.unreadNotification.public) { + await this.initialize() + .finally(_ => { + this.$store.commit('TimelineSpace/changeLoading', false) + }) + } + this.$store.commit('TimelineSpace/changeLoading', false) + }, + beforeUpdate () { + if (this.$store.state.TimelineSpace.SideMenu.unreadPublicTimeline && this.heading) { + this.$store.commit('TimelineSpace/SideMenu/changeUnreadPublicTimeline', false) + } }, beforeDestroy () { - this.$store.dispatch('TimelineSpace/Contents/Public/stopPublicStreaming') + if (!this.unreadNotification.public) { + this.$store.dispatch('TimelineSpace/stopPublicStreaming') + this.$store.dispatch('TimelineSpace/unbindPublicStreaming') + } }, destroyed () { this.$store.commit('TimelineSpace/Contents/Public/changeHeading', true) this.$store.commit('TimelineSpace/Contents/Public/mergeTimeline') this.$store.commit('TimelineSpace/Contents/Public/archiveTimeline') - this.$store.commit('TimelineSpace/Contents/Public/clearTimeline') + if (!this.unreadNotification.public) { + this.$store.commit('TimelineSpace/Contents/Public/clearTimeline') + } if (document.getElementById('scrollable') !== undefined && document.getElementById('scrollable') !== null) { document.getElementById('scrollable').removeEventListener('scroll', this.onScroll) document.getElementById('scrollable').scrollTop = 0 @@ -110,21 +127,15 @@ export default { }, methods: { async initialize () { - try { - await this.$store.dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline') - } catch (err) { - this.$message({ - message: this.$t('message.timeline_fetch_error'), - type: 'error' - }) - } - this.$store.dispatch('TimelineSpace/Contents/Public/startPublicStreaming') - .catch(() => { + await this.$store.dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline') + .catch(_ => { this.$message({ - message: this.$t('message.start_streaming_error'), + message: this.$t('message.timeline_fetch_error'), type: 'error' }) }) + await this.$store.dispatch('TimelineSpace/bindPublicStreaming') + this.$store.dispatch('TimelineSpace/startPublicStreaming') }, updateToot (message) { this.$store.commit('TimelineSpace/Contents/Public/updateToot', message) @@ -154,21 +165,6 @@ export default { this.$store.commit('TimelineSpace/changeLoading', true) try { await this.reloadable() - await this.$store.dispatch('TimelineSpace/Contents/Public/stopPublicStreaming') - await this.$store.dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline') - .catch(() => { - this.$message({ - message: this.$t('message.timeline_fetch_error'), - type: 'error' - }) - }) - this.$store.dispatch('TimelineSpace/Contents/Public/startPublicStreaming') - .catch(() => { - this.$message({ - message: this.$t('message.start_streaming_error'), - type: 'error' - }) - }) } finally { this.$store.commit('TimelineSpace/changeLoading', false) } diff --git a/src/renderer/components/TimelineSpace/SideMenu.vue b/src/renderer/components/TimelineSpace/SideMenu.vue index 43e9facc..a582bdfd 100644 --- a/src/renderer/components/TimelineSpace/SideMenu.vue +++ b/src/renderer/components/TimelineSpace/SideMenu.vue @@ -68,6 +68,8 @@ {{ $t("side_menu.public") }} + + @@ -115,6 +117,7 @@ export default { unreadNotifications: state => state.unreadNotifications, unreadLocalTimeline: state => state.unreadLocalTimeline, unreadDirectMessagesTimeline: state => state.unreadDirectMessagesTimeline, + unreadPublicTimeline: state => state.unreadPublicTimeline, lists: state => state.lists, tags: state => state.tags, collapse: state => state.collapse diff --git a/src/renderer/components/mixins/reloadable.vue b/src/renderer/components/mixins/reloadable.vue index b24ce284..2ee7e619 100644 --- a/src/renderer/components/mixins/reloadable.vue +++ b/src/renderer/components/mixins/reloadable.vue @@ -10,17 +10,9 @@ export default { }) throw err }) - await this.$store.dispatch('TimelineSpace/stopUserStreaming') - await this.$store.dispatch('TimelineSpace/stopLocalStreaming') - await this.$store.dispatch('TimelineSpace/stopDirectMessagesStreaming') - - await this.$store.dispatch('TimelineSpace/Contents/Home/fetchTimeline', account) - await this.$store.dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline', account) - await this.$store.dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', account) - - this.$store.dispatch('TimelineSpace/startUserStreaming', account) - this.$store.dispatch('TimelineSpace/startLocalStreaming', account) - this.$store.dispatch('TimelineSpace/startDirectMessagesStreaming', account) + await this.$store.dispatch('TimelineSpace/stopStreamings', account) + await this.$store.dispatch('TimelineSpace/fetchContentsTimelines', account) + await this.$store.dispatch('TimelineSpace/startStreamings', account) return account } } diff --git a/src/renderer/router/index.js b/src/renderer/router/index.js index 6c7b4d4c..c0e1e2a6 100644 --- a/src/renderer/router/index.js +++ b/src/renderer/router/index.js @@ -60,6 +60,10 @@ export default new Router({ { path: 'general', component: require('@/components/Settings/General').default + }, + { + path: 'timeline', + component: require('@/components/Settings/Timeline').default } ] }, diff --git a/src/renderer/store/Settings.js b/src/renderer/store/Settings.js index fbe0b9a9..32b7d4ed 100644 --- a/src/renderer/store/Settings.js +++ b/src/renderer/store/Settings.js @@ -1,8 +1,18 @@ import General from './Settings/General' +import Timeline from './Settings/Timeline' export default { namespaced: true, modules: { - General + General, + Timeline + }, + state: { + accountID: null + }, + mutations: { + changeAccountID (state, id) { + state.accountID = id + } } } diff --git a/src/renderer/store/Settings/Timeline.js b/src/renderer/store/Settings/Timeline.js new file mode 100644 index 00000000..b21dc264 --- /dev/null +++ b/src/renderer/store/Settings/Timeline.js @@ -0,0 +1,56 @@ +import { ipcRenderer } from 'electron' +import unreadSettings from '~/src/constants/unreadNotification' + +export default { + namespaced: true, + state: { + unreadNotification: { + direct: unreadSettings.Direct.default, + local: unreadSettings.Local.default, + public: unreadSettings.Public.default + } + }, + mutations: { + updateUnreadNotification (state, settings) { + state.unreadNotification = settings + } + }, + actions: { + loadUnreadNotification ({ commit, rootState }) { + return new Promise((resolve, reject) => { + ipcRenderer.once('response-get-unread-notification', (event, settings) => { + ipcRenderer.removeAllListeners('error-get-unread-notification') + commit('updateUnreadNotification', settings) + resolve(settings) + }) + ipcRenderer.once('error-get-unread-notification', (event, err) => { + ipcRenderer.removeAllListeners('response-get-unread-notification') + commit('updateUnreadNotification', { + direct: unreadSettings.Direct.default, + local: unreadSettings.Local.default, + public: unreadSettings.Public.default + }) + resolve(null) + }) + ipcRenderer.send('get-unread-notification', rootState.Settings.accountID) + }) + }, + changeUnreadNotification ({ dispatch, state, rootState }, timeline) { + const settings = Object.assign({}, state.unreadNotification, timeline, { + accountID: rootState.Settings.accountID + }) + return new Promise((resolve, reject) => { + ipcRenderer.once('response-update-unread-notification', (event, _) => { + ipcRenderer.removeAllListeners('error-update-unread-notification') + dispatch('loadUnreadNotification') + resolve(settings) + }) + ipcRenderer.once('error-update-unread-notification', (event, err) => { + ipcRenderer.removeAllListeners('response-update-unread-notification') + reject(err) + }) + ipcRenderer.send('update-unread-notification', settings) + }) + } + } +} diff --git a/src/renderer/store/TimelineSpace.js b/src/renderer/store/TimelineSpace.js index 387b5173..bb9bc726 100644 --- a/src/renderer/store/TimelineSpace.js +++ b/src/renderer/store/TimelineSpace.js @@ -5,6 +5,7 @@ import HeaderMenu from './TimelineSpace/HeaderMenu' import Modals from './TimelineSpace/Modals' import Contents from './TimelineSpace/Contents' import router from '../router' +import unreadSettings from '~/src/constants/unreadNotification' const TimelineSpace = { namespaced: true, @@ -22,7 +23,12 @@ const TimelineSpace = { }, loading: false, emojis: [], - tootMax: 500 + tootMax: 500, + unreadNotification: { + direct: unreadSettings.Direct.default, + local: unreadSettings.Local.default, + public: unreadSettings.Public.default + } }, mutations: { updateAccount (state, account) { @@ -45,9 +51,15 @@ const TimelineSpace = { } else { state.tootMax = 500 } + }, + updateUnreadNotification (state, settings) { + state.unreadNotification = settings } }, actions: { + // ------------------------------------------------- + // Accounts + // ------------------------------------------------- localAccount ({ dispatch, commit }, id) { return new Promise((resolve, reject) => { ipcRenderer.send('get-local-account', id) @@ -87,6 +99,131 @@ const TimelineSpace = { }) }) }, + async clearAccount ({ commit }) { + commit( + 'updateAccount', + { + domain: '', + _id: '', + username: '' + } + ) + return 'clearAccount' + }, + // ----------------------------------------------- + // Shortcuts + // ----------------------------------------------- + watchShortcutEvents ({ commit, dispatch }) { + ipcRenderer.on('CmdOrCtrl+N', () => { + dispatch('TimelineSpace/Modals/NewToot/openModal', {}, { root: true }) + }) + ipcRenderer.on('CmdOrCtrl+K', () => { + commit('TimelineSpace/Modals/Jump/changeModal', true, { root: true }) + }) + }, + async removeShortcutEvents () { + ipcRenderer.removeAllListeners('CmdOrCtrl+N') + ipcRenderer.removeAllListeners('CmdOrCtrl+K') + return 'removeShortcutEvents' + }, + /** + * clearUnread + */ + async clearUnread ({ dispatch }) { + dispatch('TimelineSpace/SideMenu/clearUnread', {}, { root: true }) + }, + /** + * fetchEmojis + */ + async fetchEmojis ({ commit }, account) { + const data = await Mastodon.get('/custom_emojis', {}, account.baseURL + '/api/v1') + commit('updateEmojis', data) + return data + }, + /** + * fetchInstance + */ + async fetchInstance ({ commit }, account) { + const data = await Mastodon.get('/instance', {}, account.baseURL + '/api/v1') + commit('updateTootMax', data.max_toot_chars) + return data + }, + loadUnreadNotification ({ commit, rootState }, accountID) { + return new Promise((resolve, reject) => { + ipcRenderer.once('response-get-unread-notification', (event, settings) => { + ipcRenderer.removeAllListeners('error-get-unread-notification') + commit('updateUnreadNotification', settings) + resolve(settings) + }) + ipcRenderer.once('error-get-unread-notification', (event, err) => { + ipcRenderer.removeAllListeners('response-get-unread-notification') + commit('updateUnreadNotification', { + direct: unreadSettings.Direct.default, + local: unreadSettings.Local.default, + public: unreadSettings.Public.default + }) + resolve(null) + }) + ipcRenderer.send('get-unread-notification', accountID) + }) + }, + async fetchContentsTimelines ({ dispatch, state }, account) { + await dispatch('TimelineSpace/Contents/Home/fetchTimeline', account, { root: true }) + await dispatch('TimelineSpace/Contents/Notifications/fetchNotifications', account, { root: true }) + if (state.unreadNotification.direct) { + await dispatch('TimelineSpace/Contents/DirectMessages/fetchTimeline', {}, { root: true }) + } + if (state.unreadNotification.local) { + await dispatch('TimelineSpace/Contents/Local/fetchLocalTimeline', {}, { root: true }) + } + if (state.unreadNotification.public) { + await dispatch('TimelineSpace/Contents/Public/fetchPublicTimeline', {}, { root: true }) + } + }, + clearContentsTimelines ({ commit }) { + commit('TimelineSpace/Contents/Home/clearTimeline', {}, { root: true }) + commit('TimelineSpace/Contents/Local/clearTimeline', {}, { root: true }) + commit('TimelineSpace/Contents/DirectMessages/clearTimeline', {}, { root: true }) + commit('TimelineSpace/Contents/Notifications/clearNotifications', {}, { root: true }) + commit('TimelineSpace/Contents/Public/clearTimeline', {}, { root: true }) + }, + bindStreamings ({ dispatch, state }, account) { + dispatch('bindUserStreaming', account) + if (state.unreadNotification.direct) { + dispatch('bindDirectMessagesStreaming') + } + if (state.unreadNotification.local) { + dispatch('bindLocalStreaming') + } + if (state.unreadNotification.public) { + dispatch('bindPublicStreaming') + } + }, + startStreamings ({ dispatch, state }, account) { + dispatch('startUserStreaming', account) + if (state.unreadNotification.direct) { + dispatch('startDirectMessagesStreaming') + } + if (state.unreadNotification.local) { + dispatch('startLocalStreaming') + } + if (state.unreadNotification.public) { + dispatch('startPublicStreaming') + } + }, + stopStreamings ({ dispatch }, account) { + dispatch('stopUserStreaming') + dispatch('stopDirectMessagesStreaming') + dispatch('stopLocalStreaming') + dispatch('stopPublicStreaming') + dispatch('unbindUserStreaming') + dispatch('unbindDirectMessagesStreaming') + dispatch('unbindLocalStreaming') + dispatch('unbindPublicStreaming') + }, + // ------------------------------------------------ + // Each streaming methods + // ------------------------------------------------ bindUserStreaming ({ commit, rootState }, account) { ipcRenderer.on('update-start-user-streaming', (event, update) => { commit('TimelineSpace/Contents/Home/appendTimeline', update, { root: true }) @@ -127,14 +264,31 @@ const TimelineSpace = { commit('TimelineSpace/SideMenu/changeUnreadLocalTimeline', true, { root: true }) }) }, - startLocalStreaming (_, account) { + startLocalStreaming ({ state }) { return new Promise((resolve, reject) => { - ipcRenderer.send('start-local-streaming', account) + ipcRenderer.send('start-local-streaming', state.account) ipcRenderer.once('error-start-local-streaming', (event, err) => { reject(err) }) }) }, + bindPublicStreaming ({ commit, rootState }) { + ipcRenderer.on('update-start-public-streaming', (event, update) => { + commit('TimelineSpace/Contents/Public/appendTimeline', update, { root: true }) + if (rootState.TimelineSpace.Contents.Public.heading && Math.random() > 0.8) { + commit('TimelineSpace/Contents/Public/archiveTimeline', {}, { root: true }) + } + }) + commit('TimelineSpace/SideMenu/changeUnreadPublicTimeline', true, { root: true }) + }, + startPublicStreaming ({ state }) { + return new Promise((resolve, reject) => { + ipcRenderer.send('start-public-streaming', state.account) + ipcRenderer.once('error-start-public-streaming', (event, err) => { + reject(err) + }) + }) + }, bindDirectMessagesStreaming ({ commit, rootState }) { ipcRenderer.on('update-start-directmessages-streaming', (event, update) => { commit('TimelineSpace/Contents/DirectMessages/appendTimeline', update, { root: true }) @@ -144,9 +298,9 @@ const TimelineSpace = { commit('TimelineSpace/SideMenu/changeUnreadDirectMessagesTimeline', true, { root: true }) }) }, - startDirectMessagesStreaming (_, account) { + startDirectMessagesStreaming ({ state }) { return new Promise((resolve, reject) => { - ipcRenderer.send('start-directmessages-streaming', account) + ipcRenderer.send('start-directmessages-streaming', state.account) ipcRenderer.once('error-start-directmessages-streaming', (event, err) => { reject(err) }) @@ -167,49 +321,19 @@ const TimelineSpace = { stopLocalStreaming () { ipcRenderer.send('stop-local-streaming') }, + unbindPublicStreaming () { + ipcRenderer.removeAllListeners('error-start-public-streaming') + ipcRenderer.removeAllListeners('update-start-public-streaming') + }, + stopPublicStreaming () { + ipcRenderer.send('stop-public-streaming') + }, unbindDirectMessagesStreaming () { ipcRenderer.removeAllListeners('error-start-directmessages-streaming') ipcRenderer.removeAllListeners('update-start-directmessages-streaming') }, stopDirectMessagesStreaming () { ipcRenderer.send('stop-drectmessages-streaming') - }, - watchShortcutEvents ({ commit, dispatch }) { - ipcRenderer.on('CmdOrCtrl+N', () => { - dispatch('TimelineSpace/Modals/NewToot/openModal', {}, { root: true }) - }) - ipcRenderer.on('CmdOrCtrl+K', () => { - commit('TimelineSpace/Modals/Jump/changeModal', true, { root: true }) - }) - }, - async removeShortcutEvents () { - ipcRenderer.removeAllListeners('CmdOrCtrl+N') - ipcRenderer.removeAllListeners('CmdOrCtrl+K') - return 'removeShortcutEvents' - }, - async clearAccount ({ commit }) { - commit( - 'updateAccount', - { - domain: '', - _id: '', - username: '' - } - ) - return 'clearAccount' - }, - async clearUnread ({ dispatch }) { - dispatch('TimelineSpace/SideMenu/clearUnread', {}, { root: true }) - }, - async fetchEmojis ({ commit }, account) { - const data = await Mastodon.get('/custom_emojis', {}, account.baseURL + '/api/v1') - commit('updateEmojis', data) - return data - }, - async fetchInstance ({ commit }, account) { - const data = await Mastodon.get('/instance', {}, account.baseURL + '/api/v1') - commit('updateTootMax', data.max_toot_chars) - return data } } } diff --git a/src/renderer/store/TimelineSpace/Contents/DirectMessages.js b/src/renderer/store/TimelineSpace/Contents/DirectMessages.js index 829d65b8..d9620a59 100644 --- a/src/renderer/store/TimelineSpace/Contents/DirectMessages.js +++ b/src/renderer/store/TimelineSpace/Contents/DirectMessages.js @@ -71,7 +71,7 @@ const DirectMessages = { } }, actions: { - fetchTimeline ({ state, commit, rootState }, account) { + fetchTimeline ({ state, commit, rootState }) { const client = new Mastodon( rootState.TimelineSpace.account.accessToken, rootState.TimelineSpace.account.baseURL + '/api/v1' diff --git a/src/renderer/store/TimelineSpace/Contents/Local.js b/src/renderer/store/TimelineSpace/Contents/Local.js index 60dae5b1..e4024560 100644 --- a/src/renderer/store/TimelineSpace/Contents/Local.js +++ b/src/renderer/store/TimelineSpace/Contents/Local.js @@ -70,10 +70,10 @@ const Local = { } }, actions: { - fetchLocalTimeline ({ commit }, account) { + fetchLocalTimeline ({ commit, rootState }) { const client = new Mastodon( - account.accessToken, - account.baseURL + '/api/v1' + rootState.TimelineSpace.account.accessToken, + rootState.TimelineSpace.account.baseURL + '/api/v1' ) return client.get('/timelines/public', { limit: 40, local: true }) .then(res => { diff --git a/src/renderer/store/TimelineSpace/Contents/Public.js b/src/renderer/store/TimelineSpace/Contents/Public.js index ceaf828b..1b884cbc 100644 --- a/src/renderer/store/TimelineSpace/Contents/Public.js +++ b/src/renderer/store/TimelineSpace/Contents/Public.js @@ -1,4 +1,3 @@ -import { ipcRenderer } from 'electron' import Mastodon from 'megalodon' const Public = { @@ -81,25 +80,6 @@ const Public = { commit('updateTimeline', res.data) }) }, - startPublicStreaming ({ state, commit, rootState }) { - ipcRenderer.on('update-start-public-streaming', (event, update) => { - commit('appendTimeline', update) - if (state.heading && Math.random() > 0.8) { - commit('archiveTimeline') - } - }) - return new Promise((resolve, reject) => { - ipcRenderer.send('start-public-streaming', rootState.TimelineSpace.account) - ipcRenderer.once('error-start-public-streaming', (event, err) => { - reject(err) - }) - }) - }, - stopPublicStreaming ({ commit }) { - ipcRenderer.removeAllListeners('error-start-public-streaming') - ipcRenderer.removeAllListeners('update-start-public-streaming') - ipcRenderer.send('stop-public-streaming') - }, lazyFetchTimeline ({ state, commit, rootState }, last) { if (last === undefined || last === null) { return Promise.resolve(null) diff --git a/src/renderer/store/TimelineSpace/SideMenu.js b/src/renderer/store/TimelineSpace/SideMenu.js index 4a5d13f2..dc02e4c9 100644 --- a/src/renderer/store/TimelineSpace/SideMenu.js +++ b/src/renderer/store/TimelineSpace/SideMenu.js @@ -8,6 +8,7 @@ const SideMenu = { unreadNotifications: false, unreadLocalTimeline: false, unreadDirectMessagesTimeline: false, + unreadPublicTimeline: false, lists: [], tags: [], collapse: false @@ -25,6 +26,9 @@ const SideMenu = { changeUnreadDirectMessagesTimeline (state, value) { state.unreadDirectMessagesTimeline = value }, + changeUnreadPublicTimeline (state, value) { + state.unreadPublicTimeline = value + }, updateLists (state, lists) { state.lists = lists }, @@ -53,6 +57,7 @@ const SideMenu = { commit('changeUnreadNotifications', false) commit('changeUnreadLocalTimeline', false) commit('changeUnreadDirectMessagesTimeline', false) + commit('changeUnreadPublicTimeline', false) }, changeCollapse ({ commit }, value) { commit('changeCollapse', value)