diff --git a/src/renderer/components/TimelineSpace/Modals/NewToot.vue b/src/renderer/components/TimelineSpace/Modals/NewToot.vue index bb40cc14..a1d3bfee 100644 --- a/src/renderer/components/TimelineSpace/Modals/NewToot.vue +++ b/src/renderer/components/TimelineSpace/Modals/NewToot.vue @@ -4,20 +4,15 @@ :visible.sync="newTootModal" :before-close="closeConfirm" width="400px" - class="new-toot-modal"> + class="new-toot-modal" + >
- +
@@ -28,11 +23,12 @@ class="image-description" :placeholder="$t('modals.new_toot.description')" v-model="mediaDescriptions[media.id]" - v-shortkey="{left: ['arrowleft'], right: ['arrowright']}" + v-shortkey="{ left: ['arrowleft'], right: ['arrowright'] }" @shortkey="handleDescriptionKey" role="textbox" contenteditable="true" - aria-multiline="true"> + aria-multiline="true" + >
@@ -41,7 +37,7 @@ - +
@@ -69,24 +65,50 @@
- +
- + CW
- +
- {{ tootMax - status.length }} - {{ $t('modals.new_toot.cancel') }} - {{ $t('modals.new_toot.toot') }} +
+ + {{ tootMax - status.length }} + + {{ $t('modals.new_toot.cancel') }} + {{ + $t('modals.new_toot.toot') + }} +
@@ -103,7 +125,7 @@ export default { components: { Status }, - data () { + data() { return { status: '', mediaDescriptions: {}, @@ -114,7 +136,7 @@ export default { }, computed: { ...mapState('TimelineSpace/Modals/NewToot', { - replyToId: (state) => { + replyToId: state => { if (state.replyToMessage !== null) { return state.replyToMessage.id } else { @@ -128,7 +150,7 @@ export default { sensitive: state => state.sensitive, initialStatus: state => state.initialStatus, initialSpoiler: state => state.initialSpoiler, - visibilityIcon: (state) => { + visibilityIcon: state => { switch (state.visibility) { case Visibility.Public.value: return 'globe' @@ -141,19 +163,18 @@ export default { default: return 'globe' } - } + }, + loading: state => state.loading }), ...mapState('TimelineSpace', { tootMax: state => state.tootMax }), - ...mapGetters('TimelineSpace/Modals/NewToot', [ - 'hashtagInserting' - ]), + ...mapGetters('TimelineSpace/Modals/NewToot', ['hashtagInserting']), newTootModal: { - get () { + get() { return this.$store.state.TimelineSpace.Modals.NewToot.modalOpen }, - set (value) { + set(value) { if (value) { this.$store.dispatch('TimelineSpace/Modals/NewToot/openModal') } else { @@ -162,16 +183,19 @@ export default { } }, pinedHashtag: { - get () { + get() { return this.$store.state.TimelineSpace.Modals.NewToot.pinedHashtag }, - set (value) { + set(value) { this.$store.commit('TimelineSpace/Modals/NewToot/changePinedHashtag', value) } } }, + created() { + this.$store.dispatch('TimelineSpace/Modals/NewToot/setupLoading') + }, watch: { - newTootModal: function (newState, oldState) { + newTootModal: function(newState, oldState) { if (!oldState && newState) { this.showContentWarning = this.initialSpoiler this.status = this.initialStatus @@ -180,12 +204,12 @@ export default { } }, methods: { - close () { + close() { this.filteredAccount = [] this.$store.dispatch('TimelineSpace/Modals/NewToot/resetMediaId') this.$store.dispatch('TimelineSpace/Modals/NewToot/closeModal') }, - async toot () { + async toot() { if (!this.newTootModal) { return } @@ -195,7 +219,7 @@ export default { type: 'error' }) } - const visibilityKey = Object.keys(Visibility).find((key) => { + const visibilityKey = Object.keys(Visibility).find(key => { return Visibility[key].value === this.visibility }) let form = { @@ -217,15 +241,18 @@ export default { }) } form = Object.assign(form, { - media_ids: this.attachedMedias.map((m) => { return m.id }) + media_ids: this.attachedMedias.map(m => { + return m.id + }) }) } - const status = await this.$store.dispatch('TimelineSpace/Modals/NewToot/updateMedia', this.mediaDescriptions) + const status = await this.$store + .dispatch('TimelineSpace/Modals/NewToot/updateMedia', this.mediaDescriptions) .then(() => { return this.$store.dispatch('TimelineSpace/Modals/NewToot/postToot', form) }) - .catch((e) => { + .catch(e => { console.error(e) this.$message({ message: this.$t('message.toot_error'), @@ -235,10 +262,10 @@ export default { this.$store.dispatch('TimelineSpace/Modals/NewToot/updateHashtags', status.tags) this.close() }, - selectImage () { + selectImage() { this.$refs.image.click() }, - onChangeImage (e) { + onChangeImage(e) { if (e.target.files.item(0) === null || e.target.files.item(0) === undefined) { return } @@ -252,7 +279,7 @@ export default { } this.updateImage(file) }, - onPaste (e) { + onPaste(e) { const mimeTypes = clipboard.availableFormats().filter(type => type.startsWith('image')) if (mimeTypes.length === 0) { return @@ -268,43 +295,40 @@ export default { const file = new File([data], 'clipboard.picture', { type: mimeTypes[0] }) this.updateImage(file) }, - updateImage (file) { + updateImage(file) { this.$store.dispatch('TimelineSpace/Modals/NewToot/incrementMediaId') - this.$store.dispatch('TimelineSpace/Modals/NewToot/uploadImage', file) - .catch(() => { - this.$message({ - message: this.$t('message.attach_error'), - type: 'error' - }) + this.$store.dispatch('TimelineSpace/Modals/NewToot/uploadImage', file).catch(() => { + this.$message({ + message: this.$t('message.attach_error'), + type: 'error' }) + }) }, - removeAttachment (media) { + removeAttachment(media) { this.$store.commit('TimelineSpace/Modals/NewToot/removeMedia', media) delete this.mediaDescriptions[media.id] }, - changeVisibility (level) { + changeVisibility(level) { this.$store.commit('TimelineSpace/Modals/NewToot/changeVisibilityValue', level) }, - changeSensitive () { + changeSensitive() { this.$store.commit('TimelineSpace/Modals/NewToot/changeSensitive', !this.sensitive) }, - closeConfirm (done) { + closeConfirm(done) { if (this.status.length === 0) { done() } else { - this.$confirm( - this.$t('modals.new_toot.close_confirm'), - { - confirmButtonText: this.$t('modals.new_toot.close_confirm_ok'), - cancelButtonText: this.$t('modals.new_toot.close_confirm_cancel') - }) + this.$confirm(this.$t('modals.new_toot.close_confirm'), { + confirmButtonText: this.$t('modals.new_toot.close_confirm_ok'), + cancelButtonText: this.$t('modals.new_toot.close_confirm_cancel') + }) .then(_ => { done() }) .catch(_ => {}) } }, - handleDescriptionKey (event) { + handleDescriptionKey(event) { const current = event.target.selectionStart switch (event.srcKey) { case 'left': @@ -451,9 +475,20 @@ export default { color: #909399; } - .text-count { - padding-right: 10px; - color: #909399; + .info { + display: flex; + justify-content: flex-end; + align-items: center; + + .loading { + width: 18px; + margin-right: 4px; + } + + .text-count { + padding-right: 10px; + color: #909399; + } } .toot-action { diff --git a/src/renderer/store/TimelineSpace/Modals/NewToot.ts b/src/renderer/store/TimelineSpace/Modals/NewToot.ts index efeb6c5a..3228ebe6 100644 --- a/src/renderer/store/TimelineSpace/Modals/NewToot.ts +++ b/src/renderer/store/TimelineSpace/Modals/NewToot.ts @@ -4,19 +4,21 @@ import Visibility, { VisibilityType } from '~/src/constants/visibility' import TootStatus, { StatusState } from './NewToot/Status' import { Module, MutationTree, ActionTree, GetterTree } from 'vuex' import { RootState } from '@/store' +import AxiosLoading from '@/utils/axiosLoading' export interface NewTootState { - modalOpen: boolean, - initialStatus: string, - initialSpoiler: string, - replyToMessage: Status | null, - blockSubmit: boolean, - attachedMedias: Array, - visibility: number, - sensitive: boolean, - attachedMediaId: number, - pinedHashtag: boolean, + modalOpen: boolean + initialStatus: string + initialSpoiler: string + replyToMessage: Status | null + blockSubmit: boolean + attachedMedias: Array + visibility: number + sensitive: boolean + attachedMediaId: number + pinedHashtag: boolean hashtags: Array + loading: boolean } export interface NewTootModuleState extends NewTootState { @@ -34,7 +36,8 @@ const state = (): NewTootState => ({ sensitive: false, attachedMediaId: 0, pinedHashtag: false, - hashtags: [] + hashtags: [], + loading: false }) export const MUTATION_TYPES = { @@ -50,7 +53,8 @@ export const MUTATION_TYPES = { CHANGE_SENSITIVE: 'changeSensitive', UPDATE_MEDIA_ID: 'updateMediaId', CHANGE_PINED_HASHTAG: 'changePinedHashtag', - UPDATE_HASHTAGS: 'updateHashtags' + UPDATE_HASHTAGS: 'updateHashtags', + CHANGE_LOADING: 'changeLoading' } const mutations: MutationTree = { @@ -72,7 +76,7 @@ const mutations: MutationTree = { [MUTATION_TYPES.APPEND_ATTACHED_MEDIAS]: (state, media: Attachment) => { state.attachedMedias = state.attachedMedias.concat([media]) }, - [MUTATION_TYPES.CLEAR_ATTACHED_MEDIAS]: (state) => { + [MUTATION_TYPES.CLEAR_ATTACHED_MEDIAS]: state => { state.attachedMedias = [] }, [MUTATION_TYPES.REMOVE_MEDIA]: (state, media: Attachment) => { @@ -98,26 +102,34 @@ const mutations: MutationTree = { }, [MUTATION_TYPES.UPDATE_HASHTAGS]: (state, tags: Array) => { state.hashtags = tags + }, + [MUTATION_TYPES.CHANGE_LOADING]: (state, value: boolean) => { + state.loading = value } } const actions: ActionTree = { + setupLoading: ({ commit }) => { + const axiosLoading = new AxiosLoading() + axiosLoading.on('start', (_: number) => { + commit(MUTATION_TYPES.CHANGE_LOADING, true) + }) + axiosLoading.on('done', () => { + commit(MUTATION_TYPES.CHANGE_LOADING, false) + }) + }, 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' - ) + const client = new Mastodon(rootState.TimelineSpace.account.accessToken, rootState.TimelineSpace.account.baseURL + '/api/v1') const attachments = Object.keys(media).map(async id => { return client.put(`/media/${id}`, { description: media[id] }) }) - return Promise.all(attachments) - .catch(err => { - console.error(err) - throw err - }) + return Promise.all(attachments).catch(err => { + console.error(err) + throw err + }) }, postToot: async ({ state, commit, rootState }, form) => { if (rootState.TimelineSpace.account.accessToken === undefined || rootState.TimelineSpace.account.accessToken === null) { @@ -127,11 +139,9 @@ const actions: ActionTree = { 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) + 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 @@ -142,9 +152,10 @@ const actions: ActionTree = { }, openReply: ({ commit, rootState }, message: Status) => { commit(MUTATION_TYPES.SET_REPLY_TO, message) - const mentionAccounts = [message.account.acct].concat(message.mentions.map(a => a.acct)) + 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) + .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) @@ -179,13 +190,11 @@ const actions: ActionTree = { 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 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) + return client + .post('/media', formData) .then(res => { commit(MUTATION_TYPES.CHANGE_BLOCK_SUBMIT, false) if (res.data.type === 'unknown') throw new UnknownTypeError() @@ -210,12 +219,9 @@ const actions: ActionTree = { } }, fetchVisibility: 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 = await client.get('/accounts/verify_credentials') - const visibility: VisibilityType | undefined = (Object.values(Visibility) as Array).find((v) => { + const visibility: VisibilityType | undefined = (Object.values(Visibility) as Array).find(v => { return v.key === res.data.source!.privacy }) if (visibility === undefined) { @@ -227,7 +233,7 @@ const actions: ActionTree = { } const getters: GetterTree = { - hashtagInserting: (state) => { + hashtagInserting: state => { return !state.replyToMessage && state.pinedHashtag } } diff --git a/src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts b/src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts index 697bf16e..73915d7f 100644 --- a/src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts +++ b/src/renderer/store/TimelineSpace/Modals/NewToot/Status.ts @@ -3,7 +3,7 @@ import { Module, MutationTree, ActionTree, GetterTree } from 'vuex' import { RootState } from '@/store/index' interface Suggest { - name: string, + name: string image: string | null } @@ -12,7 +12,7 @@ interface SuggestAccount extends Suggest {} interface SuggestHashtag extends Suggest {} export interface StatusState { - filteredAccounts: Array, + filteredAccounts: Array filteredHashtags: Array } @@ -25,7 +25,7 @@ export const MUTATION_TYPES = { UPDATE_FILTERED_ACCOUNTS: 'updateFilteredAccounts', CLEAR_FILTERED_ACCOUNTS: 'clearFilteredAccounts', UPDATE_FILTERED_HASHTAGS: 'updateFilteredHashtags', - CLAER_FILTERED_HASHTAGS: 'clearFilteredHashtags' + CLEAR_FILTERED_HASHTAGS: 'clearFilteredHashtags' } const mutations: MutationTree = { @@ -35,7 +35,7 @@ const mutations: MutationTree = { image: null })) }, - [MUTATION_TYPES.CLEAR_FILTERED_ACCOUNTS]: (state) => { + [MUTATION_TYPES.CLEAR_FILTERED_ACCOUNTS]: state => { state.filteredAccounts = [] }, [MUTATION_TYPES.UPDATE_FILTERED_HASHTAGS]: (state, tags: Array) => { @@ -44,27 +44,21 @@ const mutations: MutationTree = { image: null })) }, - [MUTATION_TYPES.CLEAR_FILTERED_ACCOUNTS]: (state) => { + [MUTATION_TYPES.CLEAR_FILTERED_HASHTAGS]: 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 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 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') @@ -74,18 +68,20 @@ const actions: ActionTree = { 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 - } - }) + 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 + } + }) } }