diff --git a/src/renderer/components/TimelineSpace/Contents/Lists/Show.vue b/src/renderer/components/TimelineSpace/Contents/Lists/Show.vue index e6e36cad..b5ee31e4 100644 --- a/src/renderer/components/TimelineSpace/Contents/Lists/Show.vue +++ b/src/renderer/components/TimelineSpace/Contents/Lists/Show.vue @@ -157,7 +157,7 @@ export default { if (((event.target.clientHeight + event.target.scrollTop) >= document.getElementsByName('list')[0].clientHeight - 10) && !this.lazyloading) { this.$store.dispatch('TimelineSpace/Contents/Lists/Show/lazyFetchTimeline', { list_id: this.list_id, - last: this.timeline[this.timeline.length - 1] + status: this.timeline[this.timeline.length - 1] }) } // for unread control diff --git a/src/renderer/store/TimelineSpace/Contents/Lists.js b/src/renderer/store/TimelineSpace/Contents/Lists.js deleted file mode 100644 index 7e049ac6..00000000 --- a/src/renderer/store/TimelineSpace/Contents/Lists.js +++ /dev/null @@ -1,12 +0,0 @@ -import Index from './Lists/Index' -import Show from './Lists/Show' -import Edit from './Lists/Edit' - -export default { - namespaced: true, - modules: { - Index, - Show, - Edit - } -} diff --git a/src/renderer/store/TimelineSpace/Contents/Lists.ts b/src/renderer/store/TimelineSpace/Contents/Lists.ts new file mode 100644 index 00000000..e2589b3c --- /dev/null +++ b/src/renderer/store/TimelineSpace/Contents/Lists.ts @@ -0,0 +1,25 @@ +import Index, { IndexState } from './Lists/Index' +import Show, { ShowState } from './Lists/Show' +import Edit, { EditState } from './Lists/Edit' +import { Module } from 'vuex' +import { RootState } from '@/store' + +export interface ListsState {} + +export interface ListsModuleState extends ListsState { + Index: IndexState, + Show: ShowState, + Edit: EditState +} + +const state = (): ListsState => ({}) + +export default { + namespaced: true, + state: state, + modules: { + Index, + Show, + Edit + } +} as Module diff --git a/src/renderer/store/TimelineSpace/Contents/Lists/Edit.js b/src/renderer/store/TimelineSpace/Contents/Lists/Edit.js deleted file mode 100644 index b4d27565..00000000 --- a/src/renderer/store/TimelineSpace/Contents/Lists/Edit.js +++ /dev/null @@ -1,36 +0,0 @@ -import Mastodon from 'megalodon' - -export default { - namespaced: true, - state: { - members: [] - }, - mutations: { - changeMembers (state, members) { - state.members = members - } - }, - actions: { - fetchMembers ({ commit, rootState }, listId) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.get(`/lists/${listId}/accounts`, { - limit: 0 - }) - .then((res) => { - commit('changeMembers', res.data) - }) - }, - removeAccount ({ rootState }, obj) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.del(`/lists/${obj.listId}/accounts`, { - account_ids: [obj.account.id] - }) - } - } -} diff --git a/src/renderer/store/TimelineSpace/Contents/Lists/Edit.ts b/src/renderer/store/TimelineSpace/Contents/Lists/Edit.ts new file mode 100644 index 00000000..10101871 --- /dev/null +++ b/src/renderer/store/TimelineSpace/Contents/Lists/Edit.ts @@ -0,0 +1,52 @@ +import Mastodon, { Account, Response } from 'megalodon' +import { Module, MutationTree, ActionTree } from 'vuex' +import { RootState } from '@/store' +import { RemoveAccountFromList } from '~/src/types/remove_account_from_list' + +export interface EditState { + members: Array +} + +const state = (): EditState => ({ + members: [] +}) + +export const MUTATION_TYPES = { + CHANGE_MEMBERS: 'changeMembers' +} + +const mutations: MutationTree = { + [MUTATION_TYPES.CHANGE_MEMBERS]: (state, members: Array) => { + state.members = members + } +} + +const actions: ActionTree = { + fetchMembers: async ({ commit, rootState }, listId: number): Promise> => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const res: Response> = await client.get>(`/lists/${listId}/accounts`, { + limit: 0 + }) + commit(MUTATION_TYPES.CHANGE_MEMBERS, res.data) + return res.data + }, + removeAccount: async ({ rootState }, remove: RemoveAccountFromList): Promise<{}> => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + return client.del<{}>(`/lists/${remove.listId}/accounts`, { + account_ids: [remove.account.id] + }) + } +} + +export default { + namespaced: true, + state: state, + mutations: mutations, + actions: actions +} as Module diff --git a/src/renderer/store/TimelineSpace/Contents/Lists/Index.js b/src/renderer/store/TimelineSpace/Contents/Lists/Index.js deleted file mode 100644 index 01ef12b0..00000000 --- a/src/renderer/store/TimelineSpace/Contents/Lists/Index.js +++ /dev/null @@ -1,35 +0,0 @@ -import Mastodon from 'megalodon' - -export default { - namespaced: true, - state: { - lists: [] - }, - mutations: { - changeLists (state, lists) { - state.lists = lists - } - }, - actions: { - fetchLists ({ commit, rootState }) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.get('/lists') - .then((res) => { - commit('changeLists', res.data) - return res.data - }) - }, - createList ({ rootState }, title) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.post('/lists', { - title: title - }) - } - } -} diff --git a/src/renderer/store/TimelineSpace/Contents/Lists/Index.ts b/src/renderer/store/TimelineSpace/Contents/Lists/Index.ts new file mode 100644 index 00000000..7d9bdaf6 --- /dev/null +++ b/src/renderer/store/TimelineSpace/Contents/Lists/Index.ts @@ -0,0 +1,50 @@ +import Mastodon, { List, Response } from 'megalodon' +import { Module, MutationTree, ActionTree } from 'vuex' +import { RootState } from '@/store' + +export interface IndexState { + lists: Array +} + +const state = (): IndexState => ({ + lists: [] +}) + +export const MUTATION_TYPES = { + CHANGE_LISTS: 'changeLists' +} + +const mutations: MutationTree = { + [MUTATION_TYPES.CHANGE_LISTS]: (state, lists: Array) => { + state.lists = lists + } +} + +const actions: ActionTree = { + fetchLists: async ({ commit, rootState }): Promise> => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const res: Response> = await client.get>('/lists') + commit(MUTATION_TYPES.CHANGE_LISTS, res.data) + return res.data + }, + createList: async ({ rootState }, title: string): Promise => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const res: Response = await client.post('/lists', { + title: title + }) + return res.data + } +} + +export default { + namespaced: true, + state: state, + mutations: mutations, + actions: actions +} as Module diff --git a/src/renderer/store/TimelineSpace/Contents/Lists/Show.js b/src/renderer/store/TimelineSpace/Contents/Lists/Show.js deleted file mode 100644 index 3c17de35..00000000 --- a/src/renderer/store/TimelineSpace/Contents/Lists/Show.js +++ /dev/null @@ -1,134 +0,0 @@ -import { ipcRenderer } from 'electron' -import Mastodon from 'megalodon' - -const Show = { - namespaced: true, - state: { - timeline: [], - unreadTimeline: [], - lazyLoading: false, - heading: true, - filter: '' - }, - mutations: { - changeHeading (state, value) { - state.heading = value - }, - appendTimeline (state, update) { - if (state.heading) { - state.timeline = [update].concat(state.timeline) - } else { - state.unreadTimeline = [update].concat(state.unreadTimeline) - } - }, - updateTimeline (state, timeline) { - state.timeline = timeline - }, - mergeTimeline (state) { - state.timeline = state.unreadTimeline.slice(0, 80).concat(state.timeline) - state.unreadTimeline = [] - }, - insertTimeline (state, messages) { - state.timeline = state.timeline.concat(messages) - }, - archiveTimeline (state) { - state.timeline = state.timeline.slice(0, 40) - }, - clearTimeline (state) { - state.timeline = [] - state.unreadTimeline = [] - }, - updateToot (state, message) { - 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 - } - }) - }, - deleteToot (state, message) { - state.timeline = state.timeline.filter((toot) => { - if (toot.reblog !== null && toot.reblog.id === message.id) { - return false - } else { - return toot.id !== message.id - } - }) - }, - changeLazyLoading (state, value) { - state.lazyLoading = value - }, - changeFilter (state, filter) { - state.filter = filter - } - }, - actions: { - fetchTimeline ({ commit, rootState }, listID) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.get(`/timelines/list/${listID}`, { limit: 40 }) - .then(res => { - commit('updateTimeline', res.data) - return res.data - }) - }, - startStreaming ({ state, commit, rootState }, listID) { - ipcRenderer.on('update-start-list-streaming', (event, update) => { - commit('appendTimeline', update) - if (state.heading && Math.random() > 0.8) { - commit('archiveTimeline') - } - }) - return new Promise((resolve, reject) => { - ipcRenderer.send('start-list-streaming', { - listID: listID, - account: rootState.TimelineSpace.account, - useWebsocket: rootState.TimelineSpace.useWebsocket - }) - ipcRenderer.once('error-start-list-streaming', (event, err) => { - reject(err) - }) - }) - }, - stopStreaming () { - return new Promise(resolve => { - ipcRenderer.removeAllListeners('error-start-list-streaming') - ipcRenderer.removeAllListeners('update-start-list-streaming') - ipcRenderer.send('stop-list-streaming') - resolve() - }) - }, - lazyFetchTimeline ({ state, commit, rootState }, obj) { - if (state.lazyLoading) { - return Promise.resolve(null) - } - commit('changeLazyLoading', true) - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.get(`/timelines/list/${obj.list_id}`, { max_id: obj.last.id, limit: 40 }) - .then(res => { - commit('insertTimeline', res.data) - commit('changeLazyLoading', false) - return res.data - }) - .catch(err => { - commit('changeLazyLoading', false) - throw err - }) - } - } -} - -export default Show diff --git a/src/renderer/store/TimelineSpace/Contents/Lists/Show.ts b/src/renderer/store/TimelineSpace/Contents/Lists/Show.ts new file mode 100644 index 00000000..31bce231 --- /dev/null +++ b/src/renderer/store/TimelineSpace/Contents/Lists/Show.ts @@ -0,0 +1,160 @@ +import { ipcRenderer } from 'electron' +import Mastodon, { Status, Response } from 'megalodon' +import { Module, MutationTree, ActionTree } from 'vuex' +import { RootState } from '@/store' +import { LoadPositionWithList } from '~src/types/load_position' + +export interface ShowState { + timeline: Array, + unreadTimeline: Array, + lazyLoading: boolean, + heading: boolean, + filter: string +} + +const state = (): ShowState => ({ + timeline: [], + unreadTimeline: [], + lazyLoading: false, + heading: true, + filter: '' +}) + +export const MUTATION_TYPES = { + CHANGE_HEADING: 'changeHeading', + APPEND_TIMELINE: 'appendTimeline', + UPDATE_TIMELINE: 'updateTimeline', + MERGE_TIMELINE: 'mergeTimeline', + INSERT_TIMELINE: 'insertTimeline', + ARCHIVE_TIMELINE: 'archiveTimeline', + CLEAR_TIMELINE: 'clearTimeline', + UPDATE_TOOT: 'updateToot', + DELETE_TOOT: 'deleteToot', + CHANGE_LAZY_LOADING: 'changeLazyLoading', + CHANGE_FILTER: 'changeFilter' +} + +const mutations: MutationTree = { + [MUTATION_TYPES.CHANGE_HEADING]: (state, value: boolean) => { + state.heading = value + }, + [MUTATION_TYPES.APPEND_TIMELINE]: (state, update: Status) => { + if (state.heading) { + state.timeline = [update].concat(state.timeline) + } else { + state.unreadTimeline = [update].concat(state.unreadTimeline) + } + }, + [MUTATION_TYPES.UPDATE_TIMELINE]: (state, timeline: Array) => { + state.timeline = timeline + }, + [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) => { + state.timeline = state.timeline.slice(0, 40) + }, + [MUTATION_TYPES.CLEAR_TIMELINE]: (state) => { + state.timeline = [] + state.unreadTimeline = [] + }, + [MUTATION_TYPES.UPDATE_TOOT]: (state, message: 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, message: Status) => { + state.timeline = state.timeline.filter((toot) => { + if (toot.reblog !== null && toot.reblog.id === message.id) { + return false + } else { + return toot.id !== message.id + } + }) + }, + [MUTATION_TYPES.CHANGE_LAZY_LOADING]: (state, value: boolean) => { + state.lazyLoading = value + }, + [MUTATION_TYPES.CHANGE_FILTER]: (state, filter: string) => { + state.filter = filter + } +} + +const actions: ActionTree = { + fetchTimeline: async ({ commit, rootState }, listID: number): Promise> => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const res: Response> = await client.get>(`/timelines/list/${listID}`, { limit: 40 }) + commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data) + return res.data + }, + startStreaming: ({ state, commit, rootState }, listID: number) => { + ipcRenderer.on('update-start-list-streaming', (_, update: Status) => { + commit(MUTATION_TYPES.APPEND_TIMELINE, update) + if (state.heading && Math.random() > 0.8) { + commit(MUTATION_TYPES.ARCHIVE_TIMELINE) + } + }) + // @ts-ignore + return new Promise((resolve, reject) => { // eslint-disable-line no-unused-vars + ipcRenderer.send('start-list-streaming', { + listID: listID, + account: rootState.TimelineSpace.account, + useWebsocket: rootState.TimelineSpace.useWebsocket + }) + ipcRenderer.once('error-start-list-streaming', (_, err: Error) => { + reject(err) + }) + }) + }, + stopStreaming: () => { + return new Promise(resolve => { + ipcRenderer.removeAllListeners('error-start-list-streaming') + ipcRenderer.removeAllListeners('update-start-list-streaming') + ipcRenderer.send('stop-list-streaming') + resolve() + }) + }, + lazyFetchTimeline: async ({ state, commit, rootState }, loadPosition: LoadPositionWithList): Promise | null> => { + if (state.lazyLoading) { + 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/list/${loadPosition.list_id}`, { max_id: loadPosition.status.id, limit: 40 }) + .then(res => { + commit(MUTATION_TYPES.INSERT_TIMELINE, res.data) + return res.data + }) + .finally(() => { + commit(MUTATION_TYPES.CHANGE_LAZY_LOADING, false) + }) + } +} + +export default { + namespaced: true, + state: state, + mutations: mutations, + actions: actions +} as Module diff --git a/src/renderer/store/TimelineSpace/Contents/SideBar/AccountProfile/Timeline.ts b/src/renderer/store/TimelineSpace/Contents/SideBar/AccountProfile/Timeline.ts index 05c31a5b..bb79035d 100644 --- a/src/renderer/store/TimelineSpace/Contents/SideBar/AccountProfile/Timeline.ts +++ b/src/renderer/store/TimelineSpace/Contents/SideBar/AccountProfile/Timeline.ts @@ -1,7 +1,7 @@ import Mastodon, { Status, Response } from 'megalodon' import { Module, MutationTree, ActionTree } from 'vuex' import { RootState } from '@/store' -import { LoadPosition } from '~src/types/load_position' +import { LoadPositionWithAccount } from '~src/types/load_position' export interface TimelineState { timeline: Array, @@ -96,7 +96,7 @@ const actions: ActionTree = { commit(MUTATION_TYPES.UPDATE_TIMELINE, res.data) return res.data }, - lazyFetchTimeline: async ({ state, commit, rootState }, loadPosition: LoadPosition): Promise => { + lazyFetchTimeline: async ({ state, commit, rootState }, loadPosition: LoadPositionWithAccount): Promise => { if (state.lazyLoading) { return Promise.resolve(null) } diff --git a/src/types/load_position.ts b/src/types/load_position.ts index 32ded190..47b731f1 100644 --- a/src/types/load_position.ts +++ b/src/types/load_position.ts @@ -1,6 +1,13 @@ import { Status, Account } from 'megalodon' export interface LoadPosition { - status: Status, + status: Status +} + +export interface LoadPositionWithAccount extends LoadPosition { account: Account } + +export interface LoadPositionWithList extends LoadPosition { + list_id: number +} diff --git a/src/types/remove_account_from_list.ts b/src/types/remove_account_from_list.ts new file mode 100644 index 00000000..108da874 --- /dev/null +++ b/src/types/remove_account_from_list.ts @@ -0,0 +1,6 @@ +import { Account } from 'megalodon' + +export interface RemoveAccountFromList { + account: Account, + listId: number +}