Merge branch 'feature/reduce-nav-sidebars' into develop

This commit is contained in:
Angelina Filippova 2021-03-19 20:50:14 +03:00
commit b1ce85fd33
64 changed files with 782 additions and 419 deletions

View file

@ -28,6 +28,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Replace regular inputs with textareas for setting welcome messages in the Settings section
- Update rendering Moderation Log Messages so that all usernames are links to the pages of the corresponding users in Admin-FE
- Remove Websocket based federation settings
- Move Settings tab navigation from the tabbed menu to the main sidebar menu. A separate route is created for each tab.
- Move Emoji packs configuration to the Emoji tab in the Settings section
- 401 and 404 error pages updated
- Remove unused components

View file

@ -69,7 +69,7 @@
"vue": "^2.6.8",
"vue-count-to": "1.0.13",
"vue-i18n": "^8.9.0",
"vue-router": "3.0.2",
"vue-router": "^3.5.1",
"vue-splitpane": "1.0.2",
"vuedraggable": "^2.16.0",
"vuex": "3.0.1",

View file

@ -15,7 +15,16 @@ export async function deleteInstanceDocument(name, authHost, token) {
export async function fetchDescription(authHost, token) {
return await request({
baseURL: baseName(authHost),
url: `/api/pleroma/admin/config/descriptions`,
url: `/api/v1/pleroma/admin/config/descriptions`,
method: 'get',
headers: authHeaders(token)
})
}
export async function fetchDescription2(authHost, token) {
return await request({
baseURL: baseName(authHost),
url: `/api/v2/pleroma/admin/config/descriptions`,
method: 'get',
headers: authHeaders(token)
})

View file

@ -70,6 +70,7 @@ export default {
chats: 'Chats',
settings: 'Settings',
moderationLog: 'Moderation Log',
relays: 'Relays',
mediaProxyCache: 'MediaProxy Cache',
'emoji-packs': 'Emoji packs'
},
@ -426,6 +427,7 @@ export default {
activityPub: 'ActivityPub',
auth: 'Authentication',
captcha: 'Captcha',
emoji: 'Emoji',
frontend: 'Frontend',
http: 'HTTP',
mrf: 'MRF',
@ -437,11 +439,6 @@ export default {
esshd: 'BBS / SSH access',
rateLimiters: 'Rate limiters',
other: 'Other',
relays: 'Relays',
follow: 'Follow',
followRelay: 'Follow new relay',
followedBack: 'Followed Back',
instanceUrl: 'Instance URL',
success: 'Settings changed successfully!',
description: 'Description',
removeFromDB: 'Remove setting from the DB',
@ -483,6 +480,13 @@ export default {
frontendStartedInstallation: 'Installation started',
inProcess: 'In process'
},
relays: {
relays: 'Relays',
follow: 'Follow',
followRelay: 'Follow new relay',
followedBack: 'Followed Back',
instanceUrl: 'Instance URL'
},
invites: {
inviteTokens: 'Invite tokens',
createInviteToken: 'Generate invite token',

View file

@ -28,7 +28,7 @@ export const beforeEachRoute = (to, from, next) => {
store.dispatch('GetUserInfo').then(res => {
const roles = res.data.pleroma.is_admin ? ['admin'] : []
store.dispatch('GenerateRoutes', { roles }).then(() => {
router.addRoutes(store.getters.addRouters)
store.getters.addRouters.forEach(route => router.addRoute(route))
next({ ...to, replace: true })
})
}).catch((err) => {

View file

@ -21,19 +21,26 @@ import Layout from '@/views/layout/Layout'
const disabledFeatures = process.env.DISABLED_FEATURES || []
const settingsDisabled = disabledFeatures.includes('settings')
const settingsChildren = () => {
return localStorage.getItem('settingsTabs')
? JSON.parse(localStorage.getItem('settingsTabs')).map(({ label, path }) => {
return {
path,
component: () => import(`@/views/settings`),
name: label,
meta: { title: label }
}
})
: []
}
const settings = {
path: '/settings',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/settings/index'),
name: 'Settings',
meta: { title: 'settings', icon: 'settings', noCache: true }
}
]
hasSubmenu: true,
meta: { title: 'settings', icon: 'el-icon-setting', noCache: true },
children: settingsChildren()
}
const statusesDisabled = disabledFeatures.includes('statuses')
const statuses = {
path: '/statuses',
@ -43,7 +50,7 @@ const statuses = {
path: 'index',
component: () => import('@/views/statuses/index'),
name: 'Statuses',
meta: { title: 'statuses', icon: 'form', noCache: true }
meta: { title: 'statuses', icon: 'el-icon-chat-line-square', noCache: true }
}
]
}
@ -57,7 +64,7 @@ const reports = {
path: 'index',
component: () => import('@/views/reports/index'),
name: 'Reports',
meta: { title: 'reports', icon: 'documentation', noCache: true }
meta: { title: 'reports', icon: 'el-icon-receiving', noCache: true }
}
]
}
@ -71,21 +78,21 @@ const invites = {
path: 'index',
component: () => import('@/views/invites/index'),
name: 'Invites',
meta: { title: 'invites', icon: 'guide', noCache: true }
meta: { title: 'invites', icon: 'el-icon-postcard', noCache: true }
}
]
}
const emojiPacksDisabled = disabledFeatures.includes('emoji-packs')
const emojiPacks = {
path: '/emoji_packs',
const relaysDisabled = disabledFeatures.includes('relays')
const relays = {
path: '/relays',
component: Layout,
children: [
{
path: 'index',
component: () => import('@/views/emojiPacks/index'),
name: 'Emoji Packs',
meta: { title: 'emoji-packs', icon: 'eye-open', noCache: true }
component: () => import('@/views/relays/index'),
name: 'Relays',
meta: { title: 'relays', icon: 'el-icon-connection', noCache: true }
}
]
}
@ -99,7 +106,7 @@ const moderationLog = {
path: 'index',
component: () => import('@/views/moderationLog/index'),
name: 'Moderation Log',
meta: { title: 'moderationLog', icon: 'list', noCache: true }
meta: { title: 'moderationLog', icon: 'el-icon-notebook-2', noCache: true }
}
]
}
@ -113,7 +120,7 @@ const mediaProxyCache = {
path: 'index',
component: () => import('@/views/mediaProxyCache/index'),
name: 'MediaProxy Cache',
meta: { title: 'mediaProxyCache', icon: 'example', noCache: true }
meta: { title: 'mediaProxyCache', icon: 'el-icon-coin', noCache: true }
}
]
}
@ -158,7 +165,8 @@ export const constantRouterMap = [
{
path: '',
component: Layout,
redirect: '/users/index'
redirect: '/users/index',
hidden: true
}
]
@ -177,15 +185,15 @@ export const asyncRouterMap = [
path: 'index',
component: () => import('@/views/users/index'),
name: 'Users',
meta: { title: 'users', icon: 'peoples', noCache: true }
meta: { title: 'users', icon: 'el-icon-user', noCache: true }
}
]
},
...(statusesDisabled ? [] : [statuses]),
...(reportsDisabled ? [] : [reports]),
...(invitesDisabled ? [] : [invites]),
...(emojiPacksDisabled ? [] : [emojiPacks]),
...(moderationLogDisabled ? [] : [moderationLog]),
...(relaysDisabled ? [] : [relays]),
...(mediaProxyCacheDisabled ? [] : [mediaProxyCache]),
...(settingsDisabled ? [] : [settings]),
{

View file

@ -17,6 +17,7 @@ const getters = {
errorLogs: state => state.errorLog.logs,
users: state => state.users.fetchedUsers,
authHost: state => state.user.authHost,
settings: state => state.settings
settings: state => state.settings,
tabs: state => state.settings.tabs
}
export default getters

View file

@ -46,15 +46,10 @@ const permission = {
}
},
actions: {
GenerateRoutes({ commit }, data) {
GenerateRoutes({ commit }, { roles, _routesWithSettings }) {
return new Promise(resolve => {
const { roles } = data
let accessedRouters
if (roles.includes('admin')) {
accessedRouters = asyncRouterMap
} else {
accessedRouters = filterAsyncRouter(asyncRouterMap, roles)
}
const routes = _routesWithSettings || asyncRouterMap
const accessedRouters = roles.includes('admin') ? routes : filterAsyncRouter(asyncRouterMap, roles)
commit('SET_ROUTERS', accessedRouters)
resolve()
})

View file

@ -9,11 +9,11 @@ import {
updateInstanceDocument,
updateSettings } from '@/api/settings'
import { formSearchObject, parseNonTuples, parseTuples, valueHasTuples, wrapUpdatedSettings } from './normalizers'
import { tabs } from '../../utils/tabs'
import _ from 'lodash'
const settings = {
state: {
activeTab: 'instance',
configDisabled: true,
frontends: [],
db: {},
@ -21,7 +21,9 @@ const settings = {
instancePanel: '',
loading: true,
searchData: {},
searchQuery: '',
settings: {},
tabs: [],
termsOfServices: '',
updatedSettings: {}
},
@ -38,9 +40,6 @@ const settings = {
state.updatedSettings = updatedSettings
}
},
SET_ACTIVE_TAB: (state, tab) => {
state.activeTab = tab
},
SET_DESCRIPTION: (state, data) => {
state.description = data
},
@ -53,6 +52,9 @@ const settings = {
SET_SEARCH: (state, searchObject) => {
state.searchData = searchObject
},
SET_SEARCH_QUERY: (state, query) => {
state.searchQuery = query
},
SET_SETTINGS: (state, data) => {
const newSettings = data.reduce((acc, { group, key, value }) => {
const parsedValue = valueHasTuples(key, value)
@ -72,6 +74,9 @@ const settings = {
state.settings = newSettings
state.db = newDbSettings
},
SET_TABS: (state, tabs) => {
state.tabs = tabs
},
SET_TERMS_OF_SERVICES: (state, data) => {
state.termsOfServices = data
},
@ -107,15 +112,16 @@ const settings = {
async FetchSettings({ commit, getters }) {
commit('SET_LOADING', true)
try {
const response = await fetchSettings(getters.authHost, getters.token)
const description = await fetchDescription(getters.authHost, getters.token)
commit('SET_DESCRIPTION', description.data)
const searchObject = formSearchObject(description.data)
const settings = await fetchSettings(getters.authHost, getters.token)
commit('SET_SETTINGS', settings.data.configs)
const { data } = await fetchDescription(getters.authHost, getters.token)
commit('SET_DESCRIPTION', data)
const searchObject = formSearchObject(data)
commit('SET_SEARCH', searchObject)
commit('SET_SETTINGS', response.data.configs)
commit('SET_TABS', tabs)
} catch (_e) {
commit('TOGGLE_TABS', true)
commit('SET_ACTIVE_TAB', 'relays')
commit('SET_LOADING', false)
return
}
@ -138,8 +144,8 @@ const settings = {
commit('TOGGLE_REBOOT', response.data.need_reboot)
commit('REMOVE_SETTING_FROM_UPDATED', { group, key, subkeys: subkeys || [] })
},
SetActiveTab({ commit }, tab) {
commit('SET_ACTIVE_TAB', tab)
SetSearchQuery({ commit }, query) {
commit('SET_SEARCH_QUERY', query)
},
async SubmitChanges({ getters, commit, state }) {
const configs = Object.keys(state.updatedSettings).reduce((acc, group) => {

View file

@ -91,7 +91,7 @@
}
.submenu-title-noDropdown {
padding-left: 10px !important;
padding-left: 8px !important;
position: relative;
.el-tooltip {

View file

@ -19,7 +19,7 @@ $menuHover:#263445;
$subMenuBg:#1f2d3d;
$subMenuHover:#001528;
$sideBarWidth: 180px;
$sideBarWidth: 205px;
// the :export directive is the magic sauce for webpack
:export {

22
src/utils/tabs.js Normal file
View file

@ -0,0 +1,22 @@
export const tabs = [
{ label: 'ActivityPub', path: 'activity-pub', tab: ':activity_pub' },
{ label: 'Authentication', path: 'authentication', tab: ':authentication' },
{ label: 'Captcha', path: 'captcha', tab: ':captcha' },
{ label: 'BBS / SSH access', path: 'esshd', tab: ':esshd' },
{ label: 'Emoji', path: 'emoji', tab: ':emoji' },
{ label: 'Frontend', path: 'frontend', tab: ':frontend' },
{ label: 'Gopher', path: 'gopher', tab: ':gopher' },
{ label: 'HTTP', path: 'http', tab: ':http' },
{ label: 'Instance', path: 'instance', tab: ':instance' },
{ label: 'Job queue', path: 'job-queue', tab: ':job_queue' },
{ label: 'Link Formatter', path: 'link-formatter', tab: ':link_formatter' },
{ label: 'Logger', path: 'logger', tab: ':logger' },
{ label: 'Mailer', path: 'mailer', tab: ':mailer' },
{ label: 'Media Proxy', path: 'media-proxy', tab: ':media_proxy' },
{ label: 'Metadata', path: 'metadata', tab: ':metadata' },
{ label: 'MRF', path: 'mrf', tab: ':mrf' },
{ label: 'Rate limiters', path: 'rate-limiters', tab: ':rate_limiters' },
{ label: 'Web push encryption', path: 'web-push', tab: ':web_push' },
{ label: 'Upload', path: 'upload', tab: ':upload' },
{ label: 'Other', path: 'other', tab: ':other' }
]

View file

@ -1,9 +1,9 @@
<template>
<span>
<svg-icon :icon-class="icon"/>
<div>
<i v-if="icon" :class="icon" class="menu-item-icon"/>
<span slot="title">{{ title }}</span>
<el-badge :value="count" type="primary" class="count-badge" />
</span>
</div>
</template>
<script>
@ -31,4 +31,11 @@ export default {
margin-left: 5px;
height: 48px;
}
.menu-item-icon {
margin-right: 5px;
width: 18px;
text-align: center;
font-size: 18px;
vertical-align: middle;
}
</style>

View file

@ -1,7 +1,7 @@
<template>
<div v-if="!item.hidden && item.children && invitesEnabled" class="menu-wrapper">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<div v-if="!item.hidden && invitesEnabled" class="menu-wrapper">
<template
v-if="item.children && hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
<app-link :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item
@ -12,7 +12,7 @@
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)">
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" :id="item.meta.title">
<template slot="title">
<item
v-if="item.meta"
@ -32,7 +32,7 @@
class="nest-menu" />
<app-link v-else :to="resolvePath(child.path)" :key="child.name">
<el-menu-item :index="resolvePath(child.path)">
<el-menu-item :index="resolvePath(child.path)" class="submenu-item">
<item
v-if="child.meta"
:count="showCount(item) ? normalizedReportsCount : null"
@ -90,14 +90,14 @@ export default {
},
methods: {
hasOneShowingChild(children, parent) {
const showingChildren = children.filter(item => {
if (item.hidden) {
if (parent.hasSubmenu) {
return false
} else {
}
const showingChildren = children.filter(item => {
// Temp set(will be used if only has one showing child)
this.onlyOneChild = item
return true
}
})
// When there is only one child router, the child router is displayed by default
@ -129,3 +129,9 @@ export default {
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.submenu-item {
padding-left: 54px !important;
}
</style>

View file

@ -7,6 +7,7 @@
:text-color="variables.menuText"
:active-text-color="variables.menuActiveText"
mode="vertical"
@open="handleOpen"
>
<sidebar-item v-for="route in permission_routers" :key="route.path" :item="route" :base-path="route.path"/>
</el-menu>
@ -17,13 +18,17 @@
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
import router from '@/router'
import { asyncRouterMap } from '@/router'
export default {
components: { SidebarItem },
computed: {
...mapGetters([
'permission_routers',
'sidebar'
'roles',
'sidebar',
'tabs'
]),
variables() {
return variables
@ -34,6 +39,49 @@ export default {
},
mounted() {
this.$store.dispatch('FetchOpenReportsCount')
},
methods: {
getMergedRoutes() {
const routes = router.getRoutes().filter(item => !item.hidden)
return routes.reduce((acc, element) => {
if (!element.parent || element.parent.path !== '/settings') {
return acc
} else {
const index = acc.findIndex(route => route.path === '/settings')
acc[index] = { ...acc[index], children: [...acc[index].children, element] }
return acc
}
}, [...asyncRouterMap])
},
async handleOpen($event) {
if ($event === '/settings') {
if (!localStorage.getItem('settingsTabs')) {
await this.$store.dispatch('FetchSettings')
const menuItems = this.tabs
localStorage.setItem('settingsTabs', JSON.stringify(menuItems))
menuItems.forEach(({ label, path }) => {
router.addRoute('Settings', {
path,
component: () => import(`@/views/settings`),
name: label,
meta: { title: label }
})
})
const routes = this.getMergedRoutes()
this.$store.dispatch('GenerateRoutes', { roles: this.roles, _routesWithSettings: routes })
}
let isRequesting = true
const step = () => {
document.querySelector('#settings').scrollIntoView({ block: 'start', behavior: 'smooth' })
if (isRequesting) requestAnimationFrame(step)
}
requestAnimationFrame(step)
setTimeout(() => {
isRequesting = false
}, 300) // this equals to the hide-timeout of the el-submenu
}
}
}
}
</script>

View file

@ -197,9 +197,6 @@ h1 {
.router-link {
text-decoration: none;
}
.search-container {
text-align: right;
}
.pagination {
text-align: center;
}

View file

@ -1,15 +1,21 @@
<template>
<div v-if="!loading" class="relays-container">
<div class="relays-header-container">
<h1>
{{ $t('relays.relays') }}
</h1>
<reboot-button/>
</div>
<div class="follow-relay-container">
<el-input v-model="newRelay" :placeholder="$t('settings.followRelay')" class="follow-relay" @keyup.enter.native="followRelay"/>
<el-button @click.native="followRelay">{{ $t('settings.follow') }}</el-button>
<el-input v-model="newRelay" :placeholder="$t('relays.followRelay')" class="follow-relay" @keyup.enter.native="followRelay"/>
<el-button @click.native="followRelay">{{ $t('relays.follow') }}</el-button>
</div>
<el-table :data="relays">
<el-table-column
:label="$t('settings.instanceUrl')"
:label="$t('relays.instanceUrl')"
prop="actor"/>
<el-table-column
:label="$t('settings.followedBack')"
:label="$t('relays.followedBack')"
:width="getLabelWidth"
prop="followed_back"
align="center">
@ -32,8 +38,11 @@
</template>
<script>
import RebootButton from '@/components/RebootButton'
export default {
name: 'Relays',
components: { RebootButton },
data() {
return {
newRelay: ''
@ -70,5 +79,5 @@ export default {
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@include settings
@include relays
</style>

View file

@ -56,6 +56,9 @@ export default {
loading() {
return this.$store.state.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
user() {
return this.settings.description.find(setting => setting.key === ':user')
},
@ -63,6 +66,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', ':user']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -80,6 +92,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -81,6 +81,18 @@ export default {
},
pleromaAuthenticatorData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Auth.Authenticator']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
@ -100,6 +112,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -61,6 +61,18 @@ export default {
},
loading() {
return this.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
@ -80,6 +92,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -1,9 +1,5 @@
<template>
<div class="emoji-packs">
<div class="emoji-packs-header">
<h1>{{ $t('emoji.emojiPacks') }}</h1>
<reboot-button/>
</div>
<div class="emoji-header-container">
<div class="emoji-packs-header-button-container">
<el-button class="reload-emoji-button" @click="reloadEmoji">{{ $t('emoji.reloadEmoji') }}</el-button>
@ -15,7 +11,7 @@
</div>
</div>
<el-tabs v-model="activeTab" type="card" class="emoji-packs-tabs">
<el-tab-pane :label="$t('emoji.localPacks')" name="local">
<el-tab-pane v-if="!emojiPacksDisabled" :label="$t('emoji.localPacks')" name="local">
<el-form :label-width="labelWidth" class="emoji-packs-form">
<el-form-item :label="$t('emoji.localPacks')">
<el-button @click="refreshLocalPacks">{{ $t('emoji.refreshLocalPacks') }}</el-button>
@ -49,7 +45,7 @@
/>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('emoji.remotePacks')" name="remote">
<el-tab-pane v-if="!emojiPacksDisabled" :label="$t('emoji.remotePacks')" name="remote">
<el-form :label-width="labelWidth" class="emoji-packs-form">
<el-form-item :label="$t('emoji.remotePacks')">
<div class="create-pack">
@ -82,18 +78,31 @@
/>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('settings.settings')" name="settings">
<div v-if="!loading" :class="isSidebarOpen" class="form-container">
<el-form :model="emojiData" :label-position="labelPosition" :label-width="settingsLabelWidth">
<setting :setting-group="emoji" :data="emojiData"/>
</el-form>
<div class="submit-button-container">
<el-button class="submit-button" type="primary" @click="onSubmit">Submit</el-button>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import LocalEmojiPack from './components/LocalEmojiPack'
import RemoteEmojiPack from './components/RemoteEmojiPack'
import { mapGetters } from 'vuex'
import i18n from '@/lang'
import RebootButton from '@/components/RebootButton'
import LocalEmojiPack from '../../emojiPacks/LocalEmojiPack'
import RemoteEmojiPack from '../../emojiPacks/RemoteEmojiPack'
import Setting from './Setting'
import _ from 'lodash'
export default {
components: { LocalEmojiPack, RebootButton, RemoteEmojiPack },
name: 'Emoji',
components: { LocalEmojiPack, RemoteEmojiPack, Setting },
data() {
return {
activeTab: 'local',
@ -104,18 +113,37 @@ export default {
}
},
computed: {
...mapGetters([
'settings'
]),
currentLocalPacksPage() {
return this.$store.state.emojiPacks.currentLocalPacksPage
},
currentRemotePacksPage() {
return this.$store.state.emojiPacks.currentRemotePacksPage
},
emoji() {
return this.settings.description.find(setting => setting.key === ':emoji')
},
emojiData() {
return _.get(this.settings.settings, [':pleroma', ':emoji']) || {}
},
emojiPacksDisabled() {
const disabledFeatures = process.env.DISABLED_FEATURES || []
return disabledFeatures.includes('emoji-packs')
},
isMobile() {
return this.$store.state.app.device === 'mobile'
},
isSidebarOpen() {
return this.$store.state.app.sidebar.opened ? 'sidebar-opened' : 'sidebar-closed'
},
isTablet() {
return this.$store.state.app.device === 'tablet'
},
labelPosition() {
return this.isMobile ? 'top' : 'right'
},
labelWidth() {
if (this.isMobile) {
return '105px'
@ -125,6 +153,9 @@ export default {
return '200px'
}
},
loading() {
return this.settings.loading
},
localPacks() {
return this.$store.state.emojiPacks.localPacks
},
@ -147,12 +178,35 @@ export default {
},
remotePacksCount() {
return this.$store.state.emojiPacks.remotePacksCount
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
settingsLabelWidth() {
if (this.isMobile) {
return '120px'
} else if (this.isTablet) {
return '200px'
} else {
return '280px'
}
}
},
mounted() {
this.$store.dispatch('GetNodeInfo')
this.$store.dispatch('NeedReboot')
this.refreshLocalPacks()
if (this.searchQuery.length > 0) {
this.activeTab = 'settings'
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
console.log(selectedSetting)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
closeLocalTabs() {
@ -193,6 +247,17 @@ export default {
this.$store.dispatch('ReloadEmoji')
})
},
async onSubmit() {
try {
await this.$store.dispatch('SubmitChanges')
} catch (e) {
return
}
this.$message({
type: 'success',
message: i18n.t('settings.success')
})
},
refreshLocalPacks() {
try {
this.$store.dispatch('FetchLocalEmojiPacks', this.currentLocalPacksPage)
@ -225,120 +290,7 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
.create-pack {
display: flex;
justify-content: space-between
}
.create-pack-button {
margin-left: 10px;
}
.emoji-header-container {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 15px 22px 15px;
}
.emoji-name-warning {
color: #666666;
font-size: 13px;
line-height: 22px;
margin: 5px 0 0 0;
overflow-wrap: break-word;
overflow: hidden;
text-overflow: ellipsis;
}
.emoji-packs-header-button-container {
display: flex;
}
.emoji-packs-form {
margin-top: 15px;
}
.emoji-packs-header {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 15px 15px 15px;
}
.emoji-packs-tabs {
margin: 0 15px 15px 15px;
}
.import-pack-button {
margin-left: 10px;
width: 30%;
max-width: 700px;
}
h1 {
margin: 0;
}
.line {
width: 100%;
height: 0;
border: 1px solid #eee;
margin-bottom: 22px;
}
.pagination {
margin: 25px 0;
text-align: center;
}
.reboot-button {
padding: 10px;
margin: 0;
width: 145px;
}
@media only screen and (min-width: 1824px) {
.emoji-packs {
max-width: 1824px;
margin: auto;
}
}
@media only screen and (max-width:480px) {
.create-pack {
height: 82px;
flex-direction: column;
}
.create-pack-button {
margin-left: 0;
}
.divider {
margin: 15px 0;
}
.el-message {
min-width: 80%;
}
.el-message-box {
width: 80%;
}
.emoji-header-container {
flex-direction: column;
align-items: flex-start;
}
.emoji-packs-form {
margin: 0 7px;
label {
padding-right: 8px;
}
.el-form-item {
margin-bottom: 15px;
}
}
.emoji-packs-header {
margin: 15px;
}
.emoji-packs-header-button-container {
height: 82px;
flex-direction: column;
.el-button+.el-button {
margin: 7px 0 0 0;
width: fit-content;
}
}
.import-pack-button {
width: 90%;
}
.reload-emoji-button {
width: fit-content;
}
}
@import '../../styles/settings';
@include settings;
@include emoji;
</style>

View file

@ -51,6 +51,18 @@ export default {
},
loading() {
return this.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
@ -76,6 +88,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -18,10 +18,6 @@
<setting :setting-group="assets" :data="assetsData"/>
</el-form>
<el-divider v-if="assets" class="divider thick-line"/>
<el-form :model="emojiData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="emoji" :data="emojiData"/>
</el-form>
<el-divider v-if="emoji" class="divider thick-line"/>
<el-form :model="chatData" :label-position="labelPosition" :label-width="labelWidth">
<setting :setting-group="chat" :data="chatData"/>
</el-form>
@ -65,12 +61,6 @@ export default {
chatData() {
return _.get(this.settings.settings, [':pleroma', ':chat']) || {}
},
emoji() {
return this.settings.description.find(setting => setting.key === ':emoji')
},
emojiData() {
return _.get(this.settings.settings, [':pleroma', ':emoji']) || {}
},
frontend() {
return this.settings.description.find(setting => setting.key === ':frontend_configurations')
},
@ -122,6 +112,9 @@ export default {
preloadData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Preload']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
staticFe() {
return this.settings.description.find(setting => setting.key === ':static_fe')
},
@ -129,6 +122,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', ':static_fe']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -146,6 +148,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -51,6 +51,18 @@ export default {
},
loading() {
return this.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
@ -70,6 +82,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -76,6 +76,9 @@ export default {
loading() {
return this.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
webCacheTtl() {
return this.settings.description.find(setting => setting.key === ':web_cache_ttl')
},
@ -83,6 +86,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', ':web_cache_ttl']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -100,6 +112,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -394,6 +394,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -146,6 +146,9 @@ export default {
restrictUnauthenticatedData() {
return _.get(this.settings.settings, [':pleroma', ':restrict_unauthenticated']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
scheduledActivity() {
return this.$store.state.settings.description.find(setting => setting.key === 'Pleroma.ScheduledActivity')
},
@ -172,6 +175,14 @@ export default {
}
},
async mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
await this.$store.dispatch('FetchInstanceDocument', 'instance-panel')
},
methods: {
@ -198,6 +209,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -96,6 +96,9 @@ export default {
poolsData() {
return _.get(this.settings.settings, [':pleroma', ':pools']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
workers() {
return this.settings.description.find(setting => setting.key === ':workers')
},
@ -103,6 +106,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', ':workers']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -120,6 +132,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -51,6 +51,18 @@ export default {
},
loading() {
return this.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
@ -70,6 +82,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -76,6 +76,9 @@ export default {
loggerData() {
return _.get(this.settings.settings, [':logger', ':backends']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
quack() {
return this.settings.description.find(setting => setting.group === ':quack')
},
@ -83,6 +86,15 @@ export default {
return _.get(this.settings.settings, [':quack']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -100,6 +112,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -51,6 +51,18 @@ export default {
},
mrfSettings() {
return this.settings.description.filter(el => el.tab === 'mrf')
},
searchQuery() {
return this.$store.state.settings.searchQuery
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
@ -83,6 +95,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -82,6 +82,9 @@ export default {
newUsersDigestEmailData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.NewUsersDigestEmail']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
swoosh() {
return this.settings.description.find(setting => setting.group === ':swoosh')
},
@ -95,6 +98,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Emails.UserEmail']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -112,6 +124,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -76,6 +76,9 @@ export default {
mediaProxyData() {
return _.get(this.settings.settings, [':pleroma', ':media_proxy']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
scriptInvalidation() {
return this.settings.description.find(setting => setting.key === 'Pleroma.Web.MediaProxy.Invalidation.Script')
},
@ -83,6 +86,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.MediaProxy.Invalidation.Script']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -100,6 +112,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -56,6 +56,9 @@ export default {
metadataData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Metadata']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
richMedia() {
return this.settings.description.find(setting => setting.key === ':rich_media')
},
@ -63,6 +66,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', ':rich_media']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -80,6 +92,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -110,6 +110,9 @@ export default {
remoteIpData() {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Web.Plugs.RemoteIp']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
termsOfServicesContent: {
get() {
return this.$store.state.settings.termsOfServices
@ -120,6 +123,14 @@ export default {
}
},
async mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
await this.$store.dispatch('FetchInstanceDocument', 'terms-of-service')
},
methods: {
@ -146,6 +157,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -51,6 +51,18 @@ export default {
},
loading() {
return this.$store.state.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
@ -70,6 +82,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -179,6 +179,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -72,6 +72,9 @@ export default {
s3Data() {
return _.get(this.settings.settings, [':ex_aws', ':s3']) || {}
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
showUploadersS3() {
const uploader = _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload', ':uploader'])
return uploader === 'Pleroma.Uploaders.S3'
@ -111,6 +114,15 @@ export default {
return _.get(this.settings.settings, [':pleroma', 'Pleroma.Upload.Filter.AnonymizeFilename']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -128,6 +140,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -46,6 +46,9 @@ export default {
loading() {
return this.settings.loading
},
searchQuery() {
return this.$store.state.settings.searchQuery
},
vapidDetails() {
return this.settings.description.find(setting => setting.key === ':vapid_details')
},
@ -53,6 +56,15 @@ export default {
return _.get(this.settings.settings, [':web_push_encryption', ':vapid_details']) || {}
}
},
mounted() {
if (this.searchQuery.length > 0) {
const selectedSetting = document.querySelector(`[data-search="${this.searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
this.$store.dispatch('SetSearchQuery', '')
}
},
methods: {
async onSubmit() {
try {
@ -70,6 +82,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../styles/main';
@import '../../styles/settings';
@include settings
</style>

View file

@ -1,6 +1,7 @@
export { default as ActivityPub } from './ActivityPub'
export { default as Authentication } from './Authentication'
export { default as Captcha } from './Captcha'
export { default as Emoji } from './Emoji'
export { default as Esshd } from './Esshd'
export { default as Frontend } from './Frontend'
export { default as Gopher } from './Gopher'
@ -15,6 +16,5 @@ export { default as Metadata } from './Metadata'
export { default as Mrf } from './MRF'
export { default as Other } from './Other'
export { default as RateLimiters } from './RateLimiters'
export { default as Relays } from './Relays'
export { default as Upload } from './Upload'
export { default as WebPush } from './WebPush'

View file

@ -115,6 +115,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -192,6 +192,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -202,6 +202,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include tiptap
</style>

View file

@ -103,6 +103,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -124,7 +124,7 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings;
.image-upload-area {

View file

@ -103,6 +103,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -99,6 +99,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -77,6 +77,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -148,6 +148,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -69,6 +69,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -110,6 +110,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -93,6 +93,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -62,6 +62,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss'>
@import '../../styles/main';
@import '../../../styles/settings';
@include settings
</style>

View file

@ -16,9 +16,13 @@ export const tabs = description => {
label: 'settings.captcha',
settings: ['Pleroma.Captcha', 'Pleroma.Captcha.Kocaptcha']
},
'emoji': {
label: 'settings.emoji',
settings: [':emoji']
},
'frontend': {
label: 'settings.frontend',
settings: [':assets', ':chat', ':frontends', ':emoji', ':frontend_configurations', ':markup', ':static_fe']
settings: [':assets', ':chat', ':frontends', ':emoji', ':frontend_configurations', ':markup', ':static_fe', 'Pleroma.Web.Preload']
},
'gopher': {
label: 'settings.gopher',
@ -64,10 +68,6 @@ export const tabs = description => {
label: 'settings.rateLimiters',
settings: [':rate_limit']
},
'relays': {
label: 'settings.relays',
settings: ['relays']
},
'web-push': {
label: 'settings.webPush',
settings: [':vapid_details']

View file

@ -4,7 +4,7 @@
<reboot-button/>
</div>
<div v-if="isDesktop">
<div :class="isSidebarOpen" class="settings-header-container">
<div :class="isSidebarOpen">
<h1 class="settings-header">{{ $t('settings.settings') }}</h1>
<div class="docs-search-container">
<el-link
@ -29,31 +29,11 @@
@select="handleSearchSelect"/>
</div>
</div>
<el-tabs v-model="activeTab" tab-position="left">
<el-tab-pane
v-for="(value, componentName) in tabs"
:label="$t(value.label)"
:disabled="configDisabled || settingsCantBeChanged(value.settings)"
:key="componentName"
:name="componentName"
lazy>
<component :is="componentName"/>
</el-tab-pane>
</el-tabs>
</div>
<div v-if="isMobile || isTablet">
<div :class="isSidebarOpen" class="settings-header-container">
<h1 class="settings-header">{{ $t('settings.settings') }}</h1>
</div>
<div class="nav-container">
<el-select v-model="activeTab" class="settings-menu" placeholder="Select">
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="configDisabled"/>
</el-select>
<el-link
:underline="false"
href="https://docs-develop.pleroma.social/backend/administration/CLI_tasks/config/"
@ -66,38 +46,29 @@
</el-button>
</el-link>
</div>
<div class="settings-search-input-container"/>
<activity-pub v-if="activeTab === 'activityPub'"/>
<authentication v-if="activeTab === 'auth'"/>
<link-formatter v-if="activeTab === 'linkFormatter'"/>
<esshd v-if="activeTab === 'esshd'"/>
<captcha v-if="activeTab === 'captcha'"/>
<frontend v-if="activeTab === 'frontend'"/>
<gopher v-if="activeTab === 'gopher'"/>
<http v-if="activeTab === 'http'"/>
<instance v-if="activeTab === 'instance'"/>
<job-queue v-if="activeTab === 'jobQueue'"/>
<logger v-if="activeTab === 'logger'"/>
<mailer v-if="activeTab === 'mailer'"/>
<media-proxy v-if="activeTab === 'mediaProxy'"/>
<metadata v-if="activeTab === 'metadata'"/>
<mrf v-if="activeTab === 'mrf'"/>
<rate-limiters v-if="activeTab === 'rateLimiters'"/>
<relays v-if="activeTab === 'relays'"/>
<web-push v-if="activeTab === 'webPush'"/>
<upload v-if="activeTab === 'upload'"/>
<other v-if="activeTab === 'other'"/>
<div class="settings-search-container">
<el-autocomplete
v-model="searchQuery"
:fetch-suggestions="querySearch"
:trigger-on-focus="false"
clearable
placeholder="Search"
prefix-icon="el-icon-search"
class="settings-search-input"
@select="handleSearchSelect"/>
</div>
<component :is="componentName"/>
</div>
</div>
</template>
<script>
import i18n from '@/lang'
import { tabs } from './components/tabs'
import {
ActivityPub,
Authentication,
Captcha,
Emoji,
Esshd,
Frontend,
Gopher,
@ -112,7 +83,6 @@ import {
Mrf,
Other,
RateLimiters,
Relays,
Upload,
WebPush
} from './components'
@ -123,6 +93,7 @@ export default {
ActivityPub,
Authentication,
Captcha,
Emoji,
Esshd,
Frontend,
Gopher,
@ -137,46 +108,18 @@ export default {
Mrf,
Other,
RateLimiters,
Relays,
RebootButton,
Upload,
WebPush
},
data() {
return {
options: [
{ value: 'activityPub', label: i18n.t('settings.activityPub') },
{ value: 'auth', label: i18n.t('settings.auth') },
{ value: 'linkFormatter', label: i18n.t('settings.linkFormatter') },
{ value: 'esshd', label: i18n.t('settings.esshd') },
{ value: 'captcha', label: i18n.t('settings.captcha') },
{ value: 'frontend', label: i18n.t('settings.frontend') },
{ value: 'gopher', label: i18n.t('settings.gopher') },
{ value: 'http', label: i18n.t('settings.http') },
{ value: 'instance', label: i18n.t('settings.instance') },
{ value: 'jobQueue', label: i18n.t('settings.jobQueue') },
{ value: 'logger', label: i18n.t('settings.logger') },
{ value: 'mailer', label: i18n.t('settings.mailer') },
{ value: 'mediaProxy', label: i18n.t('settings.mediaProxy') },
{ value: 'metadata', label: i18n.t('settings.metadata') },
{ value: 'mrf', label: i18n.t('settings.mrf') },
{ value: 'rateLimiters', label: i18n.t('settings.rateLimiters') },
{ value: 'relays', label: i18n.t('settings.relays') },
{ value: 'webPush', label: i18n.t('settings.webPush') },
{ value: 'upload', label: i18n.t('settings.upload') },
{ value: 'other', label: i18n.t('settings.other') }
],
searchQuery: ''
}
},
computed: {
activeTab: {
get() {
return this.$store.state.settings.activeTab
},
set(tab) {
this.$store.dispatch('SetActiveTab', tab)
}
componentName() {
return this.$route.path.split('/settings/').pop()
},
configDisabled() {
return this.$store.state.settings.configDisabled
@ -209,12 +152,19 @@ export default {
this.$store.dispatch('FetchSettings')
},
methods: {
async handleSearchSelect(selectedValue) {
handleSearchSelect(selectedValue) {
this.$store.dispatch('SetSearchQuery', selectedValue.key)
const tab = Object.keys(this.tabs).find(tab => {
return this.tabs[tab].settings.includes(selectedValue.group === ':pleroma' ? selectedValue.key : selectedValue.group)
})
await this.$store.dispatch('SetActiveTab', tab)
const selectedSetting = document.querySelector(`[data-search="${selectedValue.key}"]`)
if (this.$router.currentRoute.path === `/settings/${tab}`) {
this.scrollTo(selectedValue.key)
} else if (tab) {
this.$router.push({ path: `/settings/${tab}` })
}
},
scrollTo(searchQuery) {
const selectedSetting = document.querySelector(`[data-search="${searchQuery}"]`)
if (selectedSetting) {
selectedSetting.scrollIntoView({ block: 'start', behavior: 'smooth' })
}
@ -232,8 +182,6 @@ export default {
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
}
@ -245,6 +193,6 @@ export default {
</script>
<style rel='stylesheet/scss' lang='scss' scoped>
@import './styles/main';
@import '../styles/settings';
@include settings
</style>

View file

@ -0,0 +1,31 @@
@mixin relays {
.follow-relay {
width: 350px;
margin-right: 7px;
}
.relays-container {
margin: 0 15px;
}
.relays-header-container {
display: flex;
align-items: center;
justify-content: space-between;
}
@media only screen and (max-width:480px) {
.follow-relay {
width: 75%;
margin-right: 5px;
input {
width: 100%;
}
}
.follow-relay-container {
display: flex;
justify-content: space-between;
margin: 0 5px;
}
.relays-container {
margin: 0 10px;
}
}
}

View file

@ -30,7 +30,8 @@
height: 2px;
}
.docs-search-container {
float: right;
display: flex;
justify-content: flex-end;
margin-right: 30px;
}
.editable-keyword-container {
@ -68,10 +69,6 @@
padding: 2px 3px;
}
}
.follow-relay {
width: 350px;
margin-right: 7px;
}
.form-container {
margin-bottom: 80px;
}
@ -271,9 +268,6 @@
right: 0;
z-index: 2000;
}
.relays-container {
margin: 0 15px;
}
.replacement-input {
width: 80%;
margin-left: 8px;
@ -325,9 +319,6 @@
.header-sidebar-closed {
max-width: 1728px;
}
.settings-header-container {
height: 87px;
}
.settings-search-input {
width: 350px;
margin-left: 5px;
@ -457,18 +448,6 @@
.divider .thick-line {
height: 2px;
}
.follow-relay {
width: 75%;
margin-right: 5px;
input {
width: 100%;
}
}
.follow-relay-container {
display: flex;
justify-content: space-between;
margin: 0 5px;
}
.frontend-container {
margin: 0 15px 10px 15px;
.description-container {
@ -515,13 +494,6 @@
.limit-input {
width: 45%;
}
.nav-container {
display: flex;
height: 36px;
justify-content: space-between;
align-items: center;
margin: 15px;
}
.proxy-url-input {
flex-direction: column;
align-items: flex-start;
@ -567,19 +539,9 @@
display: inline-block;
margin: 10px 15px 15px 15px;
}
.docs-search-container {
float: right;
}
.settings-search-input {
width: 100%;
margin-left: 0;
}
.settings-search-input-container {
margin: 0 15px 15px 15px;
}
.settings-menu {
width: 163px;
margin-right: 5px;
margin: 0 15px 25px 15px;
width: stretch;
}
.socks5-checkbox-container {
width: 100%;
@ -660,16 +622,15 @@
width: 40%;
margin-right: 4px
}
.relays-container {
margin: 0 10px;
}
.replacement-input {
width: 60%;
margin-left: 4px;
margin-right: 5px
}
.settings-header-container {
height: 45px;
display: flex;
justify-content: space-between;
margin-right: 15px;
}
.value-input {
width: 60%;
@ -677,6 +638,7 @@
margin-right: 8px
}
}
@media only screen and (max-width:818px) and (min-width: 481px) {
.delete-setting-button {
margin: 4px 0 0 10px;
@ -708,13 +670,6 @@
display: flex;
justify-content: space-between;
}
.nav-container {
display: flex;
height: 36px;
justify-content: space-between;
align-items: center;
margin: 15px 30px 15px 15px;
}
.rate-limit-content {
width: 65%;
}
@ -725,7 +680,14 @@
float: right;
}
.settings-header-container {
height: 36px;
display: flex;
justify-content: space-between;
margin-right: 15px;
}
.settings-search-container {
display: flex;
justify-content: flex-end;
margin-right: 15px;
}
.settings-search-input {
width: 250px;
@ -898,3 +860,122 @@
}
}
}
@mixin emoji {
.create-pack {
display: flex;
justify-content: space-between
}
.create-pack-button {
margin-left: 10px;
}
.emoji-header-container {
display: flex;
align-items: center;
justify-content: space-between;
margin: 0 15px 22px 15px;
}
.emoji-name-warning {
color: #666666;
font-size: 13px;
line-height: 22px;
margin: 5px 0 0 0;
overflow-wrap: break-word;
overflow: hidden;
text-overflow: ellipsis;
}
.emoji-packs-header-button-container {
display: flex;
}
.emoji-packs-form {
margin-top: 15px;
}
.emoji-packs-header {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 15px 15px 15px;
}
.emoji-packs-tabs {
margin: 0 15px 15px 15px;
}
.import-pack-button {
margin-left: 10px;
width: 30%;
max-width: 700px;
}
h1 {
margin: 0;
}
.line {
width: 100%;
height: 0;
border: 1px solid #eee;
margin-bottom: 22px;
}
.pagination {
margin: 25px 0;
text-align: center;
}
.reboot-button {
padding: 10px;
margin: 0;
width: 145px;
}
@media only screen and (min-width: 1824px) {
.emoji-packs {
max-width: 1824px;
margin: auto;
}
}
@media only screen and (max-width:480px) {
.create-pack {
height: 82px;
flex-direction: column;
}
.create-pack-button {
margin-left: 0;
}
.divider {
margin: 15px 0;
}
.el-message {
min-width: 80%;
}
.el-message-box {
width: 80%;
}
.emoji-header-container {
flex-direction: column;
align-items: flex-start;
}
.emoji-packs-form {
margin: 0 7px;
label {
padding-right: 8px;
}
.el-form-item {
margin-bottom: 15px;
}
}
.emoji-packs-header {
margin: 15px;
}
.emoji-packs-header-button-container {
height: 82px;
flex-direction: column;
.el-button+.el-button {
margin: 7px 0 0 0;
width: fit-content;
}
}
.import-pack-button {
width: 90%;
}
.reload-emoji-button {
width: fit-content;
}
}
}

View file

@ -173,7 +173,8 @@ describe('Form search object', () => {
label: "Admin token" }
]
}]
const expected = [{
const expected = [
{
label: "Admin token",
key: ":admin_token",
groupKey: ":pleroma",
@ -193,7 +194,8 @@ describe('Form search object', () => {
key: ':terms_of_services',
label: 'Terms of Services',
search: ['Terms of Services', ':terms_of_services']
}]
}
]
expect(_.isEqual(formSearchObject(description), expected)).toBeTruthy()
})
@ -269,7 +271,8 @@ describe('Form search object', () => {
key: ':instance_panel',
label: 'Instance Panel',
search: ['Instance Panel', ':instance_panel']
}, {
},
{
groupKey: ':terms_of_services',
groupLabel: 'Terms of Services',
key: ':terms_of_services',

View file

@ -7,6 +7,7 @@ import app from '@/store/modules/app'
import settings from '@/store/modules/settings'
import user from '@/store/modules/user'
import getters from '@/store/getters'
import _ from 'lodash'
config.mocks["$t"] = () => {}
@ -22,6 +23,8 @@ describe('Settings search', () => {
let store
let actions
let appActions
let $route
let $router
beforeEach(() => {
appActions = { ...app.actions, NeedReboot: jest.fn() }
@ -34,11 +37,17 @@ describe('Settings search', () => {
},
getters
})
$route = { path: '/settings/path' }
$router = { push: jest.fn(), currentRoute: {} }
})
it('shows search input', async (done) => {
const wrapper = mount(Settings, {
store,
mocks: {
$route,
$router
},
localVue
})
@ -47,22 +56,31 @@ describe('Settings search', () => {
expect(searchInput.exists()).toBe(true)
done()
})
it('changes tab when search value was selected', async (done) => {
const wrapper = mount(Settings, {
store,
mocks: {
$route,
$router
},
localVue
})
wrapper.vm.handleSearchSelect({ group: 'Pleroma.Upload', key: 'Pleroma.Upload' })
expect(store.state.settings.activeTab).toBe('upload')
expect(store.state.settings.searchQuery).toBe('Pleroma.Upload')
expect($router.push).toHaveBeenCalledWith({ path: '/settings/upload' })
wrapper.vm.handleSearchSelect({ group: ':swoosh', key: ':serve_mailbox' })
expect(store.state.settings.activeTab).toBe('mailer')
expect(store.state.settings.searchQuery).toBe(':serve_mailbox')
expect($router.push).toHaveBeenCalledWith({ path: '/settings/mailer' })
wrapper.vm.handleSearchSelect({ group: ':pleroma', key: ':admin_token' })
expect(store.state.settings.activeTab).toBe('instance')
expect(store.state.settings.searchQuery).toBe(':admin_token')
expect($router.push).toHaveBeenCalledWith({ path: '/settings/instance' })
wrapper.vm.handleSearchSelect({ group: ':media_proxy', key: ':ssl_options' })
expect(store.state.settings.activeTab).toBe('media-proxy')
expect(store.state.settings.searchQuery).toBe(':ssl_options')
expect($router.push).toHaveBeenCalledWith({ path: '/settings/media-proxy' })
done()
})

View file

@ -11258,10 +11258,10 @@ vue-loader@15.3.0:
vue-hot-reload-api "^2.3.0"
vue-style-loader "^4.1.0"
vue-router@3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be"
integrity sha512-opKtsxjp9eOcFWdp6xLQPLmRGgfM932Tl56U9chYTnoWqKxQ8M20N7AkdEbM5beUh6wICoFGYugAX9vQjyJLFg==
vue-router@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.5.1.tgz#edf3cf4907952d1e0583e079237220c5ff6eb6c9"
integrity sha512-RRQNLT8Mzr8z7eL4p7BtKvRaTSGdCbTy2+Mm5HTJvLGYSSeG9gDzNasJPP/yOYKLy+/cLG/ftrqq5fvkFwBJEw==
vue-splitpane@1.0.2:
version "1.0.2"