aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/App.js7
-rw-r--r--src/App.scss24
-rw-r--r--src/App.vue5
-rw-r--r--src/_mixins.scss17
-rw-r--r--src/boot/after_store.js4
-rw-r--r--src/components/desktop_nav/desktop_nav.scss20
-rw-r--r--src/components/extra_buttons/extra_buttons.js19
-rw-r--r--src/components/extra_buttons/extra_buttons.vue40
-rw-r--r--src/components/favorite_button/favorite_button.js12
-rw-r--r--src/components/favorite_button/favorite_button.vue51
-rw-r--r--src/components/react_button/react_button.js17
-rw-r--r--src/components/react_button/react_button.vue41
-rw-r--r--src/components/reply_button/reply_button.js12
-rw-r--r--src/components/reply_button/reply_button.vue35
-rw-r--r--src/components/retweet_button/retweet_button.js14
-rw-r--r--src/components/retweet_button/retweet_button.vue51
-rw-r--r--src/components/settings_modal/helpers/boolean_setting.js3
-rw-r--r--src/components/settings_modal/helpers/boolean_setting.vue7
-rw-r--r--src/components/settings_modal/helpers/choice_setting.js3
-rw-r--r--src/components/settings_modal/helpers/choice_setting.vue5
-rw-r--r--src/components/settings_modal/helpers/integer_setting.js3
-rw-r--r--src/components/settings_modal/helpers/integer_setting.vue5
-rw-r--r--src/components/settings_modal/helpers/size_setting.js67
-rw-r--r--src/components/settings_modal/helpers/size_setting.vue54
-rw-r--r--src/components/settings_modal/tabs/general_tab.js16
-rw-r--r--src/components/settings_modal/tabs/general_tab.vue43
-rw-r--r--src/i18n/en.json6
-rw-r--r--src/i18n/ru.json9
-rw-r--r--src/modules/config.js10
-rw-r--r--src/services/push/push.js2
-rw-r--r--src/services/style_setter/style_setter.js31
31 files changed, 562 insertions, 71 deletions
diff --git a/src/App.js b/src/App.js
index f5bd7e2e..d1ad16d5 100644
--- a/src/App.js
+++ b/src/App.js
@@ -60,6 +60,13 @@ export default {
'-' + this.layoutType
]
},
+ navClasses () {
+ const { navbarColumnStretch } = this.$store.getters.mergedConfig
+ return [
+ '-' + this.layoutType,
+ ...(navbarColumnStretch ? ['-column-stretch'] : [])
+ ]
+ },
currentUser () { return this.$store.state.users.currentUser },
userBackground () { return this.currentUser.background_image },
instanceBackground () {
diff --git a/src/App.scss b/src/App.scss
index ab025d63..e5bc4c54 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -182,13 +182,18 @@ nav {
.app-layout {
--miniColumn: 25rem;
- --maxiColumn: minmax(var(--miniColumn), 45rem);
+ --maxiColumn: 45rem;
--columnGap: 1em;
--status-margin: 0.75em;
+ --effectiveSidebarColumnWidth: minmax(var(--miniColumn), var(--sidebarColumnWidth, var(--miniColumn)));
+ --effectiveNotifsColumnWidth: minmax(var(--miniColumn), var(--notifsColumnWidth, var(--miniColumn)));
+ --effectiveContentColumnWidth: minmax(var(--miniColumn), var(--contentColumnWidth, var(--maxiColumn)));
position: relative;
display: grid;
- grid-template-columns: var(--miniColumn) var(--maxiColumn);
+ grid-template-columns:
+ var(--effectiveSidebarColumnWidth)
+ var(--effectiveContentColumnWidth);
grid-template-areas: "sidebar content";
grid-template-rows: 1fr;
box-sizing: border-box;
@@ -282,15 +287,24 @@ nav {
}
&.-reverse:not(.-wide):not(.-mobile) {
- grid-template-columns: var(--maxiColumn) var(--miniColumn);
+ grid-template-columns:
+ var(--effectiveContentColumnWidth)
+ var(--effectiveSidebarColumnWidth);
grid-template-areas: "content sidebar";
}
&.-wide {
- grid-template-columns: var(--miniColumn) var(--maxiColumn) var(--miniColumn);
+ grid-template-columns:
+ var(--effectiveSidebarColumnWidth)
+ var(--effectiveContentColumnWidth)
+ var(--effectiveNotifsColumnWidth);
grid-template-areas: "sidebar content notifs";
&.-reverse {
+ grid-template-columns:
+ var(--effectiveNotifsColumnWidth)
+ var(--effectiveContentColumnWidth)
+ var(--effectiveSidebarColumnWidth);
grid-template-areas: "notifs content sidebar";
}
}
@@ -752,7 +766,7 @@ option {
}
.fa-old-padding {
- &.svg-inline--fa {
+ &.svg-inline--fa, &-layer {
padding: 0 0.3em;
}
}
diff --git a/src/App.vue b/src/App.vue
index c741aa70..1f96efe8 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -8,7 +8,10 @@
class="app-bg-wrapper"
/>
<MobileNav v-if="layoutType === 'mobile'" />
- <DesktopNav v-else />
+ <DesktopNav
+ v-else
+ :class="navClasses"
+ />
<Notifications v-if="currentUser" />
<div
id="content"
diff --git a/src/_mixins.scss b/src/_mixins.scss
new file mode 100644
index 00000000..1fed16c3
--- /dev/null
+++ b/src/_mixins.scss
@@ -0,0 +1,17 @@
+@mixin unfocused-style {
+ @content;
+
+ &:focus:not(:focus-visible):not(:hover) {
+ @content;
+ }
+}
+
+@mixin focused-style {
+ &:hover, &:focus {
+ @content;
+ }
+
+ &:focus-visible {
+ @content;
+ }
+}
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 908d905a..38b5f38e 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -12,7 +12,7 @@ import { windowWidth, windowHeight } from '../services/window_utils/window_utils
import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js'
import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
-import { applyTheme } from '../services/style_setter/style_setter.js'
+import { applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
import FaviconService from '../services/favicon_service/favicon_service.js'
let staticInitialResults = null
@@ -360,6 +360,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
console.error('Failed to load any theme!')
}
+ applyConfig(store.state.config)
+
// Now we can try getting the server settings and logging in
// Most of these are preloaded into the index.html so blocking is minimized
await Promise.all([
diff --git a/src/components/desktop_nav/desktop_nav.scss b/src/components/desktop_nav/desktop_nav.scss
index 71202244..f6c7c43d 100644
--- a/src/components/desktop_nav/desktop_nav.scss
+++ b/src/components/desktop_nav/desktop_nav.scss
@@ -23,6 +23,26 @@
max-width: 980px;
}
+ &.-column-stretch .inner-nav {
+ --miniColumn: 25rem;
+ --maxiColumn: 45rem;
+ --columnGap: 1em;
+ max-width: calc(
+ var(--sidebarColumnWidth, var(--miniColumn)) +
+ var(--contentColumnWidth, var(--maxiColumn)) +
+ var(--columnGap)
+ );
+ }
+
+ &.-column-stretch.-wide .inner-nav {
+ max-width: calc(
+ var(--sidebarColumnWidth, var(--miniColumn)) +
+ var(--contentColumnWidth, var(--maxiColumn)) +
+ var(--notifsColumnWidth, var(--miniColumn)) +
+ var(--columnGap)
+ );
+ }
+
&.-logoLeft .inner-nav {
grid-template-columns: auto 2fr 2fr;
grid-template-areas: "logo sitename actions";
diff --git a/src/components/extra_buttons/extra_buttons.js b/src/components/extra_buttons/extra_buttons.js
index 22ffb65a..68fa66ad 100644
--- a/src/components/extra_buttons/extra_buttons.js
+++ b/src/components/extra_buttons/extra_buttons.js
@@ -6,7 +6,9 @@ import {
faEyeSlash,
faThumbtack,
faShareAlt,
- faExternalLinkAlt
+ faExternalLinkAlt,
+ faPlus,
+ faTimes
} from '@fortawesome/free-solid-svg-icons'
import {
faBookmark as faBookmarkReg,
@@ -21,13 +23,26 @@ library.add(
faThumbtack,
faShareAlt,
faExternalLinkAlt,
- faFlag
+ faFlag,
+ faPlus,
+ faTimes
)
const ExtraButtons = {
props: ['status'],
components: { Popover },
+ data () {
+ return {
+ expanded: false
+ }
+ },
methods: {
+ onShow () {
+ this.expanded = true
+ },
+ onClose () {
+ this.expanded = false
+ },
deleteStatus () {
const confirmed = window.confirm(this.$t('status.delete_confirm'))
if (confirmed) {
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index 2c893bf3..011dff9b 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -6,6 +6,8 @@
:offset="{ y: 5 }"
:bound-to="{ x: 'container' }"
remove-padding
+ @show="onShow"
+ @close="onClose"
>
<template #content="{close}">
<div class="dropdown-menu">
@@ -122,10 +124,24 @@
</template>
<template #trigger>
<span class="button-unstyled popover-trigger">
- <FAIcon
- class="fa-scale-110 fa-old-padding"
- icon="ellipsis-h"
- />
+ <FALayers class="fa-old-padding-layer">
+ <FAIcon
+ class="fa-scale-110 "
+ icon="ellipsis-h"
+ />
+ <FAIcon
+ v-show="!expanded"
+ class="focus-marker"
+ transform="shrink-6 up-8 right-16"
+ icon="plus"
+ />
+ <FAIcon
+ v-show="expanded"
+ class="focus-marker"
+ transform="shrink-6 up-8 right-16"
+ icon="times"
+ />
+ </FALayers>
</span>
</template>
</Popover>
@@ -135,6 +151,7 @@
<style lang="scss">
@import '../../_variables.scss';
+@import '../../_mixins.scss';
.ExtraButtons {
/* override of popover internal stuff */
@@ -151,6 +168,21 @@
color: $fallback--text;
color: var(--text, $fallback--text);
}
+
+ }
+
+ .popover-trigger-button {
+ @include unfocused-style {
+ .focus-marker {
+ visibility: hidden;
+ }
+ }
+
+ @include focused-style {
+ .focus-marker {
+ visibility: visible;
+ }
+ }
}
}
</style>
diff --git a/src/components/favorite_button/favorite_button.js b/src/components/favorite_button/favorite_button.js
index 5cd05f73..c996cba2 100644
--- a/src/components/favorite_button/favorite_button.js
+++ b/src/components/favorite_button/favorite_button.js
@@ -1,13 +1,21 @@
import { mapGetters } from 'vuex'
import { library } from '@fortawesome/fontawesome-svg-core'
-import { faStar } from '@fortawesome/free-solid-svg-icons'
+import {
+ faStar,
+ faPlus,
+ faMinus,
+ faCheck
+} from '@fortawesome/free-solid-svg-icons'
import {
faStar as faStarRegular
} from '@fortawesome/free-regular-svg-icons'
library.add(
faStar,
- faStarRegular
+ faStarRegular,
+ faPlus,
+ faMinus,
+ faCheck
)
const FavoriteButton = {
diff --git a/src/components/favorite_button/favorite_button.vue b/src/components/favorite_button/favorite_button.vue
index d5c4c61e..74a1dfbb 100644
--- a/src/components/favorite_button/favorite_button.vue
+++ b/src/components/favorite_button/favorite_button.vue
@@ -7,11 +7,31 @@
:title="$t('tool_tip.favorite')"
@click.prevent="favorite()"
>
- <FAIcon
- class="fa-scale-110 fa-old-padding"
- :icon="[status.favorited ? 'fas' : 'far', 'star']"
- :spin="animated"
- />
+ <FALayers class="fa-scale-110 fa-old-padding-layer">
+ <FAIcon
+ class="fa-scale-110"
+ :icon="[status.favorited ? 'fas' : 'far', 'star']"
+ :spin="animated"
+ />
+ <FAIcon
+ v-if="status.favorited"
+ class="active-marker"
+ transform="shrink-6 up-9 right-12"
+ icon="check"
+ />
+ <FAIcon
+ v-if="!status.favorited"
+ class="focus-marker"
+ transform="shrink-6 up-9 right-12"
+ icon="plus"
+ />
+ <FAIcon
+ v-else
+ class="focus-marker"
+ transform="shrink-6 up-9 right-12"
+ icon="minus"
+ />
+ </FALayers>
</button>
<span v-else>
<FAIcon
@@ -33,6 +53,7 @@
<style lang="scss">
@import '../../_variables.scss';
+@import '../../_mixins.scss';
.FavoriteButton {
display: flex;
@@ -57,6 +78,26 @@
color: $fallback--cOrange;
color: var(--cOrange, $fallback--cOrange);
}
+
+ @include unfocused-style {
+ .focus-marker {
+ visibility: hidden;
+ }
+
+ .active-marker {
+ visibility: visible;
+ }
+ }
+
+ @include focused-style {
+ .focus-marker {
+ visibility: visible;
+ }
+
+ .active-marker {
+ visibility: hidden;
+ }
+ }
}
}
</style>
diff --git a/src/components/react_button/react_button.js b/src/components/react_button/react_button.js
index 37d6e7d0..5e052e1e 100644
--- a/src/components/react_button/react_button.js
+++ b/src/components/react_button/react_button.js
@@ -1,15 +1,21 @@
import Popover from '../popover/popover.vue'
import { library } from '@fortawesome/fontawesome-svg-core'
+import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'
import { faSmileBeam } from '@fortawesome/free-regular-svg-icons'
import { trim } from 'lodash'
-library.add(faSmileBeam)
+library.add(
+ faPlus,
+ faTimes,
+ faSmileBeam
+)
const ReactButton = {
props: ['status'],
data () {
return {
- filterWord: ''
+ filterWord: '',
+ expanded: false
}
},
components: {
@@ -25,6 +31,13 @@ const ReactButton = {
}
close()
},
+ onShow () {
+ this.expanded = true
+ this.focusInput()
+ },
+ onClose () {
+ this.expanded = false
+ },
focusInput () {
this.$nextTick(() => {
const input = this.$el.querySelector('input')
diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue
index 5a809847..254c49db 100644
--- a/src/components/react_button/react_button.vue
+++ b/src/components/react_button/react_button.vue
@@ -7,7 +7,8 @@
:bound-to="{ x: 'container' }"
remove-padding
popover-class="ReactButton popover-default"
- @show="focusInput"
+ @show="onShow"
+ @close="onClose"
>
<template #content="{close}">
<div class="reaction-picker-filter">
@@ -46,10 +47,24 @@
class="button-unstyled popover-trigger"
:title="$t('tool_tip.add_reaction')"
>
- <FAIcon
- class="fa-scale-110 fa-old-padding"
- :icon="['far', 'smile-beam']"
- />
+ <FALayers>
+ <FAIcon
+ class="fa-scale-110 fa-old-padding"
+ :icon="['far', 'smile-beam']"
+ />
+ <FAIcon
+ v-show="!expanded"
+ class="focus-marker"
+ transform="shrink-6 up-9 right-17"
+ icon="plus"
+ />
+ <FAIcon
+ v-show="expanded"
+ class="focus-marker"
+ transform="shrink-6 up-9 right-17"
+ icon="times"
+ />
+ </FALayers>
</span>
</template>
</Popover>
@@ -59,6 +74,7 @@
<style lang="scss">
@import '../../_variables.scss';
+@import '../../_mixins.scss';
.ReactButton {
.reaction-picker-filter {
@@ -125,6 +141,21 @@
color: $fallback--text;
color: var(--text, $fallback--text);
}
+
+ }
+
+ .popover-trigger-button {
+ @include unfocused-style {
+ .focus-marker {
+ visibility: hidden;
+ }
+ }
+
+ @include focused-style {
+ .focus-marker {
+ visibility: visible;
+ }
+ }
}
}
diff --git a/src/components/reply_button/reply_button.js b/src/components/reply_button/reply_button.js
index c7bd2a2b..d6382982 100644
--- a/src/components/reply_button/reply_button.js
+++ b/src/components/reply_button/reply_button.js
@@ -1,7 +1,15 @@
import { library } from '@fortawesome/fontawesome-svg-core'
-import { faReply } from '@fortawesome/free-solid-svg-icons'
+import {
+ faReply,
+ faPlus,
+ faTimes
+} from '@fortawesome/free-solid-svg-icons'
-library.add(faReply)
+library.add(
+ faReply,
+ faPlus,
+ faTimes
+)
const ReplyButton = {
name: 'ReplyButton',
diff --git a/src/components/reply_button/reply_button.vue b/src/components/reply_button/reply_button.vue
index c17041da..ea97fbaa 100644
--- a/src/components/reply_button/reply_button.vue
+++ b/src/components/reply_button/reply_button.vue
@@ -7,10 +7,24 @@
:title="$t('tool_tip.reply')"
@click.prevent="$emit('toggle')"
>
- <FAIcon
- class="fa-scale-110 fa-old-padding"
- icon="reply"
- />
+ <FALayers class="fa-old-padding-layer">
+ <FAIcon
+ class="fa-scale-110"
+ icon="reply"
+ />
+ <FAIcon
+ v-if="!replying"
+ class="focus-marker"
+ transform="shrink-6 up-8 right-11"
+ icon="plus"
+ />
+ <FAIcon
+ v-else
+ class="focus-marker"
+ transform="shrink-6 up-8 right-11"
+ icon="times"
+ />
+ </FALayers>
</button>
<span v-else>
<FAIcon
@@ -32,6 +46,7 @@
<style lang="scss">
@import '../../_variables.scss';
+@import '../../_mixins.scss';
.ReplyButton {
display: flex;
@@ -52,6 +67,18 @@
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
+
+ @include unfocused-style {
+ .focus-marker {
+ visibility: hidden;
+ }
+ }
+
+ @include focused-style {
+ .focus-marker {
+ visibility: visible;
+ }
+ }
}
}
diff --git a/src/components/retweet_button/retweet_button.js b/src/components/retweet_button/retweet_button.js
index 2103fd0b..b7911814 100644
--- a/src/components/retweet_button/retweet_button.js
+++ b/src/components/retweet_button/retweet_button.js
@@ -1,7 +1,17 @@
import { library } from '@fortawesome/fontawesome-svg-core'
-import { faRetweet } from '@fortawesome/free-solid-svg-icons'
+import {
+ faRetweet,
+ faPlus,
+ faMinus,
+ faCheck
+} from '@fortawesome/free-solid-svg-icons'
-library.add(faRetweet)
+library.add(
+ faRetweet,
+ faPlus,
+ faMinus,
+ faCheck
+)
const RetweetButton = {
props: ['status', 'loggedIn', 'visibility'],
diff --git a/src/components/retweet_button/retweet_button.vue b/src/components/retweet_button/retweet_button.vue
index 5a15d387..396d1200 100644
--- a/src/components/retweet_button/retweet_button.vue
+++ b/src/components/retweet_button/retweet_button.vue
@@ -7,11 +7,31 @@
:title="$t('tool_tip.repeat')"
@click.prevent="retweet()"
>
- <FAIcon
- class="fa-scale-110 fa-old-padding"
- icon="retweet"
- :spin="animated"
- />
+ <FALayers class="fa-old-padding-layer">
+ <FAIcon
+ class="fa-scale-110"
+ icon="retweet"
+ :spin="animated"
+ />
+ <FAIcon
+ v-if="status.repeated"
+ class="active-marker"
+ transform="shrink-6 up-9 right-12"
+ icon="check"
+ />
+ <FAIcon
+ v-if="!status.repeated"
+ class="focus-marker"
+ transform="shrink-6 up-9 right-12"
+ icon="plus"
+ />
+ <FAIcon
+ v-else
+ class="focus-marker"
+ transform="shrink-6 up-9 right-12"
+ icon="minus"
+ />
+ </FALayers>
</button>
<span v-else-if="loggedIn">
<FAIcon
@@ -40,6 +60,7 @@
<style lang="scss">
@import '../../_variables.scss';
+@import '../../_mixins.scss';
.RetweetButton {
display: flex;
@@ -64,6 +85,26 @@
color: $fallback--cGreen;
color: var(--cGreen, $fallback--cGreen);
}
+
+ @include unfocused-style {
+ .focus-marker {
+ visibility: hidden;
+ }
+
+ .active-marker {
+ visibility: visible;
+ }
+ }
+
+ @include focused-style {
+ .focus-marker {
+ visibility: visible;
+ }
+
+ .active-marker {
+ visibility: hidden;
+ }
+ }
}
}
</style>
diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js
index 353e551c..dc832044 100644
--- a/src/components/settings_modal/helpers/boolean_setting.js
+++ b/src/components/settings_modal/helpers/boolean_setting.js
@@ -42,6 +42,9 @@ export default {
methods: {
update (e) {
set(this.$parent, this.path, e)
+ },
+ reset () {
+ set(this.$parent, this.path, this.defaultState)
}
}
}
diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue
index 69584808..41142966 100644
--- a/src/components/settings_modal/helpers/boolean_setting.vue
+++ b/src/components/settings_modal/helpers/boolean_setting.vue
@@ -15,7 +15,12 @@
<slot />
</span>
{{ ' ' }}
- <ModifiedIndicator :changed="isChanged" /><ServerSideIndicator :server-side="isServerSide" /> </Checkbox>
+ <ModifiedIndicator
+ :changed="isChanged"
+ :onclick="reset"
+ />
+ <ServerSideIndicator :server-side="isServerSide" />
+ </Checkbox>
</label>
</template>
diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js
index 4677d4c1..3da559fe 100644
--- a/src/components/settings_modal/helpers/choice_setting.js
+++ b/src/components/settings_modal/helpers/choice_setting.js
@@ -43,6 +43,9 @@ export default {
methods: {
update (e) {
set(this.$parent, this.path, e)
+ },
+ reset () {
+ set(this.$parent, this.path, this.defaultState)
}
}
}
diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue
index 258c7422..d141a0d6 100644
--- a/src/components/settings_modal/helpers/choice_setting.vue
+++ b/src/components/settings_modal/helpers/choice_setting.vue
@@ -19,7 +19,10 @@
{{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }}
</option>
</Select>
- <ModifiedIndicator :changed="isChanged" />
+ <ModifiedIndicator
+ :changed="isChanged"
+ :onclick="reset"
+ />
<ServerSideIndicator :server-side="isServerSide" />
</label>
</template>
diff --git a/src/components/settings_modal/helpers/integer_setting.js b/src/components/settings_modal/helpers/integer_setting.js
index 17dc0e7b..e64d0cee 100644
--- a/src/components/settings_modal/helpers/integer_setting.js
+++ b/src/components/settings_modal/helpers/integer_setting.js
@@ -36,6 +36,9 @@ export default {
methods: {
update (e) {
set(this.$parent, this.path, parseInt(e.target.value))
+ },
+ reset () {
+ set(this.$parent, this.path, this.defaultState)
}
}
}
diff --git a/src/components/settings_modal/helpers/integer_setting.vue b/src/components/settings_modal/helpers/integer_setting.vue
index e661a025..695e2673 100644
--- a/src/components/settings_modal/helpers/integer_setting.vue
+++ b/src/components/settings_modal/helpers/integer_setting.vue
@@ -17,7 +17,10 @@
@change="update"
>
{{ ' ' }}
- <ModifiedIndicator :changed="isChanged" />
+ <ModifiedIndicator
+ :changed="isChanged"
+ :onclick="reset"
+ />
</span>
</template>
diff --git a/src/components/settings_modal/helpers/size_setting.js b/src/components/settings_modal/helpers/size_setting.js
new file mode 100644
index 00000000..58697412
--- /dev/null
+++ b/src/components/settings_modal/helpers/size_setting.js
@@ -0,0 +1,67 @@
+import { get, set } from 'lodash'
+import ModifiedIndicator from './modified_indicator.vue'
+import Select from 'src/components/select/select.vue'
+
+export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%']
+export const defaultHorizontalUnits = ['px', 'rem', 'vw']
+export const defaultVerticalUnits = ['px', 'rem', 'vh']
+
+export default {
+ components: {
+ ModifiedIndicator,
+ Select
+ },
+ props: {
+ path: String,
+ disabled: Boolean,
+ min: Number,
+ units: {
+ type: [String],
+ default: () => allCssUnits
+ },
+ expert: [Number, String]
+ },
+ computed: {
+ pathDefault () {
+ const [firstSegment, ...rest] = this.path.split('.')
+ return [firstSegment + 'DefaultValue', ...rest].join('.')
+ },
+ stateUnit () {
+ return (this.state || '').replace(/\d+/, '')
+ },
+ stateValue () {
+ return (this.state || '').replace(/\D+/, '')
+ },
+ state () {
+ const value = get(this.$parent, this.path)
+ if (value === undefined) {
+ return this.defaultState
+ } else {
+ return value
+ }
+ },
+ defaultState () {
+ return get(this.$parent, this.pathDefault)
+ },
+ isChanged () {
+ return this.state !== this.defaultState
+ },
+ matchesExpertLevel () {
+ return (this.expert || 0) <= this.$parent.expertLevel
+ }
+ },
+ methods: {
+ update (e) {
+ set(this.$parent, this.path, e)
+ },
+ reset () {
+ set(this.$parent, this.path, this.defaultState)
+ },
+ updateValue (e) {
+ set(this.$parent, this.path, parseInt(e.target.value) + this.stateUnit)
+ },
+ updateUnit (e) {
+ set(this.$parent, this.path, this.stateValue + e.target.value)
+ }
+ }
+}
diff --git a/src/components/settings_modal/helpers/size_setting.vue b/src/components/settings_modal/helpers/size_setting.vue
new file mode 100644
index 00000000..90c9f538
--- /dev/null
+++ b/src/components/settings_modal/helpers/size_setting.vue
@@ -0,0 +1,54 @@
+<template>
+ <span
+ v-if="matchesExpertLevel"
+ class="SizeSetting"
+ >
+ <label
+ :for="path"
+ class="size-label"
+ >
+ <slot />
+ </label>
+ <input
+ :id="path"
+ class="number-input"
+ type="number"
+ step="1"
+ :disabled="disabled"
+ :min="min || 0"
+ :value="stateValue"
+ @change="updateValue"
+ >
+ <Select
+ :id="path"
+ :model-value="stateUnit"
+ :disabled="disabled"
+ class="css-unit-input"
+ @change="updateUnit"
+ >
+ <option
+ v-for="option in units"
+ :key="option"
+ :value="option"
+ >
+ {{ option }}
+ </option>
+ </Select>
+ {{ ' ' }}
+ <ModifiedIndicator
+ :changed="isChanged"
+ :onclick="reset"
+ />
+ </span>
+</template>
+
+<script src="./size_setting.js"></script>
+
+<style lang="scss">
+.css-unit-input, .css-unit-input select {
+ margin-left: 0.5em;
+ width: 4em !important;
+ max-width: 4em !important;
+ min-width: 4em !important;
+}
+</style>
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 1e11b9e0..a22b9b03 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -2,6 +2,7 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
import ChoiceSetting from '../helpers/choice_setting.vue'
import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
import IntegerSetting from '../helpers/integer_setting.vue'
+import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue'
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
@@ -56,11 +57,15 @@ const GeneralTab = {
BooleanSetting,
ChoiceSetting,
IntegerSetting,
+ SizeSetting,
InterfaceLanguageSwitcher,
ScopeSelector,
ServerSideIndicator
},
computed: {
+ horizontalUnits () {
+ return defaultHorizontalUnits
+ },
postFormats () {
return this.$store.state.instance.postFormats || []
},
@@ -71,6 +76,17 @@ const GeneralTab = {
label: this.$t(`post_status.content_type["${format}"]`)
}))
},
+ columns () {
+ const mode = this.$store.getters.mergedConfig.thirdColumnMode
+
+ const notif = mode === 'none' ? [] : ['notifs']
+
+ if (this.$store.getters.mergedConfig.sidebarRight || mode === 'postform') {
+ return [...notif, 'content', 'sidebar']
+ } else {
+ return ['sidebar', 'content', ...notif]
+ }
+ },
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
instanceWallpaperUsed () {
return this.$store.state.instance.background &&
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index 91015955..333b5493 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -15,11 +15,6 @@
{{ $t('settings.hide_isp') }}
</BooleanSetting>
</li>
- <li>
- <BooleanSetting path="sidebarRight">
- {{ $t('settings.right_sidebar') }}
- </BooleanSetting>
- </li>
<li v-if="instanceWallpaperUsed">
<BooleanSetting path="hideInstanceWallpaper">
{{ $t('settings.hide_wallpaper') }}
@@ -65,16 +60,6 @@
</BooleanSetting>
</li>
<li>
- <BooleanSetting path="disableStickyHeaders">
- {{ $t('settings.disable_sticky_headers') }}
- </BooleanSetting>
- </li>
- <li>
- <BooleanSetting path="showScrollbars">
- {{ $t('settings.show_scrollbars') }}
- </BooleanSetting>
- </li>
- <li>
<BooleanSetting
path="userPopoverZoom"
expert="1"
@@ -91,16 +76,6 @@
</BooleanSetting>
</li>
<li>
- <ChoiceSetting
- v-if="user"
- id="thirdColumnMode"
- path="thirdColumnMode"
- :options="thirdColumnModeOptions"
- >
- {{ $t('settings.third_column_mode') }}
- </ChoiceSetting>
- </li>
- <li>
<BooleanSetting
path="alwaysShowNewPostButton"
expert="1"
@@ -148,6 +123,11 @@
</BooleanSetting>
</li>
<li>
+ <BooleanSetting path="navbarColumnStretch">
+ {{ $t('settings.navbar_column_stretch') }}
+ </BooleanSetting>
+ </li>
+ <li>
<ChoiceSetting
v-if="user"
id="thirdColumnMode"
@@ -480,3 +460,16 @@
</template>
<script src="./general_tab.js"></script>
+
+<style lang="scss">
+.column-settings {
+ display: flex;
+ justify-content: space-evenly;
+ flex-wrap: wrap;
+}
+.column-settings .size-label {
+ display: block;
+ margin-bottom: 0.5em;
+ margin-top: 0.5em;
+}
+</style>
diff --git a/src/i18n/en.json b/src/i18n/en.json
index 2e845959..a5d3cec6 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -412,6 +412,7 @@
"hide_isp": "Hide instance-specific panel",
"hide_shoutbox": "Hide instance shoutbox",
"right_sidebar": "Reverse order of columns",
+ "navbar_column_stretch": "Stretch navbar to columns width",
"always_show_post_button": "Always show floating New Post button",
"hide_wallpaper": "Hide instance wallpaper",
"preload_images": "Preload images",
@@ -533,6 +534,11 @@
"third_column_mode_none": "Don't show third column at all",
"third_column_mode_notifications": "Notifications column",
"third_column_mode_postform": "Main post form and navigation",
+ "columns": "Columns",
+ "column_sizes": "Column sizes",
+ "column_sizes_sidebar": "Sidebar",
+ "column_sizes_content": "Content",
+ "column_sizes_notifs": "Notifications",
"tree_advanced": "Allow more flexible navigation in tree view",
"tree_fade_ancestors": "Display ancestors of the current status in faint text",
"conversation_display_linear": "Linear-style",
diff --git a/src/i18n/ru.json b/src/i18n/ru.json
index 7e6ff3f5..02815f3e 100644
--- a/src/i18n/ru.json
+++ b/src/i18n/ru.json
@@ -456,6 +456,15 @@
"subject_line_mastodon": "Как в Mastodon: скопировать как есть",
"subject_line_email": "Как в электронной почте: \"re: тема\"",
"subject_line_behavior": "Копировать тему в ответах",
+ "third_column_mode": "Когда недостаточно места, показывать третью колонку содержащую",
+ "third_column_mode_none": "Не показывать третью колонку совсем",
+ "third_column_mode_notifications": "Колонку уведомлений",
+ "third_column_mode_postform": "Форму отправки сообщения и навигацию",
+ "columns": "Колонки",
+ "column_sizes": "Размеры колонок",
+ "column_sizes_sidebar": "Боковой",
+ "column_sizes_content": "Содержимого",
+ "column_sizes_notifs": "Уведомлений",
"no_mutes": "Нет игнорируемых",
"no_blocks": "Нет блокировок",
"notification_visibility_emoji_reactions": "Реакции",
diff --git a/src/modules/config.js b/src/modules/config.js
index c34b2c8c..1c2a45c2 100644
--- a/src/modules/config.js
+++ b/src/modules/config.js
@@ -1,5 +1,5 @@
import Cookies from 'js-cookie'
-import { setPreset, applyTheme } from '../services/style_setter/style_setter.js'
+import { setPreset, applyTheme, applyConfig } from '../services/style_setter/style_setter.js'
import messages from '../i18n/messages'
import localeService from '../services/locale/locale.service.js'
@@ -87,6 +87,7 @@ export const defaultState = {
sidebarColumnWidth: '25rem',
contentColumnWidth: '45rem',
notifsColumnWidth: '25rem',
+ navbarColumnStretch: false,
listsNavigation: false,
greentext: undefined, // instance default
useAtIcon: undefined, // instance default
@@ -165,12 +166,17 @@ const config = {
setHighlight ({ commit, dispatch }, { user, color, type }) {
commit('setHighlight', { user, color, type })
},
- setOption ({ commit, dispatch }, { name, value }) {
+ setOption ({ commit, dispatch, state }, { name, value }) {
commit('setOption', { name, value })
switch (name) {
case 'theme':
setPreset(value)
break
+ case 'sidebarColumnWidth':
+ case 'contentColumnWidth':
+ case 'notifsColumnWidth':
+ applyConfig(state)
+ break
case 'customTheme':
case 'customThemeSource':
applyTheme(value)
diff --git a/src/services/push/push.js b/src/services/push/push.js
index 40d7b0fd..1787ac36 100644
--- a/src/services/push/push.js
+++ b/src/services/push/push.js
@@ -1,4 +1,4 @@
-import runtime from 'serviceworker-webpack-plugin/lib/runtime'
+import runtime from 'serviceworker-webpack5-plugin/lib/runtime'
function urlBase64ToUint8Array (base64String) {
const padding = '='.repeat((4 - base64String.length % 4) % 4)
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index 543aa874..d6e973a1 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -1,6 +1,7 @@
import { convert } from 'chromatism'
import { rgb2hex, hex2rgb, rgba2css, getCssColor, relativeLuminance } from '../color_convert/color_convert.js'
import { getColors, computeDynamicColor, getOpacitySlot } from '../theme_data/theme_data.service.js'
+import { defaultState } from '../../modules/config.js'
export const applyTheme = (input) => {
const { rules } = generatePreset(input)
@@ -20,6 +21,36 @@ export const applyTheme = (input) => {
body.classList.remove('hidden')
}
+const configColumns = ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth }) =>
+ ({ sidebarColumnWidth, contentColumnWidth, notifsColumnWidth })
+
+const defaultConfigColumns = configColumns(defaultState)
+
+export const applyConfig = (config) => {
+ const columns = configColumns(config)
+
+ if (columns === defaultConfigColumns) {
+ return
+ }
+
+ const head = document.head
+ const body = document.body
+ body.classList.add('hidden')
+
+ const rules = Object
+ .entries(columns)
+ .filter(([k, v]) => v)
+ .map(([k, v]) => `--${k}: ${v}`).join(';')
+
+ const styleEl = document.createElement('style')
+ head.appendChild(styleEl)
+ const styleSheet = styleEl.sheet
+
+ styleSheet.toString()
+ styleSheet.insertRule(`:root { ${rules} }`, 'index-max')
+ body.classList.remove('hidden')
+}
+
export const getCssShadow = (input, usesDropShadow) => {
if (input.length === 0) {
return 'none'