diff options
| author | Henry Jameson <me@hjkos.com> | 2024-07-25 11:53:58 +0300 |
|---|---|---|
| committer | Henry Jameson <me@hjkos.com> | 2024-07-25 11:53:58 +0300 |
| commit | e8d7d341f03846a3faf85670866b2cb8d9d12cc9 (patch) | |
| tree | bdb2d0d8cd673ef6ee13df47f0cd3f986da0b248 /src/components | |
| parent | 0ca9a2c8c0d04b3468c93de4a53fac8197581d8b (diff) | |
| parent | 4797b13625f5848e3f7d437ec9dcf6073607a647 (diff) | |
Merge remote-tracking branch 'origin/develop' into fix-develop-issues
Diffstat (limited to 'src/components')
28 files changed, 1125 insertions, 545 deletions
diff --git a/src/components/emoji_picker/emoji_picker.js b/src/components/emoji_picker/emoji_picker.js index eb665c40..d71bc1bb 100644 --- a/src/components/emoji_picker/emoji_picker.js +++ b/src/components/emoji_picker/emoji_picker.js @@ -120,6 +120,7 @@ const EmojiPicker = { groupRefs: {}, emojiRefs: {}, filteredEmojiGroups: [], + emojiSize: 0, width: 0 } }, @@ -130,6 +131,23 @@ const EmojiPicker = { Popover }, methods: { + updateEmojiSize () { + const css = window.getComputedStyle(this.$refs.popover.$el) + const emojiSize = css.getPropertyValue('--emojiSize') + const emojiSizeUnit = emojiSize.replace(/[0-9,.]+/, '') + const emojiSizeValue = Number(emojiSize.replace(/[^0-9,.]+/, '')) + const fontSize = css.getPropertyValue('font-size').replace(/[^0-9,.]+/, '') + + let emojiSizeReal + if (emojiSizeUnit.endsWith('em')) { + emojiSizeReal = emojiSizeValue * fontSize + } else { + emojiSizeReal = emojiSizeValue + } + + const fullEmojiSize = emojiSizeReal + (2 * 0.2 * fontSize) + this.emojiSize = fullEmojiSize + }, showPicker () { this.$refs.popover.showPopover() this.onShowing() @@ -224,6 +242,7 @@ const EmojiPicker = { }, onShowing () { const oldContentLoaded = this.contentLoaded + this.updateEmojiSize() this.recalculateItemPerRow() this.$nextTick(() => { this.$refs.search.focus() @@ -266,16 +285,20 @@ const EmojiPicker = { }, computed: { minItemSize () { - return this.emojiHeight + return this.emojiSize }, - emojiHeight () { - return 32 + 4 + // used to watch it + fontSize () { + this.$nextTick(() => { + this.updateEmojiSize() + }) + return this.$store.getters.mergedConfig.fontSize }, - emojiWidth () { - return 32 + 4 + emojiHeight () { + return this.emojiSize }, itemPerRow () { - return this.width ? Math.floor(this.width / this.emojiWidth - 1) : 6 + return this.width ? Math.floor(this.width / this.emojiSize) : 6 }, activeGroupView () { return this.showingStickers ? '' : this.activeGroup diff --git a/src/components/emoji_picker/emoji_picker.scss b/src/components/emoji_picker/emoji_picker.scss index 5602a16b..12c09388 100644 --- a/src/components/emoji_picker/emoji_picker.scss +++ b/src/components/emoji_picker/emoji_picker.scss @@ -1,9 +1,6 @@ -$emoji-picker-header-height: 36px; -$emoji-picker-header-picture-width: 32px; -$emoji-picker-header-picture-height: 32px; -$emoji-picker-emoji-size: 32px; - .emoji-picker { + --__emoji-picker-header: 2.2em; + width: 25em; max-width: calc(100vw - 20px); // popover gives 10px margin from window edge display: flex; @@ -13,24 +10,26 @@ $emoji-picker-emoji-size: 32px; display: inline-flex; justify-content: center; align-items: center; - width: $emoji-picker-header-picture-width; - max-width: $emoji-picker-header-picture-width; - height: $emoji-picker-header-picture-height; - max-height: $emoji-picker-header-picture-height; + width: var(--__emoji-picker-header); + max-width: var(--__emoji-picker-header); + height: var(--__emoji-picker-header); + max-height: var(--__emoji-picker-header); .still-image { - max-width: 100%; - max-height: 100%; - height: 100%; - width: 100%; + width: var(--__emoji-picker-header); + max-width: var(--__emoji-picker-header); + height: var(--__emoji-picker-header); + max-height: var(--__emoji-picker-header); object-fit: contain; + + --_still_image-label-scale: 0.5; } } .keep-open, .too-many-emoji, .hide-custom-emoji { - padding: 7px; + padding: 0.5em; line-height: normal; } @@ -44,13 +43,13 @@ $emoji-picker-emoji-size: 32px; } .keep-open-label { - padding: 0 7px; + padding: 0 0.5em; display: flex; } .heading { display: flex; - padding: 10px 7px 5px; + padding: 0.7em 0.5em 0; } .content { @@ -65,13 +64,14 @@ $emoji-picker-emoji-size: 32px; display: flex; flex-flow: row nowrap; overflow-x: auto; + overflow-y: hidden; } .additional-tabs { display: flex; border-left: 1px solid; border-left-color: var(--border); - padding-left: 7px; + padding-left: 0.5em; flex: 0 0 auto; } @@ -80,25 +80,29 @@ $emoji-picker-emoji-size: 32px; flex-basis: auto; display: flex; align-content: center; + scrollbar-width: thin; &-item { - padding: 0 7px; + padding: 0 0.5em; cursor: pointer; - font-size: 1.85em; - width: $emoji-picker-header-picture-width; - max-width: $emoji-picker-header-picture-width; - height: $emoji-picker-header-picture-height; - max-height: $emoji-picker-header-picture-height; + width: var(--__emoji-picker-header); + max-width: var(--__emoji-picker-header); + height: var(--__emoji-picker-header); + max-height: var(--__emoji-picker-header); display: flex; align-items: center; + .svg-inline--fa { + font-size: 1.85em; + } + &.disabled { opacity: 0.5; pointer-events: none; } &.toggled { - border-bottom: 4px solid; + border-bottom: 0.2em solid; } } } @@ -125,7 +129,7 @@ $emoji-picker-emoji-size: 32px; .emoji { &-search { - padding: 5px; + padding: 0.3em; flex: 0 0 auto; input { @@ -139,6 +143,7 @@ $emoji-picker-emoji-size: 32px; flex: 1 1 1px; position: relative; overflow: auto; + scrollbar-gutter: stable both-edges; user-select: none; mask: linear-gradient(to top, white 0, transparent 100%) bottom no-repeat, @@ -165,13 +170,13 @@ $emoji-picker-emoji-size: 32px; display: flex; align-items: center; flex-wrap: wrap; - padding-left: 5px; justify-content: left; &-title { font-size: 0.85em; width: 100%; margin: 0; + padding-left: 0.3em; &.disabled { display: none; @@ -180,24 +185,28 @@ $emoji-picker-emoji-size: 32px; } &-item { - width: $emoji-picker-emoji-size; - height: $emoji-picker-emoji-size; + width: var(--emoji-size); + height: var(--emoji-size); box-sizing: border-box; display: flex; - line-height: $emoji-picker-emoji-size; + line-height: var(--emoji-size); align-items: center; justify-content: center; - margin: 4px; + margin: 0.2em; cursor: pointer; .emoji-picker-emoji.-custom { object-fit: contain; - max-width: 100%; - max-height: 100%; + width: var(--emoji-size); + max-width: var(--emoji-size); + height: var(--emoji-size); + max-height: var(--emoji-size); + + --_still_image-label-scale: 0.5; } .emoji-picker-emoji.-unicode { - font-size: 24px; + font-size: 1.6em; overflow: hidden; } } diff --git a/src/components/emoji_reactions/emoji_reactions.vue b/src/components/emoji_reactions/emoji_reactions.vue index ad4a3c0b..3ab4c125 100644 --- a/src/components/emoji_reactions/emoji_reactions.vue +++ b/src/components/emoji_reactions/emoji_reactions.vue @@ -79,7 +79,7 @@ margin-top: 0.25em; flex-wrap: wrap; - --emoji-size: calc(1.25em * var(--emojiReactionsScale, 1)); + --emoji-size: calc(var(--emojiSize, 1.25em) * var(--emojiReactionsScale, 1)); .emoji-reaction-container { display: flex; diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js index 92ee3f30..d9394945 100644 --- a/src/components/font_control/font_control.js +++ b/src/components/font_control/font_control.js @@ -1,63 +1,59 @@ -import { set } from 'lodash' import Select from '../select/select.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' +import Popover from 'src/components/popover/popover.vue' + +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faExclamationTriangle, + faKeyboard, + faFont +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faExclamationTriangle, + faKeyboard, + faFont +) export default { components: { - Select + Select, + Checkbox, + Popover }, props: [ 'name', 'label', 'modelValue', 'fallback', 'options', 'no-inherit' ], + mounted () { + this.$store.dispatch('queryLocalFonts') + }, emits: ['update:modelValue'], data () { return { - lValue: this.modelValue, + manualEntry: false, availableOptions: [ this.noInherit ? '' : 'inherit', - 'custom', - ...(this.options || []), 'serif', + 'sans-serif', 'monospace', - 'sans-serif' + ...(this.options || []) ].filter(_ => _) } }, - beforeUpdate () { - this.lValue = this.modelValue + methods: { + toggleManualEntry () { + this.manualEntry = !this.manualEntry + } }, computed: { present () { - return typeof this.lValue !== 'undefined' - }, - dValue () { - return this.lValue || this.fallback || {} - }, - family: { - get () { - return this.dValue.family - }, - set (v) { - set(this.lValue, 'family', v) - this.$emit('update:modelValue', this.lValue) - } + return typeof this.modelValue !== 'undefined' }, - isCustom () { - return this.preset === 'custom' + localFontsList () { + return this.$store.state.interface.localFonts }, - preset: { - get () { - if (this.family === 'serif' || - this.family === 'sans-serif' || - this.family === 'monospace' || - this.family === 'inherit') { - return this.family - } else { - return 'custom' - } - }, - set (v) { - this.family = v === 'custom' ? '' : v - } + localFontsSize () { + return this.$store.state.interface.localFonts?.length } } } diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue index d2d1b388..fca3b360 100644 --- a/src/components/font_control/font_control.vue +++ b/src/components/font_control/font_control.vue @@ -1,6 +1,6 @@ <template> <div - class="font-control style-control" + class="font-control" :class="{ custom: isCustom }" > <label @@ -10,43 +10,121 @@ > {{ label }} </label> - <input + {{ ' ' }} + <Checkbox v-if="typeof fallback !== 'undefined'" :id="name + '-o'" - :aria-labelledby="name + '-label'" - class="input -checkbox opt exlcude-disabled visible-for-screenreader-only" - type="checkbox" - :checked="present" + :modelValue="present" @change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)" > - <label - v-if="typeof fallback !== 'undefined'" - class="opt-l" - :for="name + '-o'" - :aria-hidden="true" - /> - {{ ' ' }} - <Select - :id="name + '-font-switcher'" - v-model="preset" - :disabled="!present" - class="font-switcher" - > - <option - v-for="option in availableOptions" - :key="option" - :value="option" + {{ $t('settings.style.themes3.define') }} + </Checkbox> + <p v-if="modelValue?.family"> + <label + v-if="manualEntry" + :id="name + '-label'" + :for="preset === 'custom' ? name : name + '-font-switcher'" + class="label" > - {{ option === 'custom' ? $t('settings.style.fonts.custom') : option }} - </option> - </Select> - <input - v-if="isCustom" - :id="name" - v-model="family" - class="input custom-font" - type="text" - > + <i18n-t + keypath="settings.style.themes3.font.entry" + tag="span" + > + <template #fontFamily> + <code>font-family</code> + </template> + </i18n-t> + </label> + <label + v-else + :id="name + '-label'" + :for="preset === 'custom' ? name : name + '-font-switcher'" + class="label" + > + {{ $t('settings.style.themes3.font.select') }} + </label> + {{ ' ' }} + <span + v-if="manualEntry" + class="btn-group" + > + <button + class="btn button-default" + @click="toggleManualEntry" + :title="$t('settings.style.themes3.font.lookup_local_fonts')" + > + <FAIcon + fixed-width + icon="font" + /> + </button> + <input + :id="name" + :model-value="modelValue.family" + class="input custom-font" + type="text" + @update:modelValue="$emit('update:modelValue', { ...(modelValue || {}), family: $event.target.value })" + > + </span> + <span + v-else + class="btn-group" + > + <button + class="btn button-default" + @click="toggleManualEntry" + :title="$t('settings.style.themes3.font.enter_manually')" + > + <FAIcon + fixed-width + icon="keyboard" + /> + </button> + <Select + :id="name + '-local-font-switcher'" + :model-value="modelValue?.family" + class="custom-font" + @update:modelValue="v => $emit('update:modelValue', { ...(modelValue || {}), family: v })" + > + <optgroup + :label="$t('settings.style.themes3.font.group-builtin')" + > + <option + v-for="option in availableOptions" + :key="option" + :value="option" + :style="{ fontFamily: option === 'inherit' ? null : option }" + > + {{ $t('settings.style.themes3.font.builtin.' + option) }} + </option> + </optgroup> + <optgroup + v-if="localFontsSize > 0" + :label="$t('settings.style.themes3.font.group-local')" + > + <option + v-for="option in localFontsList" + :key="option" + :value="option" + :style="{ fontFamily: option }" + > + {{ option }} + </option> + </optgroup> + <optgroup + v-else + :label="$t('settings.style.themes3.font.group-local')" + > + <option disabled> + {{ $t('settings.style.themes3.font.local-unavailable1') }} + </option> + <option disabled> + {{ $t('settings.style.themes3.font.local-unavailable2') }} + </option> + </optgroup> + </Select> + </span> + </p> </div> </template> @@ -54,21 +132,15 @@ <style lang="scss"> .font-control { - input.custom-font { - min-width: 10em; + .custom-font { + min-width: 20em; + max-width: 20em; } +} - &.custom { - /* TODO Should make proper joiners... */ - .font-switcher { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - .custom-font { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - } +.invalid-tooltip { + margin: 0.5em 1em; + min-width: 10em; + text-align: center; } </style> diff --git a/src/components/mobile_nav/mobile_nav.vue b/src/components/mobile_nav/mobile_nav.vue index 6e134ef2..76a90d3e 100644 --- a/src/components/mobile_nav/mobile_nav.vue +++ b/src/components/mobile_nav/mobile_nav.vue @@ -129,7 +129,7 @@ .mobile-nav { display: grid; line-height: var(--navbar-height); - grid-template-rows: 50px; + grid-template-rows: var(--navbar-height); grid-template-columns: 2fr auto; width: 100%; box-sizing: border-box; @@ -190,8 +190,8 @@ justify-content: space-between; z-index: calc(var(--ZI_navbar) + 100); width: 100%; - height: 50px; - line-height: 50px; + height: 3.5em; + line-height: 3.5em; position: absolute; box-shadow: var(--shadow); @@ -214,7 +214,7 @@ } .mobile-notifications { - margin-top: 50px; + margin-top: 3.5em; width: 100vw; height: calc(100vh - var(--navbar-height)); overflow-x: hidden; diff --git a/src/components/navigation/navigation_pins.vue b/src/components/navigation/navigation_pins.vue index 36eb1ebe..decd1c04 100644 --- a/src/components/navigation/navigation_pins.vue +++ b/src/components/navigation/navigation_pins.vue @@ -49,6 +49,7 @@ } &.toggled { + margin-bottom: -4px; border-bottom: 4px solid; } } diff --git a/src/components/notification/notification.style.js b/src/components/notification/notification.style.js index 0d36760a..c6d317d1 100644 --- a/src/components/notification/notification.style.js +++ b/src/components/notification/notification.style.js @@ -11,7 +11,8 @@ export default { 'RichContent', 'Input', 'Avatar', - 'Attachment' + 'Attachment', + 'PollGraph' ], defaultRules: [] } diff --git a/src/components/panel.style.js b/src/components/panel.style.js index ad16c18f..1bba4766 100644 --- a/src/components/panel.style.js +++ b/src/components/panel.style.js @@ -20,6 +20,16 @@ export default { 'Tab', 'ListItem' ], + validInnerComponentsLite: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'Button', + 'Input', + 'PanelHeader', + 'Alert' + ], defaultRules: [ { directives: { diff --git a/src/components/root.style.js b/src/components/root.style.js index f9bdf16e..4bd735aa 100644 --- a/src/components/root.style.js +++ b/src/components/root.style.js @@ -12,6 +12,11 @@ export default { 'Alert', 'Button' // mobile post button ], + validInnerComponentsLite: [ + 'Underlay', + 'Scrollbar', + 'ScrollbarElement' + ], defaultRules: [ { directives: { diff --git a/src/components/settings_modal/helpers/number_setting.vue b/src/components/settings_modal/helpers/number_setting.vue index 23c1a5dd..32dc6f83 100644 --- a/src/components/settings_modal/helpers/number_setting.vue +++ b/src/components/settings_modal/helpers/number_setting.vue @@ -15,6 +15,7 @@ </template> <slot v-else /> </label> + {{ ' ' }} <input :id="path" class="input number-input" 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/unit_setting.js b/src/components/settings_modal/helpers/unit_setting.js index c9c23cb0..daeddd81 100644 --- a/src/components/settings_modal/helpers/unit_setting.js +++ b/src/components/settings_modal/helpers/unit_setting.js @@ -21,15 +21,23 @@ export default { unitSet: { type: String, default: 'none' + }, + step: { + type: Number, + default: 1 + }, + resetDefault: { + type: Object, + default: null } }, computed: { ...Setting.computed, stateUnit () { - return this.state.replace(/\d+/, '') + return typeof this.state === 'string' ? this.state.replace(/[0-9,.]+/, '') : '' }, stateValue () { - return this.state.replace(/\D+/, '') + return typeof this.state === 'string' ? this.state.replace(/[^0-9,.]+/, '') : '' } }, methods: { @@ -39,10 +47,18 @@ export default { return this.$t(['settings', 'units', this.unitSet, value].join('.')) }, updateValue (e) { - this.configSink(this.path, parseInt(e.target.value) + this.stateUnit) + this.configSink(this.path, parseFloat(e.target.value) + this.stateUnit) }, updateUnit (e) { - this.configSink(this.path, this.stateValue + e.target.value) + 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/unit_setting.vue b/src/components/settings_modal/helpers/unit_setting.vue index 68f52b1c..40ab6880 100644 --- a/src/components/settings_modal/helpers/unit_setting.vue +++ b/src/components/settings_modal/helpers/unit_setting.vue @@ -9,11 +9,12 @@ > <slot /> </label> + {{ ' ' }} <input :id="path" class="input number-input" type="number" - step="1" + :step="step" :disabled="disabled" :min="min || 0" :value="stateValue" 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.vue b/src/components/settings_modal/settings_modal.vue index 50859c94..90dbbde0 100644 --- a/src/components/settings_modal/settings_modal.vue +++ b/src/components/settings_modal/settings_modal.vue @@ -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_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.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..98d38288 --- /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/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..55f0d1be --- /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; + } + + .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.vue b/src/components/settings_modal/tabs/filtering_tab.vue index 821ca750..c86810d5 100644 --- a/src/components/settings_modal/tabs/filtering_tab.vue +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -50,24 +50,6 @@ </BooleanSetting> </li> <li> - <BooleanSetting path="hideMutedFederationRestrictions"> - {{ $t('settings.hide_muted_federation_restrictions') }} - </BooleanSetting> - <ul - class="setting-list suboptions" - :class="[{disabled: !streaming}]" - > - <li - v-for="item in muteFederationRestrictionsLevels" - :key="'mute_' + item + '_federation_restriction'" - > - <BooleanSetting :path="'muteFederationRestrictions.' + item"> - {{ $t('settings.mute_' + item + '_federation_restriction') }} - </BooleanSetting> - </li> - </ul> - </li> - <li> <BooleanSetting path="hidePostStats"> {{ $t('settings.hide_post_stats') }} </BooleanSetting> diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js index 7d701d34..96caab07 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 UnitSetting, { defaultHorizontalUnits } from '../helpers/unit_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, @@ -70,9 +65,6 @@ const GeneralTab = { ProfileSettingIndicator }, computed: { - horizontalUnits () { - return defaultHorizontalUnits - }, postFormats () { return this.$store.state.instance.postFormats || [] }, @@ -83,29 +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) { - this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val }) - } - }, ...SharedComputedObject() }, methods: { diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue index 208c49ee..4ece6cf4 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"> - <UnitSetting - v-for="column in columns" - :key="column" - :path="column + 'ColumnWidth'" - :units="horizontalUnits" - expert="1" - > - {{ $t('settings.column_sizes_' + column) }} - </UnitSetting> - </div> - </li> <li class="select-multiple"> <span class="label">{{ $t('settings.confirm_dialogs') }}</span> <ul class="option-list"> @@ -201,14 +149,6 @@ <h2>{{ $t('settings.post_look_feel') }}</h2> <ul class="setting-list"> <li> - <BooleanSetting - path="forceThemeRecompilation" - :expert="1" - > - {{ $t('settings.force_theme_recompilation_debug') }} - </BooleanSetting> - </li> - <li> <ChoiceSetting id="conversationDisplay" path="conversationDisplay" @@ -277,15 +217,6 @@ {{ $t('settings.no_rich_text_description') }} </BooleanSetting> </li> - <li> - <FloatSetting - v-if="user" - path="emojiReactionsScale" - expert="1" - > - {{ $t('settings.emoji_reactions_scale') }} - </FloatSetting> - </li> <h3>{{ $t('settings.attachments') }}</h3> <li> <BooleanSetting @@ -528,17 +459,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/theme_tab/preview.vue b/src/components/settings_modal/tabs/theme_tab/preview.vue index 1837620f..3fb0558b 100644 --- a/src/components/settings_modal/tabs/theme_tab/preview.vue +++ b/src/components/settings_modal/tabs/theme_tab/preview.vue @@ -99,15 +99,9 @@ > <div class="actions"> - <span class="checkbox"> - <input - id="preview_checkbox" - checked="very yes" - type="checkbox" - class="input" - > - <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> @@ -118,6 +112,7 @@ </template> <script> +import Checkbox from 'src/components/checkbox/checkbox.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes, @@ -133,12 +128,116 @@ library.add( faReply ) -export default {} +export default { + components: { + Checkbox + } +} </script> <style lang="scss"> .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 { @@ -148,4 +247,4 @@ export default {} 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 11c90b03..39dc372e 100644 --- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -1,7 +1,8 @@ import { rgb2hex, hex2rgb, - getContrastRatioLayers + getContrastRatioLayers, + relativeLuminance } from 'src/services/color_convert/color_convert.js' import { getThemes @@ -23,10 +24,17 @@ import { generateShadows, generateRadii, generateFonts, - composePreset, 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' @@ -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,17 +502,14 @@ 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, @@ -532,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() }, @@ -610,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 { @@ -692,6 +688,8 @@ export default { } else { this.shadowsLocal = shadows } + this.updatePreviewColors() + this.updatePreviewShadows() this.shadowSelected = this.shadowsAvailable[0] } @@ -699,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 @@ -713,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 @@ -727,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 @@ -738,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 5e633120..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,4 +1,9 @@ .theme-tab { + .deprecation-warning { + padding: 0.5em; + margin: 2em; + } + padding-bottom: 2em; .preset-switcher { @@ -10,6 +15,10 @@ margin-right: 0.25em; } + .btn-group .btn { + margin: 0; + } + .style-control { display: flex; align-items: baseline; @@ -157,107 +166,6 @@ } } - .preview-container { - 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%; - - .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 { - display: inline-flex; - align-items: baseline; - margin-right: 1em; - flex: 1; - } - } - - .separator { - margin: 1em; - border-bottom: 1px solid; - border-color: var(--border); - } - - .btn { - min-width: 3em; - } - } - } - .radius-item { flex-basis: auto; } @@ -310,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> diff --git a/src/components/status/post.style.js b/src/components/status/post.style.js index 8dce527e..d0038424 100644 --- a/src/components/status/post.style.js +++ b/src/components/status/post.style.js @@ -17,6 +17,15 @@ export default { 'Attachment', 'PollGraph' ], + validInnerComponentsLite: [ + 'Text', + 'Link', + 'Icon', + 'Border', + 'ButtonUnstyled', + 'RichContent', + 'Avatar' + ], defaultRules: [ { directives: { |
