Merge remote-tracking branch 'upstream/develop' into birthdays

This commit is contained in:
tusooa 2023-01-22 09:34:01 -05:00
commit b1e75c25bd
No known key found for this signature in database
GPG key ID: 7B467EDE43A08224
300 changed files with 13879 additions and 9055 deletions

View file

@ -41,7 +41,16 @@ export default {
},
methods: {
update (e) {
const [firstSegment, ...rest] = this.path.split('.')
set(this.$parent, this.path, e)
// Updating nested properties does not trigger update on its parent.
// probably still not as reliable, but works for depth=1 at least
if (rest.length > 0) {
set(this.$parent, firstSegment, { ...get(this.$parent, firstSegment) })
}
},
reset () {
set(this.$parent, this.path, this.defaultState)
}
}
}

View file

@ -15,7 +15,12 @@
<slot />
</span>
{{ ' ' }}
<ModifiedIndicator :changed="isChanged" /><ServerSideIndicator :server-side="isServerSide" /> </Checkbox>
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ServerSideIndicator :server-side="isServerSide" />
</Checkbox>
</label>
</template>

View file

@ -43,6 +43,9 @@ export default {
methods: {
update (e) {
set(this.$parent, this.path, e)
},
reset () {
set(this.$parent, this.path, this.defaultState)
}
}
}

View file

@ -19,14 +19,12 @@
{{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }}
</option>
</Select>
<ModifiedIndicator :changed="isChanged" />
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
<ServerSideIndicator :server-side="isServerSide" />
</label>
</template>
<script src="./choice_setting.js"></script>
<style lang="scss">
.ChoiceSetting {
}
</style>

View file

@ -36,6 +36,9 @@ export default {
methods: {
update (e) {
set(this.$parent, this.path, parseInt(e.target.value))
},
reset () {
set(this.$parent, this.path, this.defaultState)
}
}
}

View file

@ -17,7 +17,10 @@
@change="update"
>
{{ ' ' }}
<ModifiedIndicator :changed="isChanged" />
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
</span>
</template>

View file

@ -0,0 +1,67 @@
import { get, set } from 'lodash'
import ModifiedIndicator from './modified_indicator.vue'
import Select from 'src/components/select/select.vue'
export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%']
export const defaultHorizontalUnits = ['px', 'rem', 'vw']
export const defaultVerticalUnits = ['px', 'rem', 'vh']
export default {
components: {
ModifiedIndicator,
Select
},
props: {
path: String,
disabled: Boolean,
min: Number,
units: {
type: [String],
default: () => allCssUnits
},
expert: [Number, String]
},
computed: {
pathDefault () {
const [firstSegment, ...rest] = this.path.split('.')
return [firstSegment + 'DefaultValue', ...rest].join('.')
},
stateUnit () {
return (this.state || '').replace(/\d+/, '')
},
stateValue () {
return (this.state || '').replace(/\D+/, '')
},
state () {
const value = get(this.$parent, this.path)
if (value === undefined) {
return this.defaultState
} else {
return value
}
},
defaultState () {
return get(this.$parent, this.pathDefault)
},
isChanged () {
return this.state !== this.defaultState
},
matchesExpertLevel () {
return (this.expert || 0) <= this.$parent.expertLevel
}
},
methods: {
update (e) {
set(this.$parent, this.path, e)
},
reset () {
set(this.$parent, this.path, this.defaultState)
},
updateValue (e) {
set(this.$parent, this.path, parseInt(e.target.value) + this.stateUnit)
},
updateUnit (e) {
set(this.$parent, this.path, this.stateValue + e.target.value)
}
}
}

View file

@ -0,0 +1,55 @@
<template>
<span
v-if="matchesExpertLevel"
class="SizeSetting"
>
<label
:for="path"
class="size-label"
>
<slot />
</label>
<input
:id="path"
class="number-input"
type="number"
step="1"
:disabled="disabled"
:min="min || 0"
:value="stateValue"
@change="updateValue"
>
<Select
:id="path"
:model-value="stateUnit"
:disabled="disabled"
class="css-unit-input"
@change="updateUnit"
>
<option
v-for="option in units"
:key="option"
:value="option"
>
{{ option }}
</option>
</Select>
{{ ' ' }}
<ModifiedIndicator
:changed="isChanged"
:onclick="reset"
/>
</span>
</template>
<script src="./size_setting.js"></script>
<style lang="scss">
.css-unit-input,
.css-unit-input select {
margin-left: 0.5em;
width: 4em;
max-width: 4em;
min-width: 4em;
}
</style>

View file

@ -1,4 +1,5 @@
@import 'src/_variables.scss';
@import "src/variables";
.settings-modal {
overflow: hidden;
@ -6,32 +7,13 @@
.option-list {
list-style-type: none;
padding-left: 2em;
li {
margin-bottom: 0.5em;
}
.suboptions {
margin-top: 0.3em
}
}
&.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 (max-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));
}
margin-top: 0.3em;
}
}
@ -63,6 +45,7 @@
.settings-footer {
display: flex;
>* {
margin-right: 0.5em;
}
@ -72,4 +55,26 @@
flex-grow: 1;
}
}
&.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 (max-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));
}
}
}
}

View file

@ -1,4 +1,5 @@
@import 'src/_variables.scss';
@import "src/variables";
.settings_tab-switcher {
height: 100%;
@ -10,7 +11,8 @@
> div,
> label {
display: block;
margin-bottom: .5em;
margin-bottom: 0.5em;
&:last-child {
margin-bottom: 0;
}
@ -21,7 +23,7 @@
.option-list {
margin: 0;
padding-left: .5em;
padding-left: 0.5em;
}
}

View file

@ -77,6 +77,16 @@
>
{{ $t('settings.download_backup') }}
</a>
<span
v-else-if="backup.state === 'running'"
>
{{ $tc('settings.backup_running', backup.processed_number, { number: backup.processed_number }) }}
</span>
<span
v-else-if="backup.state === 'failed'"
>
{{ $t('settings.backup_failed') }}
</span>
<span
v-else
>

View file

@ -1,4 +1,4 @@
import { filter, trim } from 'lodash'
import { filter, trim, debounce } from 'lodash'
import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
@ -29,24 +29,20 @@ const FilteringTab = {
},
set (value) {
this.muteWordsStringLocal = value
this.debouncedSetMuteWords(value)
}
},
debouncedSetMuteWords () {
return debounce((value) => {
this.$store.dispatch('setOption', {
name: 'muteWords',
value: filter(value.split('\n'), (word) => trim(word).length > 0)
})
}
}, 1000)
}
},
// Updating nested properties
watch: {
notificationVisibility: {
handler (value) {
this.$store.dispatch('setOption', {
name: 'notificationVisibility',
value: this.$store.getters.mergedConfig.notificationVisibility
})
},
deep: true
},
replyVisibility () {
this.$store.dispatch('queueFlushAll')
}

View file

@ -2,6 +2,7 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
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 SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue'
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
@ -43,6 +44,11 @@ const GeneralTab = {
value: mode,
label: this.$t(`settings.third_column_mode_${mode}`)
})),
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({
key: mode,
value: mode,
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
})),
loopSilentAvailable:
// Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
@ -56,11 +62,15 @@ const GeneralTab = {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
SizeSetting,
InterfaceLanguageSwitcher,
ScopeSelector,
ServerSideIndicator
},
computed: {
horizontalUnits () {
return defaultHorizontalUnits
},
postFormats () {
return this.$store.state.instance.postFormats || []
},
@ -71,6 +81,17 @@ 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 &&

View file

@ -15,11 +15,6 @@
{{ $t('settings.hide_isp') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="sidebarRight">
{{ $t('settings.right_sidebar') }}
</BooleanSetting>
</li>
<li v-if="instanceWallpaperUsed">
<BooleanSetting path="hideInstanceWallpaper">
{{ $t('settings.hide_wallpaper') }}
@ -65,22 +60,14 @@
</BooleanSetting>
</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="userPopoverZoom"
<ChoiceSetting
id="userPopoverAvatarAction"
path="userPopoverAvatarAction"
:options="userPopoverAvatarActionOptions"
expert="1"
>
{{ $t('settings.user_popover_avatar_zoom') }}
</BooleanSetting>
{{ $t('settings.user_popover_avatar_action') }}
</ChoiceSetting>
</li>
<li>
<BooleanSetting
@ -90,16 +77,6 @@
{{ $t('settings.user_popover_avatar_overlay') }}
</BooleanSetting>
</li>
<li>
<ChoiceSetting
v-if="user"
id="thirdColumnMode"
path="thirdColumnMode"
:options="thirdColumnModeOptions"
>
{{ $t('settings.third_column_mode') }}
</ChoiceSetting>
</li>
<li>
<BooleanSetting
path="alwaysShowNewPostButton"
@ -124,6 +101,53 @@
{{ $t('settings.hide_shoutbox') }}
</BooleanSetting>
</li>
<li>
<h3>{{ $t('settings.columns') }}</h3>
</li>
<li>
<BooleanSetting path="disableStickyHeaders">
{{ $t('settings.disable_sticky_headers') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="showScrollbars">
{{ $t('settings.show_scrollbars') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="sidebarRight">
{{ $t('settings.right_sidebar') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="navbarColumnStretch">
{{ $t('settings.navbar_column_stretch') }}
</BooleanSetting>
</li>
<li>
<ChoiceSetting
v-if="user"
id="thirdColumnMode"
path="thirdColumnMode"
:options="thirdColumnModeOptions"
>
{{ $t('settings.third_column_mode') }}
</ChoiceSetting>
</li>
<li v-if="expertLevel > 0">
{{ $t('settings.column_sizes') }}
<div class="column-settings">
<SizeSetting
v-for="column in columns"
:key="column"
:path="column + 'ColumnWidth'"
:units="horizontalUnits"
expert="1"
>
{{ $t('settings.column_sizes_' + column) }}
</SizeSetting>
</div>
</li>
</ul>
</div>
<div class="setting-item">
@ -433,3 +457,17 @@
</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>

View file

@ -1,29 +1,29 @@
.mutes-and-blocks-tab {
height: 100%;
height: 100%;
.usersearch-wrapper {
padding: 1em;
}
.usersearch-wrapper {
padding: 1em;
}
.bulk-actions {
text-align: right;
padding: 0 1em;
min-height: 2em;
}
.bulk-actions {
text-align: right;
padding: 0 1em;
min-height: 2em;
}
.bulk-action-button {
width: 10em
}
.bulk-action-button {
width: 10em;
}
.domain-mute-form {
padding: 1em;
display: flex;
flex-direction: column
}
.domain-mute-form {
padding: 1em;
display: flex;
flex-direction: column;
}
.domain-mute-button {
align-self: flex-end;
margin-top: 1em;
width: 10em
}
.domain-mute-button {
align-self: flex-end;
margin-top: 1em;
width: 10em;
}
}

View file

@ -56,7 +56,7 @@
<div :label="$t('settings.mutes_tab')">
<tab-switcher>
<div label="Users">
<div :label="$t('settings.user_mutes')">
<div class="usersearch-wrapper">
<Autosuggest
:filter="filterUnMutedUsers"

View file

@ -66,7 +66,7 @@ const ProfileTab = {
emojiUserSuggestor () {
return suggestor({
emoji: [
...this.$store.state.instance.emoji,
...this.$store.getters.standardEmojiList,
...this.$store.state.instance.customEmoji
],
store: this.$store
@ -75,7 +75,7 @@ const ProfileTab = {
emojiSuggestor () {
return suggestor({
emoji: [
...this.$store.state.instance.emoji,
...this.$store.getters.standardEmojiList,
...this.$store.state.instance.customEmoji
]
})
@ -157,7 +157,7 @@ const ProfileTab = {
return false
},
deleteField (index, event) {
this.$delete(this.newFields, index)
this.newFields.splice(index, 1)
},
uploadFile (slot, e) {
const file = e.target.files[0]

View file

@ -1,4 +1,5 @@
@import '../../../_variables.scss';
@import "../../../variables";
.profile-tab {
.bio {
margin: 0;
@ -8,7 +9,7 @@
padding-top: 5px;
}
input[type=file] {
input[type="file"] {
padding: 5px;
height: auto;
}
@ -52,7 +53,7 @@
right: 0.2em;
border-radius: $fallback--tooltipRadius;
border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
background-color: rgba(0, 0, 0, 0.6);
background-color: rgb(0 0 0 / 60%);
opacity: 0.7;
width: 1.5em;
height: 1.5em;

View file

@ -137,9 +137,11 @@
<script src="./mfa.js"></script>
<style lang="scss">
@import '../../../../_variables.scss';
@import "../../../../variables";
.mfa-settings {
.mfa-heading, .method-item {
.mfa-heading,
.method-item {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
@ -155,18 +157,19 @@
display: flex;
justify-content: center;
flex-wrap: wrap;
.qr-code {
flex: 1;
padding-right: 10px;
}
.verify { flex: 1; }
.error { margin: 4px 0 0 0; }
.error { margin: 4px 0 0; }
.confirm-otp-actions {
button {
width: 15em;
margin-top: 5px;
}
}
}
}

View file

@ -21,13 +21,14 @@
</template>
<script src="./mfa_backup_codes.js"></script>
<style lang="scss">
@import '../../../../_variables.scss';
@import "../../../../variables";
.mfa-backup-codes {
.warning {
color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange);
}
.backup-codes {
font-family: var(--postCodeFont, monospace);
}

View file

@ -241,7 +241,7 @@
class="btn button-default"
@click="confirmDelete"
>
{{ $t('settings.save') }}
{{ $t('settings.delete_account') }}
</button>
</div>
</div>

View file

@ -33,10 +33,10 @@
scope="global"
keypath="settings.style.preview.text"
>
<code style="font-family: var(--postCodeFont)">
<code style="font-family: var(--postCodeFont);">
{{ $t('settings.style.preview.mono') }}
</code>
<a style="color: var(--link)">
<a style="color: var(--link);">
{{ $t('settings.style.preview.link') }}
</a>
</i18n-t>
@ -44,25 +44,25 @@
<div class="icons">
<FAIcon
fixed-width
style="color: var(--cBlue)"
style="color: var(--cBlue);"
class="fa-scale-110 fa-old-padding"
icon="reply"
/>
<FAIcon
fixed-width
style="color: var(--cGreen)"
style="color: var(--cGreen);"
class="fa-scale-110 fa-old-padding"
icon="retweet"
/>
<FAIcon
fixed-width
style="color: var(--cOrange)"
style="color: var(--cOrange);"
class="fa-scale-110 fa-old-padding"
icon="star"
/>
<FAIcon
fixed-width
style="color: var(--cRed)"
style="color: var(--cRed);"
class="fa-scale-110 fa-old-padding"
icon="times"
/>
@ -81,7 +81,7 @@
class="faint"
scope="global"
>
<a style="color: var(--faintLink)">
<a style="color: var(--faintLink);">
{{ $t('settings.style.preview.faint_link') }}
</a>
</i18n-t>
@ -138,6 +138,7 @@ export default {}
.preview-container {
position: relative;
}
.underlay-preview {
position: absolute;
top: 0;

View file

@ -279,6 +279,9 @@ export default {
opacity
)
// Temporary patch for null-y value errors
if (layers.flat().some(v => v == null)) return acc
return {
...acc,
...textColors.reduce((acc, textColorKey) => {
@ -300,6 +303,7 @@ export default {
return Object.entries(ratios).reduce((acc, [k, v]) => { acc[k] = hints(v); return acc }, {})
} catch (e) {
console.warn('Failure computing contrasts', e)
return {}
}
},
previewRules () {

View file

@ -1,20 +1,17 @@
@import 'src/_variables.scss';
@import "src/variables";
.theme-tab {
padding-bottom: 2em;
.theme-warning {
display: flex;
align-items: baseline;
margin-bottom: .5em;
.buttons {
.btn {
margin-bottom: .5em;
}
}
}
.preset-switcher {
margin-right: 1em;
}
.btn {
margin-left: 0.25em;
margin-right: 0.25em;
}
.style-control {
display: flex;
align-items: baseline;
@ -24,35 +21,37 @@
flex: 1;
}
&.disabled {
input, select {
opacity: .5
}
}
.opt {
margin: .5em;
margin: 0.5em;
}
.color-input {
flex: 0 0 0;
}
input, select {
input,
select {
min-width: 3em;
margin: 0;
flex: 0;
&[type=number] {
&[type="number"] {
min-width: 5em;
}
&[type=range] {
&[type="range"] {
flex: 1;
min-width: 3em;
align-self: flex-start;
}
}
&.disabled {
input,
select {
opacity: 0.5;
}
}
}
.reset-container {
@ -63,8 +62,7 @@
.reset-container,
.apply-container,
.radius-container,
.color-container,
{
.color-container, {
display: flex;
}
@ -73,10 +71,11 @@
flex-direction: column;
}
.color-container{
.color-container {
> h4 {
width: 99%;
}
flex-wrap: wrap;
justify-content: space-between;
}
@ -100,7 +99,7 @@
p {
flex: 1;
margin: 0;
margin-right: .5em;
margin-right: 0.5em;
}
}
@ -112,15 +111,16 @@
min-width: 1px;
flex: 0 auto;
padding: 0 1em;
margin-bottom: .5em;
margin-bottom: 0.5em;
}
}
.shadow-selector {
.override {
flex: 1;
margin-left: .5em;
margin-left: 0.5em;
}
.select-container {
margin-top: -4px;
margin-bottom: -3px;
@ -136,7 +136,7 @@
.presets,
.import-export {
margin-bottom: .5em;
margin-bottom: 0.5em;
}
.import-export {
@ -144,16 +144,17 @@
}
.override {
margin-left: .5em;
margin-left: 0.5em;
}
}
.save-load-options {
flex-wrap: wrap;
margin-top: .5em;
margin-top: 0.5em;
justify-content: center;
.keep-option {
margin: 0 .5em .5em;
margin: 0 0.5em 0.5em;
min-width: 25%;
}
}
@ -179,11 +180,11 @@
flex: 1;
h4 {
margin-bottom: .25em;
margin-bottom: 0.25em;
}
.icons {
margin-top: .5em;
margin-top: 0.5em;
display: flex;
i {
@ -199,8 +200,20 @@
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%);
.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;
@ -251,33 +264,33 @@
}
}
.radius-item {
flex-basis: auto;
}
.radius-item,
.color-item {
min-width: 20em;
margin: 5px 6px 0 0;
display:flex;
display: flex;
flex-direction: column;
flex: 1 1 0;
&.wide {
min-width: 60%
min-width: 60%;
}
&:not(.wide):nth-child(2n+1) {
margin-right: 7px;
}
.color, .opacity {
display:flex;
.color,
.opacity {
display: flex;
align-items: baseline;
}
}
.radius-item {
flex-basis: auto;
}
.theme-radius-rn,
.theme-color-cl {
border: 0;
@ -295,14 +308,11 @@
.theme-radius-in {
min-width: 1em;
}
.theme-radius-in {
max-width: 7em;
flex: 1;
}
.theme-radius-lb{
.theme-radius-lb {
max-width: 50em;
}
@ -310,9 +320,16 @@
padding: 20px;
}
.btn {
margin-left: .25em;
margin-right: .25em;
.theme-warning {
display: flex;
align-items: baseline;
margin-bottom: 0.5em;
.buttons {
.btn {
margin-bottom: 0.5em;
}
}
}
}
@ -323,6 +340,7 @@
justify-content: space-around;
flex-grow: 1;
/* stylelint-disable-next-line no-descending-specificity */
.btn {
flex-grow: 1;
min-height: 2em;