separated preview and exported from style_switcher

This commit is contained in:
Henry Jameson 2018-12-11 02:46:17 +03:00
parent a17ac74df7
commit 51dccb7887
5 changed files with 200 additions and 147 deletions

View file

@ -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>

View file

@ -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>

View file

@ -8,6 +8,8 @@ import ShadowControl from '../shadow_control/shadow_control.vue'
import FontControl from '../font_control/font_control.vue' import FontControl from '../font_control/font_control.vue'
import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' import ContrastRatio from '../contrast_ratio/contrast_ratio.vue'
import TabSwitcher from '../tab_switcher/tab_switcher.jsx' 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 // List of color values used in v1
const v1OnlyNames = [ const v1OnlyNames = [
@ -26,7 +28,6 @@ export default {
return { return {
availableStyles: [], availableStyles: [],
selected: this.$store.state.config.theme, selected: this.$store.state.config.theme,
invalidThemeImported: false,
previewShadows: {}, previewShadows: {},
previewColors: {}, previewColors: {},
@ -293,20 +294,11 @@ export default {
}, },
themeValid () { themeValid () {
return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid return !this.shadowsInvalid && !this.colorsInvalid && !this.radiiInvalid
}
}, },
components: { exportedTheme () {
ColorInput,
OpacityInput,
RangeInput,
ContrastRatio,
ShadowControl,
FontControl,
TabSwitcher
},
methods: {
exportCurrentTheme () {
const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepColors && !this.keepOpacity && !this.keepRoundness const saveEverything = !this.keepFonts && !this.keepShadows && !this.keepColors && !this.keepOpacity && !this.keepRoundness
// TODO change into delete-less version.
const theme = { const theme = {
shadows: this.shadowsLocal, shadows: this.shadowsLocal,
fonts: this.fontsLocal, fonts: this.fontsLocal,
@ -331,57 +323,24 @@ export default {
delete theme.radii delete theme.radii
} }
const stringified = JSON.stringify({ return {
// To separate from other random JSON files and possible future theme formats // To separate from other random JSON files and possible future theme formats
_pleroma_theme_version: 2, theme _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)
}, },
components: {
importTheme () { ColorInput,
this.invalidThemeImported = false OpacityInput,
const filePicker = document.createElement('input') RangeInput,
filePicker.setAttribute('type', 'file') ContrastRatio,
filePicker.setAttribute('accept', '.json') ShadowControl,
FontControl,
filePicker.addEventListener('change', event => { TabSwitcher,
if (event.target.files[0]) { Preview,
// eslint-disable-next-line no-undef ExportImport
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)
}, },
methods: {
setCustomTheme () { setCustomTheme () {
this.$store.dispatch('setOption', { this.$store.dispatch('setOption', {
name: 'customTheme', 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 () { clearAll () {
const state = this.$store.state.config.customTheme const state = this.$store.state.config.customTheme
const version = state.colors ? 2 : 'l1' const version = state.colors ? 2 : 'l1'

View file

@ -18,11 +18,14 @@
<i class="icon-down-open"/> <i class="icon-down-open"/>
</label> </label>
</div> </div>
<div class="import-export"> <export-import
<button class="btn" @click="exportCurrentTheme">{{ $t('settings.export_theme') }}</button> :exportObject='exportedTheme'
<button class="btn" @click="importTheme">{{ $t('settings.import_theme') }}</button> :exportLabel='$t("settings.export_theme")'
<p v-if="invalidThemeImported" class="import-warning">{{ $t('settings.invalid_theme_imported') }}</p> :importLabel='$t("settings.import_theme")'
</div> :importFailedText='$t("settings.invalid_theme_imported")'
:onImport='onImport'
:validator='importValidator'
/>
</div> </div>
<div class="save-load-options"> <div class="save-load-options">
<span> <span>
@ -58,82 +61,7 @@
</div> </div>
<div class="preview-container"> <div class="preview-container">
<div class="panel dummy" :style="previewRules"> <preview :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>
</div> </div>
<keep-alive> <keep-alive>
@ -235,6 +163,7 @@
<OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/> <OpacityInput name="faintOpacity" v-model="faintOpacityLocal" :fallback="previewTheme.opacity.faint || 0.5"/>
</div> </div>
</div> </div>
<div :label="$t('settings.style.radii._tab_label')" class="radius-container"> <div :label="$t('settings.style.radii._tab_label')" class="radius-container">
<div class="tab-header"> <div class="tab-header">
<p>{{$t('settings.radii_help')}}</p> <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="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"/> <RangeInput name="tooltipRadius" :label="$t('settings.tooltipRadius')" v-model="tooltipRadiusLocal" :fallback="previewTheme.radii.tooltip" max="50" hardMin="0"/>
</div> </div>
<div :label="$t('settings.style.shadows._tab_label')" class="shadow-container"> <div :label="$t('settings.style.shadows._tab_label')" class="shadow-container">
<div class="tab-header shadow-selector"> <div class="tab-header shadow-selector">
<div class="select-container"> <div class="select-container">
@ -294,6 +224,7 @@
<p>{{$t('settings.style.shadows.filter_hint.spread_zero')}}</p> <p>{{$t('settings.style.shadows.filter_hint.spread_zero')}}</p>
</div> </div>
</div> </div>
<div :label="$t('settings.style.fonts._tab_label')" class="fonts-container"> <div :label="$t('settings.style.fonts._tab_label')" class="fonts-container">
<div class="tab-header"> <div class="tab-header">
<p>{{$t('settings.style.fonts.help')}}</p> <p>{{$t('settings.style.fonts.help')}}</p>

View file

@ -1,5 +1,5 @@
import { set, delete as del } from 'vue' 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] const browserLocale = (window.navigator.language || 'en').split('-')[0]
@ -57,7 +57,7 @@ const config = {
setPreset(value, commit) setPreset(value, commit)
break break
case 'customTheme': case 'customTheme':
setColors(value, commit) applyTheme(value, commit)
} }
} }
} }