import generator, { Entity } from 'megalodon' import { Module, MutationTree, ActionTree } from 'vuex' import { RootState } from '@/store' export type LocalState = { timeline: Array lazyLoading: boolean heading: boolean scrolling: boolean } const state = (): LocalState => ({ timeline: [], lazyLoading: false, heading: true, scrolling: false }) export const MUTATION_TYPES = { CHANGE_HEADING: 'changeHeading', APPEND_TIMELINE: 'appendTimeline', UPDATE_TIMELINE: 'updateTimeline', INSERT_TIMELINE: 'insertTimeline', ARCHIVE_TIMELINE: 'archiveTimeline', CLEAR_TIMELINE: 'clearTimeline', UPDATE_TOOT: 'updateToot', DELETE_TOOT: 'deleteToot', CHANGE_LAZY_LOADING: 'changeLazyLoading', CHANGE_SCROLLING: 'changeScrolling' } const mutations: MutationTree = { [MUTATION_TYPES.CHANGE_HEADING]: (state, value: boolean) => { state.heading = value }, [MUTATION_TYPES.APPEND_TIMELINE]: (state, update: Entity.Status) => { // Reject duplicated status in timeline if (!state.timeline.find(item => item.id === update.id)) { state.timeline = [update].concat(state.timeline) } }, [MUTATION_TYPES.UPDATE_TIMELINE]: (state, messages: Array) => { state.timeline = messages }, [MUTATION_TYPES.INSERT_TIMELINE]: (state, messages: Array) => { state.timeline = state.timeline.concat(messages) }, [MUTATION_TYPES.ARCHIVE_TIMELINE]: state => { state.timeline = state.timeline.slice(0, 40) }, [MUTATION_TYPES.CLEAR_TIMELINE]: state => { state.timeline = [] }, [MUTATION_TYPES.UPDATE_TOOT]: (state, message: Entity.Status) => { 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 } }) }, [MUTATION_TYPES.DELETE_TOOT]: (state, id: string) => { state.timeline = state.timeline.filter(toot => { if (toot.reblog !== null && toot.reblog.id === id) { return false } else { return toot.id !== id } }) }, [MUTATION_TYPES.CHANGE_LAZY_LOADING]: (state, value: boolean) => { state.lazyLoading = value }, [MUTATION_TYPES.CHANGE_SCROLLING]: (state, value: boolean) => { state.scrolling = value } } const actions: ActionTree = { fetchLocalTimeline: async ({ dispatch, commit, rootState }): Promise> => { const client = generator( rootState.TimelineSpace.sns, rootState.TimelineSpace.account.baseURL, rootState.TimelineSpace.account.accessToken, rootState.App.userAgent ) try { const res = await client.getLocalTimeline({ limit: 40 }) commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data) return res.data } catch (err) { // Disable local timeline dispatch('TimelineSpace/SideMenu/disableLocal', {}, { root: true }) return [] } }, lazyFetchTimeline: async ({ state, commit, rootState }, lastStatus: Entity.Status): Promise | null> => { if (state.lazyLoading) { return Promise.resolve(null) } commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, true) const client = generator( rootState.TimelineSpace.sns, rootState.TimelineSpace.account.baseURL, rootState.TimelineSpace.account.accessToken, rootState.App.userAgent ) return client .getLocalTimeline({ max_id: lastStatus.id, limit: 40 }) .then(res => { commit(MUTATION_TYPES.INSERT_TIMELINE, res.data) return res.data }) .finally(() => { commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, false) }) } } const Local: Module = { namespaced: true, state: state, mutations: mutations, actions: actions } export default Local