diff --git a/CHANGELOG.md b/CHANGELOG.md
index cef6d401..ab689c77 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Added a quick settings to timeline header for easier access
- Added option to mark posts as sensitive by default
- Added quick filters for notifications
+- Implemented user option to change sidebar position to the right side
+- Implemented user option to hide floating shout panel
+### Fixed
+- Fixed follow request count showing in the wrong location in mobile view
## [2.3.0] - 2021-03-01
### Fixed
diff --git a/build/dev-server.js b/build/dev-server.js
index 48574214..c06192bd 100644
--- a/build/dev-server.js
+++ b/build/dev-server.js
@@ -21,6 +21,7 @@ var compiler = webpack(webpackConfig)
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
+ writeToDisk: true,
stats: {
colors: true,
chunks: false
diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js
index d987eff1..900d824b 100644
--- a/build/webpack.base.conf.js
+++ b/build/webpack.base.conf.js
@@ -3,6 +3,7 @@ var config = require('../config')
var utils = require('./utils')
var projectRoot = path.resolve(__dirname, '../')
var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin')
+var CopyPlugin = require('copy-webpack-plugin');
var env = process.env.NODE_ENV
// check env & config/index.js to decide weither to enable CSS Sourcemaps for the
@@ -93,6 +94,19 @@ module.exports = {
new ServiceWorkerWebpackPlugin({
entry: path.join(__dirname, '..', 'src/sw.js'),
filename: 'sw-pleroma.js'
+ }),
+ // This copies Ruffle's WASM to a directory so that JS side can access it
+ new CopyPlugin({
+ patterns: [
+ {
+ from: "node_modules/ruffle-mirror/*",
+ to: "static/ruffle",
+ flatten: true
+ },
+ ],
+ options: {
+ concurrency: 100,
+ },
})
]
}
diff --git a/package.json b/package.json
index 8dbf2503..99301266 100644
--- a/package.json
+++ b/package.json
@@ -32,6 +32,7 @@
"phoenix": "^1.3.0",
"portal-vue": "^2.1.4",
"punycode.js": "^2.1.0",
+ "ruffle-mirror": "^2021.4.10",
"v-click-outside": "^2.1.1",
"vue": "^2.6.11",
"vue-i18n": "^7.3.2",
@@ -57,6 +58,7 @@
"chalk": "^1.1.3",
"chromedriver": "^87.0.1",
"connect-history-api-fallback": "^1.1.0",
+ "copy-webpack-plugin": "^6.4.1",
"cross-spawn": "^4.0.2",
"css-loader": "^0.28.0",
"custom-event-polyfill": "^1.0.7",
@@ -111,7 +113,7 @@
"url-loader": "^1.1.2",
"vue-loader": "^14.0.0",
"vue-style-loader": "^4.0.0",
- "webpack": "^4.0.0",
+ "webpack": "^4.44.0",
"webpack-dev-middleware": "^3.6.0",
"webpack-hot-middleware": "^2.12.2",
"webpack-merge": "^0.14.1"
diff --git a/src/App.js b/src/App.js
index 1158534f..0d55ffb4 100644
--- a/src/App.js
+++ b/src/App.js
@@ -4,8 +4,7 @@ import Notifications from './components/notifications/notifications.vue'
import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'
import FeaturesPanel from './components/features_panel/features_panel.vue'
import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
-import ChatList from './components/chat_list/chat_list.vue'
-import ChatPanel from './components/chat_panel/chat_panel.vue'
+import ShoutPanel from './components/shout_panel/shout_panel.vue'
import SettingsModal from './components/settings_modal/settings_modal.vue'
import MediaModal from './components/media_modal/media_modal.vue'
import SideDrawer from './components/side_drawer/side_drawer.vue'
@@ -27,8 +26,7 @@ export default {
InstanceSpecificPanel,
FeaturesPanel,
WhoToFollowPanel,
- ChatPanel,
- ChatList,
+ ShoutPanel,
MediaModal,
SideDrawer,
MobilePostStatusButton,
@@ -67,7 +65,7 @@ export default {
}
}
},
- chat () { return this.$store.state.chat.channel.state === 'joined' },
+ shout () { return this.$store.state.shout.channel.state === 'joined' },
suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled },
showInstanceSpecificPanel () {
return this.$store.state.instance.showInstanceSpecificPanel &&
@@ -78,11 +76,14 @@ export default {
return this.$store.getters.mergedConfig.showThirdColumn || false
},
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
+ hideShoutbox () {
+ return this.$store.getters.mergedConfig.hideShoutbox
+ },
isMobileLayout () { return this.$store.state.interface.mobileLayout },
privateMode () { return this.$store.state.instance.private },
sidebarAlign () {
return {
- 'order': this.$store.state.instance.sidebarRight ? 99 : 0
+ 'order': this.$store.getters.mergedConfig.sidebarRight ? 99 : 0
}
},
thirdColumnLayout () {
diff --git a/src/App.scss b/src/App.scss
index d515bc45..4ae30ddf 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -187,7 +187,7 @@ a {
}
}
-input, textarea, .select, .input {
+input, textarea, .input {
&.unstyled {
border-radius: 0;
@@ -217,47 +217,11 @@ input, textarea, .select, .input {
hyphens: none;
padding: 8px .5em;
- &.select {
- padding: 0;
- }
-
- &:disabled, &[disabled=disabled] {
+ &:disabled, &[disabled=disabled], &.disabled {
cursor: not-allowed;
opacity: 0.5;
}
- .select-down-icon {
- position: absolute;
- top: 0;
- bottom: 0;
- right: 5px;
- height: 100%;
- color: $fallback--text;
- color: var(--inputText, $fallback--text);
- line-height: 28px;
- z-index: 0;
- pointer-events: none;
- }
-
- select {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- background: transparent;
- border: none;
- color: $fallback--text;
- color: var(--inputText, --text, $fallback--text);
- margin: 0;
- padding: 0 2em 0 .2em;
- font-family: sans-serif;
- font-family: var(--inputFont, sans-serif);
- font-size: 14px;
- width: 100%;
- z-index: 1;
- height: 28px;
- line-height: 16px;
- }
-
&[type=range] {
background: none;
border: none;
@@ -830,13 +794,6 @@ nav {
}
}
-.select-multiple {
- display: flex;
- .option-list {
- margin: 0;
- padding-left: .5em;
- }
-}
.setting-list,
.option-list{
list-style-type: none;
diff --git a/src/App.vue b/src/App.vue
index e9c9860b..8331de28 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -66,10 +66,10 @@
-
diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 45090e5d..cc0c7c5e 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -240,7 +240,7 @@ const getNodeInfo = async ({ store }) => {
store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations })
store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') })
store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') })
- store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') })
+ store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') })
store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
diff --git a/src/boot/routes.js b/src/boot/routes.js
index b5d3c631..1bc1f9f7 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -16,7 +16,7 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue'
import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
import Notifications from 'components/notifications/notifications.vue'
import AuthForm from 'components/auth_form/auth_form.js'
-import ChatPanel from 'components/chat_panel/chat_panel.vue'
+import ShoutPanel from 'components/shout_panel/shout_panel.vue'
import WhoToFollow from 'components/who_to_follow/who_to_follow.vue'
import About from 'components/about/about.vue'
import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue'
@@ -64,7 +64,7 @@ export default (store) => {
{ name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute },
{ name: 'notifications', path: '/:username/notifications', component: Notifications, beforeEnter: validateAuthenticatedRoute },
{ name: 'login', path: '/login', component: AuthForm },
- { name: 'chat-panel', path: '/chat-panel', component: ChatPanel, props: () => ({ floating: false }) },
+ { name: 'shout-panel', path: '/shout-panel', component: ShoutPanel, props: () => ({ floating: false }) },
{ name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },
{ name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) },
{ name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute },
diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue
index ab5d1d29..1e31151c 100644
--- a/src/components/account_actions/account_actions.vue
+++ b/src/components/account_actions/account_actions.vue
@@ -6,10 +6,7 @@
:bound-to="{ x: 'container' }"
remove-padding
>
-
+
-
-
-
-
+
+
+
+
@@ -83,7 +79,6 @@
}
.ellipsis-button {
- cursor: pointer;
width: 2.5em;
margin: -0.5em 0;
padding: 0.5em 0;
diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js
index 5f5779a0..8849f501 100644
--- a/src/components/attachment/attachment.js
+++ b/src/components/attachment/attachment.js
@@ -1,4 +1,5 @@
import StillImage from '../still-image/still-image.vue'
+import Flash from '../flash/flash.vue'
import VideoAttachment from '../video_attachment/video_attachment.vue'
import nsfwImage from '../../assets/nsfw.png'
import fileTypeService from '../../services/file_type/file_type.service.js'
@@ -43,6 +44,7 @@ const Attachment = {
}
},
components: {
+ Flash,
StillImage,
VideoAttachment
},
diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue
index 2c1c1682..f80badfd 100644
--- a/src/components/attachment/attachment.vue
+++ b/src/components/attachment/attachment.vue
@@ -117,6 +117,11 @@
+
+
@@ -172,6 +177,7 @@
}
.non-gallery.attachment {
+ &.flash,
&.video {
flex: 1 0 40%;
}
diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue
index e23eec13..f98b7ed2 100644
--- a/src/components/chat_list/chat_list.vue
+++ b/src/components/chat_list/chat_list.vue
@@ -23,10 +23,7 @@
class="timeline"
>
-
+
-
+
-
-
+
+
+
+
-
- {{ createdAt }}
-
+
+
+ {{ createdAt }}
+
+
diff --git a/src/components/domain_mute_card/domain_mute_card.vue b/src/components/domain_mute_card/domain_mute_card.vue
index 3b5aec14..836688aa 100644
--- a/src/components/domain_mute_card/domain_mute_card.vue
+++ b/src/components/domain_mute_card/domain_mute_card.vue
@@ -9,7 +9,7 @@
class="btn button-default"
>
{{ $t('domain_mute_card.unmute') }}
-
+
{{ $t('domain_mute_card.unmute_progress') }}
@@ -19,7 +19,7 @@
class="btn button-default"
>
{{ $t('domain_mute_card.mute') }}
-
+
{{ $t('domain_mute_card.mute_progress') }}
diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js
index 18a121cc..af6283d7 100644
--- a/src/components/emoji_input/emoji_input.js
+++ b/src/components/emoji_input/emoji_input.js
@@ -57,6 +57,7 @@ const EmojiInput = {
required: true,
type: Function
},
+ // TODO VUE3: change to modelValue, change 'input' event to 'input'
value: {
/**
* Used for v-model
@@ -143,32 +144,31 @@ const EmojiInput = {
}
},
mounted () {
- const slots = this.$slots.default
- if (!slots || slots.length === 0) return
- const input = slots.find(slot => ['input', 'textarea'].includes(slot.tag))
+ const { root } = this.$refs
+ const input = root.querySelector('.emoji-input > input') || root.querySelector('.emoji-input > textarea')
if (!input) return
this.input = input
this.resize()
- input.elm.addEventListener('blur', this.onBlur)
- input.elm.addEventListener('focus', this.onFocus)
- input.elm.addEventListener('paste', this.onPaste)
- input.elm.addEventListener('keyup', this.onKeyUp)
- input.elm.addEventListener('keydown', this.onKeyDown)
- input.elm.addEventListener('click', this.onClickInput)
- input.elm.addEventListener('transitionend', this.onTransition)
- input.elm.addEventListener('input', this.onInput)
+ input.addEventListener('blur', this.onBlur)
+ input.addEventListener('focus', this.onFocus)
+ input.addEventListener('paste', this.onPaste)
+ input.addEventListener('keyup', this.onKeyUp)
+ input.addEventListener('keydown', this.onKeyDown)
+ input.addEventListener('click', this.onClickInput)
+ input.addEventListener('transitionend', this.onTransition)
+ input.addEventListener('input', this.onInput)
},
unmounted () {
const { input } = this
if (input) {
- input.elm.removeEventListener('blur', this.onBlur)
- input.elm.removeEventListener('focus', this.onFocus)
- input.elm.removeEventListener('paste', this.onPaste)
- input.elm.removeEventListener('keyup', this.onKeyUp)
- input.elm.removeEventListener('keydown', this.onKeyDown)
- input.elm.removeEventListener('click', this.onClickInput)
- input.elm.removeEventListener('transitionend', this.onTransition)
- input.elm.removeEventListener('input', this.onInput)
+ input.removeEventListener('blur', this.onBlur)
+ input.removeEventListener('focus', this.onFocus)
+ input.removeEventListener('paste', this.onPaste)
+ input.removeEventListener('keyup', this.onKeyUp)
+ input.removeEventListener('keydown', this.onKeyDown)
+ input.removeEventListener('click', this.onClickInput)
+ input.removeEventListener('transitionend', this.onTransition)
+ input.removeEventListener('input', this.onInput)
}
},
watch: {
@@ -216,7 +216,7 @@ const EmojiInput = {
}, 0)
},
togglePicker () {
- this.input.elm.focus()
+ this.input.focus()
this.showPicker = !this.showPicker
if (this.showPicker) {
this.scrollIntoView()
@@ -262,13 +262,13 @@ const EmojiInput = {
this.$emit('input', newValue)
const position = this.caret + (insertion + spaceAfter + spaceBefore).length
if (!keepOpen) {
- this.input.elm.focus()
+ this.input.focus()
}
this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion
// Set selection right after the replacement instead of the very end
- this.input.elm.setSelectionRange(position, position)
+ this.input.setSelectionRange(position, position)
this.caret = position
})
},
@@ -285,9 +285,9 @@ const EmojiInput = {
this.$nextTick(function () {
// Re-focus inputbox after clicking suggestion
- this.input.elm.focus()
+ this.input.focus()
// Set selection right after the replacement instead of the very end
- this.input.elm.setSelectionRange(position, position)
+ this.input.setSelectionRange(position, position)
this.caret = position
})
e.preventDefault()
@@ -349,7 +349,7 @@ const EmojiInput = {
}
this.$nextTick(() => {
- const { offsetHeight } = this.input.elm
+ const { offsetHeight } = this.input
const { picker } = this.$refs
const pickerBottom = picker.$el.getBoundingClientRect().bottom
if (pickerBottom > window.innerHeight) {
@@ -414,8 +414,8 @@ const EmojiInput = {
// Scroll the input element to the position of the cursor
this.$nextTick(() => {
- this.input.elm.blur()
- this.input.elm.focus()
+ this.input.blur()
+ this.input.focus()
})
}
// Disable suggestions hotkeys if suggestions are hidden
@@ -444,7 +444,7 @@ const EmojiInput = {
// de-focuses the element (i.e. default browser behavior)
if (key === 'Escape') {
if (!this.temporarilyHideSuggestions) {
- this.input.elm.focus()
+ this.input.focus()
}
}
@@ -480,7 +480,7 @@ const EmojiInput = {
if (!panel) return
const picker = this.$refs.picker.$el
const panelBody = this.$refs['panel-body']
- const { offsetHeight, offsetTop } = this.input.elm
+ const { offsetHeight, offsetTop } = this.input
const offsetBottom = offsetTop + offsetHeight
this.setPlacement(panelBody, panel, offsetBottom)
@@ -494,7 +494,7 @@ const EmojiInput = {
if (this.placement === 'top' || (this.placement === 'auto' && this.overflowsBottom(container))) {
target.style.top = 'auto'
- target.style.bottom = this.input.elm.offsetHeight + 'px'
+ target.style.bottom = this.input.offsetHeight + 'px'
}
},
overflowsBottom (el) {
diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue
index ad62484d..e6f9a9d3 100644
--- a/src/components/emoji_input/emoji_input.vue
+++ b/src/components/emoji_input/emoji_input.vue
@@ -3,6 +3,7 @@
v-click-outside="onClickOutside"
class="emoji-input"
:class="{ 'with-picker': !hideEmojiButton }"
+ ref='root'
>
diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue
index c6cb9fbe..a3c3c767 100644
--- a/src/components/extra_buttons/extra_buttons.vue
+++ b/src/components/extra_buttons/extra_buttons.vue
@@ -7,10 +7,7 @@
:bound-to="{ x: 'container' }"
remove-padding
>
-
+
-
-
-
-
+
+
+
+
diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js
index 8b142d08..d177efeb 100644
--- a/src/components/features_panel/features_panel.js
+++ b/src/components/features_panel/features_panel.js
@@ -2,7 +2,7 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for
const FeaturesPanel = {
computed: {
- chat: function () { return this.$store.state.instance.chatAvailable },
+ shout: function () { return this.$store.state.instance.shoutAvailable },
pleromaChatMessages: function () { return this.$store.state.instance.pleromaChatMessagesAvailable },
gopher: function () { return this.$store.state.instance.gopherAvailable },
whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled },
diff --git a/src/components/features_panel/features_panel.vue b/src/components/features_panel/features_panel.vue
index 9605d09d..a58a99af 100644
--- a/src/components/features_panel/features_panel.vue
+++ b/src/components/features_panel/features_panel.vue
@@ -8,8 +8,8 @@
- -
- {{ $t('features_panel.chat') }}
+
-
+ {{ $t('features_panel.shout') }}
-
{{ $t('features_panel.pleroma_chat_messages') }}
diff --git a/src/components/flash/flash.js b/src/components/flash/flash.js
new file mode 100644
index 00000000..d03384c7
--- /dev/null
+++ b/src/components/flash/flash.js
@@ -0,0 +1,52 @@
+import RuffleService from '../../services/ruffle_service/ruffle_service.js'
+import { library } from '@fortawesome/fontawesome-svg-core'
+import {
+ faStop,
+ faExclamationTriangle
+} from '@fortawesome/free-solid-svg-icons'
+
+library.add(
+ faStop,
+ faExclamationTriangle
+)
+
+const Flash = {
+ props: [ 'src' ],
+ data () {
+ return {
+ player: false, // can be true, "hidden", false. hidden = element exists
+ loaded: false,
+ ruffleInstance: null
+ }
+ },
+ methods: {
+ openPlayer () {
+ if (this.player) return // prevent double-loading, or re-loading on failure
+ this.player = 'hidden'
+ RuffleService.getRuffle().then((ruffle) => {
+ const player = ruffle.newest().createPlayer()
+ player.config = {
+ letterbox: 'on'
+ }
+ const container = this.$refs.container
+ container.appendChild(player)
+ player.style.width = '100%'
+ player.style.height = '100%'
+ player.load(this.src).then(() => {
+ this.player = true
+ }).catch((e) => {
+ console.error('Error loading ruffle', e)
+ this.player = 'error'
+ })
+ this.ruffleInstance = player
+ })
+ },
+ closePlayer () {
+ console.log(this.ruffleInstance)
+ this.ruffleInstance.remove()
+ this.player = false
+ }
+ }
+}
+
+export default Flash
diff --git a/src/components/flash/flash.vue b/src/components/flash/flash.vue
new file mode 100644
index 00000000..d20d037b
--- /dev/null
+++ b/src/components/flash/flash.vue
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js
index 6274780b..137ef9c0 100644
--- a/src/components/font_control/font_control.js
+++ b/src/components/font_control/font_control.js
@@ -1,14 +1,10 @@
import { set } from 'vue'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
- faChevronDown
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
- faChevronDown
-)
+import Select from '../select/select.vue'
export default {
+ components: {
+ Select
+ },
props: [
'name', 'label', 'value', 'fallback', 'options', 'no-inherit'
],
diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue
index dd117ec0..29605084 100644
--- a/src/components/font_control/font_control.vue
+++ b/src/components/font_control/font_control.vue
@@ -22,30 +22,20 @@
class="opt-l"
:for="name + '-o'"
/>
-
+ {{ option === 'custom' ? $t('settings.style.fonts.custom') : option }}
+
+
{{ $t('settings.interfaceLanguage') }}
-
+ {{ lang.name }}
+
+
@@ -32,16 +23,12 @@ import languagesObject from '../../i18n/messages'
import localeService from '../../services/locale/locale.service.js'
import ISO6391 from 'iso-639-1'
import _ from 'lodash'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
- faChevronDown
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
- faChevronDown
-)
+import Select from '../select/select.vue'
export default {
+ components: {
+ Select
+ },
computed: {
languages () {
return _.map(languagesObject.languages, (code) => ({ code: code, name: this.getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name))
diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue
index c4c6ee46..96476abe 100644
--- a/src/components/moderation_tools/moderation_tools.vue
+++ b/src/components/moderation_tools/moderation_tools.vue
@@ -8,7 +8,7 @@
@show="setToggled(true)"
@close="setToggled(false)"
>
-
+
-
-
+
+
+
+
-
+
{{ $t('user_card.admin_menu.delete_user') }}
{{ $t('user_card.admin_menu.delete_user_confirmation') }}
-
+
diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js
new file mode 100644
index 00000000..1dda49f2
--- /dev/null
+++ b/src/components/settings_modal/helpers/boolean_setting.js
@@ -0,0 +1,30 @@
+import { get, set } from 'lodash'
+import Checkbox from 'src/components/checkbox/checkbox.vue'
+import ModifiedIndicator from './modified_indicator.vue'
+export default {
+ components: {
+ Checkbox,
+ ModifiedIndicator
+ },
+ props: [
+ 'path',
+ 'disabled'
+ ],
+ computed: {
+ pathDefault () {
+ const [firstSegment, ...rest] = this.path.split('.')
+ return [firstSegment + 'DefaultValue', ...rest].join('.')
+ },
+ state () {
+ return get(this.$parent, this.path)
+ },
+ isChanged () {
+ return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault)
+ }
+ },
+ methods: {
+ update (e) {
+ set(this.$parent, this.path, e)
+ }
+ }
+}
diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue
index 146ad6c1..c3ee6583 100644
--- a/src/components/settings_modal/helpers/boolean_setting.vue
+++ b/src/components/settings_modal/helpers/boolean_setting.vue
@@ -18,40 +18,4 @@
-
-
-
+
diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js
new file mode 100644
index 00000000..042e8106
--- /dev/null
+++ b/src/components/settings_modal/helpers/choice_setting.js
@@ -0,0 +1,34 @@
+import { get, set } from 'lodash'
+import Select from 'src/components/select/select.vue'
+import ModifiedIndicator from './modified_indicator.vue'
+export default {
+ components: {
+ Select,
+ ModifiedIndicator
+ },
+ props: [
+ 'path',
+ 'disabled',
+ 'options'
+ ],
+ computed: {
+ pathDefault () {
+ const [firstSegment, ...rest] = this.path.split('.')
+ return [firstSegment + 'DefaultValue', ...rest].join('.')
+ },
+ state () {
+ return get(this.$parent, this.path)
+ },
+ defaultState () {
+ return get(this.$parent, this.pathDefault)
+ },
+ isChanged () {
+ return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault)
+ }
+ },
+ methods: {
+ update (e) {
+ set(this.$parent, this.path, e)
+ }
+ }
+}
diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue
new file mode 100644
index 00000000..fa17661b
--- /dev/null
+++ b/src/components/settings_modal/helpers/choice_setting.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
diff --git a/src/components/settings_modal/helpers/modified_indicator.vue b/src/components/settings_modal/helpers/modified_indicator.vue
index 9f4e81fe..ad212db9 100644
--- a/src/components/settings_modal/helpers/modified_indicator.vue
+++ b/src/components/settings_modal/helpers/modified_indicator.vue
@@ -6,18 +6,18 @@
-
+
-
-
- {{ $t('settings.setting_changed') }}
-
+
+
+
+ {{ $t('settings.setting_changed') }}
+
+
diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue
index c7da5433..583c2ecc 100644
--- a/src/components/settings_modal/settings_modal.vue
+++ b/src/components/settings_modal/settings_modal.vue
@@ -62,20 +62,18 @@
:bound-to="{ x: 'container' }"
remove-padding
>
-
- {{ $t("settings.file_export_import.backup_restore") }}
-
-
-
+
+
+ {{ $t("settings.file_export_import.backup_restore") }}
+
+
+
+
-
+
diff --git a/src/components/settings_modal/settings_modal_content.scss b/src/components/settings_modal/settings_modal_content.scss
index f066234c..81ab434b 100644
--- a/src/components/settings_modal/settings_modal_content.scss
+++ b/src/components/settings_modal/settings_modal_content.scss
@@ -7,13 +7,24 @@
margin: 1em 1em 1.4em;
padding-bottom: 1.4em;
- > div {
+ > div,
+ > label {
+ display: block;
margin-bottom: .5em;
&:last-child {
margin-bottom: 0;
}
}
+ .select-multiple {
+ display: flex;
+
+ .option-list {
+ margin: 0;
+ padding-left: .5em;
+ }
+ }
+
&:last-child {
border-bottom: none;
padding-bottom: 0;
diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js
index 6e95f7af..4eaf4217 100644
--- a/src/components/settings_modal/tabs/filtering_tab.js
+++ b/src/components/settings_modal/tabs/filtering_tab.js
@@ -1,24 +1,23 @@
import { filter, trim } from 'lodash'
import BooleanSetting from '../helpers/boolean_setting.vue'
+import ChoiceSetting from '../helpers/choice_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
- faChevronDown
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
- faChevronDown
-)
const FilteringTab = {
data () {
return {
- muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n')
+ muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n'),
+ replyVisibilityOptions: ['all', 'following', 'self'].map(mode => ({
+ key: mode,
+ value: mode,
+ label: this.$t(`settings.reply_visibility_${mode}`)
+ }))
}
},
components: {
- BooleanSetting
+ BooleanSetting,
+ ChoiceSetting
},
computed: {
...SharedComputedObject(),
diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue
index 402c2a4a..6fc9ceaa 100644
--- a/src/components/settings_modal/tabs/filtering_tab.vue
+++ b/src/components/settings_modal/tabs/filtering_tab.vue
@@ -36,29 +36,13 @@
-
+
{{ $t('settings.replies_in_timeline') }}
-
-
+
{{ $t('settings.hide_post_stats') }}
diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js
index 2db523be..eeda61bf 100644
--- a/src/components/settings_modal/tabs/general_tab.js
+++ b/src/components/settings_modal/tabs/general_tab.js
@@ -1,21 +1,25 @@
import BooleanSetting from '../helpers/boolean_setting.vue'
+import ChoiceSetting from '../helpers/choice_setting.vue'
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
import { library } from '@fortawesome/fontawesome-svg-core'
import {
- faChevronDown,
faGlobe
} from '@fortawesome/free-solid-svg-icons'
library.add(
- faChevronDown,
faGlobe
)
const GeneralTab = {
data () {
return {
+ subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({
+ key: mode,
+ value: mode,
+ label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`)
+ })),
loopSilentAvailable:
// Firefox
Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') ||
@@ -27,17 +31,26 @@ const GeneralTab = {
},
components: {
BooleanSetting,
+ ChoiceSetting,
InterfaceLanguageSwitcher
},
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}"]`)
+ }))
+ },
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
instanceWallpaperUsed () {
return this.$store.state.instance.background &&
!this.$store.state.users.currentUser.background_image
},
+ instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable },
...SharedComputedObject()
}
}
diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue
index d867f16a..f81451c6 100644
--- a/src/components/settings_modal/tabs/general_tab.vue
+++ b/src/components/settings_modal/tabs/general_tab.vue
@@ -16,6 +16,11 @@
{{ $t('settings.show_third_column') }}
+
+
+ {{ $t('settings.right_sidebar') }}
+
+
{{ $t('settings.hide_wallpaper') }}
@@ -31,6 +36,11 @@
{{ $t('settings.compact_user_panel') }}
+
+
+ {{ $t('settings.hide_shoutbox') }}
+
+
@@ -100,62 +110,22 @@
-
+
{{ $t('settings.subject_line_behavior') }}
-
-
+
-
+
{{ $t('settings.post_status_content_type') }}
-
-
+
diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
index 63d36bf9..32a21415 100644
--- a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
+++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue
@@ -10,20 +10,18 @@
:query="queryUserIds"
:placeholder="$t('settings.search_user_to_block')"
>
-
+
+
+
-
+
{{ $t('user_card.block') }}
-
+
{{ $t('user_card.block_progress') }}
@@ -41,19 +39,16 @@
:click="() => unblockUsers(selected)"
>
{{ $t('user_card.unblock') }}
-
+
{{ $t('user_card.unblock_progress') }}
-
+
-
+
{{ $t('settings.no_blocks') }}
@@ -68,20 +63,18 @@
:query="queryUserIds"
:placeholder="$t('settings.search_user_to_mute')"
>
-
+
+
+
-
+
{{ $t('user_card.mute') }}
-
+
{{ $t('user_card.mute_progress') }}
@@ -99,19 +92,16 @@
:click="() => unmuteUsers(selected)"
>
{{ $t('user_card.unmute') }}
-
+
{{ $t('user_card.unmute_progress') }}
-
+
-
+
{{ $t('settings.no_mutes') }}
@@ -124,20 +114,18 @@
:query="queryKnownDomains"
:placeholder="$t('settings.type_domains_to_mute')"
>
-
+
+
+
-
+
{{ $t('domain_mute_card.unmute') }}
-
+
{{ $t('domain_mute_card.unmute_progress') }}
-
+
-
+
{{ $t('settings.no_mutes') }}
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js
index 8960c566..1388f74b 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.js
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js
@@ -36,16 +36,9 @@ import FontControl from 'src/components/font_control/font_control.vue'
import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
import Checkbox from 'src/components/checkbox/checkbox.vue'
+import Select from 'src/components/select/select.vue'
import Preview from './preview.vue'
-import { library } from '@fortawesome/fontawesome-svg-core'
-import {
- faChevronDown
-} from '@fortawesome/free-solid-svg-icons'
-
-library.add(
- faChevronDown
-)
// List of color values used in v1
const v1OnlyNames = [
@@ -395,7 +388,8 @@ export default {
FontControl,
TabSwitcher,
Preview,
- Checkbox
+ Checkbox,
+ Select
},
methods: {
loadTheme (
diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
index 62378867..548dc852 100644
--- a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
+++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue
@@ -55,7 +55,7 @@
for="preset-switcher"
class="select"
>
-
-
+
@@ -907,28 +903,19 @@
{{ $t('settings.style.shadows.component') }}
-
+ {{ $t('settings.style.shadows.components.' + shadow) }}
+
+