aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/export_import/export_import.vue75
-rw-r--r--src/components/style_switcher/preview.vue78
-rw-r--r--src/components/style_switcher/style_switcher.js97
-rw-r--r--src/components/style_switcher/style_switcher.vue93
-rw-r--r--src/modules/config.js4
5 files changed, 200 insertions, 147 deletions
diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue
new file mode 100644
index 00000000..9914d54a
--- /dev/null
+++ b/src/components/export_import/export_import.vue
@@ -0,0 +1,75 @@
+<template>
+<div class="import-export">
+ <button class="btn" @click="exportData">{{ exportLabel }}</button>
+ <button class="btn" @click="importData">{{ importLabel }}</button>
+ <p v-if="importFailed" class="import-warning">{{ importFailedText }}</p>
+</div>
+</template>
+
+<script>
+export default {
+ props: [
+ 'exportObject',
+ 'importLabel',
+ 'exportLabel',
+ 'importFailedText',
+ 'validator',
+ 'onImport',
+ 'onImportFailure'
+ ],
+ data () {
+ return {
+ importFailed: false
+ }
+ },
+ methods: {
+ exportData () {
+ const stringified = JSON.stringify(this.exportObject) // Pretty-print and indent with 2 spaces
+
+ // Create an invisible link with a data url and simulate a click
+ const e = document.createElement('a')
+ e.setAttribute('download', 'pleroma_theme.json')
+ e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified))
+ e.style.display = 'none'
+
+ document.body.appendChild(e)
+ e.click()
+ document.body.removeChild(e)
+ },
+ importData () {
+ this.importFailed = false
+ const filePicker = document.createElement('input')
+ filePicker.setAttribute('type', 'file')
+ filePicker.setAttribute('accept', '.json')
+
+ filePicker.addEventListener('change', event => {
+ if (event.target.files[0]) {
+ // eslint-disable-next-line no-undef
+ const reader = new FileReader()
+ reader.onload = ({target}) => {
+ try {
+ const parsed = JSON.parse(target.result)
+ const valid = this.validator(parsed)
+ if (valid) {
+ this.onImport(parsed)
+ } else {
+ this.importFailed = true
+ // this.onImportFailure(valid)
+ }
+ } catch (e) {
+ // This will happen both if there is a JSON syntax error or the theme is missing components
+ this.importFailed = true
+ // this.onImportFailure(e)
+ }
+ }
+ reader.readAsText(event.target.files[0])
+ }
+ })
+
+ document.body.appendChild(filePicker)
+ filePicker.click()
+ document.body.removeChild(filePicker)
+ }
+ }
+}
+</script>
diff --git a/src/components/style_switcher/preview.vue b/src/components/style_switcher/preview.vue
new file mode 100644
index 00000000..09a136e9
--- /dev/null
+++ b/src/components/style_switcher/preview.vue
@@ -0,0 +1,78 @@
+<template>
+<div class="panel dummy">
+ <div class="panel-heading">
+ <div class="title">
+ {{$t('settings.style.preview.header')}}
+ <span class="badge badge-notification">
+ 99
+ </span>
+ </div>
+ <span class="faint">
+ {{$t('settings.style.preview.header_faint')}}
+ </span>
+ <span class="alert error">
+ {{$t('settings.style.preview.error')}}
+ </span>
+ <button class="btn">
+ {{$t('settings.style.preview.button')}}
+ </button>
+ </div>
+ <div class="panel-body theme-preview-content">
+ <div class="post">
+ <div class="avatar">
+ ( ͡° ͜ʖ ͡°)
+ </div>
+ <div class="content">
+ <h4>
+ {{$t('settings.style.preview.content')}}
+ </h4>
+
+ <i18n path="settings.style.preview.text">
+ <code style="font-family: var(--postCodeFont)">
+ {{$t('settings.style.preview.mono')}}
+ </code>
+ <a style="color: var(--link)">
+ {{$t('settings.style.preview.link')}}
+ </a>
+ </i18n>
+
+ <div class="icons">
+ <i style="color: var(--cBlue)" class="icon-reply"/>
+ <i style="color: var(--cGreen)" class="icon-retweet"/>
+ <i style="color: var(--cOrange)" class="icon-star"/>
+ <i style="color: var(--cRed)" class="icon-cancel"/>
+ </div>
+ </div>
+ </div>
+
+ <div class="after-post">
+ <div class="avatar-alt">
+ :^)
+ </div>
+ <div class="content">
+ <i18n path="settings.style.preview.fine_print" tag="span" class="faint">
+ <a style="color: var(--faintLink)">
+ {{$t('settings.style.preview.faint_link')}}
+ </a>
+ </i18n>
+ </div>
+ </div>
+ <div class="separator"></div>
+
+ <span class="alert error">
+ {{$t('settings.style.preview.error')}}
+ </span>
+ <input :value="$t('settings.style.preview.input')" type="text">
+
+ <div class="actions">
+ <span class="checkbox">
+ <input checked="very yes" type="checkbox" id="preview_checkbox">
+ <label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label>
+ </span>
+ <button class="btn">
+ {{$t('settings.style.preview.button')}}
+ </button>
+ </div>
+ </div>
+</div>
+</template>
diff --git a/src/components/style_switcher/style_switcher.js b/src/components/style_switcher/style_switcher.js
index adcbee25..50cd1e6f 100644
--- a/src/components/style_switcher/style_switcher.js
+++ b/src/components/style_switcher/style_switcher.js
@@ -8,6 +8,8 @@ import ShadowControl from '../shadow_control/shadow_control.vue'
import FontControl from '../font_control/font_control.vue'
import ContrastRatio from '../contrast_ratio/contrast_ratio.vue'
import TabSwitcher from '../tab_switcher/tab_switcher.jsx'
+import Preview from './preview.vue'
+import ExportImport from '../export_import/export_import.vue'
// List of color values used in v1
const v1OnlyNames = [
@@ -26,7 +28,6 @@ export default {
return {
availableStyles: [],
selected: this.$store.state.config.theme,
- invalidThemeImported: false,
previewShadows: {},
previewColors: {},
@@ -293,20 +294,11 @@ export default {
},
themeValid () {
return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid
- }
- },
- components: {
- ColorInput,
- OpacityInput,
- RangeInput,
- ContrastRatio,
- ShadowControl,
- FontControl,
- TabSwitcher
- },
- methods: {
- exportCurrentTheme () {
+ },
+ exportedTheme () {
const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepColors && !this.keepOpacity && !this.keepRoundness
+
+ // TODO change into delete-less version.
const theme = {
shadows: this.shadowsLocal,
fonts: this.fontsLocal,
@@ -331,57 +323,24 @@ export default {
delete theme.radii
}
- const stringified = JSON.stringify({
+ return {
// To separate from other random JSON files and possible future theme formats
_pleroma_theme_version: 2, theme
- }, null, 2) // Pretty-print and indent with 2 spaces
-
- // Create an invisible link with a data url and simulate a click
- const e = document.createElement('a')
- e.setAttribute('download', 'pleroma_theme.json')
- e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified))
- e.style.display = 'none'
-
- document.body.appendChild(e)
- e.click()
- document.body.removeChild(e)
- },
-
- importTheme () {
- this.invalidThemeImported = false
- const filePicker = document.createElement('input')
- filePicker.setAttribute('type', 'file')
- filePicker.setAttribute('accept', '.json')
-
- filePicker.addEventListener('change', event => {
- if (event.target.files[0]) {
- // eslint-disable-next-line no-undef
- const reader = new FileReader()
- reader.onload = ({target}) => {
- try {
- const parsed = JSON.parse(target.result)
- if (parsed._pleroma_theme_version === 1) {
- this.normalizeLocalState(parsed, 1)
- } else if (parsed._pleroma_theme_version === 2) {
- this.normalizeLocalState(parsed.theme, 2)
- } else {
- // A theme from the future, spooky
- this.invalidThemeImported = true
- }
- } catch (e) {
- // This will happen both if there is a JSON syntax error or the theme is missing components
- this.invalidThemeImported = true
- }
- }
- reader.readAsText(event.target.files[0])
- }
- })
-
- document.body.appendChild(filePicker)
- filePicker.click()
- document.body.removeChild(filePicker)
- },
-
+ }
+ }
+ },
+ components: {
+ ColorInput,
+ OpacityInput,
+ RangeInput,
+ ContrastRatio,
+ ShadowControl,
+ FontControl,
+ TabSwitcher,
+ Preview,
+ ExportImport
+ },
+ methods: {
setCustomTheme () {
this.$store.dispatch('setOption', {
name: 'customTheme',
@@ -394,7 +353,17 @@ export default {
}
})
},
-
+ onImport (parsed) {
+ if (parsed._pleroma_theme_version === 1) {
+ this.normalizeLocalState(parsed, 1)
+ } else if (parsed._pleroma_theme_version === 2) {
+ this.normalizeLocalState(parsed.theme, 2)
+ }
+ },
+ importValidator (parsed) {
+ const version = parsed._pleroma_theme_version
+ return version >= 1 || version <= 2
+ },
clearAll () {
const state = this.$store.state.config.customTheme
const version = state.colors ? 2 : 'l1'
diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/style_switcher/style_switcher.vue
index 9de60f7b..730bfef0 100644
--- a/src/components/style_switcher/style_switcher.vue
+++ b/src/components/style_switcher/style_switcher.vue
@@ -18,11 +18,14 @@
<i class="icon-down-open"/>
</label>
</div>
- <div class="import-export">
- <button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button>
- <button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button>
- <p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p>
- </div>
+ <export-import
+ :exportObject='exportedTheme'
+ :exportLabel='$t("settings.export_theme")'
+ :importLabel='$t("settings.import_theme")'
+ :importFailedText='$t("settings.invalid_theme_imported")'
+ :onImport='onImport'
+ :validator='importValidator'
+ />
</div>
<div class="save-load-options">
<span>
@@ -58,82 +61,7 @@
</div>
<div class="preview-container">
- <div class="panel dummy" :style="previewRules">
- <div class="panel-heading">
- <div class="title">
- {{$t('settings.style.preview.header')}}
- <span class="badge badge-notification">
- 99
- </span>
- </div>
- <span class="faint">
- {{$t('settings.style.preview.header_faint')}}
- </span>
- <span class="alert error">
- {{$t('settings.style.preview.error')}}
- </span>
- <button class="btn">
- {{$t('settings.style.preview.button')}}
- </button>
- </div>
- <div class="panel-body theme-preview-content">
- <div class="post">
- <div class="avatar">
- ( ͡° ͜ʖ ͡°)
- </div>
- <div class="content">
- <h4>
- {{$t('settings.style.preview.content')}}
- </h4>
-
- <i18n path="settings.style.preview.text">
- <code style="font-family: var(--postCodeFont)">
- {{$t('settings.style.preview.mono')}}
- </code>
- <a style="color: var(--link)">
- {{$t('settings.style.preview.link')}}
- </a>
- </i18n>
-
- <div class="icons">
- <i style="color: var(--cBlue)" class="icon-reply"/>
- <i style="color: var(--cGreen)" class="icon-retweet"/>
- <i style="color: var(--cOrange)" class="icon-star"/>
- <i style="color: var(--cRed)" class="icon-cancel"/>
- </div>
- </div>
- </div>
-
- <div class="after-post">
- <div class="avatar-alt">
- :^)
- </div>
- <div class="content">
- <i18n path="settings.style.preview.fine_print" tag="span" class="faint">
- <a style="color: var(--faintLink)">
- {{$t('settings.style.preview.faint_link')}}
- </a>
- </i18n>
- </div>
- </div>
- <div class="separator"></div>
-
- <span class="alert error">
- {{$t('settings.style.preview.error')}}
- </span>
- <input :value="$t('settings.style.preview.input')" type="text">
-
- <div class="actions">
- <span class="checkbox">
- <input checked="very yes" type="checkbox" id="preview_checkbox">
- <label for="preview_checkbox">{{$t('settings.style.preview.checkbox')}}</label>
- </span>
- <button class="btn">
- {{$t('settings.style.preview.button')}}
- </button>
- </div>
- </div>
- </div>
+ <preview :style="previewRules"/>
</div>
<keep-alive>
@@ -235,6 +163,7 @@
<OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/>
</div>
</div>
+
<div :label="$t('settings.style.radii._tab_label')" class="radius-container">
<div class="tab-header">
<p>{{$t('settings.radii_help')}}</p>
@@ -249,6 +178,7 @@
<RangeInput name="attachmentRadius" :label="$t('settings.attachmentRadius')" v-model="attachmentRadiusLocal" :fallback="previewTheme.radii.attachment" max="50" hardMin="0"/>
<RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/>
</div>
+
<div :label="$t('settings.style.shadows._tab_label')" class="shadow-container">
<div class="tab-header shadow-selector">
<div class="select-container">
@@ -294,6 +224,7 @@
<p>{{$t('settings.style.shadows.filter_hint.spread_zero')}}</p>
</div>
</div>
+
<div :label="$t('settings.style.fonts._tab_label')" class="fonts-container">
<div class="tab-header">
<p>{{$t('settings.style.fonts.help')}}</p>
diff --git a/src/modules/config.js b/src/modules/config.js
index fb9b3ca6..45ac8f65 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -1,5 +1,5 @@
import { set, delete as del } from 'vue'
-import { setPreset, setColors } from '../services/style_setter/style_setter.js'
+import { setPreset, applyTheme } from '../services/style_setter/style_setter.js'
const browserLocale = (window.navigator.language || 'en').split('-')[0]
@@ -57,7 +57,7 @@ const config = {
setPreset(value, commit)
break
case 'customTheme':
- setColors(value, commit)
+ applyTheme(value, commit)
}
}
}