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 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()

View File

@ -1,12 +1,17 @@
<template>
<div id="contents">
<div id="scrollable" :class="openSideBar ? 'timeline-wrapper-with-side-bar' : 'timeline-wrapper'">
<router-view></router-view>
<div id="contents">
<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>
</div>
<side-bar :overlaid="modalOpened"></side-bar>
</div>
<side-bar
:overlaid="modalOpened"
></side-bar>
</div>
</template>
<script>
@ -19,12 +24,13 @@ export default {
SideBar
},
computed: {
...mapState({
openSideBar: state => state.TimelineSpace.Contents.SideBar.openSideBar
...mapState('TimelineSpace/Contents', {
loading: state => state.loading
}),
...mapGetters('TimelineSpace/Modals', [
'modalOpened'
])
...mapState('TimelineSpace/Contents/SideBar', {
openSideBar: state => state.openSideBar
}),
...mapGetters('TimelineSpace/Modals', ['modalOpened'])
}
}
</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 { 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<MyEmoji>,
tootMax: number,
unreadNotification: UnreadNotification,
useWebsocket: boolean,
account: LocalAccount
loading: boolean
emojis: Array<MyEmoji>
tootMax: number
unreadNotification: UnreadNotification
useWebsocket: boolean
pleroma: boolean
}
@ -76,7 +78,7 @@ const mutations: MutationTree<TimelineSpaceState> = {
state.loading = value
},
[MUTATION_TYPES.UPDATE_EMOJIS]: (state, emojis: Array<Emoji>) => {
state.emojis = emojis.map((e) => {
state.emojis = emojis.map(e => {
return {
name: `:${e.shortcode}:`,
image: e.url
@ -102,10 +104,32 @@ const mutations: MutationTree<TimelineSpaceState> = {
}
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
// -------------------------------------------------
localAccount: ({ dispatch, commit }, id: string): Promise<LocalAccount> => {
localAccount: async ({ dispatch, commit }, id: string): Promise<LocalAccount> => {
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<TimelineSpaceState, RootState> = {
commit(MUTATION_TYPES.UPDATE_ACCOUNT, acct)
resolve(acct)
})
.catch((err) => {
.catch(err => {
reject(err)
})
} else {
@ -216,7 +240,11 @@ const actions: ActionTree<TimelineSpaceState, RootState> = {
})
},
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<TimelineSpaceState, RootState> = {
},
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<TimelineSpaceState, RootState> = {
},
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<TimelineSpaceState, RootState> = {
},
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<TimelineSpaceState, RootState> = {
},
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<TimelineSpaceState, RootState> = {
}
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<TimelineSpaceState, RootState> = {
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 {

View File

@ -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<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> = {
namespaced: true,
@ -47,7 +67,9 @@ const Contents: Module<ContentsState, RootState> = {
Lists,
Hashtag,
FollowRequests
}
},
mutations: mutations,
actions: actions
}
export default Contents

View File

@ -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<Status>,
unreadTimeline: Array<Status>,
lazyLoading: boolean
heading: boolean
showReblogs: boolean
showReplies: boolean
timeline: Array<Status>
unreadTimeline: Array<Status>
filter: string
}
@ -55,23 +55,23 @@ const mutations: MutationTree<HomeState> = {
[MUTATION_TYPES.UPDATE_TIMELINE]: (state, messages: Array<Status>) => {
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<Status>) => {
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<HomeState> = {
})
},
[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<HomeState> = {
const actions: ActionTree<HomeState, RootState> = {
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<Array<Status>> = await client.get<Array<Status>>('/timelines/home', { limit: 40 })
commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data)
return res.data
@ -121,11 +118,9 @@ const actions: ActionTree<HomeState, RootState> = {
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<Array<Status>>('/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<Array<Status>>('/timelines/home', { max_id: lastStatus.id, limit: 40 })
.then(res => {
commit(MUTATION_TYPES.INSERT_TIMELINE, res.data)
return res.data