biome format --write
This commit is contained in:
parent
8372348148
commit
9262e803ec
415 changed files with 54076 additions and 17419 deletions
|
|
@ -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