Merge branch 'develop' into 'feature/route-for-status'

# Conflicts:
#   src/store/modules/status.js
This commit is contained in:
Angelina Filippova 2020-05-24 17:00:12 +00:00
commit 1c2691478b
38 changed files with 686 additions and 286 deletions

View file

@ -6,6 +6,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## Unreleased
### Changed
- Statuses count changes when an instance is selected and shows the amount of statuses from an originating instance
- Add a dialog window with a confirmation when a remove button is clicked on the Settings page
- Disable tab on the Settings page if there are no settings on this tab that can be changed in Admin FE
- Settings that can't be altered in Admin FE are removed: HTTP Signatures settings, Federation publisher modules and Oban Repo
### Fixed
- Send `true` and `false` as booleans if they are values of single selects on the Settings page
## [2.0.3] - 2020-04-29
### Added
- Link settings that enable registrations and invites

View file

@ -0,0 +1,4 @@
export async function fetchPeers(authHost, token) {
const data = ['lain.com', 'heaven.com']
return Promise.resolve({ data })
}

View file

@ -5,3 +5,66 @@ export async function changeStatusScope(id, sensitive, visibility, authHost, tok
export async function deleteStatus(id, authHost, token) {
return Promise.resolve()
}
export async function fetchStatusesByInstance({ instance, authHost, token, pageSize, page }) {
let data
if (pageSize === 1) {
data = page === 1 || page === 2
? [{
'account': {
'avatar': 'http://localhost:4000/images/avi.png',
'display_name': 'sky',
'url': 'http://localhost:4000/users/sky'
},
'content': 'A nice young couple contacted us from Brazil to decorate their newly acquired apartment.',
'created_at': '2020-01-31T18:20:01.000Z',
'id': '9rZIr0Jzao5Gjgfmro',
'sensitive': false,
'url': 'http://localhost:4000/objects/7af9abbd-fb6c-4318-aeb7-6636c138ac98',
'visibility': 'unlisted'
}]
: []
} else {
data = [
{
'account': {
'avatar': 'http://localhost:4000/images/avi.png',
'display_name': 'sky',
'url': 'http://localhost:4000/users/sky'
},
'content': 'A nice young couple contacted us from Brazil to decorate their newly acquired apartment.',
'created_at': '2020-01-31T18:20:01.000Z',
'id': '9rZIr0Jzao5Gjgfmro',
'sensitive': false,
'url': 'http://localhost:4000/objects/7af9abbd-fb6c-4318-aeb7-6636c138ac98',
'visibility': 'unlisted'
},
{
'account': {
'avatar': 'http://localhost:4000/images/avi.png',
'display_name': 'sky',
'url': 'http://localhost:4000/users/sky'
},
'content': 'the happiest man ever',
'created_at': '2019-11-23T12:56:18.000Z',
'id': '9pFoVfWMU3A96Rzq3k',
'sensitive': false,
'url': 'http://localhost:4000/objects/449c90fe-c457-4c64-baf2-fe6d0a59ca25',
'visibility': 'unlisted'
}]
}
return Promise.resolve({ data })
}
export async function fetchStatusesCount(instance, authHost, token) {
const data = instance === 'heaven.com'
? {
'status_visibility':
{ 'direct': 1, 'private': 2, 'public': 3, 'unlisted': 0 }
}
: {
'status_visibility':
{ 'direct': 4, 'private': 10, 'public': 4, 'unlisted': 10 }
}
return Promise.resolve({ data })
}

View file

@ -2,22 +2,77 @@ import request from '@/utils/request'
import { getToken } from '@/utils/auth'
import { baseName } from './utils'
import _ from 'lodash'
export async function addNewEmojiFile(packName, file, shortcode, filename, host, token) {
const data = new FormData()
if (filename.trim() !== '') {
data.set('filename', filename)
}
if (shortcode.trim() !== '') {
data.set('shortcode', shortcode)
}
data.set('file', file)
export async function deletePack(host, token, name) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/${name}`,
url: `/api/pleroma/emoji/packs/${packName}/files`,
method: 'post',
headers: authHeaders(token),
data
})
}
export function addressOfEmojiInPack(host, packName, name) {
return `${baseName(host)}/emoji/${packName}/${name}`
}
export async function createPack(host, token, packName) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/${packName}`,
method: 'post',
headers: authHeaders(token)
})
}
export async function deleteEmojiFile(packName, shortcode, host, token) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/${packName}/files`,
method: 'delete',
headers: authHeaders(token),
data: { shortcode }
})
}
export async function deletePack(host, token, packName) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/${packName}`,
method: 'delete',
headers: authHeaders(token)
})
}
export async function reloadEmoji(host, token) {
export async function downloadFrom(host, instance, packName, as, token) {
if (as.trim() === '') {
as = null
}
return await request({
baseURL: baseName(host),
url: '/api/pleroma/admin/reload_emoji',
url: '/api/pleroma/emoji/packs/download',
method: 'post',
headers: authHeaders(token),
data: { url: baseName(instance), name: packName, as },
timeout: 0
})
}
export async function fetchPack(packName, host, token) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/${packName}`,
method: 'get',
headers: authHeaders(token)
})
}
@ -25,17 +80,8 @@ export async function reloadEmoji(host, 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',
url: '/api/pleroma/emoji/packs/import',
method: 'get',
headers: authHeaders(token)
})
}
@ -51,106 +97,40 @@ export async function listPacks(host) {
export async function listRemotePacks(host, token, instance) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/list_from`,
method: 'post',
headers: authHeaders(token),
data: { instance_address: baseName(instance) }
url: `/api/pleroma/emoji/packs/remote?url=${baseName(instance)}`,
method: 'get',
headers: authHeaders(token)
})
}
export async function downloadFrom(host, instance_address, pack_name, as, token) {
if (as.trim() === '') {
as = null
}
export async function reloadEmoji(host, token) {
return await request({
baseURL: baseName(host),
url: '/api/pleroma/emoji/packs/download_from',
url: '/api/pleroma/admin/reload_emoji',
method: 'post',
headers: authHeaders(token),
data: { instance_address: baseName(instance_address), pack_name, as },
timeout: 0
headers: authHeaders(token)
})
}
export async function savePackMetadata(host, token, name, new_data) {
export async function savePackMetadata(host, token, packName, metadata) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/${name}/update_metadata`,
method: 'post',
url: `/api/pleroma/emoji/packs/${packName}`,
method: 'patch',
headers: authHeaders(token),
data: { name, new_data },
data: { metadata },
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
export async function updateEmojiFile(packName, shortcode, newShortcode, newFilename, force, host, token) {
return await request({
baseURL: baseName(host),
url: `/api/pleroma/emoji/packs/${packName}/update_file`,
method: 'post',
url: `/api/pleroma/emoji/packs/${packName}/files`,
method: 'patch',
headers: authHeaders(token),
data: data,
timeout: 0
data: { shortcode, new_shortcode: newShortcode, new_filename: newFilename, force }
})
}
export function addressOfEmojiInPack(host, packName, name) {
return `${baseName(host)}/emoji/${packName}/${name}`
}
const authHeaders = (token) => token ? { 'Authorization': `Bearer ${getToken()}` } : {}

View file

@ -39,10 +39,10 @@ export async function fetchStatuses({ godmode, localOnly, authHost, token, pageS
})
}
export async function fetchStatusesCount(authHost, token) {
export async function fetchStatusesCount(instance, authHost, token) {
return await request({
baseURL: baseName(authHost),
url: `/api/pleroma/admin/stats`,
url: instance ? `/api/pleroma/admin/stats?instance=${instance}` : `/api/pleroma/admin/stats`,
method: 'get',
headers: authHeaders(token)
})

View file

@ -387,7 +387,8 @@ export default {
corsPlug: 'CORS plug config',
instanceReboot: 'Reboot Instance',
restartApp: 'You must restart the instance to apply settings',
restartSuccess: 'Instance rebooted successfully!'
restartSuccess: 'Instance rebooted successfully!',
removeSettingConfirmation: 'Are you sure you want to remove this setting\'s value from the database?'
},
invites: {
inviteTokens: 'Invite tokens',

View file

@ -1,13 +1,16 @@
import {
addNewEmojiFile,
createPack,
deleteEmojiFile,
deletePack,
downloadFrom,
importFromFS,
listPacks,
listRemotePacks,
downloadFrom,
reloadEmoji,
createPack,
deletePack,
savePackMetadata,
importFromFS,
updatePackFile } from '@/api/emojiPacks'
updateEmojiFile
} from '@/api/emojiPacks'
import i18n from '@/lang'
import { Message } from 'element-ui'
@ -49,6 +52,36 @@ const packs = {
}
},
actions: {
async AddNewEmojiFile({ commit, getters }, { packName, file, shortcode, filename }) {
let result
try {
result = await addNewEmojiFile(packName, file, shortcode, filename, getters.authHost, getters.token)
} catch (_e) {
return
}
Message({
message: `${i18n.t('settings.successfullyUpdated')} ${packName} ${i18n.t('settings.metadatLowerCase')}`,
type: 'success',
duration: 5 * 1000
})
commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data })
},
async DeleteEmojiFile({ commit, getters }, { packName, shortcode }) {
let result
try {
result = await deleteEmojiFile(packName, shortcode, getters.authHost, getters.token)
} catch (_e) {
return
}
Message({
message: `${i18n.t('settings.successfullyUpdated')} ${packName} ${i18n.t('settings.metadatLowerCase')}`,
type: 'success',
duration: 5 * 1000
})
commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data })
},
async CreatePack({ getters }, { name }) {
await createPack(getters.authHost, getters.token, name)
},
@ -116,20 +149,23 @@ const packs = {
commit('SET_REMOTE_INSTANCE', remoteInstance)
commit('SET_REMOTE_PACKS', data)
},
async UpdateAndSavePackFile({ commit, getters }, args) {
const result = await updatePackFile(getters.authHost, getters.token, args)
if (result.status === 200) {
const { packName } = args
Message({
message: `${i18n.t('settings.successfullyUpdated')} ${packName} ${i18n.t('settings.metadatLowerCase')}`,
type: 'success',
duration: 5 * 1000
})
commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data })
SetRemoteInstance({ commit }, instance) {
commit('SET_REMOTE_INSTANCE', instance)
},
async UpdateEmojiFile({ commit, getters }, { packName, shortcode, newShortcode, newFilename, force }) {
let result
try {
result = await updateEmojiFile(packName, shortcode, newShortcode, newFilename, force, getters.authHost, getters.token)
} catch (_e) {
return
}
Message({
message: `${i18n.t('settings.successfullyUpdated')} ${packName} ${i18n.t('settings.metadatLowerCase')}`,
type: 'success',
duration: 5 * 1000
})
commit('UPDATE_LOCAL_PACK_FILES', { name: packName, files: result.data })
},
async UpdateLocalPackVal({ commit }, args) {
commit('UPDATE_LOCAL_PACK_VAL', args)

View file

@ -1,5 +1,14 @@
import _ from 'lodash'
export const getBooleanValue = value => {
if (value === 'true') {
return true
} else if (value === 'false') {
return false
}
return value
}
export const checkPartialUpdate = (settings, updatedSettings, description) => {
return Object.keys(updatedSettings).reduce((acc, group) => {
acc[group] = Object.keys(updatedSettings[group]).reduce((acc, key) => {

View file

@ -68,6 +68,14 @@ const status = {
dispatch('FetchStatus', statusId)
}
},
ClearState({ commit }) {
commit('CHANGE_SELECTED_INSTANCE', '')
commit('SET_STATUSES_BY_INSTANCE', [])
commit('CHANGE_LOCAL_CHECKBOX_VALUE', false)
commit('CHANGE_GODMODE_CHECKBOX_VALUE', false)
commit('SET_ALL_LOADED', false)
commit('CHANGE_PAGE', 1)
},
async DeleteStatus({ dispatch, getters }, { statusId, reportCurrentPage, userId, godmode, fetchStatusesByInstance }) {
await deleteStatus(statusId, getters.authHost, getters.token)
if (reportCurrentPage !== 0) { // called from Reports
@ -89,12 +97,13 @@ const status = {
},
async FetchStatusesCount({ commit, getters }) {
commit('SET_LOADING', true)
const { data } = await fetchStatusesCount(getters.authHost, getters.token)
const { data } = await fetchStatusesCount(instance, getters.authHost, getters.token)
commit('SET_STATUS_VISIBILITY', data.status_visibility)
commit('SET_LOADING', false)
},
async FetchStatusesByInstance({ commit, getters, state, rootState }) {
async FetchStatusesByInstance({ commit, dispatch, getters, state, rootState }) {
commit('SET_LOADING', true)
dispatch('FetchStatusesCount', state.statusesByInstance.selectedInstance)
if (state.statusesByInstance.selectedInstance === '') {
commit('SET_STATUSES_BY_INSTANCE', [])
} else {

View file

@ -44,11 +44,11 @@
</el-collapse-item>
<el-collapse-item v-if="Object.keys(pack.files).length > 0" :title=" $t('emoji.manageEmoji')" name="manageEmoji" class="no-background">
<single-emoji-editor
v-for="(file, ename) in pack.files"
:key="ename"
v-for="(file, shortcode) in pack.files"
:key="shortcode"
:host="host"
:pack-name="name"
:name="ename"
:shortcode="shortcode"
:file="file"
:is-local="isLocal" />
</el-collapse-item>
@ -96,9 +96,9 @@ export default {
if (this.isMobile) {
return '90px'
} else if (this.isTablet) {
return '120px'
return '155px'
} else {
return '120px'
return '155px'
}
},
share: {

View file

@ -1,7 +1,7 @@
<template>
<el-form :label-position="isMobile ? 'top' : 'left'" label-width="130px" size="small" class="new-emoji-uploader-form">
<el-form-item :label="$t('emoji.shortcode')">
<el-input v-model="shortcode" :placeholder="$t('emoji.required')"/>
<el-input v-model="shortcode" :placeholder="$t('emoji.optional')"/>
</el-form-item>
<el-form-item :label="$t('emoji.customFilename')">
<el-input v-model="customFileName" :placeholder="$t('emoji.optional')"/>
@ -9,7 +9,7 @@
<el-form-item :label="$t('emoji.uploadFile')">
<div class="upload-file-url">
<el-input v-model="imageUploadURL" :placeholder="$t('emoji.url')"/>
<el-button :disabled="shortcodePresent" type="primary" class="upload-button" @click="uploadEmoji">{{ $t('emoji.upload') }}</el-button>
<el-button type="primary" class="upload-button" @click="uploadEmoji">{{ $t('emoji.upload') }}</el-button>
</div>
<div class="upload-container">
<p class="text">or</p>
@ -18,7 +18,7 @@
:multiple="false"
:show-file-list="false"
action="add">
<el-button :disabled="shortcodePresent" type="primary">{{ $t('emoji.clickToUpload') }}</el-button>
<el-button type="primary">{{ $t('emoji.clickToUpload') }}</el-button>
</el-upload>
</div>
</el-form-item>
@ -46,26 +46,25 @@ export default {
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
shortcodePresent() {
return this.shortcode.trim() === ''
}
},
methods: {
uploadEmoji({ file }) {
this.$store.dispatch('UpdateAndSavePackFile', {
action: 'add',
packName: this.packName,
shortcode: this.shortcode,
file: file || this.imageUploadURL,
fileName: this.customFileName
}).then(() => {
this.shortcode = ''
this.imageUploadURL = ''
this.customFileName = ''
async uploadEmoji({ file }) {
try {
this.$store.dispatch('AddNewEmojiFile', {
packName: this.packName,
file: file || this.imageUploadURL,
shortcode: this.shortcode,
filename: this.customFileName
})
} catch (e) {
return
}
this.shortcode = ''
this.imageUploadURL = ''
this.customFileName = ''
this.$store.dispatch('ReloadEmoji')
})
this.$store.dispatch('ReloadEmoji')
}
}
}

View file

@ -36,11 +36,11 @@
<el-collapse v-model="showPackContent" class="contents-collapse">
<el-collapse-item v-if="Object.keys(pack.files).length > 0" :title=" $t('emoji.manageEmoji')" name="manageEmoji" class="no-background">
<single-emoji-editor
v-for="(file, ename) in pack.files"
:key="ename"
v-for="(file, shortcode) in pack.files"
:key="shortcode"
:host="host"
:pack-name="name"
:name="ename"
:shortcode="shortcode"
:file="file"
:is-local="isLocal" />
</el-collapse-item>
@ -52,7 +52,7 @@
</p>
<div class="download-shared-pack">
<el-input v-model="downloadSharedAs" :placeholder=" $t('emoji.downloadAsOptional')"/>
<el-button type="primary" class="download-shared-pack-button" @click="downloadFromInstance(pack.pack['homepage'])">
<el-button type="primary" class="download-shared-pack-button" @click="downloadFromInstance">
{{ isDesktop ? $t('emoji.downloadSharedPack') : $t('emoji.downloadSharedPackMobile') }}
</el-button>
</div>
@ -113,6 +113,9 @@ export default {
loadRemotePack() {
return this.$store.state.emojiPacks.activeCollapseItems.includes(this.name)
},
remoteInstanceAddress() {
return this.$store.state.emojiPacks.remoteInstance
},
share: {
get() { return this.pack.pack['share-files'] },
set(value) {
@ -171,11 +174,10 @@ export default {
}
},
methods: {
downloadFromInstance(url) {
const instanceAddress = `${new URL(url).protocol}//${new URL(url).hostname}`
downloadFromInstance() {
this.$store.dispatch(
'DownloadFrom',
{ instanceAddress, packName: this.name, as: this.downloadSharedAs }
{ instanceAddress: this.remoteInstanceAddress, packName: this.name, as: this.downloadSharedAs }
).then(() => this.$store.dispatch('ReloadEmoji'))
.then(() => this.$store.dispatch('SetLocalEmojiPacks'))
}

View file

@ -34,6 +34,7 @@
<el-button
:disabled="!copyToLocalPackName"
type="primary"
class="copy-to-local-pack-button"
@click="copyToLocal">{{ $t('emoji.copy') }}</el-button>
<el-button slot="reference" type="primary" class="emoji-button">{{ $t('emoji.copyToLocalPack') }}</el-button>
</el-popover>
@ -54,7 +55,7 @@ export default {
type: String,
required: true
},
name: {
shortcode: {
type: String,
required: true
},
@ -69,7 +70,7 @@ export default {
},
data() {
return {
newName: null,
newShortcode: null,
newFile: null,
copyToLocalPackName: null,
copyPopoverVisible: false,
@ -80,14 +81,14 @@ export default {
computed: {
emojiName: {
get() {
// Return a modified name if it was modified, otherwise return the old name
return this.newName !== null ? this.newName : this.name
// Return a modified shortcode if it was modified, otherwise return the old shortcode
return this.newShortcode !== null ? this.newShortcode : this.shortcode
},
set(val) { this.newName = val }
set(val) { this.newShortcode = val }
},
emojiFile: {
get() {
// Return a modified name if it was modified, otherwise return the old name
// Return a modified file name if it was modified, otherwise return the old file name
return this.newFile !== null ? this.newFile : this.file
},
set(val) { this.newFile = val }
@ -102,23 +103,26 @@ export default {
return this.$store.state.emojiPacks.localPacks
},
remoteInstance() {
return this.$store.state.emojiPacks.remoteInstance
return new URL(this.$store.state.emojiPacks.remoteInstance).host
}
},
methods: {
update() {
this.$store.dispatch('UpdateAndSavePackFile', {
action: 'update',
packName: this.packName,
oldName: this.name,
newName: this.emojiName,
newFilename: this.emojiFile
}).then(() => {
this.newName = null
this.newFile = null
async update() {
try {
this.$store.dispatch('UpdateEmojiFile', {
packName: this.packName,
shortcode: this.shortcode,
newShortcode: this.emojiName,
newFilename: this.emojiFile,
force: true
})
} catch (e) {
return
}
this.newShortcode = null
this.newFile = null
this.$store.dispatch('ReloadEmoji')
})
this.$store.dispatch('ReloadEmoji')
},
remove() {
this.$confirm('This will delete the emoji, are you sure?', 'Warning', {
@ -126,12 +130,11 @@ export default {
cancelButtonText: 'No, leave it be',
type: 'warning'
}).then(() => {
this.$store.dispatch('UpdateAndSavePackFile', {
action: 'remove',
this.$store.dispatch('DeleteEmojiFile', {
packName: this.packName,
name: this.name
shortcode: this.shortcode
}).then(() => {
this.newName = null
this.newShortcode = null
this.newFile = null
this.$store.dispatch('ReloadEmoji')
@ -139,20 +142,22 @@ export default {
})
},
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 = ''
try {
this.$store.dispatch('AddNewEmojiFile', {
packName: this.copyToLocalPackName,
file: this.addressOfEmojiInPack(this.remoteInstance, this.packName, this.file),
shortcode: this.copyToShortcode.trim() !== '' ? this.copyToShortcode.trim() : this.shortcode,
filename: this.copyToFilename.trim() !== '' ? this.copyToFilename.trim() : this.file
})
} catch (e) {
return
}
this.copyToLocalPackName = null
this.copyToLocalVisible = false
this.copyToShortcode = ''
this.copyToFilename = ''
this.$store.dispatch('ReloadEmoji')
})
this.$store.dispatch('ReloadEmoji')
},
addressOfEmojiInPack
}
@ -163,6 +168,10 @@ export default {
.copy-popover {
width: 330px
}
.copy-to-local-pack-button {
margin-top: 15px;
float: right;
}
.emoji-buttons {
place-self: center;
min-width: 200px

View file

@ -69,7 +69,6 @@ export default {
components: { LocalEmojiPack, RebootButton, RemoteEmojiPack },
data() {
return {
remoteInstanceAddress: '',
newPackName: '',
activeLocalPack: [],
activeRemotePack: [],
@ -95,6 +94,14 @@ export default {
localPacks() {
return this.$store.state.emojiPacks.localPacks
},
remoteInstanceAddress: {
get() {
return this.$store.state.emojiPacks.remoteInstance
},
set(instance) {
this.$store.dispatch('SetRemoteInstance', instance)
}
},
remotePacks() {
return this.$store.state.emojiPacks.remotePacks
}

View file

@ -3,7 +3,7 @@
<el-form ref="activitypubData" :model="activitypubData" :label-position="labelPosition" :label-width="labelWidth" data-search=":activitypub">
<setting :setting-group="activitypub" :data="activitypubData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="activitypub" class="divider thick-line"/>
<el-form ref="userData" :model="userData" :label-position="labelPosition" :label-width="labelWidth" data-search=":user">
<setting :setting-group="user" :data="userData"/>
</el-form>

View file

@ -3,15 +3,15 @@
<el-form ref="pleromaAuthenticatorData" :model="pleromaAuthenticatorData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="pleromaAuthenticator" :data="pleromaAuthenticatorData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="pleromaAuthenticator" class="divider thick-line"/>
<el-form ref="authData" :model="authData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="auth" :data="authData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="auth" class="divider thick-line"/>
<el-form ref="ldapData" :model="ldapData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="ldap" :data="ldapData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="oauth2" class="divider thick-line"/>
<el-form ref="oauth2" :model="oauth2Data" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="oauth2" :data="oauth2Data"/>
</el-form>

View file

@ -3,7 +3,7 @@
<el-form ref="captchaData" :model="captchaData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="captcha" :data="captchaData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="captcha" class="divider thick-line"/>
<el-form ref="kocaptchaData" :model="kocaptchaData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="kocaptcha" :data="kocaptchaData"/>
</el-form>

View file

@ -6,27 +6,27 @@
<el-form ref="staticFeData" :model="staticFeData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="staticFe" :data="staticFeData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="staticFe" class="divider thick-line"/>
<el-form ref="assetsData" :model="assetsData" :label-position="labelPosition" :label-width="labelWidth">
<el-form-item class="grouped-settings-header">
<el-form-item v-if="assets" class="grouped-settings-header">
<span class="label-font label-with-margin">{{ $t('settings.assets') }}</span>
</el-form-item>
<setting :setting-group="assets" :data="assetsData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="assets" class="divider thick-line"/>
<el-form ref="emojiData" :model="emojiData" :label-position="labelPosition" :label-width="labelWidth">
<el-form-item data-search=":emoji" class="grouped-settings-header">
<el-form-item v-if="emoji" data-search=":emoji" class="grouped-settings-header">
<span class="label-font label-with-margin">{{ $t('settings.emoji') }}</span>
</el-form-item>
<setting :setting-group="emoji" :data="emojiData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="emoji" class="divider thick-line"/>
<el-form ref="chatData" :model="chatData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="chat" :data="chatData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="chat" class="divider thick-line"/>
<el-form ref="markupData" :model="markupData" :label-position="labelPosition" :label-width="labelWidth">
<el-form-item data-search=":markup" class="grouped-settings-header">
<el-form-item v-if="markup" data-search=":markup" class="grouped-settings-header">
<span class="label-font label-with-margin">{{ $t('settings.markup') }}</span>
</el-form-item>
<setting :setting-group="markup" :data="markupData"/>

View file

@ -4,20 +4,20 @@
<setting :setting-group="http" :data="httpData"/>
</el-form>
<el-form ref="corsPlugData" :model="corsPlugData" :label-position="labelPosition" :label-width="labelWidth">
<el-form-item data-search=":cors_plug" class="grouped-settings-header">
<el-form-item v-if="corsPlug" data-search=":cors_plug" class="grouped-settings-header">
<span class="label-font label-with-margin">{{ $t('settings.corsPlug') }}</span>
</el-form-item>
<setting :setting-group="corsPlug" :data="corsPlugData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="corsPlug" class="divider thick-line"/>
<el-form ref="httpSignatures" :model="httpSignaturesData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="httpSignatures" :data="httpSignaturesData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="httpSignatures" class="divider thick-line"/>
<el-form ref="httpSecurityData" :model="httpSecurityData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="httpSecurity" :data="httpSecurityData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="httpSecurity" class="divider thick-line"/>
<el-form ref="webCacheTtl" :model="webCacheTtlData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="webCacheTtl" :data="webCacheTtlData"/>
</el-form>
@ -58,12 +58,6 @@ export default {
httpSecurityData() {
return _.get(this.settings.settings, [':pleroma', ':http_security']) || {}
},
httpSignatures() {
return this.settings.description.find(setting => setting.group === ':http_signatures')
},
httpSignaturesData() {
return _.get(this.settings.settings, [':http_signatures']) || {}
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},

View file

@ -55,7 +55,7 @@
:data-search="setting.key || setting.group"
@change="update($event, settingGroup.group, settingGroup.key, settingParent, setting.key, setting.type, nested)"/>
<el-select
v-if="!reducedSelects && (setting.type === 'module' || (setting.type.includes('atom') && setting.type.includes('dropdown')))"
v-if="renderSingleSelect(setting.type)"
:value="inputValue === false ? 'false' : inputValue"
:data-search="setting.key || setting.group"
clearable
@ -67,7 +67,7 @@
:key="index"/>
</el-select>
<el-select
v-if="!reducedSelects && renderMultipleSelect(setting.type)"
v-if="renderMultipleSelect(setting.type)"
:value="inputValue"
:data-search="setting.key || setting.group"
multiple
@ -99,12 +99,12 @@
<editable-keyword-input v-if="editableKeyword(setting.key, setting.type)" :data="keywordData" :setting-group="settingGroup" :setting="setting"/>
<icons-input v-if="setting.key === ':icons'" :data="iconsData" :setting-group="settingGroup" :setting="setting"/>
<mascots-input v-if="setting.key === ':mascots'" :data="keywordData" :setting-group="settingGroup" :setting="setting"/>
<multiple-select v-if="setting.key === ':backends' || setting.key === ':args'" :data="data" :setting-group="settingGroup" :setting="setting"/>
<proxy-url-input v-if="setting.key === ':proxy_url'" :data="data[setting.key]" :setting-group="settingGroup" :setting="setting" :parents="settingParent"/>
<prune-input v-if="setting.key === ':prune'" :data="data[setting.key]" :setting-group="settingGroup" :setting="setting"/>
<rate-limit-input v-if="settingGroup.key === ':rate_limit'" :data="data" :setting-group="settingGroup" :setting="setting"/>
<reg-invites-input v-if="[':registrations_open', ':invites_enabled'].includes(setting.key)" :data="data" :setting-group="settingGroup" :setting="setting"/>
<select-input-with-reduced-labels v-if="reducedSelects" :data="data" :setting-group="settingGroup" :setting="setting"/>
<specific-multiple-select v-if="setting.key === ':backends' || setting.key === ':args'" :data="data" :setting-group="settingGroup" :setting="setting"/>
<!-------------------->
<el-tooltip v-if="canBeDeleted && isTablet" :content="$t('settings.removeFromDB')" placement="bottom-end" class="delete-setting-button-container">
<el-button icon="el-icon-delete" circle size="mini" class="delete-setting-button" @click="removeSetting"/>
@ -126,13 +126,13 @@ import {
EditableKeywordInput,
IconsInput,
MascotsInput,
MultipleSelect,
ProxyUrlInput,
PruneInput,
RateLimitInput,
RegInvitesInput,
SelectInputWithReducedLabels } from './inputComponents'
import { processNested } from '@/store/modules/normalizers'
SelectInputWithReducedLabels,
SpecificMultipleSelect } from './inputComponents'
import { getBooleanValue, processNested } from '@/store/modules/normalizers'
import _ from 'lodash'
import marked from 'marked'
@ -144,12 +144,12 @@ export default {
EditableKeywordInput,
IconsInput,
MascotsInput,
MultipleSelect,
ProxyUrlInput,
PruneInput,
RateLimitInput,
RegInvitesInput,
SelectInputWithReducedLabels
SelectInputWithReducedLabels,
SpecificMultipleSelect
},
props: {
customLabelWidth: {
@ -215,7 +215,7 @@ export default {
return Array.isArray(this.data[':icons']) ? this.data[':icons'] : []
},
inputValue() {
if ([':esshd', ':cors_plug', ':quack', ':http_signatures', ':tesla', ':swoosh'].includes(this.settingGroup.group) &&
if ([':esshd', ':cors_plug', ':quack', ':tesla', ':swoosh'].includes(this.settingGroup.group) &&
this.data[this.setting.key]) {
return this.setting.type === 'atom' && this.data[this.setting.key].value[0] === ':'
? this.data[this.setting.key].value.substr(1)
@ -296,21 +296,34 @@ export default {
{ group, key: parentKey, input: setting.key, value: valueForState })
},
async removeSetting() {
const config = this.settingGroup.key
? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [this.setting.key] }]
: [{ group: this.settingGroup.group, key: this.setting.key, delete: true }]
try {
await this.$store.dispatch('RemoveSetting', config)
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.successfullyRemoved')
this.$confirm(
this.$t('settings.removeSettingConfirmation'),
{
confirmButtonText: this.$t('users.ok'),
cancelButtonText: this.$t('users.cancel'),
type: 'warning'
}).then(async() => {
const config = this.settingGroup.key
? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [this.setting.key] }]
: [{ group: this.settingGroup.group, key: this.setting.key, delete: true }]
try {
await this.$store.dispatch('RemoveSetting', config)
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.successfullyRemoved')
})
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('users.canceled')
})
})
},
renderMultipleSelect(type) {
return Array.isArray(type) && this.setting.key !== ':backends' && this.setting.key !== ':args' && (
return !this.reducedSelects && Array.isArray(type) && this.setting.key !== ':backends' && this.setting.key !== ':args' && (
type.includes('module') ||
(type.includes('list') && type.includes('string')) ||
(type.includes('list') && type.includes('atom')) ||
@ -318,10 +331,14 @@ export default {
this.setting.key === ':args'
)
},
renderSingleSelect(type) {
return !this.reducedSelects && (type === 'module' || (type.includes('atom') && type.includes('dropdown')))
},
update(value, group, key, parents, input, type, nested) {
const updatedValue = this.renderSingleSelect(type) ? getBooleanValue(value) : value
nested
? this.processNestedData(value, group, key, parents)
: this.updateSetting(value, group, key, input, type)
? this.processNestedData(updatedValue, group, key, parents)
: this.updateSetting(updatedValue, group, key, input, type)
},
updateSetting(value, group, key, input, type) {
this.$store.dispatch('UpdateSettings', { group, key, input, value, type })

View file

@ -3,27 +3,27 @@
<el-form ref="instanceData" :model="instanceData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="instance" :data="instanceData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="instance" class="divider thick-line"/>
<el-form ref="adminToken" :model="adminTokenData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="adminToken" :data="adminTokenData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="adminToken" class="divider thick-line"/>
<el-form ref="scheduledActivity" :model="scheduledActivityData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="scheduledActivity" :data="scheduledActivityData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="scheduledActivity" class="divider thick-line"/>
<el-form ref="manifest" :model="manifestData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="manifest" :data="manifestData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="manifest" class="divider thick-line"/>
<el-form ref="pleromaUser" :model="pleromaUserData" :label-position="labelPosition" :label-width="labelWidth" data-search="Pleroma.User">
<setting :setting-group="pleromaUser" :data="pleromaUserData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="pleromaUser" class="divider thick-line"/>
<el-form ref="uriSchemes" :model="uriSchemesData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="uriSchemes" :data="uriSchemesData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="uriSchemes" class="divider thick-line"/>
<el-form ref="feed" :model="feedData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="feed" :data="feedData"/>
</el-form>

View file

@ -3,15 +3,15 @@
<el-form ref="loggerData" :model="loggerData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="logger" :data="loggerData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="logger" class="divider thick-line"/>
<el-form ref="consoleData" :model="consoleData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="console" :data="consoleData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="console" class="divider thick-line"/>
<el-form ref="exsysloggerData" :model="exsysloggerData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="exsyslogger" :data="exsysloggerData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="exsyslogger" class="divider thick-line"/>
<el-form ref="quackData" :model="quackData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="quack" :data="quackData"/>
</el-form>

View file

@ -3,15 +3,15 @@
<el-form ref="mrfSimple" :model="mrfSimpleData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfSimple" :data="mrfSimpleData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mrfSimple" class="divider thick-line"/>
<el-form ref="mrfRejectnonpublic" :model="mrfRejectnonpublicData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfRejectnonpublic" :data="mrfRejectnonpublicData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mrfRejectnonpublic" class="divider thick-line"/>
<el-form ref="mrfHellthread" :model="mrfHellthreadData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfHellthread" :data="mrfHellthreadData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mrfHellthread" class="divider thick-line"/>
<el-form ref="mrfKeyword" :model="mrfKeywordData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfKeyword" :data="mrfKeywordData"/>
</el-form>
@ -21,19 +21,19 @@
<el-form ref="mrfMention" :model="mrfMentionData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfMention" :data="mrfMentionData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mrfMention" class="divider thick-line"/>
<el-form ref="mrfNormalizeMarkup" :model="mrfNormalizeMarkupData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfNormalizeMarkup" :data="mrfNormalizeMarkupData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mrfNormalizeMarkup" class="divider thick-line"/>
<el-form ref="mrfVocabulary" :model="mrfVocabularyData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfVocabulary" :data="mrfVocabularyData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mrfVocabulary" class="divider thick-line"/>
<el-form ref="mrfObjectAge" :model="mrfObjectAgeData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mrfObjectAge" :data="mrfObjectAgeData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mrfObjectAge" class="divider thick-line"/>
<el-form ref="modules" :model="modulesData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="modules" :data="modulesData"/>
</el-form>

View file

@ -3,11 +3,11 @@
<el-form ref="mailer" :model="mailerData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="mailer" :data="mailerData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="mailer" class="divider thick-line"/>
<el-form ref="swoosh" :model="swooshData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="swoosh" :data="swooshData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="swoosh" class="divider thick-line"/>
<el-form ref="emailNotifications" :model="emailNotificationsData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="emailNotifications" :data="emailNotificationsData"/>
</el-form>

View file

@ -3,7 +3,7 @@
<el-form ref="metadata" :model="metadataData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="metadata" :data="metadataData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="metadata" class="divider thick-line"/>
<el-form ref="richMedia" :model="richMediaData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="richMedia" :data="richMediaData"/>
</el-form>

View file

@ -130,17 +130,30 @@ export default {
return marked(desc)
},
async removeSetting(key) {
const config = this.settingGroup.key
? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [key] }]
: [{ group: this.settingGroup.group, key, delete: true }]
try {
await this.$store.dispatch('RemoveSetting', config)
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.successfullyRemoved')
this.$confirm(
this.$t('settings.removeSettingConfirmation'),
{
confirmButtonText: this.$t('users.ok'),
cancelButtonText: this.$t('users.cancel'),
type: 'warning'
}).then(async() => {
const config = this.settingGroup.key
? [{ group: this.settingGroup.group, key: this.settingGroup.key, delete: true, subkeys: [key] }]
: [{ group: this.settingGroup.group, key, delete: true }]
try {
await this.$store.dispatch('RemoveSetting', config)
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.successfullyRemoved')
})
}).catch(() => {
this.$message({
type: 'info',
message: this.$t('users.canceled')
})
})
},
updateSetting(value, tab, input) {

View file

@ -4,20 +4,20 @@
<setting :setting-group="upload" :data="uploadData"/>
</el-form>
<el-form v-if="showUploadersLocal" ref="uploadersLocal" :model="uploadersLocalData" :label-position="labelPosition" :label-width="labelWidth">
<el-form-item class="grouped-settings-header">
<el-form-item v-if="uploadersLocal" class="grouped-settings-header">
<span class="label-font label-with-margin">Pleroma.Uploaders.Local</span>
</el-form-item>
<setting :setting-group="uploadersLocal" :data="uploadersLocalData"/>
<el-divider class="divider thick-line"/>
<el-divider v-if="uploadersLocal" class="divider thick-line"/>
</el-form>
<el-form v-if="showUploadersS3" ref="uploadersS3" :model="uploadersS3Data" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="uploadersS3" :data="uploadersS3Data"/>
<el-divider class="divider thick-line"/>
<el-divider v-if="uploadersS3" class="divider thick-line"/>
</el-form>
<el-form ref="uploadFilterMogrify" :model="uploadFilterMogrifyData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="uploadFilterMogrify" :data="uploadFilterMogrifyData"/>
</el-form>
<el-divider class="divider thick-line"/>
<el-divider v-if="uploadFilterMogrify" class="divider thick-line"/>
<el-form ref="uploadAnonymizeFilename" :model="uploadAnonymizeFilenameData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="uploadAnonymizeFilename" :data="uploadAnonymizeFilenameData"/>
</el-form>

View file

@ -13,7 +13,7 @@
</el-select>
<el-select
v-if="setting.type === 'module' || (setting.type.includes('atom') && setting.type.includes('dropdown'))"
:value="inputValue"
:value="inputValue === false ? 'false' : inputValue"
:data-search="setting.key || setting.group"
clearable
class="input"
@ -28,6 +28,8 @@
</template>
<script>
import { getBooleanValue } from '@/store/modules/normalizers'
export default {
name: 'SelectInputWithReducedLabels',
props: {
@ -107,8 +109,9 @@ export default {
})
},
updateSetting(value, group, key, input, type) {
this.$store.dispatch('UpdateSettings', { group, key, input, value, type })
this.$store.dispatch('UpdateState', { group, key, input, value })
const updatedValue = getBooleanValue(value)
this.$store.dispatch('UpdateSettings', { group, key, input, value: updatedValue, type })
this.$store.dispatch('UpdateState', { group, key, input, value: updatedValue })
}
}
}

View file

@ -5,8 +5,6 @@
:value="data.value"
:data-search="setting.key || setting.group"
multiple
filterable
allow-create
class="input"
@change="updateSetting($event, settingGroup.group, settingGroup.key, setting.key, setting.type)">
<el-option value=":console" label="console"/>
@ -18,8 +16,6 @@
:value="data[setting.key]"
:data-search="setting.key || setting.group"
multiple
filterable
allow-create
class="input"
@change="updateSetting($event, settingGroup.group, settingGroup.key, setting.key, setting.type)">
<el-option value="strip" label="strip"/>
@ -31,7 +27,7 @@
<script>
export default {
name: 'MultipleSelect',
name: 'SpecificMultipleSelect',
props: {
data: {
type: [Object, Array],

View file

@ -3,9 +3,9 @@ export { default as EditableKeywordInput } from './EditableKeywordInput'
export { default as CrontabInput } from './CrontabInput'
export { default as IconsInput } from './IconsInput'
export { default as MascotsInput } from './MascotsInput'
export { default as MultipleSelect } from './MultipleSelect'
export { default as ProxyUrlInput } from './ProxyUrlInput'
export { default as PruneInput } from './PruneInput'
export { default as RateLimitInput } from './RateLimitInput'
export { default as RegInvitesInput } from './RegInvitesInput'
export { default as SelectInputWithReducedLabels } from './SelectInputWithReducedLabels'
export { default as SpecificMultipleSelect } from './SpecificMultipleSelect'

View file

@ -29,7 +29,7 @@ export const tabs = {
},
'http': {
label: 'settings.http',
settings: [':cors_plug', ':http', ':http_security', ':http_signatures', ':web_cache_ttl']
settings: [':cors_plug', ':http', ':http_security', ':web_cache_ttl']
},
'instance': {
label: 'settings.instance',
@ -65,7 +65,7 @@ export const tabs = {
},
'relays': {
label: 'settings.relays',
settings: []
settings: ['relays']
},
'web-push': {
label: 'settings.webPush',

View file

@ -33,7 +33,7 @@
<el-tab-pane
v-for="(value, componentName) in tabs"
:label="$t(value.label)"
:disabled="configDisabled"
:disabled="configDisabled || settingsCantBeChanged(value.settings)"
:key="componentName"
:name="componentName"
lazy>
@ -227,6 +227,20 @@ export default {
: { value: `${searchObj.label} in ${searchObj.groupLabel}`, group: searchObj.groupKey, key: searchObj.key }
})
cb(results)
},
settingsCantBeChanged(settings) {
const existingSettings = settings.filter(setting => {
if ([':esshd', ':cors_plug', ':quack', ':logger', ':swoosh', ':mime'].includes(setting)) {
return this.$store.state.settings.description.findIndex(el => el.group === setting) !== -1
} else if (setting === 'Pleroma.Web.Auth.Authenticator' || setting === ':admin_token') {
return this.$store.state.settings.description.findIndex(el => el.children[0].key === setting) !== -1
} else if (setting === 'relays') {
return [setting]
} else {
return this.$store.state.settings.description.findIndex(el => el.key === setting) !== -1
}
})
return existingSettings.length === 0
}
}
}

View file

@ -55,7 +55,7 @@
padding-right: 10px;
}
.el-tabs__header {
z-index: 3000;
z-index: 2002;
}
.esshd-list {
margin: 0;

View file

@ -8,10 +8,18 @@
</div>
<div class="statuses-header-container">
<el-button-group>
<el-button plain>{{ $t('statuses.direct') }}: {{ statusVisibility.direct }}</el-button>
<el-button plain>{{ $t('statuses.private') }}: {{ statusVisibility.private }}</el-button>
<el-button plain>{{ $t('statuses.public') }}: {{ statusVisibility.public }}</el-button>
<el-button plain>{{ $t('statuses.unlisted') }}: {{ statusVisibility.unlisted }}</el-button>
<el-button plain class="direct-button">
{{ $t('statuses.direct') }}: {{ normalizedCount(statusVisibility.direct) }}
</el-button>
<el-button plain class="private-button">
{{ $t('statuses.private') }}: {{ normalizedCount(statusVisibility.private) }}
</el-button>
<el-button plain class="public-button">
{{ $t('statuses.public') }}: {{ normalizedCount(statusVisibility.public) }}
</el-button>
<el-button plain class="unlisted-button">
{{ $t('statuses.unlisted') }}: {{ normalizedCount(statusVisibility.unlisted) }}
</el-button>
</el-button-group>
</div>
<div class="filter-container">
@ -61,6 +69,7 @@
import MultipleUsersMenu from '@/views/users/components/MultipleUsersMenu'
import Status from '@/components/Status'
import RebootButton from '@/components/RebootButton'
import numeral from 'numeral'
export default {
name: 'Statuses',
@ -142,7 +151,14 @@ export default {
this.$store.dispatch('FetchPeers')
this.$store.dispatch('FetchStatusesCount')
},
destroyed() {
this.clearSelection()
this.$store.dispatch('ClearState')
},
methods: {
clearSelection() {
this.selectedUsers = []
},
handleFilterChange() {
this.$store.dispatch('HandlePageChange', 1)
this.$store.dispatch('FetchStatusesByInstance')
@ -152,14 +168,14 @@ export default {
this.$store.dispatch('FetchStatusesPageByInstance')
},
clearSelection() {
this.selectedUsers = []
},
handleStatusSelection(user) {
if (this.selectedUsers.find(selectedUser => user.id === selectedUser.id) !== undefined) {
return
}
this.selectedUsers = [...this.selectedUsers, user]
},
normalizedCount(count) {
return numeral(count).format('0a')
}
}
}
@ -175,6 +191,13 @@ export default {
margin: 0 0 10px;
}
}
.statuses-header-container {
.el-button.is-plain:focus, .el-button.is-plain:hover {
border-color: #DCDFE6;
color: #606266;
cursor: default
}
}
.checkbox-container {
margin-bottom: 15px;
}
@ -228,8 +251,26 @@ export default {
.statuses-header-container {
flex-direction: column;
align-items: flex-start;
.el-button-group {
width: 100%;
}
.el-button {
padding: 10px 6.5px;
width: 50%;
}
.el-button-group>.el-button:first-child {
border-bottom-left-radius: 0;
}
.el-button-group>.el-button:not(:first-child):not(:last-child).private-button {
border-top-right-radius: 4px;
}
.el-button-group>.el-button:not(:first-child):not(:last-child).public-button {
border-bottom-left-radius: 4px;
border-top: white;
}
.el-button-group>.el-button:last-child {
border-top-right-radius: 0;
border-top: white;
}
.reboot-button {
margin: 10px 0 0 0;

View file

@ -0,0 +1,103 @@
import Vuex from 'vuex'
import { mount, createLocalVue, config } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import Element from 'element-ui'
import Statuses from '@/views/statuses/index'
import storeConfig from './store.conf'
import { cloneDeep } from 'lodash'
config.mocks["$t"] = () => {}
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Element)
jest.mock('@/api/app')
jest.mock('@/api/status')
jest.mock('@/api/peers')
jest.mock('@/api/nodeInfo')
describe('Statuses', () => {
let store
beforeEach(() => {
store = new Vuex.Store(cloneDeep(storeConfig))
})
it('fetches peers and statuses count', async (done) => {
mount(Statuses, {
store,
localVue
})
await flushPromises()
const statusVisibilityCount = store.state.status.statusVisibility
expect(statusVisibilityCount.direct).toEqual(4)
expect(statusVisibilityCount.private).toEqual(10)
expect(statusVisibilityCount.public).toEqual(4)
expect(statusVisibilityCount.unlisted).toEqual(10)
done()
})
it('fetches statuses from selected instance and updates the count', async (done) => {
const wrapper = mount(Statuses, {
store,
localVue
})
await flushPromises()
store.dispatch('HandleFilterChange', 'heaven.com')
wrapper.vm.handleFilterChange()
await flushPromises()
const statusVisibilityCount = store.state.status.statusVisibility
expect(statusVisibilityCount.direct).toEqual(1)
expect(statusVisibilityCount.private).toEqual(2)
expect(statusVisibilityCount.public).toEqual(3)
expect(statusVisibilityCount.unlisted).toEqual(0)
expect(store.state.status.fetchedStatuses.length).toEqual(2)
done()
})
it('handles status select', async (done) => {
const wrapper = mount(Statuses, {
store: store,
localVue
})
await flushPromises()
store.dispatch('HandleFilterChange', 'heaven.com')
wrapper.vm.handleFilterChange()
await flushPromises()
wrapper.find('.status-checkbox input').setChecked()
await flushPromises()
expect(wrapper.vm.selectedUsers.length).toEqual(1)
expect(wrapper.vm.selectedUsers[0].display_name).toBe('sky')
done()
})
it('clear state after component was destroyed', async (done) => {
const wrapper = mount(Statuses, {
store: store,
localVue
})
await flushPromises()
store.dispatch('HandleFilterChange', 'heaven.com')
wrapper.vm.handleFilterChange()
await flushPromises()
wrapper.find('.status-checkbox input').setChecked()
await flushPromises()
expect(wrapper.vm.selectedUsers.length).toEqual(1)
expect(store.state.status.statusesByInstance.selectedInstance).toBe('heaven.com')
expect(store.state.status.fetchedStatuses.length).toEqual(2)
wrapper.destroy()
expect(wrapper.vm.selectedUsers.length).toEqual(0)
expect(store.state.status.statusesByInstance.selectedInstance).toBe('')
expect(store.state.status.fetchedStatuses.length).toEqual(0)
done()
})
})

View file

@ -0,0 +1,53 @@
import Vuex from 'vuex'
import { mount, createLocalVue, config } from '@vue/test-utils'
import flushPromises from 'flush-promises'
import Element from 'element-ui'
import Statuses from '@/views/statuses/index'
import storeConfig from './storeForPagination.conf'
import { cloneDeep } from 'lodash'
config.mocks["$t"] = () => {}
const localVue = createLocalVue()
localVue.use(Vuex)
localVue.use(Element)
jest.mock('@/api/app')
jest.mock('@/api/status')
jest.mock('@/api/peers')
jest.mock('@/api/nodeInfo')
describe('Statuses', () => {
let store
beforeEach(() => {
store = new Vuex.Store(cloneDeep(storeConfig))
})
it('pagination', async (done) => {
const wrapper = mount(Statuses, {
store,
localVue
})
await flushPromises()
store.dispatch('HandleFilterChange', 'heaven.com')
wrapper.vm.handleFilterChange()
await flushPromises()
expect(store.state.status.statusesByInstance.allLoaded).toBe(false)
expect(store.state.status.statusesByInstance.page).toBe(1)
wrapper.find('.statuses-pagination button').trigger('click')
await flushPromises()
expect(store.state.status.statusesByInstance.allLoaded).toBe(false)
expect(store.state.status.statusesByInstance.page).toBe(2)
wrapper.find('.statuses-pagination button').trigger('click')
await flushPromises()
expect(store.state.status.statusesByInstance.allLoaded).toBe(true)
done()
})
})

View file

@ -0,0 +1,17 @@
import app from '@/store/modules/app'
import peers from '@/store/modules/peers'
import user from '@/store/modules/user'
import settings from '@/store/modules/settings'
import status from '@/store/modules/status'
import getters from '@/store/getters'
export default {
modules: {
app,
peers,
settings,
status,
user: { ...user, state: { ...user.state, authHost: 'localhost:4000' }}
},
getters
}

View file

@ -0,0 +1,17 @@
import app from '@/store/modules/app'
import peers from '@/store/modules/peers'
import user from '@/store/modules/user'
import settings from '@/store/modules/settings'
import status from '@/store/modules/status'
import getters from '@/store/getters'
export default {
modules: {
app,
peers,
settings,
status: { ...status, state: { ...status.state, statusesByInstance: { ...status.state.statusesByInstance, pageSize: 1 }}},
user: { ...user, state: { ...user.state, authHost: 'localhost:4000' }}
},
getters
}