diff options
Diffstat (limited to 'src/components/settings_modal')
36 files changed, 1253 insertions, 599 deletions
diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.scss b/src/components/settings_modal/admin_tabs/emoji_tab.scss index cc918870..3e77e019 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.scss +++ b/src/components/settings_modal/admin_tabs/emoji_tab.scss @@ -1,5 +1,3 @@ -@import "src/variables"; - .emoji-tab { .btn-group .btn:not(:first-child) { margin-left: 0.5em; @@ -25,7 +23,7 @@ } .emoji-unsaved { - box-shadow: 0 3px 5px var(--cBlue, $fallback--cBlue); + box-shadow: 0 3px 5px var(--cBlue); } .emoji-list { @@ -56,6 +54,6 @@ } .warning { - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } } diff --git a/src/components/settings_modal/admin_tabs/emoji_tab.vue b/src/components/settings_modal/admin_tabs/emoji_tab.vue index 5231f860..5742d2ce 100644 --- a/src/components/settings_modal/admin_tabs/emoji_tab.vue +++ b/src/components/settings_modal/admin_tabs/emoji_tab.vue @@ -13,13 +13,15 @@ <button class="button button-default btn" type="button" - @click="reloadEmoji"> + @click="reloadEmoji" + > {{ $t('admin_dash.emoji.reload') }} </button> <button class="button button-default btn" type="button" - @click="importFromFS"> + @click="importFromFS" + > {{ $t('admin_dash.emoji.importFS') }} </button> </li> @@ -28,7 +30,8 @@ <button class="button button-default btn" type="button" - @click="$refs.remotePackPopover.showPopover"> + @click="$refs.remotePackPopover.showPopover" + > {{ $t('admin_dash.emoji.remote_packs') }} <Popover @@ -43,11 +46,16 @@ <template #content> <div class="emoji-tab-popover-input"> <h3>{{ $t('admin_dash.emoji.remote_pack_instance') }}</h3> - <input v-model="remotePackInstance" :placeholder="$t('admin_dash.emoji.remote_pack_instance')"> + <input + v-model="remotePackInstance" + class="input" + :placeholder="$t('admin_dash.emoji.remote_pack_instance')" + > <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="listRemotePacks"> + @click="listRemotePacks" + > {{ $t('admin_dash.emoji.do_list') }} </button> </div> @@ -61,9 +69,22 @@ <li> <h4>{{ $t('admin_dash.emoji.edit_pack') }}</h4> - <Select class="form-control" v-model="packName"> - <option value="" disabled hidden>{{ $t('admin_dash.emoji.emoji_pack') }}</option> - <option v-for="(pack, listPackName) in knownPacks" :label="listPackName" :key="listPackName"> + <Select + v-model="packName" + class="form-control" + > + <option + value="" + disabled + hidden + > + {{ $t('admin_dash.emoji.emoji_pack') }} + </option> + <option + v-for="(pack, listPackName) in knownPacks" + :key="listPackName" + :label="listPackName" + > {{ listPackName }} </option> </Select> @@ -71,7 +92,8 @@ <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="$refs.createPackPopover.showPopover"> + @click="$refs.createPackPopover.showPopover" + > {{ $t('admin_dash.emoji.create_pack') }} </button> <Popover @@ -86,11 +108,16 @@ <template #content> <div class="emoji-tab-popover-input"> <h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3> - <input v-model="newPackName" :placeholder="$t('admin_dash.emoji.new_pack_name')"> + <input + v-model="newPackName" + :placeholder="$t('admin_dash.emoji.new_pack_name')" + class="input" + > <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="createEmojiPack"> + @click="createEmojiPack" + > {{ $t('admin_dash.emoji.create') }} </button> </div> @@ -105,67 +132,96 @@ <li> <label> {{ $t('admin_dash.emoji.description') }} - <ModifiedIndicator :changed="metaEdited('description')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('description')" + message-key="admin_dash.emoji.metadata_changed" + /> <textarea v-model="packMeta.description" :disabled="pack.remote !== undefined" - class="bio resize-height" /> + class="bio resize-height input" + /> </label> </li> <li> <label> {{ $t('admin_dash.emoji.homepage') }} - <ModifiedIndicator :changed="metaEdited('homepage')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('homepage')" + message-key="admin_dash.emoji.metadata_changed" + /> - <input - class="emoji-info-input" v-model="packMeta.homepage" - :disabled="pack.remote !== undefined"> + <input + v-model="packMeta.homepage" + class="emoji-info-input input" + :disabled="pack.remote !== undefined" + > </label> </li> <li> <label> {{ $t('admin_dash.emoji.fallback_src') }} - <ModifiedIndicator :changed="metaEdited('fallback-src')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('fallback-src')" + message-key="admin_dash.emoji.metadata_changed" + /> - <input class="emoji-info-input" v-model="packMeta['fallback-src']" :disabled="pack.remote !== undefined"> + <input + v-model="packMeta['fallback-src']" + class="emoji-info-input input" + :disabled="pack.remote !== undefined" + > </label> </li> <li> <label> {{ $t('admin_dash.emoji.fallback_sha256') }} - <input :disabled="true" class="emoji-info-input" v-model="packMeta['fallback-src-sha256']"> + <input + v-model="packMeta['fallback-src-sha256']" + :disabled="true" + class="emoji-info-input input" + > </label> </li> <li> - <Checkbox :disabled="pack.remote !== undefined" v-model="packMeta['share-files']"> + <Checkbox + v-model="packMeta['share-files']" + :disabled="pack.remote !== undefined" + > {{ $t('admin_dash.emoji.share') }} </Checkbox> - <ModifiedIndicator :changed="metaEdited('share-files')" message-key="admin_dash.emoji.metadata_changed" /> + <ModifiedIndicator + :changed="metaEdited('share-files')" + message-key="admin_dash.emoji.metadata_changed" + /> </li> <li class="btn-group"> <button + v-if="pack.remote === undefined" class="button button-default btn" type="button" - v-if="pack.remote === undefined" - @click="savePackMetadata"> + @click="savePackMetadata" + > {{ $t('admin_dash.emoji.save_meta') }} </button> <button + v-if="pack.remote === undefined" class="button button-default btn" type="button" - v-if="pack.remote === undefined" - @click="savePackMetadata"> + @click="savePackMetadata" + > {{ $t('admin_dash.emoji.revert_meta') }} </button> <button - class="button button-default btn" v-if="pack.remote === undefined" + class="button button-default btn" type="button" - @click="deleteModalVisible = true"> + @click="deleteModalVisible = true" + > {{ $t('admin_dash.emoji.delete_pack') }} <ConfirmModal @@ -174,16 +230,18 @@ :cancel-text="$t('status.delete_confirm_cancel_button')" :confirm-text="$t('status.delete_confirm_accept_button')" @cancelled="deleteModalVisible = false" - @accepted="deleteEmojiPack" > + @accepted="deleteEmojiPack" + > {{ $t('admin_dash.emoji.delete_confirm', [packName]) }} </ConfirmModal> </button> <button + v-if="pack.remote !== undefined" class="button button-default btn" type="button" - v-if="pack.remote !== undefined" - @click="$refs.dlPackPopover.showPopover"> + @click="$refs.dlPackPopover.showPopover" + > {{ $t('admin_dash.emoji.download_pack') }} <Popover @@ -202,12 +260,17 @@ <div class="emoji-tab-popover-input"> <label> {{ $t('admin_dash.emoji.download_as_name') }} - <input class="emoji-data-input" + <input v-model="remotePackDownloadAs" - :placeholder="$t('admin_dash.emoji.download_as_name_full')"> + class="emoji-data-input input" + :placeholder="$t('admin_dash.emoji.download_as_name_full')" + > </label> - <div v-if="downloadWillReplaceLocal" class="warning"> + <div + v-if="downloadWillReplaceLocal" + class="warning" + > <em>{{ $t('admin_dash.emoji.replace_warning') }}</em> </div> </div> @@ -215,7 +278,8 @@ <button class="button button-default btn" type="button" - @click="downloadRemotePack"> + @click="downloadRemotePack" + > {{ $t('admin_dash.emoji.download') }} </button> </div> @@ -231,31 +295,47 @@ <h4> {{ $t('admin_dash.emoji.files') }} - <ModifiedIndicator v-if="pack" + <ModifiedIndicator + v-if="pack" :changed="$refs.emojiPopovers && $refs.emojiPopovers.some(p => p.isEdited)" - message-key="admin_dash.emoji.emoji_changed"/> + message-key="admin_dash.emoji.emoji_changed" + /> </h4> - <div class="emoji-list" v-if="pack"> + <div + v-if="pack" + class="emoji-list" + > <EmojiEditingPopover v-if="pack.remote === undefined" - placement="bottom" new-upload + placement="bottom" + new-upload :title="$t('admin_dash.emoji.adding_new')" - :packName="packName" - @updatePackFiles="updatePackFiles" @displayError="displayError" + :pack-name="packName" + @updatePackFiles="updatePackFiles" + @displayError="displayError" > <template #trigger> - <FAIcon icon="plus" size="2x" :title="$t('admin_dash.emoji.add_file')" /> + <FAIcon + icon="plus" + size="2x" + :title="$t('admin_dash.emoji.add_file')" + /> </template> </EmojiEditingPopover> <EmojiEditingPopover - placement="top" ref="emojiPopovers" - v-for="(file, shortcode) in pack.files" :key="shortcode" + v-for="(file, shortcode) in pack.files" + ref="emojiPopovers" + :key="shortcode" + placement="top" :title="$t('admin_dash.emoji.editing', [shortcode])" :disabled="pack.remote !== undefined" - :shortcode="shortcode" :file="file" :packName="packName" - @updatePackFiles="updatePackFiles" @displayError="displayError" + :shortcode="shortcode" + :file="file" + :pack-name="packName" + @updatePackFiles="updatePackFiles" + @displayError="displayError" > <template #trigger> <StillImage diff --git a/src/components/settings_modal/admin_tabs/frontends_tab.vue b/src/components/settings_modal/admin_tabs/frontends_tab.vue index 097877bc..8fb3d399 100644 --- a/src/components/settings_modal/admin_tabs/frontends_tab.vue +++ b/src/components/settings_modal/admin_tabs/frontends_tab.vue @@ -6,7 +6,10 @@ <div class="setting-item"> <h2>{{ $t('admin_dash.tabs.frontends') }}</h2> <p>{{ $t('admin_dash.frontend.wip_notice') }}</p> - <ul class="setting-list" v-if="adminDraft"> + <ul + v-if="adminDraft" + class="setting-list" + > <li> <h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3> <p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p> @@ -23,12 +26,18 @@ </ul> </li> </ul> - <div v-else class="setting-list"> + <div + v-else + class="setting-list" + > {{ $t('admin_dash.frontend.default_frontend_unavail') }} </div> <div class="setting-list relative"> - <PanelLoading class="overlay" v-if="working"/> + <PanelLoading + v-if="working" + class="overlay" + /> <h3>{{ $t('admin_dash.frontend.available_frontends') }}</h3> <ul class="cards-list"> <li @@ -107,7 +116,7 @@ <button v-for="ref in frontend.refs" :key="ref" - class="button-default dropdown-item" + class="menu-item dropdown-item" @click.prevent="update(frontend, ref)" @click="close" > @@ -164,7 +173,7 @@ <button v-for="ref in frontend.installedRefs || frontend.refs" :key="ref" - class="button-default dropdown-item" + class="menu-item dropdown-item" @click.prevent="setDefault(frontend, ref)" @click="close" > diff --git a/src/components/settings_modal/admin_tabs/instance_tab.vue b/src/components/settings_modal/admin_tabs/instance_tab.vue index a0e3351e..32e8df25 100644 --- a/src/components/settings_modal/admin_tabs/instance_tab.vue +++ b/src/components/settings_modal/admin_tabs/instance_tab.vue @@ -8,7 +8,10 @@ </li> <!-- See https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3963 --> <li v-if="adminDraft[':pleroma'][':instance'][':favicon'] !== undefined"> - <AttachmentSetting compact path=":pleroma.:instance.:favicon" /> + <AttachmentSetting + compact + path=":pleroma.:instance.:favicon" + /> </li> <li> <StringSetting path=":pleroma.:instance.:email" /> @@ -20,7 +23,10 @@ <StringSetting path=":pleroma.:instance.:short_description" /> </li> <li> - <AttachmentSetting compact path=":pleroma.:instance.:instance_thumbnail" /> + <AttachmentSetting + compact + path=":pleroma.:instance.:instance_thumbnail" + /> </li> <li> <AttachmentSetting path=":pleroma.:instance.:background_image" /> diff --git a/src/components/settings_modal/helpers/attachment_setting.vue b/src/components/settings_modal/helpers/attachment_setting.vue index b50231f2..96c80ab1 100644 --- a/src/components/settings_modal/helpers/attachment_setting.vue +++ b/src/components/settings_modal/helpers/attachment_setting.vue @@ -29,7 +29,7 @@ <label for="path">{{ $t('settings.url') }}</label> <input :id="path" - class="string-input" + class="input string-input" :disabled="shouldBeDisabled" :value="realDraftMode ? draft : state" @change="update" diff --git a/src/components/settings_modal/helpers/emoji_editing_popover.vue b/src/components/settings_modal/helpers/emoji_editing_popover.vue index cdd3e403..f0465dd5 100644 --- a/src/components/settings_modal/helpers/emoji_editing_popover.vue +++ b/src/components/settings_modal/helpers/emoji_editing_popover.vue @@ -1,10 +1,10 @@ <template> <Popover + ref="emojiPopover" trigger="click" :placement="placement" bound-to-selector=".emoji-list" popover-class="emoji-tab-edit-popover popover-default" - ref="emojiPopover" :bound-to="{ x: 'container' }" :offset="{ y: 5 }" :disabled="disabled" @@ -18,23 +18,36 @@ {{ title }} </h3> - <StillImage class="emoji" v-if="emojiPreview" :src="emojiPreview" /> - <div v-else class="emoji"></div> - - <div class="emoji-tab-popover-input" v-if="newUpload"> + <StillImage + v-if="emojiPreview" + class="emoji" + :src="emojiPreview" + /> + <div + v-else + class="emoji" + /> + + <div + v-if="newUpload" + class="emoji-tab-popover-input" + > <input type="file" accept="image/*" - class="emoji-tab-popover-file" - @change="uploadFile = $event.target.files"> + class="emoji-tab-popover-file input" + @change="uploadFile = $event.target.files" + > </div> <div> <div class="emoji-tab-popover-input"> <label> {{ $t('admin_dash.emoji.shortcode') }} - <input class="emoji-data-input" + <input v-model="editedShortcode" - :placeholder="$t('admin_dash.emoji.new_shortcode')"> + class="emoji-data-input input" + :placeholder="$t('admin_dash.emoji.new_shortcode')" + > </label> </div> @@ -42,9 +55,11 @@ <label> {{ $t('admin_dash.emoji.filename') }} - <input class="emoji-data-input" + <input v-model="editedFile" - :placeholder="$t('admin_dash.emoji.new_filename')"> + class="emoji-data-input input" + :placeholder="$t('admin_dash.emoji.new_filename')" + > </label> </div> @@ -52,7 +67,8 @@ class="button button-default btn" type="button" :disabled="newUpload ? uploadFile.length == 0 : !isEdited" - @click="newUpload ? uploadEmoji() : saveEditedEmoji()"> + @click="newUpload ? uploadEmoji() : saveEditedEmoji()" + > {{ $t('admin_dash.emoji.save') }} </button> @@ -60,13 +76,15 @@ <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="deleteModalVisible = true"> + @click="deleteModalVisible = true" + > {{ $t('admin_dash.emoji.delete') }} </button> <button class="button button-default btn emoji-tab-popover-button" type="button" - @click="revertEmoji"> + @click="revertEmoji" + > {{ $t('admin_dash.emoji.revert') }} </button> <ConfirmModal @@ -75,7 +93,8 @@ :cancel-text="$t('status.delete_confirm_cancel_button')" :confirm-text="$t('status.delete_confirm_accept_button')" @cancelled="deleteModalVisible = false" - @accepted="deleteEmoji" > + @accepted="deleteEmoji" + > {{ $t('admin_dash.emoji.delete_confirm', [shortcode]) }} </ConfirmModal> </template> @@ -91,6 +110,30 @@ import StillImage from 'components/still-image/still-image.vue' export default { components: { Popover, ConfirmModal, StillImage }, + inject: ['emojiAddr'], + props: { + placement: String, + disabled: { + type: Boolean, + default: false + }, + + newUpload: Boolean, + + title: String, + packName: String, + shortcode: { + type: String, + // Only exists when this is not a new upload + default: '' + }, + file: { + type: String, + // Only exists when this is not a new upload + default: '' + } + }, + emits: ['updatePackFiles', 'displayError'], data () { return { uploadFile: [], @@ -113,7 +156,6 @@ export default { return !this.newUpload && (this.editedShortcode !== this.shortcode || this.editedFile !== this.file) } }, - inject: ['emojiAddr'], methods: { saveEditedEmoji () { if (!this.isEdited) return @@ -167,29 +209,6 @@ export default { this.$emit('updatePackFiles', resp) }) } - }, - emits: ['updatePackFiles', 'displayError'], - props: { - placement: String, - disabled: { - type: Boolean, - default: false - }, - - newUpload: Boolean, - - title: String, - packName: String, - shortcode: { - type: String, - // Only exists when this is not a new upload - default: '' - }, - file: { - type: String, - // Only exists when this is not a new upload - default: '' - } } } </script> diff --git a/src/components/settings_modal/helpers/number_setting.vue b/src/components/settings_modal/helpers/number_setting.vue index 93f11331..32dc6f83 100644 --- a/src/components/settings_modal/helpers/number_setting.vue +++ b/src/components/settings_modal/helpers/number_setting.vue @@ -15,9 +15,10 @@ </template> <slot v-else /> </label> + {{ ' ' }} <input :id="path" - class="number-input" + class="input number-input" type="number" :step="step || 1" :disabled="shouldBeDisabled" diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js index abf9cfdf..3b3e6268 100644 --- a/src/components/settings_modal/helpers/setting.js +++ b/src/components/settings_modal/helpers/setting.js @@ -48,6 +48,10 @@ export default { draftMode: { type: Boolean, default: undefined + }, + timedApplyMode: { + type: Boolean, + default: false } }, inject: { @@ -161,7 +165,11 @@ export default { case 'admin': return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v }) default: - return (k, v) => this.$store.dispatch('setOption', { name: k, value: v }) + if (this.timedApplyMode) { + return (k, v) => this.$store.dispatch('setOptionTemporarily', { name: k, value: v }) + } else { + return (k, v) => this.$store.dispatch('setOption', { name: k, value: v }) + } } }, defaultState () { diff --git a/src/components/settings_modal/helpers/size_setting.js b/src/components/settings_modal/helpers/size_setting.js deleted file mode 100644 index 12cef705..00000000 --- a/src/components/settings_modal/helpers/size_setting.js +++ /dev/null @@ -1,40 +0,0 @@ -import Select from 'src/components/select/select.vue' -import Setting from './setting.js' - -export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%'] -export const defaultHorizontalUnits = ['px', 'rem', 'vw'] -export const defaultVerticalUnits = ['px', 'rem', 'vh'] - -export default { - ...Setting, - components: { - ...Setting.components, - Select - }, - props: { - ...Setting.props, - min: Number, - units: { - type: Array, - default: () => allCssUnits - } - }, - computed: { - ...Setting.computed, - stateUnit () { - return this.state.replace(/\d+/, '') - }, - stateValue () { - return this.state.replace(/\D+/, '') - } - }, - methods: { - ...Setting.methods, - updateValue (e) { - this.configSink(this.path, parseInt(e.target.value) + this.stateUnit) - }, - updateUnit (e) { - this.configSink(this.path, this.stateValue + e.target.value) - } - } -} diff --git a/src/components/settings_modal/helpers/string_setting.vue b/src/components/settings_modal/helpers/string_setting.vue index 0cfa61ce..7b30d1b9 100644 --- a/src/components/settings_modal/helpers/string_setting.vue +++ b/src/components/settings_modal/helpers/string_setting.vue @@ -17,7 +17,7 @@ </label> <input :id="path" - class="string-input" + class="input string-input" :disabled="shouldBeDisabled" :value="realDraftMode ? draft : state" @change="update" diff --git a/src/components/settings_modal/helpers/unit_setting.js b/src/components/settings_modal/helpers/unit_setting.js new file mode 100644 index 00000000..daeddd81 --- /dev/null +++ b/src/components/settings_modal/helpers/unit_setting.js @@ -0,0 +1,64 @@ +import Select from 'src/components/select/select.vue' +import Setting from './setting.js' + +export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%'] +export const defaultHorizontalUnits = ['px', 'rem', 'vw'] +export const defaultVerticalUnits = ['px', 'rem', 'vh'] + +export default { + ...Setting, + components: { + ...Setting.components, + Select + }, + props: { + ...Setting.props, + min: Number, + units: { + type: Array, + default: () => allCssUnits + }, + unitSet: { + type: String, + default: 'none' + }, + step: { + type: Number, + default: 1 + }, + resetDefault: { + type: Object, + default: null + } + }, + computed: { + ...Setting.computed, + stateUnit () { + return typeof this.state === 'string' ? this.state.replace(/[0-9,.]+/, '') : '' + }, + stateValue () { + return typeof this.state === 'string' ? this.state.replace(/[^0-9,.]+/, '') : '' + } + }, + methods: { + ...Setting.methods, + getUnitString (value) { + if (this.unitSet === 'none') return value + return this.$t(['settings', 'units', this.unitSet, value].join('.')) + }, + updateValue (e) { + this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit) + }, + updateUnit (e) { + let value = this.stateValue + const newUnit = e.target.value + if (this.resetDefault) { + const replaceValue = this.resetDefault[newUnit] + if (replaceValue != null) { + value = replaceValue + } + } + this.configSink(this.path, value + newUnit) + } + } +} diff --git a/src/components/settings_modal/helpers/size_setting.vue b/src/components/settings_modal/helpers/unit_setting.vue index 6c3fbaeb..40ab6880 100644 --- a/src/components/settings_modal/helpers/size_setting.vue +++ b/src/components/settings_modal/helpers/unit_setting.vue @@ -1,7 +1,7 @@ <template> <span v-if="matchesExpertLevel" - class="SizeSetting" + class="UnitSetting" > <label :for="path" @@ -9,11 +9,12 @@ > <slot /> </label> + {{ ' ' }} <input :id="path" - class="number-input" + class="input number-input" type="number" - step="1" + :step="step" :disabled="disabled" :min="min || 0" :value="stateValue" @@ -23,7 +24,7 @@ :id="path" :model-value="stateUnit" :disabled="disabled" - class="css-unit-input" + class="unit-input unstyled" @change="updateUnit" > <option @@ -31,7 +32,7 @@ :key="option" :value="option" > - {{ option }} + {{ getUnitString(option) }} </option> </Select> {{ ' ' }} @@ -42,20 +43,19 @@ </span> </template> -<script src="./size_setting.js"></script> +<script src="./unit_setting.js"></script> <style lang="scss"> -.SizeSetting { +.UnitSetting { .number-input { max-width: 6.5em; + text-align: right; } - .css-unit-input, - .css-unit-input select { - margin-left: 0.5em; - width: 4em; - max-width: 4em; + .unit-input, + .unit-input select { min-width: 4em; + width: auto; } } diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index ff58f2c3..63c9b24a 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -4,6 +4,7 @@ import AsyncComponentError from 'src/components/async_component_error/async_comp import getResettableAsyncComponent from 'src/services/resettable_async_component.js' import Popover from '../popover/popover.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' +import ConfirmModal from 'src/components/confirm_modal/confirm_modal.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { cloneDeep, isEqual } from 'lodash' import { @@ -53,6 +54,7 @@ const SettingsModal = { Modal, Popover, Checkbox, + ConfirmModal, SettingsModalUserContent: getResettableAsyncComponent( () => import('./settings_modal_user_content.vue'), { @@ -165,6 +167,7 @@ const SettingsModal = { }, computed: { currentSaveStateNotice () { + console.log(this.$store.state.interface.settings.currentSaveStateNotice) return this.$store.state.interface.settings.currentSaveStateNotice }, modalActivated () { diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss index 6bc9459b..d01553db 100644 --- a/src/components/settings_modal/settings_modal.scss +++ b/src/components/settings_modal/settings_modal.scss @@ -1,5 +1,3 @@ -@import "src/variables"; - .settings-modal { overflow: hidden; diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue index 4e7fd931..90dbbde0 100644 --- a/src/components/settings_modal/settings_modal.vue +++ b/src/components/settings_modal/settings_modal.vue @@ -14,7 +14,7 @@ <div v-if="currentSaveStateNotice" class="alert" - :class="{ transparent: !currentSaveStateNotice.error, error: currentSaveStateNotice.error}" + :class="{ success: !currentSaveStateNotice.error, error: currentSaveStateNotice.error}" @click.prevent > {{ currentSaveStateNotice.error ? $t('settings.saving_err') : $t('settings.saving_ok') }} @@ -70,7 +70,7 @@ <template #content="{close}"> <div class="dropdown-menu"> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" @click.prevent="backup" @click="close" > @@ -80,7 +80,7 @@ /><span>{{ $t("settings.file_export_import.backup_settings") }}</span> </button> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" @click.prevent="backupWithTheme" @click="close" > @@ -90,7 +90,7 @@ /><span>{{ $t("settings.file_export_import.backup_settings_theme") }}</span> </button> <button - class="button-default dropdown-item dropdown-item-icon" + class="menu-item dropdown-item dropdown-item-icon" @click.prevent="restore" @click="close" > @@ -147,6 +147,18 @@ </span> </div> </div> + <teleport to="#modal"> + <ConfirmModal + v-if="$store.state.interface.temporaryChangesTimeoutId" + :title="$t('settings.confirm_new_setting')" + :cancel-text="$t('settings.revert')" + :confirm-text="$t('settings.confirm')" + @cancelled="$store.state.interface.temporaryChangesRevert" + @accepted="$store.state.interface.temporaryChangesConfirm" + > + {{ $t('settings.confirm_new_question') }} + </ConfirmModal> + </teleport> </Modal> </template> diff --git a/src/components/settings_modal/settings_modal_admin_content.scss b/src/components/settings_modal/settings_modal_admin_content.scss index c984d703..a5314fe1 100644 --- a/src/components/settings_modal/settings_modal_admin_content.scss +++ b/src/components/settings_modal/settings_modal_admin_content.scss @@ -1,10 +1,8 @@ -@import "src/variables"; - .settings_tab-switcher { height: 100%; .setting-item { - border-bottom: 2px solid var(--fg, $fallback--fg); + border-bottom: 2px solid var(--border); margin: 1em 1em 1.4em; padding-bottom: 1.4em; @@ -33,10 +31,6 @@ margin-bottom: 1em; } - select { - min-width: 10em; - } - textarea { width: 100%; max-width: 100%; @@ -45,8 +39,7 @@ .unavailable, .unavailable svg { - color: var(--cRed, $fallback--cRed); - color: $fallback--cRed; + color: var(--cRed); } } } diff --git a/src/components/settings_modal/settings_modal_user_content.js b/src/components/settings_modal/settings_modal_user_content.js index 9ac0301f..ebd5329f 100644 --- a/src/components/settings_modal/settings_modal_user_content.js +++ b/src/components/settings_modal/settings_modal_user_content.js @@ -7,6 +7,7 @@ import FilteringTab from './tabs/filtering_tab.vue' import SecurityTab from './tabs/security_tab/security_tab.vue' import ProfileTab from './tabs/profile_tab.vue' import GeneralTab from './tabs/general_tab.vue' +import AppearanceTab from './tabs/appearance_tab.vue' import VersionTab from './tabs/version_tab.vue' import ThemeTab from './tabs/theme_tab/theme_tab.vue' @@ -19,7 +20,8 @@ import { faBell, faDownload, faEyeSlash, - faInfo + faInfo, + faWindowRestore } from '@fortawesome/free-solid-svg-icons' library.add( @@ -30,7 +32,8 @@ library.add( faBell, faDownload, faEyeSlash, - faInfo + faInfo, + faWindowRestore ) const SettingsModalContent = { @@ -44,6 +47,7 @@ const SettingsModalContent = { SecurityTab, ProfileTab, GeneralTab, + AppearanceTab, VersionTab, ThemeTab }, diff --git a/src/components/settings_modal/settings_modal_user_content.scss b/src/components/settings_modal/settings_modal_user_content.scss index c984d703..a5314fe1 100644 --- a/src/components/settings_modal/settings_modal_user_content.scss +++ b/src/components/settings_modal/settings_modal_user_content.scss @@ -1,10 +1,8 @@ -@import "src/variables"; - .settings_tab-switcher { height: 100%; .setting-item { - border-bottom: 2px solid var(--fg, $fallback--fg); + border-bottom: 2px solid var(--border); margin: 1em 1em 1.4em; padding-bottom: 1.4em; @@ -33,10 +31,6 @@ margin-bottom: 1em; } - select { - min-width: 10em; - } - textarea { width: 100%; max-width: 100%; @@ -45,8 +39,7 @@ .unavailable, .unavailable svg { - color: var(--cRed, $fallback--cRed); - color: $fallback--cRed; + color: var(--cRed); } } } diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue index 0221cccb..1441d892 100644 --- a/src/components/settings_modal/settings_modal_user_content.vue +++ b/src/components/settings_modal/settings_modal_user_content.vue @@ -14,6 +14,20 @@ <GeneralTab /> </div> <div + :label="$t('settings.appearance')" + icon="window-restore" + data-tab-name="appearance" + > + <AppearanceTab /> + </div> + <div + :label="$t('settings.theme')" + icon="paint-brush" + data-tab-name="theme" + > + <ThemeTab /> + </div> + <div v-if="isLoggedIn" :label="$t('settings.profile_tab')" icon="user" @@ -23,6 +37,14 @@ </div> <div v-if="isLoggedIn" + :label="$t('settings.notifications')" + icon="bell" + data-tab-name="notifications" + > + <NotificationsTab /> + </div> + <div + v-if="isLoggedIn" :label="$t('settings.security_tab')" icon="lock" data-tab-name="security" @@ -37,19 +59,13 @@ <FilteringTab /> </div> <div - :label="$t('settings.theme')" - icon="paint-brush" - data-tab-name="theme" - > - <ThemeTab /> - </div> - <div v-if="isLoggedIn" - :label="$t('settings.notifications')" - icon="bell" - data-tab-name="notifications" + :label="$t('settings.mutes_and_blocks')" + :fullHeight="true" + icon="eye-slash" + data-tab-name="mutesAndBlocks" > - <NotificationsTab /> + <MutesAndBlocksTab /> </div> <div v-if="isLoggedIn" @@ -60,15 +76,6 @@ <DataImportExportTab /> </div> <div - v-if="isLoggedIn" - :label="$t('settings.mutes_and_blocks')" - :fullHeight="true" - icon="eye-slash" - data-tab-name="mutesAndBlocks" - > - <MutesAndBlocksTab /> - </div> - <div :label="$t('settings.version.title')" icon="info" data-tab-name="version" diff --git a/src/components/settings_modal/tabs/appearance_tab.js b/src/components/settings_modal/tabs/appearance_tab.js new file mode 100644 index 00000000..b5fd6c4c --- /dev/null +++ b/src/components/settings_modal/tabs/appearance_tab.js @@ -0,0 +1,195 @@ +import BooleanSetting from '../helpers/boolean_setting.vue' +import ChoiceSetting from '../helpers/choice_setting.vue' +import IntegerSetting from '../helpers/integer_setting.vue' +import FloatSetting from '../helpers/float_setting.vue' +import UnitSetting, { defaultHorizontalUnits } from '../helpers/unit_setting.vue' + +import FontControl from 'src/components/font_control/font_control.vue' + +import { normalizeThemeData } from 'src/modules/interface' + +import { + getThemes +} from 'src/services/style_setter/style_setter.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 +} from 'src/services/theme_data/css_utils.js' + +import SharedComputedObject from '../helpers/shared_computed_object.js' +import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faGlobe +} from '@fortawesome/free-solid-svg-icons' + +import Preview from './theme_tab/theme_preview.vue' + +library.add( + faGlobe +) + +const AppearanceTab = { + data () { + return { + availableStyles: [], + intersectionObserver: null, + thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.third_column_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, i) => ({ + key: mode, + value: mode, + label: this.$t(`settings.style.themes3.hacks.underlay_override_mode_${mode}`) + })) + } + }, + components: { + BooleanSetting, + ChoiceSetting, + IntegerSetting, + FloatSetting, + UnitSetting, + ProfileSettingIndicator, + FontControl, + Preview + }, + mounted () { + getThemes() + .then((promises) => { + return Promise.all( + Object.entries(promises) + .map(([k, v]) => v.then(res => [k, res])) + ) + }) + .then(themes => themes.reduce((acc, [k, v]) => { + if (v) { + return [ + ...acc, + { + name: v.name || v[0], + key: k, + data: v + } + ] + } else { + return acc + } + }, [])) + .then((themesComplete) => { + this.availableStyles = themesComplete + }) + + 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) theme.ready = true + }) + observer.unobserve(target) + }) + }, { + root: this.$refs.themeList + }) + } + }, + updated () { + this.$nextTick(() => { + this.$refs.themeList.querySelectorAll('.theme-preview').forEach(node => { + this.intersectionObserver.observe(node) + }) + }) + }, + computed: { + noIntersectionObserver () { + return !window.IntersectionObserver + }, + horizontalUnits () { + return defaultHorizontalUnits + }, + fontsOverride () { + return this.$store.getters.mergedConfig.fontsOverride + }, + columns () { + const mode = this.$store.getters.mergedConfig.thirdColumnMode + + const notif = mode === 'none' ? [] : ['notifs'] + + if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') { + return [...notif, 'content', 'sidebar'] + } else { + return ['sidebar', 'content', ...notif] + } + }, + instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, + instanceWallpaperUsed () { + return this.$store.state.instance.background && + !this.$store.state.users.currentUser.background_image + }, + instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, + language: { + get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, + set: function (val) { + this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) + } + }, + isCustomThemeUsed () { + const { theme } = this.mergedConfig + return theme === 'custom' || theme === null + }, + ...SharedComputedObject() + }, + methods: { + updateFont (key, value) { + console.log(key, value) + this.$store.dispatch('setOption', { + name: 'theme3hacks', + value: { + ...this.mergedConfig.theme3hacks, + fonts: { + ...this.mergedConfig.theme3hacks.fonts, + [key]: value + } + } + }) + }, + isThemeActive (key) { + const { theme } = this.mergedConfig + return key === theme + }, + setTheme (name) { + this.$store.dispatch('setTheme', { themeName: name, saveData: true, recompile: true }) + }, + previewTheme (key, input) { + const style = normalizeThemeData(input) + const x = 2 + if (x === 1) return + const theme2 = convertTheme2To3(style) + const theme3 = init({ + inputRuleset: theme2, + ultimateBackgroundColor: '#000000', + liteMode: true, + debug: true, + onlyNormalState: true + }) + + return getScopedVersion( + getCssRules(theme3.eager), + '#theme-preview-' + key + ).join('\n') + } + } +} + +export default AppearanceTab diff --git a/src/components/settings_modal/tabs/appearance_tab.vue b/src/components/settings_modal/tabs/appearance_tab.vue new file mode 100644 index 00000000..de6eb8e7 --- /dev/null +++ b/src/components/settings_modal/tabs/appearance_tab.vue @@ -0,0 +1,313 @@ +<template> + <div class="appearance-tab" :label="$t('settings.general')"> + <div class="setting-item"> + <h2>{{ $t('settings.theme') }}</h2> + <ul + class="theme-list" + ref="themeList" + > + <button + v-if="isCustomThemeUsed" + disabled + class="button-default theme-preview" + > + <preview /> + <h4 class="theme-name">{{ $t('settings.style.custom_theme_used') }}</h4> + </button> + <button + v-for="style in availableStyles" + :data-theme-key="style.key" + :key="style.key" + class="button-default theme-preview" + :class="{ toggled: isThemeActive(style.key) }" + @click="setTheme(style.key)" + > + <!-- eslint-disable vue/no-v-text-v-html-on-component --> + <component + :is="'style'" + v-if="style.ready || noIntersectionObserver" + v-html="previewTheme(style.key, style.data)" + /> + <!-- eslint-enable vue/no-v-text-v-html-on-component --> + <preview :class="{ placeholder: ready }" :id="'theme-preview-' + style.key"/> + <h4 class="theme-name">{{ style.name }}</h4> + </button> + </ul> + </div> + <div class="alert neutral theme-notice"> + {{ $t("settings.style.appearance_tab_note") }} + </div> + <div class="setting-item"> + <h2>{{ $t('settings.scale_and_layout') }}</h2> + <ul class="setting-list"> + <li> + <UnitSetting + path="textSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 14, 'rem': 1 }" + timed-apply-mode + > + {{ $t('settings.text_size') }} + </UnitSetting> + <div> + <small> + <i18n-t + scope="global" + keypath="settings.text_size_tip" + tag="span" + > + <code>px</code> + <code>rem</code> + </i18n-t> + <br/> + <i18n-t + scope="global" + keypath="settings.text_size_tip2" + tag="span" + > + <code>14px</code> + </i18n-t> + </small> + </div> + </li> + <li> + <h3>{{ $t('settings.style.interface_font_user_override') }}</h3> + <ul class="setting-list"> + <li> + <FontControl + :model-value="mergedConfig.theme3hacks.fonts.interface" + name="ui" + :label="$t('settings.style.fonts.components.interface')" + :fallback="{ family: 'sans-serif' }" + no-inherit="1" + @update:modelValue="v => updateFont('interface', v)" + /> + </li> + <li> + <FontControl + v-if="expertLevel > 0" + :model-value="mergedConfig.theme3hacks.fonts.input" + name="input" + :fallback="{ family: 'inherit' }" + :label="$t('settings.style.fonts.components.input')" + @update:modelValue="v => updateFont('input', v)" + /> + </li> + <li> + <FontControl + v-if="expertLevel > 0" + :model-value="mergedConfig.theme3hacks.fonts.post" + name="post" + :fallback="{ family: 'inherit' }" + :label="$t('settings.style.fonts.components.post')" + @update:modelValue="v => updateFont('post', v)" + /> + </li> + <li> + <FontControl + v-if="expertLevel > 0" + :model-value="mergedConfig.theme3hacks.fonts.monospace" + name="postCode" + :fallback="{ family: 'monospace' }" + :label="$t('settings.style.fonts.components.monospace')" + @update:modelValue="v => updateFont('monospace', v)" + /> + </li> + </ul> + </li> + <li> + <UnitSetting + path="emojiSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 32, 'rem': 2.2 }" + > + {{ $t('settings.emoji_size') }} + </UnitSetting> + <ul + class="setting-list suboptions" + > + <li> + <FloatSetting + v-if="user" + path="emojiReactionsScale" + expert="1" + > + {{ $t('settings.emoji_reactions_scale') }} + </FloatSetting> + </li> + </ul> + </li> + <li> + <UnitSetting + path="navbarSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 55, 'rem': 3.5 }" + > + {{ $t('settings.navbar_size') }} + </UnitSetting> + </li> + <h3>{{ $t('settings.columns') }}</h3> + <li> + <UnitSetting + path="panelHeaderSize" + step="0.1" + :units="['px', 'rem']" + :reset-default="{ 'px': 52, 'rem': 3.2 }" + timed-apply-mode + > + {{ $t('settings.panel_header_size') }} + </UnitSetting> + </li> + <li> + <BooleanSetting path="sidebarRight"> + {{ $t('settings.right_sidebar') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="navbarColumnStretch"> + {{ $t('settings.navbar_column_stretch') }} + </BooleanSetting> + </li> + <li> + <ChoiceSetting + v-if="user" + id="thirdColumnMode" + path="thirdColumnMode" + :options="thirdColumnModeOptions" + > + {{ $t('settings.third_column_mode') }} + </ChoiceSetting> + </li> + <li v-if="expertLevel > 0"> + {{ $t('settings.column_sizes') }} + <div class="column-settings"> + <UnitSetting + v-for="column in columns" + :key="column" + :path="column + 'ColumnWidth'" + :units="horizontalUnits" + expert="1" + > + {{ $t('settings.column_sizes_' + column) }} + </UnitSetting> + </div> + </li> + <li> + <BooleanSetting path="disableStickyHeaders"> + {{ $t('settings.disable_sticky_headers') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="showScrollbars"> + {{ $t('settings.show_scrollbars') }} + </BooleanSetting> + </li> + </ul> + </div> + <div class="setting-item"> + <h2>{{ $t('settings.visual_tweaks') }}</h2> + <ul class="setting-list"> + <li> + <ChoiceSetting + id="forcedRoundness" + path="forcedRoundness" + :options="forcedRoundnessOptions" + > + {{ $t('settings.style.themes3.hacks.force_interface_roundness') }} + </ChoiceSetting> + </li> + <li> + <ChoiceSetting + id="underlayOverride" + path="theme3hacks.underlay" + :options="underlayOverrideModes" + > + {{ $t('settings.style.themes3.hacks.underlay_overrides') }} + </ChoiceSetting> + </li> + <li v-if="instanceWallpaperUsed"> + <BooleanSetting path="hideInstanceWallpaper"> + {{ $t('settings.hide_wallpaper') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="forceThemeRecompilation" + :expert="1" + > + {{ $t('settings.force_theme_recompilation_debug') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting + path="themeDebug" + :expert="1" + > + {{ $t('settings.theme_debug') }} + </BooleanSetting> + </li> + </ul> + </div> + </div> +</template> + +<script src="./appearance_tab.js"></script> + +<style lang="scss"> +.appearance-tab { + .theme-notice { + padding: 0.5em; + margin: 1em; + } + + .column-settings { + display: flex; + justify-content: space-evenly; + flex-wrap: wrap; + } + + .column-settings .size-label { + display: block; + margin-bottom: 0.5em; + margin-top: 0.5em; + } + + .theme-list { + list-style: none; + display: flex; + flex-wrap: wrap; + margin: -0.5em 0; + height: 25em; + overflow-x: hidden; + overflow-y: auto; + scrollbar-gutter: stable; + border-radius: var(--roundness); + border: 1px solid var(--border); + padding: 0; + + .theme-preview { + font-size: 1rem; // fix for firefox + width: 19rem; + display: flex; + flex-direction: column; + align-items: center; + margin: 0.5em; + + &.placeholder { + opacity: 0.2; + } + + .theme-preview-container { + pointer-events: none; + zoom: 0.5; + border: none; + border-radius: var(--roundness); + text-align: left; + } + } + } +} +</style> diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js index 7c37f0bc..fbace15d 100644 --- a/src/components/settings_modal/tabs/filtering_tab.js +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -1,6 +1,7 @@ import { filter, trim, debounce } from 'lodash' import BooleanSetting from '../helpers/boolean_setting.vue' import ChoiceSetting from '../helpers/choice_setting.vue' +import UnitSetting from '../helpers/unit_setting.vue' import IntegerSetting from '../helpers/integer_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' @@ -19,6 +20,7 @@ const FilteringTab = { components: { BooleanSetting, ChoiceSetting, + UnitSetting, IntegerSetting }, computed: { diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue index 9e82fcfd..c86810d5 100644 --- a/src/components/settings_modal/tabs/filtering_tab.vue +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -45,6 +45,11 @@ </BooleanSetting> </li> <li> + <BooleanSetting path="muteSensitiveStatuses"> + {{ $t('settings.mute_sensitive_posts') }} + </BooleanSetting> + </li> + <li> <BooleanSetting path="hidePostStats"> {{ $t('settings.hide_post_stats') }} </BooleanSetting> @@ -67,7 +72,7 @@ <textarea id="muteWords" v-model="muteWordsString" - class="resize-height" + class="input resize-height" /> <div>{{ $t('settings.filtering_explanation') }}</div> </li> @@ -96,6 +101,17 @@ {{ $t('settings.hide_scrobbles') }} </BooleanSetting> </li> + <li> + <UnitSetting + key="hideScrobblesAfter" + path="hideScrobblesAfter" + :units="['m', 'h', 'd']" + unitSet="time" + expert="1" + > + {{ $t('settings.hide_scrobbles_after') }} + </UnitSetting> + </li> </ul> </div> <div diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 3f2bcb13..e9e8fa72 100644 --- a/src/components/settings_modal/tabs/general_tab.js +++ b/src/components/settings_modal/tabs/general_tab.js @@ -3,7 +3,7 @@ import ChoiceSetting from '../helpers/choice_setting.vue' import ScopeSelector from 'src/components/scope_selector/scope_selector.vue' import IntegerSetting from '../helpers/integer_setting.vue' import FloatSetting from '../helpers/float_setting.vue' -import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue' +import UnitSetting from '../helpers/unit_setting.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' @@ -40,11 +40,6 @@ const GeneralTab = { value: mode, label: this.$t(`settings.mention_link_display_${mode}`) })), - thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({ - key: mode, - value: mode, - label: this.$t(`settings.third_column_mode_${mode}`) - })), userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({ key: mode, value: mode, @@ -64,15 +59,12 @@ const GeneralTab = { ChoiceSetting, IntegerSetting, FloatSetting, - SizeSetting, + UnitSetting, InterfaceLanguageSwitcher, ScopeSelector, ProfileSettingIndicator }, computed: { - horizontalUnits () { - return defaultHorizontalUnits - }, postFormats () { return this.$store.state.instance.postFormats || [] }, @@ -83,23 +75,6 @@ const GeneralTab = { label: this.$t(`post_status.content_type["${format}"]`) })) }, - columns () { - const mode = this.$store.getters.mergedConfig.thirdColumnMode - - const notif = mode === 'none' ? [] : ['notifs'] - - if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') { - return [...notif, 'content', 'sidebar'] - } else { - return ['sidebar', 'content', ...notif] - } - }, - instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, - instanceWallpaperUsed () { - return this.$store.state.instance.background && - !this.$store.state.users.currentUser.background_image - }, - instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, language: { get: function () { return this.$store.getters.mergedConfig.interfaceLanguage }, set: function (val) { diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index f56fa8e0..82d5da89 100644 --- a/src/components/settings_modal/tabs/general_tab.vue +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -15,11 +15,6 @@ {{ $t('settings.hide_isp') }} </BooleanSetting> </li> - <li v-if="instanceWallpaperUsed"> - <BooleanSetting path="hideInstanceWallpaper"> - {{ $t('settings.hide_wallpaper') }} - </BooleanSetting> - </li> <li> <BooleanSetting path="stopGifs"> {{ $t('settings.stop_gifs') }} @@ -98,53 +93,6 @@ {{ $t('settings.hide_shoutbox') }} </BooleanSetting> </li> - <li> - <h3>{{ $t('settings.columns') }}</h3> - </li> - <li> - <BooleanSetting path="disableStickyHeaders"> - {{ $t('settings.disable_sticky_headers') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="showScrollbars"> - {{ $t('settings.show_scrollbars') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="sidebarRight"> - {{ $t('settings.right_sidebar') }} - </BooleanSetting> - </li> - <li> - <BooleanSetting path="navbarColumnStretch"> - {{ $t('settings.navbar_column_stretch') }} - </BooleanSetting> - </li> - <li> - <ChoiceSetting - v-if="user" - id="thirdColumnMode" - path="thirdColumnMode" - :options="thirdColumnModeOptions" - > - {{ $t('settings.third_column_mode') }} - </ChoiceSetting> - </li> - <li v-if="expertLevel > 0"> - {{ $t('settings.column_sizes') }} - <div class="column-settings"> - <SizeSetting - v-for="column in columns" - :key="column" - :path="column + 'ColumnWidth'" - :units="horizontalUnits" - expert="1" - > - {{ $t('settings.column_sizes_' + column) }} - </SizeSetting> - </div> - </li> <li class="select-multiple"> <span class="label">{{ $t('settings.confirm_dialogs') }}</span> <ul class="option-list"> @@ -270,14 +218,28 @@ </BooleanSetting> </li> <li> - <FloatSetting - v-if="user" - path="emojiReactionsScale" + <BooleanSetting + path="useAbsoluteTimeFormat" expert="1" > - {{ $t('settings.emoji_reactions_scale') }} - </FloatSetting> + {{ $t('settings.absolute_time_format') }} + </BooleanSetting> </li> + <ul + class="setting-list suboptions" + v-if="mergedConfig.useAbsoluteTimeFormat" + > + <li> + <UnitSetting + path="absoluteTimeFormatMinAge" + unit-set="time" + :units="['s', 'm', 'h', 'd']" + :min="0" + > + {{ $t('settings.absolute_time_format_min_age') }} + </UnitSetting> + </li> + </ul> <h3>{{ $t('settings.attachments') }}</h3> <li> <BooleanSetting @@ -520,17 +482,3 @@ </template> <script src="./general_tab.js"></script> - -<style lang="scss"> -.column-settings { - display: flex; - justify-content: space-evenly; - flex-wrap: wrap; -} - -.column-settings .size-label { - display: block; - margin-bottom: 0.5em; - margin-top: 0.5em; -} -</style> diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue index 9ace4c36..10228888 100644 --- a/src/components/settings_modal/tabs/notifications_tab.vue +++ b/src/components/settings_modal/tabs/notifications_tab.vue @@ -19,7 +19,10 @@ </div> </li> <li> - <BooleanSetting path="unseenAtTop" expert="1"> + <BooleanSetting + path="unseenAtTop" + expert="1" + > {{ $t('settings.notification_setting_unseen_at_top') }} </BooleanSetting> </li> @@ -38,7 +41,9 @@ </li> <li> <h3> {{ $t('settings.notification_visibility') }}</h3> - <p v-if="expertLevel > 0">{{ $t('settings.notification_setting_filters_chrome_push') }}</p> + <p v-if="expertLevel > 0"> + {{ $t('settings.notification_setting_filters_chrome_push') }} + </p> <ul class="setting-list two-column"> <li> <h4> {{ $t('settings.notification_visibility_mentions') }}</h4> @@ -56,6 +61,21 @@ </ul> </li> <li> + <h4> {{ $t('settings.notification_visibility_statuses') }}</h4> + <ul class="setting-list"> + <li> + <BooleanSetting path="notificationVisibility.statuses"> + {{ $t('settings.notification_visibility_in_column') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="notificationNative.statuses"> + {{ $t('settings.notification_visibility_native_notifications') }} + </BooleanSetting> + </li> + </ul> + </li> + <li> <h4> {{ $t('settings.notification_visibility_likes') }}</h4> <ul class="setting-list"> <li> diff --git a/src/components/settings_modal/tabs/profile_tab.scss b/src/components/settings_modal/tabs/profile_tab.scss index ee253ffe..7eda943b 100644 --- a/src/components/settings_modal/tabs/profile_tab.scss +++ b/src/components/settings_modal/tabs/profile_tab.scss @@ -1,5 +1,3 @@ -@import "../../../variables"; - .profile-tab { .bio { margin: 0; @@ -43,16 +41,14 @@ display: block; width: 100%; height: 100%; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); + border-radius: var(--roundness); } .reset-button { position: absolute; top: 0.2em; right: 0.2em; - border-radius: $fallback--tooltipRadius; - border-radius: var(--tooltipRadius, $fallback--tooltipRadius); + border-radius: var(--roundness); background-color: rgb(0 0 0 / 60%); opacity: 0.7; width: 1.5em; diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue index de5219a7..034034a1 100644 --- a/src/components/settings_modal/tabs/profile_tab.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -12,7 +12,7 @@ <input id="username" v-model="newName" - class="name-changer" + class="input name-changer" v-bind="propsToNative(inputProps)" > </template> @@ -26,7 +26,7 @@ <template #default="inputProps"> <textarea v-model="newBio" - class="bio resize-height" + class="input bio resize-height" v-bind="propsToNative(inputProps)" /> </template> @@ -47,7 +47,7 @@ id="birthday" v-model="newBirthday" type="date" - class="birthday-input" + class="input birthday-input" > <Checkbox v-model="showBirthday"> {{ $t('settings.birthday.show_birthday') }} @@ -71,6 +71,7 @@ v-model="newFields[i].name" :placeholder="$t('settings.profile_fields.name')" v-bind="propsToNative(inputProps)" + class="input" > </template> </EmojiInput> @@ -85,6 +86,7 @@ v-model="newFields[i].value" :placeholder="$t('settings.profile_fields.value')" v-bind="propsToNative(inputProps)" + class="input" > </template> </EmojiInput> @@ -205,6 +207,7 @@ <div> <input type="file" + class="input" @change="uploadFile('banner', $event)" > </div> @@ -247,6 +250,7 @@ <div> <input type="file" + class="input" @change="uploadFile('background', $event)" > </div> diff --git a/src/components/settings_modal/tabs/security_tab/mfa.vue b/src/components/settings_modal/tabs/security_tab/mfa.vue index ee5b6b13..9421b16e 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa.vue @@ -99,12 +99,14 @@ <input v-model="otpConfirmToken" type="text" + class="input" > <p>{{ $t('settings.enter_current_password_to_confirm') }}:</p> <input v-model="currentPassword" type="password" + class="input" > <div class="confirm-otp-actions"> <button @@ -137,8 +139,6 @@ <script src="./mfa.js"></script> <style lang="scss"> -@import "../../../../variables"; - .mfa-settings { .mfa-heading, .method-item { @@ -149,8 +149,7 @@ } .warning { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } .setup-otp { diff --git a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue index 923161b2..32a8a759 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue @@ -21,16 +21,13 @@ </template> <script src="./mfa_backup_codes.js"></script> <style lang="scss"> -@import "../../../../variables"; - .mfa-backup-codes { .warning { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); + color: var(--cOrange); } .backup-codes { - font-family: var(--postCodeFont, monospace); + font-family: var(--monoFont); } } </style> diff --git a/src/components/settings_modal/tabs/security_tab/mfa_totp.vue b/src/components/settings_modal/tabs/security_tab/mfa_totp.vue index 8e767bd0..99b66818 100644 --- a/src/components/settings_modal/tabs/security_tab/mfa_totp.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa_totp.vue @@ -30,6 +30,7 @@ <input v-model="currentPassword" type="password" + class="input" > </confirm> <div diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue index d36d478f..74103f6f 100644 --- a/src/components/settings_modal/tabs/security_tab/security_tab.vue +++ b/src/components/settings_modal/tabs/security_tab/security_tab.vue @@ -8,6 +8,7 @@ v-model="newEmail" type="email" autocomplete="email" + class="input" > </div> <div> @@ -16,6 +17,7 @@ v-model="changeEmailPassword" type="password" autocomplete="current-password" + class="input" > </div> <button @@ -40,6 +42,7 @@ <input v-model="changePasswordInputs[0]" type="password" + class="input" > </div> <div> @@ -47,6 +50,7 @@ <input v-model="changePasswordInputs[1]" type="password" + class="input" > </div> <div> @@ -54,6 +58,7 @@ <input v-model="changePasswordInputs[2]" type="password" + class="input" > </div> <button @@ -155,6 +160,7 @@ </i18n-t> <input v-model="addAliasTarget" + class="input" > </div> <button @@ -187,6 +193,7 @@ </i18n-t> <input v-model="moveAccountTarget" + class="input" > </div> <div> @@ -195,6 +202,7 @@ v-model="moveAccountPassword" type="password" autocomplete="current-password" + class="input" > </div> <button @@ -222,6 +230,7 @@ <input v-model="deleteAccountConfirmPasswordInput" type="password" + class="input" > <button class="btn button-default" diff --git a/src/components/settings_modal/tabs/theme_tab/preview.vue b/src/components/settings_modal/tabs/theme_tab/theme_preview.vue index d755279a..dbaecee4 100644 --- a/src/components/settings_modal/tabs/theme_tab/preview.vue +++ b/src/components/settings_modal/tabs/theme_tab/theme_preview.vue @@ -1,11 +1,11 @@ <template> - <div class="preview-container"> + <div class="theme-preview-container"> <div class="underlay underlay-preview" /> <div class="panel dummy"> <div class="panel-heading"> <div class="title"> {{ $t('settings.style.preview.header') }} - <span class="badge badge-notification"> + <span class="badge -notification"> 99 </span> </div> @@ -81,7 +81,7 @@ class="faint" scope="global" > - <a style="color: var(--faintLink);"> + <a style="color: var(--linkFaint);"> {{ $t('settings.style.preview.faint_link') }} </a> </i18n-t> @@ -95,17 +95,13 @@ <input :value="$t('settings.style.preview.input')" type="text" + class="input" > <div class="actions"> - <span class="checkbox"> - <input - id="preview_checkbox" - checked="very yes" - type="checkbox" - > - <label for="preview_checkbox">{{ $t('settings.style.preview.checkbox') }}</label> - </span> + <Checkbox> + {{ $t('settings.style.preview.checkbox') }} + </Checkbox> <button class="btn button-default"> {{ $t('settings.style.preview.button') }} </button> @@ -116,6 +112,7 @@ </template> <script> +import Checkbox from 'src/components/checkbox/checkbox.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -131,19 +128,123 @@ library.add( faReply ) -export default {} +export default { + components: { + Checkbox + } +} </script> <style lang="scss"> -.preview-container { +.theme-preview-container { position: relative; -} + border-top: 1px dashed; + border-bottom: 1px dashed; + border-color: var(--border); + margin: 1em 0; + padding: 1em; + background-color: var(--wallpaper); + background-image: var(--body-background-image); + background-size: cover; + background-position: 50% 50%; + + .theme-preview-content { + padding: 20px; + } + + .dummy { + .post { + font-family: var(--postFont); + display: flex; + + .content { + flex: 1; + + h4 { + margin-bottom: 0.25em; + } + + .icons { + margin-top: 0.5em; + display: flex; + + i { + margin-right: 1em; + } + } + } + } + + .after-post { + margin-top: 1em; + display: flex; + align-items: center; + } + + .avatar, + .avatar-alt { + background: + linear-gradient( + 135deg, + #b8e1fc 0%, + #a9d2f3 10%, + #90bae4 25%, + #90bcea 37%, + #90bff0 50%, + #6ba8e5 51%, + #a2daf5 83%, + #bdf3fd 100% + ); + color: black; + font-family: sans-serif; + text-align: center; + margin-right: 1em; + } + + .avatar-alt { + flex: 0 auto; + margin-left: 28px; + font-size: 12px; + min-width: 20px; + min-height: 20px; + line-height: 20px; + } + + .avatar { + flex: 0 auto; + width: 48px; + height: 48px; + font-size: 14px; + line-height: 48px; + } + + .actions { + display: flex; + align-items: baseline; + + .checkbox { + margin-right: 1em; + flex: 1; + } + } + + .separator { + margin: 1em; + border-bottom: 1px solid; + border-color: var(--border); + } + + .btn { + min-width: 3em; + } + } -.underlay-preview { - position: absolute; - top: 0; - bottom: 0; - left: 10px; - right: 10px; + .underlay-preview { + position: absolute; + top: 0; + bottom: 0; + left: 10px; + right: 10px; + } } -</style> + </style> diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js index 58f8d44a..25836559 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -1,18 +1,11 @@ import { rgb2hex, hex2rgb, - getContrastRatioLayers + getContrastRatioLayers, + relativeLuminance } from 'src/services/color_convert/color_convert.js' import { - DEFAULT_SHADOWS, - generateColors, - generateShadows, - generateRadii, - generateFonts, - composePreset, - getThemes, - shadows2to3, - colors2to3 + getThemes } from 'src/services/style_setter/style_setter.js' import { newImporter, @@ -25,8 +18,23 @@ import { CURRENT_VERSION, OPACITIES, getLayers, - getOpacitySlot + getOpacitySlot, + DEFAULT_SHADOWS, + generateColors, + generateShadows, + generateRadii, + generateFonts, + shadows2to3, + 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 +} from 'src/services/theme_data/css_utils.js' + import ColorInput from 'src/components/color_input/color_input.vue' import RangeInput from 'src/components/range_input/range_input.vue' import OpacityInput from 'src/components/opacity_input/opacity_input.vue' @@ -37,7 +45,7 @@ import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx' import Checkbox from 'src/components/checkbox/checkbox.vue' import Select from 'src/components/select/select.vue' -import Preview from './preview.vue' +import Preview from './theme_preview.vue' // List of color values used in v1 const v1OnlyNames = [ @@ -62,6 +70,7 @@ const colorConvert = (color) => { export default { data () { return { + themeV3Preview: [], themeImporter: newImporter({ validator: this.importValidator, onImport: this.onImport, @@ -78,10 +87,7 @@ export default { tempImportFile: undefined, engineVersion: 0, - previewShadows: {}, - previewColors: {}, - previewRadii: {}, - previewFonts: {}, + previewTheme: {}, shadowsInvalid: true, colorsInvalid: true, @@ -232,13 +238,6 @@ export default { chatMessage: this.chatMessageRadiusLocal } }, - preview () { - return composePreset(this.previewColors, this.previewRadii, this.previewShadows, this.previewFonts) - }, - previewTheme () { - if (!this.preview.theme.colors) return { colors: {}, opacity: {}, radii: {}, shadows: {}, fonts: {} } - return this.preview.theme - }, // This needs optimization maybe previewContrast () { try { @@ -306,14 +305,6 @@ export default { return {} } }, - previewRules () { - if (!this.preview.rules) return '' - return [ - ...Object.values(this.preview.rules), - 'color: var(--text)', - 'font-family: var(--interfaceFont, sans-serif)' - ].join(';') - }, shadowsAvailable () { return Object.keys(DEFAULT_SHADOWS).sort() }, @@ -511,16 +502,15 @@ export default { } }, setCustomTheme () { - this.$store.dispatch('setOption', { - name: 'customTheme', - value: { + this.$store.dispatch('setThemeV2', { + customTheme: { + ignore: true, + themeFileVersion: this.selectedVersion, themeEngineVersion: CURRENT_VERSION, ...this.previewTheme - } - }) - this.$store.dispatch('setOption', { - name: 'customThemeSource', - value: { + }, + customThemeSource: { + themeFileVersion: this.selectedVersion, themeEngineVersion: CURRENT_VERSION, shadows: this.shadowsLocal, fonts: this.fontsLocal, @@ -530,16 +520,24 @@ export default { } }) }, - updatePreviewColorsAndShadows () { - this.previewColors = generateColors({ + updatePreviewColors () { + const result = generateColors({ opacity: this.currentOpacity, colors: this.currentColors }) - this.previewShadows = generateShadows( - { shadows: this.shadowsLocal, opacity: this.previewTheme.opacity, themeEngineVersion: this.engineVersion }, - this.previewColors.theme.colors, - this.previewColors.mod - ) + this.previewTheme.colors = result.theme.colors + this.previewTheme.opacity = result.theme.opacity + }, + updatePreviewShadows () { + this.previewTheme.shadows = generateShadows( + { + shadows: this.shadowsLocal, + opacity: this.previewTheme.opacity, + themeEngineVersion: this.engineVersion + }, + this.previewTheme.colors, + relativeLuminance(this.previewTheme.colors.bg) < 0.5 ? 1 : -1 + ).theme.shadows }, importTheme () { this.themeImporter.importData() }, exportTheme () { this.themeExporter.exportData() }, @@ -608,7 +606,7 @@ export default { normalizeLocalState (theme, version = 0, source, forceSource = false) { let input if (typeof source !== 'undefined') { - if (forceSource || source.themeEngineVersion === CURRENT_VERSION) { + if (forceSource || source?.themeEngineVersion === CURRENT_VERSION) { input = source version = source.themeEngineVersion } else { @@ -690,6 +688,8 @@ export default { } else { this.shadowsLocal = shadows } + this.updatePreviewColors() + this.updatePreviewShadows() this.shadowSelected = this.shadowsAvailable[0] } @@ -697,12 +697,25 @@ export default { this.clearFonts() this.fontsLocal = fonts } + }, + updateTheme3Preview () { + const theme2 = convertTheme2To3(this.previewTheme) + const theme3 = init({ + inputRuleset: theme2, + ultimateBackgroundColor: '#000000', + liteMode: true + }) + + this.themeV3Preview = getScopedVersion( + getCssRules(theme3.eager), + '#theme-preview' + ).join('\n') } }, watch: { currentRadii () { try { - this.previewRadii = generateRadii({ radii: this.currentRadii }) + this.previewTheme.radii = generateRadii({ radii: this.currentRadii }).theme.radii this.radiiInvalid = false } catch (e) { this.radiiInvalid = true @@ -711,9 +724,8 @@ export default { }, shadowsLocal: { handler () { - if (Object.getOwnPropertyNames(this.previewColors).length === 1) return try { - this.updatePreviewColorsAndShadows() + this.updatePreviewShadows() this.shadowsInvalid = false } catch (e) { this.shadowsInvalid = true @@ -725,7 +737,7 @@ export default { fontsLocal: { handler () { try { - this.previewFonts = generateFonts({ fonts: this.fontsLocal }) + this.previewTheme.fonts = generateFonts({ fonts: this.fontsLocal }).theme.fonts this.fontsInvalid = false } catch (e) { this.fontsInvalid = true @@ -736,18 +748,16 @@ export default { }, currentColors () { try { - this.updatePreviewColorsAndShadows() + this.updatePreviewColors() this.colorsInvalid = false - this.shadowsInvalid = false } catch (e) { this.colorsInvalid = true - this.shadowsInvalid = true console.warn(e) } }, currentOpacity () { try { - this.updatePreviewColorsAndShadows() + this.updatePreviewColors() } catch (e) { console.warn(e) } diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss index 9935c2e7..84933fb8 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.scss +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss @@ -1,6 +1,9 @@ -@import "src/variables"; - .theme-tab { + .deprecation-warning { + padding: 0.5em; + margin: 2em; + } + padding-bottom: 2em; .preset-switcher { @@ -12,6 +15,10 @@ margin-right: 0.25em; } + .btn-group .btn { + margin: 0; + } + .style-control { display: flex; align-items: baseline; @@ -159,111 +166,6 @@ } } - .preview-container { - border-top: 1px dashed; - border-bottom: 1px dashed; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - margin: 1em 0; - padding: 1em; - background-color: var(--wallpaper); - background-image: var(--body-background-image); - background-size: cover; - background-position: 50% 50%; - - .dummy { - .post { - font-family: var(--postFont); - display: flex; - - .content { - flex: 1; - - h4 { - margin-bottom: 0.25em; - } - - .icons { - margin-top: 0.5em; - display: flex; - - i { - margin-right: 1em; - } - } - } - } - - .after-post { - margin-top: 1em; - display: flex; - align-items: center; - } - - .avatar, - .avatar-alt { - background: - linear-gradient( - 135deg, - #b8e1fc 0%, - #a9d2f3 10%, - #90bae4 25%, - #90bcea 37%, - #90bff0 50%, - #6ba8e5 51%, - #a2daf5 83%, - #bdf3fd 100% - ); - color: black; - font-family: sans-serif; - text-align: center; - margin-right: 1em; - } - - .avatar-alt { - flex: 0 auto; - margin-left: 28px; - font-size: 12px; - min-width: 20px; - min-height: 20px; - line-height: 20px; - border-radius: $fallback--avatarAltRadius; - border-radius: var(--avatarAltRadius, $fallback--avatarAltRadius); - } - - .avatar { - flex: 0 auto; - width: 48px; - height: 48px; - font-size: 14px; - line-height: 48px; - } - - .actions { - display: flex; - align-items: baseline; - - .checkbox { - display: inline-flex; - align-items: baseline; - margin-right: 1em; - flex: 1; - } - } - - .separator { - margin: 1em; - border-bottom: 1px solid; - border-color: $fallback--border; - border-color: var(--border, $fallback--border); - } - - .btn { - min-width: 3em; - } - } - } - .radius-item { flex-basis: auto; } @@ -296,7 +198,7 @@ border: 0; box-shadow: none; background: transparent; - color: var(--faint, $fallback--faint); + color: var(--textFaint); align-self: stretch; } @@ -316,10 +218,6 @@ max-width: 50em; } - .theme-preview-content { - padding: 20px; - } - .theme-warning { display: flex; align-items: baseline; diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue index ff2fece9..4498c143 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue @@ -1,5 +1,8 @@ <template> <div class="theme-tab"> + <div class="alert warning deprecation-warning"> + {{ $t("settings.style.themes2_outdated") }} + </div> <div class="presets-container"> <div class="save-load"> <div @@ -120,7 +123,19 @@ </div> </div> - <preview :style="previewRules" /> + <!-- eslint-disable vue/no-v-text-v-html-on-component --> + <component :is="'style'" v-html="themeV3Preview"/> + <!-- eslint-enable vue/no-v-text-v-html-on-component --> + <preview id="theme-preview"/> + + <div> + <button + class="btn button-default" + @click="updateTheme3Preview" + > + {{ $t("settings.style.update_preview") }} + </button> + </div> <keep-alive> <tab-switcher key="style-tweak"> @@ -156,7 +171,7 @@ <OpacityInput v-model="bgOpacityLocal" name="bgOpacity" - :fallback="previewTheme.opacity.bg" + :fallback="previewTheme.opacity?.bg" /> <ColorInput v-model="textColorLocal" @@ -167,14 +182,14 @@ <ColorInput v-model="accentColorLocal" name="accentColor" - :fallback="previewTheme.colors.link" + :fallback="previewTheme.colors?.link" :label="$t('settings.accent')" :show-optional-tickbox="typeof linkColorLocal !== 'undefined'" /> <ColorInput v-model="linkColorLocal" name="linkColor" - :fallback="previewTheme.colors.accent" + :fallback="previewTheme.colors?.accent" :label="$t('settings.links')" :show-optional-tickbox="typeof accentColorLocal !== 'undefined'" /> @@ -190,13 +205,13 @@ v-model="fgTextColorLocal" name="fgTextColor" :label="$t('settings.text')" - :fallback="previewTheme.colors.fgText" + :fallback="previewTheme.colors?.fgText" /> <ColorInput v-model="fgLinkColorLocal" name="fgLinkColor" :label="$t('settings.links')" - :fallback="previewTheme.colors.fgLink" + :fallback="previewTheme.colors?.fgLink" /> <p>{{ $t('settings.style.common_colors.foreground_hint') }}</p> </div> @@ -256,14 +271,14 @@ <ColorInput v-model="postLinkColorLocal" name="postLinkColor" - :fallback="previewTheme.colors.accent" + :fallback="previewTheme.colors?.accent" :label="$t('settings.links')" /> <ContrastRatio :contrast="previewContrast.postLink" /> <ColorInput v-model="postGreentextColorLocal" name="postGreentextColor" - :fallback="previewTheme.colors.cGreen" + :fallback="previewTheme.colors?.cGreen" :label="$t('settings.greentext')" /> <ContrastRatio :contrast="previewContrast.postGreentext" /> @@ -272,13 +287,13 @@ v-model="alertErrorColorLocal" name="alertError" :label="$t('settings.style.advanced_colors.alert_error')" - :fallback="previewTheme.colors.alertError" + :fallback="previewTheme.colors?.alertError" /> <ColorInput v-model="alertErrorTextColorLocal" name="alertErrorText" :label="$t('settings.text')" - :fallback="previewTheme.colors.alertErrorText" + :fallback="previewTheme.colors?.alertErrorText" /> <ContrastRatio :contrast="previewContrast.alertErrorText" @@ -288,13 +303,13 @@ v-model="alertWarningColorLocal" name="alertWarning" :label="$t('settings.style.advanced_colors.alert_warning')" - :fallback="previewTheme.colors.alertWarning" + :fallback="previewTheme.colors?.alertWarning" /> <ColorInput v-model="alertWarningTextColorLocal" name="alertWarningText" :label="$t('settings.text')" - :fallback="previewTheme.colors.alertWarningText" + :fallback="previewTheme.colors?.alertWarningText" /> <ContrastRatio :contrast="previewContrast.alertWarningText" @@ -304,13 +319,13 @@ v-model="alertNeutralColorLocal" name="alertNeutral" :label="$t('settings.style.advanced_colors.alert_neutral')" - :fallback="previewTheme.colors.alertNeutral" + :fallback="previewTheme.colors?.alertNeutral" /> <ColorInput v-model="alertNeutralTextColorLocal" name="alertNeutralText" :label="$t('settings.text')" - :fallback="previewTheme.colors.alertNeutralText" + :fallback="previewTheme.colors?.alertNeutralText" /> <ContrastRatio :contrast="previewContrast.alertNeutralText" @@ -319,7 +334,7 @@ <OpacityInput v-model="alertOpacityLocal" name="alertOpacity" - :fallback="previewTheme.opacity.alert" + :fallback="previewTheme.opacity?.alert" /> </div> <div class="color-item"> @@ -328,13 +343,13 @@ v-model="badgeNotificationColorLocal" name="badgeNotification" :label="$t('settings.style.advanced_colors.badge_notification')" - :fallback="previewTheme.colors.badgeNotification" + :fallback="previewTheme.colors?.badgeNotification" /> <ColorInput v-model="badgeNotificationTextColorLocal" name="badgeNotificationText" :label="$t('settings.text')" - :fallback="previewTheme.colors.badgeNotificationText" + :fallback="previewTheme.colors?.badgeNotificationText" /> <ContrastRatio :contrast="previewContrast.badgeNotificationText" @@ -346,19 +361,19 @@ <ColorInput v-model="panelColorLocal" name="panelColor" - :fallback="previewTheme.colors.panel" + :fallback="previewTheme.colors?.panel" :label="$t('settings.background')" /> <OpacityInput v-model="panelOpacityLocal" name="panelOpacity" - :fallback="previewTheme.opacity.panel" + :fallback="previewTheme.opacity?.panel" :disabled="panelColorLocal === 'transparent'" /> <ColorInput v-model="panelTextColorLocal" name="panelTextColor" - :fallback="previewTheme.colors.panelText" + :fallback="previewTheme.colors?.panelText" :label="$t('settings.text')" /> <ContrastRatio @@ -368,7 +383,7 @@ <ColorInput v-model="panelLinkColorLocal" name="panelLinkColor" - :fallback="previewTheme.colors.panelLink" + :fallback="previewTheme.colors?.panelLink" :label="$t('settings.links')" /> <ContrastRatio @@ -381,20 +396,20 @@ <ColorInput v-model="topBarColorLocal" name="topBarColor" - :fallback="previewTheme.colors.topBar" + :fallback="previewTheme.colors?.topBar" :label="$t('settings.background')" /> <ColorInput v-model="topBarTextColorLocal" name="topBarTextColor" - :fallback="previewTheme.colors.topBarText" + :fallback="previewTheme.colors?.topBarText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.topBarText" /> <ColorInput v-model="topBarLinkColorLocal" name="topBarLinkColor" - :fallback="previewTheme.colors.topBarLink" + :fallback="previewTheme.colors?.topBarLink" :label="$t('settings.links')" /> <ContrastRatio :contrast="previewContrast.topBarLink" /> @@ -404,19 +419,19 @@ <ColorInput v-model="inputColorLocal" name="inputColor" - :fallback="previewTheme.colors.input" + :fallback="previewTheme.colors?.input" :label="$t('settings.background')" /> <OpacityInput v-model="inputOpacityLocal" name="inputOpacity" - :fallback="previewTheme.opacity.input" + :fallback="previewTheme.opacity?.input" :disabled="inputColorLocal === 'transparent'" /> <ColorInput v-model="inputTextColorLocal" name="inputTextColor" - :fallback="previewTheme.colors.inputText" + :fallback="previewTheme.colors?.inputText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.inputText" /> @@ -426,33 +441,33 @@ <ColorInput v-model="btnColorLocal" name="btnColor" - :fallback="previewTheme.colors.btn" + :fallback="previewTheme.colors?.btn" :label="$t('settings.background')" /> <OpacityInput v-model="btnOpacityLocal" name="btnOpacity" - :fallback="previewTheme.opacity.btn" + :fallback="previewTheme.opacity?.btn" :disabled="btnColorLocal === 'transparent'" /> <ColorInput v-model="btnTextColorLocal" name="btnTextColor" - :fallback="previewTheme.colors.btnText" + :fallback="previewTheme.colors?.btnText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.btnText" /> <ColorInput v-model="btnPanelTextColorLocal" name="btnPanelTextColor" - :fallback="previewTheme.colors.btnPanelText" + :fallback="previewTheme.colors?.btnPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ContrastRatio :contrast="previewContrast.btnPanelText" /> <ColorInput v-model="btnTopBarTextColorLocal" name="btnTopBarTextColor" - :fallback="previewTheme.colors.btnTopBarText" + :fallback="previewTheme.colors?.btnTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <ContrastRatio :contrast="previewContrast.btnTopBarText" /> @@ -460,27 +475,27 @@ <ColorInput v-model="btnPressedColorLocal" name="btnPressedColor" - :fallback="previewTheme.colors.btnPressed" + :fallback="previewTheme.colors?.btnPressed" :label="$t('settings.background')" /> <ColorInput v-model="btnPressedTextColorLocal" name="btnPressedTextColor" - :fallback="previewTheme.colors.btnPressedText" + :fallback="previewTheme.colors?.btnPressedText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.btnPressedText" /> <ColorInput v-model="btnPressedPanelTextColorLocal" name="btnPressedPanelTextColor" - :fallback="previewTheme.colors.btnPressedPanelText" + :fallback="previewTheme.colors?.btnPressedPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ContrastRatio :contrast="previewContrast.btnPressedPanelText" /> <ColorInput v-model="btnPressedTopBarTextColorLocal" name="btnPressedTopBarTextColor" - :fallback="previewTheme.colors.btnPressedTopBarText" + :fallback="previewTheme.colors?.btnPressedTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <ContrastRatio :contrast="previewContrast.btnPressedTopBarText" /> @@ -488,52 +503,52 @@ <ColorInput v-model="btnDisabledColorLocal" name="btnDisabledColor" - :fallback="previewTheme.colors.btnDisabled" + :fallback="previewTheme.colors?.btnDisabled" :label="$t('settings.background')" /> <ColorInput v-model="btnDisabledTextColorLocal" name="btnDisabledTextColor" - :fallback="previewTheme.colors.btnDisabledText" + :fallback="previewTheme.colors?.btnDisabledText" :label="$t('settings.text')" /> <ColorInput v-model="btnDisabledPanelTextColorLocal" name="btnDisabledPanelTextColor" - :fallback="previewTheme.colors.btnDisabledPanelText" + :fallback="previewTheme.colors?.btnDisabledPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ColorInput v-model="btnDisabledTopBarTextColorLocal" name="btnDisabledTopBarTextColor" - :fallback="previewTheme.colors.btnDisabledTopBarText" + :fallback="previewTheme.colors?.btnDisabledTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <h5>{{ $t('settings.style.advanced_colors.toggled') }}</h5> <ColorInput v-model="btnToggledColorLocal" name="btnToggledColor" - :fallback="previewTheme.colors.btnToggled" + :fallback="previewTheme.colors?.btnToggled" :label="$t('settings.background')" /> <ColorInput v-model="btnToggledTextColorLocal" name="btnToggledTextColor" - :fallback="previewTheme.colors.btnToggledText" + :fallback="previewTheme.colors?.btnToggledText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.btnToggledText" /> <ColorInput v-model="btnToggledPanelTextColorLocal" name="btnToggledPanelTextColor" - :fallback="previewTheme.colors.btnToggledPanelText" + :fallback="previewTheme.colors?.btnToggledPanelText" :label="$t('settings.style.advanced_colors.panel_header')" /> <ContrastRatio :contrast="previewContrast.btnToggledPanelText" /> <ColorInput v-model="btnToggledTopBarTextColorLocal" name="btnToggledTopBarTextColor" - :fallback="previewTheme.colors.btnToggledTopBarText" + :fallback="previewTheme.colors?.btnToggledTopBarText" :label="$t('settings.style.advanced_colors.top_bar')" /> <ContrastRatio :contrast="previewContrast.btnToggledTopBarText" /> @@ -543,20 +558,20 @@ <ColorInput v-model="tabColorLocal" name="tabColor" - :fallback="previewTheme.colors.tab" + :fallback="previewTheme.colors?.tab" :label="$t('settings.background')" /> <ColorInput v-model="tabTextColorLocal" name="tabTextColor" - :fallback="previewTheme.colors.tabText" + :fallback="previewTheme.colors?.tabText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.tabText" /> <ColorInput v-model="tabActiveTextColorLocal" name="tabActiveTextColor" - :fallback="previewTheme.colors.tabActiveText" + :fallback="previewTheme.colors?.tabActiveText" :label="$t('settings.text')" /> <ContrastRatio :contrast="previewContrast.tabActiveText" /> @@ -566,13 +581,13 @@ <ColorInput v-model="borderColorLocal" name="borderColor" - :fallback="previewTheme.colors.border" + :fallback="previewTheme.colors?.border" :label="$t('settings.style.common.color')" /> <OpacityInput v-model="borderOpacityLocal" name="borderOpacity" - :fallback="previewTheme.opacity.border" + :fallback="previewTheme.opacity?.border" :disabled="borderColorLocal === 'transparent'" /> </div> @@ -581,25 +596,25 @@ <ColorInput v-model="faintColorLocal" name="faintColor" - :fallback="previewTheme.colors.faint" + :fallback="previewTheme.colors?.faint" :label="$t('settings.text')" /> <ColorInput v-model="faintLinkColorLocal" name="faintLinkColor" - :fallback="previewTheme.colors.faintLink" + :fallback="previewTheme.colors?.faintLink" :label="$t('settings.links')" /> <ColorInput v-model="panelFaintColorLocal" name="panelFaintColor" - :fallback="previewTheme.colors.panelFaint" + :fallback="previewTheme.colors?.panelFaint" :label="$t('settings.style.advanced_colors.panel_header')" /> <OpacityInput v-model="faintOpacityLocal" name="faintOpacity" - :fallback="previewTheme.opacity.faint" + :fallback="previewTheme.opacity?.faint" /> </div> <div class="color-item"> @@ -608,12 +623,12 @@ v-model="underlayColorLocal" name="underlay" :label="$t('settings.style.advanced_colors.underlay')" - :fallback="previewTheme.colors.underlay" + :fallback="previewTheme.colors?.underlay" /> <OpacityInput v-model="underlayOpacityLocal" name="underlayOpacity" - :fallback="previewTheme.opacity.underlay" + :fallback="previewTheme.opacity?.underlay" :disabled="underlayOpacityLocal === 'transparent'" /> </div> @@ -623,7 +638,7 @@ v-model="wallpaperColorLocal" name="wallpaper" :label="$t('settings.style.advanced_colors.wallpaper')" - :fallback="previewTheme.colors.wallpaper" + :fallback="previewTheme.colors?.wallpaper" /> </div> <div class="color-item"> @@ -632,13 +647,13 @@ v-model="pollColorLocal" name="poll" :label="$t('settings.background')" - :fallback="previewTheme.colors.poll" + :fallback="previewTheme.colors?.poll" /> <ColorInput v-model="pollTextColorLocal" name="pollText" :label="$t('settings.text')" - :fallback="previewTheme.colors.pollText" + :fallback="previewTheme.colors?.pollText" /> </div> <div class="color-item"> @@ -647,7 +662,7 @@ v-model="iconColorLocal" name="icon" :label="$t('settings.style.advanced_colors.icons')" - :fallback="previewTheme.colors.icon" + :fallback="previewTheme.colors?.icon" /> </div> <div class="color-item"> @@ -656,20 +671,20 @@ v-model="highlightColorLocal" name="highlight" :label="$t('settings.background')" - :fallback="previewTheme.colors.highlight" + :fallback="previewTheme.colors?.highlight" /> <ColorInput v-model="highlightTextColorLocal" name="highlightText" :label="$t('settings.text')" - :fallback="previewTheme.colors.highlightText" + :fallback="previewTheme.colors?.highlightText" /> <ContrastRatio :contrast="previewContrast.highlightText" /> <ColorInput v-model="highlightLinkColorLocal" name="highlightLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.highlightLink" + :fallback="previewTheme.colors?.highlightLink" /> <ContrastRatio :contrast="previewContrast.highlightLink" /> </div> @@ -679,26 +694,26 @@ v-model="popoverColorLocal" name="popover" :label="$t('settings.background')" - :fallback="previewTheme.colors.popover" + :fallback="previewTheme.colors?.popover" /> <OpacityInput v-model="popoverOpacityLocal" name="popoverOpacity" - :fallback="previewTheme.opacity.popover" + :fallback="previewTheme.opacity?.popover" :disabled="popoverOpacityLocal === 'transparent'" /> <ColorInput v-model="popoverTextColorLocal" name="popoverText" :label="$t('settings.text')" - :fallback="previewTheme.colors.popoverText" + :fallback="previewTheme.colors?.popoverText" /> <ContrastRatio :contrast="previewContrast.popoverText" /> <ColorInput v-model="popoverLinkColorLocal" name="popoverLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.popoverLink" + :fallback="previewTheme.colors?.popoverLink" /> <ContrastRatio :contrast="previewContrast.popoverLink" /> </div> @@ -708,20 +723,20 @@ v-model="selectedPostColorLocal" name="selectedPost" :label="$t('settings.background')" - :fallback="previewTheme.colors.selectedPost" + :fallback="previewTheme.colors?.selectedPost" /> <ColorInput v-model="selectedPostTextColorLocal" name="selectedPostText" :label="$t('settings.text')" - :fallback="previewTheme.colors.selectedPostText" + :fallback="previewTheme.colors?.selectedPostText" /> <ContrastRatio :contrast="previewContrast.selectedPostText" /> <ColorInput v-model="selectedPostLinkColorLocal" name="selectedPostLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.selectedPostLink" + :fallback="previewTheme.colors?.selectedPostLink" /> <ContrastRatio :contrast="previewContrast.selectedPostLink" /> </div> @@ -731,20 +746,20 @@ v-model="selectedMenuColorLocal" name="selectedMenu" :label="$t('settings.background')" - :fallback="previewTheme.colors.selectedMenu" + :fallback="previewTheme.colors?.selectedMenu" /> <ColorInput v-model="selectedMenuTextColorLocal" name="selectedMenuText" :label="$t('settings.text')" - :fallback="previewTheme.colors.selectedMenuText" + :fallback="previewTheme.colors?.selectedMenuText" /> <ContrastRatio :contrast="previewContrast.selectedMenuText" /> <ColorInput v-model="selectedMenuLinkColorLocal" name="selectedMenuLink" :label="$t('settings.links')" - :fallback="previewTheme.colors.selectedMenuLink" + :fallback="previewTheme.colors?.selectedMenuLink" /> <ContrastRatio :contrast="previewContrast.selectedMenuLink" /> </div> @@ -753,57 +768,57 @@ <ColorInput v-model="chatBgColorLocal" name="chatBgColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.background')" /> <h5>{{ $t('settings.style.advanced_colors.chat.incoming') }}</h5> <ColorInput v-model="chatMessageIncomingBgColorLocal" name="chatMessageIncomingBgColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.background')" /> <ColorInput v-model="chatMessageIncomingTextColorLocal" name="chatMessageIncomingTextColor" - :fallback="previewTheme.colors.text" + :fallback="previewTheme.colors?.text" :label="$t('settings.text')" /> <ColorInput v-model="chatMessageIncomingLinkColorLocal" name="chatMessageIncomingLinkColor" - :fallback="previewTheme.colors.link" + :fallback="previewTheme.colors?.link" :label="$t('settings.links')" /> <ColorInput v-model="chatMessageIncomingBorderColorLocal" name="chatMessageIncomingBorderLinkColor" - :fallback="previewTheme.colors.fg" + :fallback="previewTheme.colors?.fg" :label="$t('settings.style.advanced_colors.chat.border')" /> <h5>{{ $t('settings.style.advanced_colors.chat.outgoing') }}</h5> <ColorInput v-model="chatMessageOutgoingBgColorLocal" name="chatMessageOutgoingBgColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.background')" /> <ColorInput v-model="chatMessageOutgoingTextColorLocal" name="chatMessageOutgoingTextColor" - :fallback="previewTheme.colors.text" + :fallback="previewTheme.colors?.text" :label="$t('settings.text')" /> <ColorInput v-model="chatMessageOutgoingLinkColorLocal" name="chatMessageOutgoingLinkColor" - :fallback="previewTheme.colors.link" + :fallback="previewTheme.colors?.link" :label="$t('settings.links')" /> <ColorInput v-model="chatMessageOutgoingBorderColorLocal" name="chatMessageOutgoingBorderLinkColor" - :fallback="previewTheme.colors.bg" + :fallback="previewTheme.colors?.bg" :label="$t('settings.style.advanced_colors.chat.border')" /> </div> @@ -826,7 +841,7 @@ v-model="btnRadiusLocal" name="btnRadius" :label="$t('settings.btnRadius')" - :fallback="previewTheme.radii.btn" + :fallback="previewTheme.radii?.btn" max="16" hard-min="0" /> @@ -834,7 +849,7 @@ v-model="inputRadiusLocal" name="inputRadius" :label="$t('settings.inputRadius')" - :fallback="previewTheme.radii.input" + :fallback="previewTheme.radii?.input" max="9" hard-min="0" /> @@ -842,7 +857,7 @@ v-model="checkboxRadiusLocal" name="checkboxRadius" :label="$t('settings.checkboxRadius')" - :fallback="previewTheme.radii.checkbox" + :fallback="previewTheme.radii?.checkbox" max="16" hard-min="0" /> @@ -850,7 +865,7 @@ v-model="panelRadiusLocal" name="panelRadius" :label="$t('settings.panelRadius')" - :fallback="previewTheme.radii.panel" + :fallback="previewTheme.radii?.panel" max="50" hard-min="0" /> @@ -858,7 +873,7 @@ v-model="avatarRadiusLocal" name="avatarRadius" :label="$t('settings.avatarRadius')" - :fallback="previewTheme.radii.avatar" + :fallback="previewTheme.radii?.avatar" max="28" hard-min="0" /> @@ -866,7 +881,7 @@ v-model="avatarAltRadiusLocal" name="avatarAltRadius" :label="$t('settings.avatarAltRadius')" - :fallback="previewTheme.radii.avatarAlt" + :fallback="previewTheme.radii?.avatarAlt" max="28" hard-min="0" /> @@ -874,7 +889,7 @@ v-model="attachmentRadiusLocal" name="attachmentRadius" :label="$t('settings.attachmentRadius')" - :fallback="previewTheme.radii.attachment" + :fallback="previewTheme.radii?.attachment" max="50" hard-min="0" /> @@ -882,7 +897,7 @@ v-model="tooltipRadiusLocal" name="tooltipRadius" :label="$t('settings.tooltipRadius')" - :fallback="previewTheme.radii.tooltip" + :fallback="previewTheme.radii?.tooltip" max="50" hard-min="0" /> @@ -890,7 +905,7 @@ v-model="chatMessageRadiusLocal" name="chatMessageRadius" :label="$t('settings.chatMessageRadius')" - :fallback="previewTheme.radii.chatMessage || 2" + :fallback="previewTheme.radii?.chatMessage || 2" max="50" hard-min="0" /> @@ -996,26 +1011,26 @@ v-model="fontsLocal.interface" name="ui" :label="$t('settings.style.fonts.components.interface')" - :fallback="previewTheme.fonts.interface" + :fallback="previewTheme.fonts?.interface" no-inherit="1" /> <FontControl v-model="fontsLocal.input" name="input" :label="$t('settings.style.fonts.components.input')" - :fallback="previewTheme.fonts.input" + :fallback="previewTheme.fonts?.input" /> <FontControl v-model="fontsLocal.post" name="post" :label="$t('settings.style.fonts.components.post')" - :fallback="previewTheme.fonts.post" + :fallback="previewTheme.fonts?.post" /> <FontControl v-model="fontsLocal.postCode" name="postCode" :label="$t('settings.style.fonts.components.postCode')" - :fallback="previewTheme.fonts.postCode" + :fallback="previewTheme.fonts?.postCode" /> </div> </tab-switcher> |
