diff --git a/CHANGELOG.md b/CHANGELOG.md index 2309c1bd..030c8593 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - On Reports page add links to reported account and the author of the report - In Notes add link to the note author's profile page - In Moderation log add link to the actor's profile page +- Support pagination of local emoji packs and files - Adds MRF Activity Expiration setting ### Changed diff --git a/src/api/emojiPacks.js b/src/api/emojiPacks.js index 61319cec..b2f980a1 100644 --- a/src/api/emojiPacks.js +++ b/src/api/emojiPacks.js @@ -37,10 +37,9 @@ export async function createPack(host, token, packName) { export async function deleteEmojiFile(packName, shortcode, host, token) { return await request({ baseURL: baseName(host), - url: `/api/pleroma/emoji/packs/${packName}/files`, + url: `/api/pleroma/emoji/packs/${packName}/files?shortcode=${shortcode}`, method: 'delete', - headers: authHeaders(token), - data: { shortcode } + headers: authHeaders(token) }) } @@ -53,25 +52,23 @@ export async function deletePack(host, token, packName) { }) } -export async function downloadFrom(host, instance, packName, as, token) { - if (as.trim() === '') { - as = null - } - +export async function downloadFrom(instanceAddress, packName, as, host, token) { return await request({ baseURL: baseName(host), url: '/api/pleroma/emoji/packs/download', method: 'post', headers: authHeaders(token), - data: { url: baseName(instance), name: packName, as }, + data: as.trim() === '' + ? { url: baseName(instanceAddress), name: packName } + : { url: baseName(instanceAddress), name: packName, as }, timeout: 0 }) } -export async function fetchPack(packName, host, token) { +export async function fetchPack(packName, page, pageSize, host, token) { return await request({ baseURL: baseName(host), - url: `/api/pleroma/emoji/packs/${packName}`, + url: `/api/pleroma/emoji/packs/${packName}?page=${page}&page_size=${pageSize}`, method: 'get', headers: authHeaders(token) }) @@ -86,11 +83,12 @@ export async function importFromFS(host, token) { }) } -export async function listPacks(host) { +export async function listPacks(page, pageSize, host, token) { return await request({ baseURL: baseName(host), - url: `/api/pleroma/emoji/packs/`, - method: 'get' + url: `/api/pleroma/emoji/packs?page=${page}&page_size=${pageSize}`, + method: 'get', + headers: authHeaders(token) }) } diff --git a/src/lang/en.js b/src/lang/en.js index 2ef9a89c..16ff35f2 100644 --- a/src/lang/en.js +++ b/src/lang/en.js @@ -475,6 +475,7 @@ export default { specifyShortcode: 'Specify a custom shortcode', specifyFilename: 'Specify a custom filename', copy: 'Copy', - copyToLocalPack: 'Copy to local pack' + copyToLocalPack: 'Copy to local pack', + emptyPack: 'This emoji pack is empty' } } diff --git a/src/store/index.js b/src/store/index.js index 586f2b07..e2fcd651 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,9 +1,11 @@ import Vue from 'vue' import Vuex from 'vuex' import app from './modules/app' +import emojiPacks from './modules/emojiPacks' import errorLog from './modules/errorLog' -import moderationLog from './modules/moderationLog' +import getters from './getters' import invites from './modules/invites' +import moderationLog from './modules/moderationLog' import peers from './modules/peers' import permission from './modules/permission' import relays from './modules/relays' @@ -14,8 +16,6 @@ import tagsView from './modules/tagsView' import user from './modules/user' import userProfile from './modules/userProfile' import users from './modules/users' -import getters from './getters' -import emojiPacks from './modules/emojiPacks.js' Vue.use(Vuex) @@ -23,6 +23,7 @@ const store = new Vuex.Store({ modules: { app, errorLog, + emojiPacks, moderationLog, invites, peers, @@ -34,8 +35,7 @@ const store = new Vuex.Store({ tagsView, user, userProfile, - users, - emojiPacks + users }, getters }) diff --git a/src/store/modules/emojiPacks.js b/src/store/modules/emojiPacks.js index e4748c26..6874ed9c 100644 --- a/src/store/modules/emojiPacks.js +++ b/src/store/modules/emojiPacks.js @@ -4,6 +4,7 @@ import { deleteEmojiFile, deletePack, downloadFrom, + fetchPack, importFromFS, listPacks, listRemotePacks, @@ -16,20 +17,41 @@ import { Message } from 'element-ui' import Vue from 'vue' -const packs = { +const emojiPacks = { state: { - activeCollapseItems: [], + activeTab: '', + currentFilesPage: 1, + currentPage: 1, + filesPageSize: 30, + localPackFilesCount: 0, localPacks: {}, + localPacksCount: 0, + pageSize: 50, remoteInstance: '', remotePacks: {} }, mutations: { - SET_ACTIVE_COLLAPSE_ITEMS: (state, items) => { - state.activeCollapseItems = items + SET_ACTIVE_TAB: (state, tab) => { + state.activeTab = tab + }, + SET_FILES_COUNT: (state, count) => { + state.localPackFilesCount = count + }, + SET_FILES_PAGE: (state, page) => { + state.currentFilesPage = page }, SET_LOCAL_PACKS: (state, packs) => { state.localPacks = packs }, + SET_LOCAL_PACKS_COUNT: (state, count) => { + state.localPacksCount = count + }, + SET_PACK_FILES: (state, { name, files }) => { + state.localPacks = { ...state.localPacks, [name]: { ...state.localPacks[name], files }} + }, + SET_PAGE: (state, page) => { + state.currentPage = page + }, SET_REMOTE_INSTANCE: (state, name) => { state.remoteInstance = name }, @@ -67,10 +89,12 @@ const packs = { commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data }) }, - async DeleteEmojiFile({ commit, getters }, { packName, shortcode }) { - let result + async DeleteEmojiFile({ commit, dispatch, getters, state }, { packName, shortcode }) { + const { [shortcode]: value, ...updatedPackFiles } = state.localPacks[packName].files + commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: updatedPackFiles }) + try { - result = await deleteEmojiFile(packName, shortcode, getters.authHost, getters.token) + await deleteEmojiFile(packName, shortcode, getters.authHost, getters.token) } catch (_e) { return } @@ -79,8 +103,11 @@ const packs = { type: 'success', duration: 5 * 1000 }) - - commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data }) + if (Object.keys(updatedPackFiles).length === 0 && state.currentFilesPage > 1) { + dispatch('FetchSinglePack', { name: packName, page: state.currentFilesPage - 1 }) + } else { + dispatch('FetchSinglePack', { name: packName, page: state.currentFilesPage }) + } }, async CreatePack({ getters }, { name }) { await createPack(getters.authHost, getters.token, name) @@ -89,7 +116,7 @@ const packs = { await deletePack(getters.authHost, getters.token, name) }, async DownloadFrom({ getters }, { instanceAddress, packName, as }) { - const result = await downloadFrom(getters.authHost, instanceAddress, packName, as, getters.token) + const result = await downloadFrom(instanceAddress, packName, as, getters.authHost, getters.token) if (result.data === 'ok') { Message({ @@ -99,6 +126,25 @@ const packs = { }) } }, + async FetchLocalEmojiPacks({ commit, getters, state }, page) { + const { data } = await listPacks(page, state.pageSize, getters.authHost, getters.token) + const { packs, count } = data + const updatedPacks = Object.keys(packs).reduce((acc, packName) => { + const { files, ...pack } = packs[packName] + acc[packName] = pack + return acc + }, {}) + commit('SET_LOCAL_PACKS', updatedPacks) + commit('SET_LOCAL_PACKS_COUNT', count) + commit('SET_PAGE', page) + }, + async FetchSinglePack({ getters, commit, state }, { name, page }) { + const { data } = await fetchPack(name, page, state.filesPageSize, getters.authHost, getters.token) + const { files, files_count } = data + commit('SET_PACK_FILES', { name, files }) + commit('SET_FILES_COUNT', files_count) + commit('SET_FILES_PAGE', page) + }, async ImportFromFS({ getters }) { const result = await importFromFS(getters.authHost, getters.token) @@ -136,12 +182,8 @@ const packs = { commit('UPDATE_LOCAL_PACK_PACK', { name: packName, pack: result.data }) } }, - SetActiveCollapseItems({ commit, state }, activeItems) { - commit('SET_ACTIVE_COLLAPSE_ITEMS', activeItems) - }, - async SetLocalEmojiPacks({ commit, getters }) { - const { data } = await listPacks(getters.authHost) - commit('SET_LOCAL_PACKS', data) + SetActiveTab({ commit }, activeTab) { + commit('SET_ACTIVE_TAB', activeTab) }, async SetRemoteEmojiPacks({ commit, getters }, { remoteInstance }) { const { data } = await listRemotePacks(getters.authHost, getters.token, remoteInstance) @@ -152,10 +194,19 @@ const packs = { SetRemoteInstance({ commit }, instance) { commit('SET_REMOTE_INSTANCE', instance) }, - async UpdateEmojiFile({ commit, getters }, { packName, shortcode, newShortcode, newFilename, force }) { - let result + async UpdateEmojiFile({ commit, dispatch, getters, state }, { packName, shortcode, newShortcode, newFilename, force }) { + const updatedPackFiles = Object.keys(state.localPacks[packName].files).reduce((acc, el) => { + if (el === shortcode) { + acc[newShortcode] = newFilename + } else { + acc[el] = state.localPacks[packName].files[el] + } + return acc + }, {}) + commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: updatedPackFiles }) + try { - result = await updateEmojiFile(packName, shortcode, newShortcode, newFilename, force, getters.authHost, getters.token) + await updateEmojiFile(packName, shortcode, newShortcode, newFilename, force, getters.authHost, getters.token) } catch (_e) { return } @@ -165,7 +216,7 @@ const packs = { duration: 5 * 1000 }) - commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data }) + dispatch('FetchSinglePack', { name: packName, page: state.currentFilesPage }) }, async UpdateLocalPackVal({ commit }, args) { commit('UPDATE_LOCAL_PACK_VAL', args) @@ -173,4 +224,4 @@ const packs = { } } -export default packs +export default emojiPacks diff --git a/src/views/emojiPacks/components/LocalEmojiPack.vue b/src/views/emojiPacks/components/LocalEmojiPack.vue index 1cf4bdae..8f401756 100644 --- a/src/views/emojiPacks/components/LocalEmojiPack.vue +++ b/src/views/emojiPacks/components/LocalEmojiPack.vue @@ -38,19 +38,32 @@ - + - - + +
+ +
+ {{ $t('emoji.emptyPack') }} +
+ +
@@ -86,6 +99,12 @@ export default { } }, computed: { + currentFilesPage() { + return this.$store.state.emojiPacks.currentFilesPage + }, + currentPage() { + return this.$store.state.emojiPacks.currentPage + }, isMobile() { return this.$store.state.app.device === 'mobile' }, @@ -101,6 +120,12 @@ export default { return '155px' } }, + localPackFilesCount() { + return this.$store.state.emojiPacks.localPackFilesCount + }, + pageSize() { + return this.$store.state.emojiPacks.filesPageSize + }, share: { get() { return this.pack.pack['share-files'] }, set(value) { @@ -159,6 +184,9 @@ export default { } }, methods: { + collapse() { + this.showPackContent = [] + }, deletePack() { this.$confirm('This will delete the pack, are you sure?', 'Warning', { confirmButtonText: 'Yes, delete the pack', @@ -167,9 +195,24 @@ export default { }).then(() => { this.$store.dispatch('DeletePack', { name: this.name }) .then(() => this.$store.dispatch('ReloadEmoji')) - .then(() => this.$store.dispatch('SetLocalEmojiPacks')) + .then(() => { + const { [this.name]: value, ...updatedPacks } = this.$store.state.emojiPacks.localPacks + if (Object.keys(updatedPacks).length === 0 && this.currentPage > 1) { + this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage - 1) + } else { + this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage) + } + }) }).catch(() => {}) }, + handleChange(openTabs, name) { + if (openTabs.includes('manageEmoji')) { + this.$store.dispatch('FetchSinglePack', { name, page: 1 }) + } + }, + handleFilesPageChange(page) { + this.$store.dispatch('FetchSinglePack', { name: this.name, page }) + }, savePackMetadata() { this.$store.dispatch('SavePackMetadata', { packName: this.name }) } @@ -217,6 +260,10 @@ export default { margin-bottom: 10px; } } +.files-pagination { + margin: 25px 0; + text-align: center; +} .has-background .el-collapse-item__header { background: #f6f6f6; } diff --git a/src/views/emojiPacks/components/RemoteEmojiPack.vue b/src/views/emojiPacks/components/RemoteEmojiPack.vue index 3ae2af5d..a3ebe017 100644 --- a/src/views/emojiPacks/components/RemoteEmojiPack.vue +++ b/src/views/emojiPacks/components/RemoteEmojiPack.vue @@ -24,7 +24,7 @@ - - + +
+ +
+ {{ $t('emoji.emptyPack') }}

@@ -92,6 +95,9 @@ export default { } }, computed: { + currentPage() { + return this.$store.state.emojiPacks.currentPage + }, isDesktop() { return this.$store.state.app.device === 'desktop' }, @@ -111,7 +117,7 @@ export default { } }, loadRemotePack() { - return this.$store.state.emojiPacks.activeCollapseItems.includes(this.name) + return this.$store.state.emojiPacks.activeTab === this.name }, remoteInstanceAddress() { return this.$store.state.emojiPacks.remoteInstance @@ -179,7 +185,7 @@ export default { 'DownloadFrom', { instanceAddress: this.remoteInstanceAddress, packName: this.name, as: this.downloadSharedAs } ).then(() => this.$store.dispatch('ReloadEmoji')) - .then(() => this.$store.dispatch('SetLocalEmojiPacks')) + .then(() => this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage)) } } } diff --git a/src/views/emojiPacks/components/SingleEmojiEditor.vue b/src/views/emojiPacks/components/SingleEmojiEditor.vue index bb82a298..9a773f67 100644 --- a/src/views/emojiPacks/components/SingleEmojiEditor.vue +++ b/src/views/emojiPacks/components/SingleEmojiEditor.vue @@ -103,7 +103,7 @@ export default { return this.$store.state.emojiPacks.localPacks }, remoteInstance() { - return new URL(this.$store.state.emojiPacks.remoteInstance).host + return this.$store.state.emojiPacks.remoteInstance } }, methods: { diff --git a/src/views/emojiPacks/index.vue b/src/views/emojiPacks/index.vue index c9173f43..0dd0eedc 100644 --- a/src/views/emojiPacks/index.vue +++ b/src/views/emojiPacks/index.vue @@ -6,56 +6,72 @@

- {{ $t('emoji.reloadEmoji') }} + {{ $t('emoji.reloadEmoji') }} - + {{ $t('emoji.importPacks') }}
- - - - {{ $t('emoji.refreshLocalPacks') }} - - -
- - - {{ $t('users.create') }} - + + + + + {{ $t('emoji.refreshLocalPacks') }} + + +
+ + + {{ $t('users.create') }} + +
+
+ + + + + +
+ - - - - - - - - -
- - - {{ $t('emoji.refreshRemote') }} - -
-
- - - - - - +
+ + + +
+ + + {{ $t('emoji.refreshRemote') }} + +
+
+ + + + + +
+
+
@@ -69,6 +85,7 @@ export default { components: { LocalEmojiPack, RebootButton, RemoteEmojiPack }, data() { return { + activeTab: 'local', newPackName: '', activeLocalPack: [], activeRemotePack: [], @@ -76,6 +93,9 @@ export default { } }, computed: { + currentPage() { + return this.$store.state.emojiPacks.currentPage + }, isMobile() { return this.$store.state.app.device === 'mobile' }, @@ -88,12 +108,18 @@ export default { } else if (this.isTablet) { return '180px' } else { - return '240px' + return '200px' } }, localPacks() { return this.$store.state.emojiPacks.localPacks }, + localPacksCount() { + return this.$store.state.emojiPacks.localPacksCount + }, + pageSize() { + return this.$store.state.emojiPacks.pageSize + }, remoteInstanceAddress: { get() { return this.$store.state.emojiPacks.remoteInstance @@ -117,25 +143,23 @@ export default { .then(() => { this.newPackName = '' - this.$store.dispatch('SetLocalEmojiPacks') + this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage) this.$store.dispatch('ReloadEmoji') }) }, + handlePageChange(page) { + this.$store.dispatch('FetchLocalEmojiPacks', page) + }, importFromFS() { this.$store.dispatch('ImportFromFS') .then(() => { - this.$store.dispatch('SetLocalEmojiPacks') + this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage) this.$store.dispatch('ReloadEmoji') }) }, - sortPack(pack) { - const orderedFiles = Object.keys(pack.files).sort((a, b) => a.localeCompare(b)) - .map(key => [key, pack.files[key]]) - return { ...pack, files: orderedFiles } - }, refreshLocalPacks() { try { - this.$store.dispatch('SetLocalEmojiPacks') + this.$store.dispatch('FetchLocalEmojiPacks', this.currentPage) } catch (e) { return } @@ -160,15 +184,22 @@ export default { message: i18n.t('emoji.reloaded') }) }, - setActiveCollapseItems(activeItems) { - const items = Array.isArray(activeItems) ? activeItems : [activeItems] - this.$store.dispatch('SetActiveCollapseItems', items) + setActiveTab(activeTab) { + this.$refs.localEmojiPack.forEach(el => el.collapse()) + this.$store.dispatch('SetActiveTab', activeTab) } } }