Page MenuHomePhorge

No OneTemporary

Authored By
Unknown
Size
52 KB
Referenced Files
None
Subscribers
None
diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue
index efd9243fda..b863d253a2 100644
--- a/src/components/font_control/font_control.vue
+++ b/src/components/font_control/font_control.vue
@@ -1,170 +1,178 @@
<template>
<div class="font-control">
<div class="setting-item">
<Checkbox
v-if="typeof fallback !== 'undefined'"
:id="name + '-o'"
class="font-checkbox setting-control setting-label"
:model-value="present"
@change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)"
>
<i18n-t
scope="global"
keypath="settings.style.fonts.override"
tag="span"
>
- <span class="label">
+ <span>
{{ label }}
</span>
</i18n-t>
</Checkbox>
</div>
{{ ' ' }}
<div
v-if="modelValue?.family"
class="font-input setting-item"
>
<label
v-if="manualEntry"
:id="name + '-label'"
:for="manualEntry ? name : name + '-font-switcher'"
class="label"
>
<i18n-t
keypath="settings.style.themes3.font.entry"
tag="span"
scope="global"
>
<template #fontFamily>
<code>font-family</code>
</template>
</i18n-t>
</label>
<label
v-else
:id="name + '-label'"
:for="manualEntry ? 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"
:title="$t('settings.style.themes3.font.lookup_local_fonts')"
@click="toggleManualEntry"
>
<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"
+ class="font-selector btn-group"
>
<button
class="btn button-default"
:title="$t('settings.style.themes3.font.enter_manually')"
@click="toggleManualEntry"
>
<FAIcon
fixed-width
icon="keyboard"
/>
</button>
<Select
:id="name + '-local-font-switcher'"
:model-value="modelValue?.family"
class="custom-font"
@update:model-value="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>
</div>
</div>
</template>
<script src="./font_control.js"></script>
<style lang="scss">
// Copy-paste of BooleanSetting
.font-control {
display: grid;
grid-template-columns: subgrid;
.checkbox {
display: grid;
grid-template-columns: subgrid;
}
.label {
grid-area: label;
text-align: right;
}
.-mobile & {
+ .font-input {
+ display: block;
+ }
+
+ .font-selector {
+ margin-top: 0.5em;
+ }
+
.label {
text-align: left;
}
}
.checkbox-indicator {
grid-area: control;
}
}
.invalid-tooltip {
margin: 0.5em 1em;
min-width: 10em;
text-align: center;
}
</style>
diff --git a/src/components/interface_language_switcher/interface_language_switcher.vue b/src/components/interface_language_switcher/interface_language_switcher.vue
index 155d46f351..1f7d419e97 100644
--- a/src/components/interface_language_switcher/interface_language_switcher.vue
+++ b/src/components/interface_language_switcher/interface_language_switcher.vue
@@ -1,71 +1,86 @@
<template>
<ul class="interface-language-switcher setting-list">
<li
v-for="index of controlledLanguage.keys()"
:key="index"
class="setting-item"
>
<label
class="setting-label"
:for="uniqueId+index"
>
{{ index === 0 ? $t('settings.primary_language') : $t('settings.fallback_language', { index }, index) }}
</label>
<span class="setting-control btn-group">
<Select
:name="uniqueId+index"
:id="uniqueId+index"
class="language-select"
:model-value="controlledLanguage[index]"
@update:model-value="val => setLanguageAt(index, val)"
>
<option
v-for="lang in languages"
:key="lang.code"
:value="lang.code"
>
{{ lang.name }}
</option>
</Select>
<button
v-if="controlledLanguage.length > 1 && index !== 0"
class="button-default btn"
@click="() => removeLanguageAt(index)"
>
{{ $t('settings.remove_language') }}
</button>
</span>
</li>
<li class="add-button">
<button
class="button-default btn"
@click="addLanguage"
>
{{ $t('settings.add_language') }}
</button>
</li>
</ul>
</template>
<script src="./interface_language_switcher.js"></script>
<style lang="scss">
.interface-language-switcher {
.setting-list {
.setting-item {
display: grid;
grid-template-columns: subgrid;
}
}
.add-button {
display: block;
text-align: center;
+ padding-bottom: 1em;
.default-button {
display: block;
width: auto;
}
}
+
+ .-mobile & {
+ li.setting-item {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5em;
+ align-items: stretch;
+ border-bottom: none;
+ }
+
+ .add-button {
+ border-bottom: 1px solid var(--border);
+ }
+ }
}
</style>
diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue
index 4cde6926b9..7f3adf5ade 100644
--- a/src/components/settings_modal/helpers/boolean_setting.vue
+++ b/src/components/settings_modal/helpers/boolean_setting.vue
@@ -1,70 +1,72 @@
<template>
<label
v-if="matchesExpertLevel"
class="BooleanSetting setting-item"
>
<Checkbox
class="setting-control setting-label"
:model-value="visibleState"
:disabled="shouldBeDisabled"
:indeterminate="isIndeterminate"
@update:model-value="update"
>
<span
class="label"
:class="{ 'faint': shouldBeDisabled }"
>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
{{ ' ' }}
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</span>
</Checkbox>
<p
- v-if="backendDescriptionDescription"
+ v-if="backendDescriptionDescription || showDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
- {{ backendDescriptionDescription + ' ' }}
+ <slot name="description">
+ {{ backendDescriptionDescription + ' ' }}
+ </slot>
</p>
<DraftButtons />
</label>
</template>
<script src="./boolean_setting.js"></script>
<style lang="scss">
.BooleanSetting {
display: grid;
grid-template-columns: subgrid;
.checkbox {
display: grid;
grid-template-columns: subgrid;
}
.label {
grid-area: label;
text-align: right;
}
.-mobile & {
.label {
text-align: left;
}
}
.checkbox-indicator {
grid-area: control;
}
}
</style>
diff --git a/src/components/settings_modal/helpers/list_setting.vue b/src/components/settings_modal/helpers/list_setting.vue
index b90331cdbb..1dacba4103 100644
--- a/src/components/settings_modal/helpers/list_setting.vue
+++ b/src/components/settings_modal/helpers/list_setting.vue
@@ -1,90 +1,92 @@
<template>
<div
v-if="matchesExpertLevel"
class="ListSetting setting-item"
>
<label
class="setting-label"
:class="{ 'faint': shouldBeDisabled }"
>
<template v-if="backendDescriptionLabel">
{{ backendDescriptionLabel + ' ' }}
</template>
<template v-else-if="source === 'admin'">
MISSING LABEL FOR {{ path }}
</template>
<slot v-else />
</label>
<p
v-if="backendDescriptionDescription"
class="setting-description"
:class="{ 'faint': shouldBeDisabled }"
>
{{ backendDescriptionDescription + ' ' }}
</p>
<ul class="setting-list">
<li
v-for="(item, i) in builtinEntries"
:key="i"
>
<Checkbox
:disabled="shouldBeDisabled"
:model-value="optionPresent(item.value)"
@update:model-value="event => update({ event, value: item.value, eventType: 'toggle' })"
>
{{ item.label }}
</Checkbox>
</li>
<li
v-for="(item, index) in extraEntries"
:key="index"
>
<div class="btn-group">
<input
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:value="item"
@change="e => update({ event: e, index, eventType: 'edit' })"
>
<button
class="button-default"
@click="e => update({ index, eventType: 'remove' })"
>
<FAIcon icon="times" />
</button>
</div>
</li>
<li v-if="showNew">
<div class="btn-group">
<input
v-model="newValue"
class="input string-input"
:class="{ disabled: shouldBeDisabled }"
:disabled="shouldBeDisabled"
>
<button
class="button-default"
@click="e => update({ eventType: 'add' })"
>
<FAIcon icon="plus" />
</button>
</div>
</li>
</ul>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ProfileSettingIndicator :is-profile="isProfileSetting" />
<DraftButtons />
</div>
</template>
<script src="./list_setting.js"></script>
<style lang="scss">
.ListSetting {
- .btn-group {
- display: flex
+ .setting-list {
+ .checkbox {
+ padding: 0.5em 0;
+ }
}
}
</style>
diff --git a/src/components/settings_modal/helpers/setting.js b/src/components/settings_modal/helpers/setting.js
index f786a35da2..4ed6227a19 100644
--- a/src/components/settings_modal/helpers/setting.js
+++ b/src/components/settings_modal/helpers/setting.js
@@ -1,305 +1,311 @@
import ModifiedIndicator from './modified_indicator.vue'
import ProfileSettingIndicator from './profile_setting_indicator.vue'
import DraftButtons from './draft_buttons.vue'
import { get, set, cloneDeep, isEqual } from 'lodash'
export default {
components: {
ModifiedIndicator,
DraftButtons,
ProfileSettingIndicator
},
props: {
modelValue: {
type: String,
default: null
},
path: {
type: [String, Array],
required: false
},
+ showDescription: {
+ type: Boolean,
+ required: false
+ },
descriptionPathOverride: {
type: [String, Array],
required: false
},
suggestions: {
type: [String, Array],
required: false
},
subgroup: {
type: String,
required: false
},
disabled: {
type: Boolean,
default: false
},
parentPath: {
type: [String, Array]
},
parentInvert: {
type: Boolean,
default: false
},
expert: {
type: [Number, String],
default: 0
},
source: {
type: String,
default: undefined
},
hideDraftButtons: { // this is for the weird backend hybrid (Boolean|String or Boolean|Number) settings
required: false,
type: Boolean
},
hideLabel: {
type: Boolean
},
hideDescription: {
type: Boolean
},
swapDescriptionAndLabel: {
type: Boolean
},
backendDescriptionPath: {
type: [String, Array]
},
overrideBackendDescription: {
type: Boolean
},
overrideBackendDescriptionLabel: {
- type: Boolean
+ type: [Boolean, String]
},
draftMode: {
type: Boolean,
default: undefined
},
timedApplyMode: {
type: Boolean,
default: false
}
},
inject: {
defaultSource: {
default: 'default'
},
defaultDraftMode: {
default: false
}
},
data () {
return {
localDraft: null
}
},
created () {
if (this.realDraftMode && (this.realSource !== 'admin' || this.path == null)) {
this.draft = cloneDeep(this.state)
}
},
computed: {
draft: {
get () {
if (this.realSource === 'admin' || this.path == null) {
return get(this.$store.state.adminSettings.draft, this.canonPath)
} else {
return this.localDraft
}
},
set (value) {
if (this.realSource === 'admin' || this.path == null) {
this.$store.commit('updateAdminDraft', { path: this.canonPath, value })
} else {
this.localDraft = value
}
}
},
state () {
if (this.path == null) {
return this.modelValue
}
const value = get(this.configSource, this.canonPath)
if (value === undefined) {
return this.defaultState
} else {
return value
}
},
visibleState () {
return this.realDraftMode ? this.draft : this.state
},
realSource () {
return this.source || this.defaultSource
},
realDraftMode () {
return typeof this.draftMode === 'undefined' ? this.defaultDraftMode : this.draftMode
},
backendDescription () {
return get(this.$store.state.adminSettings.descriptions, this.descriptionPath)
},
backendDescriptionLabel () {
if (this.realSource !== 'admin') return ''
if (this.overrideBackendDescriptionLabel !== '' && typeof this.overrideBackendDescriptionLabel === 'string') {
return this.overrideBackendDescriptionLabel
}
if (!this.backendDescription || this.overrideBackendDescriptionLabel) {
return this.$t([
'admin_dash',
'temp_overrides',
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
'label'
].join('.'))
} else {
return this.swapDescriptionAndLabel
? this.backendDescription?.description
: this.backendDescription?.label
}
},
backendDescriptionDescription () {
+ console.log('LOL', this.description)
+ if (this.description) return this.description
if (this.realSource !== 'admin') return ''
if (this.hideDescription) return null
if (!this.backendDescription || this.overrideBackendDescription) {
return this.$t([
'admin_dash',
'temp_overrides',
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
'description'
].join('.'))
} else {
return this.swapDescriptionAndLabel
? this.backendDescription?.label
: this.backendDescription?.description
}
},
backendDescriptionSuggestions () {
return this.backendDescription?.suggestions || this.suggestions
},
shouldBeDisabled () {
if (this.path == null) {
return this.disabled
}
let parentValue = null
if (this.parentPath !== undefined && this.realSource === 'admin') {
if (this.realDraftMode) {
parentValue = get(this.$store.state.adminSettings.draft, this.parentPath)
} else {
parentValue = get(this.configSource, this.parentPath)
}
}
return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false)
},
configSource () {
switch (this.realSource) {
case 'profile':
return this.$store.state.profileConfig
case 'admin':
return this.$store.state.adminSettings.config
default:
return this.$store.getters.mergedConfig
}
},
configSink () {
if (this.path == null) {
return (k, v) => this.$emit('update:modelValue', v)
}
switch (this.realSource) {
case 'profile':
return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
case 'admin':
return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v })
default:
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 () {
switch (this.realSource) {
case 'profile':
return {}
default:
return get(this.$store.getters.defaultConfig, this.path)
}
},
isProfileSetting () {
return this.realSource === 'profile'
},
isChanged () {
if (this.path == null) return false
switch (this.realSource) {
case 'profile':
case 'admin':
return false
default:
return this.state !== this.defaultState
}
},
canonPath () {
if (this.path == null) return null
return Array.isArray(this.path) ? this.path : this.path.split('.')
},
descriptionPath () {
if (this.path == null) return null
if (this.descriptionPathOverride) return this.descriptionPathOverride
const path = Array.isArray(this.path) ? this.path : this.path.split('.')
if (this.subgroup) {
return [
...path.slice(0, path.length - 1),
':subgroup,' + this.subgroup,
...path.slice(path.length - 1)
]
}
return path
},
isDirty () {
if (this.path == null) return false
if (this.realSource === 'admin' && this.canonPath.length > 3) {
return false // should not show draft buttons for "grouped" values
} else {
return this.realDraftMode && !isEqual(this.draft, this.state)
}
},
canHardReset () {
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths?.has(this.canonPath.join(' -> '))
},
matchesExpertLevel () {
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
}
},
methods: {
getValue (e) {
return e.target.value
},
update (e) {
if (this.realDraftMode) {
this.draft = this.getValue(e)
} else {
this.configSink(this.path, this.getValue(e))
}
},
commitDraft () {
if (this.realDraftMode) {
this.configSink(this.path, this.draft)
}
},
reset () {
if (this.realDraftMode) {
this.draft = cloneDeep(this.state)
} else {
set(this.$store.getters.mergedConfig, this.path, cloneDeep(this.defaultState))
}
},
hardReset () {
switch (this.realSource) {
case 'admin':
return this.$store.dispatch('resetAdminSetting', { path: this.path })
.then(() => { this.draft = this.state })
default:
console.warn('Hard reset not implemented yet!')
}
}
}
}
diff --git a/src/components/settings_modal/helpers/vertical_tab_switcher.scss b/src/components/settings_modal/helpers/vertical_tab_switcher.scss
index af765843e0..4bb4b7167c 100644
--- a/src/components/settings_modal/helpers/vertical_tab_switcher.scss
+++ b/src/components/settings_modal/helpers/vertical_tab_switcher.scss
@@ -1,179 +1,183 @@
.vertical-tab-switcher {
display: flex;
flex-direction: row;
container-type: inline-size;
> .tabs {
flex: 0 0 15em;
flex-direction: column;
overflow: hidden auto;
white-space: nowrap;
text-overflow: ellipsis;
width: 15em;
min-width: 15em;
border-right: 1px solid;
border-right-color: var(--border);
box-sizing: border-box;
> .menu-item {
padding: 0.5em 1em;
.tab-icon {
vertical-align: middle;
margin-right: 0.75em;
}
}
}
> .contents {
.tab-content-label {
box-sizing: border-box;
margin: 0;
border-bottom: 1px solid var(--border);
display: none;
button {
box-sizing: border-box;
padding: 0.5em;
}
}
.tab-slot-wrapper {
flex: 1 1 auto;
height: 100%;
overflow-y: auto;
display: grid;
grid-template-columns: minmax(1em, 1fr) minmax(min-content, 45em) minmax(1em, 1fr);
grid-template-areas: ". content .";
flex-direction: column;
.tab-content {
grid-area: content;
&.-full-width {
grid-column: 1 / 4;
}
&.-full-height {
> * {
height: 100%;
}
}
}
}
.tab-content-wrapper {
display: flex;
flex-direction: column;
height: 100%;
flex: 1 1 auto;
&.-hidden {
display: none;
}
}
}
&.-mobile {
> .contents {
.tab-content-label {
display: block
}
}
&.-nav-contents {
> .contents {
display: block;
flex-grow: 1;
flex-shrink: 1;
}
> .tabs {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
}
&.-nav-tabs {
> .tabs {
display: block;
flex-grow: 1;
}
> .contents {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
}
+
+ .tab-slot-wrapper {
+ grid-template-columns: 0 minmax(min-content, 45em) 0;
+ }
}
@supports (container-type: inline-size) {
&,
&.-mobile {
&.-nav-contents,
&.-nav-tabs {
/* I THINK it's a false positive and eslint doesn't understand the @-rule */
/* stylelint-disable no-descending-specificity */
> .contents {
display: block;
flex-grow: 1;
}
> .tabs {
display: block;
flex-grow: 0;
}
/* stylelint-enable no-descending-specificity */
}
}
@container (width < 50em) {
> .contents {
.tab-content-label {
display: block
}
}
&.-mobile {
> .contents {
.tab-content-label {
display: block
}
}
}
&,
&.-mobile {
&.-nav-contents {
> .contents {
display: block;
flex-grow: 1;
}
> .tabs {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
}
&.-nav-tabs {
/* stylelint-disable no-descending-specificity */
> .tabs {
display: block;
flex-grow: 1;
}
> .contents {
display: none;
flex-grow: 0;
flex-shrink: 1;
}
/* stylelint-enable no-descending-specificity */
}
}
}
}
}
diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss
index 44197bafee..303d1ae00a 100644
--- a/src/components/settings_modal/settings_modal.scss
+++ b/src/components/settings_modal/settings_modal.scss
@@ -1,207 +1,231 @@
.settings-modal {
overflow: hidden;
h2 {
font-size: 1.3rem;
font-weight: 500;
margin-top: 1em;
margin-bottom: 1em;
+ margin-right: 1em;
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-top: 1em;
margin-bottom: 0.5em;
+ margin-right: 1em;
border-bottom: 1px solid var(--border);
padding-bottom: 0.25em;
box-sizing: border-box;
padding-left: 0.5em;
}
h4 {
font-size: 1.1rem;
margin-top: 1em;
+ margin-right: 1em;
margin-bottom: 0.5em;
+ margin-left: 1em;
}
h5 {
font-size: 1rem;
- margin-bottom: 0.5em;
- margin-top: 0;
+ margin-right: 1em;
+ margin-bottom: 0.25em;
+ margin-top: 0.75em;
}
p {
line-height: 1.5;
}
+ .sidenote {
+ margin-left: 5em;
+ padding: 0.25em 1em;
+ margin-top: 0.25em;
+ }
+
+ .setting-description {
+ margin-top: 0.2em;
+ margin-bottom: 0;
+ font-size: 80%;
+ }
+
.setting-item {
display: grid;
grid-template-areas:
"label control"
". desc"
". draft";
- grid-template-columns: 1fr 1fr;
+ grid-template-columns: 2fr 3fr;
column-gap: 0.5em;
align-items: baseline;
padding: 0.5em 0;
.setting-label {
grid-area: label;
text-align: right;
}
.ModifiedIndicator,
.ProfileSettingIndicator {
grid-area: indicator;
}
.setting-control {
grid-area: control;
}
.setting-control.setting-label {
grid-column: 1 / 3;
text-align: left;
}
.setting-description {
grid-area: desc;
}
.DraftButtons {
grid-area: draft;
}
}
.vertical-tab-switcher {
height: 100%;
}
.setting-list,
.option-list {
list-style-type: none;
padding-left: 0;
margin: 0;
.btn:not(.dropdown-button) {
padding: 0 2em;
}
.btn-group {
.button-default {
flex: 0 1 auto;
}
}
&.two-column {
display: grid;
grid-template-columns: 1fr 1fr;
+ margin-left: 2em;
+ border-bottom: 1px solid var(--border);
+ padding-bottom: 0.5em;
+ margin-bottom: 1em;
+
+ .setting-item {
+ grid-template-columns: 3fr 1fr;
+ }
> li {
margin: 0;
break-inside: avoid;
}
}
}
- .setting-description {
- margin-top: 0.2em;
- margin-bottom: 0;
- font-size: 80%;
- }
-
.settings-modal-panel {
overflow: hidden;
transition: transform;
transition-timing-function: ease-in-out;
transition-duration: 300ms;
width: 70em;
max-width: 90vw;
height: 90vh;
@media all and (width <= 800px) {
max-width: 100vw;
height: 100%;
}
>.panel-body {
height: 100%;
overflow-y: hidden;
.btn {
min-height: 2em;
}
}
}
.settings-footer {
display: flex;
flex-wrap: wrap;
line-height: 2;
>* {
margin-right: 0.5em;
}
.extra-content {
display: flex;
flex-grow: 1;
}
}
&.-mobile {
.tabs {
.menu-item {
font-size: 1.2em;
padding-top: 0.75em;
padding-bottom: 0.75em;
}
}
.setting-item {
- border-bottom: 1px solid var(--border);
grid-template-columns: 1fr min-content;
column-gap: 0.5em;
- padding: 1em 0;
+ padding: 1em 1em;
align-items: center;
.setting-label {
text-align: left;
}
}
ul {
padding: 0;
+
+ li:not(:first-child) {
+ .setting-item {
+ border-top: 1px solid var(--border);
+ }
+ }
}
.setting-list:not(.suboptions),
.option-list {
&.two-column {
grid-template-columns: 1fr;
}
}
}
&.peek {
.settings-modal-panel {
/* Explanation:
* Modal is positioned vertically centered.
* 100vh - 100% = Distance between modal's top+bottom boundaries and screen
* (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen
* + 100% - we move modal completely off-screen, it's top boundary touches
* bottom of the screen
* - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible
*/
transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px));
@media all and (width <= 800px) {
/* For mobile, the modal takes 100% of the available screen.
This ensures the minimized modal is always 50px above the browser bottom
bar regardless of whether or not it is visible.
*/
transform: translateY(calc(100% - 50px));
}
}
}
}
diff --git a/src/components/settings_modal/settings_modal_user_content.vue b/src/components/settings_modal/settings_modal_user_content.vue
index 5360089d62..2bde5b21be 100644
--- a/src/components/settings_modal/settings_modal_user_content.vue
+++ b/src/components/settings_modal/settings_modal_user_content.vue
@@ -1,140 +1,139 @@
<template>
<vertical-tab-switcher
ref="tabSwitcher"
class="settings_tab-switcher"
:scrollable-tabs="true"
:child-collapsed="childCollapsed"
:body-scroll-lock="bodyLock"
:hide-header="navHideHeader"
>
<div
:label="$t('settings.general')"
icon="wrench"
data-tab-name="general"
>
<GeneralTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.profile_tab')"
icon="user"
data-tab-name="profile"
:full-width="true"
>
<ProfileTab />
</div>
<div
:label="$t('settings.composing')"
icon="pen-alt"
data-tab-name="composing"
:delay-render="true"
>
<ComposingTab />
</div>
<div
:label="$t('settings.posts')"
icon="message"
data-tab-name="posts"
:delay-render="true"
>
<PostsTab />
</div>
<div
:full-width="true"
:label="$t('settings.appearance')"
icon="window-restore"
data-tab-name="appearance"
:delay-render="true"
>
<AppearanceTab />
</div>
<div
- :full-width="$store.getters.mergedConfig.expertLevel > 0"
:label="$t('settings.layout')"
icon="table-columns"
data-tab-name="layout"
:delay-render="true"
>
<LayoutTab />
</div>
<div
v-if="isLoggedIn"
:full-width="true"
:label="$t('settings.notifications')"
icon="bell"
data-tab-name="notifications"
>
<NotificationsTab />
</div>
<div
:label="$t('settings.filtering')"
icon="filter"
data-tab-name="filtering"
>
<FilteringTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.mutes_and_blocks')"
icon="eye-slash"
data-tab-name="mutesAndBlocks"
:full-width="true"
:full-height="true"
>
<MutesAndBlocksTab />
</div>
<div
:label="$t('settings.clutter')"
icon="broom"
data-tab-name="clutter"
>
<ClutterTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.security_tab')"
icon="lock"
data-tab-name="security"
>
<SecurityTab />
</div>
<div
v-if="isLoggedIn"
:label="$t('settings.data_import_export_tab')"
icon="download"
data-tab-name="dataImportExport"
>
<DataImportExportTab />
</div>
<div
v-if="expertLevel > 0"
:label="$t('settings.style.themes3.editor.title')"
icon="palette"
data-tab-name="style"
:delay-render="true"
:full-width="true"
>
<StyleTab />
</div>
<div
v-if="expertLevel > 0"
:label="$t('settings.theme_old')"
icon="paint-brush"
data-tab-name="theme"
:delay-render="true"
:full-width="true"
>
<OldThemeTab />
</div>
<div
v-if="expertLevel > 0"
:label="$t('settings.developer')"
icon="code"
data-tab-name="developer"
>
<DeveloperTab />
</div>
</vertical-tab-switcher>
</template>
<script src="./settings_modal_user_content.js"></script>
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 4f1911cd8d..285fe1b68a 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -1,209 +1,208 @@
<template>
<div>
<div class="setting-section">
<h3>{{ $t('settings.format_and_language') }}</h3>
<ul class="setting-list">
<h4>{{ $t('settings.interfaceLanguage') }}</h4>
<interface-language-switcher
v-model="language"
class="lang-selector"
@update="val => language = val"
/>
<h4>
{{ $t('settings.email_language') }}
{{ ' ' }}
<ProfileSettingIndicator :is-profile="true" />
</h4>
<interface-language-switcher
v-model="emailLanguage"
class="lang-selector"
:profile="true"
@update:model-value="updateProfile()"
/>
<li>
<BooleanSetting path="useAbsoluteTimeFormat">
{{ $t('settings.absolute_time_format') }}
</BooleanSetting>
</li>
<li>
<ChoiceSetting
id="absoluteTime12h"
path="absoluteTime12h"
:options="absoluteTime12hOptions"
>
{{ $t('settings.absolute_time_format_12h') }}
</ChoiceSetting>
</li>
</ul>
<h3>{{ $t('settings.scale_and_font') }}</h3>
<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>
+ <p class="sidenote">
+ <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>
+ </p>
</li>
<li>
<FontControl
:model-value="mergedConfig.theme3hacks.fonts.interface"
name="ui"
:label="$t('settings.style.fonts.components_inline.interface')"
:fallback="{ family: 'sans-serif' }"
no-inherit="1"
@update:model-value="v => updateFont('interface', v)"
/>
</li>
<li>
<FontControl
:model-value="mergedConfig.theme3hacks.fonts.input"
name="input"
:fallback="{ family: 'inherit' }"
:label="$t('settings.style.fonts.components_inline.input')"
@update:model-value="v => updateFont('input', v)"
/>
</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"
>
{{ $t('settings.emoji_reactions_scale') }}
</FloatSetting>
</li>
</ul>
</li>
</ul>
<h3>{{ $t('settings.timelines') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path="streaming">
{{ $t('settings.streaming') }}
</BooleanSetting>
<ul class="setting-list suboptions">
<li>
<BooleanSetting
path="pauseOnUnfocused"
parent-path="streaming"
>
{{ $t('settings.pause_on_unfocused') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<BooleanSetting
path="useStreamingApi"
expert="1"
>
{{ $t('settings.useStreamingApi') }}
</BooleanSetting>
</li>
</ul>
<h3 v-if="expertLevel > 0">
{{ $t('settings.confirmations') }}
</h3>
<ul
v-if="expertLevel > 0"
class="setting-list"
>
<li class="select-multiple">
- <span class="label">{{ $t('settings.confirm_dialogs') }}</span>
+ <h4 class="label">{{ $t('settings.confirm_dialogs') }}</h4>
<ul class="option-list">
<li>
<BooleanSetting path="modalOnRepeat">
{{ $t('settings.confirm_dialogs_repeat') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnUnfollow">
{{ $t('settings.confirm_dialogs_unfollow') }}
</BooleanSetting>
</li>
- <li>
+ <li
+ v-if="!blockExpirationSupported"
+ >
<BooleanSetting
- v-if="!blockExpirationSupported"
path="modalOnBlock"
>
{{ $t('settings.confirm_dialogs_block') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnMuteConversation">
{{ $t('settings.confirm_dialogs_mute_conversation') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnMuteDomain">
{{ $t('settings.confirm_dialogs_mute_domain') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnDelete">
{{ $t('settings.confirm_dialogs_delete') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnLogout">
{{ $t('settings.confirm_dialogs_logout') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnApproveFollow">
{{ $t('settings.confirm_dialogs_approve_follow') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnDenyFollow">
{{ $t('settings.confirm_dialogs_deny_follow') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="modalOnRemoveUserFromFollowers">
{{ $t('settings.confirm_dialogs_remove_follower') }}
</BooleanSetting>
</li>
</ul>
</li>
</ul>
</div>
</div>
</template>
<script src="./general_tab.js"></script>
diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue
index f9e2b93248..ea859b18ac 100644
--- a/src/components/settings_modal/tabs/notifications_tab.vue
+++ b/src/components/settings_modal/tabs/notifications_tab.vue
@@ -1,290 +1,294 @@
<template>
<div :label="$t('settings.notifications')">
<div class="setting-section">
<h3>{{ $t('settings.notification_setting_annoyance') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path="closingDrawerMarksAsSeen">
{{ $t('settings.notification_setting_drawer_marks_as_seen') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="ignoreInactionableSeen">
{{ $t('settings.notification_setting_ignore_inactionable_seen') }}
</BooleanSetting>
- <div>
- <small>
- {{ $t('settings.notification_setting_ignore_inactionable_seen_tip') }}
- </small>
- </div>
+ <p class="sidenote">
+ {{ $t('settings.notification_setting_ignore_inactionable_seen_tip') }}
+ </p>
</li>
<li>
<BooleanSetting
path="unseenAtTop"
expert="1"
>
{{ $t('settings.notification_setting_unseen_at_top') }}
</BooleanSetting>
</li>
</ul>
</div>
<div class="setting-section">
<h3>{{ $t('settings.notification_setting_filters') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting
source="profile"
path="blockNotificationsFromStrangers"
>
{{ $t('settings.notification_setting_block_from_strangers') }}
</BooleanSetting>
</li>
<li>
<h4> {{ $t('settings.notification_visibility') }}</h4>
- <p v-if="expertLevel > 0">
+ <p
+ class="sidenote"
+ v-if="expertLevel > 0"
+ >
{{ $t('settings.notification_setting_filters_chrome_push') }}
</p>
<ul class="setting-list two-column">
<li>
<h5> {{ $t('settings.notification_visibility_mentions') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.mentions">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.mentions">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<h5> {{ $t('settings.notification_visibility_statuses') }}</h5>
<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>
<h5> {{ $t('settings.notification_visibility_likes') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.likes">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.likes">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<h5> {{ $t('settings.notification_visibility_repeats') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.repeats">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.repeats">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<h5> {{ $t('settings.notification_visibility_emoji_reactions') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.emojiReactions">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.emojiReactions">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<h5> {{ $t('settings.notification_visibility_follows') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.follows">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.follows">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<h5> {{ $t('settings.notification_visibility_follow_requests') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.followRequest">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.followRequest">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<h5> {{ $t('settings.notification_visibility_moves') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.moves">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.moves">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li>
<h5> {{ $t('settings.notification_visibility_polls') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.polls">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.polls">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
<li v-if="canReceiveReports">
<h5> {{ $t('settings.notification_visibility_reports') }}</h5>
<ul class="setting-list">
<li>
<BooleanSetting path="notificationVisibility.reports">
{{ $t('settings.notification_visibility_in_column') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="notificationNative.reports">
{{ $t('settings.notification_visibility_native_notifications') }}
</BooleanSetting>
</li>
</ul>
</li>
</ul>
</li>
<li>
<BooleanSetting path="showExtraNotifications">
{{ $t('settings.notification_show_extra') }}
</BooleanSetting>
<ul class="setting-list suboptions">
<li>
<BooleanSetting
path="showChatsInExtraNotifications"
:disabled="!mergedConfig.showExtraNotifications"
>
{{ $t('settings.notification_extra_chats') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="showAnnouncementsInExtraNotifications"
:disabled="!mergedConfig.showExtraNotifications"
>
{{ $t('settings.notification_extra_announcements') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="showFollowRequestsInExtraNotifications"
:disabled="!mergedConfig.showExtraNotifications"
>
{{ $t('settings.notification_extra_follow_requests') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting
path="showExtraNotificationsTip"
:disabled="!mergedConfig.showExtraNotifications"
>
{{ $t('settings.notification_extra_tip') }}
</BooleanSetting>
</li>
</ul>
</li>
</ul>
</div>
<div
v-if="expertLevel > 0"
class="setting-section"
>
<h3>{{ $t('settings.notification_setting_privacy') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting
path="webPushNotifications"
expert="1"
>
{{ $t('settings.enable_web_push_notifications') }}
</BooleanSetting>
<ul class="setting-list suboptions">
<li>
<BooleanSetting
path="webPushAlwaysShowNotifications"
:disabled="!mergedConfig.webPushNotifications"
>
{{ $t('settings.enable_web_push_always_show') }}
</BooleanSetting>
- <div :class="{ faint: !mergedConfig.webPushNotifications }">
- <small>
- {{ $t('settings.enable_web_push_always_show_tip') }}
- </small>
- </div>
+ <p
+ :class="{ faint: !mergedConfig.webPushNotifications }"
+ class="sidenote"
+ >
+ {{ $t('settings.enable_web_push_always_show_tip') }}
+ </p>
</li>
</ul>
</li>
<li>
<BooleanSetting
source="profile"
path="webPushHideContents"
expert="1"
>
{{ $t('settings.notification_setting_hide_notification_contents') }}
</BooleanSetting>
</li>
</ul>
</div>
- <div class="setting-section">
- <p>{{ $t('settings.notification_mutes') }}</p>
- <p>{{ $t('settings.notification_blocks') }}</p>
- </div>
+ <p class="sidenote">
+ <ul>
+ <li>{{ $t('settings.notification_mutes') }}</li>
+ <li>{{ $t('settings.notification_blocks') }}</li>
+ </ul>
+ </p>
</div>
</template>
<script src="./notifications_tab.js"></script>
<!-- <style lang="scss" src="./profile.scss"></style> -->

File Metadata

Mime Type
text/x-diff
Expires
Thu, Oct 29, 2:14 PM (1 d, 3 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1798183
Default Alt Text
(52 KB)

Event Timeline