forked from AkkomaGang/akkoma-fe
changed importexport into a service instead of component for simplicity
This commit is contained in:
parent
4baa397ed0
commit
bd5b62b107
4 changed files with 112 additions and 142 deletions
|
@ -1,102 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="import-export-container">
|
|
||||||
<slot name="before" />
|
|
||||||
<button
|
|
||||||
class="btn button-default"
|
|
||||||
@click="exportData"
|
|
||||||
>
|
|
||||||
{{ exportLabel }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn button-default"
|
|
||||||
@click="importData"
|
|
||||||
>
|
|
||||||
{{ importLabel }}
|
|
||||||
</button>
|
|
||||||
<slot name="afterButtons" />
|
|
||||||
<p
|
|
||||||
v-if="importFailed"
|
|
||||||
class="alert error"
|
|
||||||
>
|
|
||||||
{{ importFailedText }}
|
|
||||||
</p>
|
|
||||||
<slot name="afterError" />
|
|
||||||
</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, 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)
|
|
||||||
},
|
|
||||||
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>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
.import-export-container {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: baseline;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -15,6 +15,10 @@ import {
|
||||||
shadows2to3,
|
shadows2to3,
|
||||||
colors2to3
|
colors2to3
|
||||||
} from 'src/services/style_setter/style_setter.js'
|
} from 'src/services/style_setter/style_setter.js'
|
||||||
|
import {
|
||||||
|
newImporter,
|
||||||
|
newExporter
|
||||||
|
} from 'src/services/export_import/export_import.js'
|
||||||
import {
|
import {
|
||||||
SLOT_INHERITANCE
|
SLOT_INHERITANCE
|
||||||
} from 'src/services/theme_data/pleromafe.js'
|
} from 'src/services/theme_data/pleromafe.js'
|
||||||
|
@ -31,7 +35,6 @@ import ShadowControl from 'src/components/shadow_control/shadow_control.vue'
|
||||||
import FontControl from 'src/components/font_control/font_control.vue'
|
import FontControl from 'src/components/font_control/font_control.vue'
|
||||||
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
|
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
|
||||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
|
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
|
||||||
import ExportImport from 'src/components/export_import/export_import.vue'
|
|
||||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||||
|
|
||||||
import Preview from './preview.vue'
|
import Preview from './preview.vue'
|
||||||
|
@ -67,6 +70,15 @@ const colorConvert = (color) => {
|
||||||
export default {
|
export default {
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
|
themeImporter: newImporter({
|
||||||
|
validator: this.importValidator,
|
||||||
|
onImport: this.onImport,
|
||||||
|
onImportFailure: this.onImportFailure
|
||||||
|
}),
|
||||||
|
themeExporter: newExporter({
|
||||||
|
filename: 'pleroma_theme',
|
||||||
|
getExportedObject: () => this.exportedTheme
|
||||||
|
}),
|
||||||
availableStyles: [],
|
availableStyles: [],
|
||||||
selected: this.$store.getters.mergedConfig.theme,
|
selected: this.$store.getters.mergedConfig.theme,
|
||||||
themeWarning: undefined,
|
themeWarning: undefined,
|
||||||
|
@ -383,7 +395,6 @@ export default {
|
||||||
FontControl,
|
FontControl,
|
||||||
TabSwitcher,
|
TabSwitcher,
|
||||||
Preview,
|
Preview,
|
||||||
ExportImport,
|
|
||||||
Checkbox
|
Checkbox
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -528,10 +539,15 @@ export default {
|
||||||
this.previewColors.mod
|
this.previewColors.mod
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
importTheme () { this.themeImporter.importData() },
|
||||||
|
exportTheme () { this.themeExporter.exportData() },
|
||||||
onImport (parsed, forceSource = false) {
|
onImport (parsed, forceSource = false) {
|
||||||
this.tempImportFile = parsed
|
this.tempImportFile = parsed
|
||||||
this.loadTheme(parsed, 'file', forceSource)
|
this.loadTheme(parsed, 'file', forceSource)
|
||||||
},
|
},
|
||||||
|
onImportFailure (result) {
|
||||||
|
this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' })
|
||||||
|
},
|
||||||
importValidator (parsed) {
|
importValidator (parsed) {
|
||||||
const version = parsed._pleroma_theme_version
|
const version = parsed._pleroma_theme_version
|
||||||
return version >= 1 || version <= 2
|
return version >= 1 || version <= 2
|
||||||
|
|
|
@ -48,46 +48,45 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ExportImport
|
<div class="top">
|
||||||
:export-object="exportedTheme"
|
<div class="presets">
|
||||||
:export-label="$t("settings.export_theme")"
|
{{ $t('settings.presets') }}
|
||||||
:import-label="$t("settings.import_theme")"
|
<label
|
||||||
:import-failed-text="$t("settings.invalid_theme_imported")"
|
for="preset-switcher"
|
||||||
:on-import="onImport"
|
class="select"
|
||||||
:validator="importValidator"
|
>
|
||||||
>
|
<select
|
||||||
<template slot="before">
|
id="preset-switcher"
|
||||||
<div class="presets">
|
v-model="selected"
|
||||||
{{ $t('settings.presets') }}
|
class="preset-switcher"
|
||||||
<label
|
|
||||||
for="preset-switcher"
|
|
||||||
class="select"
|
|
||||||
>
|
>
|
||||||
<select
|
<option
|
||||||
id="preset-switcher"
|
v-for="style in availableStyles"
|
||||||
v-model="selected"
|
:key="style.name"
|
||||||
class="preset-switcher"
|
:value="style"
|
||||||
|
:style="{
|
||||||
|
backgroundColor: style[1] || (style.theme || style.source).colors.bg,
|
||||||
|
color: style[3] || (style.theme || style.source).colors.text
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<option
|
{{ style[0] || style.name }}
|
||||||
v-for="style in availableStyles"
|
</option>
|
||||||
:key="style.name"
|
</select>
|
||||||
:value="style"
|
<FAIcon
|
||||||
:style="{
|
class="select-down-icon"
|
||||||
backgroundColor: style[1] || (style.theme || style.source).colors.bg,
|
icon="chevron-down"
|
||||||
color: style[3] || (style.theme || style.source).colors.text
|
/>
|
||||||
}"
|
</label>
|
||||||
>
|
</div>
|
||||||
{{ style[0] || style.name }}
|
<div class="export-import">
|
||||||
</option>
|
<button class="btn button-default" @click="importTheme">
|
||||||
</select>
|
{{ $t("settings.import_theme") }}
|
||||||
<FAIcon
|
</button>
|
||||||
class="select-down-icon"
|
<button class="btn button-default" @click="exportTheme">
|
||||||
icon="chevron-down"
|
{{ $t("settings.export_theme") }}
|
||||||
/>
|
</button>
|
||||||
</label>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
|
||||||
</ExportImport>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="save-load-options">
|
<div class="save-load-options">
|
||||||
<span class="keep-option">
|
<span class="keep-option">
|
||||||
|
|
57
src/services/export_import/export_import.js
Normal file
57
src/services/export_import/export_import.js
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
export const newExporter = ({
|
||||||
|
filename = 'data',
|
||||||
|
getExportedObject
|
||||||
|
}) => ({
|
||||||
|
exportData () {
|
||||||
|
const stringified = JSON.stringify(getExportedObject(), 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', `${filename}.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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const newImporter = ({
|
||||||
|
onImport,
|
||||||
|
onImportFailure,
|
||||||
|
validator = () => true
|
||||||
|
}) => ({
|
||||||
|
importData () {
|
||||||
|
const filePicker = document.createElement('input')
|
||||||
|
filePicker.setAttribute('type', 'file')
|
||||||
|
filePicker.setAttribute('accept', '.json')
|
||||||
|
console.log(1)
|
||||||
|
|
||||||
|
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 validationResult = validator(parsed)
|
||||||
|
if (validationResult === true) {
|
||||||
|
onImport(parsed)
|
||||||
|
} else {
|
||||||
|
onImportFailure({ validationResult })
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
onImportFailure({ error })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(2)
|
||||||
|
reader.readAsText(event.target.files[0])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
document.body.appendChild(filePicker)
|
||||||
|
filePicker.click()
|
||||||
|
document.body.removeChild(filePicker)
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in a new issue