forked from AkkomaGang/admin-fe
Add configuration for sharing emoji packs
This commit is contained in:
parent
795800e974
commit
2841de76cf
14 changed files with 971 additions and 6 deletions
|
@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Emoji pack configuration
|
||||||
|
|
||||||
## [1.1.0] - 2019-09-15
|
## [1.1.0] - 2019-09-15
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
@ -18,7 +18,8 @@ To compile everything for production run `yarn build:prod`.
|
||||||
|
|
||||||
#### Disabling features
|
#### Disabling features
|
||||||
|
|
||||||
You can disable certain AdminFE features, like reports or settings by modifying `config/prod.env.js` env variable `DISABLED_FEATURES`, e.g. if you want to compile AdminFE without "Settings" you'll need to set it to: `DISABLED_FEATURES: '["settings"]'`.
|
You can disable certain AdminFE features, like reports or settings by modifying `config/prod.env.js` env variable `DISABLED_FEATURES`, e.g. if you want to compile AdminFE without "Settings" you'll need to set it to: `DISABLED_FEATURES: '["settings"]'`,
|
||||||
|
to disable emoji pack settings add `"emoji-packs"` to the list.
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ const devWebpackConfig = merge(baseWebpackConfig, {
|
||||||
poll: config.dev.poll
|
poll: config.dev.poll
|
||||||
},
|
},
|
||||||
headers: {
|
headers: {
|
||||||
'content-security-policy': "base-uri 'self'; frame-ancestors 'none'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; manifest-src 'self'; script-src 'self';"
|
'content-security-policy': "base-uri 'self'; frame-ancestors 'none'; img-src 'self' data: https: http:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; manifest-src 'self'; script-src 'self';"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
147
src/api/emoji_packs.js
Normal file
147
src/api/emoji_packs.js
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
import request from '@/utils/request'
|
||||||
|
import { getToken } from '@/utils/auth'
|
||||||
|
import { baseName } from './utils'
|
||||||
|
|
||||||
|
import _ from 'lodash'
|
||||||
|
|
||||||
|
export async function deletePack(host, token, name) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: `/api/pleroma/emoji/packs/${name}`,
|
||||||
|
method: 'delete',
|
||||||
|
headers: authHeaders(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function reloadEmoji(host, token) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: '/api/pleroma/admin/reload_emoji',
|
||||||
|
method: 'post',
|
||||||
|
headers: authHeaders(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function importFromFS(host, token) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: '/api/pleroma/emoji/packs/import_from_fs',
|
||||||
|
method: 'post',
|
||||||
|
headers: authHeaders(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createPack(host, token, name) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: `/api/pleroma/emoji/packs/${name}`,
|
||||||
|
method: 'put',
|
||||||
|
headers: authHeaders(token)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listPacks(host) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: `/api/pleroma/emoji/packs/`,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downloadFrom(host, instance_address, pack_name, as, token) {
|
||||||
|
if (as.trim() === '') {
|
||||||
|
as = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: '/api/pleroma/emoji/packs/download_from',
|
||||||
|
method: 'post',
|
||||||
|
headers: authHeaders(token),
|
||||||
|
data: { instance_address, pack_name, as },
|
||||||
|
timeout: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function savePackMetadata(host, token, name, new_data) {
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: `/api/pleroma/emoji/packs/${name}/update_metadata`,
|
||||||
|
method: 'post',
|
||||||
|
headers: authHeaders(token),
|
||||||
|
data: { name, new_data },
|
||||||
|
timeout: 0 // This might take a long time
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function fileUpdateFormData(d) {
|
||||||
|
const data = new FormData()
|
||||||
|
|
||||||
|
_.each(d, (v, k) => {
|
||||||
|
data.set(k, v)
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updatePackFile(host, token, args) {
|
||||||
|
let data = null
|
||||||
|
|
||||||
|
switch (args.action) {
|
||||||
|
case 'add': {
|
||||||
|
const { shortcode, file, fileName } = args
|
||||||
|
|
||||||
|
data = fileUpdateFormData({
|
||||||
|
action: 'add',
|
||||||
|
shortcode: shortcode,
|
||||||
|
file: file
|
||||||
|
})
|
||||||
|
if (fileName.trim() !== '') {
|
||||||
|
data.set('filename', fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'update': {
|
||||||
|
const { oldName, newName, newFilename } = args
|
||||||
|
|
||||||
|
data = fileUpdateFormData({
|
||||||
|
action: 'update',
|
||||||
|
shortcode: oldName,
|
||||||
|
new_shortcode: newName,
|
||||||
|
new_filename: newFilename
|
||||||
|
})
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'remove': {
|
||||||
|
const { name } = args
|
||||||
|
data = fileUpdateFormData({
|
||||||
|
action: 'remove',
|
||||||
|
shortcode: name
|
||||||
|
})
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { packName } = args
|
||||||
|
|
||||||
|
return await request({
|
||||||
|
baseURL: baseName(host),
|
||||||
|
url: `/api/pleroma/emoji/packs/${packName}/update_file`,
|
||||||
|
method: 'post',
|
||||||
|
headers: authHeaders(token),
|
||||||
|
data: data,
|
||||||
|
timeout: 0
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addressOfEmojiInPack(host, packName, name) {
|
||||||
|
// This needs http because hackney on the BE does not understand URLs with just "//"
|
||||||
|
return `http://${baseName(host)}/emoji/${packName}/${name}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}
|
|
@ -66,7 +66,8 @@ export default {
|
||||||
externalLink: 'External Link',
|
externalLink: 'External Link',
|
||||||
users: 'Users',
|
users: 'Users',
|
||||||
reports: 'Reports',
|
reports: 'Reports',
|
||||||
settings: 'Settings'
|
settings: 'Settings',
|
||||||
|
'emoji-packs': 'Emoji packs'
|
||||||
},
|
},
|
||||||
navbar: {
|
navbar: {
|
||||||
logOut: 'Log Out',
|
logOut: 'Log Out',
|
||||||
|
|
|
@ -35,6 +35,20 @@ const reports = {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const emojiPacksDisabled = disabledFeatures.includes('emoji-packs')
|
||||||
|
const emojiPacks = {
|
||||||
|
path: '/emoji-packs',
|
||||||
|
component: Layout,
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
component: () => import('@/views/emoji-packs/index'),
|
||||||
|
name: 'Emoji packs',
|
||||||
|
meta: { title: 'emoji-packs', icon: 'settings', noCache: true }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
export const constantRouterMap = [
|
export const constantRouterMap = [
|
||||||
{
|
{
|
||||||
path: '/redirect',
|
path: '/redirect',
|
||||||
|
@ -100,6 +114,7 @@ export const asyncRouterMap = [
|
||||||
},
|
},
|
||||||
...(settingsDisabled ? [] : [settings]),
|
...(settingsDisabled ? [] : [settings]),
|
||||||
...(reportsDisabled ? [] : [reports]),
|
...(reportsDisabled ? [] : [reports]),
|
||||||
|
...(emojiPacksDisabled ? [] : [emojiPacks]),
|
||||||
{
|
{
|
||||||
path: '/users/:id',
|
path: '/users/:id',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import user from './modules/user'
|
||||||
import userProfile from './modules/userProfile'
|
import userProfile from './modules/userProfile'
|
||||||
import users from './modules/users'
|
import users from './modules/users'
|
||||||
import getters from './getters'
|
import getters from './getters'
|
||||||
|
import emoji_packs from './modules/emoji_packs.js'
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
@ -23,7 +24,8 @@ const store = new Vuex.Store({
|
||||||
tagsView,
|
tagsView,
|
||||||
user,
|
user,
|
||||||
userProfile,
|
userProfile,
|
||||||
users
|
users,
|
||||||
|
emoji_packs
|
||||||
},
|
},
|
||||||
getters
|
getters
|
||||||
})
|
})
|
||||||
|
|
129
src/store/modules/emoji_packs.js
Normal file
129
src/store/modules/emoji_packs.js
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
import { listPacks,
|
||||||
|
downloadFrom,
|
||||||
|
reloadEmoji,
|
||||||
|
createPack,
|
||||||
|
deletePack,
|
||||||
|
savePackMetadata,
|
||||||
|
importFromFS,
|
||||||
|
updatePackFile } from '@/api/emoji_packs'
|
||||||
|
|
||||||
|
import { Message } from 'element-ui'
|
||||||
|
|
||||||
|
import Vue from 'vue'
|
||||||
|
|
||||||
|
const packs = {
|
||||||
|
state: {
|
||||||
|
localPacks: {},
|
||||||
|
remotePacks: {}
|
||||||
|
},
|
||||||
|
mutations: {
|
||||||
|
SET_LOCAL_PACKS: (state, packs) => {
|
||||||
|
state.localPacks = packs
|
||||||
|
},
|
||||||
|
SET_REMOTE_PACKS: (state, packs) => {
|
||||||
|
state.remotePacks = packs
|
||||||
|
},
|
||||||
|
|
||||||
|
UPDATE_LOCAL_PACK_VAL: (state, { name, key, value }) => {
|
||||||
|
Vue.set(state.localPacks[name]['pack'], key, value)
|
||||||
|
},
|
||||||
|
|
||||||
|
UPDATE_LOCAL_PACK_PACK: (state, { name, pack }) => {
|
||||||
|
state.localPacks[name]['pack'] = pack
|
||||||
|
},
|
||||||
|
|
||||||
|
UPDATE_LOCAL_PACK_FILES: (state, { name, files }) => {
|
||||||
|
// Use vue.set in case "files" was null
|
||||||
|
Vue.set(
|
||||||
|
state.localPacks[name],
|
||||||
|
'files',
|
||||||
|
files
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
async SetLocalEmojiPacks({ commit, getters, state }) {
|
||||||
|
const { data } = await listPacks(getters.authHost)
|
||||||
|
commit('SET_LOCAL_PACKS', data)
|
||||||
|
},
|
||||||
|
async SetRemoteEmojiPacks({ commit, getters, state }, { remoteInstance }) {
|
||||||
|
const { data } = await listPacks(remoteInstance)
|
||||||
|
commit('SET_REMOTE_PACKS', data)
|
||||||
|
},
|
||||||
|
async DownloadFrom({ commit, getters, state }, { instanceAddress, packName, as }) {
|
||||||
|
const result = await downloadFrom(getters.authHost, instanceAddress, packName, as, getters.token)
|
||||||
|
|
||||||
|
if (result.data === 'ok') {
|
||||||
|
Message({
|
||||||
|
message: `Successfully downloaded ${packName}`,
|
||||||
|
type: 'success',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async ReloadEmoji({ commit, getters, state }) {
|
||||||
|
await reloadEmoji(getters.authHost, getters.token)
|
||||||
|
},
|
||||||
|
async ImportFromFS({ commit, getters, state }) {
|
||||||
|
const result = await importFromFS(getters.authHost, getters.token)
|
||||||
|
|
||||||
|
if (result.status === 200) {
|
||||||
|
const message = result.data.length > 0 ? `Successfully imported ${result.data}` : 'No new packs to import'
|
||||||
|
|
||||||
|
Message({
|
||||||
|
message,
|
||||||
|
type: 'success',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async DeletePack({ commit, getters, state }, { name }) {
|
||||||
|
await deletePack(getters.authHost, getters.token, name)
|
||||||
|
},
|
||||||
|
async CreatePack({ commit, getters, state }, { name }) {
|
||||||
|
await createPack(getters.authHost, getters.token, name)
|
||||||
|
},
|
||||||
|
|
||||||
|
async UpdateLocalPackVal({ commit, getters, state }, args) {
|
||||||
|
commit('UPDATE_LOCAL_PACK_VAL', args)
|
||||||
|
},
|
||||||
|
|
||||||
|
async SavePackMetadata({ commit, getters, state }, { packName }) {
|
||||||
|
const result =
|
||||||
|
await savePackMetadata(
|
||||||
|
getters.authHost,
|
||||||
|
getters.token,
|
||||||
|
packName,
|
||||||
|
state.localPacks[packName]['pack']
|
||||||
|
)
|
||||||
|
|
||||||
|
if (result.status === 200) {
|
||||||
|
Message({
|
||||||
|
message: `Successfully updated ${packName} metadata`,
|
||||||
|
type: 'success',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
|
|
||||||
|
commit('UPDATE_LOCAL_PACK_PACK', { name: packName, pack: result.data })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async UpdateAndSavePackFile({ commit, getters, state }, args) {
|
||||||
|
const result = await updatePackFile(getters.authHost, getters.token, args)
|
||||||
|
|
||||||
|
if (result.status === 200) {
|
||||||
|
const { packName } = args
|
||||||
|
|
||||||
|
Message({
|
||||||
|
message: `Successfully updated ${packName} files`,
|
||||||
|
type: 'success',
|
||||||
|
duration: 5 * 1000
|
||||||
|
})
|
||||||
|
|
||||||
|
commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default packs
|
|
@ -10,9 +10,9 @@ const service = axios.create({
|
||||||
service.interceptors.response.use(
|
service.interceptors.response.use(
|
||||||
response => response,
|
response => response,
|
||||||
error => {
|
error => {
|
||||||
console.log('err' + error)
|
console.log('Error ' + error)
|
||||||
Message({
|
Message({
|
||||||
message: error.message,
|
message: `${error.message} - ${error.response.data}`,
|
||||||
type: 'error',
|
type: 'error',
|
||||||
duration: 5 * 1000
|
duration: 5 * 1000
|
||||||
})
|
})
|
||||||
|
|
218
src/views/emoji-packs/components/EmojiPack.vue
Normal file
218
src/views/emoji-packs/components/EmojiPack.vue
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h2>{{ name }}</h2>
|
||||||
|
|
||||||
|
<prop-editing-row name="Share pack">
|
||||||
|
<el-switch v-model="share" :disabled="!isLocal" />
|
||||||
|
</prop-editing-row>
|
||||||
|
<prop-editing-row name="Homepage">
|
||||||
|
<el-input v-if="isLocal" v-model="homepage" />
|
||||||
|
<el-input v-else :value="homepage" />
|
||||||
|
</prop-editing-row>
|
||||||
|
<prop-editing-row name="Description">
|
||||||
|
<el-input v-if="isLocal" :rows="2" v-model="description" type="textarea" />
|
||||||
|
<el-input v-else :rows="2" :value="description" type="textarea" />
|
||||||
|
</prop-editing-row>
|
||||||
|
<prop-editing-row name="License">
|
||||||
|
<el-input v-if="isLocal" v-model="license" />
|
||||||
|
<el-input v-else :value="license" />
|
||||||
|
</prop-editing-row>
|
||||||
|
<prop-editing-row name="Fallback source">
|
||||||
|
<el-input v-if="isLocal" v-model="fallbackSrc" />
|
||||||
|
<el-input v-else :value="fallbackSrc" />
|
||||||
|
</prop-editing-row>
|
||||||
|
|
||||||
|
<prop-editing-row v-if="fallbackSrc && fallbackSrc.trim() !== ''" name="Fallback source SHA">
|
||||||
|
{{ pack.pack["fallback-src-sha256"] }}
|
||||||
|
</prop-editing-row>
|
||||||
|
|
||||||
|
<el-button v-if="isLocal" type="success" @click="savePackMetadata">Save pack metadata</el-button>
|
||||||
|
|
||||||
|
<el-collapse v-model="shownPackEmoji" class="contents-collapse">
|
||||||
|
<el-collapse-item :name="name" title="Show pack contents">
|
||||||
|
<new-emoji-uploader v-if="isLocal" :pack-name="name" class="new-emoji-uploader" />
|
||||||
|
|
||||||
|
<h4>Manage existing emoji</h4>
|
||||||
|
|
||||||
|
<single-emoji-editor
|
||||||
|
v-for="(file, ename) in pack.files"
|
||||||
|
:key="ename"
|
||||||
|
:host="host"
|
||||||
|
:pack-name="name"
|
||||||
|
:name="ename"
|
||||||
|
:file="file"
|
||||||
|
:is-local="isLocal" />
|
||||||
|
</el-collapse-item>
|
||||||
|
</el-collapse>
|
||||||
|
|
||||||
|
<div v-if="!isLocal" class="shared-pack-dl-box">
|
||||||
|
<div>
|
||||||
|
This will download the "{{ name }}" pack to the current instance under the name
|
||||||
|
"{{ downloadSharedAs.trim() === '' ? name : downloadSharedAs }}" (can be changed below).
|
||||||
|
It will then be usable and shareable from the current instance.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-button type="primary" @click="downloadFromInstance">
|
||||||
|
Download shared pack to current instance
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-input v-model="downloadSharedAs" class="dl-as-input" placeholder="Download as (optional)" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-link
|
||||||
|
v-if="pack.pack['can-download']"
|
||||||
|
:href="`//${host}/api/pleroma/emoji/packs/${name}/download_shared`"
|
||||||
|
type="primary"
|
||||||
|
target="_blank">
|
||||||
|
Download pack archive
|
||||||
|
</el-link>
|
||||||
|
|
||||||
|
<div v-if="isLocal" class="pack-actions">
|
||||||
|
<el-button type="danger" @click="deletePack">
|
||||||
|
Delete the local pack
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.shared-pack-dl-box {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dl-as-input {
|
||||||
|
margin: 1em;
|
||||||
|
max-width: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contents-collapse {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pack-actions {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-emoji-uploader {
|
||||||
|
margin-bottom: 3em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import PropEditingRow from './PropertyEditingRow.vue'
|
||||||
|
import SingleEmojiEditor from './SingleEmojiEditor.vue'
|
||||||
|
import NewEmojiUploader from './NewEmojiUploader.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
|
||||||
|
components: { PropEditingRow, SingleEmojiEditor, NewEmojiUploader },
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
pack: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
host: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
isLocal: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
shownPackEmoji: [],
|
||||||
|
downloadSharedAs: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
share: {
|
||||||
|
get() { return this.pack.pack['share-files'] },
|
||||||
|
set(value) {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'UpdateLocalPackVal',
|
||||||
|
{ name: this.name, key: 'share-files', value }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
homepage: {
|
||||||
|
get() { return this.pack.pack['homepage'] },
|
||||||
|
set(value) {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'UpdateLocalPackVal',
|
||||||
|
{ name: this.name, key: 'homepage', value }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
description: {
|
||||||
|
get() { return this.pack.pack['description'] },
|
||||||
|
set(value) {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'UpdateLocalPackVal',
|
||||||
|
{ name: this.name, key: 'description', value }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
license: {
|
||||||
|
get() { return this.pack.pack['license'] },
|
||||||
|
set(value) {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'UpdateLocalPackVal',
|
||||||
|
{ name: this.name, key: 'license', value }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fallbackSrc: {
|
||||||
|
get() { return this.pack.pack['fallback-src'] },
|
||||||
|
set(value) {
|
||||||
|
if (value.trim() !== '') {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'UpdateLocalPackVal',
|
||||||
|
{ name: this.name, key: 'fallback-src', value }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'UpdateLocalPackVal',
|
||||||
|
{ name: this.name, key: 'fallback-src', value: null }
|
||||||
|
)
|
||||||
|
this.$store.dispatch(
|
||||||
|
'UpdateLocalPackVal',
|
||||||
|
{ name: this.name, key: 'fallback-src-sha256', value: null }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
downloadFromInstance() {
|
||||||
|
this.$store.dispatch(
|
||||||
|
'DownloadFrom',
|
||||||
|
{ instanceAddress: this.host, packName: this.name, as: this.downloadSharedAs }
|
||||||
|
).then(() => this.$store.dispatch('ReloadEmoji'))
|
||||||
|
.then(() => this.$store.dispatch('SetLocalEmojiPacks'))
|
||||||
|
},
|
||||||
|
|
||||||
|
deletePack() {
|
||||||
|
this.$confirm('This will delete the pack, are you sure?', 'Warning', {
|
||||||
|
confirmButtonText: 'Yes, delete the pack',
|
||||||
|
cancelButtonText: 'No, leave it be',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.$store.dispatch('DeletePack', { name: this.name })
|
||||||
|
.then(() => this.$store.dispatch('ReloadEmoji'))
|
||||||
|
.then(() => this.$store.dispatch('SetLocalEmojiPacks'))
|
||||||
|
}).catch(() => {})
|
||||||
|
},
|
||||||
|
|
||||||
|
savePackMetadata() {
|
||||||
|
this.$store.dispatch('SavePackMetadata', { packName: this.name })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
93
src/views/emoji-packs/components/NewEmojiUploader.vue
Normal file
93
src/views/emoji-packs/components/NewEmojiUploader.vue
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h4>Add new emoji to the pack</h4>
|
||||||
|
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="4" class="new-emoji-col">
|
||||||
|
<el-input v-model="shortcode" placeholder="Shortcode" />
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="8">
|
||||||
|
<div>
|
||||||
|
<h5>Upload a file</h5>
|
||||||
|
</div>
|
||||||
|
File name
|
||||||
|
<el-input v-model="customFileName" size="mini" placeholder="Custom file name (optional)"/>
|
||||||
|
<input ref="fileUpload" type="file" accept="image/*" >
|
||||||
|
|
||||||
|
<div class="or">
|
||||||
|
or
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h5>Enter a URL</h5>
|
||||||
|
</div>
|
||||||
|
<el-input v-model="imageUploadURL" placeholder="Image URL" />
|
||||||
|
|
||||||
|
<small>
|
||||||
|
(If both are filled, the file is used)
|
||||||
|
</small>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="4" class="new-emoji-col">
|
||||||
|
<el-button :disabled="shortcode.trim() == ''" @click="upload">Upload</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.new-emoji-col {
|
||||||
|
margin-top: 8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.or {
|
||||||
|
margin: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
packName: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
shortcode: '',
|
||||||
|
imageUploadURL: '',
|
||||||
|
customFileName: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
upload() {
|
||||||
|
let file = null
|
||||||
|
|
||||||
|
if (this.$refs.fileUpload.files.length > 0) {
|
||||||
|
file = this.$refs.fileUpload.files[0]
|
||||||
|
} else if (this.imageUploadURL.trim() !== '') {
|
||||||
|
file = this.imageUploadURL
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file !== null) {
|
||||||
|
this.$store.dispatch('UpdateAndSavePackFile', {
|
||||||
|
action: 'add',
|
||||||
|
packName: this.packName,
|
||||||
|
shortcode: this.shortcode,
|
||||||
|
file: file,
|
||||||
|
fileName: this.customFileName
|
||||||
|
}).then(() => {
|
||||||
|
this.shortcode = ''
|
||||||
|
this.imageUploadURL = ''
|
||||||
|
|
||||||
|
this.$store.dispatch('ReloadEmoji')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
27
src/views/emoji-packs/components/PropertyEditingRow.vue
Normal file
27
src/views/emoji-packs/components/PropertyEditingRow.vue
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<template>
|
||||||
|
<el-row :gutter="20" class="prop-row">
|
||||||
|
<el-col :span="4">
|
||||||
|
<b>{{ name }}</b>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="10">
|
||||||
|
<slot/>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.prop-row {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
175
src/views/emoji-packs/components/SingleEmojiEditor.vue
Normal file
175
src/views/emoji-packs/components/SingleEmojiEditor.vue
Normal file
|
@ -0,0 +1,175 @@
|
||||||
|
<template>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="4">
|
||||||
|
<el-input v-if="isLocal" v-model="modifyingName" placeholder="Name/Shortcode" />
|
||||||
|
<el-input v-else :value="modifyingName" placeholder="Name/Shortcode" />
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-input v-if="isLocal" v-model="modifyingFile" placeholder="File"/>
|
||||||
|
<el-input v-else :value="modifyingFile" placeholder="File"/>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col v-if="isLocal" :span="2">
|
||||||
|
<el-button type="primary" @click="update">Update</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col v-if="isLocal" :span="2">
|
||||||
|
<el-button type="danger" @click="remove">Remove</el-button>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col v-if="!isLocal" :span="4">
|
||||||
|
<el-popover v-model="copyToLocalVisible" placement="bottom">
|
||||||
|
<p>Select the local pack to copy to</p>
|
||||||
|
<el-select v-model="copyToLocalPackName" placeholder="Local pack">
|
||||||
|
<el-option
|
||||||
|
v-for="(_pack, name) in $store.state.emoji_packs.localPacks"
|
||||||
|
:key="name"
|
||||||
|
:label="name"
|
||||||
|
:value="name" />
|
||||||
|
</el-select>
|
||||||
|
|
||||||
|
<p>Specify a custom shortcode (leave empty to use the same shortcode)</p>
|
||||||
|
<el-input v-model="copyToShortcode" placeholder="Shortcode (optional)" />
|
||||||
|
|
||||||
|
<p>Specify a custom filename (leavy empty to use the same filename)</p>
|
||||||
|
<el-input v-model="copyToFilename" placeholder="Filename (optional)" />
|
||||||
|
|
||||||
|
<el-button
|
||||||
|
:disabled="!copyToLocalPackName"
|
||||||
|
type="success"
|
||||||
|
class="copy-to-local-button"
|
||||||
|
@click="copyToLocal">Copy</el-button>
|
||||||
|
|
||||||
|
<el-button slot="reference" type="primary">Copy to local pack...</el-button>
|
||||||
|
</el-popover>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="2">
|
||||||
|
<img
|
||||||
|
:src="addressOfEmojiInPack(host, packName, file)"
|
||||||
|
class="emoji-preview-img">
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.emoji-preview-img {
|
||||||
|
max-width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-to-local-button {
|
||||||
|
margin-top: 2em;
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { addressOfEmojiInPack } from '@/api/emoji_packs'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
host: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
packName: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
file: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
isLocal: {
|
||||||
|
type: Boolean,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
newName: null,
|
||||||
|
newFile: null,
|
||||||
|
|
||||||
|
copyToLocalPackName: null,
|
||||||
|
copyToLocalVisible: false,
|
||||||
|
copyToShortcode: '',
|
||||||
|
copyToFilename: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
modifyingName: {
|
||||||
|
get() {
|
||||||
|
// Return a modified name if it was actually modified, otherwise return the old name
|
||||||
|
return this.newName !== null ? this.newName : this.name
|
||||||
|
},
|
||||||
|
set(val) { this.newName = val }
|
||||||
|
},
|
||||||
|
modifyingFile: {
|
||||||
|
get() {
|
||||||
|
// Return a modified name if it was actually modified, otherwise return the old name
|
||||||
|
return this.newFile !== null ? this.newFile : this.file
|
||||||
|
},
|
||||||
|
set(val) { this.newFile = val }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
update() {
|
||||||
|
this.$store.dispatch('UpdateAndSavePackFile', {
|
||||||
|
action: 'update',
|
||||||
|
packName: this.packName,
|
||||||
|
oldName: this.name,
|
||||||
|
newName: this.modifyingName,
|
||||||
|
newFilename: this.modifyingFile
|
||||||
|
}).then(() => {
|
||||||
|
this.newName = null
|
||||||
|
this.newFile = null
|
||||||
|
|
||||||
|
this.$store.dispatch('ReloadEmoji')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
remove() {
|
||||||
|
this.$confirm('This will delete the emoji, are you sure?', 'Warning', {
|
||||||
|
confirmButtonText: 'Yes, delete the emoji',
|
||||||
|
cancelButtonText: 'No, leave it be',
|
||||||
|
type: 'warning'
|
||||||
|
}).then(() => {
|
||||||
|
this.$store.dispatch('UpdateAndSavePackFile', {
|
||||||
|
action: 'remove',
|
||||||
|
packName: this.packName,
|
||||||
|
name: this.name
|
||||||
|
}).then(() => {
|
||||||
|
this.newName = null
|
||||||
|
this.newFile = null
|
||||||
|
|
||||||
|
this.$store.dispatch('ReloadEmoji')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
copyToLocal() {
|
||||||
|
this.$store.dispatch('UpdateAndSavePackFile', {
|
||||||
|
action: 'add',
|
||||||
|
packName: this.copyToLocalPackName,
|
||||||
|
shortcode: this.copyToShortcode.trim() !== '' ? this.copyToShortcode.trim() : this.name,
|
||||||
|
fileName: this.copyToFilename.trim() !== '' ? this.copyToFilename.trim() : this.file,
|
||||||
|
file: this.addressOfEmojiInPack(this.host, this.packName, this.file)
|
||||||
|
}).then(() => {
|
||||||
|
this.copyToLocalPackName = null
|
||||||
|
this.copyToLocalVisible = false
|
||||||
|
this.copyToShortcode = ''
|
||||||
|
this.copyToFilename = ''
|
||||||
|
|
||||||
|
this.$store.dispatch('ReloadEmoji')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
addressOfEmojiInPack
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
151
src/views/emoji-packs/index.vue
Normal file
151
src/views/emoji-packs/index.vue
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
<template>
|
||||||
|
<el-container class="emoji-packs-container">
|
||||||
|
<el-header>
|
||||||
|
<h1>
|
||||||
|
Emoji packs
|
||||||
|
</h1>
|
||||||
|
</el-header>
|
||||||
|
|
||||||
|
<el-row class="local-packs-actions">
|
||||||
|
<el-button type="primary" @click="reloadEmoji">
|
||||||
|
Reload emoji
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-tooltip effects="dark" content="Importing from the filesystem will scan the directories and import those without pack.json but with emoji.txt or without neither" placement="bottom">
|
||||||
|
<el-button type="success" @click="importFromFS">
|
||||||
|
Import packs from the server filesystem
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-tabs v-model="activeName">
|
||||||
|
<el-tab-pane label="Local packs" name="local">
|
||||||
|
<div>
|
||||||
|
Local packs can be viewed and downloaded for backup here.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="local-packs-actions">
|
||||||
|
<el-popover
|
||||||
|
v-model="createNewPackVisible"
|
||||||
|
placement="bottom"
|
||||||
|
trigger="click">
|
||||||
|
|
||||||
|
<el-input v-model="newPackName" placeholder="Name" />
|
||||||
|
<el-button
|
||||||
|
:disabled="newPackName.trim() === ''"
|
||||||
|
class="create-pack-button"
|
||||||
|
type="success"
|
||||||
|
@click="createLocalPack" >
|
||||||
|
Create
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<el-button slot="reference" type="success">
|
||||||
|
Create a new local pack
|
||||||
|
</el-button>
|
||||||
|
</el-popover>
|
||||||
|
|
||||||
|
<el-button type="primary" @click="refreshLocalPacks">
|
||||||
|
Refresh local packs
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-for="(pack, name) in $store.state.emoji_packs.localPacks" :key="name">
|
||||||
|
<emoji-pack :name="name" :pack="pack" :host="$store.getters.authHost" :is-local="true" />
|
||||||
|
<el-divider />
|
||||||
|
</div>
|
||||||
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<el-tab-pane label="Remote packs" name="remote">
|
||||||
|
<el-input
|
||||||
|
v-model="remoteInstanceAddress"
|
||||||
|
class="remote-instance-input"
|
||||||
|
placeholder="Remote instance address" />
|
||||||
|
<el-button type="primary" @click="refreshRemotePacks">
|
||||||
|
Refresh remote packs
|
||||||
|
</el-button>
|
||||||
|
|
||||||
|
<div v-for="(pack, name) in $store.state.emoji_packs.remotePacks" :key="name">
|
||||||
|
<emoji-pack :name="name" :pack="pack" :host="remoteInstanceAddress" :is-local="false" />
|
||||||
|
<el-divider />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.emoji-packs-container {
|
||||||
|
margin: 22px 0 0 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.local-packs-actions {
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remote-instance-input {
|
||||||
|
max-width: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-pack-button {
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import EmojiPack from './components/EmojiPack'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { EmojiPack },
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
activeName: 'local',
|
||||||
|
remoteInstanceAddress: '',
|
||||||
|
downloadFromState: null,
|
||||||
|
|
||||||
|
newPackName: '',
|
||||||
|
createNewPackVisible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.refreshLocalPacks()
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
createLocalPack() {
|
||||||
|
this.createNewPackVisible = false
|
||||||
|
|
||||||
|
this.$store.dispatch('CreatePack', { name: this.newPackName })
|
||||||
|
.then(() => {
|
||||||
|
this.newPackName = ''
|
||||||
|
|
||||||
|
this.$store.dispatch('SetLocalEmojiPacks')
|
||||||
|
this.$store.dispatch('ReloadEmoji')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshLocalPacks() {
|
||||||
|
this.$store.dispatch('SetLocalEmojiPacks')
|
||||||
|
},
|
||||||
|
|
||||||
|
refreshRemotePacks() {
|
||||||
|
this.$store.dispatch('SetRemoteEmojiPacks', { remoteInstance: this.remoteInstanceAddress })
|
||||||
|
},
|
||||||
|
|
||||||
|
reloadEmoji() {
|
||||||
|
this.$store.dispatch('ReloadEmoji')
|
||||||
|
},
|
||||||
|
|
||||||
|
importFromFS() {
|
||||||
|
this.$store.dispatch('ImportFromFS')
|
||||||
|
.then(() => {
|
||||||
|
this.$store.dispatch('SetLocalEmojiPacks')
|
||||||
|
this.$store.dispatch('ReloadEmoji')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
Loading…
Reference in a new issue