refs #915 Stop loading after fetch home timeline

This commit is contained in:
AkiraFukushima 2019-05-16 23:02:05 +09:00
parent 50948a03c3
commit 582e751fba
7 changed files with 130 additions and 85 deletions

View File

@ -28,6 +28,8 @@ import Contents from './TimelineSpace/Contents'
import Modals from './TimelineSpace/Modals' import Modals from './TimelineSpace/Modals'
import Mousetrap from 'mousetrap' import Mousetrap from 'mousetrap'
import ReceiveDrop from './TimelineSpace/ReceiveDrop' import ReceiveDrop from './TimelineSpace/ReceiveDrop'
import { AccountLoadError } from '@/errors/load'
import { TimelineFetchError } from '@/errors/fetch'
export default { export default {
name: 'timeline-space', name: 'timeline-space',
@ -50,9 +52,7 @@ export default {
}, },
async created() { async created() {
this.$store.dispatch('TimelineSpace/Contents/SideBar/close') this.$store.dispatch('TimelineSpace/Contents/SideBar/close')
this.$store.commit('TimelineSpace/changeLoading', true)
await this.initialize().finally(() => { await this.initialize().finally(() => {
this.$store.commit('TimelineSpace/changeLoading', false)
this.$store.commit('GlobalHeader/updateChanging', false) this.$store.commit('GlobalHeader/updateChanging', false)
}) })
}, },
@ -84,33 +84,21 @@ export default {
async initialize() { async initialize() {
await this.clear() await this.clear()
this.$store.dispatch('TimelineSpace/watchShortcutEvents') try {
const account = await this.$store.dispatch('TimelineSpace/localAccount', this.$route.params.id).catch(() => { this.$store.dispatch('TimelineSpace/initLoad', this.$route.params.id)
} catch (err) {
if (err instanceof AccountLoadError) {
this.$message({ this.$message({
message: this.$t('message.account_load_error'), message: this.$t('message.account_load_error'),
type: 'error' type: 'error'
}) })
}) } else if (err instanceof TimelineFetchError) {
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({ this.$message({
message: this.$t('message.timeline_fetch_error'), message: this.$t('message.timeline_fetch_error'),
type: '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)
}, },
handleDrop(e) { handleDrop(e) {
e.preventDefault() e.preventDefault()

View File

@ -1,12 +1,17 @@
<template> <template>
<div id="contents"> <div id="contents">
<div id="scrollable" :class="openSideBar ? 'timeline-wrapper-with-side-bar' : 'timeline-wrapper'"> <div
id="scrollable"
:class="openSideBar ? 'timeline-wrapper-with-side-bar' : 'timeline-wrapper'"
v-loading="loading"
:element-loading-text="$t('message.loading')"
element-loading-spinner="el-icon-loading"
element-loading-background="rgba(0, 0, 0, 0.8)"
>
<router-view></router-view> <router-view></router-view>
</div> </div>
<side-bar <side-bar :overlaid="modalOpened"></side-bar>
:overlaid="modalOpened" </div>
></side-bar>
</div>
</template> </template>
<script> <script>
@ -19,12 +24,13 @@ export default {
SideBar SideBar
}, },
computed: { computed: {
...mapState({ ...mapState('TimelineSpace/Contents', {
openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar loading: state => state.loading
}), }),
...mapGetters('TimelineSpace/Modals', [ ...mapState('TimelineSpace/Contents/SideBar', {
'modalOpened' openSideBar: state => state.openSideBar
]) }),
...mapGetters('TimelineSpace/Modals', ['modalOpened'])
} }
} }
</script> </script>

View File

@ -0,0 +1 @@
export class TimelineFetchError extends Error {}

View File

@ -0,0 +1 @@
export class AccountLoadError extends Error {}

View File

@ -12,21 +12,23 @@ import LocalAccount from '~/src/types/localAccount'
import { Notify } from '~/src/types/notify' import { Notify } from '~/src/types/notify'
import { RootState } from '@/store' import { RootState } from '@/store'
import { UnreadNotification } from '~/src/types/unreadNotification' import { UnreadNotification } from '~/src/types/unreadNotification'
import { AccountLoadError } from '@/errors/load'
import { TimelineFetchError } from '@/errors/fetch'
declare var Notification: any declare var Notification: any
interface MyEmoji { interface MyEmoji {
name: string, name: string
image: string image: string
} }
export interface TimelineSpaceState { export interface TimelineSpaceState {
account: LocalAccount, account: LocalAccount
loading: boolean, loading: boolean
emojis: Array<MyEmoji>, emojis: Array<MyEmoji>
tootMax: number, tootMax: number
unreadNotification: UnreadNotification, unreadNotification: UnreadNotification
useWebsocket: boolean, useWebsocket: boolean
pleroma: boolean pleroma: boolean
} }
@ -76,7 +78,7 @@ const mutations: MutationTree<TimelineSpaceState> = {
state.loading = value state.loading = value
}, },
[MUTATION_TYPES.UPDATE_EMOJIS]: (state, emojis: Array<Emoji>) => { [MUTATION_TYPES.UPDATE_EMOJIS]: (state, emojis: Array<Emoji>) => {
state.emojis = emojis.map((e) => { state.emojis = emojis.map(e => {
return { return {
name: `:${e.shortcode}:`, name: `:${e.shortcode}:`,
image: e.url image: e.url
@ -102,10 +104,32 @@ const mutations: MutationTree<TimelineSpaceState> = {
} }
const actions: ActionTree<TimelineSpaceState, RootState> = { const actions: ActionTree<TimelineSpaceState, RootState> = {
initLoad: async ({ dispatch, commit }, accountId: number): Promise<Account> => {
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 // Accounts
// ------------------------------------------------- // -------------------------------------------------
localAccount: ({ dispatch, commit }, id: string): Promise<LocalAccount> => { localAccount: async ({ dispatch, commit }, id: string): Promise<LocalAccount> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
ipcRenderer.send('get-local-account', id) ipcRenderer.send('get-local-account', id)
ipcRenderer.once('error-get-local-account', (_, err: Error) => { ipcRenderer.once('error-get-local-account', (_, err: Error) => {
@ -121,7 +145,7 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
commit(MUTATION_TYPES.UPDATE_ACCOUNT, acct) commit(MUTATION_TYPES.UPDATE_ACCOUNT, acct)
resolve(acct) resolve(acct)
}) })
.catch((err) => { .catch(err => {
reject(err) reject(err)
}) })
} else { } else {
@ -216,7 +240,11 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
}) })
}, },
fetchContentsTimelines: async ({ dispatch, state }) => { 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/Notifications/fetchNotifications', {}, { root: true })
await dispatch('TimelineSpace/Contents/Mentions/fetchMentions', {}, { root: true }) await dispatch('TimelineSpace/Contents/Mentions/fetchMentions', {}, { root: true })
if (state.unreadNotification.direct) { if (state.unreadNotification.direct) {
@ -308,7 +336,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
}, },
startUserStreaming: ({ state }): Promise<{}> => { startUserStreaming: ({ state }): Promise<{}> => {
// @ts-ignore // @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', { ipcRenderer.send('start-user-streaming', {
account: state.account, account: state.account,
useWebsocket: state.useWebsocket useWebsocket: state.useWebsocket
@ -329,7 +358,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
}, },
startLocalStreaming: ({ state }) => { startLocalStreaming: ({ state }) => {
// @ts-ignore // @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', { ipcRenderer.send('start-local-streaming', {
account: state.account, account: state.account,
useWebsocket: state.useWebsocket useWebsocket: state.useWebsocket
@ -350,7 +380,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
}, },
startPublicStreaming: ({ state }) => { startPublicStreaming: ({ state }) => {
// @ts-ignore // @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', { ipcRenderer.send('start-public-streaming', {
account: state.account, account: state.account,
useWebsocket: state.useWebsocket useWebsocket: state.useWebsocket
@ -371,7 +402,8 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
}, },
startDirectMessagesStreaming: ({ state }) => { startDirectMessagesStreaming: ({ state }) => {
// @ts-ignore // @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', { ipcRenderer.send('start-directmessages-streaming', {
account: state.account, account: state.account,
useWebsocket: state.useWebsocket useWebsocket: state.useWebsocket
@ -413,9 +445,9 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
} }
export interface TimelineSpaceModuleState extends TimelineSpaceState { export interface TimelineSpaceModuleState extends TimelineSpaceState {
SideMenu: SideMenuState, SideMenu: SideMenuState
HeaderMenu: HeaderMenuState, HeaderMenu: HeaderMenuState
Modals: ModalsModuleState, Modals: ModalsModuleState
Contents: ContentsModuleState Contents: ContentsModuleState
} }
@ -434,7 +466,7 @@ const TimelineSpace: Module<TimelineSpaceState, RootState> = {
export default TimelineSpace export default TimelineSpace
function createNotification (notification: NotificationType, notifyConfig: Notify) { function createNotification(notification: NotificationType, notifyConfig: Notify) {
switch (notification.type) { switch (notification.type) {
case 'favourite': case 'favourite':
if (notifyConfig.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 !== '') { if (account.display_name !== '') {
return account.display_name return account.display_name
} else { } else {

View File

@ -10,10 +10,12 @@ import Hashtag, { HashtagModuleState } from './Contents/Hashtag'
import DirectMessages, { DirectMessagesState } from './Contents/DirectMessages' import DirectMessages, { DirectMessagesState } from './Contents/DirectMessages'
import FollowRequests, { FollowRequestsState } from './Contents/FollowRequests' import FollowRequests, { FollowRequestsState } from './Contents/FollowRequests'
import Mentions, { MentionsState } from './Contents/Mentions' import Mentions, { MentionsState } from './Contents/Mentions'
import { Module } from 'vuex' import { Module, MutationTree, ActionTree } from 'vuex'
import { RootState } from '@/store' import { RootState } from '@/store'
export interface ContentsState {} export interface ContentsState {
loading: boolean
}
export interface ContentsModuleState extends ContentsState { export interface ContentsModuleState extends ContentsState {
SideBar: SideBarModuleState SideBar: SideBarModuleState
@ -29,7 +31,25 @@ export interface ContentsModuleState extends ContentsState {
FollowRequests: FollowRequestsState FollowRequests: FollowRequestsState
} }
const state = (): ContentsState => ({}) const state = (): ContentsState => ({
loading: false
})
export const MUTATION_TYPES = {
CHANGE_LOADING: 'changeLoading'
}
const mutations: MutationTree<ContentsState> = {
[MUTATION_TYPES.CHANGE_LOADING]: (state, loading: boolean) => {
state.loading = loading
}
}
const actions: ActionTree<ContentsState, RootState> = {
changeLoading: ({ commit }, loading) => {
commit(MUTATION_TYPES.CHANGE_LOADING, loading)
}
}
const Contents: Module<ContentsState, RootState> = { const Contents: Module<ContentsState, RootState> = {
namespaced: true, namespaced: true,
@ -47,7 +67,9 @@ const Contents: Module<ContentsState, RootState> = {
Lists, Lists,
Hashtag, Hashtag,
FollowRequests FollowRequests
} },
mutations: mutations,
actions: actions
} }
export default Contents export default Contents

View File

@ -3,12 +3,12 @@ import { Module, MutationTree, ActionTree } from 'vuex'
import { RootState } from '@/store' import { RootState } from '@/store'
export interface HomeState { export interface HomeState {
lazyLoading: boolean, lazyLoading: boolean
heading: boolean, heading: boolean
showReblogs: boolean, showReblogs: boolean
showReplies: boolean, showReplies: boolean
timeline: Array<Status>, timeline: Array<Status>
unreadTimeline: Array<Status>, unreadTimeline: Array<Status>
filter: string filter: string
} }
@ -55,23 +55,23 @@ const mutations: MutationTree<HomeState> = {
[MUTATION_TYPES.UPDATE_TIMELINE]: (state, messages: Array<Status>) => { [MUTATION_TYPES.UPDATE_TIMELINE]: (state, messages: Array<Status>) => {
state.timeline = messages state.timeline = messages
}, },
[MUTATION_TYPES.MERGE_TIMELINE]: (state) => { [MUTATION_TYPES.MERGE_TIMELINE]: state => {
state.timeline = state.unreadTimeline.slice(0, 80).concat(state.timeline) state.timeline = state.unreadTimeline.slice(0, 80).concat(state.timeline)
state.unreadTimeline = [] state.unreadTimeline = []
}, },
[MUTATION_TYPES.INSERT_TIMELINE]: (state, messages: Array<Status>) => { [MUTATION_TYPES.INSERT_TIMELINE]: (state, messages: Array<Status>) => {
state.timeline = state.timeline.concat(messages) state.timeline = state.timeline.concat(messages)
}, },
[MUTATION_TYPES.ARCHIVE_TIMELINE]: (state) => { [MUTATION_TYPES.ARCHIVE_TIMELINE]: state => {
state.timeline = state.timeline.slice(0, 40) state.timeline = state.timeline.slice(0, 40)
}, },
[MUTATION_TYPES.CLEAR_TIMELINE]: (state) => { [MUTATION_TYPES.CLEAR_TIMELINE]: state => {
state.timeline = [] state.timeline = []
state.unreadTimeline = [] state.unreadTimeline = []
}, },
[MUTATION_TYPES.UPDATE_TOOT]: (state, message: Status) => { [MUTATION_TYPES.UPDATE_TOOT]: (state, message: Status) => {
// Replace target message in homeTimeline and notifications // Replace target message in homeTimeline and notifications
state.timeline = state.timeline.map((toot) => { state.timeline = state.timeline.map(toot => {
if (toot.id === message.id) { if (toot.id === message.id) {
return message return message
} else if (toot.reblog !== null && toot.reblog.id === message.id) { } else if (toot.reblog !== null && toot.reblog.id === message.id) {
@ -87,7 +87,7 @@ const mutations: MutationTree<HomeState> = {
}) })
}, },
[MUTATION_TYPES.DELETE_TOOT]: (state, message: Status) => { [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) { if (toot.reblog !== null && toot.reblog.id === message.id) {
return false return false
} else { } else {
@ -108,10 +108,7 @@ const mutations: MutationTree<HomeState> = {
const actions: ActionTree<HomeState, RootState> = { const actions: ActionTree<HomeState, RootState> = {
fetchTimeline: async ({ commit, rootState }) => { fetchTimeline: async ({ commit, rootState }) => {
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!,
rootState.TimelineSpace.account.baseURL + '/api/v1'
)
const res: Response<Array<Status>> = await client.get<Array<Status>>('/timelines/home', { limit: 40 }) const res: Response<Array<Status>> = await client.get<Array<Status>>('/timelines/home', { limit: 40 })
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data) commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
return res.data return res.data
@ -121,11 +118,9 @@ const actions: ActionTree<HomeState, RootState> = {
return Promise.resolve(null) return Promise.resolve(null)
} }
commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true) commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true)
const client = new Mastodon( const client = new Mastodon(rootState.TimelineSpace.account.accessToken!, rootState.TimelineSpace.account.baseURL + '/api/v1')
rootState.TimelineSpace.account.accessToken!, return client
rootState.TimelineSpace.account.baseURL + '/api/v1' .get<Array<Status>>('/timelines/home', { max_id: lastStatus.id, limit: 40 })
)
return client.get<Array<Status>>('/timelines/home', { max_id: lastStatus.id, limit: 40 })
.then(res => { .then(res => {
commit(MUTATION_TYPES.INSERT_TIMELINE, res.data) commit(MUTATION_TYPES.INSERT_TIMELINE, res.data)
return res.data return res.data