diff --git a/src/renderer/components/TimelineSpace.vue b/src/renderer/components/TimelineSpace.vue index dc4ebac6..b82c8591 100644 --- a/src/renderer/components/TimelineSpace.vue +++ b/src/renderer/components/TimelineSpace.vue @@ -28,6 +28,8 @@ import Contents from './TimelineSpace/Contents' import Modals from './TimelineSpace/Modals' import Mousetrap from 'mousetrap' import ReceiveDrop from './TimelineSpace/ReceiveDrop' +import { AccountLoadError } from '@/errors/load' +import { TimelineFetchError } from '@/errors/fetch' export default { name: 'timeline-space', @@ -50,9 +52,7 @@ export default { }, async created() { this.$store.dispatch('TimelineSpace/Contents/SideBar/close') - this.$store.commit('TimelineSpace/changeLoading', true) await this.initialize().finally(() => { - this.$store.commit('TimelineSpace/changeLoading', false) this.$store.commit('GlobalHeader/updateChanging', false) }) }, @@ -84,33 +84,21 @@ export default { async initialize() { await this.clear() - this.$store.dispatch('TimelineSpace/watchShortcutEvents') - const account = await this.$store.dispatch('TimelineSpace/localAccount', this.$route.params.id).catch(() => { - this.$message({ - message: this.$t('message.account_load_error'), - type: 'error' - }) - }) - this.$store.dispatch('TimelineSpace/SideMenu/fetchLists', account) - this.$store.dispatch('TimelineSpace/SideMenu/fetchFollowRequests', account) - 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.timeline_fetch_error'), - type: 'error' - }) - }) - - await this.$store.dispatch('TimelineSpace/detectPleroma') - // 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) + try { + this.$store.dispatch('TimelineSpace/initLoad', this.$route.params.id) + } catch (err) { + if (err instanceof AccountLoadError) { + this.$message({ + message: this.$t('message.account_load_error'), + type: 'error' + }) + } else if (err instanceof TimelineFetchError) { + this.$message({ + message: this.$t('message.timeline_fetch_error'), + type: 'error' + }) + } + } }, handleDrop(e) { e.preventDefault() diff --git a/src/renderer/components/TimelineSpace/Contents.vue b/src/renderer/components/TimelineSpace/Contents.vue index 6e9bc71a..b28e3c68 100644 --- a/src/renderer/components/TimelineSpace/Contents.vue +++ b/src/renderer/components/TimelineSpace/Contents.vue @@ -1,12 +1,17 @@ diff --git a/src/renderer/errors/fetch.js b/src/renderer/errors/fetch.js new file mode 100644 index 00000000..efded1ff --- /dev/null +++ b/src/renderer/errors/fetch.js @@ -0,0 +1 @@ +export class TimelineFetchError extends Error {} diff --git a/src/renderer/errors/load.js b/src/renderer/errors/load.js new file mode 100644 index 00000000..6c44bd2b --- /dev/null +++ b/src/renderer/errors/load.js @@ -0,0 +1 @@ +export class AccountLoadError extends Error {} diff --git a/src/renderer/store/TimelineSpace.ts b/src/renderer/store/TimelineSpace.ts index 461a3dd7..85cbf98a 100644 --- a/src/renderer/store/TimelineSpace.ts +++ b/src/renderer/store/TimelineSpace.ts @@ -12,21 +12,23 @@ import LocalAccount from '~/src/types/localAccount' import { Notify } from '~/src/types/notify' import { RootState } from '@/store' import { UnreadNotification } from '~/src/types/unreadNotification' +import { AccountLoadError } from '@/errors/load' +import { TimelineFetchError } from '@/errors/fetch' declare var Notification: any interface MyEmoji { - name: string, + name: string image: string } export interface TimelineSpaceState { - account: LocalAccount, - loading: boolean, - emojis: Array, - tootMax: number, - unreadNotification: UnreadNotification, - useWebsocket: boolean, + account: LocalAccount + loading: boolean + emojis: Array + tootMax: number + unreadNotification: UnreadNotification + useWebsocket: boolean pleroma: boolean } @@ -76,7 +78,7 @@ const mutations: MutationTree = { state.loading = value }, [MUTATION_TYPES.UPDATE_EMOJIS]: (state, emojis: Array) => { - state.emojis = emojis.map((e) => { + state.emojis = emojis.map(e => { return { name: `:${e.shortcode}:`, image: e.url @@ -102,10 +104,32 @@ const mutations: MutationTree = { } const actions: ActionTree = { + initLoad: async ({ dispatch, commit }, accountId: number): Promise => { + commit(MUTATION_TYPES.CHANGE_LOADING, true) + dispatch('watchShortcutEvents') + const account = await dispatch('localAccount', accountId).catch(_ => { + commit(MUTATION_TYPES.CHANGE_LOADING, false) + throw new AccountLoadError() + }) + + dispatch('TimelineSpace/SideMenu/fetchLists', account, { root: true }) + dispatch('TimelineSpace/SideMenu/fetchFollowRequests', account, { root: true }) + await dispatch('loadUnreadNotification', accountId) + commit(MUTATION_TYPES.CHANGE_LOADING, false) + await dispatch('fetchContentsTimelines', account).catch(_ => { + throw new TimelineFetchError() + }) + await dispatch('detectPleroma') + await dispatch('bindStreamings', account) + dispatch('startStreamings', account) + dispatch('fetchEmojis', account) + dispatch('fetchInstance', account) + return account + }, // ------------------------------------------------- // Accounts // ------------------------------------------------- - localAccount: ({ dispatch, commit }, id: string): Promise => { + localAccount: async ({ dispatch, commit }, id: string): Promise => { return new Promise((resolve, reject) => { ipcRenderer.send('get-local-account', id) ipcRenderer.once('error-get-local-account', (_, err: Error) => { @@ -121,7 +145,7 @@ const actions: ActionTree = { commit(MUTATION_TYPES.UPDATE_ACCOUNT, acct) resolve(acct) }) - .catch((err) => { + .catch(err => { reject(err) }) } else { @@ -216,7 +240,11 @@ const actions: ActionTree = { }) }, fetchContentsTimelines: async ({ dispatch, state }) => { - await dispatch('TimelineSpace/Contents/Home/fetchTimeline', {}, { root: true }) + dispatch('TimelineSpace/Contents/changeLoading', true, { root: true }) + await dispatch('TimelineSpace/Contents/Home/fetchTimeline', {}, { root: true }).finally(() => { + dispatch('TimelineSpace/Contents/changeLoading', false, { root: true }) + }) + await dispatch('TimelineSpace/Contents/Notifications/fetchNotifications', {}, { root: true }) await dispatch('TimelineSpace/Contents/Mentions/fetchMentions', {}, { root: true }) if (state.unreadNotification.direct) { @@ -308,7 +336,8 @@ const actions: ActionTree = { }, startUserStreaming: ({ state }): Promise<{}> => { // @ts-ignore - return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars + return new Promise((resolve, reject) => { + // eslint-disable-line no-unused-vars ipcRenderer.send('start-user-streaming', { account: state.account, useWebsocket: state.useWebsocket @@ -329,7 +358,8 @@ const actions: ActionTree = { }, startLocalStreaming: ({ state }) => { // @ts-ignore - return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars + return new Promise((resolve, reject) => { + // eslint-disable-line no-unused-vars ipcRenderer.send('start-local-streaming', { account: state.account, useWebsocket: state.useWebsocket @@ -350,7 +380,8 @@ const actions: ActionTree = { }, startPublicStreaming: ({ state }) => { // @ts-ignore - return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars + return new Promise((resolve, reject) => { + // eslint-disable-line no-unused-vars ipcRenderer.send('start-public-streaming', { account: state.account, useWebsocket: state.useWebsocket @@ -371,7 +402,8 @@ const actions: ActionTree = { }, startDirectMessagesStreaming: ({ state }) => { // @ts-ignore - return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars + return new Promise((resolve, reject) => { + // eslint-disable-line no-unused-vars ipcRenderer.send('start-directmessages-streaming', { account: state.account, useWebsocket: state.useWebsocket @@ -413,9 +445,9 @@ const actions: ActionTree = { } export interface TimelineSpaceModuleState extends TimelineSpaceState { - SideMenu: SideMenuState, - HeaderMenu: HeaderMenuState, - Modals: ModalsModuleState, + SideMenu: SideMenuState + HeaderMenu: HeaderMenuState + Modals: ModalsModuleState Contents: ContentsModuleState } @@ -434,7 +466,7 @@ const TimelineSpace: Module = { export default TimelineSpace -function createNotification (notification: NotificationType, notifyConfig: Notify) { +function createNotification(notification: NotificationType, notifyConfig: Notify) { switch (notification.type) { case 'favourite': if (notifyConfig.favourite) { @@ -471,7 +503,7 @@ function createNotification (notification: NotificationType, notifyConfig: Notif } } -function username (account: Account) { +function username(account: Account) { if (account.display_name !== '') { return account.display_name } else { diff --git a/src/renderer/store/TimelineSpace/Contents.ts b/src/renderer/store/TimelineSpace/Contents.ts index 3e580d22..5f492331 100644 --- a/src/renderer/store/TimelineSpace/Contents.ts +++ b/src/renderer/store/TimelineSpace/Contents.ts @@ -10,10 +10,12 @@ import Hashtag, { HashtagModuleState } from './Contents/Hashtag' import DirectMessages, { DirectMessagesState } from './Contents/DirectMessages' import FollowRequests, { FollowRequestsState } from './Contents/FollowRequests' import Mentions, { MentionsState } from './Contents/Mentions' -import { Module } from 'vuex' +import { Module, MutationTree, ActionTree } from 'vuex' import { RootState } from '@/store' -export interface ContentsState {} +export interface ContentsState { + loading: boolean +} export interface ContentsModuleState extends ContentsState { SideBar: SideBarModuleState @@ -29,7 +31,25 @@ export interface ContentsModuleState extends ContentsState { FollowRequests: FollowRequestsState } -const state = (): ContentsState => ({}) +const state = (): ContentsState => ({ + loading: false +}) + +export const MUTATION_TYPES = { + CHANGE_LOADING: 'changeLoading' +} + +const mutations: MutationTree = { + [MUTATION_TYPES.CHANGE_LOADING]: (state, loading: boolean) => { + state.loading = loading + } +} + +const actions: ActionTree = { + changeLoading: ({ commit }, loading) => { + commit(MUTATION_TYPES.CHANGE_LOADING, loading) + } +} const Contents: Module = { namespaced: true, @@ -47,7 +67,9 @@ const Contents: Module = { Lists, Hashtag, FollowRequests - } + }, + mutations: mutations, + actions: actions } export default Contents diff --git a/src/renderer/store/TimelineSpace/Contents/Home.ts b/src/renderer/store/TimelineSpace/Contents/Home.ts index 8a7f5b2f..022d5b8f 100644 --- a/src/renderer/store/TimelineSpace/Contents/Home.ts +++ b/src/renderer/store/TimelineSpace/Contents/Home.ts @@ -3,12 +3,12 @@ import { Module, MutationTree, ActionTree } from 'vuex' import { RootState } from '@/store' export interface HomeState { - lazyLoading: boolean, - heading: boolean, - showReblogs: boolean, - showReplies: boolean, - timeline: Array, - unreadTimeline: Array, + lazyLoading: boolean + heading: boolean + showReblogs: boolean + showReplies: boolean + timeline: Array + unreadTimeline: Array filter: string } @@ -55,23 +55,23 @@ const mutations: MutationTree = { [MUTATION_TYPES.UPDATE_TIMELINE]: (state, messages: Array) => { state.timeline = messages }, - [MUTATION_TYPES.MERGE_TIMELINE]: (state) => { + [MUTATION_TYPES.MERGE_TIMELINE]: state => { state.timeline = state.unreadTimeline.slice(0, 80).concat(state.timeline) state.unreadTimeline = [] }, [MUTATION_TYPES.INSERT_TIMELINE]: (state, messages: Array) => { state.timeline = state.timeline.concat(messages) }, - [MUTATION_TYPES.ARCHIVE_TIMELINE]: (state) => { + [MUTATION_TYPES.ARCHIVE_TIMELINE]: state => { state.timeline = state.timeline.slice(0, 40) }, - [MUTATION_TYPES.CLEAR_TIMELINE]: (state) => { + [MUTATION_TYPES.CLEAR_TIMELINE]: state => { state.timeline = [] state.unreadTimeline = [] }, [MUTATION_TYPES.UPDATE_TOOT]: (state, message: Status) => { // Replace target message in homeTimeline and notifications - state.timeline = state.timeline.map((toot) => { + state.timeline = state.timeline.map(toot => { if (toot.id === message.id) { return message } else if (toot.reblog !== null && toot.reblog.id === message.id) { @@ -87,7 +87,7 @@ const mutations: MutationTree = { }) }, [MUTATION_TYPES.DELETE_TOOT]: (state, message: Status) => { - state.timeline = state.timeline.filter((toot) => { + state.timeline = state.timeline.filter(toot => { if (toot.reblog !== null && toot.reblog.id === message.id) { return false } else { @@ -108,10 +108,7 @@ const mutations: MutationTree = { const actions: ActionTree = { fetchTimeline: async ({ commit, rootState }) => { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken!, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) + const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1') const res: Response> = await client.get>('/timelines/home', { limit: 40 }) commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data) return res.data @@ -121,11 +118,9 @@ const actions: ActionTree = { return Promise.resolve(null) } commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true) - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken!, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.get>('/timelines/home', { max_id: lastStatus.id, limit: 40 }) + const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1') + return client + .get>('/timelines/home', { max_id: lastStatus.id, limit: 40 }) .then(res => { commit(MUTATION_TYPES.INSERT_TIMELINE, res.data) return res.data