biome format --write
This commit is contained in:
parent
8372348148
commit
9262e803ec
415 changed files with 54076 additions and 17419 deletions
|
|
@ -11,10 +11,10 @@ import MapSetting from '../helpers/map_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const AuthTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -26,14 +26,16 @@ const AuthTab = {
|
|||
AttachmentSetting,
|
||||
GroupSetting,
|
||||
ListSetting,
|
||||
MapSetting
|
||||
MapSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
LDAPEnabled () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':ldap'][':enabled']
|
||||
LDAPEnabled() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':ldap'][
|
||||
':enabled'
|
||||
]
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default AuthTab
|
||||
|
|
|
|||
|
|
@ -16,15 +16,10 @@ import {
|
|||
faArrowsRotate,
|
||||
faFolderOpen,
|
||||
faServer,
|
||||
faDownload
|
||||
faDownload,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faArrowsRotate,
|
||||
faFolderOpen,
|
||||
faDownload,
|
||||
faServer
|
||||
)
|
||||
library.add(faArrowsRotate, faFolderOpen, faDownload, faServer)
|
||||
|
||||
const EmojiTab = {
|
||||
components: {
|
||||
|
|
@ -36,14 +31,14 @@ const EmojiTab = {
|
|||
Popover,
|
||||
ConfirmModal,
|
||||
ModifiedIndicator,
|
||||
EmojiEditingPopover
|
||||
EmojiEditingPopover,
|
||||
},
|
||||
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
knownLocalPacks: { },
|
||||
knownRemotePacks: { },
|
||||
editedMetadata: { },
|
||||
knownLocalPacks: {},
|
||||
knownRemotePacks: {},
|
||||
editedMetadata: {},
|
||||
packName: '',
|
||||
newPackName: '',
|
||||
deleteModalVisible: false,
|
||||
|
|
@ -51,20 +46,20 @@ const EmojiTab = {
|
|||
remotePackDownloadAs: '',
|
||||
|
||||
remotePackURL: '',
|
||||
remotePackFile: null
|
||||
remotePackFile: null,
|
||||
}
|
||||
},
|
||||
|
||||
provide () {
|
||||
provide() {
|
||||
return { emojiAddr: this.emojiAddr }
|
||||
},
|
||||
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
pack () {
|
||||
pack() {
|
||||
return this.packName !== '' ? this.knownPacks[this.packName] : undefined
|
||||
},
|
||||
packMeta () {
|
||||
packMeta() {
|
||||
if (this.packName === '') return {}
|
||||
if (this.editedMetadata[this.packName] === undefined) {
|
||||
this.editedMetadata[this.packName] = clone(this.pack.pack)
|
||||
|
|
@ -72,31 +67,36 @@ const EmojiTab = {
|
|||
|
||||
return this.editedMetadata[this.packName]
|
||||
},
|
||||
knownPacks () {
|
||||
knownPacks() {
|
||||
// Copy the object itself but not the children, so they are still passed by reference and modified
|
||||
const result = clone(this.knownLocalPacks)
|
||||
for (const instName in this.knownRemotePacks) {
|
||||
for (const instPack in this.knownRemotePacks[instName]) {
|
||||
result[`${instPack}@${instName}`] = this.knownRemotePacks[instName][instPack]
|
||||
result[`${instPack}@${instName}`] =
|
||||
this.knownRemotePacks[instName][instPack]
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
},
|
||||
downloadWillReplaceLocal () {
|
||||
return (this.remotePackDownloadAs.trim() === '' && this.pack.remote && this.pack.remote.baseName in this.knownLocalPacks) ||
|
||||
(this.remotePackDownloadAs in this.knownLocalPacks)
|
||||
}
|
||||
downloadWillReplaceLocal() {
|
||||
return (
|
||||
(this.remotePackDownloadAs.trim() === '' &&
|
||||
this.pack.remote &&
|
||||
this.pack.remote.baseName in this.knownLocalPacks) ||
|
||||
this.remotePackDownloadAs in this.knownLocalPacks
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
reloadEmoji () {
|
||||
reloadEmoji() {
|
||||
this.$store.state.api.backendInteractor.reloadEmoji()
|
||||
},
|
||||
importFromFS () {
|
||||
importFromFS() {
|
||||
this.$store.state.api.backendInteractor.importEmojiFromFS()
|
||||
},
|
||||
emojiAddr (name) {
|
||||
emojiAddr(name) {
|
||||
if (this.pack.remote !== undefined) {
|
||||
// Remote pack
|
||||
return `${this.pack.remote.instance}/emoji/${encodeURIComponent(this.pack.remote.baseName)}/${name}`
|
||||
|
|
@ -105,115 +105,141 @@ const EmojiTab = {
|
|||
}
|
||||
},
|
||||
|
||||
createEmojiPack () {
|
||||
this.$store.state.api.backendInteractor.createEmojiPack(
|
||||
{ name: this.newPackName }
|
||||
).then(resp => resp.json()).then(resp => {
|
||||
if (resp === 'ok') {
|
||||
return this.refreshPackList()
|
||||
} else {
|
||||
this.displayError(resp.error)
|
||||
return Promise.reject(resp)
|
||||
}
|
||||
}).then(() => {
|
||||
this.packName = this.newPackName
|
||||
this.newPackName = ''
|
||||
})
|
||||
createEmojiPack() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.createEmojiPack({ name: this.newPackName })
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp === 'ok') {
|
||||
return this.refreshPackList()
|
||||
} else {
|
||||
this.displayError(resp.error)
|
||||
return Promise.reject(resp)
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
this.packName = this.newPackName
|
||||
this.newPackName = ''
|
||||
})
|
||||
},
|
||||
deleteEmojiPack () {
|
||||
this.$store.state.api.backendInteractor.deleteEmojiPack(
|
||||
{ name: this.packName }
|
||||
).then(resp => resp.json()).then(resp => {
|
||||
if (resp === 'ok') {
|
||||
return this.refreshPackList()
|
||||
} else {
|
||||
this.displayError(resp.error)
|
||||
return Promise.reject(resp)
|
||||
}
|
||||
}).then(() => {
|
||||
delete this.editedMetadata[this.packName]
|
||||
deleteEmojiPack() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.deleteEmojiPack({ name: this.packName })
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp === 'ok') {
|
||||
return this.refreshPackList()
|
||||
} else {
|
||||
this.displayError(resp.error)
|
||||
return Promise.reject(resp)
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
delete this.editedMetadata[this.packName]
|
||||
|
||||
this.deleteModalVisible = false
|
||||
this.packName = ''
|
||||
})
|
||||
this.deleteModalVisible = false
|
||||
this.packName = ''
|
||||
})
|
||||
},
|
||||
|
||||
metaEdited (prop) {
|
||||
metaEdited(prop) {
|
||||
if (!this.pack) return
|
||||
|
||||
const def = this.pack.pack[prop] || ''
|
||||
const edited = this.packMeta[prop] || ''
|
||||
return edited !== def
|
||||
},
|
||||
savePackMetadata () {
|
||||
this.$store.state.api.backendInteractor.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta }).then(
|
||||
resp => resp.json()
|
||||
).then(resp => {
|
||||
if (resp.error !== undefined) {
|
||||
this.displayError(resp.error)
|
||||
return
|
||||
}
|
||||
savePackMetadata() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.saveEmojiPackMetadata({ name: this.packName, newData: this.packMeta })
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp.error !== undefined) {
|
||||
this.displayError(resp.error)
|
||||
return
|
||||
}
|
||||
|
||||
// Update actual pack data
|
||||
this.pack.pack = resp
|
||||
// Delete edited pack data, should auto-update itself
|
||||
delete this.editedMetadata[this.packName]
|
||||
})
|
||||
// Update actual pack data
|
||||
this.pack.pack = resp
|
||||
// Delete edited pack data, should auto-update itself
|
||||
delete this.editedMetadata[this.packName]
|
||||
})
|
||||
},
|
||||
|
||||
updatePackFiles (newFiles, packName) {
|
||||
updatePackFiles(newFiles, packName) {
|
||||
this.knownPacks[packName].files = newFiles
|
||||
this.sortPackFiles(packName)
|
||||
},
|
||||
|
||||
loadPacksPaginated (listFunction) {
|
||||
loadPacksPaginated(listFunction) {
|
||||
const pageSize = 25
|
||||
const allPacks = {}
|
||||
|
||||
return listFunction({ instance: this.remotePackInstance, page: 1, pageSize: 0 })
|
||||
.then(data => data.json())
|
||||
.then(data => {
|
||||
if (data.error !== undefined) { return Promise.reject(data.error) }
|
||||
return listFunction({
|
||||
instance: this.remotePackInstance,
|
||||
page: 1,
|
||||
pageSize: 0,
|
||||
})
|
||||
.then((data) => data.json())
|
||||
.then((data) => {
|
||||
if (data.error !== undefined) {
|
||||
return Promise.reject(data.error)
|
||||
}
|
||||
|
||||
let resultingPromise = Promise.resolve({})
|
||||
for (let i = 0; i < Math.ceil(data.count / pageSize); i++) {
|
||||
resultingPromise = resultingPromise.then(() => listFunction({ instance: this.remotePackInstance, page: i, pageSize })
|
||||
).then(data => data.json()).then(pageData => {
|
||||
if (pageData.error !== undefined) { return Promise.reject(pageData.error) }
|
||||
resultingPromise = resultingPromise
|
||||
.then(() =>
|
||||
listFunction({
|
||||
instance: this.remotePackInstance,
|
||||
page: i,
|
||||
pageSize,
|
||||
}),
|
||||
)
|
||||
.then((data) => data.json())
|
||||
.then((pageData) => {
|
||||
if (pageData.error !== undefined) {
|
||||
return Promise.reject(pageData.error)
|
||||
}
|
||||
|
||||
assign(allPacks, pageData.packs)
|
||||
})
|
||||
assign(allPacks, pageData.packs)
|
||||
})
|
||||
}
|
||||
|
||||
return resultingPromise
|
||||
})
|
||||
.then(() => allPacks)
|
||||
.catch(data => {
|
||||
.catch((data) => {
|
||||
this.displayError(data)
|
||||
})
|
||||
},
|
||||
|
||||
refreshPackList () {
|
||||
this.loadPacksPaginated(this.$store.state.api.backendInteractor.listEmojiPacks)
|
||||
.then(allPacks => {
|
||||
this.knownLocalPacks = allPacks
|
||||
for (const name of Object.keys(this.knownLocalPacks)) {
|
||||
this.sortPackFiles(name)
|
||||
}
|
||||
})
|
||||
refreshPackList() {
|
||||
this.loadPacksPaginated(
|
||||
this.$store.state.api.backendInteractor.listEmojiPacks,
|
||||
).then((allPacks) => {
|
||||
this.knownLocalPacks = allPacks
|
||||
for (const name of Object.keys(this.knownLocalPacks)) {
|
||||
this.sortPackFiles(name)
|
||||
}
|
||||
})
|
||||
},
|
||||
listRemotePacks () {
|
||||
this.loadPacksPaginated(this.$store.state.api.backendInteractor.listRemoteEmojiPacks)
|
||||
.then(allPacks => {
|
||||
listRemotePacks() {
|
||||
this.loadPacksPaginated(
|
||||
this.$store.state.api.backendInteractor.listRemoteEmojiPacks,
|
||||
)
|
||||
.then((allPacks) => {
|
||||
let inst = this.remotePackInstance
|
||||
if (!inst.startsWith('http')) { inst = 'https://' + inst }
|
||||
if (!inst.startsWith('http')) {
|
||||
inst = 'https://' + inst
|
||||
}
|
||||
const instUrl = new URL(inst)
|
||||
inst = instUrl.host
|
||||
|
||||
for (const packName in allPacks) {
|
||||
allPacks[packName].remote = {
|
||||
baseName: packName,
|
||||
instance: instUrl.origin
|
||||
instance: instUrl.origin,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,89 +248,101 @@ const EmojiTab = {
|
|||
this.sortPackFiles(`${pack}@${inst}`)
|
||||
}
|
||||
})
|
||||
.catch(data => {
|
||||
.catch((data) => {
|
||||
this.displayError(data)
|
||||
})
|
||||
},
|
||||
downloadRemotePack () {
|
||||
downloadRemotePack() {
|
||||
if (this.remotePackDownloadAs.trim() === '') {
|
||||
this.remotePackDownloadAs = this.pack.remote.baseName
|
||||
}
|
||||
|
||||
this.$store.state.api.backendInteractor.downloadRemoteEmojiPack({
|
||||
instance: this.pack.remote.instance, packName: this.pack.remote.baseName, as: this.remotePackDownloadAs
|
||||
})
|
||||
.then(data => data.json())
|
||||
.then(resp => {
|
||||
this.$store.state.api.backendInteractor
|
||||
.downloadRemoteEmojiPack({
|
||||
instance: this.pack.remote.instance,
|
||||
packName: this.pack.remote.baseName,
|
||||
as: this.remotePackDownloadAs,
|
||||
})
|
||||
.then((data) => data.json())
|
||||
.then((resp) => {
|
||||
if (resp === 'ok') {
|
||||
return this.refreshPackList()
|
||||
} else {
|
||||
this.displayError(resp.error)
|
||||
return Promise.reject(resp)
|
||||
}
|
||||
}).then(() => {
|
||||
})
|
||||
.then(() => {
|
||||
this.packName = this.remotePackDownloadAs
|
||||
this.remotePackDownloadAs = ''
|
||||
})
|
||||
},
|
||||
downloadRemoteURLPack () {
|
||||
this.$store.state.api.backendInteractor.downloadRemoteEmojiPackZIP({
|
||||
url: this.remotePackURL, packName: this.newPackName
|
||||
})
|
||||
.then(data => data.json())
|
||||
.then(resp => {
|
||||
downloadRemoteURLPack() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.downloadRemoteEmojiPackZIP({
|
||||
url: this.remotePackURL,
|
||||
packName: this.newPackName,
|
||||
})
|
||||
.then((data) => data.json())
|
||||
.then((resp) => {
|
||||
if (resp === 'ok') {
|
||||
return this.refreshPackList()
|
||||
} else {
|
||||
this.displayError(resp.error)
|
||||
return Promise.reject(resp)
|
||||
}
|
||||
}).then(() => {
|
||||
})
|
||||
.then(() => {
|
||||
this.packName = this.newPackName
|
||||
this.newPackName = ''
|
||||
this.remotePackURL = ''
|
||||
})
|
||||
},
|
||||
downloadRemoteFilePack () {
|
||||
this.$store.state.api.backendInteractor.downloadRemoteEmojiPackZIP({
|
||||
file: this.remotePackFile[0], packName: this.newPackName
|
||||
})
|
||||
.then(data => data.json())
|
||||
.then(resp => {
|
||||
downloadRemoteFilePack() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.downloadRemoteEmojiPackZIP({
|
||||
file: this.remotePackFile[0],
|
||||
packName: this.newPackName,
|
||||
})
|
||||
.then((data) => data.json())
|
||||
.then((resp) => {
|
||||
if (resp === 'ok') {
|
||||
return this.refreshPackList()
|
||||
} else {
|
||||
this.displayError(resp.error)
|
||||
return Promise.reject(resp)
|
||||
}
|
||||
}).then(() => {
|
||||
})
|
||||
.then(() => {
|
||||
this.packName = this.newPackName
|
||||
this.newPackName = ''
|
||||
this.remotePackURL = ''
|
||||
})
|
||||
},
|
||||
|
||||
displayError (msg) {
|
||||
displayError(msg) {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'admin_dash.emoji.error',
|
||||
messageArgs: [msg],
|
||||
level: 'error'
|
||||
level: 'error',
|
||||
})
|
||||
},
|
||||
sortPackFiles (nameOfPack) {
|
||||
sortPackFiles(nameOfPack) {
|
||||
// Sort by key
|
||||
const sorted = Object.keys(this.knownPacks[nameOfPack].files).sort().reduce((acc, key) => {
|
||||
if (key.length === 0) return acc
|
||||
acc[key] = this.knownPacks[nameOfPack].files[key]
|
||||
return acc
|
||||
}, {})
|
||||
const sorted = Object.keys(this.knownPacks[nameOfPack].files)
|
||||
.sort()
|
||||
.reduce((acc, key) => {
|
||||
if (key.length === 0) return acc
|
||||
acc[key] = this.knownPacks[nameOfPack].files[key]
|
||||
return acc
|
||||
}, {})
|
||||
this.knownPacks[nameOfPack].files = sorted
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted () {
|
||||
mounted() {
|
||||
this.refreshPackList()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default EmojiTab
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import MapSetting from '../helpers/map_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const FederationTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -24,11 +24,11 @@ const FederationTab = {
|
|||
ListSetting,
|
||||
ListTupleSetting,
|
||||
GroupSetting,
|
||||
MapSetting
|
||||
MapSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default FederationTab
|
||||
|
|
|
|||
|
|
@ -9,24 +9,20 @@ import { useInterfaceStore } from 'src/stores/interface'
|
|||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { faGlobe } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe
|
||||
)
|
||||
library.add(faGlobe)
|
||||
|
||||
const FrontendsTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
working: false
|
||||
working: false,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -36,26 +32,26 @@ const FrontendsTab = {
|
|||
StringSetting,
|
||||
GroupSetting,
|
||||
PanelLoading,
|
||||
Popover
|
||||
Popover,
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
if (this.user.rights.admin) {
|
||||
this.$store.dispatch('loadFrontendsStuff')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
frontends () {
|
||||
frontends() {
|
||||
return this.$store.state.adminSettings.frontends
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
canInstall (frontend) {
|
||||
const fe = this.frontends.find(f => f.name === frontend.name)
|
||||
canInstall(frontend) {
|
||||
const fe = this.frontends.find((f) => f.name === frontend.name)
|
||||
if (!fe) return false
|
||||
return fe.refs.includes(frontend.ref)
|
||||
},
|
||||
getSuggestedRef (frontend) {
|
||||
getSuggestedRef(frontend) {
|
||||
if (this.adminDraft) {
|
||||
const defaultFe = this.adminDraft[':pleroma'][':frontends'][':primary']
|
||||
if (defaultFe?.name === frontend.name && this.canInstall(defaultFe)) {
|
||||
|
|
@ -67,13 +63,14 @@ const FrontendsTab = {
|
|||
return frontend.refs[0]
|
||||
}
|
||||
},
|
||||
update (frontend, suggestRef) {
|
||||
update(frontend, suggestRef) {
|
||||
const ref = suggestRef || this.getSuggestedRef(frontend)
|
||||
const { name } = frontend
|
||||
const payload = { name, ref }
|
||||
|
||||
this.working = true
|
||||
this.$store.state.api.backendInteractor.installFrontend({ payload })
|
||||
this.$store.state.api.backendInteractor
|
||||
.installFrontend({ payload })
|
||||
.finally(() => {
|
||||
this.working = false
|
||||
})
|
||||
|
|
@ -86,29 +83,32 @@ const FrontendsTab = {
|
|||
messageKey: 'admin_dash.frontend.failure_installing_frontend',
|
||||
messageArgs: {
|
||||
version: name + '/' + ref,
|
||||
reason: reason.error
|
||||
reason: reason.error,
|
||||
},
|
||||
timeout: 5000
|
||||
timeout: 5000,
|
||||
})
|
||||
} else {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'success',
|
||||
messageKey: 'admin_dash.frontend.success_installing_frontend',
|
||||
messageArgs: {
|
||||
version: name + '/' + ref
|
||||
version: name + '/' + ref,
|
||||
},
|
||||
timeout: 2000
|
||||
timeout: 2000,
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
setDefault (frontend, suggestRef) {
|
||||
setDefault(frontend, suggestRef) {
|
||||
const ref = suggestRef || this.getSuggestedRef(frontend)
|
||||
const { name } = frontend
|
||||
|
||||
this.$store.commit('updateAdminDraft', { path: [':pleroma', ':frontends', ':primary'], value: { name, ref } })
|
||||
}
|
||||
}
|
||||
this.$store.commit('updateAdminDraft', {
|
||||
path: [':pleroma', ':frontends', ':primary'],
|
||||
value: { name, ref },
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default FrontendsTab
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
|||
import { get } from 'lodash'
|
||||
|
||||
const HTTPTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -29,18 +29,23 @@ const HTTPTab = {
|
|||
GroupSetting,
|
||||
ListSetting,
|
||||
TupleSetting,
|
||||
ProxySetting
|
||||
ProxySetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
sslOptions () {
|
||||
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:http.:adapter.:ssl_options.:versions')
|
||||
return new Set(desc.suggestions.map(option => ({
|
||||
label: option.replace(':tlsv', 'TLS v'),
|
||||
value: option
|
||||
})))
|
||||
sslOptions() {
|
||||
const desc = get(
|
||||
this.$store.state.adminSettings.descriptions,
|
||||
':pleroma.:http.:adapter.:ssl_options.:versions',
|
||||
)
|
||||
return new Set(
|
||||
desc.suggestions.map((option) => ({
|
||||
label: option.replace(':tlsv', 'TLS v'),
|
||||
value: option,
|
||||
})),
|
||||
)
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default HTTPTab
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
|||
import { get } from 'lodash'
|
||||
|
||||
const InstanceTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -29,25 +29,40 @@ const InstanceTab = {
|
|||
ListSetting,
|
||||
PWAManifestIconsSetting,
|
||||
MapSetting,
|
||||
GroupSetting
|
||||
GroupSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
providersOptions () {
|
||||
const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Web.Metadata', ':providers'])
|
||||
return new Set(desc.suggestions.map(option => ({
|
||||
label: option.replace('Pleroma.Web.Metadata.Providers.', ''),
|
||||
value: option
|
||||
})))
|
||||
providersOptions() {
|
||||
const desc = get(this.$store.state.adminSettings.descriptions, [
|
||||
':pleroma',
|
||||
'Pleroma.Web.Metadata',
|
||||
':providers',
|
||||
])
|
||||
return new Set(
|
||||
desc.suggestions.map((option) => ({
|
||||
label: option.replace('Pleroma.Web.Metadata.Providers.', ''),
|
||||
value: option,
|
||||
})),
|
||||
)
|
||||
},
|
||||
limitLocalContentOptions () {
|
||||
const desc = get(this.$store.state.adminSettings.descriptions, [':pleroma', ':instance', ':limit_to_local_content'])
|
||||
return new Set(desc.suggestions.map(option => ({
|
||||
label: option !== 'false' ? this.$t('admin_dash.instance.' + option) : this.$t('general.no'),
|
||||
value: option
|
||||
})))
|
||||
}
|
||||
}
|
||||
limitLocalContentOptions() {
|
||||
const desc = get(this.$store.state.adminSettings.descriptions, [
|
||||
':pleroma',
|
||||
':instance',
|
||||
':limit_to_local_content',
|
||||
])
|
||||
return new Set(
|
||||
desc.suggestions.map((option) => ({
|
||||
label:
|
||||
option !== 'false'
|
||||
? this.$t('admin_dash.instance.' + option)
|
||||
: this.$t('general.no'),
|
||||
value: option,
|
||||
})),
|
||||
)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default InstanceTab
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import ListSetting from '../helpers/list_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const JobQueuesTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -24,11 +24,11 @@ const JobQueuesTab = {
|
|||
TupleSetting,
|
||||
AttachmentSetting,
|
||||
GroupSetting,
|
||||
ListSetting
|
||||
ListSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default JobQueuesTab
|
||||
|
|
|
|||
|
|
@ -10,11 +10,11 @@ const LimitsTab = {
|
|||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
StringSetting
|
||||
StringSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default LimitsTab
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
|||
import { get } from 'lodash'
|
||||
|
||||
const LinksTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -26,84 +26,109 @@ const LinksTab = {
|
|||
AttachmentSetting,
|
||||
GroupSetting,
|
||||
ListSetting,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
computed: {
|
||||
classIsPresent () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':class'] !== false
|
||||
classIsPresent() {
|
||||
return (
|
||||
this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][
|
||||
':class'
|
||||
] !== false
|
||||
)
|
||||
},
|
||||
relIsPresent () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':rel'] !== false
|
||||
relIsPresent() {
|
||||
return (
|
||||
this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][
|
||||
':rel'
|
||||
] !== false
|
||||
)
|
||||
},
|
||||
truncateIsPresent () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][':truncate'] !== false
|
||||
truncateIsPresent() {
|
||||
return (
|
||||
this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Formatter'][
|
||||
':truncate'
|
||||
] !== false
|
||||
)
|
||||
},
|
||||
truncateDescription () {
|
||||
return get(this.$store.state.adminSettings.descriptions, [':pleroma', 'Pleroma.Formatter', ':truncate'])
|
||||
truncateDescription() {
|
||||
return get(this.$store.state.adminSettings.descriptions, [
|
||||
':pleroma',
|
||||
'Pleroma.Formatter',
|
||||
':truncate',
|
||||
])
|
||||
},
|
||||
ttlSettersOptions () {
|
||||
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:ttl_setters')
|
||||
return new Set(desc.suggestions.map(option => ({
|
||||
label: option.replace('Pleroma.Web.RichMedia.Parser.TTL.', ''),
|
||||
value: option
|
||||
})))
|
||||
ttlSettersOptions() {
|
||||
const desc = get(
|
||||
this.$store.state.adminSettings.descriptions,
|
||||
':pleroma.:rich_media.:ttl_setters',
|
||||
)
|
||||
return new Set(
|
||||
desc.suggestions.map((option) => ({
|
||||
label: option.replace('Pleroma.Web.RichMedia.Parser.TTL.', ''),
|
||||
value: option,
|
||||
})),
|
||||
)
|
||||
},
|
||||
parsersOptions () {
|
||||
const desc = get(this.$store.state.adminSettings.descriptions, ':pleroma.:rich_media.:parsers')
|
||||
return new Set(desc.suggestions.map(option => ({
|
||||
label: option.replace('Pleroma.Web.RichMedia.Parsers.', ''),
|
||||
value: option
|
||||
})))
|
||||
parsersOptions() {
|
||||
const desc = get(
|
||||
this.$store.state.adminSettings.descriptions,
|
||||
':pleroma.:rich_media.:parsers',
|
||||
)
|
||||
return new Set(
|
||||
desc.suggestions.map((option) => ({
|
||||
label: option.replace('Pleroma.Web.RichMedia.Parsers.', ''),
|
||||
value: option,
|
||||
})),
|
||||
)
|
||||
},
|
||||
validateTLDOptions () {
|
||||
return [{
|
||||
label: this.$t('general.yes'),
|
||||
value: true
|
||||
}, {
|
||||
label: this.$t('general.no'),
|
||||
value: false
|
||||
}, {
|
||||
label: this.$t('admin_dash.links.no_scheme'),
|
||||
value: ':no_scheme'
|
||||
}]
|
||||
validateTLDOptions() {
|
||||
return [
|
||||
{
|
||||
label: this.$t('general.yes'),
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
label: this.$t('general.no'),
|
||||
value: false,
|
||||
},
|
||||
{
|
||||
label: this.$t('admin_dash.links.no_scheme'),
|
||||
value: ':no_scheme',
|
||||
},
|
||||
]
|
||||
},
|
||||
mediaProxyEnabled () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled']
|
||||
mediaProxyEnabled() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][
|
||||
':enabled'
|
||||
]
|
||||
},
|
||||
mediaInvalidationProvider () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider']
|
||||
mediaInvalidationProvider() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][
|
||||
':invalidation'
|
||||
][':provider']
|
||||
},
|
||||
...SharedComputedObject()
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
checkRel (e) {
|
||||
this.$store.commit(
|
||||
'updateAdminDraft',
|
||||
{
|
||||
path: [':pleroma','Pleroma.Formatter',':rel'],
|
||||
value: e ? '' : false
|
||||
}
|
||||
)
|
||||
checkRel(e) {
|
||||
this.$store.commit('updateAdminDraft', {
|
||||
path: [':pleroma', 'Pleroma.Formatter', ':rel'],
|
||||
value: e ? '' : false,
|
||||
})
|
||||
},
|
||||
checkClass (e) {
|
||||
this.$store.commit(
|
||||
'updateAdminDraft',
|
||||
{
|
||||
path: [':pleroma','Pleroma.Formatter',':class'],
|
||||
value: e ? '' : false
|
||||
}
|
||||
)
|
||||
checkClass(e) {
|
||||
this.$store.commit('updateAdminDraft', {
|
||||
path: [':pleroma', 'Pleroma.Formatter', ':class'],
|
||||
value: e ? '' : false,
|
||||
})
|
||||
},
|
||||
checkTruncate (e) {
|
||||
this.$store.commit(
|
||||
'updateAdminDraft',
|
||||
{
|
||||
path: [':pleroma','Pleroma.Formatter',':truncate'],
|
||||
value: e ? 20 : false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
checkTruncate(e) {
|
||||
this.$store.commit('updateAdminDraft', {
|
||||
path: [':pleroma', 'Pleroma.Formatter', ':truncate'],
|
||||
value: e ? 20 : false,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default LinksTab
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import AttachmentSetting from '../helpers/attachment_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const MailerTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -22,44 +22,50 @@ const MailerTab = {
|
|||
StringSetting,
|
||||
AttachmentSetting,
|
||||
ColorSetting,
|
||||
GroupSetting
|
||||
GroupSetting,
|
||||
},
|
||||
computed: {
|
||||
adaptersLabels () {
|
||||
adaptersLabels() {
|
||||
const prefix = 'Swoosh.Adapters.'
|
||||
const descriptions = this.$store.state.adminSettings.descriptions
|
||||
const options = descriptions[':pleroma']['Pleroma.Emails.Mailer'][':adapter'].suggestions
|
||||
const options =
|
||||
descriptions[':pleroma']['Pleroma.Emails.Mailer'][':adapter']
|
||||
.suggestions
|
||||
|
||||
return Object.fromEntries(options.map(value => [
|
||||
value, value.replace(prefix, '')
|
||||
]))
|
||||
return Object.fromEntries(
|
||||
options.map((value) => [value, value.replace(prefix, '')]),
|
||||
)
|
||||
},
|
||||
startTLSLabels () {
|
||||
startTLSLabels() {
|
||||
return {
|
||||
':always': this.$t('admin_dash.generic_enforcement.always'),
|
||||
':if_available': this.$t('admin_dash.generic_enforcement.if_available'),
|
||||
':never': this.$t('admin_dash.generic_enforcement.never')
|
||||
':never': this.$t('admin_dash.generic_enforcement.never'),
|
||||
}
|
||||
// return Object.fromEntries(options.map(value => [
|
||||
// value, value.replace(prefix, '')
|
||||
// ]))
|
||||
},
|
||||
adapter () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':adapter']
|
||||
adapter() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][
|
||||
'Pleroma.Emails.Mailer'
|
||||
][':adapter']
|
||||
},
|
||||
mailerEnabled () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Emails.Mailer'][':enabled']
|
||||
mailerEnabled() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][
|
||||
'Pleroma.Emails.Mailer'
|
||||
][':enabled']
|
||||
},
|
||||
...SharedComputedObject()
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
adapterHasKey (key) {
|
||||
adapterHasKey(key) {
|
||||
const descriptions = this.$store.state.adminSettings.descriptions
|
||||
const mailerStuff = descriptions[':pleroma']['Pleroma.Emails.Mailer']
|
||||
const adapterStuff = mailerStuff[':subgroup,' + this.adapter]
|
||||
return Object.hasOwn(adapterStuff, key)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default MailerTab
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import ListSetting from '../helpers/list_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const MediaProxyTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -22,17 +22,21 @@ const MediaProxyTab = {
|
|||
StringSetting,
|
||||
AttachmentSetting,
|
||||
GroupSetting,
|
||||
ListSetting
|
||||
ListSetting,
|
||||
},
|
||||
computed: {
|
||||
mediaProxyEnabled () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':enabled']
|
||||
mediaProxyEnabled() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][
|
||||
':enabled'
|
||||
]
|
||||
},
|
||||
mediaInvalidationProvider () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][':invalidation'][':provider']
|
||||
mediaInvalidationProvider() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][':media_proxy'][
|
||||
':invalidation'
|
||||
][':provider']
|
||||
},
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default MediaProxyTab
|
||||
|
|
|
|||
|
|
@ -8,19 +8,15 @@ import ListSetting from '../helpers/list_setting.vue'
|
|||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { faGlobe } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe
|
||||
)
|
||||
library.add(faGlobe)
|
||||
|
||||
const MonitoringTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -30,13 +26,12 @@ const MonitoringTab = {
|
|||
StringSetting,
|
||||
AttachmentSetting,
|
||||
GroupSetting,
|
||||
ListSetting
|
||||
ListSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
}
|
||||
methods: {},
|
||||
}
|
||||
|
||||
export default MonitoringTab
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import MapSetting from '../helpers/map_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const OtherTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -28,11 +28,11 @@ const OtherTab = {
|
|||
ListSetting,
|
||||
PWAManifestIconsSetting,
|
||||
MapSetting,
|
||||
GroupSetting
|
||||
GroupSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default OtherTab
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ import MapSetting from '../helpers/map_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const PostsTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -28,11 +28,11 @@ const PostsTab = {
|
|||
ListSetting,
|
||||
PWAManifestIconsSetting,
|
||||
MapSetting,
|
||||
GroupSetting
|
||||
GroupSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default PostsTab
|
||||
|
|
|
|||
|
|
@ -3,18 +3,18 @@ import RateSetting from '../helpers/rate_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const RatesTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
RateSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default RatesTab
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import ListSetting from '../helpers/list_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const RegistrationsTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -24,11 +24,11 @@ const RegistrationsTab = {
|
|||
TupleSetting,
|
||||
AttachmentSetting,
|
||||
GroupSetting,
|
||||
ListSetting
|
||||
ListSetting,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default RegistrationsTab
|
||||
|
|
|
|||
|
|
@ -6,41 +6,47 @@ import StringSetting from '../helpers/string_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const UploadsTab = {
|
||||
provide () {
|
||||
provide() {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
defaultSource: 'admin',
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
uploaders: [{
|
||||
key: 'Pleroma.Uploaders.Local',
|
||||
value: 'Pleroma.Uploaders.Local',
|
||||
label: this.$t('admin_dash.uploads.local_uploader')
|
||||
}, {
|
||||
key: 'Pleroma.Uploaders.IPFS',
|
||||
value: 'Pleroma.Uploaders.IPFS',
|
||||
label: 'IPFS'
|
||||
}, {
|
||||
key: 'Pleroma.Uploaders.S3',
|
||||
value: 'Pleroma.Uploaders.S3',
|
||||
label: 'S3'
|
||||
}]
|
||||
uploaders: [
|
||||
{
|
||||
key: 'Pleroma.Uploaders.Local',
|
||||
value: 'Pleroma.Uploaders.Local',
|
||||
label: this.$t('admin_dash.uploads.local_uploader'),
|
||||
},
|
||||
{
|
||||
key: 'Pleroma.Uploaders.IPFS',
|
||||
value: 'Pleroma.Uploaders.IPFS',
|
||||
label: 'IPFS',
|
||||
},
|
||||
{
|
||||
key: 'Pleroma.Uploaders.S3',
|
||||
value: 'Pleroma.Uploaders.S3',
|
||||
label: 'S3',
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
StringSetting
|
||||
StringSetting,
|
||||
},
|
||||
computed: {
|
||||
uploader () {
|
||||
return this.$store.state.adminSettings.draft[':pleroma']['Pleroma.Upload'][':uploader']
|
||||
uploader() {
|
||||
return this.$store.state.adminSettings.draft[':pleroma'][
|
||||
'Pleroma.Upload'
|
||||
][':uploader']
|
||||
},
|
||||
...SharedComputedObject()
|
||||
}
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
}
|
||||
|
||||
export default UploadsTab
|
||||
|
|
|
|||
|
|
@ -11,34 +11,36 @@ export default {
|
|||
acceptTypes: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'image/*'
|
||||
}
|
||||
default: 'image/*',
|
||||
},
|
||||
},
|
||||
components: {
|
||||
...Setting.components,
|
||||
MediaUpload,
|
||||
Attachment
|
||||
Attachment,
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
attachment () {
|
||||
attachment() {
|
||||
const path = this.realDraftMode ? this.draft : this.state
|
||||
// The "server" part is primarily for local dev, but could be useful for alt-domain or multiuser usage.
|
||||
const url = path.includes('://') ? path : this.$store.state.instance.server + path
|
||||
const url = path.includes('://')
|
||||
? path
|
||||
: this.$store.state.instance.server + path
|
||||
return {
|
||||
mimetype: fileTypeExt(url),
|
||||
url
|
||||
url,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
setMediaFile (fileInfo) {
|
||||
setMediaFile(fileInfo) {
|
||||
if (this.realDraftMode) {
|
||||
this.draft = fileInfo.url
|
||||
} else {
|
||||
this.configSink(this.path, fileInfo.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,27 +5,27 @@ export default {
|
|||
...Setting,
|
||||
props: {
|
||||
...Setting.props,
|
||||
indeterminateState: [String, Object]
|
||||
indeterminateState: [String, Object],
|
||||
},
|
||||
components: {
|
||||
...Setting.components,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
isIndeterminate () {
|
||||
isIndeterminate() {
|
||||
return this.visibleState === this.indeterminateState
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue (e) {
|
||||
getValue(e) {
|
||||
// Basic tri-state toggle implementation
|
||||
if (!!this.indeterminateState && !e && this.visibleState === true) {
|
||||
// If we have indeterminate state, switching from true to false first goes through indeterminate
|
||||
return this.indeterminateState
|
||||
}
|
||||
return e
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,50 +5,50 @@ export default {
|
|||
...Setting,
|
||||
components: {
|
||||
...Setting.components,
|
||||
Select
|
||||
Select,
|
||||
},
|
||||
props: {
|
||||
...Setting.props,
|
||||
overrideOptions: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
optionLabelMap: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {}
|
||||
}
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
realOptions () {
|
||||
realOptions() {
|
||||
if (this.overrideOptions) {
|
||||
return this.options
|
||||
}
|
||||
if (this.realSource === 'admin') {
|
||||
if (
|
||||
!this.backendDescriptionSuggestions?.length ||
|
||||
this.backendDescriptionSuggestions?.length === 0
|
||||
this.backendDescriptionSuggestions?.length === 0
|
||||
) {
|
||||
return this.options
|
||||
}
|
||||
return this.backendDescriptionSuggestions.map(x => ({
|
||||
return this.backendDescriptionSuggestions.map((x) => ({
|
||||
key: x,
|
||||
value: x,
|
||||
label: this.optionLabelMap[x] || x
|
||||
label: this.optionLabelMap[x] || x,
|
||||
}))
|
||||
}
|
||||
return this.options
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue (e) {
|
||||
getValue(e) {
|
||||
return e
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ export default {
|
|||
...Setting,
|
||||
components: {
|
||||
...Setting.components,
|
||||
ColorInput
|
||||
ColorInput,
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue (e) {
|
||||
getValue(e) {
|
||||
return e
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,13 +61,11 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faWrench
|
||||
)
|
||||
library.add(faWrench)
|
||||
|
||||
export default {
|
||||
components: { Popover },
|
||||
props: ['changed']
|
||||
props: ['changed'],
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -160,53 +160,53 @@ export default {
|
|||
props: {
|
||||
placement: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
|
||||
newUpload: Boolean,
|
||||
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
packName: {
|
||||
type: String,
|
||||
required: true
|
||||
required: true,
|
||||
},
|
||||
shortcode: {
|
||||
type: String,
|
||||
// Only exists when this is not a new upload
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
file: {
|
||||
type: String,
|
||||
// Only exists when this is not a new upload
|
||||
default: ''
|
||||
default: '',
|
||||
},
|
||||
|
||||
// Only exists for emojis from remote packs
|
||||
remote: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
default: undefined,
|
||||
},
|
||||
knownLocalPacks: {
|
||||
type: Object,
|
||||
default: undefined
|
||||
}
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: ['updatePackFiles', 'displayError'],
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
uploadFile: [],
|
||||
uploadURL: '',
|
||||
editedShortcode: this.shortcode,
|
||||
editedFile: this.file,
|
||||
deleteModalVisible: false,
|
||||
copyToPack: ''
|
||||
copyToPack: '',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
emojiPreview () {
|
||||
emojiPreview() {
|
||||
if (this.newUpload && this.uploadFile.length > 0) {
|
||||
return URL.createObjectURL(this.uploadFile[0])
|
||||
} else if (this.newUpload && this.uploadURL !== '') {
|
||||
|
|
@ -217,73 +217,92 @@ export default {
|
|||
|
||||
return null
|
||||
},
|
||||
isEdited () {
|
||||
return !this.newUpload && (this.editedShortcode !== this.shortcode || this.editedFile !== this.file)
|
||||
isEdited() {
|
||||
return (
|
||||
!this.newUpload &&
|
||||
(this.editedShortcode !== this.shortcode ||
|
||||
this.editedFile !== this.file)
|
||||
)
|
||||
},
|
||||
saveButtonDisabled() {
|
||||
if (this.remote === undefined)
|
||||
return this.newUpload ? (this.uploadURL === "" && this.uploadFile.length == 0) : !this.isEdited
|
||||
else
|
||||
return this.copyToPack === ""
|
||||
}
|
||||
return this.newUpload
|
||||
? this.uploadURL === '' && this.uploadFile.length == 0
|
||||
: !this.isEdited
|
||||
else return this.copyToPack === ''
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
saveEditedEmoji () {
|
||||
saveEditedEmoji() {
|
||||
if (!this.isEdited) return
|
||||
|
||||
this.$store.state.api.backendInteractor.updateEmojiFile(
|
||||
{ packName: this.packName, shortcode: this.shortcode, newShortcode: this.editedShortcode, newFilename: this.editedFile, force: false }
|
||||
).then(resp => {
|
||||
if (resp.error !== undefined) {
|
||||
this.$emit('displayError', resp.error)
|
||||
return Promise.reject(resp.error)
|
||||
}
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateEmojiFile({
|
||||
packName: this.packName,
|
||||
shortcode: this.shortcode,
|
||||
newShortcode: this.editedShortcode,
|
||||
newFilename: this.editedFile,
|
||||
force: false,
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.error !== undefined) {
|
||||
this.$emit('displayError', resp.error)
|
||||
return Promise.reject(resp.error)
|
||||
}
|
||||
|
||||
return resp.json()
|
||||
}).then(resp => this.$emit('updatePackFiles', resp))
|
||||
return resp.json()
|
||||
})
|
||||
.then((resp) => this.$emit('updatePackFiles', resp))
|
||||
},
|
||||
uploadEmoji () {
|
||||
uploadEmoji() {
|
||||
let packName = this.remote === undefined ? this.packName : this.copyToPack
|
||||
this.$store.state.api.backendInteractor.addNewEmojiFile({
|
||||
packName: packName,
|
||||
file: this.remote === undefined
|
||||
? (this.uploadURL !== "" ? this.uploadURL : this.uploadFile[0])
|
||||
: this.emojiAddr(this.file),
|
||||
shortcode: this.editedShortcode,
|
||||
filename: this.editedFile
|
||||
}).then(resp => resp.json()).then(resp => {
|
||||
if (resp.error !== undefined) {
|
||||
this.$emit('displayError', resp.error)
|
||||
return
|
||||
}
|
||||
this.$store.state.api.backendInteractor
|
||||
.addNewEmojiFile({
|
||||
packName: packName,
|
||||
file:
|
||||
this.remote === undefined
|
||||
? this.uploadURL !== ''
|
||||
? this.uploadURL
|
||||
: this.uploadFile[0]
|
||||
: this.emojiAddr(this.file),
|
||||
shortcode: this.editedShortcode,
|
||||
filename: this.editedFile,
|
||||
})
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp.error !== undefined) {
|
||||
this.$emit('displayError', resp.error)
|
||||
return
|
||||
}
|
||||
|
||||
this.$emit('updatePackFiles', resp, packName)
|
||||
this.$refs.emojiPopover.hidePopover()
|
||||
this.$emit('updatePackFiles', resp, packName)
|
||||
this.$refs.emojiPopover.hidePopover()
|
||||
|
||||
this.editedFile = ''
|
||||
this.editedShortcode = ''
|
||||
this.uploadFile = []
|
||||
})
|
||||
this.editedFile = ''
|
||||
this.editedShortcode = ''
|
||||
this.uploadFile = []
|
||||
})
|
||||
},
|
||||
revertEmoji () {
|
||||
revertEmoji() {
|
||||
this.editedFile = this.file
|
||||
this.editedShortcode = this.shortcode
|
||||
},
|
||||
deleteEmoji () {
|
||||
deleteEmoji() {
|
||||
this.deleteModalVisible = false
|
||||
|
||||
this.$store.state.api.backendInteractor.deleteEmojiFile(
|
||||
{ packName: this.packName, shortcode: this.shortcode }
|
||||
).then(resp => resp.json()).then(resp => {
|
||||
if (resp.error !== undefined) {
|
||||
this.$emit('displayError', resp.error)
|
||||
return
|
||||
}
|
||||
this.$store.state.api.backendInteractor
|
||||
.deleteEmojiFile({ packName: this.packName, shortcode: this.shortcode })
|
||||
.then((resp) => resp.json())
|
||||
.then((resp) => {
|
||||
if (resp.error !== undefined) {
|
||||
this.$emit('displayError', resp.error)
|
||||
return
|
||||
}
|
||||
|
||||
this.$emit('updatePackFiles', resp, this.packName)
|
||||
})
|
||||
}
|
||||
}
|
||||
this.$emit('updatePackFiles', resp, this.packName)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
import NumberSetting from './number_setting.vue'
|
||||
export default {
|
||||
components: {
|
||||
NumberSetting
|
||||
}
|
||||
NumberSetting,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -22,12 +22,10 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faCircleQuestion } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faCircleQuestion
|
||||
)
|
||||
library.add(faCircleQuestion)
|
||||
|
||||
export default {
|
||||
components: { Popover }
|
||||
components: { Popover },
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
import NumberSetting from './number_setting.vue'
|
||||
export default {
|
||||
components: {
|
||||
NumberSetting
|
||||
}
|
||||
NumberSetting,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -3,43 +3,43 @@ import Setting from './setting.js'
|
|||
|
||||
export default {
|
||||
...Setting,
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
newValue: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
...Setting.components,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
props: {
|
||||
...Setting.props,
|
||||
ignoreSuggestions: {
|
||||
required: false,
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
overrideAvailableOptions: {
|
||||
required: false,
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
options: {
|
||||
required: false,
|
||||
type: Set
|
||||
type: Set,
|
||||
},
|
||||
allowNew: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: true,
|
||||
},
|
||||
forceNew: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
showNew () {
|
||||
showNew() {
|
||||
if (this.forceNew) return true
|
||||
if (!this.allowNew) return false
|
||||
|
||||
|
|
@ -52,10 +52,10 @@ export default {
|
|||
return true
|
||||
}
|
||||
},
|
||||
valueSet () {
|
||||
valueSet() {
|
||||
return new Set(this.visibleState)
|
||||
},
|
||||
suggestionsSet () {
|
||||
suggestionsSet() {
|
||||
const suggestions = this.backendDescriptionSuggestions
|
||||
if (suggestions) {
|
||||
return new Set(suggestions)
|
||||
|
|
@ -63,14 +63,14 @@ export default {
|
|||
return new Set()
|
||||
}
|
||||
},
|
||||
extraEntries () {
|
||||
extraEntries() {
|
||||
if (this.ignoreSuggestions) return [...this.valueSet.values()]
|
||||
if (!this.suggestionsSet) return []
|
||||
return [...this.valueSet.values()].filter((x) => {
|
||||
return !this.builtinEntriesValueSet.has(x)
|
||||
})
|
||||
},
|
||||
builtinEntries () {
|
||||
builtinEntries() {
|
||||
if (this.ignoreSuggestions) return []
|
||||
if (this.overrideAvailableOptions) {
|
||||
return [...this.options]
|
||||
|
|
@ -80,19 +80,19 @@ export default {
|
|||
const builtins = [...this.suggestionsSet.values()]
|
||||
return builtins.map((option) => ({
|
||||
label: option,
|
||||
value: option
|
||||
value: option,
|
||||
}))
|
||||
},
|
||||
builtinEntriesValueSet () {
|
||||
return new Set(this.builtinEntries.map(x => x.value))
|
||||
}
|
||||
builtinEntriesValueSet() {
|
||||
return new Set(this.builtinEntries.map((x) => x.value))
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
optionPresent (option) {
|
||||
optionPresent(option) {
|
||||
return this.valueSet.has(option)
|
||||
},
|
||||
getValue ({ event, value, index, eventType }) {
|
||||
getValue({ event, value, index, eventType }) {
|
||||
switch (eventType) {
|
||||
case 'toggle': {
|
||||
this.newValue = ''
|
||||
|
|
@ -128,6 +128,6 @@ export default {
|
|||
return [...pre, string, ...post]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ import ListSetting from './list_setting.js'
|
|||
|
||||
export default {
|
||||
...ListSetting,
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
newValue: ['','']
|
||||
newValue: ['', ''],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...ListSetting.methods,
|
||||
getValue ({ event, index, eventType, tuple }) {
|
||||
getValue({ event, index, eventType, tuple }) {
|
||||
switch (eventType) {
|
||||
case 'add': {
|
||||
if (!this.newValue[0] || !this.newValue[1]) return this.visibleState
|
||||
|
|
@ -39,6 +39,6 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,28 +7,31 @@ export default {
|
|||
allowNew: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
newValue: ['',''] // avoiding extra complexity by just using an array instead of an object
|
||||
newValue: ['', ''], // avoiding extra complexity by just using an array instead of an object
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
// state that we'll show in the UI, i.e. transforming map into list
|
||||
displayState () {
|
||||
displayState() {
|
||||
return Object.entries(this.visibleState)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue ({ event, key, eventType, isKey }) {
|
||||
getValue({ event, key, eventType, isKey }) {
|
||||
switch (eventType) {
|
||||
case 'add': {
|
||||
if (key === '') return this.visibleState
|
||||
const res = {...this.visibleState, ...Object.fromEntries([this.newValue])}
|
||||
const res = {
|
||||
...this.visibleState,
|
||||
...Object.fromEntries([this.newValue]),
|
||||
}
|
||||
this.newValue = ['', '']
|
||||
return res
|
||||
}
|
||||
|
|
@ -36,35 +39,35 @@ export default {
|
|||
case 'remove': {
|
||||
// initial state for this type is empty array
|
||||
if (Array.isArray(this.visibleState)) return this.visibleState
|
||||
const newEntries = Object.entries(this.visibleState).filter(([k]) => k !== key)
|
||||
const newEntries = Object.entries(this.visibleState).filter(
|
||||
([k]) => k !== key,
|
||||
)
|
||||
|
||||
if (newEntries.length === 0 ) return []
|
||||
if (newEntries.length === 0) return []
|
||||
return Object.fromEntries(newEntries)
|
||||
}
|
||||
|
||||
case 'edit': {
|
||||
const string = event.target.value
|
||||
const newEntries = Object
|
||||
.entries(this.visibleState)
|
||||
.map(([k, v]) => {
|
||||
if (isKey) {
|
||||
if (k === key) {
|
||||
return [string, v]
|
||||
} else {
|
||||
return [k, v]
|
||||
}
|
||||
const newEntries = Object.entries(this.visibleState).map(([k, v]) => {
|
||||
if (isKey) {
|
||||
if (k === key) {
|
||||
return [string, v]
|
||||
} else {
|
||||
if (k === key) {
|
||||
return [k, string]
|
||||
} else {
|
||||
return [k, v]
|
||||
}
|
||||
return [k, v]
|
||||
}
|
||||
})
|
||||
} else {
|
||||
if (k === key) {
|
||||
return [k, string]
|
||||
} else {
|
||||
return [k, v]
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return Object.fromEntries(newEntries)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,9 +27,7 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faWrench
|
||||
)
|
||||
library.add(faWrench)
|
||||
|
||||
export default {
|
||||
components: { Popover },
|
||||
|
|
@ -37,9 +35,9 @@ export default {
|
|||
changed: Boolean,
|
||||
messageKey: {
|
||||
type: String,
|
||||
default: 'settings.setting_changed'
|
||||
}
|
||||
}
|
||||
default: 'settings.setting_changed',
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -7,33 +7,33 @@ export default {
|
|||
min: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
default: 1,
|
||||
},
|
||||
max: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
default: 1,
|
||||
},
|
||||
step: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
default: 1,
|
||||
},
|
||||
truncate: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
}
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue (e) {
|
||||
getValue(e) {
|
||||
if (!this.truncate === 1) {
|
||||
return parseInt(e.target.value)
|
||||
} else if (this.truncate > 1) {
|
||||
return Math.trunc(e.target.value / this.truncate) * this.truncate
|
||||
}
|
||||
return parseFloat(e.target.value)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,13 +27,11 @@ import Popover from 'src/components/popover/popover.vue'
|
|||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faServer } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faServer
|
||||
)
|
||||
library.add(faServer)
|
||||
|
||||
export default {
|
||||
components: { Popover },
|
||||
props: ['isProfile']
|
||||
props: ['isProfile'],
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,18 +1,19 @@
|
|||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import Setting from './setting.js'
|
||||
|
||||
const getUrl = state => state?.tuple ? state.tuple[1] + ':' + state.tuple[2] : state
|
||||
const getSocks = state => state?.tuple
|
||||
const getUrl = (state) =>
|
||||
state?.tuple ? state.tuple[1] + ':' + state.tuple[2] : state
|
||||
const getSocks = (state) => state?.tuple
|
||||
|
||||
export default {
|
||||
...Setting,
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
urlField: '',
|
||||
socksField: false
|
||||
socksField: false,
|
||||
}
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
Setting.created()
|
||||
this.urlField = getUrl(this.realDraftMode ? this.draft : this.state)
|
||||
this.socksField = getSocks(this.realDraftMode ? this.draft : this.state)
|
||||
|
|
@ -20,23 +21,23 @@ export default {
|
|||
computed: {
|
||||
...Setting.computed,
|
||||
// state that we'll show in the UI, i.e. transforming map into list
|
||||
displayState () {
|
||||
displayState() {
|
||||
if (this.visibleState?.tuple) {
|
||||
return this.visibleState.tuple[1] + ':' + this.visibleState.tuple[2]
|
||||
}
|
||||
return this.visibleState
|
||||
},
|
||||
socksState () {
|
||||
socksState() {
|
||||
return getSocks(this.visibleState)
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
...Setting.components,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue ({ event, isProxy}) {
|
||||
getValue({ event, isProxy }) {
|
||||
if (isProxy) {
|
||||
this.socksField = event
|
||||
} else {
|
||||
|
|
@ -44,10 +45,10 @@ export default {
|
|||
}
|
||||
|
||||
if (this.socksField) {
|
||||
return { tuple: [ ':socks5', ...this.urlField.split(':') ] }
|
||||
return { tuple: [':socks5', ...this.urlField.split(':')] }
|
||||
} else {
|
||||
return this.urlField
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,60 +12,62 @@ export default {
|
|||
...Setting.components,
|
||||
Select,
|
||||
Attachment,
|
||||
MediaUpload
|
||||
MediaUpload,
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
purposeOptions () {
|
||||
return ['any','monochrome','maskable'].map(value => ({
|
||||
purposeOptions() {
|
||||
return ['any', 'monochrome', 'maskable'].map((value) => ({
|
||||
value,
|
||||
key: value,
|
||||
label: this.$t('admin_dash.instance.pwa.icon.' + value)
|
||||
label: this.$t('admin_dash.instance.pwa.icon.' + value),
|
||||
}))
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
attachment (e) {
|
||||
attachment(e) {
|
||||
const path = e[':src']
|
||||
if (!path) {
|
||||
return {
|
||||
mimetype: '',
|
||||
url: ''
|
||||
url: '',
|
||||
}
|
||||
}
|
||||
const url = path.includes('://') ? path : this.$store.state.instance.server + path
|
||||
const url = path.includes('://')
|
||||
? path
|
||||
: this.$store.state.instance.server + path
|
||||
|
||||
return {
|
||||
mimetype: fileTypeExt(url),
|
||||
url
|
||||
url,
|
||||
}
|
||||
},
|
||||
setMediaFile ({ event, index }) {
|
||||
setMediaFile({ event, index }) {
|
||||
this.update({
|
||||
event: {
|
||||
target: {
|
||||
value: event.url
|
||||
value: event.url,
|
||||
},
|
||||
},
|
||||
index,
|
||||
eventType: 'edit',
|
||||
field: ':src'
|
||||
field: ':src',
|
||||
})
|
||||
},
|
||||
setPurpose ({ event, index }) {
|
||||
setPurpose({ event, index }) {
|
||||
this.update({
|
||||
event: {
|
||||
target: {
|
||||
value: event
|
||||
value: event,
|
||||
},
|
||||
},
|
||||
index,
|
||||
eventType: 'edit',
|
||||
field: ':purpose'
|
||||
field: ':purpose',
|
||||
})
|
||||
},
|
||||
getValue ({ event, field, index, eventType }) {
|
||||
getValue({ event, field, index, eventType }) {
|
||||
switch (eventType) {
|
||||
case 'add': {
|
||||
const res = [...this.visibleState, {}]
|
||||
|
|
@ -85,7 +87,7 @@ export default {
|
|||
const item = clone(this.visibleState[index])
|
||||
const string = event.target.value
|
||||
|
||||
if (!string) {
|
||||
if (!string) {
|
||||
delete item[field]
|
||||
} else {
|
||||
item[field] = string
|
||||
|
|
@ -94,6 +96,6 @@ export default {
|
|||
return [...pre, item, ...post]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,40 +3,40 @@ import Setting from './setting.js'
|
|||
|
||||
export default {
|
||||
...Setting,
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
newValue: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
...Setting.components,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
props: {
|
||||
...Setting.props
|
||||
...Setting.props,
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
isSeparate () {
|
||||
isSeparate() {
|
||||
// [[a1, b1], [a2, b2]] vs [a, b]
|
||||
return Array.isArray(this.visibleState[0])
|
||||
},
|
||||
normalizedState () {
|
||||
normalizedState() {
|
||||
if (this.isSeparate) {
|
||||
return this.visibleState.map(y => y.map(x => Number(x) || 0))
|
||||
return this.visibleState.map((y) => y.map((x) => Number(x) || 0))
|
||||
} else {
|
||||
return [this.visibleState.map(x => Number(x) || 0)]
|
||||
return [this.visibleState.map((x) => Number(x) || 0)]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue ({ event, side, index, eventType }) {
|
||||
getValue({ event, side, index, eventType }) {
|
||||
if (eventType === 'edit') {
|
||||
const value = Number(event.target.value)
|
||||
if (Number.isNaN(value)) return this.visibleState
|
||||
|
||||
const newVal = [...this.normalizedState.map(x => [...x])]
|
||||
const newVal = [...this.normalizedState.map((x) => [...x])]
|
||||
newVal[side][index] = value
|
||||
return newVal
|
||||
}
|
||||
|
|
@ -48,6 +48,6 @@ export default {
|
|||
return [this.normalizedState[0]]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,119 +7,126 @@ export default {
|
|||
components: {
|
||||
ModifiedIndicator,
|
||||
DraftButtons,
|
||||
ProfileSettingIndicator
|
||||
ProfileSettingIndicator,
|
||||
},
|
||||
props: {
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: null
|
||||
default: null,
|
||||
},
|
||||
path: {
|
||||
type: [String, Array],
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
showDescription: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
descriptionPathOverride: {
|
||||
type: [String, Array],
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
suggestions: {
|
||||
type: [String, Array],
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
subgroup: {
|
||||
type: String,
|
||||
required: false
|
||||
required: false,
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
parentPath: {
|
||||
type: [String, Array]
|
||||
type: [String, Array],
|
||||
},
|
||||
parentInvert: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
expert: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
default: 0,
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
default: undefined
|
||||
default: undefined,
|
||||
},
|
||||
hideDraftButtons: { // this is for the weird backend hybrid (Boolean|String or Boolean|Number) settings
|
||||
hideDraftButtons: {
|
||||
// this is for the weird backend hybrid (Boolean|String or Boolean|Number) settings
|
||||
required: false,
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
hideLabel: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
hideDescription: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
swapDescriptionAndLabel: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
backendDescriptionPath: {
|
||||
type: [String, Array]
|
||||
type: [String, Array],
|
||||
},
|
||||
overrideBackendDescription: {
|
||||
type: Boolean
|
||||
type: Boolean,
|
||||
},
|
||||
overrideBackendDescriptionLabel: {
|
||||
type: [Boolean, String]
|
||||
type: [Boolean, String],
|
||||
},
|
||||
draftMode: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
default: undefined,
|
||||
},
|
||||
timedApplyMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
inject: {
|
||||
defaultSource: {
|
||||
default: 'default'
|
||||
default: 'default',
|
||||
},
|
||||
defaultDraftMode: {
|
||||
default: false
|
||||
}
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
localDraft: null
|
||||
localDraft: null,
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.realDraftMode && (this.realSource !== 'admin' || this.path == null)) {
|
||||
created() {
|
||||
if (
|
||||
this.realDraftMode &&
|
||||
(this.realSource !== 'admin' || this.path == null)
|
||||
) {
|
||||
this.draft = cloneDeep(this.state)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
draft: {
|
||||
get () {
|
||||
get() {
|
||||
if (this.realSource === 'admin' || this.path == null) {
|
||||
return get(this.$store.state.adminSettings.draft, this.canonPath)
|
||||
} else {
|
||||
return this.localDraft
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
set(value) {
|
||||
if (this.realSource === 'admin' || this.path == null) {
|
||||
this.$store.commit('updateAdminDraft', { path: this.canonPath, value })
|
||||
this.$store.commit('updateAdminDraft', {
|
||||
path: this.canonPath,
|
||||
value,
|
||||
})
|
||||
} else {
|
||||
this.localDraft = value
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
state () {
|
||||
state() {
|
||||
if (this.path == null) {
|
||||
return this.modelValue
|
||||
}
|
||||
|
|
@ -130,71 +137,93 @@ export default {
|
|||
return value
|
||||
}
|
||||
},
|
||||
visibleState () {
|
||||
visibleState() {
|
||||
return this.realDraftMode ? this.draft : this.state
|
||||
},
|
||||
realSource () {
|
||||
realSource() {
|
||||
return this.source || this.defaultSource
|
||||
},
|
||||
realDraftMode () {
|
||||
return typeof this.draftMode === 'undefined' ? this.defaultDraftMode : this.draftMode
|
||||
realDraftMode() {
|
||||
return typeof this.draftMode === 'undefined'
|
||||
? this.defaultDraftMode
|
||||
: this.draftMode
|
||||
},
|
||||
backendDescription () {
|
||||
return get(this.$store.state.adminSettings.descriptions, this.descriptionPath)
|
||||
backendDescription() {
|
||||
return get(
|
||||
this.$store.state.adminSettings.descriptions,
|
||||
this.descriptionPath,
|
||||
)
|
||||
},
|
||||
backendDescriptionLabel () {
|
||||
backendDescriptionLabel() {
|
||||
if (this.realSource !== 'admin') return ''
|
||||
if (this.overrideBackendDescriptionLabel !== '' && typeof this.overrideBackendDescriptionLabel === 'string') {
|
||||
if (
|
||||
this.overrideBackendDescriptionLabel !== '' &&
|
||||
typeof this.overrideBackendDescriptionLabel === 'string'
|
||||
) {
|
||||
return this.overrideBackendDescriptionLabel
|
||||
}
|
||||
if (!this.backendDescription || this.overrideBackendDescriptionLabel) {
|
||||
return this.$t([
|
||||
'admin_dash',
|
||||
'temp_overrides',
|
||||
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
|
||||
'label'
|
||||
].join('.'))
|
||||
return this.$t(
|
||||
[
|
||||
'admin_dash',
|
||||
'temp_overrides',
|
||||
...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')),
|
||||
'label',
|
||||
].join('.'),
|
||||
)
|
||||
} else {
|
||||
return this.swapDescriptionAndLabel
|
||||
? this.backendDescription?.description
|
||||
: this.backendDescription?.label
|
||||
}
|
||||
},
|
||||
backendDescriptionDescription () {
|
||||
backendDescriptionDescription() {
|
||||
if (this.description) return this.description
|
||||
if (this.realSource !== 'admin') return ''
|
||||
if (this.hideDescription) return null
|
||||
if (!this.backendDescription || this.overrideBackendDescription) {
|
||||
return this.$t([
|
||||
'admin_dash',
|
||||
'temp_overrides',
|
||||
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
|
||||
'description'
|
||||
].join('.'))
|
||||
return this.$t(
|
||||
[
|
||||
'admin_dash',
|
||||
'temp_overrides',
|
||||
...this.canonPath.map((p) => p.replace(/\./g, '_DOT_')),
|
||||
'description',
|
||||
].join('.'),
|
||||
)
|
||||
} else {
|
||||
return this.swapDescriptionAndLabel
|
||||
? this.backendDescription?.label
|
||||
: this.backendDescription?.description
|
||||
}
|
||||
},
|
||||
backendDescriptionSuggestions () {
|
||||
backendDescriptionSuggestions() {
|
||||
return this.backendDescription?.suggestions || this.suggestions
|
||||
},
|
||||
shouldBeDisabled () {
|
||||
shouldBeDisabled() {
|
||||
if (this.path == null) {
|
||||
return this.disabled
|
||||
}
|
||||
let parentValue = null
|
||||
if (this.parentPath !== undefined && this.realSource === 'admin') {
|
||||
if (this.realDraftMode) {
|
||||
parentValue = get(this.$store.state.adminSettings.draft, this.parentPath)
|
||||
parentValue = get(
|
||||
this.$store.state.adminSettings.draft,
|
||||
this.parentPath,
|
||||
)
|
||||
} else {
|
||||
parentValue = get(this.configSource, this.parentPath)
|
||||
}
|
||||
}
|
||||
return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false)
|
||||
return (
|
||||
this.disabled ||
|
||||
(parentValue !== null
|
||||
? this.parentInvert
|
||||
? parentValue
|
||||
: !parentValue
|
||||
: false)
|
||||
)
|
||||
},
|
||||
configSource () {
|
||||
configSource() {
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return this.$store.state.profileConfig
|
||||
|
|
@ -204,24 +233,31 @@ export default {
|
|||
return this.$store.getters.mergedConfig
|
||||
}
|
||||
},
|
||||
configSink () {
|
||||
configSink() {
|
||||
if (this.path == null) {
|
||||
return (k, v) => this.$emit('update:modelValue', v)
|
||||
}
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
|
||||
return (k, v) =>
|
||||
this.$store.dispatch('setProfileOption', { name: k, value: v })
|
||||
case 'admin':
|
||||
return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v })
|
||||
return (k, v) =>
|
||||
this.$store.dispatch('pushAdminSetting', { path: k, value: v })
|
||||
default:
|
||||
if (this.timedApplyMode) {
|
||||
return (k, v) => this.$store.dispatch('setOptionTemporarily', { name: k, value: v })
|
||||
return (k, v) =>
|
||||
this.$store.dispatch('setOptionTemporarily', {
|
||||
name: k,
|
||||
value: v,
|
||||
})
|
||||
} else {
|
||||
return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
|
||||
return (k, v) =>
|
||||
this.$store.dispatch('setOption', { name: k, value: v })
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultState () {
|
||||
defaultState() {
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return {}
|
||||
|
|
@ -229,10 +265,10 @@ export default {
|
|||
return get(this.$store.getters.defaultConfig, this.path)
|
||||
}
|
||||
},
|
||||
isProfileSetting () {
|
||||
isProfileSetting() {
|
||||
return this.realSource === 'profile'
|
||||
},
|
||||
isChanged () {
|
||||
isChanged() {
|
||||
if (this.path == null) return false
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
|
|
@ -242,24 +278,24 @@ export default {
|
|||
return this.state !== this.defaultState
|
||||
}
|
||||
},
|
||||
canonPath () {
|
||||
canonPath() {
|
||||
if (this.path == null) return null
|
||||
return Array.isArray(this.path) ? this.path : this.path.split('.')
|
||||
},
|
||||
descriptionPath () {
|
||||
descriptionPath() {
|
||||
if (this.path == null) return null
|
||||
if (this.descriptionPathOverride) return this.descriptionPathOverride
|
||||
const path = Array.isArray(this.path) ? this.path : this.path.split('.')
|
||||
if (this.subgroup) {
|
||||
return [
|
||||
...path.slice(0, path.length - 1),
|
||||
':subgroup,' + this.subgroup,
|
||||
...path.slice(path.length - 1)
|
||||
...path.slice(0, path.length - 1),
|
||||
':subgroup,' + this.subgroup,
|
||||
...path.slice(path.length - 1),
|
||||
]
|
||||
}
|
||||
return path
|
||||
},
|
||||
isDirty () {
|
||||
isDirty() {
|
||||
if (this.path == null) return false
|
||||
if (this.realSource === 'admin' && this.canonPath.length > 3) {
|
||||
return false // should not show draft buttons for "grouped" values
|
||||
|
|
@ -267,47 +303,59 @@ export default {
|
|||
return this.realDraftMode && !isEqual(this.draft, this.state)
|
||||
}
|
||||
},
|
||||
canHardReset () {
|
||||
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths?.has(this.canonPath.join(' -> '))
|
||||
canHardReset() {
|
||||
return (
|
||||
this.realSource === 'admin' &&
|
||||
this.$store.state.adminSettings.modifiedPaths?.has(
|
||||
this.canonPath.join(' -> '),
|
||||
)
|
||||
)
|
||||
},
|
||||
matchesExpertLevel () {
|
||||
matchesExpertLevel() {
|
||||
const settingExpertLevel = this.expert || 0
|
||||
const userToggleExpert = this.$store.state.config.expertLevel || 0
|
||||
|
||||
return settingExpertLevel <= userToggleExpert
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getValue (e) {
|
||||
getValue(e) {
|
||||
return e.target.value
|
||||
},
|
||||
update (e) {
|
||||
update(e) {
|
||||
if (this.realDraftMode) {
|
||||
this.draft = this.getValue(e)
|
||||
} else {
|
||||
this.configSink(this.path, this.getValue(e))
|
||||
}
|
||||
},
|
||||
commitDraft () {
|
||||
commitDraft() {
|
||||
if (this.realDraftMode) {
|
||||
this.configSink(this.path, this.draft)
|
||||
}
|
||||
},
|
||||
reset () {
|
||||
reset() {
|
||||
if (this.realDraftMode) {
|
||||
this.draft = cloneDeep(this.state)
|
||||
} else {
|
||||
set(this.$store.getters.mergedConfig, this.path, cloneDeep(this.defaultState))
|
||||
set(
|
||||
this.$store.getters.mergedConfig,
|
||||
this.path,
|
||||
cloneDeep(this.defaultState),
|
||||
)
|
||||
}
|
||||
},
|
||||
hardReset () {
|
||||
hardReset() {
|
||||
switch (this.realSource) {
|
||||
case 'admin':
|
||||
return this.$store.dispatch('resetAdminSetting', { path: this.path })
|
||||
.then(() => { this.draft = this.state })
|
||||
return this.$store
|
||||
.dispatch('resetAdminSetting', { path: this.path })
|
||||
.then(() => {
|
||||
this.draft = this.state
|
||||
})
|
||||
default:
|
||||
console.warn('Hard reset not implemented yet!')
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,19 +1,19 @@
|
|||
const SharedComputedObject = () => ({
|
||||
user () {
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
expertLevel () {
|
||||
expertLevel() {
|
||||
return this.$store.getters.mergedConfig.expertLevel > 0
|
||||
},
|
||||
mergedConfig () {
|
||||
mergedConfig() {
|
||||
return this.$store.getters.mergedConfig
|
||||
},
|
||||
adminConfig () {
|
||||
adminConfig() {
|
||||
return this.$store.state.adminSettings.config
|
||||
},
|
||||
adminDraft () {
|
||||
adminDraft() {
|
||||
return this.$store.state.adminSettings.draft
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export default SharedComputedObject
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import Setting from './setting.js'
|
||||
|
||||
export default {
|
||||
...Setting
|
||||
...Setting,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ export default {
|
|||
...Setting,
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue ({ e, side }) {
|
||||
getValue({ e, side }) {
|
||||
const [a, b] = this.visibleState || []
|
||||
if (side === 0) {
|
||||
return { tuple: [e.target.value, b]}
|
||||
return { tuple: [e.target.value, b] }
|
||||
} else {
|
||||
return { tuple: [a, e.target.value]}
|
||||
return { tuple: [a, e.target.value] }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,23 @@
|
|||
import Select from 'src/components/select/select.vue'
|
||||
import Setting from './setting.js'
|
||||
|
||||
export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%']
|
||||
export const allCssUnits = [
|
||||
'cm',
|
||||
'mm',
|
||||
'in',
|
||||
'px',
|
||||
'pt',
|
||||
'pc',
|
||||
'em',
|
||||
'ex',
|
||||
'ch',
|
||||
'rem',
|
||||
'vw',
|
||||
'vh',
|
||||
'vmin',
|
||||
'vmax',
|
||||
'%',
|
||||
]
|
||||
export const defaultHorizontalUnits = ['px', 'rem', 'vw']
|
||||
export const defaultVerticalUnits = ['px', 'rem', 'vh']
|
||||
|
||||
|
|
@ -9,47 +25,51 @@ export default {
|
|||
...Setting,
|
||||
components: {
|
||||
...Setting.components,
|
||||
Select
|
||||
Select,
|
||||
},
|
||||
props: {
|
||||
...Setting.props,
|
||||
min: Number,
|
||||
units: {
|
||||
type: Array,
|
||||
default: () => allCssUnits
|
||||
default: () => allCssUnits,
|
||||
},
|
||||
unitSet: {
|
||||
type: String,
|
||||
default: 'none'
|
||||
default: 'none',
|
||||
},
|
||||
step: {
|
||||
type: Number,
|
||||
default: 1
|
||||
default: 1,
|
||||
},
|
||||
resetDefault: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
stateUnit () {
|
||||
return typeof this.state === 'string' ? this.state.replace(/[0-9,.]+/, '') : ''
|
||||
stateUnit() {
|
||||
return typeof this.state === 'string'
|
||||
? this.state.replace(/[0-9,.]+/, '')
|
||||
: ''
|
||||
},
|
||||
stateValue() {
|
||||
return typeof this.state === 'string'
|
||||
? this.state.replace(/[^0-9,.]+/, '')
|
||||
: ''
|
||||
},
|
||||
stateValue () {
|
||||
return typeof this.state === 'string' ? this.state.replace(/[^0-9,.]+/, '') : ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getUnitString (value) {
|
||||
getUnitString(value) {
|
||||
if (this.unitSet === 'none') return value
|
||||
return this.$t(['settings', 'units', this.unitSet, value].join('.'))
|
||||
},
|
||||
updateValue (e) {
|
||||
updateValue(e) {
|
||||
this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit)
|
||||
},
|
||||
updateUnit (e) {
|
||||
updateUnit(e) {
|
||||
let value = this.stateValue
|
||||
const newUnit = e.target.value
|
||||
if (this.resetDefault) {
|
||||
|
|
@ -59,6 +79,6 @@ export default {
|
|||
}
|
||||
}
|
||||
this.configSink(this.path, value + newUnit)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
|||
import './vertical_tab_switcher.scss'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
const findFirstUsable = (slots) => slots.findIndex(_ => _.props)
|
||||
const findFirstUsable = (slots) => slots.findIndex((_) => _.props)
|
||||
|
||||
export default {
|
||||
name: 'VerticalTabSwitcher',
|
||||
|
|
@ -16,30 +16,30 @@ export default {
|
|||
renderOnlyFocused: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
onSwitch: {
|
||||
required: false,
|
||||
type: Function,
|
||||
default: undefined
|
||||
default: undefined,
|
||||
},
|
||||
activeTab: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: undefined
|
||||
default: undefined,
|
||||
},
|
||||
bodyScrollLock: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
default: false,
|
||||
},
|
||||
parentCollapsed: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: null
|
||||
}
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
active: findFirstUsable(this.slots()),
|
||||
resizeHandler: null,
|
||||
|
|
@ -47,82 +47,89 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
activeIndex () {
|
||||
activeIndex() {
|
||||
// In case of controlled component
|
||||
if (this.activeTab) {
|
||||
return this.slots().findIndex(slot => slot && slot.props && this.activeTab === slot.props.key)
|
||||
return this.slots().findIndex(
|
||||
(slot) => slot && slot.props && this.activeTab === slot.props.key,
|
||||
)
|
||||
} else {
|
||||
return this.active
|
||||
}
|
||||
},
|
||||
isActive () {
|
||||
return tabName => {
|
||||
const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
|
||||
isActive() {
|
||||
return (tabName) => {
|
||||
const isWanted = (slot) =>
|
||||
slot.props && slot.props['data-tab-name'] === tabName
|
||||
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
||||
}
|
||||
},
|
||||
...mapPiniaState(useInterfaceStore, {
|
||||
mobileLayout: store => store.layoutType === 'mobile'
|
||||
mobileLayout: (store) => store.layoutType === 'mobile',
|
||||
}),
|
||||
},
|
||||
beforeUpdate () {
|
||||
beforeUpdate() {
|
||||
const currentSlot = this.slots()[this.active]
|
||||
if (!currentSlot.props) {
|
||||
this.active = findFirstUsable(this.slots())
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickTab (index) {
|
||||
clickTab(index) {
|
||||
return (e) => {
|
||||
e.preventDefault()
|
||||
this.setTab(index)
|
||||
}
|
||||
},
|
||||
setTab (index) {
|
||||
setTab(index) {
|
||||
if (typeof this.onSwitch === 'function') {
|
||||
this.onSwitch.call(null, this.slots()[index].key)
|
||||
}
|
||||
this.active = index
|
||||
this.changeNavSide('content')
|
||||
},
|
||||
changeNavSide (side) {
|
||||
changeNavSide(side) {
|
||||
if (this.navSide !== side) {
|
||||
this.navSide = side
|
||||
}
|
||||
},
|
||||
// DO NOT put it to computed, it doesn't work (caching?)
|
||||
slots () {
|
||||
slots() {
|
||||
if (this.$slots.default()[0].type === Fragment) {
|
||||
return this.$slots.default()[0].children
|
||||
}
|
||||
return this.$slots.default()
|
||||
}
|
||||
},
|
||||
},
|
||||
render () {
|
||||
const tabs = this.slots()
|
||||
.map((slot, index) => {
|
||||
const props = slot.props
|
||||
if (!props) return
|
||||
const classesTab = ['vertical-tab', 'menu-item']
|
||||
if (this.activeIndex === index && useInterfaceStore().layoutType !== 'mobile') {
|
||||
classesTab.push('-active')
|
||||
}
|
||||
return (
|
||||
<button
|
||||
disabled={props.disabled}
|
||||
onClick={this.clickTab(index)}
|
||||
class={classesTab.join(' ')}
|
||||
type="button"
|
||||
role="tab"
|
||||
title={props.label}
|
||||
>
|
||||
{!props.icon ? '' : (<FAIcon class="tab-icon" size="1x" fixed-width icon={props.icon}/>)}
|
||||
<span class="text">
|
||||
{props.label}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
render() {
|
||||
const tabs = this.slots().map((slot, index) => {
|
||||
const props = slot.props
|
||||
if (!props) return
|
||||
const classesTab = ['vertical-tab', 'menu-item']
|
||||
if (
|
||||
this.activeIndex === index &&
|
||||
useInterfaceStore().layoutType !== 'mobile'
|
||||
) {
|
||||
classesTab.push('-active')
|
||||
}
|
||||
return (
|
||||
<button
|
||||
disabled={props.disabled}
|
||||
onClick={this.clickTab(index)}
|
||||
class={classesTab.join(' ')}
|
||||
type="button"
|
||||
role="tab"
|
||||
title={props.label}
|
||||
>
|
||||
{!props.icon ? (
|
||||
''
|
||||
) : (
|
||||
<FAIcon class="tab-icon" size="1x" fixed-width icon={props.icon} />
|
||||
)}
|
||||
<span class="text">{props.label}</span>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
const contents = this.slots().map((slot, index) => {
|
||||
const props = slot.props
|
||||
|
|
@ -134,9 +141,8 @@ export default {
|
|||
slot.props['delay-render'] = false
|
||||
delayRender = false
|
||||
}
|
||||
const renderSlot = (!delayRender && (!this.renderOnlyFocused || active))
|
||||
? slot
|
||||
: ''
|
||||
const renderSlot =
|
||||
!delayRender && (!this.renderOnlyFocused || active) ? slot : ''
|
||||
|
||||
const headerClasses = ['tab-content-label']
|
||||
const header = (
|
||||
|
|
@ -147,17 +153,16 @@ export default {
|
|||
title={this.$t('nav.back')}
|
||||
class="button-unstyled"
|
||||
>
|
||||
<FAIcon
|
||||
size="lg"
|
||||
class="back-button-icon"
|
||||
icon="chevron-left"
|
||||
/>
|
||||
<FAIcon size="lg" class="back-button-icon" icon="chevron-left" />
|
||||
</button>
|
||||
{props.label}
|
||||
</h2>
|
||||
)
|
||||
|
||||
const wrapperClasses = ['tab-content-wrapper', active ? '-active' : '-hidden' ]
|
||||
const wrapperClasses = [
|
||||
'tab-content-wrapper',
|
||||
active ? '-active' : '-hidden',
|
||||
]
|
||||
const contentClasses = ['tab-content']
|
||||
if (props['full-width'] || props['full-width'] === '') {
|
||||
contentClasses.push('-full-width')
|
||||
|
|
@ -168,14 +173,10 @@ export default {
|
|||
wrapperClasses.push('-full-height')
|
||||
}
|
||||
return (
|
||||
<div class={wrapperClasses} >
|
||||
<div class="tab-mobile-header">
|
||||
{header}
|
||||
</div>
|
||||
<div class={wrapperClasses}>
|
||||
<div class="tab-mobile-header">{header}</div>
|
||||
<div class="tab-slot-wrapper">
|
||||
<div class={contentClasses} >
|
||||
{renderSlot}
|
||||
</div>
|
||||
<div class={contentClasses}>{renderSlot}</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
@ -193,12 +194,8 @@ export default {
|
|||
}
|
||||
|
||||
return (
|
||||
<div ref="root" class={ rootClasses.join(' ') }>
|
||||
<div
|
||||
class="tabs"
|
||||
role="tablist"
|
||||
ref="nav"
|
||||
>
|
||||
<div ref="root" class={rootClasses.join(' ')}>
|
||||
<div class="tabs" role="tablist" ref="nav">
|
||||
{tabs}
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -211,5 +208,5 @@ export default {
|
|||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,17 +10,15 @@ import { cloneDeep, isEqual } from 'lodash'
|
|||
import { mapState, mapActions } from 'pinia'
|
||||
import {
|
||||
newImporter,
|
||||
newExporter
|
||||
newExporter,
|
||||
} from 'src/services/export_import/export_import.js'
|
||||
import {
|
||||
faTimes,
|
||||
faFileUpload,
|
||||
faFileDownload,
|
||||
faChevronDown
|
||||
faChevronDown,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import {
|
||||
faWindowMinimize
|
||||
} from '@fortawesome/free-regular-svg-icons'
|
||||
import { faWindowMinimize } from '@fortawesome/free-regular-svg-icons'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1
|
||||
|
|
@ -31,25 +29,25 @@ library.add(
|
|||
faWindowMinimize,
|
||||
faFileUpload,
|
||||
faFileDownload,
|
||||
faChevronDown
|
||||
faChevronDown,
|
||||
)
|
||||
|
||||
const SettingsModal = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
dataImporter: newImporter({
|
||||
validator: this.importValidator,
|
||||
onImport: this.onImport,
|
||||
onImportFailure: this.onImportFailure
|
||||
onImportFailure: this.onImportFailure,
|
||||
}),
|
||||
dataThemeExporter: newExporter({
|
||||
filename: 'pleromafe_settings.full',
|
||||
getExportedObject: () => this.generateExport(true)
|
||||
getExportedObject: () => this.generateExport(true),
|
||||
}),
|
||||
dataExporter: newExporter({
|
||||
filename: 'pleromafe_settings',
|
||||
getExportedObject: () => this.generateExport()
|
||||
})
|
||||
getExportedObject: () => this.generateExport(),
|
||||
}),
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -62,29 +60,29 @@ const SettingsModal = {
|
|||
{
|
||||
loadingComponent: PanelLoading,
|
||||
errorComponent: AsyncComponentError,
|
||||
delay: 0
|
||||
}
|
||||
delay: 0,
|
||||
},
|
||||
),
|
||||
SettingsModalAdminContent: getResettableAsyncComponent(
|
||||
() => import('./settings_modal_admin_content.vue'),
|
||||
{
|
||||
loadingComponent: PanelLoading,
|
||||
errorComponent: AsyncComponentError,
|
||||
delay: 0
|
||||
}
|
||||
)
|
||||
delay: 0,
|
||||
},
|
||||
),
|
||||
},
|
||||
methods: {
|
||||
closeModal () {
|
||||
closeModal() {
|
||||
useInterfaceStore().closeSettingsModal()
|
||||
},
|
||||
peekModal () {
|
||||
peekModal() {
|
||||
useInterfaceStore().togglePeekSettingsModal()
|
||||
},
|
||||
importValidator (data) {
|
||||
importValidator(data) {
|
||||
if (!Array.isArray(data._pleroma_settings_version)) {
|
||||
return {
|
||||
messageKey: 'settings.file_import_export.invalid_file'
|
||||
messageKey: 'settings.file_import_export.invalid_file',
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,8 +93,8 @@ const SettingsModal = {
|
|||
messageKey: 'settings.file_export_import.errors.file_too_new',
|
||||
messageArgs: {
|
||||
fileMajor: major,
|
||||
feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION
|
||||
}
|
||||
feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -105,94 +103,106 @@ const SettingsModal = {
|
|||
messageKey: 'settings.file_export_import.errors.file_too_old',
|
||||
messageArgs: {
|
||||
fileMajor: major,
|
||||
feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION
|
||||
}
|
||||
feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
level: 'warning',
|
||||
messageKey: 'settings.file_export_import.errors.file_slightly_new'
|
||||
messageKey: 'settings.file_export_import.errors.file_slightly_new',
|
||||
})
|
||||
}
|
||||
|
||||
return true
|
||||
},
|
||||
onImportFailure (result) {
|
||||
onImportFailure(result) {
|
||||
if (result.error) {
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_settings_imported', level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'settings.invalid_settings_imported',
|
||||
level: 'error',
|
||||
})
|
||||
} else {
|
||||
useInterfaceStore().pushGlobalNotice({ ...result.validationResult, level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
...result.validationResult,
|
||||
level: 'error',
|
||||
})
|
||||
}
|
||||
},
|
||||
onImport (data) {
|
||||
if (data) { this.$store.dispatch('loadSettings', data) }
|
||||
onImport(data) {
|
||||
if (data) {
|
||||
this.$store.dispatch('loadSettings', data)
|
||||
}
|
||||
},
|
||||
restore () {
|
||||
restore() {
|
||||
this.dataImporter.importData()
|
||||
},
|
||||
backup () {
|
||||
backup() {
|
||||
this.dataExporter.exportData()
|
||||
},
|
||||
backupWithTheme () {
|
||||
backupWithTheme() {
|
||||
this.dataThemeExporter.exportData()
|
||||
},
|
||||
generateExport (theme = false) {
|
||||
generateExport(theme = false) {
|
||||
const { config } = this.$store.state
|
||||
let sample = config
|
||||
if (!theme) {
|
||||
const ignoreList = new Set([
|
||||
'customTheme',
|
||||
'customThemeSource',
|
||||
'colors'
|
||||
'colors',
|
||||
])
|
||||
sample = Object.fromEntries(
|
||||
Object
|
||||
.entries(sample)
|
||||
.filter(([key]) => !ignoreList.has(key))
|
||||
Object.entries(sample).filter(([key]) => !ignoreList.has(key)),
|
||||
)
|
||||
}
|
||||
const clone = cloneDeep(sample)
|
||||
clone._pleroma_settings_version = [
|
||||
PLEROMAFE_SETTINGS_MAJOR_VERSION,
|
||||
PLEROMAFE_SETTINGS_MINOR_VERSION
|
||||
PLEROMAFE_SETTINGS_MINOR_VERSION,
|
||||
]
|
||||
return clone
|
||||
},
|
||||
resetAdminDraft () {
|
||||
resetAdminDraft() {
|
||||
this.$store.commit('resetAdminDraft')
|
||||
},
|
||||
pushAdminDraft () {
|
||||
pushAdminDraft() {
|
||||
this.$store.dispatch('pushAdminDraft')
|
||||
},
|
||||
...mapActions(useInterfaceStore, ['temporaryChangesRevert', 'temporaryChangesConfirm'])
|
||||
...mapActions(useInterfaceStore, [
|
||||
'temporaryChangesRevert',
|
||||
'temporaryChangesConfirm',
|
||||
]),
|
||||
},
|
||||
computed: {
|
||||
...mapState(useInterfaceStore, {
|
||||
temporaryChangesCountdown: store => store.temporaryChangesCountdown,
|
||||
currentSaveStateNotice: store => store.settings.currentSaveStateNotice,
|
||||
modalActivated: store => store.settingsModalState !== 'hidden',
|
||||
modalMode: store => store.settingsModalMode,
|
||||
modalOpenedOnceUser: store => store.settingsModalLoadedUser,
|
||||
modalOpenedOnceAdmin: store => store.settingsModalLoadedAdmin,
|
||||
modalPeeked: store => store.settingsModalState === 'minimized'
|
||||
temporaryChangesCountdown: (store) => store.temporaryChangesCountdown,
|
||||
currentSaveStateNotice: (store) => store.settings.currentSaveStateNotice,
|
||||
modalActivated: (store) => store.settingsModalState !== 'hidden',
|
||||
modalMode: (store) => store.settingsModalMode,
|
||||
modalOpenedOnceUser: (store) => store.settingsModalLoadedUser,
|
||||
modalOpenedOnceAdmin: (store) => store.settingsModalLoadedAdmin,
|
||||
modalPeeked: (store) => store.settingsModalState === 'minimized',
|
||||
}),
|
||||
expertLevel: {
|
||||
get () {
|
||||
get() {
|
||||
return this.$store.state.config.expertLevel > 0
|
||||
},
|
||||
set (value) {
|
||||
this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
|
||||
}
|
||||
set(value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'expertLevel',
|
||||
value: value ? 1 : 0,
|
||||
})
|
||||
},
|
||||
},
|
||||
adminDraftAny () {
|
||||
adminDraftAny() {
|
||||
return !isEqual(
|
||||
this.$store.state.adminSettings.config,
|
||||
this.$store.state.adminSettings.draft
|
||||
this.$store.state.adminSettings.draft,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default SettingsModal
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import {
|
|||
faUpload,
|
||||
faMessage,
|
||||
faEllipsis,
|
||||
faGauge
|
||||
faGauge,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
|
|
@ -55,7 +55,7 @@ library.add(
|
|||
faUpload,
|
||||
faMessage,
|
||||
faEllipsis,
|
||||
faGauge
|
||||
faGauge,
|
||||
)
|
||||
|
||||
const SettingsModalAdminContent = {
|
||||
|
|
@ -78,44 +78,46 @@ const SettingsModalAdminContent = {
|
|||
MonitoringTab,
|
||||
RatesTab,
|
||||
OtherTab,
|
||||
PostsTab
|
||||
PostsTab,
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
isLoggedIn () {
|
||||
isLoggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
open () {
|
||||
open() {
|
||||
return useInterfaceStore().settingsModalState !== 'hidden'
|
||||
},
|
||||
bodyLock () {
|
||||
bodyLock() {
|
||||
return useInterfaceStore().settingsModalState === 'visible'
|
||||
},
|
||||
adminDbLoaded () {
|
||||
adminDbLoaded() {
|
||||
return this.$store.state.adminSettings.loaded
|
||||
},
|
||||
adminDescriptionsLoaded () {
|
||||
adminDescriptionsLoaded() {
|
||||
return this.$store.state.adminSettings.descriptions !== null
|
||||
},
|
||||
noDb () {
|
||||
noDb() {
|
||||
return this.$store.state.adminSettings.dbConfigEnabled === false
|
||||
}
|
||||
},
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
if (this.user.rights.admin) {
|
||||
this.$store.dispatch('loadAdminStuff')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen () {
|
||||
onOpen() {
|
||||
const targetTab = useInterfaceStore().settingsModalTargetTab
|
||||
// We're being told to open in specific tab
|
||||
if (targetTab) {
|
||||
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {
|
||||
return elm.props && elm.props['data-tab-name'] === targetTab
|
||||
})
|
||||
const tabIndex = this.$refs.tabSwitcher.$slots
|
||||
.default()
|
||||
.findIndex((elm) => {
|
||||
return elm.props && elm.props['data-tab-name'] === targetTab
|
||||
})
|
||||
if (tabIndex >= 0) {
|
||||
this.$refs.tabSwitcher.setTab(tabIndex)
|
||||
}
|
||||
|
|
@ -123,16 +125,16 @@ const SettingsModalAdminContent = {
|
|||
// Clear the state of target tab, so that next time settings is opened
|
||||
// it doesn't force it.
|
||||
useInterfaceStore().clearSettingsModalTargetTab()
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
this.onOpen()
|
||||
},
|
||||
watch: {
|
||||
open: function (value) {
|
||||
if (value) this.onOpen()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default SettingsModalAdminContent
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import {
|
|||
faCode,
|
||||
faBroom,
|
||||
faLock,
|
||||
faColumns
|
||||
faColumns,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ library.add(
|
|||
faDownload,
|
||||
faPalette,
|
||||
faPaintBrush,
|
||||
faCode
|
||||
faCode,
|
||||
)
|
||||
|
||||
const SettingsModalContent = {
|
||||
|
|
@ -70,36 +70,38 @@ const SettingsModalContent = {
|
|||
AppearanceTab,
|
||||
StyleTab,
|
||||
DeveloperTab,
|
||||
OldThemeTab
|
||||
OldThemeTab,
|
||||
},
|
||||
computed: {
|
||||
isLoggedIn () {
|
||||
isLoggedIn() {
|
||||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
open () {
|
||||
open() {
|
||||
return useInterfaceStore().settingsModalState !== 'hidden'
|
||||
},
|
||||
bodyLock () {
|
||||
bodyLock() {
|
||||
return useInterfaceStore().settingsModalState === 'visible'
|
||||
},
|
||||
expertLevel () {
|
||||
expertLevel() {
|
||||
return this.$store.state.config.expertLevel
|
||||
}
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
navCollapsed: false,
|
||||
navHideHeader: false
|
||||
navHideHeader: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen () {
|
||||
onOpen() {
|
||||
const targetTab = useInterfaceStore().settingsModalTargetTab
|
||||
// We're being told to open in specific tab
|
||||
if (targetTab) {
|
||||
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {
|
||||
return elm.props && elm.props['data-tab-name'] === targetTab
|
||||
})
|
||||
const tabIndex = this.$refs.tabSwitcher.$slots
|
||||
.default()
|
||||
.findIndex((elm) => {
|
||||
return elm.props && elm.props['data-tab-name'] === targetTab
|
||||
})
|
||||
if (tabIndex >= 0) {
|
||||
this.$refs.tabSwitcher.setTab(tabIndex)
|
||||
}
|
||||
|
|
@ -107,16 +109,16 @@ const SettingsModalContent = {
|
|||
// Clear the state of target tab, so that next time settings is opened
|
||||
// it doesn't force it.
|
||||
useInterfaceStore().clearSettingsModalTargetTab()
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
this.onOpen()
|
||||
},
|
||||
watch: {
|
||||
open: function (value) {
|
||||
if (value) this.onOpen()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default SettingsModalContent
|
||||
|
|
|
|||
|
|
@ -9,11 +9,12 @@ import Preview from './old_theme_tab/theme_preview.vue'
|
|||
import { newImporter } from 'src/services/export_import/export_import.js'
|
||||
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
||||
import { init } from 'src/services/theme_data/theme_data_3.service.js'
|
||||
import {
|
||||
getCssRules
|
||||
} from 'src/services/theme_data/css_utils.js'
|
||||
import { getCssRules } from 'src/services/theme_data/css_utils.js'
|
||||
import { deserialize } from 'src/services/theme_data/iss_deserializer.js'
|
||||
import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js'
|
||||
import {
|
||||
createStyleSheet,
|
||||
adoptStyleSheets,
|
||||
} from 'src/services/style_setter/style_setter.js'
|
||||
import fileSizeFormatService from 'src/components/../services/file_size_format/file_size_format.js'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
|
@ -23,7 +24,7 @@ import { mapActions } from 'pinia'
|
|||
import { useInterfaceStore, normalizeThemeData } from 'src/stores/interface'
|
||||
|
||||
const AppearanceTab = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
availableThemesV3: [],
|
||||
availableThemesV2: [],
|
||||
|
|
@ -34,7 +35,7 @@ const AppearanceTab = {
|
|||
validator: this.importValidator,
|
||||
onImport: this.onImport,
|
||||
parser: this.importParser,
|
||||
onImportFailure: this.onImportFailure
|
||||
onImportFailure: this.onImportFailure,
|
||||
}),
|
||||
palettesKeys: [
|
||||
'bg',
|
||||
|
|
@ -44,19 +45,25 @@ const AppearanceTab = {
|
|||
'cRed',
|
||||
'cGreen',
|
||||
'cBlue',
|
||||
'cOrange'
|
||||
'cOrange',
|
||||
],
|
||||
userPalette: {},
|
||||
intersectionObserver: null,
|
||||
forcedRoundnessOptions: ['disabled', 'sharp', 'nonsharp', 'round'].map((mode, i) => ({
|
||||
key: mode,
|
||||
value: i - 1,
|
||||
label: this.$t(`settings.style.themes3.hacks.forced_roundness_mode_${mode}`)
|
||||
})),
|
||||
forcedRoundnessOptions: ['disabled', 'sharp', 'nonsharp', 'round'].map(
|
||||
(mode, i) => ({
|
||||
key: mode,
|
||||
value: i - 1,
|
||||
label: this.$t(
|
||||
`settings.style.themes3.hacks.forced_roundness_mode_${mode}`,
|
||||
),
|
||||
}),
|
||||
),
|
||||
underlayOverrideModes: ['none', 'opaque', 'transparent'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.style.themes3.hacks.underlay_override_mode_${mode}`)
|
||||
label: this.$t(
|
||||
`settings.style.themes3.hacks.underlay_override_mode_${mode}`,
|
||||
),
|
||||
})),
|
||||
backgroundUploading: false,
|
||||
background: null,
|
||||
|
|
@ -71,9 +78,9 @@ const AppearanceTab = {
|
|||
UnitSetting,
|
||||
ProfileSettingIndicator,
|
||||
Preview,
|
||||
PaletteEditor
|
||||
PaletteEditor,
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
useInterfaceStore().getThemeData()
|
||||
|
||||
const updateIndex = (resource) => {
|
||||
|
|
@ -87,120 +94,151 @@ const AppearanceTab = {
|
|||
promise = useInterfaceStore()[`fetch${capitalizedResource}sIndex`]()
|
||||
}
|
||||
|
||||
return promise.then(index => {
|
||||
return Object
|
||||
.entries(index)
|
||||
.map(([k, func]) => [k, func()])
|
||||
return promise.then((index) => {
|
||||
return Object.entries(index).map(([k, func]) => [k, func()])
|
||||
})
|
||||
}
|
||||
|
||||
updateIndex('style').then(styles => {
|
||||
styles.forEach(([key, stylePromise]) => stylePromise.then(data => {
|
||||
const meta = data.find(x => x.component === '@meta')
|
||||
this.availableThemesV3.push({ key, data, name: meta.directives.name, version: 'v3' })
|
||||
}))
|
||||
updateIndex('style').then((styles) => {
|
||||
styles.forEach(([key, stylePromise]) =>
|
||||
stylePromise.then((data) => {
|
||||
const meta = data.find((x) => x.component === '@meta')
|
||||
this.availableThemesV3.push({
|
||||
key,
|
||||
data,
|
||||
name: meta.directives.name,
|
||||
version: 'v3',
|
||||
})
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
updateIndex('theme').then(themes => {
|
||||
themes.forEach(([key, themePromise]) => themePromise.then(data => {
|
||||
if (!data) {
|
||||
console.warn(`Theme with key ${key} is empty or malformed`)
|
||||
} else if (Array.isArray(data)) {
|
||||
console.warn(`Theme with key ${key} is a v1 theme and should be moved to static/palettes/index.json`)
|
||||
} else if (!data.source && !data.theme) {
|
||||
console.warn(`Theme with key ${key} is malformed`)
|
||||
} else {
|
||||
this.availableThemesV2.push({ key, data, name: data.name, version: 'v2' })
|
||||
}
|
||||
}))
|
||||
updateIndex('theme').then((themes) => {
|
||||
themes.forEach(([key, themePromise]) =>
|
||||
themePromise.then((data) => {
|
||||
if (!data) {
|
||||
console.warn(`Theme with key ${key} is empty or malformed`)
|
||||
} else if (Array.isArray(data)) {
|
||||
console.warn(
|
||||
`Theme with key ${key} is a v1 theme and should be moved to static/palettes/index.json`,
|
||||
)
|
||||
} else if (!data.source && !data.theme) {
|
||||
console.warn(`Theme with key ${key} is malformed`)
|
||||
} else {
|
||||
this.availableThemesV2.push({
|
||||
key,
|
||||
data,
|
||||
name: data.name,
|
||||
version: 'v2',
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
this.userPalette = useInterfaceStore().paletteDataUsed || {}
|
||||
|
||||
updateIndex('palette').then(bundledPalettes => {
|
||||
bundledPalettes.forEach(([key, palettePromise]) => palettePromise.then(v => {
|
||||
let palette
|
||||
if (Array.isArray(v)) {
|
||||
const [
|
||||
name,
|
||||
bg,
|
||||
fg,
|
||||
text,
|
||||
link,
|
||||
cRed = '#FF0000',
|
||||
cGreen = '#00FF00',
|
||||
cBlue = '#0000FF',
|
||||
cOrange = '#E3FF00'
|
||||
] = v
|
||||
palette = { key, name, bg, fg, text, link, cRed, cBlue, cGreen, cOrange }
|
||||
} else {
|
||||
palette = { key, ...v }
|
||||
}
|
||||
if (!palette.key.startsWith('style.')) {
|
||||
this.bundledPalettes.push(palette)
|
||||
}
|
||||
}))
|
||||
updateIndex('palette').then((bundledPalettes) => {
|
||||
bundledPalettes.forEach(([key, palettePromise]) =>
|
||||
palettePromise.then((v) => {
|
||||
let palette
|
||||
if (Array.isArray(v)) {
|
||||
const [
|
||||
name,
|
||||
bg,
|
||||
fg,
|
||||
text,
|
||||
link,
|
||||
cRed = '#FF0000',
|
||||
cGreen = '#00FF00',
|
||||
cBlue = '#0000FF',
|
||||
cOrange = '#E3FF00',
|
||||
] = v
|
||||
palette = {
|
||||
key,
|
||||
name,
|
||||
bg,
|
||||
fg,
|
||||
text,
|
||||
link,
|
||||
cRed,
|
||||
cBlue,
|
||||
cGreen,
|
||||
cOrange,
|
||||
}
|
||||
} else {
|
||||
palette = { key, ...v }
|
||||
}
|
||||
if (!palette.key.startsWith('style.')) {
|
||||
this.bundledPalettes.push(palette)
|
||||
}
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
this.previewTheme('stock', 'v3')
|
||||
|
||||
if (window.IntersectionObserver) {
|
||||
this.intersectionObserver = new IntersectionObserver((entries, observer) => {
|
||||
entries.forEach(({ target, isIntersecting }) => {
|
||||
if (!isIntersecting) return
|
||||
const theme = this.availableStyles.find(x => x.key === target.dataset.themeKey)
|
||||
this.$nextTick(() => {
|
||||
if (theme) this.previewTheme(theme.key, theme.version, theme.data)
|
||||
this.intersectionObserver = new IntersectionObserver(
|
||||
(entries, observer) => {
|
||||
entries.forEach(({ target, isIntersecting }) => {
|
||||
if (!isIntersecting) return
|
||||
const theme = this.availableStyles.find(
|
||||
(x) => x.key === target.dataset.themeKey,
|
||||
)
|
||||
this.$nextTick(() => {
|
||||
if (theme) this.previewTheme(theme.key, theme.version, theme.data)
|
||||
})
|
||||
observer.unobserve(target)
|
||||
})
|
||||
observer.unobserve(target)
|
||||
})
|
||||
}, {
|
||||
root: this.$refs.themeList
|
||||
})
|
||||
},
|
||||
{
|
||||
root: this.$refs.themeList,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
this.availableStyles.forEach(theme => this.previewTheme(theme.key, theme.version, theme.data))
|
||||
this.availableStyles.forEach((theme) =>
|
||||
this.previewTheme(theme.key, theme.version, theme.data),
|
||||
)
|
||||
}
|
||||
},
|
||||
updated () {
|
||||
updated() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.themeList.querySelectorAll('.theme-preview').forEach(node => {
|
||||
this.intersectionObserver.observe(node)
|
||||
})
|
||||
this.$refs.themeList
|
||||
.querySelectorAll('.theme-preview')
|
||||
.forEach((node) => {
|
||||
this.intersectionObserver.observe(node)
|
||||
})
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
paletteDataUsed () {
|
||||
paletteDataUsed() {
|
||||
this.userPalette = this.paletteDataUsed || {}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isDefaultBackground () {
|
||||
return !(this.$store.state.users.currentUser.background_image)
|
||||
isDefaultBackground() {
|
||||
return !this.$store.state.users.currentUser.background_image
|
||||
},
|
||||
switchInProgress () {
|
||||
switchInProgress() {
|
||||
return useInterfaceStore().themeChangeInProgress
|
||||
},
|
||||
paletteDataUsed () {
|
||||
paletteDataUsed() {
|
||||
return useInterfaceStore().paletteDataUsed
|
||||
},
|
||||
availableStyles () {
|
||||
return [
|
||||
...this.availableThemesV3,
|
||||
...this.availableThemesV2
|
||||
]
|
||||
availableStyles() {
|
||||
return [...this.availableThemesV3, ...this.availableThemesV2]
|
||||
},
|
||||
availablePalettes () {
|
||||
return [
|
||||
...this.bundledPalettes,
|
||||
...this.stylePalettes
|
||||
]
|
||||
availablePalettes() {
|
||||
return [...this.bundledPalettes, ...this.stylePalettes]
|
||||
},
|
||||
stylePalettes () {
|
||||
stylePalettes() {
|
||||
const ruleset = useInterfaceStore().styleDataUsed || []
|
||||
if (!ruleset && ruleset.length === 0) return
|
||||
const meta = ruleset.find(x => x.component === '@meta')
|
||||
const result = ruleset.filter(x => x.component.startsWith('@palette'))
|
||||
.map(x => {
|
||||
const meta = ruleset.find((x) => x.component === '@meta')
|
||||
const result = ruleset
|
||||
.filter((x) => x.component.startsWith('@palette'))
|
||||
.map((x) => {
|
||||
const { variant, directives } = x
|
||||
const {
|
||||
bg,
|
||||
|
|
@ -212,7 +250,7 @@ const AppearanceTab = {
|
|||
cBlue,
|
||||
cGreen,
|
||||
cOrange,
|
||||
wallpaper
|
||||
wallpaper,
|
||||
} = directives
|
||||
|
||||
const result = {
|
||||
|
|
@ -227,94 +265,103 @@ const AppearanceTab = {
|
|||
cBlue,
|
||||
cGreen,
|
||||
cOrange,
|
||||
wallpaper
|
||||
wallpaper,
|
||||
}
|
||||
return Object.fromEntries(Object.entries(result).filter(([, v]) => v))
|
||||
})
|
||||
return result
|
||||
},
|
||||
noIntersectionObserver () {
|
||||
noIntersectionObserver() {
|
||||
return !window.IntersectionObserver
|
||||
},
|
||||
instanceWallpaper () {
|
||||
instanceWallpaper() {
|
||||
this.$store.state.instance.background
|
||||
},
|
||||
instanceWallpaperUsed () {
|
||||
return this.$store.state.instance.background &&
|
||||
instanceWallpaperUsed() {
|
||||
return (
|
||||
this.$store.state.instance.background &&
|
||||
!this.$store.state.users.currentUser.background_image
|
||||
)
|
||||
},
|
||||
customThemeVersion () {
|
||||
customThemeVersion() {
|
||||
const { themeVersion } = useInterfaceStore()
|
||||
return themeVersion
|
||||
},
|
||||
isCustomThemeUsed () {
|
||||
isCustomThemeUsed() {
|
||||
const { customTheme, customThemeSource } = this.mergedConfig
|
||||
return customTheme != null || customThemeSource != null
|
||||
},
|
||||
isCustomStyleUsed () {
|
||||
isCustomStyleUsed() {
|
||||
const { styleCustomData } = this.mergedConfig
|
||||
return styleCustomData != null
|
||||
},
|
||||
...SharedComputedObject()
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
importFile () {
|
||||
importFile() {
|
||||
this.fileImporter.importData()
|
||||
},
|
||||
importParser (file, filename) {
|
||||
importParser(file, filename) {
|
||||
if (filename.endsWith('.json')) {
|
||||
return JSON.parse(file)
|
||||
} else if (filename.endsWith('.iss')) {
|
||||
return deserialize(file)
|
||||
}
|
||||
},
|
||||
onImport (parsed, filename) {
|
||||
onImport(parsed, filename) {
|
||||
if (filename.endsWith('.json')) {
|
||||
useInterfaceStore().setThemeCustom(parsed.source || parsed.theme)
|
||||
} else if (filename.endsWith('.iss')) {
|
||||
useInterfaceStore().setStyleCustom(parsed)
|
||||
}
|
||||
},
|
||||
onImportFailure (result) {
|
||||
onImportFailure(result) {
|
||||
console.error('Failure importing theme:', result)
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'settings.invalid_theme_imported',
|
||||
level: 'error',
|
||||
})
|
||||
},
|
||||
importValidator (parsed, filename) {
|
||||
importValidator(parsed, filename) {
|
||||
if (filename.endsWith('.json')) {
|
||||
const version = parsed._pleroma_theme_version
|
||||
return version >= 1 || version <= 2
|
||||
} else if (filename.endsWith('.iss')) {
|
||||
if (!Array.isArray(parsed)) return false
|
||||
if (parsed.length < 1) return false
|
||||
if (parsed.find(x => x.component === '@meta') == null) return false
|
||||
if (parsed.find((x) => x.component === '@meta') == null) return false
|
||||
return true
|
||||
}
|
||||
},
|
||||
isThemeActive (key) {
|
||||
return key === (this.mergedConfig.theme || this.$store.state.instance.theme)
|
||||
isThemeActive(key) {
|
||||
return (
|
||||
key === (this.mergedConfig.theme || this.$store.state.instance.theme)
|
||||
)
|
||||
},
|
||||
isStyleActive (key) {
|
||||
return key === (this.mergedConfig.style || this.$store.state.instance.style)
|
||||
isStyleActive(key) {
|
||||
return (
|
||||
key === (this.mergedConfig.style || this.$store.state.instance.style)
|
||||
)
|
||||
},
|
||||
isPaletteActive (key) {
|
||||
return key === (this.mergedConfig.palette || this.$store.state.instance.palette)
|
||||
isPaletteActive(key) {
|
||||
return (
|
||||
key ===
|
||||
(this.mergedConfig.palette || this.$store.state.instance.palette)
|
||||
)
|
||||
},
|
||||
...mapActions(useInterfaceStore, [
|
||||
'setStyle',
|
||||
'setTheme'
|
||||
]),
|
||||
setPalette (name, data) {
|
||||
...mapActions(useInterfaceStore, ['setStyle', 'setTheme']),
|
||||
setPalette(name, data) {
|
||||
useInterfaceStore().setPalette(name)
|
||||
this.userPalette = data
|
||||
},
|
||||
setPaletteCustom (data) {
|
||||
setPaletteCustom(data) {
|
||||
useInterfaceStore().setPaletteCustom(data)
|
||||
this.userPalette = data
|
||||
},
|
||||
resetTheming () {
|
||||
resetTheming() {
|
||||
useInterfaceStore().setStyle('stock')
|
||||
},
|
||||
previewTheme (key, version, input) {
|
||||
previewTheme(key, version, input) {
|
||||
let theme3
|
||||
if (this.compilationCache[key]) {
|
||||
theme3 = this.compilationCache[key]
|
||||
|
|
@ -327,10 +374,10 @@ const AppearanceTab = {
|
|||
ultimateBackgroundColor: '#000000',
|
||||
liteMode: true,
|
||||
debug: true,
|
||||
onlyNormalState: true
|
||||
onlyNormalState: true,
|
||||
})
|
||||
} else if (version === 'v3') {
|
||||
const palette = input.find(x => x.component === '@palette')
|
||||
const palette = input.find((x) => x.component === '@palette')
|
||||
let paletteRule
|
||||
if (palette) {
|
||||
const { directives } = palette
|
||||
|
|
@ -339,21 +386,20 @@ const AppearanceTab = {
|
|||
paletteRule = {
|
||||
component: 'Root',
|
||||
directives: Object.fromEntries(
|
||||
Object
|
||||
.entries(directives)
|
||||
Object.entries(directives)
|
||||
.filter(([k]) => k && k !== 'name')
|
||||
.map(([k, v]) => ['--' + k, 'color | ' + v])
|
||||
)
|
||||
.map(([k, v]) => ['--' + k, 'color | ' + v]),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
paletteRule = null
|
||||
}
|
||||
|
||||
theme3 = init({
|
||||
inputRuleset: [...input, paletteRule].filter(x => x),
|
||||
inputRuleset: [...input, paletteRule].filter((x) => x),
|
||||
ultimateBackgroundColor: '#000000',
|
||||
liteMode: true,
|
||||
onlyNormalState: true
|
||||
onlyNormalState: true,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
|
@ -361,7 +407,7 @@ const AppearanceTab = {
|
|||
inputRuleset: [],
|
||||
ultimateBackgroundColor: '#000000',
|
||||
liteMode: true,
|
||||
onlyNormalState: true
|
||||
onlyNormalState: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -369,22 +415,29 @@ const AppearanceTab = {
|
|||
this.compilationCache[key] = theme3
|
||||
}
|
||||
|
||||
|
||||
const sheet = createStyleSheet('appearance-tab-previews', 90)
|
||||
sheet.addRule([
|
||||
'#theme-preview-', key, ' {\n',
|
||||
getCssRules(theme3.eager).join('\n'),
|
||||
'\n}'
|
||||
].join(''))
|
||||
sheet.addRule(
|
||||
[
|
||||
'#theme-preview-',
|
||||
key,
|
||||
' {\n',
|
||||
getCssRules(theme3.eager).join('\n'),
|
||||
'\n}',
|
||||
].join(''),
|
||||
)
|
||||
sheet.ready = true
|
||||
adoptStyleSheets()
|
||||
},
|
||||
uploadFile (slot, e) {
|
||||
uploadFile(slot, e) {
|
||||
const file = e.target.files[0]
|
||||
if (!file) { return }
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
if (file.size > this.$store.state.instance[slot + 'limit']) {
|
||||
const filesize = fileSizeFormatService.fileSizeFormat(file.size)
|
||||
const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
|
||||
const allowedsize = fileSizeFormatService.fileSizeFormat(
|
||||
this.$store.state.instance[slot + 'limit'],
|
||||
)
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'upload.error.message',
|
||||
messageArgs: [
|
||||
|
|
@ -392,10 +445,10 @@ const AppearanceTab = {
|
|||
filesize: filesize.num,
|
||||
filesizeunit: filesize.unit,
|
||||
allowedsize: allowedsize.num,
|
||||
allowedsizeunit: allowedsize.unit
|
||||
})
|
||||
allowedsizeunit: allowedsize.unit,
|
||||
}),
|
||||
],
|
||||
level: 'error'
|
||||
level: 'error',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
|
@ -408,29 +461,36 @@ const AppearanceTab = {
|
|||
}
|
||||
reader.readAsDataURL(file)
|
||||
},
|
||||
resetBackground () {
|
||||
const confirmed = window.confirm(this.$t('settings.reset_background_confirm'))
|
||||
resetBackground() {
|
||||
const confirmed = window.confirm(
|
||||
this.$t('settings.reset_background_confirm'),
|
||||
)
|
||||
if (confirmed) {
|
||||
this.submitBackground('')
|
||||
}
|
||||
},
|
||||
resetUploadedBackground () {
|
||||
resetUploadedBackground() {
|
||||
this.backgroundPreview = null
|
||||
},
|
||||
submitBackground (background) {
|
||||
if (!this.backgroundPreview && background !== '') { return }
|
||||
submitBackground(background) {
|
||||
if (!this.backgroundPreview && background !== '') {
|
||||
return
|
||||
}
|
||||
|
||||
this.backgroundUploading = true
|
||||
this.$store.state.api.backendInteractor.updateProfileImages({ background })
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateProfileImages({ background })
|
||||
.then((data) => {
|
||||
this.$store.commit('addNewUsers', [data])
|
||||
this.$store.commit('setCurrentUser', data)
|
||||
this.backgroundPreview = null
|
||||
})
|
||||
.catch(this.displayUploadError)
|
||||
.finally(() => { this.backgroundUploading = false })
|
||||
.finally(() => {
|
||||
this.backgroundUploading = false
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default AppearanceTab
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { mapState, mapActions } from 'pinia'
|
||||
import { mapState as mapVuexState } from 'vuex'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
|
|
@ -14,7 +14,6 @@ import Select from 'src/components/select/select.vue'
|
|||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
|
||||
const ClutterTab = {
|
||||
components: {
|
||||
BooleanSetting,
|
||||
|
|
@ -23,23 +22,23 @@ const ClutterTab = {
|
|||
IntegerSetting,
|
||||
Checkbox,
|
||||
Select,
|
||||
HelpIndicator
|
||||
HelpIndicator,
|
||||
},
|
||||
computed: {
|
||||
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
|
||||
instanceSpecificPanelPresent() {
|
||||
return this.$store.state.instance.showInstanceSpecificPanel
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
...mapState(
|
||||
useServerSideStorageStore,
|
||||
{
|
||||
muteFilters: store => Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: store => store.prefsStorage.simple.muteFilters
|
||||
}
|
||||
),
|
||||
...mapState(useServerSideStorageStore, {
|
||||
muteFilters: (store) =>
|
||||
Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
|
||||
}),
|
||||
...mapVuexState({
|
||||
blockExpirationSupported: state => state.instance.blockExpiration
|
||||
blockExpirationSupported: (state) => state.instance.blockExpiration,
|
||||
}),
|
||||
onMuteDefaultActionLv1: {
|
||||
get () {
|
||||
get() {
|
||||
const value = this.$store.state.config.onMuteDefaultAction
|
||||
if (value === 'ask' || value === 'forever') {
|
||||
return value
|
||||
|
|
@ -47,16 +46,19 @@ const ClutterTab = {
|
|||
return 'temporarily'
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
set(value) {
|
||||
let realValue = value
|
||||
if (value !== 'ask' && value !== 'forever') {
|
||||
realValue = '14d'
|
||||
}
|
||||
this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue })
|
||||
}
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'onMuteDefaultAction',
|
||||
value: realValue,
|
||||
})
|
||||
},
|
||||
},
|
||||
onBlockDefaultActionLv1: {
|
||||
get () {
|
||||
get() {
|
||||
const value = this.$store.state.config.onBlockDefaultAction
|
||||
if (value === 'ask' || value === 'forever') {
|
||||
return value
|
||||
|
|
@ -64,29 +66,36 @@ const ClutterTab = {
|
|||
return 'temporarily'
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
set(value) {
|
||||
let realValue = value
|
||||
if (value !== 'ask' && value !== 'forever') {
|
||||
realValue = '14d'
|
||||
}
|
||||
this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue })
|
||||
}
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'onBlockDefaultAction',
|
||||
value: realValue,
|
||||
})
|
||||
},
|
||||
},
|
||||
muteFiltersDraft () {
|
||||
muteFiltersDraft() {
|
||||
return Object.entries(this.muteFiltersDraftObject)
|
||||
},
|
||||
muteFiltersExpired () {
|
||||
muteFiltersExpired() {
|
||||
const now = Date.now()
|
||||
return Object
|
||||
.entries(this.muteFiltersDraftObject)
|
||||
.filter(([, { expires }]) => expires != null && expires <= now)
|
||||
}
|
||||
return Object.entries(this.muteFiltersDraftObject).filter(
|
||||
([, { expires }]) => expires != null && expires <= now,
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useServerSideStorageStore, ['setPreference', 'unsetPreference', 'pushServerSideStorage']),
|
||||
getDatetimeLocal (timestamp) {
|
||||
...mapActions(useServerSideStorageStore, [
|
||||
'setPreference',
|
||||
'unsetPreference',
|
||||
'pushServerSideStorage',
|
||||
]),
|
||||
getDatetimeLocal(timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
const fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2})
|
||||
const fmt = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2 })
|
||||
const datetime = [
|
||||
date.getFullYear(),
|
||||
'-',
|
||||
|
|
@ -96,11 +105,11 @@ const ClutterTab = {
|
|||
'T',
|
||||
fmt.format(date.getHours()),
|
||||
':',
|
||||
fmt.format(date.getMinutes())
|
||||
fmt.format(date.getMinutes()),
|
||||
].join('')
|
||||
return datetime
|
||||
},
|
||||
checkRegexValid (id) {
|
||||
checkRegexValid(id) {
|
||||
const filter = this.muteFiltersObject[id]
|
||||
if (filter.type !== 'regexp') return true
|
||||
if (filter.type !== 'user_regexp') return true
|
||||
|
|
@ -114,19 +123,21 @@ const ClutterTab = {
|
|||
}
|
||||
return valid
|
||||
},
|
||||
createFilter (filter = {
|
||||
type: 'word',
|
||||
value: '',
|
||||
name: 'New Filter',
|
||||
enabled: true,
|
||||
expires: null,
|
||||
hide: false,
|
||||
}) {
|
||||
createFilter(
|
||||
filter = {
|
||||
type: 'word',
|
||||
value: '',
|
||||
name: 'New Filter',
|
||||
enabled: true,
|
||||
expires: null,
|
||||
hide: false,
|
||||
},
|
||||
) {
|
||||
const newId = uuidv4()
|
||||
|
||||
filter.order = this.muteFilters.length + 2
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
exportFilter(id) {
|
||||
|
|
@ -137,23 +148,23 @@ const ClutterTab = {
|
|||
importFilter() {
|
||||
this.filterImporter.importData()
|
||||
},
|
||||
copyFilter (id) {
|
||||
copyFilter(id) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
const newId = uuidv4()
|
||||
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
deleteFilter (id) {
|
||||
deleteFilter(id) {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
purgeExpiredFilters () {
|
||||
purgeExpiredFilters() {
|
||||
this.muteFiltersExpired.forEach(([id]) => {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
|
|
@ -177,17 +188,20 @@ const ClutterTab = {
|
|||
this.muteFiltersDraftDirty[id] = true
|
||||
},
|
||||
saveFilter(id) {
|
||||
this.setPreference({ path: 'simple.muteFilters.' + id , value: this.muteFiltersDraftObject[id] })
|
||||
this.setPreference({
|
||||
path: 'simple.muteFilters.' + id,
|
||||
value: this.muteFiltersDraftObject[id],
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
this.muteFiltersDraftDirty[id] = false
|
||||
},
|
||||
},
|
||||
// Updating nested properties
|
||||
watch: {
|
||||
replyVisibility () {
|
||||
replyVisibility() {
|
||||
this.$store.dispatch('queueFlushAll')
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default ClutterTab
|
||||
|
|
|
|||
|
|
@ -21,69 +21,78 @@ import {
|
|||
faMessage,
|
||||
faPenAlt,
|
||||
faDatabase,
|
||||
faSliders
|
||||
faSliders,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe,
|
||||
faMessage,
|
||||
faPenAlt,
|
||||
faDatabase,
|
||||
faSliders
|
||||
)
|
||||
library.add(faGlobe, faMessage, faPenAlt, faDatabase, faSliders)
|
||||
|
||||
const ComposingTab = {
|
||||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
|
||||
subjectLineOptions: ['email', 'noop', 'masto'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
|
||||
label: this.$t(
|
||||
`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`,
|
||||
),
|
||||
})),
|
||||
conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
|
||||
conversationDisplayOptions: ['tree', 'linear'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_display_${mode}`)
|
||||
label: this.$t(`settings.conversation_display_${mode}`),
|
||||
})),
|
||||
absoluteTime12hOptions: ['24h', '12h'].map(mode => ({
|
||||
absoluteTime12hOptions: ['24h', '12h'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.absolute_time_format_12h_${mode}`)
|
||||
label: this.$t(`settings.absolute_time_format_12h_${mode}`),
|
||||
})),
|
||||
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({
|
||||
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(
|
||||
(mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_other_replies_button_${mode}`),
|
||||
}),
|
||||
),
|
||||
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(
|
||||
(mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.mention_link_display_${mode}`),
|
||||
}),
|
||||
),
|
||||
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_other_replies_button_${mode}`)
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`),
|
||||
})),
|
||||
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.mention_link_display_${mode}`)
|
||||
})),
|
||||
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
|
||||
})),
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`),
|
||||
})),
|
||||
loopSilentAvailable:
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||
// Chrome-likes
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
|
||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),
|
||||
emailLanguage: this.$store.state.users.currentUser.language || ['']
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLVideoElement.prototype,
|
||||
'mozHasAudio',
|
||||
) ||
|
||||
// Chrome-likes
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLMediaElement.prototype,
|
||||
'webkitAudioDecodedByteCount',
|
||||
) ||
|
||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLMediaElement.prototype,
|
||||
'audioTracks',
|
||||
),
|
||||
emailLanguage: this.$store.state.users.currentUser.language || [''],
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -96,61 +105,68 @@ const ComposingTab = {
|
|||
ProfileSettingIndicator,
|
||||
ScopeSelector,
|
||||
Select,
|
||||
FontControl
|
||||
FontControl,
|
||||
},
|
||||
computed: {
|
||||
postFormats () {
|
||||
postFormats() {
|
||||
return this.$store.state.instance.postFormats || []
|
||||
},
|
||||
postContentOptions () {
|
||||
return this.postFormats.map(format => ({
|
||||
postContentOptions() {
|
||||
return this.postFormats.map((format) => ({
|
||||
key: format,
|
||||
value: format,
|
||||
label: this.$t(`post_status.content_type["${format}"]`)
|
||||
label: this.$t(`post_status.content_type["${format}"]`),
|
||||
}))
|
||||
},
|
||||
language: {
|
||||
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
||||
get: function () {
|
||||
return this.$store.getters.mergedConfig.interfaceLanguage
|
||||
},
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||
}
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'interfaceLanguage',
|
||||
value: val,
|
||||
})
|
||||
},
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
...mapState({
|
||||
blockExpirationSupported: state => state.instance.blockExpiration,
|
||||
})
|
||||
blockExpirationSupported: (state) => state.instance.blockExpiration,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
changeDefaultScope (value) {
|
||||
changeDefaultScope(value) {
|
||||
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
|
||||
},
|
||||
clearCache (key) {
|
||||
clearCache(key) {
|
||||
clearCache(key)
|
||||
.then(() => {
|
||||
this.$store.dispatch('settingsSaved', { success: true })
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
this.$store.dispatch('settingsSaved', { error })
|
||||
})
|
||||
},
|
||||
tooSmall () {
|
||||
tooSmall() {
|
||||
this.$emit('tooSmall')
|
||||
},
|
||||
tooBig () {
|
||||
tooBig() {
|
||||
this.$emit('tooBig')
|
||||
},
|
||||
getNavMode () {
|
||||
getNavMode() {
|
||||
return this.$refs.tabSwitcher.getNavMode()
|
||||
},
|
||||
clearAssetCache () {
|
||||
clearAssetCache() {
|
||||
this.clearCache(cacheKey)
|
||||
},
|
||||
clearEmojiCache () {
|
||||
clearEmojiCache() {
|
||||
this.clearCache(emojiCacheKey)
|
||||
},
|
||||
updateProfile () {
|
||||
updateProfile() {
|
||||
const params = {
|
||||
language: localeService.internalToBackendLocaleMulti(this.emailLanguage)
|
||||
language: localeService.internalToBackendLocaleMulti(
|
||||
this.emailLanguage,
|
||||
),
|
||||
}
|
||||
|
||||
this.$store.state.api.backendInteractor
|
||||
|
|
@ -160,19 +176,19 @@ const ComposingTab = {
|
|||
this.$store.commit('setCurrentUser', user)
|
||||
})
|
||||
},
|
||||
updateFont (key, value) {
|
||||
updateFont(key, value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'theme3hacks',
|
||||
value: {
|
||||
...this.mergedConfig.theme3hacks,
|
||||
fonts: {
|
||||
...this.mergedConfig.theme3hacks.fonts,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
[key]: value,
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default ComposingTab
|
||||
|
|
|
|||
|
|
@ -5,81 +5,84 @@ import { mapState } from 'vuex'
|
|||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
||||
|
||||
const DataImportExportTab = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'profile',
|
||||
newDomainToMute: '',
|
||||
listBackupsError: false,
|
||||
addBackupError: false,
|
||||
addedBackup: false,
|
||||
backups: []
|
||||
backups: [],
|
||||
}
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
useOAuthTokensStore().fetchTokens()
|
||||
this.fetchBackups()
|
||||
},
|
||||
components: {
|
||||
Importer,
|
||||
Exporter,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
backendInteractor: (state) => state.api.backendInteractor,
|
||||
user: (state) => state.users.currentUser
|
||||
})
|
||||
user: (state) => state.users.currentUser,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
getFollowsContent () {
|
||||
return this.backendInteractor.exportFriends({ id: this.user.id })
|
||||
getFollowsContent() {
|
||||
return this.backendInteractor
|
||||
.exportFriends({ id: this.user.id })
|
||||
.then(this.generateExportableUsersContent)
|
||||
},
|
||||
getBlocksContent () {
|
||||
return this.backendInteractor.fetchBlocks()
|
||||
getBlocksContent() {
|
||||
return this.backendInteractor
|
||||
.fetchBlocks()
|
||||
.then(this.generateExportableUsersContent)
|
||||
},
|
||||
getMutesContent () {
|
||||
return this.backendInteractor.fetchMutes()
|
||||
getMutesContent() {
|
||||
return this.backendInteractor
|
||||
.fetchMutes()
|
||||
.then(this.generateExportableUsersContent)
|
||||
},
|
||||
importFollows (file) {
|
||||
return this.backendInteractor.importFollows({ file })
|
||||
.then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
importBlocks (file) {
|
||||
return this.backendInteractor.importBlocks({ file })
|
||||
.then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
importMutes (file) {
|
||||
return this.backendInteractor.importMutes({ file })
|
||||
.then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
generateExportableUsersContent (users) {
|
||||
// Get addresses
|
||||
return users.map((user) => {
|
||||
// check is it's a local user
|
||||
if (user && user.is_local) {
|
||||
// append the instance address
|
||||
return user.screen_name + '@' + location.hostname
|
||||
importFollows(file) {
|
||||
return this.backendInteractor.importFollows({ file }).then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
return user.screen_name
|
||||
}).join('\n')
|
||||
})
|
||||
},
|
||||
addBackup () {
|
||||
this.$store.state.api.backendInteractor.addBackup()
|
||||
importBlocks(file) {
|
||||
return this.backendInteractor.importBlocks({ file }).then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
importMutes(file) {
|
||||
return this.backendInteractor.importMutes({ file }).then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
generateExportableUsersContent(users) {
|
||||
// Get addresses
|
||||
return users
|
||||
.map((user) => {
|
||||
// check is it's a local user
|
||||
if (user && user.is_local) {
|
||||
// append the instance address
|
||||
return user.screen_name + '@' + location.hostname
|
||||
}
|
||||
return user.screen_name
|
||||
})
|
||||
.join('\n')
|
||||
},
|
||||
addBackup() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.addBackup()
|
||||
.then(() => {
|
||||
this.addedBackup = true
|
||||
this.addBackupError = false
|
||||
|
|
@ -90,8 +93,9 @@ const DataImportExportTab = {
|
|||
})
|
||||
.then(() => this.fetchBackups())
|
||||
},
|
||||
fetchBackups () {
|
||||
this.$store.state.api.backendInteractor.listBackups()
|
||||
fetchBackups() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.listBackups()
|
||||
.then((res) => {
|
||||
this.backups = res
|
||||
this.listBackupsError = false
|
||||
|
|
@ -99,8 +103,8 @@ const DataImportExportTab = {
|
|||
.catch((error) => {
|
||||
this.listBackupsError = error.error
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default DataImportExportTab
|
||||
|
|
|
|||
|
|
@ -4,43 +4,44 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
|||
|
||||
import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js'
|
||||
|
||||
const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
||||
const pleromaFeCommitUrl =
|
||||
'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
||||
|
||||
const VersionTab = {
|
||||
data () {
|
||||
data() {
|
||||
const instance = this.$store.state.instance
|
||||
return {
|
||||
backendVersion: instance.backendVersion,
|
||||
backendRepository: instance.backendRepository,
|
||||
frontendVersion: instance.frontendVersion
|
||||
frontendVersion: instance.frontendVersion,
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting
|
||||
BooleanSetting,
|
||||
},
|
||||
computed: {
|
||||
frontendVersionLink () {
|
||||
frontendVersionLink() {
|
||||
return pleromaFeCommitUrl + this.frontendVersion
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
clearAssetCache () {
|
||||
clearAssetCache() {
|
||||
this.clearCache(cacheKey)
|
||||
},
|
||||
clearEmojiCache () {
|
||||
clearEmojiCache() {
|
||||
this.clearCache(emojiCacheKey)
|
||||
},
|
||||
clearCache (key) {
|
||||
clearCache(key) {
|
||||
clearCache(key)
|
||||
.then(() => {
|
||||
this.$store.dispatch('settingsSaved', { success: true })
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
this.$store.dispatch('settingsSaved', { error })
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default VersionTab
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
import { cloneDeep } from 'lodash'
|
||||
import { mapState, mapActions } from 'pinia'
|
||||
import { mapState as mapVuexState } from 'vuex'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
import {
|
||||
newImporter,
|
||||
newExporter
|
||||
newExporter,
|
||||
} from 'src/services/export_import/export_import.js'
|
||||
|
||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
|
|
@ -24,27 +24,29 @@ import SharedComputedObject from '../helpers/shared_computed_object.js'
|
|||
const SUPPORTED_TYPES = new Set(['word', 'regexp', 'user', 'user_regexp'])
|
||||
|
||||
const FilteringTab = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
replyVisibilityOptions: ['all', 'following', 'self'].map(mode => ({
|
||||
replyVisibilityOptions: ['all', 'following', 'self'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.reply_visibility_${mode}`)
|
||||
label: this.$t(`settings.reply_visibility_${mode}`),
|
||||
})),
|
||||
muteBlockLv1Options: ['ask', 'forever', 'temporarily'].map(mode => ({
|
||||
muteBlockLv1Options: ['ask', 'forever', 'temporarily'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`user_card.mute_block_${mode}`)
|
||||
label: this.$t(`user_card.mute_block_${mode}`),
|
||||
})),
|
||||
muteFiltersDraftObject: cloneDeep(useServerSideStorageStore().prefsStorage.simple.muteFilters),
|
||||
muteFiltersDraftObject: cloneDeep(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters,
|
||||
),
|
||||
muteFiltersDraftDirty: Object.fromEntries(
|
||||
Object.entries(
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters
|
||||
).map(([k]) => [k, false])
|
||||
useServerSideStorageStore().prefsStorage.simple.muteFilters,
|
||||
).map(([k]) => [k, false]),
|
||||
),
|
||||
exportedFilter: null,
|
||||
filterImporter: newImporter({
|
||||
validator (parsed) {
|
||||
validator(parsed) {
|
||||
if (Array.isArray(parsed)) return false
|
||||
if (!SUPPORTED_TYPES.has(parsed.type)) return false
|
||||
return true
|
||||
|
|
@ -55,7 +57,7 @@ const FilteringTab = {
|
|||
expires = null,
|
||||
hide = false,
|
||||
name = '',
|
||||
value = ''
|
||||
value = '',
|
||||
} = data
|
||||
|
||||
this.createFilter({
|
||||
|
|
@ -63,22 +65,21 @@ const FilteringTab = {
|
|||
expires,
|
||||
hide,
|
||||
name,
|
||||
value
|
||||
value,
|
||||
})
|
||||
},
|
||||
onImportFailure (result) {
|
||||
onImportFailure(result) {
|
||||
console.error('Failure importing filter:', result)
|
||||
useInterfaceStore()
|
||||
.pushGlobalNotice({
|
||||
messageKey: 'settings.filter.import_failure',
|
||||
level: 'error'
|
||||
})
|
||||
}
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'settings.filter.import_failure',
|
||||
level: 'error',
|
||||
})
|
||||
},
|
||||
}),
|
||||
filterExporter: newExporter({
|
||||
filename: 'pleromafe_mute-filter',
|
||||
getExportedObject: () => this.exportedFilter
|
||||
})
|
||||
getExportedObject: () => this.exportedFilter,
|
||||
}),
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -88,23 +89,23 @@ const FilteringTab = {
|
|||
IntegerSetting,
|
||||
Checkbox,
|
||||
Select,
|
||||
HelpIndicator
|
||||
HelpIndicator,
|
||||
},
|
||||
computed: {
|
||||
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
|
||||
instanceSpecificPanelPresent() {
|
||||
return this.$store.state.instance.showInstanceSpecificPanel
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
...mapState(
|
||||
useServerSideStorageStore,
|
||||
{
|
||||
muteFilters: store => Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: store => store.prefsStorage.simple.muteFilters
|
||||
}
|
||||
),
|
||||
...mapState(useServerSideStorageStore, {
|
||||
muteFilters: (store) =>
|
||||
Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: (store) => store.prefsStorage.simple.muteFilters,
|
||||
}),
|
||||
...mapVuexState({
|
||||
blockExpirationSupported: state => state.instance.blockExpiration
|
||||
blockExpirationSupported: (state) => state.instance.blockExpiration,
|
||||
}),
|
||||
onMuteDefaultActionLv1: {
|
||||
get () {
|
||||
get() {
|
||||
const value = this.$store.state.config.onMuteDefaultAction
|
||||
if (value === 'ask' || value === 'forever') {
|
||||
return value
|
||||
|
|
@ -112,16 +113,19 @@ const FilteringTab = {
|
|||
return 'temporarily'
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
set(value) {
|
||||
let realValue = value
|
||||
if (value !== 'ask' && value !== 'forever') {
|
||||
realValue = '14d'
|
||||
}
|
||||
this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue })
|
||||
}
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'onMuteDefaultAction',
|
||||
value: realValue,
|
||||
})
|
||||
},
|
||||
},
|
||||
onBlockDefaultActionLv1: {
|
||||
get () {
|
||||
get() {
|
||||
const value = this.$store.state.config.onBlockDefaultAction
|
||||
if (value === 'ask' || value === 'forever') {
|
||||
return value
|
||||
|
|
@ -129,29 +133,36 @@ const FilteringTab = {
|
|||
return 'temporarily'
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
set(value) {
|
||||
let realValue = value
|
||||
if (value !== 'ask' && value !== 'forever') {
|
||||
realValue = '14d'
|
||||
}
|
||||
this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue })
|
||||
}
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'onBlockDefaultAction',
|
||||
value: realValue,
|
||||
})
|
||||
},
|
||||
},
|
||||
muteFiltersDraft () {
|
||||
muteFiltersDraft() {
|
||||
return Object.entries(this.muteFiltersDraftObject)
|
||||
},
|
||||
muteFiltersExpired () {
|
||||
muteFiltersExpired() {
|
||||
const now = Date.now()
|
||||
return Object
|
||||
.entries(this.muteFiltersDraftObject)
|
||||
.filter(([, { expires }]) => expires != null && expires <= now)
|
||||
}
|
||||
return Object.entries(this.muteFiltersDraftObject).filter(
|
||||
([, { expires }]) => expires != null && expires <= now,
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useServerSideStorageStore, ['setPreference', 'unsetPreference', 'pushServerSideStorage']),
|
||||
getDatetimeLocal (timestamp) {
|
||||
...mapActions(useServerSideStorageStore, [
|
||||
'setPreference',
|
||||
'unsetPreference',
|
||||
'pushServerSideStorage',
|
||||
]),
|
||||
getDatetimeLocal(timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
const fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2})
|
||||
const fmt = new Intl.NumberFormat('en-US', { minimumIntegerDigits: 2 })
|
||||
const datetime = [
|
||||
date.getFullYear(),
|
||||
'-',
|
||||
|
|
@ -161,11 +172,11 @@ const FilteringTab = {
|
|||
'T',
|
||||
fmt.format(date.getHours()),
|
||||
':',
|
||||
fmt.format(date.getMinutes())
|
||||
fmt.format(date.getMinutes()),
|
||||
].join('')
|
||||
return datetime
|
||||
},
|
||||
checkRegexValid (id) {
|
||||
checkRegexValid(id) {
|
||||
const filter = this.muteFiltersObject[id]
|
||||
if (filter.type !== 'regexp') return true
|
||||
if (filter.type !== 'user_regexp') return true
|
||||
|
|
@ -179,19 +190,21 @@ const FilteringTab = {
|
|||
}
|
||||
return valid
|
||||
},
|
||||
createFilter (filter = {
|
||||
type: 'word',
|
||||
value: '',
|
||||
name: 'New Filter',
|
||||
enabled: true,
|
||||
expires: null,
|
||||
hide: false,
|
||||
}) {
|
||||
createFilter(
|
||||
filter = {
|
||||
type: 'word',
|
||||
value: '',
|
||||
name: 'New Filter',
|
||||
enabled: true,
|
||||
expires: null,
|
||||
hide: false,
|
||||
},
|
||||
) {
|
||||
const newId = uuidv4()
|
||||
|
||||
filter.order = this.muteFilters.length + 2
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
exportFilter(id) {
|
||||
|
|
@ -202,23 +215,23 @@ const FilteringTab = {
|
|||
importFilter() {
|
||||
this.filterImporter.importData()
|
||||
},
|
||||
copyFilter (id) {
|
||||
copyFilter(id) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
const newId = uuidv4()
|
||||
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId, value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
deleteFilter (id) {
|
||||
deleteFilter(id) {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
purgeExpiredFilters () {
|
||||
purgeExpiredFilters() {
|
||||
this.muteFiltersExpired.forEach(([id]) => {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id, value: null })
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
|
|
@ -242,17 +255,20 @@ const FilteringTab = {
|
|||
this.muteFiltersDraftDirty[id] = true
|
||||
},
|
||||
saveFilter(id) {
|
||||
this.setPreference({ path: 'simple.muteFilters.' + id , value: this.muteFiltersDraftObject[id] })
|
||||
this.setPreference({
|
||||
path: 'simple.muteFilters.' + id,
|
||||
value: this.muteFiltersDraftObject[id],
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
this.muteFiltersDraftDirty[id] = false
|
||||
},
|
||||
},
|
||||
// Updating nested properties
|
||||
watch: {
|
||||
replyVisibility () {
|
||||
replyVisibility() {
|
||||
this.$store.dispatch('queueFlushAll')
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default FilteringTab
|
||||
|
|
|
|||
|
|
@ -16,17 +16,17 @@ const GeneralTab = {
|
|||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
absoluteTime12hOptions: ['24h', '12h'].map(mode => ({
|
||||
absoluteTime12hOptions: ['24h', '12h'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.absolute_time_format_12h_${mode}`)
|
||||
label: this.$t(`settings.absolute_time_format_12h_${mode}`),
|
||||
})),
|
||||
emailLanguage: this.$store.state.users.currentUser.language || ['']
|
||||
emailLanguage: this.$store.state.users.currentUser.language || [''],
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -36,24 +36,31 @@ const GeneralTab = {
|
|||
FloatSetting,
|
||||
FontControl,
|
||||
InterfaceLanguageSwitcher,
|
||||
ProfileSettingIndicator
|
||||
ProfileSettingIndicator,
|
||||
},
|
||||
computed: {
|
||||
language: {
|
||||
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
||||
get: function () {
|
||||
return this.$store.getters.mergedConfig.interfaceLanguage
|
||||
},
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||
}
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'interfaceLanguage',
|
||||
value: val,
|
||||
})
|
||||
},
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
...mapState({
|
||||
blockExpirationSupported: state => state.instance.blockExpiration,
|
||||
})
|
||||
blockExpirationSupported: (state) => state.instance.blockExpiration,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
updateProfile () {
|
||||
updateProfile() {
|
||||
const params = {
|
||||
language: localeService.internalToBackendLocaleMulti(this.emailLanguage)
|
||||
language: localeService.internalToBackendLocaleMulti(
|
||||
this.emailLanguage,
|
||||
),
|
||||
}
|
||||
|
||||
this.$store.state.api.backendInteractor
|
||||
|
|
@ -63,19 +70,19 @@ const GeneralTab = {
|
|||
this.$store.commit('setCurrentUser', user)
|
||||
})
|
||||
},
|
||||
updateFont (key, value) {
|
||||
updateFont(key, value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'theme3hacks',
|
||||
value: {
|
||||
...this.mergedConfig.theme3hacks,
|
||||
fonts: {
|
||||
...this.mergedConfig.theme3hacks.fonts,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
[key]: value,
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default GeneralTab
|
||||
|
|
|
|||
|
|
@ -9,42 +9,49 @@ const GeneralTab = {
|
|||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.third_column_mode_${mode}`)
|
||||
}))
|
||||
thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(
|
||||
(mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.third_column_mode_${mode}`),
|
||||
}),
|
||||
),
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
UnitSetting,
|
||||
ProfileSettingIndicator
|
||||
ProfileSettingIndicator,
|
||||
},
|
||||
computed: {
|
||||
postFormats () {
|
||||
postFormats() {
|
||||
return this.$store.state.instance.postFormats || []
|
||||
},
|
||||
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
|
||||
columns () {
|
||||
instanceShoutboxPresent() {
|
||||
return this.$store.state.instance.shoutAvailable
|
||||
},
|
||||
columns() {
|
||||
const mode = this.$store.getters.mergedConfig.thirdColumnMode
|
||||
|
||||
const notif = mode === 'none' ? [] : ['notifs']
|
||||
|
||||
if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') {
|
||||
if (
|
||||
this.$store.getters.mergedConfig.sidebarRight ||
|
||||
mode === 'postform'
|
||||
) {
|
||||
return [...notif, 'content', 'sidebar']
|
||||
} else {
|
||||
return ['sidebar', 'content', ...notif]
|
||||
}
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default GeneralTab
|
||||
|
|
|
|||
|
|
@ -15,31 +15,33 @@ import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
|||
|
||||
const BlockList = withLoadMore({
|
||||
fetch: (props, $store) => $store.dispatch('fetchBlocks'),
|
||||
select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),
|
||||
select: (props, $store) =>
|
||||
get($store.state.users.currentUser, 'blockIds', []),
|
||||
destroy: () => {},
|
||||
childPropName: 'items'
|
||||
childPropName: 'items',
|
||||
})(SelectableList)
|
||||
|
||||
const MuteList = withLoadMore({
|
||||
fetch: (props, $store) => $store.dispatch('fetchMutes'),
|
||||
select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),
|
||||
destroy: () => {},
|
||||
childPropName: 'items'
|
||||
childPropName: 'items',
|
||||
})(SelectableList)
|
||||
|
||||
const DomainMuteList = withSubscription({
|
||||
fetch: (props, $store) => $store.dispatch('fetchDomainMutes'),
|
||||
select: (props, $store) => get($store.state.users.currentUser, 'domainMutes', []),
|
||||
childPropName: 'items'
|
||||
select: (props, $store) =>
|
||||
get($store.state.users.currentUser, 'domainMutes', []),
|
||||
childPropName: 'items',
|
||||
})(SelectableList)
|
||||
|
||||
const MutesAndBlocks = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'profile'
|
||||
activeTab: 'profile',
|
||||
}
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
useOAuthTokensStore().fetchTokens()
|
||||
this.$store.dispatch('getKnownDomains')
|
||||
},
|
||||
|
|
@ -53,87 +55,94 @@ const MutesAndBlocks = {
|
|||
DomainMuteCard,
|
||||
ProgressButton,
|
||||
Autosuggest,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
computed: {
|
||||
knownDomains () {
|
||||
knownDomains() {
|
||||
return this.$store.state.instance.knownDomains
|
||||
},
|
||||
user () {
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
importFollows (file) {
|
||||
return this.$store.state.api.backendInteractor.importFollows({ file })
|
||||
importFollows(file) {
|
||||
return this.$store.state.api.backendInteractor
|
||||
.importFollows({ file })
|
||||
.then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
importBlocks (file) {
|
||||
return this.$store.state.api.backendInteractor.importBlocks({ file })
|
||||
importBlocks(file) {
|
||||
return this.$store.state.api.backendInteractor
|
||||
.importBlocks({ file })
|
||||
.then((status) => {
|
||||
if (!status) {
|
||||
throw new Error('failed')
|
||||
}
|
||||
})
|
||||
},
|
||||
generateExportableUsersContent (users) {
|
||||
generateExportableUsersContent(users) {
|
||||
// Get addresses
|
||||
return users.map((user) => {
|
||||
// check is it's a local user
|
||||
if (user && user.is_local) {
|
||||
// append the instance address
|
||||
return user.screen_name + '@' + location.hostname
|
||||
}
|
||||
return user.screen_name
|
||||
}).join('\n')
|
||||
return users
|
||||
.map((user) => {
|
||||
// check is it's a local user
|
||||
if (user && user.is_local) {
|
||||
// append the instance address
|
||||
return user.screen_name + '@' + location.hostname
|
||||
}
|
||||
return user.screen_name
|
||||
})
|
||||
.join('\n')
|
||||
},
|
||||
activateTab (tabName) {
|
||||
activateTab(tabName) {
|
||||
this.activeTab = tabName
|
||||
},
|
||||
filterUnblockedUsers (userIds) {
|
||||
filterUnblockedUsers(userIds) {
|
||||
return reject(userIds, (userId) => {
|
||||
const relationship = this.$store.getters.relationship(this.userId)
|
||||
return relationship.blocking || userId === this.user.id
|
||||
})
|
||||
},
|
||||
filterUnMutedUsers (userIds) {
|
||||
filterUnMutedUsers(userIds) {
|
||||
return reject(userIds, (userId) => {
|
||||
const relationship = this.$store.getters.relationship(this.userId)
|
||||
return relationship.muting || userId === this.user.id
|
||||
})
|
||||
},
|
||||
queryUserIds (query) {
|
||||
return this.$store.dispatch('searchUsers', { query })
|
||||
queryUserIds(query) {
|
||||
return this.$store
|
||||
.dispatch('searchUsers', { query })
|
||||
.then((users) => map(users, 'id'))
|
||||
},
|
||||
blockUsers (ids) {
|
||||
blockUsers(ids) {
|
||||
return this.$store.dispatch('blockUsers', ids)
|
||||
},
|
||||
unblockUsers (ids) {
|
||||
unblockUsers(ids) {
|
||||
return this.$store.dispatch('unblockUsers', ids)
|
||||
},
|
||||
muteUsers (ids) {
|
||||
muteUsers(ids) {
|
||||
return this.$store.dispatch('muteUsers', ids)
|
||||
},
|
||||
unmuteUsers (ids) {
|
||||
unmuteUsers(ids) {
|
||||
return this.$store.dispatch('unmuteUsers', ids)
|
||||
},
|
||||
filterUnMutedDomains (urls) {
|
||||
return urls.filter(url => !this.user.domainMutes.includes(url))
|
||||
filterUnMutedDomains(urls) {
|
||||
return urls.filter((url) => !this.user.domainMutes.includes(url))
|
||||
},
|
||||
queryKnownDomains (query) {
|
||||
queryKnownDomains(query) {
|
||||
return new Promise((resolve) => {
|
||||
resolve(this.knownDomains.filter(url => url.toLowerCase().includes(query)))
|
||||
resolve(
|
||||
this.knownDomains.filter((url) => url.toLowerCase().includes(query)),
|
||||
)
|
||||
})
|
||||
},
|
||||
unmuteDomains (domains) {
|
||||
unmuteDomains(domains) {
|
||||
return this.$store.dispatch('unmuteDomains', domains)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default MutesAndBlocks
|
||||
|
|
|
|||
|
|
@ -2,32 +2,36 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
|
|||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const NotificationsTab = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
activeTab: 'profile',
|
||||
notificationSettings: this.$store.state.users.currentUser.notification_settings,
|
||||
newDomainToMute: ''
|
||||
notificationSettings:
|
||||
this.$store.state.users.currentUser.notification_settings,
|
||||
newDomainToMute: '',
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting
|
||||
BooleanSetting,
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
canReceiveReports () {
|
||||
if (!this.user) { return false }
|
||||
canReceiveReports() {
|
||||
if (!this.user) {
|
||||
return false
|
||||
}
|
||||
return this.user.privileges.includes('reports_manage_reports')
|
||||
},
|
||||
...SharedComputedObject()
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
updateNotificationSettings () {
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateNotificationSettings({ settings: this.notificationSettings })
|
||||
}
|
||||
}
|
||||
updateNotificationSettings() {
|
||||
this.$store.state.api.backendInteractor.updateNotificationSettings({
|
||||
settings: this.notificationSettings,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default NotificationsTab
|
||||
|
|
|
|||
|
|
@ -2,15 +2,13 @@ import {
|
|||
rgb2hex,
|
||||
hex2rgb,
|
||||
getContrastRatioLayers,
|
||||
relativeLuminance
|
||||
relativeLuminance,
|
||||
} from 'src/services/color_convert/color_convert.js'
|
||||
import {
|
||||
newImporter,
|
||||
newExporter
|
||||
newExporter,
|
||||
} from 'src/services/export_import/export_import.js'
|
||||
import {
|
||||
SLOT_INHERITANCE
|
||||
} from 'src/services/theme_data/pleromafe.js'
|
||||
import { SLOT_INHERITANCE } from 'src/services/theme_data/pleromafe.js'
|
||||
import {
|
||||
CURRENT_VERSION,
|
||||
OPACITIES,
|
||||
|
|
@ -22,16 +20,19 @@ import {
|
|||
generateRadii,
|
||||
generateFonts,
|
||||
shadows2to3,
|
||||
colors2to3
|
||||
colors2to3,
|
||||
} from 'src/services/theme_data/theme_data.service.js'
|
||||
|
||||
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
||||
import { init } from 'src/services/theme_data/theme_data_3.service.js'
|
||||
import {
|
||||
getCssRules,
|
||||
getScopedVersion
|
||||
getScopedVersion,
|
||||
} from 'src/services/theme_data/css_utils.js'
|
||||
import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js'
|
||||
import {
|
||||
createStyleSheet,
|
||||
adoptStyleSheets,
|
||||
} from 'src/services/style_setter/style_setter.js'
|
||||
|
||||
import ColorInput from 'src/components/color_input/color_input.vue'
|
||||
import RangeInput from 'src/components/range_input/range_input.vue'
|
||||
|
|
@ -55,8 +56,8 @@ const v1OnlyNames = [
|
|||
'cRed',
|
||||
'cGreen',
|
||||
'cBlue',
|
||||
'cOrange'
|
||||
].map(_ => _ + 'ColorLocal')
|
||||
'cOrange',
|
||||
].map((_) => _ + 'ColorLocal')
|
||||
|
||||
const colorConvert = (color) => {
|
||||
if (color.startsWith('--') || color === 'transparent') {
|
||||
|
|
@ -67,16 +68,16 @@ const colorConvert = (color) => {
|
|||
}
|
||||
|
||||
export default {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
themeImporter: newImporter({
|
||||
validator: this.importValidator,
|
||||
onImport: this.onImport,
|
||||
onImportFailure: this.onImportFailure
|
||||
onImportFailure: this.onImportFailure,
|
||||
}),
|
||||
themeExporter: newExporter({
|
||||
filename: 'pleroma_theme',
|
||||
getExportedObject: () => this.exportedTheme
|
||||
getExportedObject: () => this.exportedTheme,
|
||||
}),
|
||||
availableStyles: [],
|
||||
selected: '',
|
||||
|
|
@ -98,12 +99,18 @@ export default {
|
|||
keepFonts: false,
|
||||
|
||||
...Object.keys(SLOT_INHERITANCE)
|
||||
.map(key => [key, ''])
|
||||
.reduce((acc, [key, val]) => ({ ...acc, [key + 'ColorLocal']: val }), {}),
|
||||
.map((key) => [key, ''])
|
||||
.reduce(
|
||||
(acc, [key, val]) => ({ ...acc, [key + 'ColorLocal']: val }),
|
||||
{},
|
||||
),
|
||||
|
||||
...Object.keys(OPACITIES)
|
||||
.map(key => [key, ''])
|
||||
.reduce((acc, [key, val]) => ({ ...acc, [key + 'OpacityLocal']: val }), {}),
|
||||
.map((key) => [key, ''])
|
||||
.reduce(
|
||||
(acc, [key, val]) => ({ ...acc, [key + 'OpacityLocal']: val }),
|
||||
{},
|
||||
),
|
||||
|
||||
shadowSelected: undefined,
|
||||
shadowsLocal: {},
|
||||
|
|
@ -117,10 +124,10 @@ export default {
|
|||
avatarAltRadiusLocal: '',
|
||||
attachmentRadiusLocal: '',
|
||||
tooltipRadiusLocal: '',
|
||||
chatMessageRadiusLocal: ''
|
||||
chatMessageRadiusLocal: '',
|
||||
}
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
const currentIndex = this.$store.state.instance.themesIndex
|
||||
|
||||
let promise
|
||||
|
|
@ -130,50 +137,48 @@ export default {
|
|||
promise = useInterfaceStore().fetchThemesIndex()
|
||||
}
|
||||
|
||||
promise.then(themesIndex => {
|
||||
Object
|
||||
.values(themesIndex)
|
||||
.forEach(themeFunc => {
|
||||
themeFunc().then(themeData => themeData && this.availableStyles.push(themeData))
|
||||
})
|
||||
promise.then((themesIndex) => {
|
||||
Object.values(themesIndex).forEach((themeFunc) => {
|
||||
themeFunc().then(
|
||||
(themeData) => themeData && this.availableStyles.push(themeData),
|
||||
)
|
||||
})
|
||||
})
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
if (typeof this.shadowSelected === 'undefined') {
|
||||
this.shadowSelected = this.shadowsAvailable[0]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
themeWarningHelp () {
|
||||
themeWarningHelp() {
|
||||
if (!this.themeWarning) return
|
||||
const t = this.$t
|
||||
const pre = 'settings.style.switcher.help.'
|
||||
const {
|
||||
origin,
|
||||
themeEngineVersion,
|
||||
type,
|
||||
noActionsPossible
|
||||
} = this.themeWarning
|
||||
const { origin, themeEngineVersion, type, noActionsPossible } =
|
||||
this.themeWarning
|
||||
if (origin === 'file') {
|
||||
// Loaded v2 theme from file
|
||||
if (themeEngineVersion === 2 && type === 'wrong_version') {
|
||||
return t(pre + 'v2_imported')
|
||||
}
|
||||
if (themeEngineVersion > CURRENT_VERSION) {
|
||||
return t(pre + 'future_version_imported') + ' ' +
|
||||
(
|
||||
noActionsPossible
|
||||
? t(pre + 'snapshot_missing')
|
||||
: t(pre + 'snapshot_present')
|
||||
)
|
||||
return (
|
||||
t(pre + 'future_version_imported') +
|
||||
' ' +
|
||||
(noActionsPossible
|
||||
? t(pre + 'snapshot_missing')
|
||||
: t(pre + 'snapshot_present'))
|
||||
)
|
||||
}
|
||||
if (themeEngineVersion < CURRENT_VERSION) {
|
||||
return t(pre + 'future_version_imported') + ' ' +
|
||||
(
|
||||
noActionsPossible
|
||||
? t(pre + 'snapshot_missing')
|
||||
: t(pre + 'snapshot_present')
|
||||
)
|
||||
return (
|
||||
t(pre + 'future_version_imported') +
|
||||
' ' +
|
||||
(noActionsPossible
|
||||
? t(pre + 'snapshot_missing')
|
||||
: t(pre + 'snapshot_present'))
|
||||
)
|
||||
}
|
||||
} else if (origin === 'localStorage') {
|
||||
if (type === 'snapshot_source_mismatch') {
|
||||
|
|
@ -185,38 +190,40 @@ export default {
|
|||
}
|
||||
// Admin downgraded FE
|
||||
if (themeEngineVersion > CURRENT_VERSION) {
|
||||
return t(pre + 'fe_downgraded') + ' ' +
|
||||
(
|
||||
noActionsPossible
|
||||
? t(pre + 'migration_snapshot_ok')
|
||||
: t(pre + 'migration_snapshot_gone')
|
||||
)
|
||||
return (
|
||||
t(pre + 'fe_downgraded') +
|
||||
' ' +
|
||||
(noActionsPossible
|
||||
? t(pre + 'migration_snapshot_ok')
|
||||
: t(pre + 'migration_snapshot_gone'))
|
||||
)
|
||||
}
|
||||
// Admin upgraded FE
|
||||
if (themeEngineVersion < CURRENT_VERSION) {
|
||||
return t(pre + 'fe_upgraded') + ' ' +
|
||||
(
|
||||
noActionsPossible
|
||||
? t(pre + 'migration_snapshot_ok')
|
||||
: t(pre + 'migration_snapshot_gone')
|
||||
)
|
||||
return (
|
||||
t(pre + 'fe_upgraded') +
|
||||
' ' +
|
||||
(noActionsPossible
|
||||
? t(pre + 'migration_snapshot_ok')
|
||||
: t(pre + 'migration_snapshot_gone'))
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
selectedVersion () {
|
||||
selectedVersion() {
|
||||
return Array.isArray(this.selectedTheme) ? 1 : 2
|
||||
},
|
||||
currentColors () {
|
||||
currentColors() {
|
||||
return Object.keys(SLOT_INHERITANCE)
|
||||
.map(key => [key, this[key + 'ColorLocal']])
|
||||
.map((key) => [key, this[key + 'ColorLocal']])
|
||||
.reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {})
|
||||
},
|
||||
currentOpacity () {
|
||||
currentOpacity() {
|
||||
return Object.keys(OPACITIES)
|
||||
.map(key => [key, this[key + 'OpacityLocal']])
|
||||
.map((key) => [key, this[key + 'OpacityLocal']])
|
||||
.reduce((acc, [key, val]) => ({ ...acc, [key]: val }), {})
|
||||
},
|
||||
currentRadii () {
|
||||
currentRadii() {
|
||||
return {
|
||||
btn: this.btnRadiusLocal,
|
||||
input: this.inputRadiusLocal,
|
||||
|
|
@ -226,11 +233,11 @@ export default {
|
|||
avatarAlt: this.avatarAltRadiusLocal,
|
||||
tooltip: this.tooltipRadiusLocal,
|
||||
attachment: this.attachmentRadiusLocal,
|
||||
chatMessage: this.chatMessageRadiusLocal
|
||||
chatMessage: this.chatMessageRadiusLocal,
|
||||
}
|
||||
},
|
||||
// This needs optimization maybe
|
||||
previewContrast () {
|
||||
previewContrast() {
|
||||
try {
|
||||
if (!this.previewTheme.colors.bg) return {}
|
||||
const colors = this.previewTheme.colors
|
||||
|
|
@ -243,113 +250,124 @@ export default {
|
|||
aaa: ratio >= 7,
|
||||
// same but for 18pt+ texts
|
||||
laa: ratio >= 3,
|
||||
laaa: ratio >= 4.5
|
||||
laaa: ratio >= 4.5,
|
||||
})
|
||||
const colorsConverted = Object.entries(colors).reduce((acc, [key, value]) => ({ ...acc, [key]: colorConvert(value) }), {})
|
||||
const colorsConverted = Object.entries(colors).reduce(
|
||||
(acc, [key, value]) => ({ ...acc, [key]: colorConvert(value) }),
|
||||
{},
|
||||
)
|
||||
|
||||
const ratios = Object.entries(SLOT_INHERITANCE).reduce((acc, [key, value]) => {
|
||||
const slotIsBaseText = key === 'text' || key === 'link'
|
||||
const slotIsText = slotIsBaseText || (
|
||||
typeof value === 'object' && value !== null && value.textColor
|
||||
)
|
||||
if (!slotIsText) return acc
|
||||
const { layer, variant } = slotIsBaseText ? { layer: 'bg' } : value
|
||||
const background = variant || layer
|
||||
const opacitySlot = getOpacitySlot(background)
|
||||
const textColors = [
|
||||
key,
|
||||
...(background === 'bg' ? ['cRed', 'cGreen', 'cBlue', 'cOrange'] : [])
|
||||
]
|
||||
const ratios = Object.entries(SLOT_INHERITANCE).reduce(
|
||||
(acc, [key, value]) => {
|
||||
const slotIsBaseText = key === 'text' || key === 'link'
|
||||
const slotIsText =
|
||||
slotIsBaseText ||
|
||||
(typeof value === 'object' && value !== null && value.textColor)
|
||||
if (!slotIsText) return acc
|
||||
const { layer, variant } = slotIsBaseText ? { layer: 'bg' } : value
|
||||
const background = variant || layer
|
||||
const opacitySlot = getOpacitySlot(background)
|
||||
const textColors = [
|
||||
key,
|
||||
...(background === 'bg'
|
||||
? ['cRed', 'cGreen', 'cBlue', 'cOrange']
|
||||
: []),
|
||||
]
|
||||
|
||||
const layers = getLayers(
|
||||
layer,
|
||||
variant || layer,
|
||||
opacitySlot,
|
||||
colorsConverted,
|
||||
opacity
|
||||
)
|
||||
const layers = getLayers(
|
||||
layer,
|
||||
variant || layer,
|
||||
opacitySlot,
|
||||
colorsConverted,
|
||||
opacity,
|
||||
)
|
||||
|
||||
// Temporary patch for null-y value errors
|
||||
if (layers.flat().some(v => v == null)) return acc
|
||||
// Temporary patch for null-y value errors
|
||||
if (layers.flat().some((v) => v == null)) return acc
|
||||
|
||||
return {
|
||||
...acc,
|
||||
...textColors.reduce((acc, textColorKey) => {
|
||||
const newKey = slotIsBaseText
|
||||
? 'bg' + textColorKey[0].toUpperCase() + textColorKey.slice(1)
|
||||
: textColorKey
|
||||
return {
|
||||
...acc,
|
||||
[newKey]: getContrastRatioLayers(
|
||||
colorsConverted[textColorKey],
|
||||
layers,
|
||||
colorsConverted[textColorKey]
|
||||
)
|
||||
}
|
||||
}, {})
|
||||
}
|
||||
return {
|
||||
...acc,
|
||||
...textColors.reduce((acc, textColorKey) => {
|
||||
const newKey = slotIsBaseText
|
||||
? 'bg' + textColorKey[0].toUpperCase() + textColorKey.slice(1)
|
||||
: textColorKey
|
||||
return {
|
||||
...acc,
|
||||
[newKey]: getContrastRatioLayers(
|
||||
colorsConverted[textColorKey],
|
||||
layers,
|
||||
colorsConverted[textColorKey],
|
||||
),
|
||||
}
|
||||
}, {}),
|
||||
}
|
||||
},
|
||||
{},
|
||||
)
|
||||
|
||||
return Object.entries(ratios).reduce((acc, [k, v]) => {
|
||||
acc[k] = hints(v)
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {})
|
||||
} catch (e) {
|
||||
console.warn('Failure computing contrasts', e)
|
||||
return {}
|
||||
}
|
||||
},
|
||||
themeDataUsed () {
|
||||
themeDataUsed() {
|
||||
return useInterfaceStore().themeDataUsed
|
||||
},
|
||||
shadowsAvailable () {
|
||||
shadowsAvailable() {
|
||||
return Object.keys(DEFAULT_SHADOWS).sort()
|
||||
},
|
||||
currentShadowOverriden: {
|
||||
get () {
|
||||
get() {
|
||||
return !!this.currentShadow
|
||||
},
|
||||
set (val) {
|
||||
set(val) {
|
||||
if (val) {
|
||||
this.shadowsLocal[this.shadowSelected] = (this.currentShadowFallback || [])
|
||||
.map(s => ({
|
||||
name: null,
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 0,
|
||||
spread: 0,
|
||||
inset: false,
|
||||
color: '#000000',
|
||||
alpha: 1,
|
||||
...s
|
||||
}))
|
||||
this.shadowsLocal[this.shadowSelected] = (
|
||||
this.currentShadowFallback || []
|
||||
).map((s) => ({
|
||||
name: null,
|
||||
x: 0,
|
||||
y: 0,
|
||||
blur: 0,
|
||||
spread: 0,
|
||||
inset: false,
|
||||
color: '#000000',
|
||||
alpha: 1,
|
||||
...s,
|
||||
}))
|
||||
} else {
|
||||
delete this.shadowsLocal[this.shadowSelected]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
currentShadowFallback () {
|
||||
currentShadowFallback() {
|
||||
return (this.previewTheme.shadows || {})[this.shadowSelected]
|
||||
},
|
||||
currentShadow: {
|
||||
get () {
|
||||
get() {
|
||||
return this.shadowsLocal[this.shadowSelected]
|
||||
},
|
||||
set (v) {
|
||||
set(v) {
|
||||
this.shadowsLocal[this.shadowSelected] = v
|
||||
}
|
||||
},
|
||||
},
|
||||
themeValid () {
|
||||
themeValid() {
|
||||
return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid
|
||||
},
|
||||
exportedTheme () {
|
||||
const saveEverything = (
|
||||
exportedTheme() {
|
||||
const saveEverything =
|
||||
!this.keepFonts &&
|
||||
!this.keepShadows &&
|
||||
!this.keepOpacity &&
|
||||
!this.keepRoundness &&
|
||||
!this.keepColor
|
||||
)
|
||||
|
||||
const source = {
|
||||
themeEngineVersion: CURRENT_VERSION
|
||||
themeEngineVersion: CURRENT_VERSION,
|
||||
}
|
||||
|
||||
if (this.keepFonts || saveEverything) {
|
||||
|
|
@ -370,18 +388,20 @@ export default {
|
|||
|
||||
const theme = {
|
||||
themeEngineVersion: CURRENT_VERSION,
|
||||
...this.previewTheme
|
||||
...this.previewTheme,
|
||||
}
|
||||
|
||||
return {
|
||||
// To separate from other random JSON files and possible future source formats
|
||||
_pleroma_theme_version: 2, theme, source
|
||||
_pleroma_theme_version: 2,
|
||||
theme,
|
||||
source,
|
||||
}
|
||||
},
|
||||
isActive () {
|
||||
isActive() {
|
||||
const tabSwitcher = this.$parent
|
||||
return tabSwitcher ? tabSwitcher.isActive('theme') : false
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
ColorInput,
|
||||
|
|
@ -393,70 +413,65 @@ export default {
|
|||
TabSwitcher,
|
||||
Preview,
|
||||
Checkbox,
|
||||
Select
|
||||
Select,
|
||||
},
|
||||
methods: {
|
||||
loadTheme (
|
||||
{
|
||||
theme,
|
||||
source,
|
||||
_pleroma_theme_version: fileVersion
|
||||
},
|
||||
loadTheme(
|
||||
{ theme, source, _pleroma_theme_version: fileVersion },
|
||||
origin,
|
||||
forceUseSource = false
|
||||
forceUseSource = false,
|
||||
) {
|
||||
this.dismissWarning()
|
||||
const version = (origin === 'localStorage' && !theme.colors)
|
||||
? 'l1'
|
||||
: fileVersion
|
||||
const version =
|
||||
origin === 'localStorage' && !theme.colors ? 'l1' : fileVersion
|
||||
const snapshotEngineVersion = (theme || {}).themeEngineVersion
|
||||
const themeEngineVersion = (source || {}).themeEngineVersion || 2
|
||||
const versionsMatch = themeEngineVersion === CURRENT_VERSION
|
||||
const sourceSnapshotMismatch = (
|
||||
const sourceSnapshotMismatch =
|
||||
theme !== undefined &&
|
||||
source !== undefined &&
|
||||
themeEngineVersion !== snapshotEngineVersion
|
||||
)
|
||||
source !== undefined &&
|
||||
themeEngineVersion !== snapshotEngineVersion
|
||||
// Force loading of source if user requested it or if snapshot
|
||||
// is unavailable
|
||||
const forcedSourceLoad = (source && forceUseSource) || !theme
|
||||
if (!(versionsMatch && !sourceSnapshotMismatch) &&
|
||||
!forcedSourceLoad &&
|
||||
version !== 'l1' &&
|
||||
origin !== 'defaults'
|
||||
if (
|
||||
!(versionsMatch && !sourceSnapshotMismatch) &&
|
||||
!forcedSourceLoad &&
|
||||
version !== 'l1' &&
|
||||
origin !== 'defaults'
|
||||
) {
|
||||
if (sourceSnapshotMismatch && origin === 'localStorage') {
|
||||
this.themeWarning = {
|
||||
origin,
|
||||
themeEngineVersion,
|
||||
type: 'snapshot_source_mismatch'
|
||||
type: 'snapshot_source_mismatch',
|
||||
}
|
||||
} else if (!theme) {
|
||||
this.themeWarning = {
|
||||
origin,
|
||||
noActionsPossible: true,
|
||||
themeEngineVersion,
|
||||
type: 'no_snapshot_old_version'
|
||||
type: 'no_snapshot_old_version',
|
||||
}
|
||||
} else if (!versionsMatch) {
|
||||
this.themeWarning = {
|
||||
origin,
|
||||
noActionsPossible: !source,
|
||||
themeEngineVersion,
|
||||
type: 'wrong_version'
|
||||
type: 'wrong_version',
|
||||
}
|
||||
}
|
||||
}
|
||||
this.normalizeLocalState(theme, version, source, forcedSourceLoad)
|
||||
},
|
||||
forceLoadLocalStorage () {
|
||||
forceLoadLocalStorage() {
|
||||
this.loadThemeFromLocalStorage(true)
|
||||
},
|
||||
dismissWarning () {
|
||||
dismissWarning() {
|
||||
this.themeWarning = undefined
|
||||
this.tempImportFile = undefined
|
||||
},
|
||||
forceLoad () {
|
||||
forceLoad() {
|
||||
const { origin } = this.themeWarning
|
||||
switch (origin) {
|
||||
case 'localStorage':
|
||||
|
|
@ -468,7 +483,7 @@ export default {
|
|||
}
|
||||
this.dismissWarning()
|
||||
},
|
||||
forceSnapshot () {
|
||||
forceSnapshot() {
|
||||
const { origin } = this.themeWarning
|
||||
switch (origin) {
|
||||
case 'localStorage':
|
||||
|
|
@ -480,25 +495,25 @@ export default {
|
|||
}
|
||||
this.dismissWarning()
|
||||
},
|
||||
loadThemeFromLocalStorage (confirmLoadSource = false) {
|
||||
loadThemeFromLocalStorage(confirmLoadSource = false) {
|
||||
const theme = this.themeDataUsed?.source
|
||||
if (theme) {
|
||||
this.loadTheme(
|
||||
{
|
||||
theme
|
||||
theme,
|
||||
},
|
||||
'localStorage',
|
||||
confirmLoadSource
|
||||
confirmLoadSource,
|
||||
)
|
||||
}
|
||||
},
|
||||
setCustomTheme () {
|
||||
setCustomTheme() {
|
||||
useInterfaceStore().setThemeV2({
|
||||
customTheme: {
|
||||
ignore: true,
|
||||
themeFileVersion: this.selectedVersion,
|
||||
themeEngineVersion: CURRENT_VERSION,
|
||||
...this.previewTheme
|
||||
...this.previewTheme,
|
||||
},
|
||||
customThemeSource: {
|
||||
themeFileVersion: this.selectedVersion,
|
||||
|
|
@ -507,77 +522,84 @@ export default {
|
|||
fonts: this.fontsLocal,
|
||||
opacity: this.currentOpacity,
|
||||
colors: this.currentColors,
|
||||
radii: this.currentRadii
|
||||
}
|
||||
radii: this.currentRadii,
|
||||
},
|
||||
})
|
||||
},
|
||||
updatePreviewColors () {
|
||||
updatePreviewColors() {
|
||||
const result = generateColors({
|
||||
opacity: this.currentOpacity,
|
||||
colors: this.currentColors
|
||||
colors: this.currentColors,
|
||||
})
|
||||
this.previewTheme.colors = result.theme.colors
|
||||
this.previewTheme.opacity = result.theme.opacity
|
||||
},
|
||||
updatePreviewShadows () {
|
||||
updatePreviewShadows() {
|
||||
this.previewTheme.shadows = generateShadows(
|
||||
{
|
||||
shadows: this.shadowsLocal,
|
||||
opacity: this.previewTheme.opacity,
|
||||
themeEngineVersion: this.engineVersion
|
||||
themeEngineVersion: this.engineVersion,
|
||||
},
|
||||
this.previewTheme.colors,
|
||||
relativeLuminance(this.previewTheme.colors.bg) < 0.5 ? 1 : -1
|
||||
relativeLuminance(this.previewTheme.colors.bg) < 0.5 ? 1 : -1,
|
||||
).theme.shadows
|
||||
},
|
||||
importTheme () { this.themeImporter.importData() },
|
||||
exportTheme () { this.themeExporter.exportData() },
|
||||
onImport (parsed, forceSource = false) {
|
||||
importTheme() {
|
||||
this.themeImporter.importData()
|
||||
},
|
||||
exportTheme() {
|
||||
this.themeExporter.exportData()
|
||||
},
|
||||
onImport(parsed, forceSource = false) {
|
||||
this.tempImportFile = parsed
|
||||
this.loadTheme(parsed, 'file', forceSource)
|
||||
},
|
||||
onImportFailure () {
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
onImportFailure() {
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'settings.invalid_theme_imported',
|
||||
level: 'error',
|
||||
})
|
||||
},
|
||||
importValidator (parsed) {
|
||||
importValidator(parsed) {
|
||||
const version = parsed._pleroma_theme_version
|
||||
return version >= 1 || version <= 2
|
||||
},
|
||||
clearAll () {
|
||||
clearAll() {
|
||||
this.loadThemeFromLocalStorage()
|
||||
},
|
||||
|
||||
// Clears all the extra stuff when loading V1 theme
|
||||
clearV1 () {
|
||||
clearV1() {
|
||||
Object.keys(this.$data)
|
||||
.filter(_ => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal'))
|
||||
.filter(_ => !v1OnlyNames.includes(_))
|
||||
.forEach(key => {
|
||||
.filter((_) => _.endsWith('ColorLocal') || _.endsWith('OpacityLocal'))
|
||||
.filter((_) => !v1OnlyNames.includes(_))
|
||||
.forEach((key) => {
|
||||
this.$data[key] = undefined
|
||||
})
|
||||
},
|
||||
|
||||
clearRoundness () {
|
||||
clearRoundness() {
|
||||
Object.keys(this.$data)
|
||||
.filter(_ => _.endsWith('RadiusLocal'))
|
||||
.forEach(key => {
|
||||
.filter((_) => _.endsWith('RadiusLocal'))
|
||||
.forEach((key) => {
|
||||
this.$data[key] = undefined
|
||||
})
|
||||
},
|
||||
|
||||
clearOpacity () {
|
||||
clearOpacity() {
|
||||
Object.keys(this.$data)
|
||||
.filter(_ => _.endsWith('OpacityLocal'))
|
||||
.forEach(key => {
|
||||
.filter((_) => _.endsWith('OpacityLocal'))
|
||||
.forEach((key) => {
|
||||
this.$data[key] = undefined
|
||||
})
|
||||
},
|
||||
|
||||
clearShadows () {
|
||||
clearShadows() {
|
||||
this.shadowsLocal = {}
|
||||
},
|
||||
|
||||
clearFonts () {
|
||||
clearFonts() {
|
||||
this.fontsLocal = {}
|
||||
},
|
||||
|
||||
|
|
@ -594,7 +616,7 @@ export default {
|
|||
* @param {Boolean} source - by default source won't be used if version doesn't match since it might render differently
|
||||
* this allows importing source anyway
|
||||
*/
|
||||
normalizeLocalState (theme, version = 0, source, forceSource = false) {
|
||||
normalizeLocalState(theme, version = 0, source, forceSource = false) {
|
||||
let input
|
||||
if (typeof source !== 'undefined') {
|
||||
if (forceSource || source?.themeEngineVersion === CURRENT_VERSION) {
|
||||
|
|
@ -618,11 +640,17 @@ export default {
|
|||
if (version === 0) {
|
||||
if (input.version) version = input.version
|
||||
// Old v1 naming: fg is text, btn is foreground
|
||||
if (typeof colors.text === 'undefined' && typeof colors.fg !== 'undefined') {
|
||||
if (
|
||||
typeof colors.text === 'undefined' &&
|
||||
typeof colors.fg !== 'undefined'
|
||||
) {
|
||||
version = 1
|
||||
}
|
||||
// New v2 naming: text is text, fg is foreground
|
||||
if (typeof colors.text !== 'undefined' && typeof colors.fg !== 'undefined') {
|
||||
if (
|
||||
typeof colors.text !== 'undefined' &&
|
||||
typeof colors.fg !== 'undefined'
|
||||
) {
|
||||
version = 2
|
||||
}
|
||||
}
|
||||
|
|
@ -648,7 +676,7 @@ export default {
|
|||
.add('cOrange')
|
||||
}
|
||||
|
||||
keys.forEach(key => {
|
||||
keys.forEach((key) => {
|
||||
const color = colors[key]
|
||||
const hex = rgb2hex(colors[key])
|
||||
this[key + 'ColorLocal'] = hex === '#aN' ? color : hex
|
||||
|
|
@ -689,33 +717,32 @@ export default {
|
|||
this.fontsLocal = fonts
|
||||
}
|
||||
},
|
||||
updateTheme3Preview () {
|
||||
updateTheme3Preview() {
|
||||
const theme2 = convertTheme2To3(this.previewTheme)
|
||||
const theme3 = init({
|
||||
inputRuleset: theme2,
|
||||
ultimateBackgroundColor: '#000000',
|
||||
liteMode: true
|
||||
liteMode: true,
|
||||
})
|
||||
|
||||
const sheet = createStyleSheet('theme-tab-overall-preview', 90)
|
||||
const rule = getScopedVersion(
|
||||
getCssRules(theme3.eager),
|
||||
'&'
|
||||
).join('\n')
|
||||
const rule = getScopedVersion(getCssRules(theme3.eager), '&').join('\n')
|
||||
|
||||
sheet.clear()
|
||||
sheet.addRule('#theme-preview {\n' + rule + '\n}')
|
||||
sheet.ready = true
|
||||
adoptStyleSheets()
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
themeDataUsed () {
|
||||
themeDataUsed() {
|
||||
this.loadThemeFromLocalStorage()
|
||||
},
|
||||
currentRadii () {
|
||||
currentRadii() {
|
||||
try {
|
||||
this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii
|
||||
this.previewTheme.radii = generateRadii({
|
||||
radii: this.currentRadii,
|
||||
}).theme.radii
|
||||
this.radiiInvalid = false
|
||||
} catch (e) {
|
||||
this.radiiInvalid = true
|
||||
|
|
@ -723,7 +750,7 @@ export default {
|
|||
}
|
||||
},
|
||||
shadowsLocal: {
|
||||
handler () {
|
||||
handler() {
|
||||
try {
|
||||
this.updatePreviewShadows()
|
||||
this.shadowsInvalid = false
|
||||
|
|
@ -732,21 +759,23 @@ export default {
|
|||
console.warn(e)
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
deep: true,
|
||||
},
|
||||
fontsLocal: {
|
||||
handler () {
|
||||
handler() {
|
||||
try {
|
||||
this.previewTheme.fonts = generateFonts({ fonts: this.fontsLocal }).theme.fonts
|
||||
this.previewTheme.fonts = generateFonts({
|
||||
fonts: this.fontsLocal,
|
||||
}).theme.fonts
|
||||
this.fontsInvalid = false
|
||||
} catch (e) {
|
||||
this.fontsInvalid = true
|
||||
console.warn(e)
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
deep: true,
|
||||
},
|
||||
currentColors () {
|
||||
currentColors() {
|
||||
try {
|
||||
this.updatePreviewColors()
|
||||
this.colorsInvalid = false
|
||||
|
|
@ -755,23 +784,25 @@ export default {
|
|||
console.warn(e)
|
||||
}
|
||||
},
|
||||
currentOpacity () {
|
||||
currentOpacity() {
|
||||
try {
|
||||
this.updatePreviewColors()
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
},
|
||||
selected () {
|
||||
this.selectedTheme = Object.entries(this.availableStyles).find(([, s]) => {
|
||||
if (Array.isArray(s)) {
|
||||
return s[0] === this.selected
|
||||
} else {
|
||||
return s.name === this.selected
|
||||
}
|
||||
})[1]
|
||||
selected() {
|
||||
this.selectedTheme = Object.entries(this.availableStyles).find(
|
||||
([, s]) => {
|
||||
if (Array.isArray(s)) {
|
||||
return s[0] === this.selected
|
||||
} else {
|
||||
return s.name === this.selected
|
||||
}
|
||||
},
|
||||
)[1]
|
||||
},
|
||||
selectedTheme () {
|
||||
selectedTheme() {
|
||||
this.dismissWarning()
|
||||
if (this.selectedVersion === 1) {
|
||||
if (!this.keepRoundness) {
|
||||
|
|
@ -799,8 +830,12 @@ export default {
|
|||
this.cOrangeColorLocal = this.selectedTheme[8]
|
||||
}
|
||||
} else if (this.selectedVersion >= 2) {
|
||||
this.normalizeLocalState(this.selectedTheme.theme, 2, this.selectedTheme.source)
|
||||
this.normalizeLocalState(
|
||||
this.selectedTheme.theme,
|
||||
2,
|
||||
this.selectedTheme.source,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,20 +124,15 @@ import {
|
|||
faTimes,
|
||||
faStar,
|
||||
faRetweet,
|
||||
faReply
|
||||
faReply,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faTimes,
|
||||
faStar,
|
||||
faRetweet,
|
||||
faReply
|
||||
)
|
||||
library.add(faTimes, faStar, faRetweet, faReply)
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Checkbox
|
||||
}
|
||||
Checkbox,
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -10,43 +10,56 @@ const GeneralTab = {
|
|||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
|
||||
conversationDisplayOptions: ['tree', 'linear'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_display_${mode}`)
|
||||
label: this.$t(`settings.conversation_display_${mode}`),
|
||||
})),
|
||||
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({
|
||||
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(
|
||||
(mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_other_replies_button_${mode}`),
|
||||
}),
|
||||
),
|
||||
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(
|
||||
(mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.mention_link_display_${mode}`),
|
||||
}),
|
||||
),
|
||||
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_other_replies_button_${mode}`)
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`),
|
||||
})),
|
||||
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map((mode) => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.mention_link_display_${mode}`)
|
||||
})),
|
||||
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
|
||||
})),
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`),
|
||||
})),
|
||||
loopSilentAvailable:
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||
// Chrome-likes
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
|
||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks')
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLVideoElement.prototype,
|
||||
'mozHasAudio',
|
||||
) ||
|
||||
// Chrome-likes
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLMediaElement.prototype,
|
||||
'webkitAudioDecodedByteCount',
|
||||
) ||
|
||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLMediaElement.prototype,
|
||||
'audioTracks',
|
||||
),
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
@ -54,25 +67,25 @@ const GeneralTab = {
|
|||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
FontControl,
|
||||
ProfileSettingIndicator
|
||||
ProfileSettingIndicator,
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
updateFont (key, value) {
|
||||
updateFont(key, value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'theme3hacks',
|
||||
value: {
|
||||
...this.mergedConfig.theme3hacks,
|
||||
fonts: {
|
||||
...this.mergedConfig.theme3hacks.fonts,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
[key]: value,
|
||||
},
|
||||
},
|
||||
})
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default GeneralTab
|
||||
|
|
|
|||
|
|
@ -8,17 +8,13 @@ import { library } from '@fortawesome/fontawesome-svg-core'
|
|||
import {
|
||||
faTimes,
|
||||
faPlus,
|
||||
faCircleNotch
|
||||
faCircleNotch,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faTimes,
|
||||
faPlus,
|
||||
faCircleNotch
|
||||
)
|
||||
library.add(faTimes, faPlus, faCircleNotch)
|
||||
|
||||
const ProfileTab = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
// Whether user is locked or not
|
||||
locked: this.$store.state.users.currentUser.locked,
|
||||
|
|
@ -28,18 +24,18 @@ const ProfileTab = {
|
|||
UserCard,
|
||||
Checkbox,
|
||||
BooleanSetting,
|
||||
ProfileSettingIndicator
|
||||
ProfileSettingIndicator,
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
...SharedComputedObject()
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
updateProfile () {
|
||||
updateProfile() {
|
||||
const params = {
|
||||
locked: this.locked
|
||||
locked: this.locked,
|
||||
}
|
||||
|
||||
this.$store.state.api.backendInteractor
|
||||
|
|
@ -51,13 +47,13 @@ const ProfileTab = {
|
|||
.catch((error) => {
|
||||
this.displayUploadError(error)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
locked () {
|
||||
locked() {
|
||||
this.updateProfile()
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default ProfileTab
|
||||
|
|
|
|||
|
|
@ -2,8 +2,12 @@ const Confirm = {
|
|||
props: ['disabled'],
|
||||
data: () => ({}),
|
||||
methods: {
|
||||
confirm () { this.$emit('confirm') },
|
||||
cancel () { this.$emit('cancel') }
|
||||
}
|
||||
confirm() {
|
||||
this.$emit('confirm')
|
||||
},
|
||||
cancel() {
|
||||
this.$emit('cancel')
|
||||
},
|
||||
},
|
||||
}
|
||||
export default Confirm
|
||||
|
|
|
|||
|
|
@ -6,113 +6,124 @@ import { mapState } from 'vuex'
|
|||
|
||||
const Mfa = {
|
||||
data: () => ({
|
||||
settings: { // current settings of MFA
|
||||
settings: {
|
||||
// current settings of MFA
|
||||
available: false,
|
||||
enabled: false,
|
||||
totp: false
|
||||
totp: false,
|
||||
},
|
||||
setupState: { // setup mfa
|
||||
setupState: {
|
||||
// setup mfa
|
||||
state: '', // state of setup. '' -> 'getBackupCodes' -> 'setupOTP' -> 'complete'
|
||||
setupOTPState: '' // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete'
|
||||
setupOTPState: '', // state of setup otp. '' -> 'prepare' -> 'confirm' -> 'complete'
|
||||
},
|
||||
backupCodes: {
|
||||
getNewCodes: false,
|
||||
inProgress: false, // progress of fetch codes
|
||||
codes: []
|
||||
codes: [],
|
||||
},
|
||||
otpSettings: { // pre-setup setting of OTP. secret key, qrcode url.
|
||||
otpSettings: {
|
||||
// pre-setup setting of OTP. secret key, qrcode url.
|
||||
provisioning_uri: '',
|
||||
key: ''
|
||||
key: '',
|
||||
},
|
||||
currentPassword: null,
|
||||
otpConfirmToken: null,
|
||||
error: null,
|
||||
readyInit: false
|
||||
readyInit: false,
|
||||
}),
|
||||
components: {
|
||||
'recovery-codes': RecoveryCodes,
|
||||
'totp-item': TOTP,
|
||||
qrcode: VueQrcode,
|
||||
confirm: Confirm
|
||||
confirm: Confirm,
|
||||
},
|
||||
computed: {
|
||||
canSetupOTP () {
|
||||
canSetupOTP() {
|
||||
return (
|
||||
(this.setupInProgress && this.backupCodesPrepared) ||
|
||||
this.settings.enabled
|
||||
) && !this.settings.totp && !this.setupOTPInProgress
|
||||
((this.setupInProgress && this.backupCodesPrepared) ||
|
||||
this.settings.enabled) &&
|
||||
!this.settings.totp &&
|
||||
!this.setupOTPInProgress
|
||||
)
|
||||
},
|
||||
setupInProgress () {
|
||||
return this.setupState.state !== '' && this.setupState.state !== 'complete'
|
||||
setupInProgress() {
|
||||
return (
|
||||
this.setupState.state !== '' && this.setupState.state !== 'complete'
|
||||
)
|
||||
},
|
||||
setupOTPInProgress () {
|
||||
setupOTPInProgress() {
|
||||
return this.setupState.state === 'setupOTP' && !this.completedOTP
|
||||
},
|
||||
prepareOTP () {
|
||||
prepareOTP() {
|
||||
return this.setupState.setupOTPState === 'prepare'
|
||||
},
|
||||
confirmOTP () {
|
||||
confirmOTP() {
|
||||
return this.setupState.setupOTPState === 'confirm'
|
||||
},
|
||||
completedOTP () {
|
||||
completedOTP() {
|
||||
return this.setupState.setupOTPState === 'completed'
|
||||
},
|
||||
backupCodesPrepared () {
|
||||
backupCodesPrepared() {
|
||||
return !this.backupCodes.inProgress && this.backupCodes.codes.length > 0
|
||||
},
|
||||
confirmNewBackupCodes () {
|
||||
confirmNewBackupCodes() {
|
||||
return this.backupCodes.getNewCodes
|
||||
},
|
||||
...mapState({
|
||||
backendInteractor: (state) => state.api.backendInteractor
|
||||
})
|
||||
backendInteractor: (state) => state.api.backendInteractor,
|
||||
}),
|
||||
},
|
||||
|
||||
methods: {
|
||||
activateOTP () {
|
||||
activateOTP() {
|
||||
if (!this.settings.enabled) {
|
||||
this.setupState.state = 'getBackupcodes'
|
||||
this.fetchBackupCodes()
|
||||
}
|
||||
},
|
||||
fetchBackupCodes () {
|
||||
fetchBackupCodes() {
|
||||
this.backupCodes.inProgress = true
|
||||
this.backupCodes.codes = []
|
||||
|
||||
return this.backendInteractor.generateMfaBackupCodes()
|
||||
.then((res) => {
|
||||
this.backupCodes.codes = res.codes
|
||||
this.backupCodes.inProgress = false
|
||||
})
|
||||
return this.backendInteractor.generateMfaBackupCodes().then((res) => {
|
||||
this.backupCodes.codes = res.codes
|
||||
this.backupCodes.inProgress = false
|
||||
})
|
||||
},
|
||||
getBackupCodes () { // get a new backup codes
|
||||
getBackupCodes() {
|
||||
// get a new backup codes
|
||||
this.backupCodes.getNewCodes = true
|
||||
},
|
||||
confirmBackupCodes () { // confirm getting new backup codes
|
||||
confirmBackupCodes() {
|
||||
// confirm getting new backup codes
|
||||
this.fetchBackupCodes().then(() => {
|
||||
this.backupCodes.getNewCodes = false
|
||||
})
|
||||
},
|
||||
cancelBackupCodes () { // cancel confirm form of new backup codes
|
||||
cancelBackupCodes() {
|
||||
// cancel confirm form of new backup codes
|
||||
this.backupCodes.getNewCodes = false
|
||||
},
|
||||
|
||||
// Setup OTP
|
||||
setupOTP () { // prepare setup OTP
|
||||
setupOTP() {
|
||||
// prepare setup OTP
|
||||
this.setupState.state = 'setupOTP'
|
||||
this.setupState.setupOTPState = 'prepare'
|
||||
this.backendInteractor.mfaSetupOTP()
|
||||
.then((res) => {
|
||||
this.otpSettings = res
|
||||
this.setupState.setupOTPState = 'confirm'
|
||||
})
|
||||
},
|
||||
doConfirmOTP () { // handler confirm enable OTP
|
||||
this.error = null
|
||||
this.backendInteractor.mfaConfirmOTP({
|
||||
token: this.otpConfirmToken,
|
||||
password: this.currentPassword
|
||||
this.backendInteractor.mfaSetupOTP().then((res) => {
|
||||
this.otpSettings = res
|
||||
this.setupState.setupOTPState = 'confirm'
|
||||
})
|
||||
},
|
||||
doConfirmOTP() {
|
||||
// handler confirm enable OTP
|
||||
this.error = null
|
||||
this.backendInteractor
|
||||
.mfaConfirmOTP({
|
||||
token: this.otpConfirmToken,
|
||||
password: this.currentPassword,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.error) {
|
||||
this.error = res.error
|
||||
|
|
@ -122,14 +133,15 @@ const Mfa = {
|
|||
})
|
||||
},
|
||||
|
||||
completeSetup () {
|
||||
completeSetup() {
|
||||
this.setupState.setupOTPState = 'complete'
|
||||
this.setupState.state = 'complete'
|
||||
this.currentPassword = null
|
||||
this.error = null
|
||||
this.fetchSettings()
|
||||
},
|
||||
cancelSetup () { // cancel setup
|
||||
cancelSetup() {
|
||||
// cancel setup
|
||||
this.setupState.setupOTPState = ''
|
||||
this.setupState.state = ''
|
||||
this.currentPassword = null
|
||||
|
|
@ -138,18 +150,18 @@ const Mfa = {
|
|||
// end Setup OTP
|
||||
|
||||
// fetch settings from server
|
||||
async fetchSettings () {
|
||||
async fetchSettings() {
|
||||
const result = await this.backendInteractor.settingsMFA()
|
||||
if (result.error) return
|
||||
this.settings = result.settings
|
||||
this.settings.available = true
|
||||
return result
|
||||
}
|
||||
},
|
||||
},
|
||||
mounted () {
|
||||
mounted() {
|
||||
this.fetchSettings().then(() => {
|
||||
this.readyInit = true
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
export default Mfa
|
||||
|
|
|
|||
|
|
@ -4,14 +4,20 @@ export default {
|
|||
type: Object,
|
||||
default: () => ({
|
||||
inProgress: false,
|
||||
codes: []
|
||||
})
|
||||
}
|
||||
codes: [],
|
||||
}),
|
||||
},
|
||||
},
|
||||
data: () => ({}),
|
||||
computed: {
|
||||
inProgress () { return this.backupCodes.inProgress },
|
||||
ready () { return this.backupCodes.codes.length > 0 },
|
||||
displayTitle () { return this.inProgress || this.ready }
|
||||
}
|
||||
inProgress() {
|
||||
return this.backupCodes.inProgress
|
||||
},
|
||||
ready() {
|
||||
return this.backupCodes.codes.length > 0
|
||||
},
|
||||
displayTitle() {
|
||||
return this.inProgress || this.ready
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,34 +7,38 @@ export default {
|
|||
error: false,
|
||||
currentPassword: '',
|
||||
deactivate: false,
|
||||
inProgress: false // progress peform request to disable otp method
|
||||
inProgress: false, // progress peform request to disable otp method
|
||||
}),
|
||||
components: {
|
||||
confirm: Confirm
|
||||
confirm: Confirm,
|
||||
},
|
||||
computed: {
|
||||
isActivated () {
|
||||
isActivated() {
|
||||
return this.settings.totp
|
||||
},
|
||||
...mapState({
|
||||
backendInteractor: (state) => state.api.backendInteractor
|
||||
})
|
||||
backendInteractor: (state) => state.api.backendInteractor,
|
||||
}),
|
||||
},
|
||||
methods: {
|
||||
doActivate () {
|
||||
doActivate() {
|
||||
this.$emit('activate')
|
||||
},
|
||||
cancelDeactivate () { this.deactivate = false },
|
||||
doDeactivate () {
|
||||
cancelDeactivate() {
|
||||
this.deactivate = false
|
||||
},
|
||||
doDeactivate() {
|
||||
this.error = null
|
||||
this.deactivate = true
|
||||
},
|
||||
confirmDeactivate () { // confirm deactivate TOTP method
|
||||
confirmDeactivate() {
|
||||
// confirm deactivate TOTP method
|
||||
this.error = null
|
||||
this.inProgress = true
|
||||
this.backendInteractor.mfaDisableOTP({
|
||||
password: this.currentPassword
|
||||
})
|
||||
this.backendInteractor
|
||||
.mfaDisableOTP({
|
||||
password: this.currentPassword,
|
||||
})
|
||||
.then((res) => {
|
||||
this.inProgress = false
|
||||
if (res.error) {
|
||||
|
|
@ -44,6 +48,6 @@ export default {
|
|||
this.deactivate = false
|
||||
this.$emit('deactivate')
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import localeService from 'src/services/locale/locale.service.js'
|
|||
import { useOAuthTokensStore } from 'src/stores/oauth_tokens'
|
||||
|
||||
const SecurityTab = {
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
newEmail: '',
|
||||
changeEmailError: false,
|
||||
|
|
@ -25,41 +25,44 @@ const SecurityTab = {
|
|||
listAliasesError: false,
|
||||
addAliasTarget: '',
|
||||
addedAlias: false,
|
||||
addAliasError: false
|
||||
addAliasError: false,
|
||||
}
|
||||
},
|
||||
created () {
|
||||
created() {
|
||||
useOAuthTokensStore().fetchTokens()
|
||||
this.fetchAliases()
|
||||
},
|
||||
components: {
|
||||
ProgressButton,
|
||||
Mfa,
|
||||
Checkbox
|
||||
Checkbox,
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
user() {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
pleromaExtensionsAvailable () {
|
||||
pleromaExtensionsAvailable() {
|
||||
return this.$store.state.instance.pleromaExtensionsAvailable
|
||||
},
|
||||
oauthTokens () {
|
||||
return useOAuthTokensStore().tokens.map(oauthToken => {
|
||||
oauthTokens() {
|
||||
return useOAuthTokensStore().tokens.map((oauthToken) => {
|
||||
return {
|
||||
id: oauthToken.id,
|
||||
appName: oauthToken.app_name,
|
||||
validUntil: new Date(oauthToken.valid_until).toLocaleDateString(localeService.internalToBrowserLocale(this.$i18n.locale))
|
||||
validUntil: new Date(oauthToken.valid_until).toLocaleDateString(
|
||||
localeService.internalToBrowserLocale(this.$i18n.locale),
|
||||
),
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
confirmDelete () {
|
||||
confirmDelete() {
|
||||
this.deletingAccount = true
|
||||
},
|
||||
deleteAccount () {
|
||||
this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })
|
||||
deleteAccount() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.$store.dispatch('logout')
|
||||
|
|
@ -69,13 +72,14 @@ const SecurityTab = {
|
|||
}
|
||||
})
|
||||
},
|
||||
changePassword () {
|
||||
changePassword() {
|
||||
const params = {
|
||||
password: this.changePasswordInputs[0],
|
||||
newPassword: this.changePasswordInputs[1],
|
||||
newPasswordConfirmation: this.changePasswordInputs[2]
|
||||
newPasswordConfirmation: this.changePasswordInputs[2],
|
||||
}
|
||||
this.$store.state.api.backendInteractor.changePassword(params)
|
||||
this.$store.state.api.backendInteractor
|
||||
.changePassword(params)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.changedPassword = true
|
||||
|
|
@ -87,12 +91,13 @@ const SecurityTab = {
|
|||
}
|
||||
})
|
||||
},
|
||||
changeEmail () {
|
||||
changeEmail() {
|
||||
const params = {
|
||||
email: this.newEmail,
|
||||
password: this.changeEmailPassword
|
||||
password: this.changeEmailPassword,
|
||||
}
|
||||
this.$store.state.api.backendInteractor.changeEmail(params)
|
||||
this.$store.state.api.backendInteractor
|
||||
.changeEmail(params)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.changedEmail = true
|
||||
|
|
@ -103,12 +108,13 @@ const SecurityTab = {
|
|||
}
|
||||
})
|
||||
},
|
||||
moveAccount () {
|
||||
moveAccount() {
|
||||
const params = {
|
||||
targetAccount: this.moveAccountTarget,
|
||||
password: this.moveAccountPassword
|
||||
password: this.moveAccountPassword,
|
||||
}
|
||||
this.$store.state.api.backendInteractor.moveAccount(params)
|
||||
this.$store.state.api.backendInteractor
|
||||
.moveAccount(params)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.movedAccount = true
|
||||
|
|
@ -119,12 +125,14 @@ const SecurityTab = {
|
|||
}
|
||||
})
|
||||
},
|
||||
removeAlias (alias) {
|
||||
this.$store.state.api.backendInteractor.deleteAlias({ alias })
|
||||
removeAlias(alias) {
|
||||
this.$store.state.api.backendInteractor
|
||||
.deleteAlias({ alias })
|
||||
.then(() => this.fetchAliases())
|
||||
},
|
||||
addAlias () {
|
||||
this.$store.state.api.backendInteractor.addAlias({ alias: this.addAliasTarget })
|
||||
addAlias() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.addAlias({ alias: this.addAliasTarget })
|
||||
.then(() => {
|
||||
this.addedAlias = true
|
||||
this.addAliasError = false
|
||||
|
|
@ -136,8 +144,9 @@ const SecurityTab = {
|
|||
})
|
||||
.then(() => this.fetchAliases())
|
||||
},
|
||||
fetchAliases () {
|
||||
this.$store.state.api.backendInteractor.listAliases()
|
||||
fetchAliases() {
|
||||
this.$store.state.api.backendInteractor
|
||||
.listAliases()
|
||||
.then((res) => {
|
||||
this.aliases = res.aliases
|
||||
this.listAliasesError = false
|
||||
|
|
@ -146,16 +155,16 @@ const SecurityTab = {
|
|||
this.listAliasesError = error.error
|
||||
})
|
||||
},
|
||||
logout () {
|
||||
logout() {
|
||||
this.$store.dispatch('logout')
|
||||
this.$router.replace('/')
|
||||
},
|
||||
revokeToken (id) {
|
||||
revokeToken(id) {
|
||||
if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {
|
||||
useOAuthTokensStore().revokeToken(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export default SecurityTab
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
import { ref, reactive, computed, watch, provide, getCurrentInstance } from 'vue'
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
computed,
|
||||
watch,
|
||||
provide,
|
||||
getCurrentInstance,
|
||||
} from 'vue'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { get, set, unset, throttle } from 'lodash'
|
||||
|
||||
|
|
@ -19,19 +26,28 @@ import Preview from '../old_theme_tab/theme_preview.vue'
|
|||
|
||||
import VirtualDirectivesTab from './virtual_directives_tab.vue'
|
||||
|
||||
import { createStyleSheet, adoptStyleSheets } from 'src/services/style_setter/style_setter.js'
|
||||
import { init, findColor } from 'src/services/theme_data/theme_data_3.service.js'
|
||||
import {
|
||||
createStyleSheet,
|
||||
adoptStyleSheets,
|
||||
} from 'src/services/style_setter/style_setter.js'
|
||||
import {
|
||||
init,
|
||||
findColor,
|
||||
} from 'src/services/theme_data/theme_data_3.service.js'
|
||||
import { getCssRules } from 'src/services/theme_data/css_utils.js'
|
||||
import { serialize } from 'src/services/theme_data/iss_serializer.js'
|
||||
import { deserializeShadow, deserialize } from 'src/services/theme_data/iss_deserializer.js'
|
||||
import {
|
||||
deserializeShadow,
|
||||
deserialize,
|
||||
} from 'src/services/theme_data/iss_deserializer.js'
|
||||
import {
|
||||
rgb2hex,
|
||||
hex2rgb,
|
||||
getContrastRatio
|
||||
getContrastRatio,
|
||||
} from 'src/services/color_convert/color_convert.js'
|
||||
import {
|
||||
newImporter,
|
||||
newExporter
|
||||
newExporter,
|
||||
} from 'src/services/export_import/export_import.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -40,7 +56,7 @@ import {
|
|||
faFolderOpen,
|
||||
faFile,
|
||||
faArrowsRotate,
|
||||
faCheck
|
||||
faCheck,
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
// helper for debugging
|
||||
|
|
@ -48,15 +64,10 @@ import {
|
|||
const toValue = (x) => JSON.parse(JSON.stringify(x === undefined ? 'null' : x))
|
||||
|
||||
// helper to make states comparable
|
||||
const normalizeStates = (states) => ['normal', ...(states?.filter(x => x !== 'normal') || [])].join(':')
|
||||
const normalizeStates = (states) =>
|
||||
['normal', ...(states?.filter((x) => x !== 'normal') || [])].join(':')
|
||||
|
||||
library.add(
|
||||
faFile,
|
||||
faFloppyDisk,
|
||||
faFolderOpen,
|
||||
faArrowsRotate,
|
||||
faCheck
|
||||
)
|
||||
library.add(faFile, faFloppyDisk, faFolderOpen, faArrowsRotate, faCheck)
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
@ -74,18 +85,22 @@ export default {
|
|||
RoundnessInput,
|
||||
ContrastRatio,
|
||||
Preview,
|
||||
VirtualDirectivesTab
|
||||
VirtualDirectivesTab,
|
||||
},
|
||||
setup () {
|
||||
setup() {
|
||||
const exports = {}
|
||||
const interfaceStore = useInterfaceStore()
|
||||
// All rules that are made by editor
|
||||
const allEditedRules = ref(interfaceStore.styleDataUsed || {})
|
||||
const styleDataUsed = computed(() => interfaceStore.styleDataUsed)
|
||||
|
||||
watch([styleDataUsed], () => {
|
||||
onImport(interfaceStore.styleDataUsed)
|
||||
}, { once: true })
|
||||
watch(
|
||||
[styleDataUsed],
|
||||
() => {
|
||||
onImport(interfaceStore.styleDataUsed)
|
||||
},
|
||||
{ once: true },
|
||||
)
|
||||
|
||||
exports.isActive = computed(() => {
|
||||
const tabSwitcher = getCurrentInstance().parent.ctx
|
||||
|
|
@ -105,7 +120,7 @@ export default {
|
|||
` author: ${exports.author.value};`,
|
||||
` license: ${exports.license.value};`,
|
||||
` website: ${exports.website.value};`,
|
||||
'}'
|
||||
'}',
|
||||
].join('\n')
|
||||
})
|
||||
|
||||
|
|
@ -115,8 +130,8 @@ export default {
|
|||
name: exports.name.value,
|
||||
author: exports.author.value,
|
||||
license: exports.license.value,
|
||||
website: exports.website.value
|
||||
}
|
||||
website: exports.website.value,
|
||||
},
|
||||
}))
|
||||
|
||||
// ## Palette stuff
|
||||
|
|
@ -131,7 +146,7 @@ export default {
|
|||
cRed: '#FF0000',
|
||||
cBlue: '#0095ff',
|
||||
cGreen: '#0fa00f',
|
||||
cOrange: '#ffa500'
|
||||
cOrange: '#ffa500',
|
||||
},
|
||||
{
|
||||
name: 'light',
|
||||
|
|
@ -144,8 +159,8 @@ export default {
|
|||
cRed: '#d31014',
|
||||
cGreen: '#0fa00f',
|
||||
cOrange: '#ffa500',
|
||||
border: '#d8e6f9'
|
||||
}
|
||||
border: '#d8e6f9',
|
||||
},
|
||||
])
|
||||
exports.palettes = palettes
|
||||
|
||||
|
|
@ -163,12 +178,12 @@ export default {
|
|||
|
||||
const selectedPaletteId = ref(0)
|
||||
const selectedPalette = computed({
|
||||
get () {
|
||||
get() {
|
||||
return palettes[selectedPaletteId.value]
|
||||
},
|
||||
set (newPalette) {
|
||||
set(newPalette) {
|
||||
palettes[selectedPaletteId.value] = newPalette
|
||||
}
|
||||
},
|
||||
})
|
||||
exports.selectedPaletteId = selectedPaletteId
|
||||
exports.selectedPalette = selectedPalette
|
||||
|
|
@ -186,49 +201,50 @@ export default {
|
|||
cRed: '#FF0000',
|
||||
cBlue: '#0095ff',
|
||||
cGreen: '#0fa00f',
|
||||
cOrange: '#ffa500'
|
||||
cOrange: '#ffa500',
|
||||
})
|
||||
|
||||
// Raw format
|
||||
const palettesRule = computed(() => {
|
||||
return palettes.map(palette => {
|
||||
return palettes.map((palette) => {
|
||||
const { name, ...rest } = palette
|
||||
return {
|
||||
component: '@palette',
|
||||
variant: name,
|
||||
directives: Object
|
||||
.entries(rest)
|
||||
directives: Object.entries(rest)
|
||||
.filter(([k, v]) => v && k)
|
||||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
|
||||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}),
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// Text format
|
||||
const palettesOut = computed(() => {
|
||||
return palettes.map(({ name, ...palette }) => {
|
||||
const entries = Object
|
||||
.entries(palette)
|
||||
.filter(([k, v]) => v && k)
|
||||
.map(([slot, data]) => ` ${slot}: ${data};`)
|
||||
.join('\n')
|
||||
return palettes
|
||||
.map(({ name, ...palette }) => {
|
||||
const entries = Object.entries(palette)
|
||||
.filter(([k, v]) => v && k)
|
||||
.map(([slot, data]) => ` ${slot}: ${data};`)
|
||||
.join('\n')
|
||||
|
||||
return `@palette.${name} {\n${entries}\n}`
|
||||
}).join('\n\n')
|
||||
return `@palette.${name} {\n${entries}\n}`
|
||||
})
|
||||
.join('\n\n')
|
||||
})
|
||||
|
||||
// ## Components stuff
|
||||
// Getting existing components
|
||||
const componentsContext = import.meta.glob(
|
||||
['/src/**/*.style.js', '/src/**/*.style.json'],
|
||||
{ eager: true }
|
||||
{ eager: true },
|
||||
)
|
||||
const componentKeysAll = Object.keys(componentsContext)
|
||||
const componentsMap = new Map(
|
||||
componentKeysAll
|
||||
.map(
|
||||
key => [key, componentsContext[key].default]
|
||||
).filter(([, component]) => !component.virtual && !component.notEditable)
|
||||
.map((key) => [key, componentsContext[key].default])
|
||||
.filter(
|
||||
([, component]) => !component.virtual && !component.notEditable,
|
||||
),
|
||||
)
|
||||
exports.componentsMap = componentsMap
|
||||
const componentKeys = [...componentsMap.keys()]
|
||||
|
|
@ -238,16 +254,24 @@ export default {
|
|||
const selectedComponentKey = ref(componentsMap.keys().next().value)
|
||||
exports.selectedComponentKey = selectedComponentKey
|
||||
|
||||
const selectedComponent = computed(() => componentsMap.get(selectedComponentKey.value))
|
||||
const selectedComponent = computed(() =>
|
||||
componentsMap.get(selectedComponentKey.value),
|
||||
)
|
||||
const selectedComponentName = computed(() => selectedComponent.value.name)
|
||||
|
||||
// Selection basis
|
||||
exports.selectedComponentVariants = computed(() => {
|
||||
return Object.keys({ normal: null, ...(selectedComponent.value.variants || {}) })
|
||||
return Object.keys({
|
||||
normal: null,
|
||||
...(selectedComponent.value.variants || {}),
|
||||
})
|
||||
})
|
||||
exports.selectedComponentStates = computed(() => {
|
||||
const all = Object.keys({ normal: null, ...(selectedComponent.value.states || {}) })
|
||||
return all.filter(x => x !== 'normal')
|
||||
const all = Object.keys({
|
||||
normal: null,
|
||||
...(selectedComponent.value.states || {}),
|
||||
})
|
||||
return all.filter((x) => x !== 'normal')
|
||||
})
|
||||
|
||||
// selection
|
||||
|
|
@ -269,51 +293,49 @@ export default {
|
|||
selectedState.clear()
|
||||
}
|
||||
|
||||
watch(
|
||||
selectedComponentName,
|
||||
updateSelectedComponent
|
||||
)
|
||||
watch(selectedComponentName, updateSelectedComponent)
|
||||
|
||||
// ### Rules stuff aka meat and potatoes
|
||||
// The native structure of separate rules and the child -> parent
|
||||
// relation isn't very convenient for editor, we replace the array
|
||||
// and child -> parent structure with map and parent -> child structure
|
||||
const rulesToEditorFriendly = (rules, root = {}) => rules.reduce((acc, rule) => {
|
||||
const { parent: rParent, component: rComponent } = rule
|
||||
const parent = rParent ?? rule
|
||||
const hasChildren = !!rParent
|
||||
const child = hasChildren ? rule : null
|
||||
const rulesToEditorFriendly = (rules, root = {}) =>
|
||||
rules.reduce((acc, rule) => {
|
||||
const { parent: rParent, component: rComponent } = rule
|
||||
const parent = rParent ?? rule
|
||||
const hasChildren = !!rParent
|
||||
const child = hasChildren ? rule : null
|
||||
|
||||
const {
|
||||
component: pComponent,
|
||||
variant: pVariant = 'normal',
|
||||
state: pState = [] // no relation to Intel CPUs whatsoever
|
||||
} = parent
|
||||
|
||||
const pPath = `${hasChildren ? pComponent : rComponent}.${pVariant}.${normalizeStates(pState)}`
|
||||
|
||||
let output = get(acc, pPath)
|
||||
if (!output) {
|
||||
set(acc, pPath, {})
|
||||
output = get(acc, pPath)
|
||||
}
|
||||
|
||||
if (hasChildren) {
|
||||
output._children = output._children ?? {}
|
||||
const {
|
||||
component: cComponent,
|
||||
variant: cVariant = 'normal',
|
||||
state: cState = [],
|
||||
directives
|
||||
} = child
|
||||
component: pComponent,
|
||||
variant: pVariant = 'normal',
|
||||
state: pState = [], // no relation to Intel CPUs whatsoever
|
||||
} = parent
|
||||
|
||||
const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}`
|
||||
set(output._children, cPath, { directives })
|
||||
} else {
|
||||
output.directives = parent.directives
|
||||
}
|
||||
return acc
|
||||
}, root)
|
||||
const pPath = `${hasChildren ? pComponent : rComponent}.${pVariant}.${normalizeStates(pState)}`
|
||||
|
||||
let output = get(acc, pPath)
|
||||
if (!output) {
|
||||
set(acc, pPath, {})
|
||||
output = get(acc, pPath)
|
||||
}
|
||||
|
||||
if (hasChildren) {
|
||||
output._children = output._children ?? {}
|
||||
const {
|
||||
component: cComponent,
|
||||
variant: cVariant = 'normal',
|
||||
state: cState = [],
|
||||
directives,
|
||||
} = child
|
||||
|
||||
const cPath = `${cComponent}.${cVariant}.${normalizeStates(cState)}`
|
||||
set(output._children, cPath, { directives })
|
||||
} else {
|
||||
output.directives = parent.directives
|
||||
}
|
||||
return acc
|
||||
}, root)
|
||||
|
||||
const editorFriendlyFallbackStructure = computed(() => {
|
||||
const root = {}
|
||||
|
|
@ -323,7 +345,7 @@ export default {
|
|||
const { defaultRules, name } = componentValue
|
||||
rulesToEditorFriendly(
|
||||
defaultRules.map((rule) => ({ ...rule, component: name })),
|
||||
root
|
||||
root,
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -333,79 +355,111 @@ export default {
|
|||
// Checking whether component can support some "directives" which
|
||||
// are actually virtual subcomponents, i.e. Text, Link etc
|
||||
exports.componentHas = (subComponent) => {
|
||||
return !!selectedComponent.value.validInnerComponents?.find(x => x === subComponent)
|
||||
return !!selectedComponent.value.validInnerComponents?.find(
|
||||
(x) => x === subComponent,
|
||||
)
|
||||
}
|
||||
|
||||
// Path for lodash's get and set
|
||||
const getPath = (component, directive) => {
|
||||
const pathSuffix = component ? `._children.${component}.normal.normal` : ''
|
||||
const pathSuffix = component
|
||||
? `._children.${component}.normal.normal`
|
||||
: ''
|
||||
const path = `${selectedComponentName.value}.${selectedVariant.value}.${normalizeStates([...selectedState])}${pathSuffix}.directives.${directive}`
|
||||
return path
|
||||
}
|
||||
|
||||
// Templates for directives
|
||||
const isElementPresent = (component, directive, defaultValue = '') => computed({
|
||||
get () {
|
||||
return get(allEditedRules.value, getPath(component, directive)) != null
|
||||
},
|
||||
set (value) {
|
||||
if (value) {
|
||||
const fallback = get(
|
||||
editorFriendlyFallbackStructure.value,
|
||||
getPath(component, directive)
|
||||
const isElementPresent = (component, directive, defaultValue = '') =>
|
||||
computed({
|
||||
get() {
|
||||
return (
|
||||
get(allEditedRules.value, getPath(component, directive)) != null
|
||||
)
|
||||
set(allEditedRules.value, getPath(component, directive), fallback ?? defaultValue)
|
||||
} else {
|
||||
unset(allEditedRules.value, getPath(component, directive))
|
||||
}
|
||||
exports.updateOverallPreview()
|
||||
}
|
||||
})
|
||||
},
|
||||
set(value) {
|
||||
if (value) {
|
||||
const fallback = get(
|
||||
editorFriendlyFallbackStructure.value,
|
||||
getPath(component, directive),
|
||||
)
|
||||
set(
|
||||
allEditedRules.value,
|
||||
getPath(component, directive),
|
||||
fallback ?? defaultValue,
|
||||
)
|
||||
} else {
|
||||
unset(allEditedRules.value, getPath(component, directive))
|
||||
}
|
||||
exports.updateOverallPreview()
|
||||
},
|
||||
})
|
||||
|
||||
const getEditedElement = (component, directive, postProcess = x => x) => computed({
|
||||
get () {
|
||||
let usedRule
|
||||
const fallback = editorFriendlyFallbackStructure.value
|
||||
const real = allEditedRules.value
|
||||
const path = getPath(component, directive)
|
||||
const getEditedElement = (component, directive, postProcess = (x) => x) =>
|
||||
computed({
|
||||
get() {
|
||||
let usedRule
|
||||
const fallback = editorFriendlyFallbackStructure.value
|
||||
const real = allEditedRules.value
|
||||
const path = getPath(component, directive)
|
||||
|
||||
usedRule = get(real, path) // get real
|
||||
if (usedRule === '') {
|
||||
return usedRule
|
||||
}
|
||||
if (!usedRule) {
|
||||
usedRule = get(fallback, path)
|
||||
}
|
||||
usedRule = get(real, path) // get real
|
||||
if (usedRule === '') {
|
||||
return usedRule
|
||||
}
|
||||
if (!usedRule) {
|
||||
usedRule = get(fallback, path)
|
||||
}
|
||||
|
||||
return postProcess(usedRule)
|
||||
},
|
||||
set (value) {
|
||||
if (value != null) {
|
||||
set(allEditedRules.value, getPath(component, directive), value)
|
||||
} else {
|
||||
unset(allEditedRules.value, getPath(component, directive))
|
||||
}
|
||||
exports.updateOverallPreview()
|
||||
}
|
||||
})
|
||||
return postProcess(usedRule)
|
||||
},
|
||||
set(value) {
|
||||
if (value != null) {
|
||||
set(allEditedRules.value, getPath(component, directive), value)
|
||||
} else {
|
||||
unset(allEditedRules.value, getPath(component, directive))
|
||||
}
|
||||
exports.updateOverallPreview()
|
||||
},
|
||||
})
|
||||
|
||||
// All the editable stuff for the component
|
||||
exports.editedBackgroundColor = getEditedElement(null, 'background')
|
||||
exports.isBackgroundColorPresent = isElementPresent(null, 'background', '#FFFFFF')
|
||||
exports.isBackgroundColorPresent = isElementPresent(
|
||||
null,
|
||||
'background',
|
||||
'#FFFFFF',
|
||||
)
|
||||
exports.editedOpacity = getEditedElement(null, 'opacity')
|
||||
exports.isOpacityPresent = isElementPresent(null, 'opacity', 1)
|
||||
exports.editedRoundness = getEditedElement(null, 'roundness')
|
||||
exports.isRoundnessPresent = isElementPresent(null, 'roundness', '0')
|
||||
exports.editedTextColor = getEditedElement('Text', 'textColor')
|
||||
exports.isTextColorPresent = isElementPresent('Text', 'textColor', '#000000')
|
||||
exports.isTextColorPresent = isElementPresent(
|
||||
'Text',
|
||||
'textColor',
|
||||
'#000000',
|
||||
)
|
||||
exports.editedTextAuto = getEditedElement('Text', 'textAuto')
|
||||
exports.isTextAutoPresent = isElementPresent('Text', 'textAuto', '#000000')
|
||||
exports.editedLinkColor = getEditedElement('Link', 'textColor')
|
||||
exports.isLinkColorPresent = isElementPresent('Link', 'textColor', '#000080')
|
||||
exports.isLinkColorPresent = isElementPresent(
|
||||
'Link',
|
||||
'textColor',
|
||||
'#000080',
|
||||
)
|
||||
exports.editedIconColor = getEditedElement('Icon', 'textColor')
|
||||
exports.isIconColorPresent = isElementPresent('Icon', 'textColor', '#909090')
|
||||
exports.isIconColorPresent = isElementPresent(
|
||||
'Icon',
|
||||
'textColor',
|
||||
'#909090',
|
||||
)
|
||||
exports.editedBorderColor = getEditedElement('Border', 'textColor')
|
||||
exports.isBorderColorPresent = isElementPresent('Border', 'textColor', '#909090')
|
||||
exports.isBorderColorPresent = isElementPresent(
|
||||
'Border',
|
||||
'textColor',
|
||||
'#909090',
|
||||
)
|
||||
|
||||
const getContrast = (bg, text) => {
|
||||
try {
|
||||
|
|
@ -422,7 +476,7 @@ export default {
|
|||
aaa: ratio >= 7,
|
||||
// same but for 18pt+ texts
|
||||
laa: ratio >= 3,
|
||||
laaa: ratio >= 4.5
|
||||
laaa: ratio >= 4.5,
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Failure computing contrast', e)
|
||||
|
|
@ -431,7 +485,7 @@ export default {
|
|||
}
|
||||
|
||||
const normalizeShadows = (shadows) => {
|
||||
return shadows?.map(shadow => {
|
||||
return shadows?.map((shadow) => {
|
||||
if (typeof shadow === 'object') {
|
||||
return shadow
|
||||
}
|
||||
|
|
@ -455,7 +509,8 @@ export default {
|
|||
const editedSubShadowId = ref(null)
|
||||
exports.editedSubShadowId = editedSubShadowId
|
||||
const editedSubShadow = computed(() => {
|
||||
if (editedShadow.value == null || editedSubShadowId.value == null) return null
|
||||
if (editedShadow.value == null || editedSubShadowId.value == null)
|
||||
return null
|
||||
return editedShadow.value[editedSubShadowId.value]
|
||||
})
|
||||
exports.editedSubShadow = editedSubShadow
|
||||
|
|
@ -473,7 +528,7 @@ export default {
|
|||
|
||||
newEditedShadow[editedSubShadowId.value] = {
|
||||
...newEditedShadow[editedSubShadowId.value],
|
||||
[axis]: value
|
||||
[axis]: value,
|
||||
}
|
||||
|
||||
editedShadow.value = newEditedShadow
|
||||
|
|
@ -513,12 +568,12 @@ export default {
|
|||
component,
|
||||
variant,
|
||||
state,
|
||||
directives: stateData.directives || {}
|
||||
directives: stateData.directives || {},
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
result.parent = {
|
||||
component: parent
|
||||
component: parent,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -526,13 +581,15 @@ export default {
|
|||
|
||||
// Currently we only support single depth for simplicity's sake
|
||||
if (!parent) {
|
||||
Object.entries(stateData._children || {}).forEach(([cName, child]) => convert(cName, child, component))
|
||||
Object.entries(stateData._children || {}).forEach(
|
||||
([cName, child]) => convert(cName, child, component),
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
[...componentsMap.values()].forEach(({ name }) => {
|
||||
;[...componentsMap.values()].forEach(({ name }) => {
|
||||
convert(name, allEditedRules.value[name])
|
||||
})
|
||||
|
||||
|
|
@ -540,21 +597,20 @@ export default {
|
|||
})
|
||||
|
||||
const allCustomVirtualDirectives = [...componentsMap.values()]
|
||||
.map(c => {
|
||||
return c
|
||||
.defaultRules
|
||||
.filter(c => c.component === 'Root')
|
||||
.map(x => Object.entries(x.directives))
|
||||
.map((c) => {
|
||||
return c.defaultRules
|
||||
.filter((c) => c.component === 'Root')
|
||||
.map((x) => Object.entries(x.directives))
|
||||
.flat()
|
||||
})
|
||||
.filter(x => x)
|
||||
.filter((x) => x)
|
||||
.flat()
|
||||
.map(([name, value]) => {
|
||||
const [valType, valVal] = value.split('|')
|
||||
return {
|
||||
name: name.substring(2),
|
||||
valType: valType?.trim(),
|
||||
value: valVal?.trim()
|
||||
value: valVal?.trim(),
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -568,8 +624,11 @@ export default {
|
|||
const virtualDirectivesRule = computed(() => ({
|
||||
component: 'Root',
|
||||
directives: Object.fromEntries(
|
||||
virtualDirectives.value.map(vd => [`--${vd.name}`, `${vd.valType} | ${vd.value}`])
|
||||
)
|
||||
virtualDirectives.value.map((vd) => [
|
||||
`--${vd.name}`,
|
||||
`${vd.valType} | ${vd.value}`,
|
||||
]),
|
||||
),
|
||||
}))
|
||||
|
||||
// Text format
|
||||
|
|
@ -577,16 +636,19 @@ export default {
|
|||
return [
|
||||
'Root {',
|
||||
...virtualDirectives.value
|
||||
.filter(vd => vd.name && vd.valType && vd.value)
|
||||
.map(vd => ` --${vd.name}: ${vd.valType} | ${vd.value};`),
|
||||
'}'
|
||||
.filter((vd) => vd.name && vd.valType && vd.value)
|
||||
.map((vd) => ` --${vd.name}: ${vd.valType} | ${vd.value};`),
|
||||
'}',
|
||||
].join('\n')
|
||||
})
|
||||
|
||||
exports.computeColor = (color) => {
|
||||
let computedColor
|
||||
try {
|
||||
computedColor = findColor(color, { dynamicVars: dynamicVars.value, staticVars: staticVars.value })
|
||||
computedColor = findColor(color, {
|
||||
dynamicVars: dynamicVars.value,
|
||||
staticVars: staticVars.value,
|
||||
})
|
||||
if (computedColor) {
|
||||
return rgb2hex(computedColor)
|
||||
}
|
||||
|
|
@ -600,7 +662,7 @@ export default {
|
|||
exports.contrast = computed(() => {
|
||||
return getContrast(
|
||||
exports.computeColor(previewColors.value.background),
|
||||
exports.computeColor(previewColors.value.text)
|
||||
exports.computeColor(previewColors.value.text),
|
||||
)
|
||||
})
|
||||
|
||||
|
|
@ -609,30 +671,38 @@ export default {
|
|||
filename: () => exports.name.value ?? 'pleroma_theme',
|
||||
mime: 'text/plain',
|
||||
extension: 'iss',
|
||||
getExportedObject: () => exportStyleData.value
|
||||
getExportedObject: () => exportStyleData.value,
|
||||
})
|
||||
|
||||
const onImport = parsed => {
|
||||
const editorComponents = parsed.filter(x => x.component.startsWith('@'))
|
||||
const rootComponent = parsed.find(x => x.component === 'Root')
|
||||
const rules = parsed.filter(x => !x.component.startsWith('@') && x.component !== 'Root')
|
||||
const metaIn = editorComponents.find(x => x.component === '@meta').directives
|
||||
const palettesIn = editorComponents.filter(x => x.component === '@palette')
|
||||
const onImport = (parsed) => {
|
||||
const editorComponents = parsed.filter((x) => x.component.startsWith('@'))
|
||||
const rootComponent = parsed.find((x) => x.component === 'Root')
|
||||
const rules = parsed.filter(
|
||||
(x) => !x.component.startsWith('@') && x.component !== 'Root',
|
||||
)
|
||||
const metaIn = editorComponents.find(
|
||||
(x) => x.component === '@meta',
|
||||
).directives
|
||||
const palettesIn = editorComponents.filter(
|
||||
(x) => x.component === '@palette',
|
||||
)
|
||||
|
||||
exports.name.value = metaIn.name
|
||||
exports.license.value = metaIn.license
|
||||
exports.author.value = metaIn.author
|
||||
exports.website.value = metaIn.website
|
||||
|
||||
const newVirtualDirectives = Object
|
||||
.entries(rootComponent.directives)
|
||||
.map(([name, value]) => {
|
||||
const [valType, valVal] = value.split('|').map(x => x.trim())
|
||||
const newVirtualDirectives = Object.entries(rootComponent.directives).map(
|
||||
([name, value]) => {
|
||||
const [valType, valVal] = value.split('|').map((x) => x.trim())
|
||||
return { name: name.substring(2), valType, value: valVal }
|
||||
})
|
||||
},
|
||||
)
|
||||
virtualDirectives.value = newVirtualDirectives
|
||||
|
||||
onPalettesUpdate(palettesIn.map(x => ({ name: x.variant, ...x.directives })))
|
||||
onPalettesUpdate(
|
||||
palettesIn.map((x) => ({ name: x.variant, ...x.directives })),
|
||||
)
|
||||
|
||||
allEditedRules.value = rulesToEditorFriendly(rules)
|
||||
|
||||
|
|
@ -641,12 +711,17 @@ export default {
|
|||
|
||||
const styleImporter = newImporter({
|
||||
accept: '.iss',
|
||||
parser (string) { return deserialize(string) },
|
||||
onImportFailure (result) {
|
||||
console.error('Failure importing style:', result)
|
||||
useInterfaceStore().pushGlobalNotice({ messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||
parser(string) {
|
||||
return deserialize(string)
|
||||
},
|
||||
onImport
|
||||
onImportFailure(result) {
|
||||
console.error('Failure importing style:', result)
|
||||
useInterfaceStore().pushGlobalNotice({
|
||||
messageKey: 'settings.invalid_theme_imported',
|
||||
level: 'error',
|
||||
})
|
||||
},
|
||||
onImport,
|
||||
})
|
||||
|
||||
// Raw format
|
||||
|
|
@ -654,7 +729,7 @@ export default {
|
|||
metaRule.value,
|
||||
...palettesRule.value,
|
||||
virtualDirectivesRule.value,
|
||||
...editorFriendlyToOriginal.value
|
||||
...editorFriendlyToOriginal.value,
|
||||
])
|
||||
|
||||
// Text format
|
||||
|
|
@ -663,7 +738,7 @@ export default {
|
|||
metaOut.value,
|
||||
palettesOut.value,
|
||||
virtualDirectivesOut.value,
|
||||
serialize(editorFriendlyToOriginal.value)
|
||||
serialize(editorFriendlyToOriginal.value),
|
||||
].join('\n\n')
|
||||
})
|
||||
|
||||
|
|
@ -689,7 +764,9 @@ export default {
|
|||
watch([overallPreviewRules], () => {
|
||||
let css = null
|
||||
try {
|
||||
css = getCssRules(overallPreviewRules.value).map(r => r.replace('html', '&'))
|
||||
css = getCssRules(overallPreviewRules.value).map((r) =>
|
||||
r.replace('html', '&'),
|
||||
)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return
|
||||
|
|
@ -698,11 +775,9 @@ export default {
|
|||
const sheet = createStyleSheet('style-tab-overall-preview', 90)
|
||||
|
||||
sheet.clear()
|
||||
sheet.addRule([
|
||||
'#edited-style-preview {\n',
|
||||
css.join('\n'),
|
||||
'\n}'
|
||||
].join(''))
|
||||
sheet.addRule(
|
||||
['#edited-style-preview {\n', css.join('\n'), '\n}'].join(''),
|
||||
)
|
||||
sheet.ready = true
|
||||
adoptStyleSheets()
|
||||
})
|
||||
|
|
@ -715,15 +790,14 @@ export default {
|
|||
{
|
||||
component: 'Root',
|
||||
directives: Object.fromEntries(
|
||||
Object
|
||||
.entries(selectedPalette.value)
|
||||
Object.entries(selectedPalette.value)
|
||||
.filter(([k, v]) => k && v && k !== 'name')
|
||||
.map(([k, v]) => [`--${k}`, `color | ${v}`])
|
||||
)
|
||||
}
|
||||
.map(([k, v]) => [`--${k}`, `color | ${v}`]),
|
||||
),
|
||||
},
|
||||
],
|
||||
ultimateBackgroundColor: '#000000',
|
||||
debug: true
|
||||
debug: true,
|
||||
}).eager
|
||||
} catch (e) {
|
||||
console.error('Could not compile preview theme', e)
|
||||
|
|
@ -733,30 +807,32 @@ export default {
|
|||
//
|
||||
// Apart from "hover" we can't really show how component looks like in
|
||||
// certain states, so we have to fake them.
|
||||
const simulatePseudoSelectors = (css, prefix) => css
|
||||
.replace(prefix, '.preview-block')
|
||||
.replace(':active', '.preview-active')
|
||||
.replace(':hover', '.preview-hover')
|
||||
.replace(':active', '.preview-active')
|
||||
.replace(':focus', '.preview-focus')
|
||||
.replace(':focus-within', '.preview-focus-within')
|
||||
.replace(':disabled', '.preview-disabled')
|
||||
const simulatePseudoSelectors = (css, prefix) =>
|
||||
css
|
||||
.replace(prefix, '.preview-block')
|
||||
.replace(':active', '.preview-active')
|
||||
.replace(':hover', '.preview-hover')
|
||||
.replace(':active', '.preview-active')
|
||||
.replace(':focus', '.preview-focus')
|
||||
.replace(':focus-within', '.preview-focus-within')
|
||||
.replace(':disabled', '.preview-disabled')
|
||||
|
||||
const previewRules = computed(() => {
|
||||
const filtered = overallPreviewRules.value.filter(r => {
|
||||
const filtered = overallPreviewRules.value.filter((r) => {
|
||||
const componentMatch = r.component === selectedComponentName.value
|
||||
const parentComponentMatch = r.parent?.component === selectedComponentName.value
|
||||
const parentComponentMatch =
|
||||
r.parent?.component === selectedComponentName.value
|
||||
if (!componentMatch && !parentComponentMatch) return false
|
||||
const rule = parentComponentMatch ? r.parent : r
|
||||
if (rule.component !== selectedComponentName.value) return false
|
||||
if (rule.variant !== selectedVariant.value) return false
|
||||
const ruleState = new Set(rule.state.filter(x => x !== 'normal'))
|
||||
const differenceA = [...ruleState].filter(x => !selectedState.has(x))
|
||||
const differenceB = [...selectedState].filter(x => !ruleState.has(x))
|
||||
return (differenceA.length + differenceB.length) === 0
|
||||
const ruleState = new Set(rule.state.filter((x) => x !== 'normal'))
|
||||
const differenceA = [...ruleState].filter((x) => !selectedState.has(x))
|
||||
const differenceB = [...selectedState].filter((x) => !ruleState.has(x))
|
||||
return differenceA.length + differenceB.length === 0
|
||||
})
|
||||
const sorted = [...filtered]
|
||||
.filter(x => x.component === selectedComponentName.value)
|
||||
.filter((x) => x.component === selectedComponentName.value)
|
||||
.sort((a, b) => {
|
||||
const aSelectorLength = a.selector.split(/ /g).length
|
||||
const bSelectorLength = b.selector.split(/ /g).length
|
||||
|
|
@ -765,27 +841,32 @@ export default {
|
|||
|
||||
const prefix = sorted[0].selector
|
||||
|
||||
return filtered.filter(x => x.selector.startsWith(prefix))
|
||||
return filtered.filter((x) => x.selector.startsWith(prefix))
|
||||
})
|
||||
|
||||
exports.previewClass = computed(() => {
|
||||
const selectors = []
|
||||
if (!!selectedComponent.value.variants?.normal || selectedVariant.value !== 'normal') {
|
||||
if (
|
||||
!!selectedComponent.value.variants?.normal ||
|
||||
selectedVariant.value !== 'normal'
|
||||
) {
|
||||
selectors.push(selectedComponent.value.variants[selectedVariant.value])
|
||||
}
|
||||
if (selectedState.size > 0) {
|
||||
selectedState.forEach(state => {
|
||||
selectedState.forEach((state) => {
|
||||
const original = selectedComponent.value.states[state]
|
||||
selectors.push(simulatePseudoSelectors(original))
|
||||
})
|
||||
}
|
||||
return selectors.map(x => x.substring(1)).join('')
|
||||
return selectors.map((x) => x.substring(1)).join('')
|
||||
})
|
||||
|
||||
exports.previewCss = computed(() => {
|
||||
try {
|
||||
const prefix = previewRules.value[0].selector
|
||||
const scoped = getCssRules(previewRules.value).map(x => simulatePseudoSelectors(x, prefix))
|
||||
const scoped = getCssRules(previewRules.value).map((x) =>
|
||||
simulatePseudoSelectors(x, prefix),
|
||||
)
|
||||
return scoped.join('\n')
|
||||
} catch (e) {
|
||||
console.error('Invalid ruleset', e)
|
||||
|
|
@ -798,7 +879,7 @@ export default {
|
|||
})
|
||||
|
||||
const staticVars = computed(() => {
|
||||
const rootComponent = overallPreviewRules.value.find(r => {
|
||||
const rootComponent = overallPreviewRules.value.find((r) => {
|
||||
return r.component === 'Root'
|
||||
})
|
||||
const rootDirectivesEntries = Object.entries(rootComponent.directives)
|
||||
|
|
@ -807,7 +888,10 @@ export default {
|
|||
.filter(([k, v]) => k.startsWith('--') && v.startsWith('color | '))
|
||||
.map(([k, v]) => [k.substring(2), v.substring('color | '.length)])
|
||||
.forEach(([k, v]) => {
|
||||
directives[k] = findColor(v, { dynamicVars: {}, staticVars: directives })
|
||||
directives[k] = findColor(v, {
|
||||
dynamicVars: {},
|
||||
staticVars: directives,
|
||||
})
|
||||
})
|
||||
return directives
|
||||
})
|
||||
|
|
@ -816,13 +900,18 @@ export default {
|
|||
|
||||
const previewColors = computed(() => {
|
||||
const stacked = dynamicVars.value.stacked
|
||||
const background = typeof stacked === 'string' ? stacked : rgb2hex(stacked)
|
||||
const background =
|
||||
typeof stacked === 'string' ? stacked : rgb2hex(stacked)
|
||||
return {
|
||||
text: previewRules.value.find(r => r.component === 'Text')?.virtualDirectives['--text'],
|
||||
link: previewRules.value.find(r => r.component === 'Link')?.virtualDirectives['--link'],
|
||||
border: previewRules.value.find(r => r.component === 'Border')?.virtualDirectives['--border'],
|
||||
icon: previewRules.value.find(r => r.component === 'Icon')?.virtualDirectives['--icon'],
|
||||
background
|
||||
text: previewRules.value.find((r) => r.component === 'Text')
|
||||
?.virtualDirectives['--text'],
|
||||
link: previewRules.value.find((r) => r.component === 'Link')
|
||||
?.virtualDirectives['--link'],
|
||||
border: previewRules.value.find((r) => r.component === 'Border')
|
||||
?.virtualDirectives['--border'],
|
||||
icon: previewRules.value.find((r) => r.component === 'Icon')
|
||||
?.virtualDirectives['--icon'],
|
||||
background,
|
||||
}
|
||||
})
|
||||
exports.previewColors = previewColors
|
||||
|
|
@ -836,11 +925,11 @@ export default {
|
|||
palettes,
|
||||
selectedPalette,
|
||||
selectedState,
|
||||
selectedVariant
|
||||
selectedVariant,
|
||||
],
|
||||
updateOverallPreview
|
||||
updateOverallPreview,
|
||||
)
|
||||
|
||||
return exports
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@ export default {
|
|||
Select,
|
||||
SelectMotion,
|
||||
ShadowControl,
|
||||
ColorInput
|
||||
ColorInput,
|
||||
},
|
||||
props: ['modelValue'],
|
||||
emits: ['update:modelValue'],
|
||||
setup (props, context) {
|
||||
setup(props, context) {
|
||||
const exports = {}
|
||||
const emit = context.emit
|
||||
|
||||
|
|
@ -32,23 +32,23 @@ export default {
|
|||
exports.selectedVirtualDirectiveId = selectedVirtualDirectiveId
|
||||
|
||||
const selectedVirtualDirective = computed({
|
||||
get () {
|
||||
get() {
|
||||
return props.modelValue[selectedVirtualDirectiveId.value]
|
||||
},
|
||||
set (value) {
|
||||
set(value) {
|
||||
const newVD = [...props.modelValue]
|
||||
newVD[selectedVirtualDirectiveId.value] = value
|
||||
|
||||
emit('update:modelValue', newVD)
|
||||
}
|
||||
},
|
||||
})
|
||||
exports.selectedVirtualDirective = selectedVirtualDirective
|
||||
|
||||
exports.selectedVirtualDirectiveValType = computed({
|
||||
get () {
|
||||
get() {
|
||||
return props.modelValue[selectedVirtualDirectiveId.value].valType
|
||||
},
|
||||
set (value) {
|
||||
set(value) {
|
||||
const newValType = value
|
||||
let newValue
|
||||
switch (value) {
|
||||
|
|
@ -65,9 +65,9 @@ export default {
|
|||
props.modelValue[selectedVirtualDirectiveId.value] = {
|
||||
name: newName,
|
||||
value: newValue,
|
||||
valType: newValType
|
||||
valType: newValType,
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
const draftVirtualDirectiveValid = ref(true)
|
||||
|
|
@ -83,7 +83,9 @@ export default {
|
|||
if (Array.isArray(directive.value)) {
|
||||
draftVirtualDirective.value = normalizeShadows(directive.value)
|
||||
} else {
|
||||
const splitShadow = directive.value.split(/,/g).map(x => x.trim())
|
||||
const splitShadow = directive.value
|
||||
.split(/,/g)
|
||||
.map((x) => x.trim())
|
||||
draftVirtualDirective.value = normalizeShadows(splitShadow)
|
||||
}
|
||||
break
|
||||
|
|
@ -96,7 +98,7 @@ export default {
|
|||
break
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
|
|
@ -106,11 +108,12 @@ export default {
|
|||
switch (selectedVirtualDirective.value.valType) {
|
||||
case 'shadow': {
|
||||
props.modelValue[selectedVirtualDirectiveId.value].value =
|
||||
directive.map(x => serializeShadow(x)).join(', ')
|
||||
directive.map((x) => serializeShadow(x)).join(', ')
|
||||
break
|
||||
}
|
||||
default:
|
||||
props.modelValue[selectedVirtualDirectiveId.value].value = directive
|
||||
props.modelValue[selectedVirtualDirectiveId.value].value =
|
||||
directive
|
||||
}
|
||||
draftVirtualDirectiveValid.value = true
|
||||
} catch (e) {
|
||||
|
|
@ -118,15 +121,15 @@ export default {
|
|||
draftVirtualDirectiveValid.value = false
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
exports.getNewVirtualDirective = () => ({
|
||||
name: 'newDirective',
|
||||
valType: 'generic',
|
||||
value: 'foobar'
|
||||
value: 'foobar',
|
||||
})
|
||||
|
||||
return exports
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue