Merge branch 'settings-shuffle' into shigusegubu-themes3
This commit is contained in:
commit
1abbba698d
61 changed files with 2948 additions and 1940 deletions
|
|
@ -3,6 +3,10 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## 2.9.3
|
||||
### Fixed
|
||||
- Being unable to update profile
|
||||
|
||||
## 2.9.2
|
||||
### Changed
|
||||
- BREAKING: due to some internal technical changes logging into AdminFE through PleromaFE is no longer possible
|
||||
|
|
|
|||
2
changelog.d/broken.fix
Normal file
2
changelog.d/broken.fix
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
Fix display of the broken/deleted/banned users
|
||||
|
||||
1
changelog.d/everything-instance-default.add
Normal file
1
changelog.d/everything-instance-default.add
Normal file
|
|
@ -0,0 +1 @@
|
|||
Make every configuration option default-overridable by instance admins
|
||||
40
package.json
40
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "pleroma_fe",
|
||||
"version": "2.9.2",
|
||||
"version": "2.9.3",
|
||||
"description": "Pleroma frontend, the default frontend of Pleroma social network server",
|
||||
"author": "Pleroma contributors <https://git.pleroma.social/pleroma/pleroma-fe/-/blob/develop/CONTRIBUTORS.md>",
|
||||
"private": false,
|
||||
|
|
@ -17,12 +17,12 @@
|
|||
"lint-fix": "eslint --fix src test/unit/specs test/e2e/specs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "7.28.3",
|
||||
"@babel/runtime": "7.28.4",
|
||||
"@chenfengyuan/vue-qrcode": "2.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "6.7.2",
|
||||
"@fortawesome/free-regular-svg-icons": "6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.7.2",
|
||||
"@fortawesome/vue-fontawesome": "3.1.1",
|
||||
"@fortawesome/fontawesome-svg-core": "7.1.0",
|
||||
"@fortawesome/free-regular-svg-icons": "7.1.0",
|
||||
"@fortawesome/free-solid-svg-icons": "7.1.0",
|
||||
"@fortawesome/vue-fontawesome": "3.1.2",
|
||||
"@kazvmoe-infra/pinch-zoom-element": "1.3.0",
|
||||
"@kazvmoe-infra/unicode-emoji-json": "0.4.0",
|
||||
"@ruffle-rs/ruffle": "0.1.0-nightly.2025.6.22",
|
||||
|
|
@ -39,7 +39,7 @@
|
|||
"js-cookie": "3.0.5",
|
||||
"localforage": "1.10.0",
|
||||
"parse-link-header": "2.0.0",
|
||||
"phoenix": "1.8.0",
|
||||
"phoenix": "1.8.1",
|
||||
"pinia": "^3.0.0",
|
||||
"punycode.js": "2.3.1",
|
||||
"qrcode": "1.5.4",
|
||||
|
|
@ -47,15 +47,15 @@
|
|||
"url": "0.11.4",
|
||||
"utf8": "3.0.0",
|
||||
"uuid": "11.1.0",
|
||||
"vue": "3.5.19",
|
||||
"vue": "3.5.22",
|
||||
"vue-i18n": "11",
|
||||
"vue-router": "4.5.1",
|
||||
"vue-virtual-scroller": "^2.0.0-beta.7",
|
||||
"vuex": "4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.28.3",
|
||||
"@babel/eslint-parser": "7.28.0",
|
||||
"@babel/core": "7.28.4",
|
||||
"@babel/eslint-parser": "7.28.4",
|
||||
"@babel/plugin-transform-runtime": "7.28.3",
|
||||
"@babel/preset-env": "7.28.3",
|
||||
"@babel/register": "7.28.3",
|
||||
|
|
@ -66,24 +66,24 @@
|
|||
"@vitest/ui": "^3.0.7",
|
||||
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
|
||||
"@vue/babel-plugin-jsx": "1.5.0",
|
||||
"@vue/compiler-sfc": "3.5.19",
|
||||
"@vue/compiler-sfc": "3.5.22",
|
||||
"@vue/test-utils": "2.4.6",
|
||||
"autoprefixer": "10.4.21",
|
||||
"babel-plugin-lodash": "3.3.4",
|
||||
"chai": "5.3.2",
|
||||
"chalk": "5.6.0",
|
||||
"chai": "5.3.3",
|
||||
"chalk": "5.6.2",
|
||||
"chromedriver": "135.0.4",
|
||||
"connect-history-api-fallback": "2.0.0",
|
||||
"cross-spawn": "7.0.6",
|
||||
"custom-event-polyfill": "1.0.7",
|
||||
"eslint": "9.33.0",
|
||||
"eslint": "9.37.0",
|
||||
"vue-eslint-parser": "10.2.0",
|
||||
"eslint-config-standard": "17.1.0",
|
||||
"eslint-formatter-friendly": "7.0.0",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"eslint-plugin-n": "17.21.3",
|
||||
"eslint-plugin-n": "17.23.1",
|
||||
"eslint-plugin-promise": "7.2.1",
|
||||
"eslint-plugin-vue": "10.4.0",
|
||||
"eslint-plugin-vue": "10.5.0",
|
||||
"eventsource-polyfill": "0.9.6",
|
||||
"express": "5.1.0",
|
||||
"function-bind": "1.1.2",
|
||||
|
|
@ -96,14 +96,14 @@
|
|||
"postcss": "8.5.6",
|
||||
"postcss-html": "^1.5.0",
|
||||
"postcss-scss": "^4.0.6",
|
||||
"sass": "1.89.2",
|
||||
"sass": "1.93.2",
|
||||
"selenium-server": "3.141.59",
|
||||
"semver": "7.7.2",
|
||||
"semver": "7.7.3",
|
||||
"serve-static": "2.2.0",
|
||||
"shelljs": "0.10.0",
|
||||
"sinon": "20.0.0",
|
||||
"sinon-chai": "4.0.0",
|
||||
"stylelint": "16.19.1",
|
||||
"sinon-chai": "4.0.1",
|
||||
"stylelint": "16.25.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recommended": "^16.0.0",
|
||||
"stylelint-config-recommended-scss": "^14.0.0",
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import { useI18nStore } from 'src/stores/i18n'
|
|||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
import { useAnnouncementsStore } from 'src/stores/announcements'
|
||||
import { useAuthFlowStore } from 'src/stores/auth_flow'
|
||||
import { staticOrApiConfigDefault, instanceDefaultConfig } from 'src/modules/default_config_state.js'
|
||||
|
||||
let staticInitialResults = null
|
||||
|
||||
|
|
@ -130,50 +131,15 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
|
|||
}
|
||||
|
||||
const copyInstanceOption = (name) => {
|
||||
store.dispatch('setInstanceOption', { name, value: config[name] })
|
||||
if (typeof config[name] !== 'undefined') {
|
||||
store.dispatch('setInstanceOption', { name, value: config[name] })
|
||||
}
|
||||
}
|
||||
|
||||
copyInstanceOption('theme')
|
||||
copyInstanceOption('style')
|
||||
copyInstanceOption('palette')
|
||||
copyInstanceOption('embeddedToS')
|
||||
copyInstanceOption('nsfwCensorImage')
|
||||
copyInstanceOption('background')
|
||||
copyInstanceOption('hidePostStats')
|
||||
copyInstanceOption('hideBotIndication')
|
||||
copyInstanceOption('hideUserStats')
|
||||
copyInstanceOption('hideFilteredStatuses')
|
||||
copyInstanceOption('logo')
|
||||
Object.keys(staticOrApiConfigDefault).forEach(copyInstanceOption)
|
||||
Object.keys(instanceDefaultConfig).forEach(copyInstanceOption)
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'logoMask',
|
||||
value: typeof config.logoMask === 'undefined'
|
||||
? true
|
||||
: config.logoMask
|
||||
})
|
||||
|
||||
store.dispatch('setInstanceOption', {
|
||||
name: 'logoMargin',
|
||||
value: typeof config.logoMargin === 'undefined'
|
||||
? 0
|
||||
: config.logoMargin
|
||||
})
|
||||
copyInstanceOption('logoLeft')
|
||||
useAuthFlowStore().setInitialStrategy(config.loginMethod)
|
||||
|
||||
copyInstanceOption('redirectRootNoLogin')
|
||||
copyInstanceOption('redirectRootLogin')
|
||||
copyInstanceOption('showInstanceSpecificPanel')
|
||||
copyInstanceOption('minimalScopesMode')
|
||||
copyInstanceOption('hideMutedPosts')
|
||||
copyInstanceOption('collapseMessageWithSubject')
|
||||
copyInstanceOption('scopeCopy')
|
||||
copyInstanceOption('subjectLineBehavior')
|
||||
copyInstanceOption('postContentType')
|
||||
copyInstanceOption('alwaysShowSubjectInput')
|
||||
copyInstanceOption('showFeaturesPanel')
|
||||
copyInstanceOption('hideSitename')
|
||||
copyInstanceOption('sidebarRight')
|
||||
}
|
||||
|
||||
const getTOS = async ({ store }) => {
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
|
||||
<style lang="scss">
|
||||
.exporter {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&-processing {
|
||||
margin: 0.25em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
<template>
|
||||
<div class="font-control">
|
||||
<label
|
||||
:id="name + '-label'"
|
||||
:for="manualEntry ? name : name + '-font-switcher'"
|
||||
class="label"
|
||||
>
|
||||
{{ $t('settings.style.themes3.font.label', { label }) }}
|
||||
</label>
|
||||
{{ ' ' }}
|
||||
<Checkbox
|
||||
v-if="typeof fallback !== 'undefined'"
|
||||
:id="name + '-o'"
|
||||
|
|
@ -15,8 +7,15 @@
|
|||
:model-value="present"
|
||||
@change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)"
|
||||
>
|
||||
{{ $t('settings.style.themes3.define') }}
|
||||
<i18n-t
|
||||
scope="global"
|
||||
keypath="settings.style.fonts.override"
|
||||
tag="span"
|
||||
>
|
||||
{{ label }}
|
||||
</i18n-t>
|
||||
</Checkbox>
|
||||
{{ ' ' }}
|
||||
<div
|
||||
v-if="modelValue?.family"
|
||||
class="font-input"
|
||||
|
|
@ -143,10 +142,6 @@
|
|||
margin-left: 2em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
.font-checkbox {
|
||||
margin-left: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.invalid-tooltip {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="importer">
|
||||
<div class="importer btn-group">
|
||||
<form>
|
||||
<input
|
||||
ref="input"
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
class="btn button-default"
|
||||
@click="submit"
|
||||
>
|
||||
{{ submitButtonLabel || $t('importer.submit') }}
|
||||
{{ submitButtonLabel || $t('importer.import') }}
|
||||
</button>
|
||||
<div v-if="success">
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -299,8 +299,8 @@ const Popover = {
|
|||
if (this.trigger === 'click') {
|
||||
document.removeEventListener('click', this.onClickOutside)
|
||||
}
|
||||
this.scrollable.removeEventListener('scroll', this.onScroll)
|
||||
this.scrollable.removeEventListener('resize', this.onResize)
|
||||
this.scrollable?.removeEventListener('scroll', this.onScroll)
|
||||
this.scrollable?.removeEventListener('resize', this.onResize)
|
||||
},
|
||||
resizePopover () {
|
||||
setTimeout(() => {
|
||||
|
|
|
|||
|
|
@ -104,8 +104,8 @@
|
|||
.visibility-tray {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 0.5em;
|
||||
align-items: baseline;
|
||||
margin-left: -0.5em;
|
||||
}
|
||||
|
||||
.visibility-notice {
|
||||
|
|
|
|||
|
|
@ -14,13 +14,33 @@ library.add(
|
|||
)
|
||||
|
||||
const ScopeSelector = {
|
||||
props: [
|
||||
'showAll',
|
||||
'userDefault',
|
||||
'originalScope',
|
||||
'initialScope',
|
||||
'onScopeChange'
|
||||
],
|
||||
props: {
|
||||
showAll: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
},
|
||||
userDefault: {
|
||||
required: true,
|
||||
type: String
|
||||
},
|
||||
originalScope: {
|
||||
required: false,
|
||||
type: String
|
||||
},
|
||||
initialScope: {
|
||||
required: false,
|
||||
type: String
|
||||
},
|
||||
onScopeChange: {
|
||||
required: true,
|
||||
type: Function
|
||||
},
|
||||
unstyled: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentScope: this.initialScope
|
||||
|
|
@ -43,11 +63,12 @@ const ScopeSelector = {
|
|||
return this.shouldShow('direct')
|
||||
},
|
||||
css () {
|
||||
const style = this.unstyled ? 'button-unstyled' : 'button-default'
|
||||
return {
|
||||
public: { toggled: this.currentScope === 'public' },
|
||||
unlisted: { toggled: this.currentScope === 'unlisted' },
|
||||
private: { toggled: this.currentScope === 'private' },
|
||||
direct: { toggled: this.currentScope === 'direct' }
|
||||
public: [style, { toggled: this.currentScope === 'public' }],
|
||||
unlisted: [style, { toggled: this.currentScope === 'unlisted' }],
|
||||
private: [style, { toggled: this.currentScope === 'private' }],
|
||||
direct: [style, { toggled: this.currentScope === 'direct' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div
|
||||
v-if="!showNothing"
|
||||
class="ScopeSelector"
|
||||
class="ScopeSelector btn-group"
|
||||
>
|
||||
<button
|
||||
v-if="showDirect"
|
||||
class="button-unstyled scope"
|
||||
class="scope"
|
||||
:class="css.direct"
|
||||
:title="$t('post_status.scope.direct')"
|
||||
type="button"
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
{{ ' ' }}
|
||||
<button
|
||||
v-if="showPrivate"
|
||||
class="button-unstyled scope"
|
||||
class="scope"
|
||||
:class="css.private"
|
||||
:title="$t('post_status.scope.private')"
|
||||
type="button"
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
{{ ' ' }}
|
||||
<button
|
||||
v-if="showUnlisted"
|
||||
class="button-unstyled scope"
|
||||
class="scope"
|
||||
:class="css.unlisted"
|
||||
:title="$t('post_status.scope.unlisted')"
|
||||
type="button"
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
{{ ' ' }}
|
||||
<button
|
||||
v-if="showPublic"
|
||||
class="button-unstyled scope"
|
||||
class="scope"
|
||||
:class="css.public"
|
||||
:title="$t('post_status.scope.public')"
|
||||
type="button"
|
||||
|
|
@ -65,12 +65,14 @@
|
|||
|
||||
<style lang="scss">
|
||||
.ScopeSelector {
|
||||
display: inline-block;
|
||||
|
||||
.scope {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
min-width: 1.3em;
|
||||
min-height: 1.3em;
|
||||
text-align: center;
|
||||
padding: 0.5em 0.25em
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,48 @@
|
|||
.settings-modal {
|
||||
overflow: hidden;
|
||||
|
||||
h4 {
|
||||
h2 {
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
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-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 1rem;
|
||||
margin-bottom: 0.5em;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.setting-list,
|
||||
.option-list {
|
||||
list-style-type: none;
|
||||
padding-left: 2em;
|
||||
margin: 0;
|
||||
|
||||
.btn:not(.dropdown-button) {
|
||||
padding: 0 2em;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: 0.5em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.suboptions {
|
||||
|
|
@ -42,7 +69,7 @@
|
|||
transition: transform;
|
||||
transition-timing-function: ease-in-out;
|
||||
transition-duration: 300ms;
|
||||
width: 1000px;
|
||||
width: 70em;
|
||||
max-width: 90vw;
|
||||
height: 90vh;
|
||||
|
||||
|
|
@ -77,6 +104,12 @@
|
|||
}
|
||||
|
||||
&.-mobile {
|
||||
.tabs {
|
||||
.menu-item {
|
||||
font-size: 1.2em
|
||||
}
|
||||
}
|
||||
|
||||
.setting-list,
|
||||
.option-list {
|
||||
padding-left: 0.25em;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
import VerticalTabSwitcher from 'src/components/tab_switcher/vertical_tab_switcher.jsx'
|
||||
|
||||
import InstanceTab from './admin_tabs/instance_tab.vue'
|
||||
import LimitsTab from './admin_tabs/limits_tab.vue'
|
||||
|
|
@ -31,7 +31,7 @@ library.add(
|
|||
|
||||
const SettingsModalAdminContent = {
|
||||
components: {
|
||||
TabSwitcher,
|
||||
VerticalTabSwitcher,
|
||||
|
||||
InstanceTab,
|
||||
LimitsTab,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<tab-switcher
|
||||
<vertical-tab-switcher
|
||||
v-if="adminDescriptionsLoaded && (noDb || adminDbLoaded)"
|
||||
ref="tabSwitcher"
|
||||
class="settings_tab-switcher"
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
>
|
||||
<EmojiTab />
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</vertical-tab-switcher>
|
||||
</template>
|
||||
|
||||
<script src="./settings_modal_admin_content.js"></script>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
import VerticalTabSwitcher from 'src/components/tab_switcher/vertical_tab_switcher.jsx'
|
||||
|
||||
import DataImportExportTab from './tabs/data_import_export_tab.vue'
|
||||
import MutesAndBlocksTab from './tabs/mutes_and_blocks_tab.vue'
|
||||
|
|
@ -7,15 +7,20 @@ 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 PostsTab from './tabs/posts_tab.vue'
|
||||
import ComposingTab from './tabs/composing_tab.vue'
|
||||
import ClutterTab from './tabs/clutter_tab.vue'
|
||||
import LayoutTab from './tabs/layout_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'
|
||||
import DeveloperTab from './tabs/developer_tab.vue'
|
||||
import OldThemeTab from './tabs/old_theme_tab/old_theme_tab.vue'
|
||||
import StyleTab from './tabs/style_tab/style_tab.vue'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faWrench,
|
||||
faUser,
|
||||
faMessage,
|
||||
faFilter,
|
||||
faPaintBrush,
|
||||
faPalette,
|
||||
|
|
@ -23,13 +28,16 @@ import {
|
|||
faDownload,
|
||||
faEyeSlash,
|
||||
faInfo,
|
||||
faWindowRestore
|
||||
faWindowRestore,
|
||||
faCode,
|
||||
faBroom
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
library.add(
|
||||
faWrench,
|
||||
faUser,
|
||||
faMessage,
|
||||
faFilter,
|
||||
faPaintBrush,
|
||||
faPalette,
|
||||
|
|
@ -37,12 +45,14 @@ library.add(
|
|||
faDownload,
|
||||
faEyeSlash,
|
||||
faInfo,
|
||||
faWindowRestore
|
||||
faWindowRestore,
|
||||
faBroom,
|
||||
faCode
|
||||
)
|
||||
|
||||
const SettingsModalContent = {
|
||||
components: {
|
||||
TabSwitcher,
|
||||
VerticalTabSwitcher,
|
||||
|
||||
DataImportExportTab,
|
||||
MutesAndBlocksTab,
|
||||
|
|
@ -51,10 +61,14 @@ const SettingsModalContent = {
|
|||
SecurityTab,
|
||||
ProfileTab,
|
||||
GeneralTab,
|
||||
PostsTab,
|
||||
ComposingTab,
|
||||
ClutterTab,
|
||||
LayoutTab,
|
||||
AppearanceTab,
|
||||
StyleTab,
|
||||
VersionTab,
|
||||
ThemeTab
|
||||
DeveloperTab,
|
||||
OldThemeTab
|
||||
},
|
||||
computed: {
|
||||
isLoggedIn () {
|
||||
|
|
@ -68,9 +82,12 @@ const SettingsModalContent = {
|
|||
},
|
||||
expertLevel () {
|
||||
return this.$store.state.config.expertLevel
|
||||
},
|
||||
isMobileLayout () {
|
||||
return useInterfaceStore().layoutType === 'mobile'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
navCollapsed: false,
|
||||
navHideHeader: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -1,21 +1,6 @@
|
|||
.settings_tab-switcher {
|
||||
height: 100%;
|
||||
|
||||
h1 {
|
||||
margin-bottom: 0.5em;
|
||||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
h5 {
|
||||
margin-bottom: 0;
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
border-bottom: 2px solid var(--border);
|
||||
margin: 1em 1em 1.4em;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
<template>
|
||||
<tab-switcher
|
||||
<vertical-tab-switcher
|
||||
ref="tabSwitcher"
|
||||
class="settings_tab-switcher"
|
||||
:side-tab-bar="true"
|
||||
:scrollable-tabs="true"
|
||||
:child-collapsed="childCollapsed"
|
||||
:body-scroll-lock="bodyLock"
|
||||
:hide-header="navHideHeader"
|
||||
>
|
||||
<div
|
||||
:label="$t('settings.general')"
|
||||
|
|
@ -14,6 +15,32 @@
|
|||
<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"
|
||||
|
|
@ -22,49 +49,26 @@
|
|||
<AppearanceTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="expertLevel > 0"
|
||||
:label="$t('settings.style.themes3.editor.title')"
|
||||
icon="palette"
|
||||
data-tab-name="style"
|
||||
:full-width="true"
|
||||
:label="$t('settings.layout')"
|
||||
icon="table-columns"
|
||||
data-tab-name="layout"
|
||||
:delay-render="true"
|
||||
>
|
||||
<StyleTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="expertLevel > 0"
|
||||
:label="$t('settings.theme_old')"
|
||||
icon="paint-brush"
|
||||
data-tab-name="theme"
|
||||
:delay-render="true"
|
||||
>
|
||||
<ThemeTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.profile_tab')"
|
||||
icon="user"
|
||||
data-tab-name="profile"
|
||||
>
|
||||
<ProfileTab />
|
||||
<LayoutTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:full-width="true"
|
||||
: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"
|
||||
>
|
||||
<SecurityTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.filtering')"
|
||||
:full-width="true"
|
||||
icon="filter"
|
||||
data-tab-name="filtering"
|
||||
>
|
||||
|
|
@ -73,12 +77,28 @@
|
|||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.mutes_and_blocks')"
|
||||
:fullHeight="true"
|
||||
icon="eye-slash"
|
||||
data-tab-name="mutesAndBlocks"
|
||||
:full-width="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"
|
||||
:full-width="true"
|
||||
>
|
||||
<SecurityTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.data_import_export_tab')"
|
||||
|
|
@ -88,13 +108,34 @@
|
|||
<DataImportExportTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.version.title')"
|
||||
icon="info"
|
||||
data-tab-name="version"
|
||||
v-if="expertLevel > 0"
|
||||
:label="$t('settings.style.themes3.editor.title')"
|
||||
icon="palette"
|
||||
data-tab-name="style"
|
||||
:delay-render="true"
|
||||
:full-width="true"
|
||||
>
|
||||
<VersionTab />
|
||||
<StyleTab />
|
||||
</div>
|
||||
</tab-switcher>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import VerticalTabSwitcher from 'src/components/tab_switcher/vertical_tab_switcher.jsx'
|
||||
|
||||
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 from '../helpers/unit_setting.vue'
|
||||
import { defaultHorizontalUnits } from '../helpers/unit_setting.js'
|
||||
import PaletteEditor from 'src/components/palette_editor/palette_editor.vue'
|
||||
import Preview from './theme_tab/theme_preview.vue'
|
||||
import FontControl from 'src/components/font_control/font_control.vue'
|
||||
import Preview from './old_theme_tab/theme_preview.vue'
|
||||
|
||||
import { newImporter } from 'src/services/export_import/export_import.js'
|
||||
import { convertTheme2To3 } from 'src/services/theme_data/theme2_to_theme3.js'
|
||||
|
|
@ -26,11 +26,17 @@ import { useInterfaceStore, normalizeThemeData } from 'src/stores/interface'
|
|||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
faGlobe,
|
||||
faDashboard,
|
||||
faPaintRoller,
|
||||
faTableColumns
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe
|
||||
faGlobe,
|
||||
faPaintRoller,
|
||||
faDashboard,
|
||||
faTableColumns
|
||||
)
|
||||
|
||||
const AppearanceTab = {
|
||||
|
|
@ -59,11 +65,6 @@ const AppearanceTab = {
|
|||
],
|
||||
userPalette: {},
|
||||
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,
|
||||
|
|
@ -86,9 +87,9 @@ const AppearanceTab = {
|
|||
FloatSetting,
|
||||
UnitSetting,
|
||||
ProfileSettingIndicator,
|
||||
FontControl,
|
||||
Preview,
|
||||
PaletteEditor
|
||||
PaletteEditor,
|
||||
VerticalTabSwitcher
|
||||
},
|
||||
mounted () {
|
||||
useInterfaceStore().getThemeData()
|
||||
|
|
@ -253,33 +254,10 @@ const AppearanceTab = {
|
|||
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]
|
||||
}
|
||||
},
|
||||
instanceWallpaperUsed () {
|
||||
return this.$store.state.instance.background &&
|
||||
!this.$store.state.users.currentUser.background_image
|
||||
},
|
||||
language: {
|
||||
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||
}
|
||||
},
|
||||
customThemeVersion () {
|
||||
const { themeVersion } = useInterfaceStore()
|
||||
return themeVersion
|
||||
|
|
@ -295,18 +273,6 @@ const AppearanceTab = {
|
|||
...SharedComputedObject()
|
||||
},
|
||||
methods: {
|
||||
updateFont (key, value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'theme3hacks',
|
||||
value: {
|
||||
...this.mergedConfig.theme3hacks,
|
||||
fonts: {
|
||||
...this.mergedConfig.theme3hacks.fonts,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
importFile () {
|
||||
this.fileImporter.importData()
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,28 +1,19 @@
|
|||
.appearance-tab {
|
||||
h3 {
|
||||
border: none
|
||||
}
|
||||
|
||||
.palette,
|
||||
.theme-notice {
|
||||
padding: 0.5em;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.setting-item {
|
||||
padding-bottom: 0;
|
||||
|
||||
&.heading {
|
||||
display: grid;
|
||||
align-items: baseline;
|
||||
grid-template-columns: 1fr auto auto auto;
|
||||
grid-gap: 0.5em;
|
||||
|
||||
h2 {
|
||||
flex: 1 0 auto;
|
||||
}
|
||||
}
|
||||
.theme-name {
|
||||
font-weight: 900;
|
||||
padding-bottom: 0.5em;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
padding: 0.25em;
|
||||
|
|
@ -71,6 +62,7 @@
|
|||
border-radius: var(--roundness);
|
||||
border: 1px solid var(--border);
|
||||
margin: -0.5em;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.palettes {
|
||||
|
|
@ -80,9 +72,9 @@
|
|||
padding: 0.5em;
|
||||
width: 100%;
|
||||
|
||||
h4 {
|
||||
margin: 0;
|
||||
h5 {
|
||||
grid-column: 1 / span 2;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -160,7 +152,7 @@
|
|||
|
||||
.theme-preview {
|
||||
font-size: 1rem; // fix for firefox
|
||||
width: 19rem;
|
||||
width: 14rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
<template>
|
||||
<div
|
||||
class="appearance-tab"
|
||||
:label="$t('settings.general')"
|
||||
:label="$t('settings.interface')"
|
||||
icon="table-columns"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.theme') }}</h2>
|
||||
<div
|
||||
class="setting-item"
|
||||
:label="$t('settings.theme')"
|
||||
icon="paintbrush"
|
||||
>
|
||||
<h3>{{ $t('settings.style.style_section') }}</h3>
|
||||
<ul
|
||||
ref="themeList"
|
||||
class="theme-list"
|
||||
|
|
@ -17,10 +22,10 @@
|
|||
@click="resetTheming"
|
||||
>
|
||||
<preview id="theme-preview-stock" />
|
||||
<h4 class="theme-name">
|
||||
<span class="theme-name">
|
||||
{{ $t('settings.style.stock_theme_used') }}
|
||||
<span class="alert neutral version">v3</span>
|
||||
</h4>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="isCustomThemeUsed"
|
||||
|
|
@ -28,10 +33,10 @@
|
|||
class="button-default theme-preview toggled"
|
||||
>
|
||||
<preview />
|
||||
<h4 class="theme-name">
|
||||
<span class="theme-name">
|
||||
{{ $t('settings.style.custom_theme_used') }}
|
||||
<span class="alert neutral version">v2</span>
|
||||
</h4>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-if="isCustomStyleUsed"
|
||||
|
|
@ -39,10 +44,10 @@
|
|||
class="button-default theme-preview toggled"
|
||||
>
|
||||
<preview />
|
||||
<h4 class="theme-name">
|
||||
<span class="theme-name">
|
||||
{{ $t('settings.style.custom_style_used') }}
|
||||
<span class="alert neutral version">v3</span>
|
||||
</h4>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
v-for="style in availableStyles"
|
||||
|
|
@ -54,10 +59,10 @@
|
|||
@click="style.version === 'v2' ? setTheme(style.key) : setStyle(style.key)"
|
||||
>
|
||||
<preview :id="'theme-preview-' + style.key" />
|
||||
<h4 class="theme-name">
|
||||
<span class="theme-name">
|
||||
{{ style.name }}
|
||||
<span class="alert neutral version">{{ style.version }}</span>
|
||||
</h4>
|
||||
</span>
|
||||
</button>
|
||||
</ul>
|
||||
<div class="import-file-container">
|
||||
|
|
@ -72,14 +77,14 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.style.themes3.palette.label') }}</h2>
|
||||
<h4>{{ $t('settings.style.themes3.palette.label') }}</h4>
|
||||
<div
|
||||
v-if="customThemeVersion === 'v3'"
|
||||
class="palettes-container"
|
||||
>
|
||||
<h4 v-if="stylePalettes?.length > 0">
|
||||
<h5 v-if="stylePalettes?.length > 0">
|
||||
{{ $t('settings.style.themes3.palette.style') }}
|
||||
</h4>
|
||||
</h5>
|
||||
<div class="palettes">
|
||||
<button
|
||||
v-for="p in stylePalettes || []"
|
||||
|
|
@ -103,7 +108,7 @@
|
|||
/>
|
||||
</div>
|
||||
</button>
|
||||
<h4>{{ $t('settings.style.themes3.palette.bundled') }}</h4>
|
||||
<h5>{{ $t('settings.style.themes3.palette.bundled') }}</h5>
|
||||
<button
|
||||
v-for="p in bundledPalettes"
|
||||
:key="p.name"
|
||||
|
|
@ -130,9 +135,9 @@
|
|||
</div>
|
||||
<div>
|
||||
<template v-if="customThemeVersion === 'v3'">
|
||||
<h4 v-if="expertLevel > 0">
|
||||
<h5 v-if="expertLevel > 0">
|
||||
{{ $t('settings.style.themes3.palette.user') }}
|
||||
</h4>
|
||||
</h5>
|
||||
<PaletteEditor
|
||||
v-if="expertLevel > 0"
|
||||
v-model="userPalette"
|
||||
|
|
@ -150,9 +155,7 @@
|
|||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.background') }}</h2>
|
||||
<h3>{{ $t('settings.background') }}</h3>
|
||||
<div class="banner-background-preview">
|
||||
<img :src="user.background_image">
|
||||
<button
|
||||
|
|
@ -193,193 +196,11 @@
|
|||
>
|
||||
{{ $t('settings.save') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.scale_and_layout') }}</h2>
|
||||
<h3>{{ $t('settings.visual_tweaks') }}</h3>
|
||||
<div class="alert neutral theme-notice">
|
||||
{{ $t("settings.style.appearance_tab_note") }}
|
||||
{{ $t("settings.style.visual_tweaks_section_note") }}
|
||||
</div>
|
||||
<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>
|
||||
<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.style.interface_font_user_override') }}</h3>
|
||||
<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:model-value="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:model-value="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:model-value="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:model-value="v => updateFont('monospace', v)"
|
||||
/>
|
||||
</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>
|
||||
<li>
|
||||
<UnitSetting
|
||||
path="themeEditorMinWidth"
|
||||
:units="['px', 'rem']"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.theme_editor_min_width') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.visual_tweaks') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="modalMobileCenter">
|
||||
{{ $t('settings.mobile_center_dialog') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="forcedRoundness"
|
||||
|
|
@ -403,22 +224,6 @@
|
|||
{{ $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>
|
||||
|
|
|
|||
194
src/components/settings_modal/tabs/clutter_tab.js
Normal file
194
src/components/settings_modal/tabs/clutter_tab.js
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
import { mapState, mapActions } from 'pinia'
|
||||
import { mapState as mapVuexState } from 'vuex'
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { useServerSideStorageStore } from 'src/stores/serverSideStorage'
|
||||
|
||||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import HelpIndicator from '../helpers/help_indicator.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
|
||||
const ClutterTab = {
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
UnitSetting,
|
||||
IntegerSetting,
|
||||
Checkbox,
|
||||
Select,
|
||||
HelpIndicator
|
||||
},
|
||||
computed: {
|
||||
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
|
||||
...SharedComputedObject(),
|
||||
...mapState(
|
||||
useServerSideStorageStore,
|
||||
{
|
||||
muteFilters: store => Object.entries(store.prefsStorage.simple.muteFilters),
|
||||
muteFiltersObject: store => store.prefsStorage.simple.muteFilters
|
||||
}
|
||||
),
|
||||
...mapVuexState({
|
||||
blockExpirationSupported: state => state.instance.blockExpiration
|
||||
}),
|
||||
onMuteDefaultActionLv1: {
|
||||
get () {
|
||||
const value = this.$store.state.config.onMuteDefaultAction
|
||||
if (value === 'ask' || value === 'forever') {
|
||||
return value
|
||||
} else {
|
||||
return 'temporarily'
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
let realValue = value
|
||||
if (value !== 'ask' && value !== 'forever') {
|
||||
realValue = '14d'
|
||||
}
|
||||
this.$store.dispatch('setOption', { name: 'onMuteDefaultAction', value: realValue })
|
||||
}
|
||||
},
|
||||
onBlockDefaultActionLv1: {
|
||||
get () {
|
||||
const value = this.$store.state.config.onBlockDefaultAction
|
||||
if (value === 'ask' || value === 'forever') {
|
||||
return value
|
||||
} else {
|
||||
return 'temporarily'
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
let realValue = value
|
||||
if (value !== 'ask' && value !== 'forever') {
|
||||
realValue = '14d'
|
||||
}
|
||||
this.$store.dispatch('setOption', { name: 'onBlockDefaultAction', value: realValue })
|
||||
}
|
||||
},
|
||||
muteFiltersDraft () {
|
||||
return Object.entries(this.muteFiltersDraftObject)
|
||||
},
|
||||
muteFiltersExpired () {
|
||||
const now = Date.now()
|
||||
return Object
|
||||
.entries(this.muteFiltersDraftObject)
|
||||
.filter(([, { expires }]) => expires != null && expires <= now)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useServerSideStorageStore, ['setPreference', 'unsetPreference', 'pushServerSideStorage']),
|
||||
getDatetimeLocal (timestamp) {
|
||||
const date = new Date(timestamp)
|
||||
const fmt = new Intl.NumberFormat("en-US", {minimumIntegerDigits: 2})
|
||||
const datetime = [
|
||||
date.getFullYear(),
|
||||
'-',
|
||||
fmt.format(date.getMonth() + 1),
|
||||
'-',
|
||||
fmt.format(date.getDate()),
|
||||
'T',
|
||||
fmt.format(date.getHours()),
|
||||
':',
|
||||
fmt.format(date.getMinutes())
|
||||
].join('')
|
||||
return datetime
|
||||
},
|
||||
checkRegexValid (id) {
|
||||
const filter = this.muteFiltersObject[id]
|
||||
if (filter.type !== 'regexp') return true
|
||||
if (filter.type !== 'user_regexp') return true
|
||||
const { value } = filter
|
||||
let valid = true
|
||||
try {
|
||||
new RegExp(value)
|
||||
} catch {
|
||||
valid = false
|
||||
console.error('Invalid RegExp: ' + value)
|
||||
}
|
||||
return valid
|
||||
},
|
||||
createFilter (filter = {
|
||||
type: 'word',
|
||||
value: '',
|
||||
name: 'New Filter',
|
||||
enabled: true,
|
||||
expires: null,
|
||||
hide: false,
|
||||
}) {
|
||||
const newId = uuidv4()
|
||||
|
||||
filter.order = this.muteFilters.length + 2
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
exportFilter(id) {
|
||||
this.exportedFilter = { ...this.muteFiltersDraftObject[id] }
|
||||
delete this.exportedFilter.order
|
||||
this.filterExporter.exportData()
|
||||
},
|
||||
importFilter() {
|
||||
this.filterImporter.importData()
|
||||
},
|
||||
copyFilter (id) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
const newId = uuidv4()
|
||||
|
||||
this.muteFiltersDraftObject[newId] = filter
|
||||
this.setPreference({ path: 'simple.muteFilters.' + newId , value: filter })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
deleteFilter (id) {
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
purgeExpiredFilters () {
|
||||
this.muteFiltersExpired.forEach(([id]) => {
|
||||
console.log(id)
|
||||
delete this.muteFiltersDraftObject[id]
|
||||
this.unsetPreference({ path: 'simple.muteFilters.' + id , value: null })
|
||||
})
|
||||
this.pushServerSideStorage()
|
||||
},
|
||||
updateFilter(id, field, value) {
|
||||
const filter = { ...this.muteFiltersDraftObject[id] }
|
||||
if (field === 'expires-never') {
|
||||
if (!value) {
|
||||
const offset = 1000 * 60 * 60 * 24 * 14 // 2 weeks
|
||||
const date = Date.now() + offset
|
||||
filter.expires = date
|
||||
} else {
|
||||
filter.expires = null
|
||||
}
|
||||
} else if (field === 'expires') {
|
||||
const parsed = Date.parse(value)
|
||||
filter.expires = parsed.valueOf()
|
||||
} else {
|
||||
filter[field] = value
|
||||
}
|
||||
this.muteFiltersDraftObject[id] = filter
|
||||
this.muteFiltersDraftDirty[id] = true
|
||||
},
|
||||
saveFilter(id) {
|
||||
this.setPreference({ path: 'simple.muteFilters.' + id , value: this.muteFiltersDraftObject[id] })
|
||||
this.pushServerSideStorage()
|
||||
this.muteFiltersDraftDirty[id] = false
|
||||
},
|
||||
},
|
||||
// Updating nested properties
|
||||
watch: {
|
||||
replyVisibility () {
|
||||
this.$store.dispatch('queueFlushAll')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ClutterTab
|
||||
104
src/components/settings_modal/tabs/clutter_tab.vue
Normal file
104
src/components/settings_modal/tabs/clutter_tab.vue
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
<template>
|
||||
<div class="clutter-tab">
|
||||
<div class="setting-item">
|
||||
<h3>{{ $t('settings.interface') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysShowSubjectInput"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.subject_input_always_show') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="minimalScopesMode"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.minimal_scopes_mode') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
expert="1"
|
||||
path="hidePostStats"
|
||||
>
|
||||
{{ $t('settings.hide_post_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
expert="1"
|
||||
path="hideUserStats"
|
||||
>
|
||||
{{ $t('settings.hide_user_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideBotIndication">
|
||||
{{ $t('settings.hide_actor_type_indication') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideScrobbles">
|
||||
{{ $t('settings.hide_scrobbles') }}
|
||||
</BooleanSetting>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<UnitSetting
|
||||
key="hideScrobblesAfter"
|
||||
path="hideScrobblesAfter"
|
||||
:units="['m', 'h', 'd']"
|
||||
unit-set="time"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_scrobbles_after') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<IntegerSetting
|
||||
path="maxThumbnails"
|
||||
expert="1"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.max_thumbnails') }}
|
||||
</IntegerSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachments">
|
||||
{{ $t('settings.hide_attachments_in_tl') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachmentsInConv">
|
||||
{{ $t('settings.hide_attachments_in_convo') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="userCardHidePersonalMarks"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.user_card_hide_personal_marks') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="instanceShoutboxPresent">
|
||||
<BooleanSetting
|
||||
path="hideShoutbox"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_shoutbox') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./clutter_tab.js"></script>
|
||||
178
src/components/settings_modal/tabs/composing_tab.js
Normal file
178
src/components/settings_modal/tabs/composing_tab.js
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
import { mapState } from 'vuex'
|
||||
|
||||
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 FloatSetting from '../helpers/float_setting.vue'
|
||||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
||||
import FontControl from 'src/components/font_control/font_control.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe,
|
||||
faMessage,
|
||||
faPenAlt,
|
||||
faDatabase,
|
||||
faSliders
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe,
|
||||
faMessage,
|
||||
faPenAlt,
|
||||
faDatabase,
|
||||
faSliders
|
||||
)
|
||||
|
||||
const ComposingTab = {
|
||||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
|
||||
})),
|
||||
conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_display_${mode}`)
|
||||
})),
|
||||
absoluteTime12hOptions: ['24h', '12h'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.absolute_time_format_12h_${mode}`)
|
||||
})),
|
||||
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_other_replies_button_${mode}`)
|
||||
})),
|
||||
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.mention_link_display_${mode}`)
|
||||
})),
|
||||
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
|
||||
})),
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
||||
})),
|
||||
loopSilentAvailable:
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||
// Chrome-likes
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
|
||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),
|
||||
emailLanguage: this.$store.state.users.currentUser.language || ['']
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
FloatSetting,
|
||||
UnitSetting,
|
||||
InterfaceLanguageSwitcher,
|
||||
ProfileSettingIndicator,
|
||||
ScopeSelector,
|
||||
Select,
|
||||
FontControl
|
||||
},
|
||||
computed: {
|
||||
postFormats () {
|
||||
return this.$store.state.instance.postFormats || []
|
||||
},
|
||||
postContentOptions () {
|
||||
return this.postFormats.map(format => ({
|
||||
key: format,
|
||||
value: format,
|
||||
label: this.$t(`post_status.content_type["${format}"]`)
|
||||
}))
|
||||
},
|
||||
language: {
|
||||
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||
}
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
...mapState({
|
||||
blockExpirationSupported: state => state.instance.blockExpiration,
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
changeDefaultScope (value) {
|
||||
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
|
||||
},
|
||||
clearCache (key) {
|
||||
clearCache(key)
|
||||
.then(() => {
|
||||
this.$store.dispatch('settingsSaved', { success: true })
|
||||
})
|
||||
.catch(error => {
|
||||
this.$store.dispatch('settingsSaved', { error })
|
||||
})
|
||||
},
|
||||
tooSmall () {
|
||||
this.$emit('tooSmall')
|
||||
},
|
||||
tooBig () {
|
||||
this.$emit('tooBig')
|
||||
},
|
||||
getNavMode () {
|
||||
return this.$refs.tabSwitcher.getNavMode()
|
||||
},
|
||||
clearAssetCache () {
|
||||
this.clearCache(cacheKey)
|
||||
},
|
||||
clearEmojiCache () {
|
||||
this.clearCache(emojiCacheKey)
|
||||
},
|
||||
updateProfile () {
|
||||
const params = {
|
||||
language: localeService.internalToBackendLocaleMulti(this.emailLanguage)
|
||||
}
|
||||
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateProfile({ params })
|
||||
.then((user) => {
|
||||
this.$store.commit('addNewUsers', [user])
|
||||
this.$store.commit('setCurrentUser', user)
|
||||
})
|
||||
},
|
||||
updateFont (key, value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'theme3hacks',
|
||||
value: {
|
||||
...this.mergedConfig.theme3hacks,
|
||||
fonts: {
|
||||
...this.mergedConfig.theme3hacks.fonts,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default ComposingTab
|
||||
111
src/components/settings_modal/tabs/composing_tab.vue
Normal file
111
src/components/settings_modal/tabs/composing_tab.vue
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
<template>
|
||||
<div :label="$t('settings.posts')">
|
||||
<div class="setting-item">
|
||||
<h3>{{ $t('settings.general') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<label for="default-vis">
|
||||
{{ $t('settings.default_vis') }}
|
||||
{{ ' ' }}
|
||||
<ScopeSelector
|
||||
class="scope-selector"
|
||||
:show-all="true"
|
||||
:user-default="$store.state.profileConfig.defaultScope"
|
||||
:initial-scope="$store.state.profileConfig.defaultScope"
|
||||
:on-scope-change="changeDefaultScope"
|
||||
:unstyled="false"uns
|
||||
/>
|
||||
<ProfileSettingIndicator :is-profile="true" />
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<!-- <BooleanSetting source="profile" path="defaultNSFW"> -->
|
||||
<BooleanSetting path="sensitiveByDefault">
|
||||
{{ $t('settings.sensitive_by_default') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="postFormats.length > 0">
|
||||
<ChoiceSetting
|
||||
id="postContentType"
|
||||
path="postContentType"
|
||||
:options="postContentOptions"
|
||||
>
|
||||
{{ $t('settings.default_post_status_content_type') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="padEmoji">
|
||||
{{ $t('settings.pad_emoji') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="autocompleteSelect"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.autocomplete_select_first') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="autoSaveDraft"
|
||||
>
|
||||
{{ $t('settings.auto_save_draft') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="!mergedConfig.autoSaveDraft">
|
||||
<ChoiceSetting
|
||||
id="unsavedPostAction"
|
||||
path="unsavedPostAction"
|
||||
:options="unsavedPostActionOptions"
|
||||
>
|
||||
{{ $t('settings.unsaved_post_action') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.replies') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="scopeCopy"
|
||||
>
|
||||
{{ $t('settings.scope_copy') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="subjectLineBehavior"
|
||||
path="subjectLineBehavior"
|
||||
:options="subjectLineOptions"
|
||||
>
|
||||
{{ $t('settings.subject_line_behavior') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 v-if="expertLevel > 0">{{ $t('settings.attachments') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="imageCompression"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.image_compression') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysUseJpeg"
|
||||
expert="1"
|
||||
parent-path="imageCompression"
|
||||
>
|
||||
{{ $t('settings.always_use_jpeg') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./composing_tab.js"></script>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
.data-import-export-tab {
|
||||
h3 {
|
||||
margin-right: -2em;
|
||||
}
|
||||
|
||||
.importer-exporter {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +1,61 @@
|
|||
<template>
|
||||
<div
|
||||
class="data-import-export-tab"
|
||||
:label="$t('settings.data_import_export_tab')"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.follow_import') }}</h2>
|
||||
<p>{{ $t('settings.import_followers_from_a_csv_file') }}</p>
|
||||
<Importer
|
||||
:submit-handler="importFollows"
|
||||
:success-message="$t('settings.follows_imported')"
|
||||
:error-message="$t('settings.follow_import_error')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.follow_export') }}</h2>
|
||||
<Exporter
|
||||
:get-content="getFollowsContent"
|
||||
filename="friends.csv"
|
||||
:export-button-label="$t('settings.follow_export_button')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.block_import') }}</h2>
|
||||
<p>{{ $t('settings.import_blocks_from_a_csv_file') }}</p>
|
||||
<Importer
|
||||
:submit-handler="importBlocks"
|
||||
:success-message="$t('settings.blocks_imported')"
|
||||
:error-message="$t('settings.block_import_error')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.block_export') }}</h2>
|
||||
<Exporter
|
||||
:get-content="getBlocksContent"
|
||||
filename="blocks.csv"
|
||||
:export-button-label="$t('settings.block_export_button')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.mute_import') }}</h2>
|
||||
<p>{{ $t('settings.import_mutes_from_a_csv_file') }}</p>
|
||||
<Importer
|
||||
:submit-handler="importMutes"
|
||||
:success-message="$t('settings.mutes_imported')"
|
||||
:error-message="$t('settings.mute_import_error')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.mute_export') }}</h2>
|
||||
<Exporter
|
||||
:get-content="getMutesContent"
|
||||
filename="mutes.csv"
|
||||
:export-button-label="$t('settings.mute_export_button')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.account_backup') }}</h2>
|
||||
<h3>{{ $t('settings.import_export.title') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<h4>{{ $t('settings.import_export.follows') }}</h4>
|
||||
<p>{{ $t('settings.import_followers_from_a_csv_file') }}</p>
|
||||
<div class="importer-exporter">
|
||||
<Importer
|
||||
:submit-handler="importFollows"
|
||||
:success-message="$t('settings.follows_imported')"
|
||||
:error-message="$t('settings.follow_import_error')"
|
||||
/>
|
||||
<Exporter
|
||||
:get-content="getFollowsContent"
|
||||
filename="friends.csv"
|
||||
:export-button-label="$t('settings.follow_export_button')"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<h4>{{ $t('settings.import_export.mutes') }}</h4>
|
||||
<p>{{ $t('settings.import_mutes_from_a_csv_file') }}</p>
|
||||
<div class="importer-exporter">
|
||||
<Importer
|
||||
:submit-handler="importMutes"
|
||||
:success-message="$t('settings.mutes_imported')"
|
||||
:error-message="$t('settings.mute_import_error')"
|
||||
/>
|
||||
<Exporter
|
||||
:get-content="getMutesContent"
|
||||
filename="friends.csv"
|
||||
:export-button-label="$t('settings.mute_export_button')"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<h4>{{ $t('settings.import_export.blocks') }}</h4>
|
||||
<p>{{ $t('settings.import_blocks_from_a_csv_file') }}</p>
|
||||
<div class="importer-exporter">
|
||||
<Importer
|
||||
:submit-handler="importBlocks"
|
||||
:success-message="$t('settings.blocks_imported')"
|
||||
:error-message="$t('settings.block_import_error')"
|
||||
/>
|
||||
<Exporter
|
||||
:get-content="getBlocksContent"
|
||||
filename="friends.csv"
|
||||
:export-button-label="$t('settings.block_export_button')"
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.account_backup') }}</h3>
|
||||
<p>{{ $t('settings.account_backup_description') }}</p>
|
||||
<table>
|
||||
<thead>
|
||||
|
|
@ -128,4 +129,4 @@
|
|||
</template>
|
||||
|
||||
<script src="./data_import_export_tab.js"></script>
|
||||
<!-- <style lang="scss" src="./profile.scss"></style> -->
|
||||
<style lang="scss" src="./data_import_export_tab.scss"></style>
|
||||
|
|
|
|||
|
|
@ -1,3 +1,9 @@
|
|||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js'
|
||||
|
||||
const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
|
||||
|
||||
const VersionTab = {
|
||||
|
|
@ -9,10 +15,14 @@ const VersionTab = {
|
|||
frontendVersion: instance.frontendVersion
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting
|
||||
},
|
||||
computed: {
|
||||
frontendVersionLink () {
|
||||
return pleromaFeCommitUrl + this.frontendVersion
|
||||
}
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
}
|
||||
}
|
||||
|
||||
10
src/components/settings_modal/tabs/developer_tab.scss
Normal file
10
src/components/settings_modal/tabs/developer_tab.scss
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
.developer-tab {
|
||||
dt {
|
||||
font-weight: 900;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
}
|
||||
72
src/components/settings_modal/tabs/developer_tab.vue
Normal file
72
src/components/settings_modal/tabs/developer_tab.vue
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
<template>
|
||||
<div
|
||||
:label="$t('settings.developer')"
|
||||
class="developer-tab"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h3>{{ $t('settings.version.title')}}</h3>
|
||||
<dl class="setting-list">
|
||||
<dt>{{ $t('settings.version.backend_version') }}</dt>
|
||||
<dd>
|
||||
<a
|
||||
:href="backendRepository"
|
||||
target="_blank"
|
||||
>
|
||||
{{ backendVersion }}
|
||||
</a>
|
||||
</dd>
|
||||
<dt>{{ $t('settings.version.frontend_version') }}</dt>
|
||||
<dd>
|
||||
<a
|
||||
:href="frontendVersionLink"
|
||||
target="_blank"
|
||||
>
|
||||
{{ frontendVersion }}
|
||||
</a>
|
||||
</dd>
|
||||
</dl>
|
||||
<h3>{{ $t('settings.debug')}}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="virtualScrolling">
|
||||
{{ $t('settings.virtual_scrolling') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="clearAssetCache"
|
||||
>
|
||||
{{ $t('settings.clear_asset_cache') }}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="clearEmojiCache"
|
||||
>
|
||||
{{ $t('settings.clear_emoji_cache') }}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="themeDebug"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.theme_debug') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="forceThemeRecompilation"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.force_theme_recompilation_debug') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script src="./developer_tab.js" />
|
||||
<style lang="scss" src="./developer_tab.scss"></style>
|
||||
|
|
@ -91,6 +91,7 @@ const FilteringTab = {
|
|||
HelpIndicator
|
||||
},
|
||||
computed: {
|
||||
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
|
||||
...SharedComputedObject(),
|
||||
...mapState(
|
||||
useServerSideStorageStore,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,7 @@
|
|||
<template>
|
||||
<div
|
||||
:label="$t('settings.filtering')"
|
||||
class="filtering-tab"
|
||||
>
|
||||
<div class="filtering-tab">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.filter.clutter') }}</h2>
|
||||
<h3>{{ $t('settings.filter.mute_filter') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
|
|
@ -16,70 +13,6 @@
|
|||
{{ $t('settings.replies_in_timeline') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
expert="1"
|
||||
path="hidePostStats"
|
||||
>
|
||||
{{ $t('settings.hide_post_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
expert="1"
|
||||
path="hideUserStats"
|
||||
>
|
||||
{{ $t('settings.hide_user_stats') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideBotIndication">
|
||||
{{ $t('settings.hide_actor_type_indication') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideScrobbles">
|
||||
{{ $t('settings.hide_scrobbles') }}
|
||||
</BooleanSetting>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<UnitSetting
|
||||
key="hideScrobblesAfter"
|
||||
path="hideScrobblesAfter"
|
||||
:units="['m', 'h', 'd']"
|
||||
unit-set="time"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_scrobbles_after') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
path="maxThumbnails"
|
||||
expert="1"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.max_thumbnails') }}
|
||||
</IntegerSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachments">
|
||||
{{ $t('settings.hide_attachments_in_tl') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideAttachmentsInConv">
|
||||
{{ $t('settings.hide_attachments_in_convo') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.filter.mute_filter') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
{{ $t('user_card.default_mute_expiration') }}
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -2,129 +2,57 @@ import { mapState } from 'vuex'
|
|||
|
||||
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 FloatSetting from '../helpers/float_setting.vue'
|
||||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
import FloatSetting from '../helpers/float_setting.vue'
|
||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
||||
import FontControl from 'src/components/font_control/font_control.vue'
|
||||
import { defaultHorizontalUnits } from '../helpers/unit_setting.js'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
import { clearCache, cacheKey, emojiCacheKey } from 'src/services/sw/sw.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe
|
||||
)
|
||||
|
||||
const GeneralTab = {
|
||||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
|
||||
})),
|
||||
conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_display_${mode}`)
|
||||
})),
|
||||
absoluteTime12hOptions: ['24h', '12h'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.absolute_time_format_12h_${mode}`)
|
||||
})),
|
||||
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_other_replies_button_${mode}`)
|
||||
})),
|
||||
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.mention_link_display_${mode}`)
|
||||
})),
|
||||
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
|
||||
})),
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
||||
})),
|
||||
loopSilentAvailable:
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||
// Chrome-likes
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
|
||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),
|
||||
emailLanguage: this.$store.state.users.currentUser.language || ['']
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
FloatSetting,
|
||||
UnitSetting,
|
||||
FloatSetting,
|
||||
FontControl,
|
||||
InterfaceLanguageSwitcher,
|
||||
ProfileSettingIndicator,
|
||||
ScopeSelector,
|
||||
Select
|
||||
ProfileSettingIndicator
|
||||
},
|
||||
computed: {
|
||||
postFormats () {
|
||||
return this.$store.state.instance.postFormats || []
|
||||
},
|
||||
postContentOptions () {
|
||||
return this.postFormats.map(format => ({
|
||||
key: format,
|
||||
value: format,
|
||||
label: this.$t(`post_status.content_type["${format}"]`)
|
||||
}))
|
||||
},
|
||||
language: {
|
||||
get: function () { return this.$store.getters.mergedConfig.interfaceLanguage },
|
||||
set: function (val) {
|
||||
this.$store.dispatch('setOption', { name: 'interfaceLanguage', value: val })
|
||||
}
|
||||
},
|
||||
instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
|
||||
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
|
||||
...SharedComputedObject(),
|
||||
...mapState({
|
||||
blockExpirationSupported: state => state.instance.blockExpiration,
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
changeDefaultScope (value) {
|
||||
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
|
||||
},
|
||||
clearCache (key) {
|
||||
clearCache(key)
|
||||
.then(() => {
|
||||
this.$store.dispatch('settingsSaved', { success: true })
|
||||
})
|
||||
.catch(error => {
|
||||
this.$store.dispatch('settingsSaved', { error })
|
||||
})
|
||||
},
|
||||
clearAssetCache () {
|
||||
this.clearCache(cacheKey)
|
||||
},
|
||||
clearEmojiCache () {
|
||||
this.clearCache(emojiCacheKey)
|
||||
},
|
||||
updateProfile () {
|
||||
const params = {
|
||||
language: localeService.internalToBackendLocaleMulti(this.emailLanguage)
|
||||
|
|
@ -137,6 +65,18 @@ const GeneralTab = {
|
|||
this.$store.commit('setCurrentUser', user)
|
||||
})
|
||||
},
|
||||
updateFont (key, value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'theme3hacks',
|
||||
value: {
|
||||
...this.mergedConfig.theme3hacks,
|
||||
fonts: {
|
||||
...this.mergedConfig.theme3hacks.fonts,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div :label="$t('settings.general')">
|
||||
<div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.interface') }}</h2>
|
||||
<h3>{{ $t('settings.format_and_language') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<interface-language-switcher
|
||||
|
|
@ -20,16 +20,98 @@
|
|||
{{ $t('settings.email_language') }}
|
||||
</interface-language-switcher>
|
||||
</li>
|
||||
<li v-if="instanceSpecificPanelPresent">
|
||||
<BooleanSetting path="hideISP">
|
||||
{{ $t('settings.hide_isp') }}
|
||||
<li>
|
||||
<BooleanSetting path="useAbsoluteTimeFormat">
|
||||
{{ $t('settings.absolute_time_format') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="stopGifs">
|
||||
{{ $t('settings.stop_gifs') }}
|
||||
</BooleanSetting>
|
||||
<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>
|
||||
</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') }}
|
||||
|
|
@ -53,72 +135,9 @@
|
|||
{{ $t('settings.useStreamingApi') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="virtualScrolling"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.virtual_scrolling') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="userPopoverAvatarAction"
|
||||
path="userPopoverAvatarAction"
|
||||
:options="userPopoverAvatarActionOptions"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.user_popover_avatar_action') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="userPopoverOverlay"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.user_popover_avatar_overlay') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="userCardLeftJustify"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.user_card_left_justify') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="userCardHidePersonalMarks"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.user_card_hide_personal_marks') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysShowNewPostButton"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.always_show_post_button') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="autohideFloatingPostButton"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.autohide_floating_post_button') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="instanceShoutboxPresent">
|
||||
<BooleanSetting
|
||||
path="hideShoutbox"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.hide_shoutbox') }}
|
||||
</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>
|
||||
<ul class="option-list">
|
||||
|
|
@ -179,383 +198,6 @@
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.post_look_feel') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="conversationDisplay"
|
||||
path="conversationDisplay"
|
||||
:options="conversationDisplayOptions"
|
||||
>
|
||||
{{ $t('settings.conversation_display') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<ul
|
||||
v-if="mergedConfig.conversationDisplay !== 'linear'"
|
||||
class="setting-list suboptions"
|
||||
>
|
||||
<li>
|
||||
<BooleanSetting path="conversationTreeAdvanced">
|
||||
{{ $t('settings.tree_advanced') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="conversationTreeFadeAncestors"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.tree_fade_ancestors') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
path="maxDepthInThread"
|
||||
:min="3"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.max_depth_in_thread') }}
|
||||
</IntegerSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="conversationOtherRepliesButton"
|
||||
path="conversationOtherRepliesButton"
|
||||
:options="conversationOtherRepliesButtonOptions"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.conversation_other_replies_button') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<BooleanSetting path="collapseMessageWithSubject">
|
||||
{{ $t('settings.collapse_subject') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="emojiReactionsOnTimeline"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.emoji_reactions_on_timeline') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
v-if="user"
|
||||
source="profile"
|
||||
path="stripRichContent"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.no_rich_text_description') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="useAbsoluteTimeFormat"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.absolute_time_format') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ul
|
||||
v-if="mergedConfig.useAbsoluteTimeFormat"
|
||||
class="setting-list suboptions"
|
||||
>
|
||||
<li>
|
||||
<UnitSetting
|
||||
path="absoluteTimeFormatMinAge"
|
||||
unit-set="time"
|
||||
:units="['s', 'm', 'h', 'd']"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.absolute_time_format_min_age') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="absoluteTime12h"
|
||||
path="absoluteTime12h"
|
||||
:options="absoluteTime12hOptions"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.absolute_time_format_12h') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="imageCompression"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.image_compression') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysUseJpeg"
|
||||
expert="1"
|
||||
parent-path="imageCompression"
|
||||
>
|
||||
{{ $t('settings.always_use_jpeg') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="useContainFit"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.use_contain_fit') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideNsfw">
|
||||
{{ $t('settings.nsfw_clickthrough') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="preloadImage"
|
||||
expert="1"
|
||||
parent-path="hideNsfw"
|
||||
>
|
||||
{{ $t('settings.preload_images') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="useOneClickNsfw"
|
||||
expert="1"
|
||||
parent-path="hideNsfw"
|
||||
>
|
||||
{{ $t('settings.use_one_click_nsfw') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="loopVideo"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.loop_video') }}
|
||||
</BooleanSetting>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="loopVideoSilentOnly"
|
||||
expert="1"
|
||||
parent-path="loopVideo"
|
||||
:disabled="!loopSilentAvailable"
|
||||
>
|
||||
{{ $t('settings.loop_video_silent_only') }}
|
||||
</BooleanSetting>
|
||||
<div
|
||||
v-if="!loopSilentAvailable"
|
||||
class="unavailable"
|
||||
>
|
||||
<FAIcon icon="globe" />! {{ $t('settings.limited_availability') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="playVideosInModal"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.play_videos_in_modal') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<h3>{{ $t('settings.mention_links') }}</h3>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="mentionLinkDisplay"
|
||||
path="mentionLinkDisplay"
|
||||
:options="mentionLinkDisplayOptions"
|
||||
>
|
||||
{{ $t('settings.mention_link_display') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="mentionLinkShowTooltip"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.mention_link_use_tooltip') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="mentionLinkShowAvatar">
|
||||
{{ $t('settings.mention_link_show_avatar') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="mentionLinkFadeDomain"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.mention_link_fade_domain') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="user">
|
||||
<BooleanSetting
|
||||
path="mentionLinkBoldenYou"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.mention_link_bolden_you') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<h3 v-if="expertLevel > 0">
|
||||
{{ $t('settings.fun') }}
|
||||
</h3>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="greentext"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.greentext') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="user">
|
||||
<BooleanSetting
|
||||
path="mentionLinkShowYous"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.show_yous') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="user"
|
||||
class="setting-item"
|
||||
>
|
||||
<h2>{{ $t('settings.composing') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<label for="default-vis">
|
||||
{{ $t('settings.default_vis') }} <ProfileSettingIndicator :is-profile="true" />
|
||||
<ScopeSelector
|
||||
class="scope-selector"
|
||||
:show-all="true"
|
||||
:user-default="$store.state.profileConfig.defaultScope"
|
||||
:initial-scope="$store.state.profileConfig.defaultScope"
|
||||
:on-scope-change="changeDefaultScope"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<!-- <BooleanSetting source="profile" path="defaultNSFW"> -->
|
||||
<BooleanSetting path="sensitiveByDefault">
|
||||
{{ $t('settings.sensitive_by_default') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="scopeCopy"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.scope_copy') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysShowSubjectInput"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.subject_input_always_show') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="subjectLineBehavior"
|
||||
path="subjectLineBehavior"
|
||||
:options="subjectLineOptions"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.subject_line_behavior') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li v-if="postFormats.length > 0">
|
||||
<ChoiceSetting
|
||||
id="postContentType"
|
||||
path="postContentType"
|
||||
:options="postContentOptions"
|
||||
>
|
||||
{{ $t('settings.post_status_content_type') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="minimalScopesMode"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.minimal_scopes_mode') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="padEmoji"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.pad_emoji') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="autocompleteSelect"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.autocomplete_select_first') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="autoSaveDraft"
|
||||
>
|
||||
{{ $t('settings.auto_save_draft') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="!mergedConfig.autoSaveDraft">
|
||||
<ChoiceSetting
|
||||
id="unsavedPostAction"
|
||||
path="unsavedPostAction"
|
||||
:options="unsavedPostActionOptions"
|
||||
>
|
||||
{{ $t('settings.unsaved_post_action') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div
|
||||
class="setting-item"
|
||||
>
|
||||
<h2>{{ $t('settings.cache') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="clearAssetCache"
|
||||
>
|
||||
{{ $t('settings.clear_asset_cache') }}
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button
|
||||
class="btn button-default"
|
||||
@click="clearEmojiCache"
|
||||
>
|
||||
{{ $t('settings.clear_emoji_cache') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
49
src/components/settings_modal/tabs/layout_tab.js
Normal file
49
src/components/settings_modal/tabs/layout_tab.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import UnitSetting from '../helpers/unit_setting.vue'
|
||||
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const GeneralTab = {
|
||||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
thirdColumnModeOptions: ['none', 'notifications', 'postform'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.third_column_mode_${mode}`)
|
||||
}))
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
UnitSetting,
|
||||
ProfileSettingIndicator
|
||||
},
|
||||
computed: {
|
||||
postFormats () {
|
||||
return this.$store.state.instance.postFormats || []
|
||||
},
|
||||
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]
|
||||
}
|
||||
},
|
||||
...SharedComputedObject(),
|
||||
}
|
||||
}
|
||||
|
||||
export default GeneralTab
|
||||
130
src/components/settings_modal/tabs/layout_tab.vue
Normal file
130
src/components/settings_modal/tabs/layout_tab.vue
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
<template>
|
||||
<div :label="$t('settings.layout')">
|
||||
<div class="setting-item">
|
||||
<h3>{{ $t('settings.general') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="modalMobileCenter">
|
||||
{{ $t('settings.mobile_center_dialog') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="alwaysShowNewPostButton"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.always_show_post_button') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="autohideFloatingPostButton"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.autohide_floating_post_button') }}
|
||||
</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="userPopoverOverlay"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.user_popover_avatar_overlay') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="userCardLeftJustify"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.user_card_left_justify') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<UnitSetting
|
||||
path="themeEditorMinWidth"
|
||||
:units="['px', 'rem']"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.theme_editor_min_width') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.columns') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<UnitSetting
|
||||
path="navbarSize"
|
||||
:step="0.1"
|
||||
:units="['px', 'rem']"
|
||||
:reset-default="{ 'px': 55, 'rem': 3.5 }"
|
||||
>
|
||||
{{ $t('settings.navbar_size') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
<li v-if="instanceSpecificPanelPresent">
|
||||
<BooleanSetting path="hideISP">
|
||||
{{ $t('settings.hide_isp') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./layout_tab.js"></script>
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.theme-tab {
|
||||
.old-theme-tab {
|
||||
min-width: var(--themeEditorMinWidth, fit-content);
|
||||
|
||||
.deprecation-warning {
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="theme-tab">
|
||||
<div class="old-theme-tab">
|
||||
<div class="alert warning deprecation-warning">
|
||||
{{ $t("settings.style.themes2_outdated") }}
|
||||
</div>
|
||||
|
|
@ -1020,6 +1020,6 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./theme_tab.js"></script>
|
||||
<script src="./old_theme_tab.js"></script>
|
||||
|
||||
<style src="./theme_tab.scss" lang="scss"></style>
|
||||
<style src="./old_theme_tab.scss" lang="scss"></style>
|
||||
76
src/components/settings_modal/tabs/posts_tab.js
Normal file
76
src/components/settings_modal/tabs/posts_tab.js
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
||||
import FontControl from 'src/components/font_control/font_control.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const GeneralTab = {
|
||||
props: {
|
||||
parentCollapsed: {
|
||||
required: true,
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
conversationDisplayOptions: ['tree', 'linear'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_display_${mode}`)
|
||||
})),
|
||||
conversationOtherRepliesButtonOptions: ['below', 'inside'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.conversation_other_replies_button_${mode}`)
|
||||
})),
|
||||
mentionLinkDisplayOptions: ['short', 'full_for_remote', 'full'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.mention_link_display_${mode}`)
|
||||
})),
|
||||
userPopoverAvatarActionOptions: ['close', 'zoom', 'open'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.user_popover_avatar_action_${mode}`)
|
||||
})),
|
||||
unsavedPostActionOptions: ['save', 'discard', 'confirm'].map(mode => ({
|
||||
key: mode,
|
||||
value: mode,
|
||||
label: this.$t(`settings.unsaved_post_action_${mode}`)
|
||||
})),
|
||||
loopSilentAvailable:
|
||||
// Firefox
|
||||
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
|
||||
// Chrome-likes
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'webkitAudioDecodedByteCount') ||
|
||||
// Future spec, still not supported in Nightly 63 as of 08/2018
|
||||
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks')
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
FontControl,
|
||||
ProfileSettingIndicator
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
},
|
||||
methods: {
|
||||
updateFont (key, value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'theme3hacks',
|
||||
value: {
|
||||
...this.mergedConfig.theme3hacks,
|
||||
fonts: {
|
||||
...this.mergedConfig.theme3hacks.fonts,
|
||||
[key]: value
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export default GeneralTab
|
||||
5
src/components/settings_modal/tabs/posts_tab.scss
Normal file
5
src/components/settings_modal/tabs/posts_tab.scss
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
.posts-tab {
|
||||
.greentext {
|
||||
color: var(--funtextGreentext);
|
||||
}
|
||||
}
|
||||
255
src/components/settings_modal/tabs/posts_tab.vue
Normal file
255
src/components/settings_modal/tabs/posts_tab.vue
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
<template>
|
||||
<div class="posts-tab">
|
||||
<div class="setting-item">
|
||||
<h3>{{ $t('settings.posts_appearance') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="collapseMessageWithSubject">
|
||||
{{ $t('settings.collapse_subject') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="conversationDisplay"
|
||||
path="conversationDisplay"
|
||||
:options="conversationDisplayOptions"
|
||||
>
|
||||
{{ $t('settings.conversation_display') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<FontControl
|
||||
:model-value="mergedConfig.theme3hacks.fonts.post"
|
||||
name="post"
|
||||
:fallback="{ family: 'inherit' }"
|
||||
:label="$t('settings.style.fonts.components.post')"
|
||||
@update:model-value="v => updateFont('post', v)"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<FontControl
|
||||
:model-value="mergedConfig.theme3hacks.fonts.monospace"
|
||||
name="postCode"
|
||||
:fallback="{ family: 'monospace' }"
|
||||
:label="$t('settings.style.fonts.components.monospace')"
|
||||
@update:model-value="v => updateFont('monospace', v)"
|
||||
/>
|
||||
</li>
|
||||
<ul
|
||||
v-if="mergedConfig.conversationDisplay !== 'linear'"
|
||||
class="setting-list suboptions"
|
||||
>
|
||||
<li>
|
||||
<BooleanSetting path="conversationTreeAdvanced">
|
||||
{{ $t('settings.tree_advanced') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="conversationTreeFadeAncestors"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.tree_fade_ancestors') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
path="maxDepthInThread"
|
||||
:min="3"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.max_depth_in_thread') }}
|
||||
</IntegerSetting>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="conversationOtherRepliesButton"
|
||||
path="conversationOtherRepliesButton"
|
||||
:options="conversationOtherRepliesButtonOptions"
|
||||
:expert="1"
|
||||
>
|
||||
{{ $t('settings.conversation_other_replies_button') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<BooleanSetting path="greentext">
|
||||
<i18n-t
|
||||
keypath="settings.plaintext_quotes"
|
||||
tag="span"
|
||||
>
|
||||
<span class="greentext">
|
||||
{{ $t('settings.greentext_quotes') }}
|
||||
</span>
|
||||
</i18n-t>
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="emojiReactionsOnTimeline"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.emoji_reactions_on_timeline') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.mention_links') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
id="mentionLinkDisplay"
|
||||
path="mentionLinkDisplay"
|
||||
:options="mentionLinkDisplayOptions"
|
||||
>
|
||||
{{ $t('settings.mention_link_display') }}
|
||||
</ChoiceSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="mentionLinkShowTooltip"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.mention_link_use_tooltip') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="mentionLinkShowAvatar">
|
||||
{{ $t('settings.mention_link_show_avatar') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
v-if="mergedConfig.mentionLinkDisplay !== 'short'"
|
||||
path="mentionLinkFadeDomain"
|
||||
>
|
||||
{{ $t('settings.mention_link_fade_domain') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li v-if="user">
|
||||
<BooleanSetting
|
||||
path="mentionLinkBoldenYou"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.mention_link_bolden_you') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
v-if="user"
|
||||
source="profile"
|
||||
path="stripRichContent"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.no_rich_text_description') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ul
|
||||
v-if="mergedConfig.useAbsoluteTimeFormat"
|
||||
class="setting-list suboptions"
|
||||
>
|
||||
<li>
|
||||
<UnitSetting
|
||||
path="absoluteTimeFormatMinAge"
|
||||
unit-set="time"
|
||||
:units="['s', 'm', 'h', 'd']"
|
||||
:min="0"
|
||||
>
|
||||
{{ $t('settings.absolute_time_format_min_age') }}
|
||||
</UnitSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="stopGifs">
|
||||
{{ $t('settings.stop_gifs') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="hideNsfw">
|
||||
{{ $t('settings.nsfw_clickthrough') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="preloadImage"
|
||||
expert="1"
|
||||
parent-path="hideNsfw"
|
||||
>
|
||||
{{ $t('settings.preload_images') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="useOneClickNsfw"
|
||||
expert="1"
|
||||
parent-path="hideNsfw"
|
||||
>
|
||||
{{ $t('settings.use_one_click_nsfw') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="loopVideo"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.loop_video') }}
|
||||
</BooleanSetting>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="loopVideoSilentOnly"
|
||||
expert="1"
|
||||
parent-path="loopVideo"
|
||||
:disabled="!loopSilentAvailable"
|
||||
>
|
||||
{{ $t('settings.loop_video_silent_only') }}
|
||||
</BooleanSetting>
|
||||
<div
|
||||
v-if="!loopSilentAvailable"
|
||||
class="unavailable"
|
||||
>
|
||||
<FAIcon icon="globe" />! {{ $t('settings.limited_availability') }}
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="playVideosInModal"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.play_videos_in_modal') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="useContainFit"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.use_contain_fit') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
<h3 v-if="expertLevel > 0">
|
||||
{{ $t('settings.fun') }}
|
||||
</h3>
|
||||
<ul class="setting-list">
|
||||
<li v-if="user">
|
||||
<BooleanSetting
|
||||
path="mentionLinkShowYous"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.show_yous') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./posts_tab.js"></script>
|
||||
<style src="./posts_tab.scss"></style>
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
<template>
|
||||
<div class="profile-tab">
|
||||
<div class="setting-item profile-edit">
|
||||
<h2>{{ $t('settings.account_profile_edit') }}</h2>
|
||||
<UserCard
|
||||
:user-id="user.id"
|
||||
:editable="true"
|
||||
|
|
@ -9,7 +8,7 @@
|
|||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.account_privacy') }}</h2>
|
||||
<h3>{{ $t('settings.account_privacy') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<Checkbox v-model="locked">
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import RoundnessInput from 'src/components/roundness_input/roundness_input.vue'
|
|||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
import Tooltip from 'src/components/tooltip/tooltip.vue'
|
||||
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
|
||||
import Preview from '../theme_tab/theme_preview.vue'
|
||||
import Preview from '../old_theme_tab/theme_preview.vue'
|
||||
|
||||
import VirtualDirectivesTab from './virtual_directives_tab.vue'
|
||||
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
<template>
|
||||
<div :label="$t('settings.version.title')">
|
||||
<div class="setting-item">
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<p>{{ $t('settings.version.backend_version') }}</p>
|
||||
<ul class="option-list">
|
||||
<li>
|
||||
<a
|
||||
:href="backendRepository"
|
||||
target="_blank"
|
||||
>{{ backendVersion }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<p>{{ $t('settings.version.frontend_version') }}</p>
|
||||
<ul class="option-list">
|
||||
<li>
|
||||
<a
|
||||
:href="frontendVersionLink"
|
||||
target="_blank"
|
||||
>{{ frontendVersion }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script src="./version_tab.js" />
|
||||
|
|
@ -452,7 +452,7 @@ const Status = {
|
|||
},
|
||||
scrobblePresent () {
|
||||
if (this.mergedConfig.hideScrobbles) return false
|
||||
if (!this.status.user.latestScrobble) return false
|
||||
if (!this.status.user?.latestScrobble) return false
|
||||
const value = this.mergedConfig.hideScrobblesAfter.match(/\d+/gs)[0]
|
||||
const unit = this.mergedConfig.hideScrobblesAfter.match(/\D+/gs)[0]
|
||||
let multiplier = 60 * 1000 // minutes is smallest unit
|
||||
|
|
@ -474,7 +474,7 @@ const Status = {
|
|||
return this.status.user.latestScrobble.artist
|
||||
},
|
||||
scrobble () {
|
||||
return this.status.user.latestScrobble
|
||||
return this.status.user?.latestScrobble
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
|||
|
|
@ -108,6 +108,10 @@
|
|||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
flex: 1 1 0;
|
||||
|
||||
&.unknown {
|
||||
min-width: 8em;
|
||||
}
|
||||
}
|
||||
|
||||
.heading-left {
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@
|
|||
class="left-side"
|
||||
>
|
||||
<a
|
||||
v-if="status.user?.name"
|
||||
:href="$router.resolve(userProfileLink).href"
|
||||
@click.prevent
|
||||
>
|
||||
|
|
@ -120,10 +121,17 @@
|
|||
class="post-avatar"
|
||||
:show-actor-type-indicator="showActorTypeIndicator"
|
||||
:compact="compact"
|
||||
:user="status.user"
|
||||
:user="status?.user"
|
||||
/>
|
||||
</UserPopover>
|
||||
</a>
|
||||
<UserAvatar
|
||||
v-else
|
||||
:user="status?.user"
|
||||
class="post-avatar"
|
||||
:compact="compact"
|
||||
:title="$t('status.unknown_user_info')"
|
||||
/>
|
||||
</div>
|
||||
<div class="right-side">
|
||||
<div
|
||||
|
|
@ -133,29 +141,30 @@
|
|||
<div class="heading-name-row">
|
||||
<div class="heading-left">
|
||||
<h4
|
||||
v-if="status.user.name_html"
|
||||
class="status-username"
|
||||
:title="status.user.name"
|
||||
:title="status.user?.name ?? $t('status.unknown_user_info')"
|
||||
>
|
||||
<RichContent
|
||||
:html="status.user.name"
|
||||
:emoji="status.user.emoji"
|
||||
:is-local="status.user.is_local"
|
||||
/>
|
||||
<user-link
|
||||
v-if="status.user?.name"
|
||||
class="account-name"
|
||||
:title="status.user?.screen_name_ui"
|
||||
:user="status?.user"
|
||||
:at="false"
|
||||
>
|
||||
<RichContent
|
||||
:html="status.user.name"
|
||||
:emoji="status.user.emoji"
|
||||
:is-local="status.user.is_local"
|
||||
/>
|
||||
<span>{{ status.user.name }}</span>
|
||||
</user-link>
|
||||
<span
|
||||
v-else
|
||||
class="account-name unknown"
|
||||
>
|
||||
{{ $t('status.unknown_user') }}
|
||||
</span>
|
||||
</h4>
|
||||
<h4
|
||||
v-else
|
||||
class="status-username"
|
||||
:title="status.user.name"
|
||||
>
|
||||
{{ status.user.name }}
|
||||
</h4>
|
||||
<user-link
|
||||
class="account-name"
|
||||
:title="status.user.screen_name_ui"
|
||||
:user="status.user"
|
||||
:at="false"
|
||||
/>
|
||||
<img
|
||||
v-if="!!(status.user && status.user.favicon)"
|
||||
class="status-favicon"
|
||||
|
|
|
|||
|
|
@ -31,11 +31,6 @@ export default {
|
|||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
sideTabBar: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
bodyScrollLock: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
|
|
@ -157,29 +152,28 @@ export default {
|
|||
|
||||
return (
|
||||
<div class={classes}>
|
||||
{
|
||||
this.sideTabBar
|
||||
? <h1 class="mobile-label">{props.label}</h1>
|
||||
: ''
|
||||
}
|
||||
{renderSlot}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}>
|
||||
<div
|
||||
class="tab-switcher top-tabs"
|
||||
ref="root"
|
||||
>
|
||||
<div
|
||||
class="tabs"
|
||||
role="tablist"
|
||||
ref="nav"
|
||||
>
|
||||
{tabs}
|
||||
</div>
|
||||
<div
|
||||
ref="contents"
|
||||
role="tabpanel"
|
||||
class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}
|
||||
v-body-scroll-lock={this.bodyScrollLock}
|
||||
ref="content"
|
||||
>
|
||||
{contents}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -52,104 +52,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.side-tabs {
|
||||
flex-direction: row;
|
||||
|
||||
@media all and (width <= 800px) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
> .contents {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
> .tabs {
|
||||
flex: 0 0 auto;
|
||||
overflow: hidden auto;
|
||||
flex-direction: column;
|
||||
|
||||
&::after,
|
||||
&::before {
|
||||
flex-shrink: 0;
|
||||
flex-basis: 0.5em;
|
||||
content: "";
|
||||
border-right: 1px solid;
|
||||
border-right-color: var(--border);
|
||||
}
|
||||
|
||||
&::after {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
&::before {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.tab-wrapper {
|
||||
min-width: 10em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@media all and (width <= 800px) {
|
||||
min-width: 4em;
|
||||
}
|
||||
|
||||
&:not(.active)::after {
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-right: 1px solid;
|
||||
border-right-color: var(--border);
|
||||
}
|
||||
|
||||
&::before {
|
||||
flex: 0 0 6px;
|
||||
content: "";
|
||||
border-right: 1px solid;
|
||||
border-right-color: var(--border);
|
||||
}
|
||||
|
||||
&:last-child .tab {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
flex: 1;
|
||||
box-sizing: content-box;
|
||||
max-width: 9em;
|
||||
min-width: 1px;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
padding-left: 1em;
|
||||
padding-right: calc(1em + 200px);
|
||||
margin-right: -200px;
|
||||
margin-left: 1em;
|
||||
|
||||
&:not(.active) {
|
||||
margin-top: 0;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
@media all and (width <= 800px) {
|
||||
padding-left: 0.25em;
|
||||
padding-right: calc(0.25em + 200px);
|
||||
margin-right: calc(0.25em - 200px);
|
||||
margin-left: 0.25em;
|
||||
|
||||
&:not(.active) {
|
||||
margin-top: 0;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.text {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contents {
|
||||
flex: 1 0 auto;
|
||||
min-height: 0;
|
||||
|
|
|
|||
207
src/components/tab_switcher/vertical_tab_switcher.jsx
Normal file
207
src/components/tab_switcher/vertical_tab_switcher.jsx
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
// eslint-disable-next-line no-unused
|
||||
import { h, Fragment } from 'vue'
|
||||
import { mapState } from 'pinia'
|
||||
import { throttle } from 'lodash'
|
||||
import { mapState as mapPiniaState } from 'pinia'
|
||||
import { FontAwesomeIcon as FAIcon } from '@fortawesome/vue-fontawesome'
|
||||
|
||||
import './vertical_tab_switcher.scss'
|
||||
import { useInterfaceStore } from 'src/stores/interface'
|
||||
|
||||
const findFirstUsable = (slots) => slots.findIndex(_ => _.props)
|
||||
|
||||
export default {
|
||||
name: 'VerticalTabSwitcher',
|
||||
props: {
|
||||
renderOnlyFocused: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
onSwitch: {
|
||||
required: false,
|
||||
type: Function,
|
||||
default: undefined
|
||||
},
|
||||
activeTab: {
|
||||
required: false,
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
bodyScrollLock: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
parentCollapsed: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
active: findFirstUsable(this.slots()),
|
||||
resizeHandler: null,
|
||||
navSide: 'tabs'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeIndex () {
|
||||
// In case of controlled component
|
||||
if (this.activeTab) {
|
||||
return this.slots().findIndex(slot => slot && slot.props && this.activeTab === slot.props.key)
|
||||
} else {
|
||||
return this.active
|
||||
}
|
||||
},
|
||||
isActive () {
|
||||
return tabName => {
|
||||
const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
|
||||
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
||||
}
|
||||
},
|
||||
...mapPiniaState(useInterfaceStore, {
|
||||
mobileLayout: store => store.layoutType === 'mobile'
|
||||
}),
|
||||
},
|
||||
beforeUpdate () {
|
||||
const currentSlot = this.slots()[this.active]
|
||||
if (!currentSlot.props) {
|
||||
this.active = findFirstUsable(this.slots())
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickTab (index) {
|
||||
return (e) => {
|
||||
e.preventDefault()
|
||||
this.setTab(index)
|
||||
}
|
||||
},
|
||||
setTab (index) {
|
||||
if (typeof this.onSwitch === 'function') {
|
||||
this.onSwitch.call(null, this.slots()[index].key)
|
||||
}
|
||||
this.active = index
|
||||
this.changeNavSide('content')
|
||||
},
|
||||
changeNavSide (side) {
|
||||
if (this.navSide !== side) {
|
||||
this.navSide = side
|
||||
}
|
||||
},
|
||||
// DO NOT put it to computed, it doesn't work (caching?)
|
||||
slots () {
|
||||
if (this.$slots.default()[0].type === Fragment) {
|
||||
return this.$slots.default()[0].children
|
||||
}
|
||||
return this.$slots.default()
|
||||
}
|
||||
},
|
||||
render () {
|
||||
const tabs = this.slots()
|
||||
.map((slot, index) => {
|
||||
const props = slot.props
|
||||
if (!props) return
|
||||
const classesTab = ['vertical-tab', 'menu-item']
|
||||
if (this.activeIndex === index && useInterfaceStore().layoutType !== 'mobile') {
|
||||
classesTab.push('-active')
|
||||
}
|
||||
return (
|
||||
<button
|
||||
disabled={props.disabled}
|
||||
onClick={this.clickTab(index)}
|
||||
class={classesTab.join(' ')}
|
||||
type="button"
|
||||
role="tab"
|
||||
title={props.label}
|
||||
>
|
||||
{!props.icon ? '' : (<FAIcon class="tab-icon" size="1x" fixed-width icon={props.icon}/>)}
|
||||
<span class="text">
|
||||
{props.label}
|
||||
</span>
|
||||
</button>
|
||||
)
|
||||
})
|
||||
|
||||
const contents = this.slots().map((slot, index) => {
|
||||
const props = slot.props
|
||||
if (!props) return
|
||||
const active = this.activeIndex === index
|
||||
const classes = ['tab-content-wrapper', active ? '-active' : '-hidden' ]
|
||||
if (props.fullHeight) {
|
||||
classes.push('-full-height')
|
||||
}
|
||||
let delayRender = slot.props['delay-render']
|
||||
if (delayRender && active) {
|
||||
slot.props['delay-render'] = false
|
||||
delayRender = false
|
||||
}
|
||||
const renderSlot = (!delayRender && (!this.renderOnlyFocused || active))
|
||||
? slot
|
||||
: ''
|
||||
|
||||
const headerClasses = ['tab-content-label']
|
||||
const header = (
|
||||
<h2 class={headerClasses}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => this.changeNavSide('tabs')}
|
||||
class="button-unstyled"
|
||||
>
|
||||
<FAIcon
|
||||
size="lg"
|
||||
class="back-button-icon"
|
||||
icon="chevron-left"
|
||||
/>
|
||||
</button>
|
||||
{props.label}
|
||||
</h2>
|
||||
)
|
||||
|
||||
return (
|
||||
<div class={classes} >
|
||||
<div class="tab-mobile-header">
|
||||
{header}
|
||||
</div>
|
||||
<div class="tab-slot-wrapper">
|
||||
<div class={ ['tab-content', props['full-width'] ? '-full-width' : null].join(' ') } >
|
||||
{renderSlot}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
const rootClasses = ['vertical-tab-switcher']
|
||||
if (useInterfaceStore().layoutType === 'mobile') {
|
||||
rootClasses.push('-mobile')
|
||||
}
|
||||
|
||||
if (this.navSide === 'tabs') {
|
||||
rootClasses.push('-nav-tabs')
|
||||
} else {
|
||||
rootClasses.push('-nav-contents')
|
||||
}
|
||||
|
||||
return (
|
||||
<div ref="root" class={ rootClasses.join(' ') }>
|
||||
<div
|
||||
class="tabs"
|
||||
role="tablist"
|
||||
ref="nav"
|
||||
>
|
||||
{tabs}
|
||||
</div>
|
||||
<div
|
||||
role="tabpanel"
|
||||
class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}
|
||||
v-body-scroll-lock={this.bodyScrollLock}
|
||||
ref="contents"
|
||||
>
|
||||
{contents}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
176
src/components/tab_switcher/vertical_tab_switcher.scss
Normal file
176
src/components/tab_switcher/vertical_tab_switcher.scss
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
.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 {
|
||||
flex: 1 0 35em;
|
||||
|
||||
.tab-content {
|
||||
align-self: center;
|
||||
height: 100%;
|
||||
|
||||
&:not(.-full-width) {
|
||||
max-width: 40em;
|
||||
}
|
||||
|
||||
&.-full-width {
|
||||
align-self: stretch;
|
||||
}
|
||||
}
|
||||
|
||||
.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: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tab-content-wrapper {
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
|
||||
&.-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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -132,6 +132,7 @@
|
|||
},
|
||||
"importer": {
|
||||
"submit": "Submit",
|
||||
"import": "Import",
|
||||
"success": "Imported successfully.",
|
||||
"error": "An error occured while importing this file."
|
||||
},
|
||||
|
|
@ -403,6 +404,9 @@
|
|||
"setting_server_side": "This setting is tied to your profile and affects all sessions and clients",
|
||||
"enter_current_password_to_confirm": "Enter your current password to confirm your identity",
|
||||
"post_look_feel": "Posts Look & Feel",
|
||||
"posts": "Posts",
|
||||
"developer": "Developer",
|
||||
"debug": "Debug",
|
||||
"mention_links": "Mention links",
|
||||
"appearance": "Appearance",
|
||||
"confirm_new_setting": "Confirm new setting?",
|
||||
|
|
@ -416,9 +420,14 @@
|
|||
"navbar_size": "Top bar size",
|
||||
"panel_header_size": "Panel header size",
|
||||
"visual_tweaks": "Minor visual tweaks",
|
||||
"theme_debug": "Show what background theme engine assumes when dealing with transparancy (DEBUG)",
|
||||
"theme_debug": "Show what background theme engine assumes when dealing with transparancy",
|
||||
"scale_and_layout": "Interface scale and layout",
|
||||
"timelines": "Timelines",
|
||||
"format_and_language": "Format and Language",
|
||||
"confirmations": "Confirmations",
|
||||
"layout": "Layout",
|
||||
"enabled": "Enabled",
|
||||
"clutter": "Clutter",
|
||||
"filter": {
|
||||
"clutter": "Remove clutter",
|
||||
"mute_filter": "Mute Filters",
|
||||
|
|
@ -533,6 +542,7 @@
|
|||
"chatMessageRadius": "Chat message",
|
||||
"collapse_subject": "Collapse posts with subjects",
|
||||
"composing": "Composing",
|
||||
"replies": "Replying",
|
||||
"confirm_new_password": "Confirm new password",
|
||||
"current_password": "Current password",
|
||||
"confirm_dialogs": "Ask for confirmation when",
|
||||
|
|
@ -594,6 +604,12 @@
|
|||
"follow_export_button": "Export your follows to a csv file",
|
||||
"follow_import": "Follow import",
|
||||
"follow_import_error": "Error importing followers",
|
||||
"import_export": {
|
||||
"title": "Import / Export",
|
||||
"follows": "List of users you follow",
|
||||
"blocks": "List of users you block",
|
||||
"mutes": "List of users you mute"
|
||||
},
|
||||
"follows_imported": "Follows imported! Processing them will take a while.",
|
||||
"accent": "Accent",
|
||||
"foreground": "Foreground",
|
||||
|
|
@ -745,7 +761,7 @@
|
|||
"subject_line_email": "Like email: \"re: subject\"",
|
||||
"subject_line_mastodon": "Like mastodon: copy as is",
|
||||
"subject_line_noop": "Do not copy",
|
||||
"force_theme_recompilation_debug": "Disable theme cahe, force recompile on each boot (DEBUG)",
|
||||
"force_theme_recompilation_debug": "Disable theme cahe, force recompile on each boot",
|
||||
"conversation_display": "Conversation display style",
|
||||
"conversation_display_tree": "Tree-style",
|
||||
"conversation_display_tree_quick": "Tree view",
|
||||
|
|
@ -760,6 +776,8 @@
|
|||
"column_sizes_sidebar": "Sidebar",
|
||||
"column_sizes_content": "Content",
|
||||
"column_sizes_notifs": "Notifications",
|
||||
"layout": "Layout",
|
||||
"scale_and_font": "Scale and Font",
|
||||
"theme_editor_min_width": "Minimum width of theme editor (0 for \"fit-content\")",
|
||||
"tree_advanced": "Allow more flexible navigation in tree view",
|
||||
"tree_fade_ancestors": "Display ancestors of the current status in faint text",
|
||||
|
|
@ -770,6 +788,7 @@
|
|||
"conversation_other_replies_button_inside": "Inside statuses",
|
||||
"max_depth_in_thread": "Maximum number of levels in thread to display by default",
|
||||
"post_status_content_type": "Post status content type",
|
||||
"default_post_status_content_type": "Default post status content type",
|
||||
"sensitive_by_default": "Mark posts as sensitive by default",
|
||||
"stop_gifs": "Pause animated images until you hover on them",
|
||||
"streaming": "Automatically show new posts when scrolled to the top",
|
||||
|
|
@ -811,8 +830,11 @@
|
|||
"user_popover_avatar_overlay": "Show user popover over user avatar",
|
||||
"user_card_left_justify": "Justify user bio to the left",
|
||||
"user_card_hide_personal_marks": "Hide personal marks (highlight/note) in user profiles",
|
||||
"posts_appearance": "Posts appearance",
|
||||
"fun": "Fun",
|
||||
"greentext": "Meme arrows",
|
||||
"plaintext_quotes": "Highlight plaintext {0}",
|
||||
"greentext_quotes": ">quotes",
|
||||
"show_yous": "Show (You)s",
|
||||
"notifications": "Notifications",
|
||||
"notification_setting_annoyance": "Annoyance",
|
||||
|
|
@ -832,11 +854,13 @@
|
|||
"enable_web_push_always_show_tip": "Some browsers (Chromium, Chrome) require that push messages always result in a notification, otherwise generic 'Website was updated in background' is shown, enable this to prevent this notification from showing, as Chrome seem to hide push notifications if tab is in focus. Can result in showing duplicate notifications on other browsers.",
|
||||
"more_settings": "More settings",
|
||||
"style": {
|
||||
"style_section": "Style",
|
||||
"custom_theme_used": "(Custom theme)",
|
||||
"custom_style_used": "(Custom style)",
|
||||
"stock_theme_used": "(Stock theme)",
|
||||
"themes2_outdated": "Editor for Themes V2 is being phased out and will eventually be replaced with a new one that takes advantage of new Themes V3 engine. It should still work but experience might be degraded and inconsistent.",
|
||||
"appearance_tab_note": "Changes on this tab do not affect the theme used, so exported theme will be different from what seen in the UI",
|
||||
"visual_tweaks_section_note": "Changes in this section do not affect the theme used, exported theme will be different from what seen in the UI",
|
||||
"update_preview": "Update preview",
|
||||
"themes3": {
|
||||
"define": "Override",
|
||||
|
|
@ -1075,6 +1099,13 @@
|
|||
"post": "Post text",
|
||||
"monospace": "Monospaced text"
|
||||
},
|
||||
"components_inline": {
|
||||
"interface": "interface",
|
||||
"input": "input fields",
|
||||
"post": "post text",
|
||||
"monospace": "monospaced text"
|
||||
},
|
||||
"override": "Override {0} font",
|
||||
"family": "Font name",
|
||||
"size": "Size (in px)",
|
||||
"weight": "Weight (boldness)",
|
||||
|
|
@ -1346,6 +1377,8 @@
|
|||
"show_content": "Show content",
|
||||
"hide_content": "Hide content",
|
||||
"status_deleted": "This post was deleted",
|
||||
"unknown_user": "unknown user",
|
||||
"unknown_user_info": "Unable to fetch information about this user",
|
||||
"nsfw": "NSFW",
|
||||
"expand": "Expand",
|
||||
"you": "(You)",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import localeService from '../services/locale/locale.service.js'
|
|||
import { useI18nStore } from 'src/stores/i18n.js'
|
||||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
|
||||
import { defaultState } from './default_config_state.js'
|
||||
import { instanceDefaultConfig, defaultState } from './default_config_state.js'
|
||||
|
||||
const BACKEND_LANGUAGE_COOKIE_NAME = 'userLanguage'
|
||||
const APPEARANCE_SETTINGS_KEYS = new Set([
|
||||
|
|
@ -38,9 +38,7 @@ export const multiChoiceProperties = [
|
|||
]
|
||||
|
||||
// caching the instance default properties
|
||||
export const instanceDefaultProperties = Object.entries(defaultState)
|
||||
.filter(([, value]) => value === undefined)
|
||||
.map(([key]) => key)
|
||||
export const instanceDefaultProperties = Object.keys(instanceDefaultConfig)
|
||||
|
||||
const config = {
|
||||
state: { ...defaultState },
|
||||
|
|
|
|||
|
|
@ -1,45 +1,43 @@
|
|||
const browserLocale = (window.navigator.language || 'en').split('-')[0]
|
||||
|
||||
export const defaultState = {
|
||||
expertLevel: 0, // used to track which settings to show and hide
|
||||
|
||||
// Theme stuff
|
||||
theme: undefined, // Very old theme store, stores preset name, still in use
|
||||
|
||||
// V1
|
||||
colors: {}, // VERY old theme store, just colors of V1, probably not even used anymore
|
||||
|
||||
// V2
|
||||
customTheme: undefined, // "snapshot", previously was used as actual theme store for V2 so it's still used in case of PleromaFE downgrade event.
|
||||
customThemeSource: undefined, // "source", stores original theme data
|
||||
|
||||
// V3
|
||||
style: null,
|
||||
styleCustomData: null,
|
||||
/// Instance config entries provided by static config or pleroma api
|
||||
/// Put settings here only if it does not make sense for a normal user
|
||||
/// to override it.
|
||||
export const staticOrApiConfigDefault = {
|
||||
theme: 'pleroma-dark',
|
||||
palette: null,
|
||||
paletteCustomData: null,
|
||||
themeDebug: false, // debug mode that uses computed backgrounds instead of real ones to debug contrast functions
|
||||
forceThemeRecompilation: false, // flag that forces recompilation on boot even if cache exists
|
||||
theme3hacks: { // Hacks, user overrides that are independent of theme used
|
||||
underlay: 'none',
|
||||
fonts: {
|
||||
interface: undefined,
|
||||
input: undefined,
|
||||
post: undefined,
|
||||
monospace: undefined
|
||||
}
|
||||
},
|
||||
style: null,
|
||||
defaultAvatar: '/images/avi.png',
|
||||
defaultBanner: '/images/banner.png',
|
||||
background: '/static/aurora_borealis.jpg',
|
||||
embeddedToS: true,
|
||||
logo: '/static/logo.svg',
|
||||
logoMargin: '.2em',
|
||||
logoMask: true,
|
||||
logoLeft: false,
|
||||
redirectRootLogin: '/main/friends',
|
||||
redirectRootNoLogin: '/main/all',
|
||||
hideSitename: false,
|
||||
nsfwCensorImage: undefined,
|
||||
showFeaturesPanel: true,
|
||||
showInstanceSpecificPanel: false,
|
||||
}
|
||||
|
||||
/// This object contains setting entries that makes sense
|
||||
/// at the user level. The defaults can also be overriden by
|
||||
/// instance admins in the frontend_configuration endpoint or static config.
|
||||
export const instanceDefaultConfig = {
|
||||
expertLevel: 0, // used to track which settings to show and hide
|
||||
hideISP: false,
|
||||
hideInstanceWallpaper: false,
|
||||
hideShoutbox: false,
|
||||
// bad name: actually hides posts of muted USERS
|
||||
hideMutedPosts: undefined, // instance default
|
||||
hideMutedThreads: undefined, // instance default
|
||||
hideWordFilteredPosts: undefined, // instance default
|
||||
muteBotStatuses: undefined, // instance default
|
||||
muteSensitiveStatuses: undefined, // instance default
|
||||
collapseMessageWithSubject: undefined, // instance default
|
||||
hideMutedPosts: false,
|
||||
hideMutedThreads: true,
|
||||
hideWordFilteredPosts: false,
|
||||
muteBotStatuses: false,
|
||||
muteSensitiveStatuses: false,
|
||||
collapseMessageWithSubject: false,
|
||||
padEmoji: true,
|
||||
hideAttachments: false,
|
||||
hideAttachmentsInConv: false,
|
||||
|
|
@ -50,6 +48,9 @@ export const defaultState = {
|
|||
preloadImage: true,
|
||||
loopVideo: true,
|
||||
loopVideoSilentOnly: true,
|
||||
/// This is not the streaming API configuration, but rather an option
|
||||
/// for automatically loading new posts into the timeline without
|
||||
/// the user clicking the Show New button.
|
||||
streaming: false,
|
||||
emojiReactionsOnTimeline: true,
|
||||
alwaysShowNewPostButton: false,
|
||||
|
|
@ -86,39 +87,37 @@ export const defaultState = {
|
|||
},
|
||||
webPushNotifications: false,
|
||||
webPushAlwaysShowNotifications: false,
|
||||
muteWords: [],
|
||||
highlight: {},
|
||||
interfaceLanguage: browserLocale,
|
||||
hideScopeNotice: false,
|
||||
useStreamingApi: false,
|
||||
sidebarRight: undefined, // instance default
|
||||
scopeCopy: undefined, // instance default
|
||||
subjectLineBehavior: undefined, // instance default
|
||||
alwaysShowSubjectInput: undefined, // instance default
|
||||
postContentType: undefined, // instance default
|
||||
minimalScopesMode: undefined, // instance default
|
||||
sidebarRight: false,
|
||||
scopeCopy: true,
|
||||
subjectLineBehavior: 'email',
|
||||
alwaysShowSubjectInput: true,
|
||||
postContentType: 'text/plain',
|
||||
minimalScopesMode: false,
|
||||
|
||||
// This hides statuses filtered via a word filter
|
||||
hideFilteredStatuses: undefined, // instance default
|
||||
hideFilteredStatuses: false,
|
||||
|
||||
// Confirmations
|
||||
modalOnRepeat: undefined, // instance default
|
||||
modalOnUnfollow: undefined, // instance default
|
||||
modalOnBlock: undefined, // instance default
|
||||
modalOnMute: undefined, // instance default
|
||||
modalOnMuteConversation: undefined, // instance default
|
||||
modalOnMuteDomain: undefined, // instance default
|
||||
modalOnDelete: undefined, // instance default
|
||||
modalOnLogout: undefined, // instance default
|
||||
modalOnApproveFollow: undefined, // instance default
|
||||
modalOnDenyFollow: undefined, // instance default
|
||||
modalOnRemoveUserFromFollowers: undefined, // instance default
|
||||
modalOnRepeat: false,
|
||||
modalOnUnfollow: false,
|
||||
modalOnBlock: true,
|
||||
modalOnMute: false,
|
||||
modalOnMuteConversation: false,
|
||||
modalOnMuteDomain: true,
|
||||
modalOnDelete: true,
|
||||
modalOnLogout: true,
|
||||
modalOnApproveFollow: false,
|
||||
modalOnDenyFollow: false,
|
||||
modalOnRemoveUserFromFollowers: false,
|
||||
|
||||
// Expiry confirmations/default actions
|
||||
onMuteDefaultAction: 'ask',
|
||||
onBlockDefaultAction: 'ask',
|
||||
|
||||
modalMobileCenter: undefined,
|
||||
modalMobileCenter: false,
|
||||
playVideosInModal: false,
|
||||
useOneClickNsfw: false,
|
||||
useContainFit: true,
|
||||
|
|
@ -131,45 +130,93 @@ export const defaultState = {
|
|||
sidebarColumnWidth: '25rem',
|
||||
contentColumnWidth: '45rem',
|
||||
notifsColumnWidth: '25rem',
|
||||
themeEditorMinWidth: undefined, // instance default
|
||||
emojiReactionsScale: undefined,
|
||||
textSize: undefined, // instance default
|
||||
emojiSize: undefined, // instance default
|
||||
navbarSize: undefined, // instance default
|
||||
panelHeaderSize: undefined, // instance default
|
||||
forcedRoundness: undefined, // instance default
|
||||
themeEditorMinWidth: '0rem',
|
||||
emojiReactionsScale: 0.5,
|
||||
textSize: '1rem',
|
||||
emojiSize: '2.2rem',
|
||||
navbarSize: '3.5rem',
|
||||
panelHeaderSize: '3.2rem',
|
||||
forcedRoundness: -1,
|
||||
navbarColumnStretch: false,
|
||||
greentext: undefined, // instance default
|
||||
mentionLinkDisplay: undefined, // instance default
|
||||
mentionLinkShowTooltip: undefined, // instance default
|
||||
mentionLinkShowAvatar: undefined, // instance default
|
||||
mentionLinkFadeDomain: undefined, // instance default
|
||||
mentionLinkShowYous: undefined, // instance default
|
||||
mentionLinkBoldenYou: undefined, // instance default
|
||||
hidePostStats: undefined, // instance default
|
||||
hideBotIndication: undefined, // instance default
|
||||
hideUserStats: undefined, // instance default
|
||||
virtualScrolling: undefined, // instance default
|
||||
sensitiveByDefault: undefined, // instance default
|
||||
conversationDisplay: undefined, // instance default
|
||||
conversationTreeAdvanced: undefined, // instance default
|
||||
conversationOtherRepliesButton: undefined, // instance default
|
||||
conversationTreeFadeAncestors: undefined, // instance default
|
||||
showExtraNotifications: undefined, // instance default
|
||||
showExtraNotificationsTip: undefined, // instance default
|
||||
showChatsInExtraNotifications: undefined, // instance default
|
||||
showAnnouncementsInExtraNotifications: undefined, // instance default
|
||||
showFollowRequestsInExtraNotifications: undefined, // instance default
|
||||
maxDepthInThread: undefined, // instance default
|
||||
autocompleteSelect: undefined, // instance default
|
||||
closingDrawerMarksAsSeen: undefined, // instance default
|
||||
unseenAtTop: undefined, // instance default
|
||||
ignoreInactionableSeen: undefined, // instance default
|
||||
unsavedPostAction: undefined, // instance default
|
||||
autoSaveDraft: undefined, // instance default
|
||||
useAbsoluteTimeFormat: undefined, // instance default
|
||||
absoluteTimeFormatMinAge: undefined, // instance default
|
||||
absoluteTime12h: undefined, // instance default
|
||||
greentext: false,
|
||||
mentionLinkDisplay: 'short',
|
||||
mentionLinkShowTooltip: true,
|
||||
mentionLinkShowAvatar: false,
|
||||
mentionLinkFadeDomain: true,
|
||||
mentionLinkShowYous: false,
|
||||
mentionLinkBoldenYou: true,
|
||||
hidePostStats: false,
|
||||
hideBotIndication: false,
|
||||
hideUserStats: false,
|
||||
virtualScrolling: true,
|
||||
sensitiveByDefault: false,
|
||||
conversationDisplay: 'linear',
|
||||
conversationTreeAdvanced: false,
|
||||
conversationOtherRepliesButton: 'below',
|
||||
conversationTreeFadeAncestors: false,
|
||||
showExtraNotifications: true,
|
||||
showExtraNotificationsTip: true,
|
||||
showChatsInExtraNotifications: true,
|
||||
showAnnouncementsInExtraNotifications: true,
|
||||
showFollowRequestsInExtraNotifications: true,
|
||||
maxDepthInThread: 6,
|
||||
autocompleteSelect: false,
|
||||
closingDrawerMarksAsSeen: true,
|
||||
unseenAtTop: false,
|
||||
ignoreInactionableSeen: false,
|
||||
unsavedPostAction: 'confirm',
|
||||
autoSaveDraft: false,
|
||||
useAbsoluteTimeFormat: false,
|
||||
absoluteTimeFormatMinAge: '0d',
|
||||
absoluteTime12h: '24h',
|
||||
imageCompression: true,
|
||||
alwaysUseJpeg: false
|
||||
}
|
||||
|
||||
export const makeUndefined = c => Object.fromEntries(Object.keys(c).map(key => [key, undefined]))
|
||||
|
||||
/// For properties with special processing or properties that does not
|
||||
/// make sense to be overriden on a instance-wide level.
|
||||
export const defaultState = {
|
||||
// Set these to undefined so it does not interfere with default settings check
|
||||
...makeUndefined(instanceDefaultConfig),
|
||||
|
||||
// Special processing
|
||||
// Theme stuff
|
||||
theme: undefined, // Very old theme store, stores preset name, still in use
|
||||
|
||||
// V1
|
||||
colors: {}, // VERY old theme store, just colors of V1, probably not even used anymore
|
||||
|
||||
// V2
|
||||
customTheme: undefined, // "snapshot", previously was used as actual theme store for V2 so it's still used in case of PleromaFE downgrade event.
|
||||
customThemeSource: undefined, // "source", stores original theme data
|
||||
|
||||
// V3
|
||||
style: null,
|
||||
styleCustomData: null,
|
||||
palette: null,
|
||||
paletteCustomData: null,
|
||||
themeDebug: false, // debug mode that uses computed backgrounds instead of real ones to debug contrast functions
|
||||
forceThemeRecompilation: false, // flag that forces recompilation on boot even if cache exists
|
||||
theme3hacks: { // Hacks, user overrides that are independent of theme used
|
||||
underlay: 'none',
|
||||
fonts: {
|
||||
interface: undefined,
|
||||
input: undefined,
|
||||
post: undefined,
|
||||
monospace: undefined
|
||||
}
|
||||
},
|
||||
|
||||
// Special handling: These fields are not of a primitive type, and
|
||||
// might cause problems with current code because it specifically checks
|
||||
// them in state.config (not getters.mergedConfig).
|
||||
|
||||
// Specifically, muteWords is now deprecated in favour of a server-side configuration.
|
||||
muteWords: [],
|
||||
highlight: {},
|
||||
|
||||
// If there are any configurations that does not make sense to
|
||||
// have instance-wide default, put it here and explain why.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { ensureFinalFallback } from '../i18n/languages.js'
|
|||
import { useInterfaceStore } from 'src/stores/interface.js'
|
||||
// See build/emojis_plugin for more details
|
||||
import { annotationsLoader } from 'virtual:pleroma-fe/emoji-annotations'
|
||||
import { staticOrApiConfigDefault, instanceDefaultConfig } from './default_config_state.js';
|
||||
|
||||
const SORTED_EMOJI_GROUP_IDS = [
|
||||
'smileys-and-emotion',
|
||||
|
|
@ -52,90 +53,15 @@ const defaultState = {
|
|||
vapidPublicKey: undefined,
|
||||
|
||||
// Stuff from static/config.json
|
||||
alwaysShowSubjectInput: true,
|
||||
defaultAvatar: '/images/avi.png',
|
||||
defaultBanner: '/images/banner.png',
|
||||
background: '/static/aurora_borealis.jpg',
|
||||
embeddedToS: true,
|
||||
collapseMessageWithSubject: false,
|
||||
greentext: false,
|
||||
mentionLinkDisplay: 'short',
|
||||
mentionLinkShowTooltip: true,
|
||||
mentionLinkShowAvatar: false,
|
||||
mentionLinkFadeDomain: true,
|
||||
mentionLinkShowYous: false,
|
||||
mentionLinkBoldenYou: true,
|
||||
hideFilteredStatuses: false,
|
||||
// bad name: actually hides posts of muted USERS
|
||||
hideMutedPosts: false,
|
||||
hideMutedThreads: true,
|
||||
hideWordFilteredPosts: false,
|
||||
hidePostStats: false,
|
||||
hideBotIndication: false,
|
||||
hideSitename: false,
|
||||
hideUserStats: false,
|
||||
muteBotStatuses: false,
|
||||
muteSensitiveStatuses: false,
|
||||
modalOnRepeat: false,
|
||||
modalOnUnfollow: false,
|
||||
modalOnBlock: true,
|
||||
modalOnMute: false,
|
||||
modalOnMuteConversation: false,
|
||||
modalOnMuteDomain: true,
|
||||
modalOnDelete: true,
|
||||
modalOnLogout: true,
|
||||
modalOnApproveFollow: false,
|
||||
modalOnDenyFollow: false,
|
||||
modalOnRemoveUserFromFollowers: false,
|
||||
modalMobileCenter: false,
|
||||
loginMethod: 'password',
|
||||
logo: '/static/logo.svg',
|
||||
logoMargin: '.2em',
|
||||
logoMask: true,
|
||||
logoLeft: false,
|
||||
disableUpdateNotification: false,
|
||||
minimalScopesMode: false,
|
||||
nsfwCensorImage: undefined,
|
||||
postContentType: 'text/plain',
|
||||
redirectRootLogin: '/main/friends',
|
||||
redirectRootNoLogin: '/main/all',
|
||||
scopeCopy: true,
|
||||
showFeaturesPanel: true,
|
||||
showInstanceSpecificPanel: false,
|
||||
sidebarRight: false,
|
||||
subjectLineBehavior: 'email',
|
||||
theme: 'pleroma-dark',
|
||||
palette: null,
|
||||
style: null,
|
||||
emojiReactionsScale: 0.5,
|
||||
textSize: '1rem',
|
||||
emojiSize: '2.2rem',
|
||||
navbarSize: '3.5rem',
|
||||
panelHeaderSize: '3.2rem',
|
||||
themeEditorMinWidth: '0rem',
|
||||
forcedRoundness: -1,
|
||||
|
||||
fontsOverride: {},
|
||||
virtualScrolling: true,
|
||||
sensitiveByDefault: false,
|
||||
conversationDisplay: 'linear',
|
||||
conversationTreeAdvanced: false,
|
||||
conversationOtherRepliesButton: 'below',
|
||||
conversationTreeFadeAncestors: false,
|
||||
showExtraNotifications: true,
|
||||
showExtraNotificationsTip: true,
|
||||
showChatsInExtraNotifications: true,
|
||||
showAnnouncementsInExtraNotifications: true,
|
||||
showFollowRequestsInExtraNotifications: true,
|
||||
maxDepthInThread: 6,
|
||||
autocompleteSelect: false,
|
||||
closingDrawerMarksAsSeen: true,
|
||||
unseenAtTop: false,
|
||||
ignoreInactionableSeen: false,
|
||||
unsavedPostAction: 'confirm',
|
||||
autoSaveDraft: false,
|
||||
useAbsoluteTimeFormat: false,
|
||||
absoluteTimeFormatMinAge: '0d',
|
||||
absoluteTime12h: '24h',
|
||||
|
||||
// Instance-wide configurations that should not be changed by individual users
|
||||
...staticOrApiConfigDefault,
|
||||
// Instance admins can override default settings for the whole instance
|
||||
...instanceDefaultConfig,
|
||||
|
||||
// Nasty stuff
|
||||
customEmoji: [],
|
||||
|
|
|
|||
|
|
@ -111,7 +111,10 @@ const sortTimeline = (timeline) => {
|
|||
|
||||
const getLatestScrobble = (state, user) => {
|
||||
const scrobblesSupport = state.pleromaScrobblesAvailable
|
||||
if (!scrobblesSupport) return
|
||||
|
||||
if (!scrobblesSupport || !user.name || user.id === 'undefined') {
|
||||
return
|
||||
}
|
||||
|
||||
if (state.scrobblesNextFetch[user.id] && state.scrobblesNextFetch[user.id] > Date.now()) {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ export const muteFilterHits = (muteFilters, status) => {
|
|||
const statusText = status.text.toLowerCase()
|
||||
const statusSummary = status.summary.toLowerCase()
|
||||
const replyToUser = status.in_reply_to_screen_name?.toLowerCase()
|
||||
const poster = status.user.screen_name.toLowerCase()
|
||||
const poster = status.user.screen_name?.toLowerCase()
|
||||
const mentions = (status.attentions || []).map(att => att.screen_name.toLowerCase())
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const highlightStyle = (prefs) => {
|
|||
|
||||
const highlightClass = (user) => {
|
||||
return 'USER____' + user.screen_name
|
||||
.replace(/\./g, '_')
|
||||
?.replace(/\./g, '_')
|
||||
.replace(/@/g, '_AT_')
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue