From 0208e7c22ac6a3236b1bf65198325f091ff3b9bc Mon Sep 17 00:00:00 2001 From: AkiraFukushima Date: Thu, 11 Apr 2019 00:06:43 +0900 Subject: [PATCH] refs #850 Replace NewToot and Status with typescript --- src/renderer/store/TimelineSpace/Modals.ts | 5 +- .../store/TimelineSpace/Modals/NewToot.js | 204 -------------- .../store/TimelineSpace/Modals/NewToot.ts | 249 ++++++++++++++++++ .../TimelineSpace/Modals/NewToot/Status.js | 73 ----- .../TimelineSpace/Modals/NewToot/Status.ts | 104 ++++++++ tsconfig.json | 6 +- 6 files changed, 361 insertions(+), 280 deletions(-) delete mode 100644 src/renderer/store/TimelineSpace/Modals/NewToot.js create mode 100644 src/renderer/store/TimelineSpace/Modals/NewToot.ts delete mode 100644 src/renderer/store/TimelineSpace/Modals/NewToot/Status.js create mode 100644 src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts diff --git a/src/renderer/store/TimelineSpace/Modals.ts b/src/renderer/store/TimelineSpace/Modals.ts index 3baf45a8..5c0392ae 100644 --- a/src/renderer/store/TimelineSpace/Modals.ts +++ b/src/renderer/store/TimelineSpace/Modals.ts @@ -1,4 +1,4 @@ -import NewToot from './Modals/NewToot' +import NewToot, { NewTootModuleState } from './Modals/NewToot' import ImageViewer, { ImageViewerState } from './Modals/ImageViewer' import Jump, { JumpState } from './Modals/Jump' import ListMembership, { ListMembershipState } from './Modals/ListMembership' @@ -16,7 +16,8 @@ export interface ModalsModuleState extends ModalsState { AddListMember: AddListMemberState, ImageViewer: ImageViewerState, ListMembership: ListMembershipState, - MuteConfirm: MuteConfirmState + MuteConfirm: MuteConfirmState, + NewToot: NewTootModuleState } const state = (): ModalsState => ({}) diff --git a/src/renderer/store/TimelineSpace/Modals/NewToot.js b/src/renderer/store/TimelineSpace/Modals/NewToot.js deleted file mode 100644 index 4150b724..00000000 --- a/src/renderer/store/TimelineSpace/Modals/NewToot.js +++ /dev/null @@ -1,204 +0,0 @@ -import Mastodon from 'megalodon' -import { ipcRenderer } from 'electron' -import Visibility from '~/src/constants/visibility' -import Status from './NewToot/Status' - -const NewToot = { - namespaced: true, - modules: { - Status - }, - state: { - modalOpen: false, - initialStatus: '', - initialSpoiler: '', - replyToMessage: null, - blockSubmit: false, - attachedMedias: [], - visibility: Visibility.Public.value, - sensitive: false, - attachedMediaId: 0, - pinedHashtag: false, - hashtags: [] - }, - mutations: { - changeModal (state, value) { - state.modalOpen = value - }, - setReplyTo (state, message) { - state.replyToMessage = message - }, - updateInitialStatus (state, status) { - state.initialStatus = status - }, - updateInitialSpoiler (state, cw) { - state.initialSpoiler = cw - }, - changeBlockSubmit (state, value) { - state.blockSubmit = value - }, - appendAttachedMedias (state, media) { - state.attachedMedias = state.attachedMedias.concat([media]) - }, - clearAttachedMedias (state) { - state.attachedMedias = [] - }, - removeMedia (state, media) { - state.attachedMedias = state.attachedMedias.filter(m => m.id !== media.id) - }, - /** - * changeVisibilityValue - * Update visibility using direct value - * @param state vuex state object - * @param value visibility value - */ - changeVisibilityValue (state, value) { - state.visibility = value - }, - changeSensitive (state, value) { - state.sensitive = value - }, - updateMediaId (state, value) { - state.attachedMediaId = value - }, - changePinedHashtag (state, value) { - state.pinedHashtag = value - }, - updateHashtags (state, tags) { - state.hashtags = tags - } - }, - getters: { - hashtagInserting (state) { - return !state.replyToMessage && state.pinedHashtag - } - }, - actions: { - async updateMedia ({ rootState }, media) { - if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { - throw new AuthenticationError() - } - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return Promise.all( - Object.keys(media).map(async id => { - return client.put(`/media/${id}`, { description: media[id] }) - } - )).catch(err => { - console.error(err) - throw err - }) - }, - async postToot ({ state, commit, rootState }, form) { - if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { - throw new AuthenticationError() - } - if (state.blockSubmit) { - return - } - commit('changeBlockSubmit', true) - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.post('/statuses', form) - .then(res => { - ipcRenderer.send('toot-action-sound') - return res.data - }) - .finally(() => { - commit('changeBlockSubmit', false) - }) - }, - openReply ({ commit, rootState }, message) { - commit('setReplyTo', message) - const mentionAccounts = [message.account.acct].concat(message.mentions.map(a => a.acct)) - .filter((a, i, self) => self.indexOf(a) === i) - .filter((a) => a !== rootState.TimelineSpace.account.username) - commit('updateInitialStatus', `${mentionAccounts.map(m => `@${m}`).join(' ')} `) - commit('updateInitialSpoiler', message.spoiler_text) - commit('changeModal', true) - let value = Visibility.Public.value - Object.keys(Visibility).map(key => { - const target = Visibility[key] - if (target.key === message.visibility) { - value = target.value - } - }) - commit('changeVisibilityValue', value) - }, - openModal ({ dispatch, commit, state }) { - if (!state.replyToMessage && state.pinedHashtag) { - commit('updateInitialStatus', state.hashtags.map(t => ` #${t.name}`).join()) - } - commit('changeModal', true) - dispatch('fetchVisibility') - }, - closeModal ({ commit }) { - commit('changeModal', false) - commit('updateInitialStatus', '') - commit('updateInitialSpoiler', '') - commit('setReplyTo', null) - commit('changeBlockSubmit', false) - commit('clearAttachedMedias') - commit('changeSensitive', false) - commit('changeVisibilityValue', Visibility.Public.value) - }, - uploadImage ({ commit, rootState }, image) { - commit('changeBlockSubmit', true) - if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { - throw new AuthenticationError() - } - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - const formData = new FormData() - formData.append('file', image) - return client.post('/media', formData) - .then(res => { - commit('changeBlockSubmit', false) - if (res.data.type === 'unknown') throw new UnknownTypeError() - commit('appendAttachedMedias', res.data) - return res.data - }) - .catch(err => { - commit('changeBlockSubmit', false) - console.error(err) - throw err - }) - }, - incrementMediaId ({ commit, state }) { - commit('updateMediaId', state.attachedMediaId + 1) - }, - resetMediaId ({ commit }) { - commit('updateMediaId', 0) - }, - updateHashtags ({ commit, state }, tags) { - if (state.pinedHashtag) { - commit('updateHashtags', tags) - } - }, - fetchVisibility ({ commit, rootState }) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - return client.get('/accounts/verify_credentials') - .then(res => { - const visibility = Object.values(Visibility).find((v) => { - return v.key === res.data.source.privacy - }) - commit('changeVisibilityValue', visibility.value) - return res.data - }) - } - } -} - -export default NewToot - -class AuthenticationError {} -class UnknownTypeError {} diff --git a/src/renderer/store/TimelineSpace/Modals/NewToot.ts b/src/renderer/store/TimelineSpace/Modals/NewToot.ts new file mode 100644 index 00000000..68f16c4e --- /dev/null +++ b/src/renderer/store/TimelineSpace/Modals/NewToot.ts @@ -0,0 +1,249 @@ +import Mastodon, { Status, Attachment, Tag, Response, Account } from 'megalodon' +import { ipcRenderer } from 'electron' +import Visibility from '~/src/constants/visibility' +import TootStatus, { StatusState } from './NewToot/Status' +import { Module, MutationTree, ActionTree, GetterTree } from 'vuex' +import { RootState } from '@/store' +import VisibilityType from '~/src/types/visibility' + +export interface NewTootState { + modalOpen: boolean, + initialStatus: string, + initialSpoiler: string, + replyToMessage: Status | null, + blockSubmit: boolean, + attachedMedias: Array, + visibility: number, + sensitive: boolean, + attachedMediaId: number, + pinedHashtag: boolean, + hashtags: Array +} + +export interface NewTootModuleState extends NewTootState { + Status: StatusState +} + +const state = (): NewTootState => ({ + modalOpen: false, + initialStatus: '', + initialSpoiler: '', + replyToMessage: null, + blockSubmit: false, + attachedMedias: [], + visibility: Visibility.Public.value, + sensitive: false, + attachedMediaId: 0, + pinedHashtag: false, + hashtags: [] +}) + +export const MUTATION_TYPES = { + CHANGE_MODAL: 'changeModal', + SET_REPLY_TO: 'setReplyTo', + UPDATE_INITIAL_STATUS: 'updateInitialStatus', + UPDATE_INITIAL_SPOILER: 'updateInitialSpoiler', + CHANGE_BLOCK_SUBMIT: 'changeBlockSubmit', + APPEND_ATTACHED_MEDIAS: 'appendAttachedMedias', + CLEAR_ATTACHED_MEDIAS: 'clearAttachedMedias', + REMOVE_MEDIA: 'removeMedia', + CHANGE_VISIBILITY_VALUE: 'changeVisibilityValue', + CHANGE_SENSITIVE: 'changeSensitive', + UPDATE_MEDIA_ID: 'updateMediaId', + CHANGE_PINED_HASHTAG: 'changePinedHashtag', + UPDATE_HASHTAGS: 'updateHashtags' +} + +const mutations: MutationTree = { + [MUTATION_TYPES.CHANGE_MODAL]: (state, value: boolean) => { + state.modalOpen = value + }, + [MUTATION_TYPES.SET_REPLY_TO]: (state, message: Status) => { + state.replyToMessage = message + }, + [MUTATION_TYPES.UPDATE_INITIAL_STATUS]: (state, status: string) => { + state.initialStatus = status + }, + [MUTATION_TYPES.UPDATE_INITIAL_SPOILER]: (state, cw: string) => { + state.initialSpoiler = cw + }, + [MUTATION_TYPES.CHANGE_BLOCK_SUBMIT]: (state, value: boolean) => { + state.blockSubmit = value + }, + [MUTATION_TYPES.APPEND_ATTACHED_MEDIAS]: (state, media: Attachment) => { + state.attachedMedias = state.attachedMedias.concat([media]) + }, + [MUTATION_TYPES.CLEAR_ATTACHED_MEDIAS]: (state) => { + state.attachedMedias = [] + }, + [MUTATION_TYPES.REMOVE_MEDIA]: (state, media: Attachment) => { + state.attachedMedias = state.attachedMedias.filter(m => m.id !== media.id) + }, + /** + * changeVisibilityValue + * Update visibility using direct value + * @param state vuex state object + * @param value visibility value + */ + [MUTATION_TYPES.CHANGE_VISIBILITY_VALUE]: (state, value: number) => { + state.visibility = value + }, + [MUTATION_TYPES.CHANGE_SENSITIVE]: (state, value: boolean) => { + state.sensitive = value + }, + [MUTATION_TYPES.UPDATE_MEDIA_ID]: (state, value: number) => { + state.attachedMediaId = value + }, + [MUTATION_TYPES.CHANGE_PINED_HASHTAG]: (state, value: boolean) => { + state.pinedHashtag = value + }, + [MUTATION_TYPES.UPDATE_HASHTAGS]: (state, tags: Array) => { + state.hashtags = tags + } +} + +const actions: ActionTree = { + updateMedia: async ({ rootState }, media: Attachment) => { + if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { + throw new AuthenticationError() + } + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + return Promise.all( + Object.keys(media).map(async id => { + return client.put(`/media/${id}`, { description: media[id] }) + })).catch(err => { + console.error(err) + throw err + }) + }, + postToot: async ({ state, commit, rootState }, form) => { + if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { + throw new AuthenticationError() + } + if (state.blockSubmit) { + return + } + commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, true) + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + return client.post('/statuses', form) + .then((res: Response) => { + ipcRenderer.send('toot-action-sound') + return res.data + }) + .finally(() => { + commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false) + }) + }, + openReply: ({ commit, rootState }, message: Status) => { + commit(MUTATION_TYPES.SET_REPLY_TO, message) + const mentionAccounts = [message.account.acct].concat(message.mentions.map(a => a.acct)) + .filter((a, i, self) => self.indexOf(a) === i) + .filter((a) => a !== rootState.TimelineSpace.account.username) + commit(MUTATION_TYPES.UPDATE_INITIAL_STATUS, `${mentionAccounts.map(m => `@${m}`).join(' ')} `) + commit(MUTATION_TYPES.UPDATE_INITIAL_SPOILER, message.spoiler_text) + commit(MUTATION_TYPES.CHANGE_MODAL, true) + let value: number = Visibility.Public.value + Object.keys(Visibility).map(key => { + const target = Visibility[key] + if (target.key === message.visibility) { + value = target.value + } + }) + commit(MUTATION_TYPES.CHANGE_VISIBILITY_VALUE, value) + }, + openModal: ({ dispatch, commit, state }) => { + if (!state.replyToMessage && state.pinedHashtag) { + commit(MUTATION_TYPES.UPDATE_INITIAL_STATUS, state.hashtags.map(t => ` #${t.name}`).join()) + } + commit(MUTATION_TYPES.CHANGE_MODAL, true) + dispatch('fetchVisibility') + }, + closeModal: ({ commit }) => { + commit(MUTATION_TYPES.CHANGE_MODAL, false) + commit(MUTATION_TYPES.UPDATE_INITIAL_STATUS, '') + commit(MUTATION_TYPES.UPDATE_INITIAL_SPOILER, '') + commit(MUTATION_TYPES.SET_REPLY_TO, null) + commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false) + commit(MUTATION_TYPES.CLEAR_ATTACHED_MEDIAS) + commit(MUTATION_TYPES.CHANGE_SENSITIVE, false) + commit(MUTATION_TYPES.CHANGE_VISIBILITY_VALUE, Visibility.Public.value) + }, + uploadImage: async ({ commit, rootState }, image: any) => { + commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, true) + if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { + throw new AuthenticationError() + } + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const formData = new FormData() + formData.append('file', image) + return client.post('/media', formData) + .then(res => { + commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false) + if (res.data.type === 'unknown') throw new UnknownTypeError() + commit(MUTATION_TYPES.APPEND_ATTACHED_MEDIAS, res.data) + return res.data + }) + .catch(err => { + commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false) + console.error(err) + throw err + }) + }, + incrementMediaId: ({ commit, state }) => { + commit(MUTATION_TYPES.UPDATE_MEDIA_ID, state.attachedMediaId + 1) + }, + resetMediaId: ({ commit }) => { + commit(MUTATION_TYPES.UPDATE_MEDIA_ID, 0) + }, + updateHashtags: ({ commit, state }, tags: Array) => { + if (state.pinedHashtag) { + commit(MUTATION_TYPES.UPDATE_HASHTAGS, tags) + } + }, + fetchVisibility: async ({ commit, rootState }) => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const res: Response = await client.get('/accounts/verify_credentials') + const visibility: VisibilityType = Object.values(Visibility as Array).find((v) => { + return v.key === res.data.source!.privacy + }) + if (visibility === undefined) { + throw new Error('Visibility value is invalid') + } + commit(MUTATION_TYPES.CHANGE_VISIBILITY_VALUE, visibility.value) + return res.data + } +} + +const getters: GetterTree = { + hashtagInserting: (state) => { + return !state.replyToMessage && state.pinedHashtag + } +} + +const NewToot: Module = { + namespaced: true, + modules: { + Status: TootStatus + }, + state: state, + mutations: mutations, + getters: getters, + actions: actions +} + +export default NewToot + +class AuthenticationError {} +class UnknownTypeError {} diff --git a/src/renderer/store/TimelineSpace/Modals/NewToot/Status.js b/src/renderer/store/TimelineSpace/Modals/NewToot/Status.js deleted file mode 100644 index 802a7fbe..00000000 --- a/src/renderer/store/TimelineSpace/Modals/NewToot/Status.js +++ /dev/null @@ -1,73 +0,0 @@ -import Mastodon from 'megalodon' - -const Status = { - namespaced: true, - state: { - filteredAccounts: [], - filteredHashtags: [] - }, - mutations: { - updateFilteredAccounts (state, accounts) { - state.filteredAccounts = accounts.map((a) => { - return { - name: `@${a.acct}`, - image: null - } - }) - }, - clearFilteredAccounts (state) { - state.filteredAccounts = [] - }, - updateFilteredHashtags (state, tags) { - state.filteredHashtags = tags.map((t) => { - return { - name: `#${t}`, - image: null - } - }) - }, - clearFilteredHashtags (state) { - state.filteredHashtags = [] - } - }, - actions: { - async searchAccount ({ commit, rootState }, word) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - const res = await client.get('/search', { q: word, resolve: false }) - commit('updateFilteredAccounts', res.data.accounts) - if (res.data.accounts.length === 0) throw new Error('Empty') - return res.data.accounts - }, - async searchHashtag ({ commit, rootState }, word) { - const client = new Mastodon( - rootState.TimelineSpace.account.accessToken, - rootState.TimelineSpace.account.baseURL + '/api/v1' - ) - const res = await client.get('/search', { q: word }) - commit('updateFilteredHashtags', res.data.hashtags) - if (res.data.hashtags.length === 0) throw new Error('Empty') - return res.data.hashtags - } - }, - getters: { - pickerEmojis: (state, getters, rootState) => { - return rootState.TimelineSpace.emojis.filter((e, i, array) => { - return (array.findIndex(ar => e.name === ar.name) === i) - }).map(e => { - return { - name: e.name, - short_names: [e.name], - text: e.name, - emoticons: [], - keywords: [e.name], - imageUrl: e.image - } - }) - } - } -} - -export default Status diff --git a/src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts b/src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts new file mode 100644 index 00000000..227149a2 --- /dev/null +++ b/src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts @@ -0,0 +1,104 @@ +import Mastodon, { Account, Tag, Response, Results } from 'megalodon' +import { Module, MutationTree, ActionTree, GetterTree } from 'vuex' +import { RootState } from '@/store/index' + +interface Suggest { + name: string, + image: string | null +} + +interface SuggestAccount extends Suggest {} + +interface SuggestHashtag extends Suggest {} + +export interface StatusState { + filteredAccounts: Array, + filteredHashtags: Array +} + +const state = (): StatusState => ({ + filteredAccounts: [], + filteredHashtags: [] +}) + +export const MUTATION_TYPES = { + UPDATE_FILTERED_ACCOUNTS: 'updateFilteredAccounts', + CLEAR_FILTERED_ACCOUNTS: 'clearFilteredAccounts', + UPDATE_FILTERED_HASHTAGS: 'updateFilteredHashtags', + CLAER_FILTERED_HASHTAGS: 'clearFilteredHashtags' +} + +const mutations: MutationTree = { + [MUTATION_TYPES.UPDATE_FILTERED_ACCOUNTS]: (state, accounts: Array) => { + state.filteredAccounts = accounts.map((a) => { + return { + name: `@${a.acct}`, + image: null + } + }) + }, + [MUTATION_TYPES.CLEAR_FILTERED_ACCOUNTS]: (state) => { + state.filteredAccounts = [] + }, + [MUTATION_TYPES.UPDATE_FILTERED_HASHTAGS]: (state, tags: Array) => { + state.filteredHashtags = tags.map((t) => { + return { + name: `#${t}`, + image: null + } + }) + }, + [MUTATION_TYPES.CLEAR_FILTERED_ACCOUNTS]: (state) => { + state.filteredHashtags = [] + } +} + +const actions: ActionTree = { + searchAccount: async ({ commit, rootState }, word: string) => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const res: Response = await client.get('/search', { q: word, resolve: false }) + commit(MUTATION_TYPES.UPDATE_FILTERED_ACCOUNTS, res.data.accounts) + if (res.data.accounts.length === 0) throw new Error('Empty') + return res.data.accounts + }, + searchHashtag: async ({ commit, rootState }, word: string) => { + const client = new Mastodon( + rootState.TimelineSpace.account.accessToken!, + rootState.TimelineSpace.account.baseURL + '/api/v1' + ) + const res: Response = await client.get('/search', { q: word }) + commit(MUTATION_TYPES.UPDATE_FILTERED_HASHTAGS, res.data.hashtags) + if (res.data.hashtags.length === 0) throw new Error('Empty') + return res.data.hashtags + } +} + +const getters: GetterTree = { + pickerEmojis: (_state, _getters, rootState) => { + return rootState.TimelineSpace.emojis.filter((e, i, array) => { + return (array.findIndex(ar => e.name === ar.name) === i) + }).map(e => { + return { + name: e.name, + short_names: [e.name], + text: e.name, + emoticons: [], + keywords: [e.name], + imageUrl: e.image + } + }) + } +} + +const Status: Module = { + namespaced: true, + state: state, + mutations: mutations, + actions: actions, + getters: getters +} + +export default Status diff --git a/tsconfig.json b/tsconfig.json index 821b00f6..0d48eb32 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,11 @@ "compilerOptions": { "target": "es5", "module": "es2015", - "lib": ["es6"], + "lib": [ + "dom", + "dom.iterable", + "es6" + ], "sourceMap": true, "downlevelIteration": true, "strict": true,