diff --git a/src/App.scss b/src/App.scss index 7db9461c..120eea53 100644 --- a/src/App.scss +++ b/src/App.scss @@ -860,54 +860,6 @@ nav { } } -// DELETE -.setting-item { - border-bottom: 2px solid var(--fg, $fallback--fg); - margin: 1em 1em 1.4em; - padding-bottom: 1.4em; - - > div { - margin-bottom: .5em; - &:last-child { - margin-bottom: 0; - } - } - - &:last-child { - border-bottom: none; - padding-bottom: 0; - margin-bottom: 1em; - } - - select { - min-width: 10em; - } - - - textarea { - width: 100%; - max-width: 100%; - height: 100px; - } - - .unavailable, - .unavailable i { - color: var(--cRed, $fallback--cRed); - color: $fallback--cRed; - } - - .btn { - min-height: 28px; - min-width: 10em; - padding: 0 2em; - } - - .number-input { - max-width: 6em; - } -} -// DELETE - .select-multiple { display: flex; .option-list { diff --git a/src/components/modal/modal.vue b/src/components/modal/modal.vue index cee24241..e5ecc0c0 100644 --- a/src/components/modal/modal.vue +++ b/src/components/modal/modal.vue @@ -3,6 +3,7 @@ v-show="isOpen" v-body-scroll-lock="isOpen" class="modal-view" + :class="{ 'modal-background': !noBackground }" @click.self="$emit('backdropClicked')" > <slot /> @@ -15,6 +16,10 @@ export default { isOpen: { type: Boolean, default: true + }, + noBackground: { + type: Boolean, + default: false } } } @@ -32,10 +37,19 @@ export default { justify-content: center; align-items: center; overflow: auto; + pointer-events: none; animation-duration: 0.2s; - background-color: rgba(0, 0, 0, 0.5); animation-name: modal-background-fadein; + > * { + pointer-events: initial; + } + + &.modal-background { + pointer-events: initial; + background-color: rgba(0, 0, 0, 0.5); + } + body:not(.scroll-locked) & { opacity: 0; } diff --git a/src/components/settings/settings.js b/src/components/settings/settings.js deleted file mode 100644 index 31a9e9be..00000000 --- a/src/components/settings/settings.js +++ /dev/null @@ -1,128 +0,0 @@ -/* eslint-env browser */ -import { filter, trim } from 'lodash' - -import TabSwitcher from '../tab_switcher/tab_switcher.js' -import StyleSwitcher from '../style_switcher/style_switcher.vue' -import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue' -import { extractCommit } from '../../services/version/version.service' -import { instanceDefaultProperties, defaultState as configDefaultState } from '../../modules/config.js' -import Checkbox from '../checkbox/checkbox.vue' - -const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/' -const pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/' - -const multiChoiceProperties = [ - 'postContentType', - 'subjectLineBehavior' -] - -const settings = { - data () { - const instance = this.$store.state.instance - - return { - 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'), - - backendVersion: instance.backendVersion, - frontendVersion: instance.frontendVersion - } - }, - components: { - TabSwitcher, - StyleSwitcher, - InterfaceLanguageSwitcher, - Checkbox - }, - computed: { - user () { - return this.$store.state.users.currentUser - }, - currentSaveStateNotice () { - return this.$store.state.interface.settings.currentSaveStateNotice - }, - postFormats () { - return this.$store.state.instance.postFormats || [] - }, - instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, - frontendVersionLink () { - return pleromaFeCommitUrl + this.frontendVersion - }, - backendVersionLink () { - return pleromaBeCommitUrl + extractCommit(this.backendVersion) - }, - // Getting localized values for instance-default properties - ...instanceDefaultProperties - .filter(key => multiChoiceProperties.includes(key)) - .map(key => [ - key + 'DefaultValue', - function () { - return this.$store.getters.instanceDefaultConfig[key] - } - ]) - .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), - ...instanceDefaultProperties - .filter(key => !multiChoiceProperties.includes(key)) - .map(key => [ - key + 'LocalizedValue', - function () { - return this.$t('settings.values.' + this.$store.getters.instanceDefaultConfig[key]) - } - ]) - .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), - // Generating computed values for vuex properties - ...Object.keys(configDefaultState) - .map(key => [key, { - get () { return this.$store.getters.mergedConfig[key] }, - set (value) { - this.$store.dispatch('setOption', { name: key, value }) - } - }]) - .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), - // Special cases (need to transform values or perform actions first) - muteWordsString: { - get () { return this.$store.getters.mergedConfig.muteWords.join('\n') }, - set (value) { - this.$store.dispatch('setOption', { - name: 'muteWords', - value: filter(value.split('\n'), (word) => trim(word).length > 0) - }) - } - }, - useStreamingApi: { - get () { return this.$store.getters.mergedConfig.useStreamingApi }, - set (value) { - const promise = value - ? this.$store.dispatch('enableMastoSockets') - : this.$store.dispatch('disableMastoSockets') - - promise.then(() => { - this.$store.dispatch('setOption', { name: 'useStreamingApi', value }) - }).catch((e) => { - console.error('Failed starting MastoAPI Streaming socket', e) - this.$store.dispatch('disableMastoSockets') - this.$store.dispatch('setOption', { name: 'useStreamingApi', value: false }) - }) - } - } - }, - // Updating nested properties - watch: { - notificationVisibility: { - handler (value) { - this.$store.dispatch('setOption', { - name: 'notificationVisibility', - value: this.$store.getters.mergedConfig.notificationVisibility - }) - }, - deep: true - } - } -} - -export default settings diff --git a/src/components/settings/settings.vue b/src/components/settings/settings.vue deleted file mode 100644 index 9e14b449..00000000 --- a/src/components/settings/settings.vue +++ /dev/null @@ -1,424 +0,0 @@ -<template> - <div class="settings panel panel-default"> - <div class="panel-heading"> - <div class="title"> - {{ $t('settings.settings') }} - </div> - - <transition name="fade"> - <template v-if="currentSaveStateNotice"> - <div - v-if="currentSaveStateNotice.error" - class="alert error" - @click.prevent - > - {{ $t('settings.saving_err') }} - </div> - - <div - v-if="!currentSaveStateNotice.error" - class="alert transparent" - @click.prevent - > - {{ $t('settings.saving_ok') }} - </div> - </template> - </transition> - </div> - <div class="panel-body"> - <keep-alive> - <tab-switcher> - <div :label="$t('settings.general')"> - <div class="setting-item"> - <h2>{{ $t('settings.interface') }}</h2> - <ul class="setting-list"> - <li> - <interface-language-switcher /> - </li> - <li v-if="instanceSpecificPanelPresent"> - <Checkbox v-model="hideISP"> - {{ $t('settings.hide_isp') }} - </Checkbox> - </li> - </ul> - </div> - <div class="setting-item"> - <h2>{{ $t('nav.timeline') }}</h2> - <ul class="setting-list"> - <li> - <Checkbox v-model="hideMutedPosts"> - {{ $t('settings.hide_muted_posts') }} {{ $t('settings.instance_default', { value: hideMutedPostsLocalizedValue }) }} - </Checkbox> - </li> - <li> - <Checkbox v-model="collapseMessageWithSubject"> - {{ $t('settings.collapse_subject') }} {{ $t('settings.instance_default', { value: collapseMessageWithSubjectLocalizedValue }) }} - </Checkbox> - </li> - <li> - <Checkbox v-model="streaming"> - {{ $t('settings.streaming') }} - </Checkbox> - <ul - class="setting-list suboptions" - :class="[{disabled: !streaming}]" - > - <li> - <Checkbox - v-model="pauseOnUnfocused" - :disabled="!streaming" - > - {{ $t('settings.pause_on_unfocused') }} - </Checkbox> - </li> - </ul> - </li> - <li> - <Checkbox v-model="useStreamingApi"> - {{ $t('settings.useStreamingApi') }} - <br> - <small> - {{ $t('settings.useStreamingApiWarning') }} - </small> - </Checkbox> - </li> - <li> - <Checkbox v-model="autoLoad"> - {{ $t('settings.autoload') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="hoverPreview"> - {{ $t('settings.reply_link_preview') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="emojiReactionsOnTimeline"> - {{ $t('settings.emoji_reactions_on_timeline') }} - </Checkbox> - </li> - </ul> - </div> - - <div class="setting-item"> - <h2>{{ $t('settings.composing') }}</h2> - <ul class="setting-list"> - <li> - <Checkbox v-model="scopeCopy"> - {{ $t('settings.scope_copy') }} {{ $t('settings.instance_default', { value: scopeCopyLocalizedValue }) }} - </Checkbox> - </li> - <li> - <Checkbox v-model="alwaysShowSubjectInput"> - {{ $t('settings.subject_input_always_show') }} {{ $t('settings.instance_default', { value: alwaysShowSubjectInputLocalizedValue }) }} - </Checkbox> - </li> - <li> - <div> - {{ $t('settings.subject_line_behavior') }} - <label - for="subjectLineBehavior" - class="select" - > - <select - id="subjectLineBehavior" - v-model="subjectLineBehavior" - > - <option value="email"> - {{ $t('settings.subject_line_email') }} - {{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }} - </option> - <option value="masto"> - {{ $t('settings.subject_line_mastodon') }} - {{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }} - </option> - <option value="noop"> - {{ $t('settings.subject_line_noop') }} - {{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }} - </option> - </select> - <i class="icon-down-open" /> - </label> - </div> - </li> - <li v-if="postFormats.length > 0"> - <div> - {{ $t('settings.post_status_content_type') }} - <label - for="postContentType" - class="select" - > - <select - id="postContentType" - v-model="postContentType" - > - <option - v-for="postFormat in postFormats" - :key="postFormat" - :value="postFormat" - > - {{ $t(`post_status.content_type["${postFormat}"]`) }} - {{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }} - </option> - </select> - <i class="icon-down-open" /> - </label> - </div> - </li> - <li> - <Checkbox v-model="minimalScopesMode"> - {{ $t('settings.minimal_scopes_mode') }} {{ $t('settings.instance_default', { value: minimalScopesModeLocalizedValue }) }} - </Checkbox> - </li> - <li> - <Checkbox v-model="autohideFloatingPostButton"> - {{ $t('settings.autohide_floating_post_button') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="padEmoji"> - {{ $t('settings.pad_emoji') }} - </Checkbox> - </li> - </ul> - </div> - - <div class="setting-item"> - <h2>{{ $t('settings.attachments') }}</h2> - <ul class="setting-list"> - <li> - <Checkbox v-model="hideAttachments"> - {{ $t('settings.hide_attachments_in_tl') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="hideAttachmentsInConv"> - {{ $t('settings.hide_attachments_in_convo') }} - </Checkbox> - </li> - <li> - <label for="maxThumbnails"> - {{ $t('settings.max_thumbnails') }} - </label> - <input - id="maxThumbnails" - v-model.number="maxThumbnails" - class="number-input" - type="number" - min="0" - step="1" - > - </li> - <li> - <Checkbox v-model="hideNsfw"> - {{ $t('settings.nsfw_clickthrough') }} - </Checkbox> - </li> - <ul class="setting-list suboptions"> - <li> - <Checkbox - v-model="preloadImage" - :disabled="!hideNsfw" - > - {{ $t('settings.preload_images') }} - </Checkbox> - </li> - <li> - <Checkbox - v-model="useOneClickNsfw" - :disabled="!hideNsfw" - > - {{ $t('settings.use_one_click_nsfw') }} - </Checkbox> - </li> - </ul> - <li> - <Checkbox v-model="stopGifs"> - {{ $t('settings.stop_gifs') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="loopVideo"> - {{ $t('settings.loop_video') }} - </Checkbox> - <ul - class="setting-list suboptions" - :class="[{disabled: !streaming}]" - > - <li> - <Checkbox - v-model="loopVideoSilentOnly" - :disabled="!loopVideo || !loopSilentAvailable" - > - {{ $t('settings.loop_video_silent_only') }} - </Checkbox> - <div - v-if="!loopSilentAvailable" - class="unavailable" - > - <i class="icon-globe" />! {{ $t('settings.limited_availability') }} - </div> - </li> - </ul> - </li> - <li> - <Checkbox v-model="playVideosInModal"> - {{ $t('settings.play_videos_in_modal') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="useContainFit"> - {{ $t('settings.use_contain_fit') }} - </Checkbox> - </li> - </ul> - </div> - - <div class="setting-item"> - <h2>{{ $t('settings.notifications') }}</h2> - <ul class="setting-list"> - <li> - <Checkbox v-model="webPushNotifications"> - {{ $t('settings.enable_web_push_notifications') }} - </Checkbox> - </li> - </ul> - </div> - - <div class="setting-item"> - <h2>{{ $t('settings.fun') }}</h2> - <ul class="setting-list"> - <li> - <Checkbox v-model="greentext"> - {{ $t('settings.greentext') }} {{ $t('settings.instance_default', { value: greentextLocalizedValue }) }} - </Checkbox> - </li> - </ul> - </div> - </div> - - <div :label="$t('settings.theme')"> - <div class="setting-item"> - <style-switcher /> - </div> - </div> - - <div :label="$t('settings.filtering')"> - <div class="setting-item"> - <div class="select-multiple"> - <span class="label">{{ $t('settings.notification_visibility') }}</span> - <ul class="option-list"> - <li> - <Checkbox v-model="notificationVisibility.likes"> - {{ $t('settings.notification_visibility_likes') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="notificationVisibility.repeats"> - {{ $t('settings.notification_visibility_repeats') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="notificationVisibility.follows"> - {{ $t('settings.notification_visibility_follows') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="notificationVisibility.mentions"> - {{ $t('settings.notification_visibility_mentions') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="notificationVisibility.moves"> - {{ $t('settings.notification_visibility_moves') }} - </Checkbox> - </li> - <li> - <Checkbox v-model="notificationVisibility.emojiReactions"> - {{ $t('settings.notification_visibility_emoji_reactions') }} - </Checkbox> - </li> - </ul> - </div> - <div> - {{ $t('settings.replies_in_timeline') }} - <label - for="replyVisibility" - class="select" - > - <select - id="replyVisibility" - v-model="replyVisibility" - > - <option - value="all" - selected - >{{ $t('settings.reply_visibility_all') }}</option> - <option value="following">{{ $t('settings.reply_visibility_following') }}</option> - <option value="self">{{ $t('settings.reply_visibility_self') }}</option> - </select> - <i class="icon-down-open" /> - </label> - </div> - <div> - <Checkbox v-model="hidePostStats"> - {{ $t('settings.hide_post_stats') }} {{ $t('settings.instance_default', { value: hidePostStatsLocalizedValue }) }} - </Checkbox> - </div> - <div> - <Checkbox v-model="hideUserStats"> - {{ $t('settings.hide_user_stats') }} {{ $t('settings.instance_default', { value: hideUserStatsLocalizedValue }) }} - </Checkbox> - </div> - </div> - <div class="setting-item"> - <div> - <p>{{ $t('settings.filtering_explanation') }}</p> - <textarea - id="muteWords" - v-model="muteWordsString" - /> - </div> - <div> - <Checkbox v-model="hideFilteredStatuses"> - {{ $t('settings.hide_filtered_statuses') }} {{ $t('settings.instance_default', { value: hideFilteredStatusesLocalizedValue }) }} - </Checkbox> - </div> - </div> - </div> - <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="backendVersionLink" - 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> - </tab-switcher> - </keep-alive> - </div> - </div> -</template> - -<script src="./settings.js"> -</script> diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js index 1f4c038f..d60babf6 100644 --- a/src/components/settings_modal/settings_modal.js +++ b/src/components/settings_modal/settings_modal.js @@ -1,21 +1,30 @@ -import Modal from '../modal/modal.vue' -import TabSwitcher from '../tab_switcher/tab_switcher.js' +import Modal from 'src/components/modal/modal.vue' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' -import Profile from './tabs/profile.vue' -import Security from './tabs/security.vue' -import Notifications from './tabs/notifications.vue' -import DataImportExport from './tabs/data_import_export.vue' -import MutesAndBlocks from './tabs/mutes_and_blocks.vue' +import DataImportExportTab from './tabs/data_import_export_tab.vue' +import MutesAndBlocksTab from './tabs/mutes_and_blocks_tab.vue' +import NotificationsTab from './tabs/notifications_tab.vue' +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 VersionTab from './tabs/version_tab.vue' +import ThemeTab from './tabs/theme_tab/theme_tab.vue' const SettingsModal = { components: { Modal, TabSwitcher, - Profile, - Security, - Notifications, - DataImportExport, - MutesAndBlocks + + DataImportExportTab, + MutesAndBlocksTab, + NotificationsTab, + FilteringTab, + SecurityTab, + ProfileTab, + GeneralTab, + VersionTab, + ThemeTab }, data () { return { @@ -28,11 +37,20 @@ const SettingsModal = { }, modalActivated () { return this.$store.state.interface.settingsModalState !== 'hidden' + }, + modalPeeked () { + return this.$store.state.interface.settingsModalState === 'minimized' } }, watch: { }, methods: { + closeModal () { + this.$store.dispatch('closeSettingsModal') + }, + peekModal () { + this.$store.dispatch('togglePeekSettingsModal') + } } } diff --git a/src/components/settings_modal/settings_modal.scss b/src/components/settings_modal/settings_modal.scss index 8cea52d2..3efbe205 100644 --- a/src/components/settings_modal/settings_modal.scss +++ b/src/components/settings_modal/settings_modal.scss @@ -1,14 +1,29 @@ -@import '../../_variables.scss'; +@import 'src/_variables.scss'; .settings-modal { + .settings_tab-switcher { height: 100%; } + &.peek { + .settings-modal-panel { + transform: translateY(calc(100% - 50px)); + } + } + .settings-modal-panel { + transition: transform; + transition-timing-function: ease-in-out; + transition-duration: 300ms; width: 1000px; max-width: 90vw; height: 90vh; + @media all and (max-width: 800px) { + max-width: 100vw; + height: 100vh; + } } .panel-body { + height: 100%; overflow-y: hidden; } .setting-item { @@ -16,6 +31,12 @@ margin: 1em 1em 1.4em; padding-bottom: 1.4em; + .btn { + min-height: 28px; + min-width: 10em; + padding: 0 2em; + } + > div { margin-bottom: .5em; &:last-child { @@ -33,7 +54,6 @@ min-width: 10em; } - textarea { width: 100%; max-width: 100%; @@ -46,12 +66,6 @@ color: $fallback--cRed; } - .btn { - min-height: 28px; - min-width: 10em; - padding: 0 2em; - } - .number-input { max-width: 6em; } diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue index 9e35d3f6..53481bdd 100644 --- a/src/components/settings_modal/settings_modal.vue +++ b/src/components/settings_modal/settings_modal.vue @@ -3,10 +3,20 @@ v-if="isLoggedIn && !resettingForm" :is-open="modalActivated" class="settings-modal" + :class="{ peek: modalPeeked }" + :no-background="modalPeeked" > <div class="settings-modal-panel panel"> <div class="panel-heading"> - {{ $t('settings.settings') }} + <span class="title"> + {{ $t('settings.settings') }} + </span> + <button class="btn" @click="peekModal"> + {{ $t('general.peek') }} + </button> + <button class="btn" @click="closeModal"> + {{ $t('general.close') }} + </button> </div> <div class="panel-body"> <tab-switcher @@ -15,11 +25,15 @@ :scrollableTabs="true" ref="tabSwitcher" > - <div :label="$t('settings.profile_tab')"><Profile /></div> - <div :label="$t('settings.security_tab')"><Security /></div> - <div :label="$t('settings.notifications')"><Notifications /></div> - <div :label="$t('settings.data_import_export_tab')"><DataImportExport /></div> - <div :label="$t('settings.mutes_and_blocks')"><MutesAndBlocks /></div> + <div :label="$t('settings.general')"><GeneralTab /></div> + <div :label="$t('settings.profile_tab')"><ProfileTab /></div> + <div :label="$t('settings.security_tab')"><SecurityTab /></div> + <div :label="$t('settings.filtering')"><FilteringTab /></div> + <div :label="$t('settings.theme')"><ThemeTab /></div> + <div :label="$t('settings.notifications')"><NotificationsTab /></div> + <div :label="$t('settings.data_import_export_tab')"><DataImportExportTab /></div> + <div :label="$t('settings.mutes_and_blocks')"><MutesAndBlocksTab /></div> + <div :label="$t('settings.version.title')"><VersionTab /></div> </tab-switcher> </div> </div> diff --git a/src/components/settings_modal/tabs/data_import_export.js b/src/components/settings_modal/tabs/data_import_export_tab.js similarity index 86% rename from src/components/settings_modal/tabs/data_import_export.js rename to src/components/settings_modal/tabs/data_import_export_tab.js index f68d12e9..168f89e1 100644 --- a/src/components/settings_modal/tabs/data_import_export.js +++ b/src/components/settings_modal/tabs/data_import_export_tab.js @@ -1,8 +1,8 @@ -import Importer from '../../importer/importer.vue' -import Exporter from '../../exporter/exporter.vue' -import Checkbox from '../../checkbox/checkbox.vue' +import Importer from 'src/components/importer/importer.vue' +import Exporter from 'src/components/exporter/exporter.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' -const DataImportExport = { +const DataImportExportTab = { data () { return { activeTab: 'profile', @@ -62,4 +62,4 @@ const DataImportExport = { } } -export default DataImportExport +export default DataImportExportTab diff --git a/src/components/settings_modal/tabs/data_import_export.vue b/src/components/settings_modal/tabs/data_import_export_tab.vue similarity index 96% rename from src/components/settings_modal/tabs/data_import_export.vue rename to src/components/settings_modal/tabs/data_import_export_tab.vue index 464df6d3..3ddc8b03 100644 --- a/src/components/settings_modal/tabs/data_import_export.vue +++ b/src/components/settings_modal/tabs/data_import_export_tab.vue @@ -39,5 +39,5 @@ </div> </template> -<script src="./data_import_export.js"></script> +<script src="./data_import_export_tab.js"></script> <!-- <style lang="scss" src="./profile.scss"></style> --> diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js new file mode 100644 index 00000000..ec330667 --- /dev/null +++ b/src/components/settings_modal/tabs/filtering_tab.js @@ -0,0 +1,26 @@ +import Checkbox from 'src/components/checkbox/checkbox.vue' + +import SharedComputedObject from './helpers/shared_computed_object.js' + +const FilteringTab = { + components: { + Checkbox + }, + computed: { + ...SharedComputedObject() + }, + // Updating nested properties + watch: { + notificationVisibility: { + handler (value) { + this.$store.dispatch('setOption', { + name: 'notificationVisibility', + value: this.$store.getters.mergedConfig.notificationVisibility + }) + }, + deep: true + } + } +} + +export default FilteringTab diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue new file mode 100644 index 00000000..647ec7b4 --- /dev/null +++ b/src/components/settings_modal/tabs/filtering_tab.vue @@ -0,0 +1,86 @@ +<template> +<div :label="$t('settings.filtering')"> + <div class="setting-item"> + <div class="select-multiple"> + <span class="label">{{ $t('settings.notification_visibility') }}</span> + <ul class="option-list"> + <li> + <Checkbox v-model="notificationVisibility.likes"> + {{ $t('settings.notification_visibility_likes') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="notificationVisibility.repeats"> + {{ $t('settings.notification_visibility_repeats') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="notificationVisibility.follows"> + {{ $t('settings.notification_visibility_follows') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="notificationVisibility.mentions"> + {{ $t('settings.notification_visibility_mentions') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="notificationVisibility.moves"> + {{ $t('settings.notification_visibility_moves') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="notificationVisibility.emojiReactions"> + {{ $t('settings.notification_visibility_emoji_reactions') }} + </Checkbox> + </li> + </ul> + </div> + <div> + {{ $t('settings.replies_in_timeline') }} + <label + for="replyVisibility" + class="select" + > + <select + id="replyVisibility" + v-model="replyVisibility" + > + <option + value="all" + selected + >{{ $t('settings.reply_visibility_all') }}</option> + <option value="following">{{ $t('settings.reply_visibility_following') }}</option> + <option value="self">{{ $t('settings.reply_visibility_self') }}</option> + </select> + <i class="icon-down-open" /> + </label> + </div> + <div> + <Checkbox v-model="hidePostStats"> + {{ $t('settings.hide_post_stats') }} {{ $t('settings.instance_default', { value: hidePostStatsLocalizedValue }) }} + </Checkbox> + </div> + <div> + <Checkbox v-model="hideUserStats"> + {{ $t('settings.hide_user_stats') }} {{ $t('settings.instance_default', { value: hideUserStatsLocalizedValue }) }} + </Checkbox> + </div> + </div> + <div class="setting-item"> + <div> + <p>{{ $t('settings.filtering_explanation') }}</p> + <textarea + id="muteWords" + v-model="muteWordsString" + /> + </div> + <div> + <Checkbox v-model="hideFilteredStatuses"> + {{ $t('settings.hide_filtered_statuses') }} {{ $t('settings.instance_default', { value: hideFilteredStatusesLocalizedValue }) }} + </Checkbox> + </div> + </div> +</div> +</template> +<script src="./filtering_tab.js"></script> diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js new file mode 100644 index 00000000..82bf6862 --- /dev/null +++ b/src/components/settings_modal/tabs/general_tab.js @@ -0,0 +1,32 @@ +import Checkbox from 'src/components/checkbox/checkbox.vue' +import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' + +import SharedComputedObject from './helpers/shared_computed_object.js' + +const GeneralTab = { + data () { + const instance = this.$store.state.instance + return { + 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: { + Checkbox, + InterfaceLanguageSwitcher + }, + computed: { + postFormats () { + return this.$store.state.instance.postFormats || [] + }, + instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, + ...SharedComputedObject() + } +} + +export default GeneralTab diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue new file mode 100644 index 00000000..0d2da07a --- /dev/null +++ b/src/components/settings_modal/tabs/general_tab.vue @@ -0,0 +1,272 @@ +<template> +<div :label="$t('settings.general')"> + <div class="setting-item"> + <h2>{{ $t('settings.interface') }}</h2> + <ul class="setting-list"> + <li> + <interface-language-switcher /> + </li> + <li v-if="instanceSpecificPanelPresent"> + <Checkbox v-model="hideISP"> + {{ $t('settings.hide_isp') }} + </Checkbox> + </li> + </ul> + </div> + <div class="setting-item"> + <h2>{{ $t('nav.timeline') }}</h2> + <ul class="setting-list"> + <li> + <Checkbox v-model="hideMutedPosts"> + {{ $t('settings.hide_muted_posts') }} {{ $t('settings.instance_default', { value: hideMutedPostsLocalizedValue }) }} + </Checkbox> + </li> + <li> + <Checkbox v-model="collapseMessageWithSubject"> + {{ $t('settings.collapse_subject') }} {{ $t('settings.instance_default', { value: collapseMessageWithSubjectLocalizedValue }) }} + </Checkbox> + </li> + <li> + <Checkbox v-model="streaming"> + {{ $t('settings.streaming') }} + </Checkbox> + <ul + class="setting-list suboptions" + :class="[{disabled: !streaming}]" + > + <li> + <Checkbox + v-model="pauseOnUnfocused" + :disabled="!streaming" + > + {{ $t('settings.pause_on_unfocused') }} + </Checkbox> + </li> + </ul> + </li> + <li> + <Checkbox v-model="useStreamingApi"> + {{ $t('settings.useStreamingApi') }} + <br> + <small> + {{ $t('settings.useStreamingApiWarning') }} + </small> + </Checkbox> + </li> + <li> + <Checkbox v-model="autoLoad"> + {{ $t('settings.autoload') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="hoverPreview"> + {{ $t('settings.reply_link_preview') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="emojiReactionsOnTimeline"> + {{ $t('settings.emoji_reactions_on_timeline') }} + </Checkbox> + </li> + </ul> + </div> + + <div class="setting-item"> + <h2>{{ $t('settings.composing') }}</h2> + <ul class="setting-list"> + <li> + <Checkbox v-model="scopeCopy"> + {{ $t('settings.scope_copy') }} {{ $t('settings.instance_default', { value: scopeCopyLocalizedValue }) }} + </Checkbox> + </li> + <li> + <Checkbox v-model="alwaysShowSubjectInput"> + {{ $t('settings.subject_input_always_show') }} {{ $t('settings.instance_default', { value: alwaysShowSubjectInputLocalizedValue }) }} + </Checkbox> + </li> + <li> + <div> + {{ $t('settings.subject_line_behavior') }} + <label + for="subjectLineBehavior" + class="select" + > + <select + id="subjectLineBehavior" + v-model="subjectLineBehavior" + > + <option value="email"> + {{ $t('settings.subject_line_email') }} + {{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }} + </option> + <option value="masto"> + {{ $t('settings.subject_line_mastodon') }} + {{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }} + </option> + <option value="noop"> + {{ $t('settings.subject_line_noop') }} + {{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }} + </option> + </select> + <i class="icon-down-open" /> + </label> + </div> + </li> + <li v-if="postFormats.length > 0"> + <div> + {{ $t('settings.post_status_content_type') }} + <label + for="postContentType" + class="select" + > + <select + id="postContentType" + v-model="postContentType" + > + <option + v-for="postFormat in postFormats" + :key="postFormat" + :value="postFormat" + > + {{ $t(`post_status.content_type["${postFormat}"]`) }} + {{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }} + </option> + </select> + <i class="icon-down-open" /> + </label> + </div> + </li> + <li> + <Checkbox v-model="minimalScopesMode"> + {{ $t('settings.minimal_scopes_mode') }} {{ $t('settings.instance_default', { value: minimalScopesModeLocalizedValue }) }} + </Checkbox> + </li> + <li> + <Checkbox v-model="autohideFloatingPostButton"> + {{ $t('settings.autohide_floating_post_button') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="padEmoji"> + {{ $t('settings.pad_emoji') }} + </Checkbox> + </li> + </ul> + </div> + + <div class="setting-item"> + <h2>{{ $t('settings.attachments') }}</h2> + <ul class="setting-list"> + <li> + <Checkbox v-model="hideAttachments"> + {{ $t('settings.hide_attachments_in_tl') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="hideAttachmentsInConv"> + {{ $t('settings.hide_attachments_in_convo') }} + </Checkbox> + </li> + <li> + <label for="maxThumbnails"> + {{ $t('settings.max_thumbnails') }} + </label> + <input + id="maxThumbnails" + v-model.number="maxThumbnails" + class="number-input" + type="number" + min="0" + step="1" + > + </li> + <li> + <Checkbox v-model="hideNsfw"> + {{ $t('settings.nsfw_clickthrough') }} + </Checkbox> + </li> + <ul class="setting-list suboptions"> + <li> + <Checkbox + v-model="preloadImage" + :disabled="!hideNsfw" + > + {{ $t('settings.preload_images') }} + </Checkbox> + </li> + <li> + <Checkbox + v-model="useOneClickNsfw" + :disabled="!hideNsfw" + > + {{ $t('settings.use_one_click_nsfw') }} + </Checkbox> + </li> + </ul> + <li> + <Checkbox v-model="stopGifs"> + {{ $t('settings.stop_gifs') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="loopVideo"> + {{ $t('settings.loop_video') }} + </Checkbox> + <ul + class="setting-list suboptions" + :class="[{disabled: !streaming}]" + > + <li> + <Checkbox + v-model="loopVideoSilentOnly" + :disabled="!loopVideo || !loopSilentAvailable" + > + {{ $t('settings.loop_video_silent_only') }} + </Checkbox> + <div + v-if="!loopSilentAvailable" + class="unavailable" + > + <i class="icon-globe" />! {{ $t('settings.limited_availability') }} + </div> + </li> + </ul> + </li> + <li> + <Checkbox v-model="playVideosInModal"> + {{ $t('settings.play_videos_in_modal') }} + </Checkbox> + </li> + <li> + <Checkbox v-model="useContainFit"> + {{ $t('settings.use_contain_fit') }} + </Checkbox> + </li> + </ul> + </div> + + <div class="setting-item"> + <h2>{{ $t('settings.notifications') }}</h2> + <ul class="setting-list"> + <li> + <Checkbox v-model="webPushNotifications"> + {{ $t('settings.enable_web_push_notifications') }} + </Checkbox> + </li> + </ul> + </div> + + <div class="setting-item"> + <h2>{{ $t('settings.fun') }}</h2> + <ul class="setting-list"> + <li> + <Checkbox v-model="greentext"> + {{ $t('settings.greentext') }} {{ $t('settings.instance_default', { value: greentextLocalizedValue }) }} + </Checkbox> + </li> + </ul> + </div> + </div> +</template> + +<script src="./general_tab.js"></script> diff --git a/src/components/settings_modal/tabs/helpers/shared_computed_object.js b/src/components/settings_modal/tabs/helpers/shared_computed_object.js new file mode 100644 index 00000000..61643e3b --- /dev/null +++ b/src/components/settings_modal/tabs/helpers/shared_computed_object.js @@ -0,0 +1,69 @@ +import { filter, trim } from 'lodash' +import { instanceDefaultProperties, defaultState as configDefaultState } from 'src/modules/config.js' + +const multiChoiceProperties = [ + 'postContentType', + 'subjectLineBehavior' +] + +const SharedComputedObject = () => ({ + user () { + return this.$store.state.users.currentUser + }, + // Getting localized values for instance-default properties + ...instanceDefaultProperties + .filter(key => multiChoiceProperties.includes(key)) + .map(key => [ + key + 'DefaultValue', + function () { + return this.$store.getters.instanceDefaultConfig[key] + } + ]) + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), + ...instanceDefaultProperties + .filter(key => !multiChoiceProperties.includes(key)) + .map(key => [ + key + 'LocalizedValue', + function () { + return this.$t('settings.values.' + this.$store.getters.instanceDefaultConfig[key]) + } + ]) + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), + // Generating computed values for vuex properties + ...Object.keys(configDefaultState) + .map(key => [key, { + get () { return this.$store.getters.mergedConfig[key] }, + set (value) { + this.$store.dispatch('setOption', { name: key, value }) + } + }]) + .reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {}), + // Special cases (need to transform values or perform actions first) + muteWordsString: { + get () { return this.$store.getters.mergedConfig.muteWords.join('\n') }, + set (value) { + this.$store.dispatch('setOption', { + name: 'muteWords', + value: filter(value.split('\n'), (word) => trim(word).length > 0) + }) + } + }, + useStreamingApi: { + get () { return this.$store.getters.mergedConfig.useStreamingApi }, + set (value) { + const promise = value + ? this.$store.dispatch('enableMastoSockets') + : this.$store.dispatch('disableMastoSockets') + + promise.then(() => { + this.$store.dispatch('setOption', { name: 'useStreamingApi', value }) + }).catch((e) => { + console.error('Failed starting MastoAPI Streaming socket', e) + this.$store.dispatch('disableMastoSockets') + this.$store.dispatch('setOption', { name: 'useStreamingApi', value: false }) + }) + } + } +}) + +export default SharedComputedObject diff --git a/src/components/settings_modal/tabs/mutes_and_blocks.js b/src/components/settings_modal/tabs/mutes_and_blocks_tab.js similarity index 83% rename from src/components/settings_modal/tabs/mutes_and_blocks.js rename to src/components/settings_modal/tabs/mutes_and_blocks_tab.js index 51895ddc..3f6b7205 100644 --- a/src/components/settings_modal/tabs/mutes_and_blocks.js +++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.js @@ -1,15 +1,15 @@ import get from 'lodash/get' import map from 'lodash/map' import reject from 'lodash/reject' -import Autosuggest from '../../autosuggest/autosuggest.vue' -import TabSwitcher from '../../tab_switcher/tab_switcher.js' -import BlockCard from '../../block_card/block_card.vue' -import MuteCard from '../../mute_card/mute_card.vue' -import DomainMuteCard from '../../domain_mute_card/domain_mute_card.vue' -import SelectableList from '../../selectable_list/selectable_list.vue' -import ProgressButton from '../../progress_button/progress_button.vue' -import withSubscription from '../../../hocs/with_subscription/with_subscription' -import Checkbox from '../../checkbox/checkbox.vue' +import Autosuggest from 'src/components/autosuggest/autosuggest.vue' +import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' +import BlockCard from 'src/components/block_card/block_card.vue' +import MuteCard from 'src/components/mute_card/mute_card.vue' +import DomainMuteCard from 'src/components/domain_mute_card/domain_mute_card.vue' +import SelectableList from 'src/components/selectable_list/selectable_list.vue' +import ProgressButton from 'src/components/progress_button/progress_button.vue' +import withSubscription from 'src/components/../hocs/with_subscription/with_subscription' +import Checkbox from 'src/components/checkbox/checkbox.vue' const BlockList = withSubscription({ fetch: (props, $store) => $store.dispatch('fetchBlocks'), diff --git a/src/components/settings_modal/tabs/mutes_and_blocks.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue similarity index 97% rename from src/components/settings_modal/tabs/mutes_and_blocks.vue rename to src/components/settings_modal/tabs/mutes_and_blocks_tab.vue index 3aff47a0..7fce7b78 100644 --- a/src/components/settings_modal/tabs/mutes_and_blocks.vue +++ b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue @@ -15,7 +15,7 @@ </div> <BlockList :refresh="true" - :get-key="identity" + :get-key="i => i" > <template slot="header" @@ -73,7 +73,7 @@ </div> <MuteList :refresh="true" - :get-key="identity" + :get-key="i => i" > <template slot="header" @@ -134,7 +134,7 @@ </div> <DomainMuteList :refresh="true" - :get-key="identity" + :get-key="i => i" > <template slot="header" @@ -169,5 +169,5 @@ </tab-switcher> </template> -<script src="./mutes_and_blocks.js"></script> +<script src="./mutes_and_blocks_tab.js"></script> <!-- <style lang="scss" src="./profile.scss"></style> --> diff --git a/src/components/settings_modal/tabs/notifications.js b/src/components/settings_modal/tabs/notifications_tab.js similarity index 80% rename from src/components/settings_modal/tabs/notifications.js rename to src/components/settings_modal/tabs/notifications_tab.js index 0a870b3f..3e44c95d 100644 --- a/src/components/settings_modal/tabs/notifications.js +++ b/src/components/settings_modal/tabs/notifications_tab.js @@ -1,6 +1,6 @@ -import Checkbox from '../../checkbox/checkbox.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' -const Notifications = { +const NotificationsTab = { data () { return { activeTab: 'profile', @@ -24,4 +24,4 @@ const Notifications = { } } -export default Notifications +export default NotificationsTab diff --git a/src/components/settings_modal/tabs/notifications.vue b/src/components/settings_modal/tabs/notifications_tab.vue similarity index 96% rename from src/components/settings_modal/tabs/notifications.vue rename to src/components/settings_modal/tabs/notifications_tab.vue index f9a7c17b..ab33a6a5 100644 --- a/src/components/settings_modal/tabs/notifications.vue +++ b/src/components/settings_modal/tabs/notifications_tab.vue @@ -38,5 +38,5 @@ </div> </template> -<script src="./notifications.js"></script> +<script src="./notifications_tab.js"></script> <!-- <style lang="scss" src="./profile.scss"></style> --> diff --git a/src/components/settings_modal/tabs/profile.js b/src/components/settings_modal/tabs/profile_tab.js similarity index 90% rename from src/components/settings_modal/tabs/profile.js rename to src/components/settings_modal/tabs/profile_tab.js index 18c44024..949b480b 100644 --- a/src/components/settings_modal/tabs/profile.js +++ b/src/components/settings_modal/tabs/profile_tab.js @@ -1,12 +1,12 @@ import unescape from 'lodash/unescape' -import ImageCropper from '../../image_cropper/image_cropper.vue' -import ScopeSelector from '../../scope_selector/scope_selector.vue' -import fileSizeFormatService from '../../../services/file_size_format/file_size_format.js' -import ProgressButton from '../../progress_button/progress_button.vue' -import EmojiInput from '../../emoji_input/emoji_input.vue' -import suggestor from '../../emoji_input/suggestor.js' -import Autosuggest from '../../autosuggest/autosuggest.vue' -import Checkbox from '../../checkbox/checkbox.vue' +import ImageCropper from 'src/components/image_cropper/image_cropper.vue' +import ScopeSelector from 'src/components/scope_selector/scope_selector.vue' +import fileSizeFormatService from 'src/components/../services/file_size_format/file_size_format.js' +import ProgressButton from 'src/components/progress_button/progress_button.vue' +import EmojiInput from 'src/components/emoji_input/emoji_input.vue' +import suggestor from 'src/components/emoji_input/suggestor.js' +import Autosuggest from 'src/components/autosuggest/autosuggest.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' const ProfileTab = { data () { diff --git a/src/components/settings_modal/tabs/profile.scss b/src/components/settings_modal/tabs/profile_tab.scss similarity index 100% rename from src/components/settings_modal/tabs/profile.scss rename to src/components/settings_modal/tabs/profile_tab.scss diff --git a/src/components/settings_modal/tabs/profile.vue b/src/components/settings_modal/tabs/profile_tab.vue similarity index 98% rename from src/components/settings_modal/tabs/profile.vue rename to src/components/settings_modal/tabs/profile_tab.vue index 335fc12e..9dd89b99 100644 --- a/src/components/settings_modal/tabs/profile.vue +++ b/src/components/settings_modal/tabs/profile_tab.vue @@ -209,5 +209,5 @@ </div> </template> -<script src="./profile.js"></script> -<style lang="scss" src="./profile.scss"></style> +<script src="./profile_tab.js"></script> +<style lang="scss" src="./profile_tab.scss"></style> diff --git a/src/components/user_settings/confirm.js b/src/components/settings_modal/tabs/security_tab/confirm.js similarity index 100% rename from src/components/user_settings/confirm.js rename to src/components/settings_modal/tabs/security_tab/confirm.js diff --git a/src/components/user_settings/confirm.vue b/src/components/settings_modal/tabs/security_tab/confirm.vue similarity index 100% rename from src/components/user_settings/confirm.vue rename to src/components/settings_modal/tabs/security_tab/confirm.vue diff --git a/src/components/user_settings/mfa.js b/src/components/settings_modal/tabs/security_tab/mfa.js similarity index 100% rename from src/components/user_settings/mfa.js rename to src/components/settings_modal/tabs/security_tab/mfa.js diff --git a/src/components/user_settings/mfa.vue b/src/components/settings_modal/tabs/security_tab/mfa.vue similarity index 97% rename from src/components/user_settings/mfa.vue rename to src/components/settings_modal/tabs/security_tab/mfa.vue index 14ea10a1..25c4d1dc 100644 --- a/src/components/user_settings/mfa.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa.vue @@ -137,11 +137,7 @@ <script src="./mfa.js"></script> <style lang="scss"> -@import '../../_variables.scss'; -.warning { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); -} +@import '../../../../_variables.scss'; .mfa-settings { .mfa-heading, .method-item { overflow: hidden; @@ -151,6 +147,11 @@ align-items: baseline; } + .warning { + color: $fallback--cOrange; + color: var(--cOrange, $fallback--cOrange); + } + .setup-otp { display: flex; justify-content: center; diff --git a/src/components/user_settings/mfa_backup_codes.js b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.js similarity index 100% rename from src/components/user_settings/mfa_backup_codes.js rename to src/components/settings_modal/tabs/security_tab/mfa_backup_codes.js diff --git a/src/components/user_settings/mfa_backup_codes.vue b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue similarity index 69% rename from src/components/user_settings/mfa_backup_codes.vue rename to src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue index e6c8ede2..d7e98b3c 100644 --- a/src/components/user_settings/mfa_backup_codes.vue +++ b/src/components/settings_modal/tabs/security_tab/mfa_backup_codes.vue @@ -1,5 +1,5 @@ <template> - <div> + <div class="mfa-backup-codes"> <h4 v-if="displayTitle"> {{ $t('settings.mfa.recovery_codes') }} </h4> @@ -21,13 +21,15 @@ </template> <script src="./mfa_backup_codes.js"></script> <style lang="scss"> -@import '../../_variables.scss'; +@import '../../../../_variables.scss'; -.warning { - color: $fallback--cOrange; - color: var(--cOrange, $fallback--cOrange); -} -.backup-codes { - font-family: var(--postCodeFont, monospace); +.mfa-backup-codes { + .warning { + color: $fallback--cOrange; + color: var(--cOrange, $fallback--cOrange); + } + .backup-codes { + font-family: var(--postCodeFont, monospace); + } } </style> diff --git a/src/components/user_settings/mfa_totp.js b/src/components/settings_modal/tabs/security_tab/mfa_totp.js similarity index 100% rename from src/components/user_settings/mfa_totp.js rename to src/components/settings_modal/tabs/security_tab/mfa_totp.js diff --git a/src/components/user_settings/mfa_totp.vue b/src/components/settings_modal/tabs/security_tab/mfa_totp.vue similarity index 100% rename from src/components/user_settings/mfa_totp.vue rename to src/components/settings_modal/tabs/security_tab/mfa_totp.vue diff --git a/src/components/settings_modal/tabs/security.js b/src/components/settings_modal/tabs/security_tab/security_tab.js similarity index 92% rename from src/components/settings_modal/tabs/security.js rename to src/components/settings_modal/tabs/security_tab/security_tab.js index cc791b7a..811161a5 100644 --- a/src/components/settings_modal/tabs/security.js +++ b/src/components/settings_modal/tabs/security_tab/security_tab.js @@ -1,8 +1,8 @@ -import ProgressButton from '../../progress_button/progress_button.vue' -import Checkbox from '../../checkbox/checkbox.vue' -import Mfa from '../../user_settings/mfa.vue' +import ProgressButton from 'src/components/progress_button/progress_button.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' +import Mfa from './mfa.vue' -const Security = { +const SecurityTab = { data () { return { newEmail: '', @@ -103,4 +103,4 @@ const Security = { } } -export default Security +export default SecurityTab diff --git a/src/components/settings_modal/tabs/security.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue similarity index 98% rename from src/components/settings_modal/tabs/security.vue rename to src/components/settings_modal/tabs/security_tab/security_tab.vue index 603c9a04..45bacec1 100644 --- a/src/components/settings_modal/tabs/security.vue +++ b/src/components/settings_modal/tabs/security_tab/security_tab.vue @@ -139,5 +139,5 @@ </div> </template> -<script src="./security.js"></script> +<script src="./security_tab.js"></script> <!-- <style lang="scss" src="./profile.scss"></style> --> diff --git a/src/components/style_switcher/preview.vue b/src/components/settings_modal/tabs/theme_tab/preview.vue similarity index 100% rename from src/components/style_switcher/preview.vue rename to src/components/settings_modal/tabs/theme_tab/preview.vue diff --git a/src/components/style_switcher/style_switcher.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js similarity index 96% rename from src/components/style_switcher/style_switcher.js rename to src/components/settings_modal/tabs/theme_tab/theme_tab.js index a7f586f4..9d61b0c4 100644 --- a/src/components/style_switcher/style_switcher.js +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -3,7 +3,7 @@ import { rgb2hex, hex2rgb, getContrastRatioLayers -} from '../../services/color_convert/color_convert.js' +} from 'src/services/color_convert/color_convert.js' import { DEFAULT_SHADOWS, generateColors, @@ -14,26 +14,27 @@ import { getThemes, shadows2to3, colors2to3 -} from '../../services/style_setter/style_setter.js' +} from 'src/services/style_setter/style_setter.js' import { SLOT_INHERITANCE -} from '../../services/theme_data/pleromafe.js' +} from 'src/services/theme_data/pleromafe.js' import { CURRENT_VERSION, OPACITIES, getLayers, getOpacitySlot -} from '../../services/theme_data/theme_data.service.js' -import ColorInput from '../color_input/color_input.vue' -import RangeInput from '../range_input/range_input.vue' -import OpacityInput from '../opacity_input/opacity_input.vue' -import ShadowControl from '../shadow_control/shadow_control.vue' -import FontControl from '../font_control/font_control.vue' -import ContrastRatio from '../contrast_ratio/contrast_ratio.vue' -import TabSwitcher from '../tab_switcher/tab_switcher.js' +} from 'src/services/theme_data/theme_data.service.js' +import ColorInput from 'src/components/color_input/color_input.vue' +import RangeInput from 'src/components/range_input/range_input.vue' +import OpacityInput from 'src/components/opacity_input/opacity_input.vue' +import ShadowControl from 'src/components/shadow_control/shadow_control.vue' +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 ExportImport from 'src/components/export_import/export_import.vue' +import Checkbox from 'src/components/checkbox/checkbox.vue' + import Preview from './preview.vue' -import ExportImport from '../export_import/export_import.vue' -import Checkbox from '../checkbox/checkbox.vue' // List of color values used in v1 const v1OnlyNames = [ diff --git a/src/components/style_switcher/style_switcher.scss b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss similarity index 97% rename from src/components/style_switcher/style_switcher.scss rename to src/components/settings_modal/tabs/theme_tab/theme_tab.scss index d2a40d13..75b3017d 100644 --- a/src/components/style_switcher/style_switcher.scss +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.scss @@ -1,5 +1,5 @@ -@import '../../_variables.scss'; -.style-switcher { +@import 'src/_variables.scss'; +.theme-tab { .theme-warning { display: flex; align-items: baseline; @@ -54,10 +54,6 @@ } } - .tab-switcher { - margin: 0 -1em; - } - .reset-container { flex-wrap: wrap; } @@ -161,7 +157,7 @@ border-bottom: 1px dashed; border-color: $fallback--border; border-color: var(--border, $fallback--border); - margin: 1em -1em 0; + margin: 1em 0; padding: 1em; background: var(--body-background-image); background-size: cover; @@ -328,6 +324,14 @@ padding: 20px; } + .apply-container { + .btn { + min-height: 28px; + min-width: 10em; + padding: 0 2em; + } + } + .btn { margin-left: .25em; margin-right: .25em; diff --git a/src/components/style_switcher/style_switcher.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue similarity index 92% rename from src/components/style_switcher/style_switcher.vue rename to src/components/settings_modal/tabs/theme_tab/theme_tab.vue index 62c8e634..6f6cf1d6 100644 --- a/src/components/style_switcher/style_switcher.vue +++ b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue @@ -1,90 +1,90 @@ <template> - <div class="style-switcher"> - <div class="presets-container"> - <div class="save-load"> - <div - v-if="themeWarning" - class="theme-warning" +<div class="theme-tab"> + <div class="presets-container"> + <div class="save-load"> + <div + v-if="themeWarning" + class="theme-warning" > - <div class="alert warning"> - {{ themeWarningHelp }} - </div> - <div class="buttons"> - <template v-if="themeWarning.type === 'snapshot_source_mismatch'"> - <button - class="btn" - @click="forceLoad" - > - {{ $t('settings.style.switcher.use_source') }} - </button> - <button - class="btn" - @click="forceSnapshot" - > - {{ $t('settings.style.switcher.use_snapshot') }} - </button> - </template> - <template v-else-if="themeWarning.noActionsPossible"> - <button - class="btn" - @click="dismissWarning" - > - {{ $t('general.dismiss') }} - </button> - </template> - <template v-else> - <button - class="btn" - @click="forceLoad" - > - {{ $t('settings.style.switcher.load_theme') }} - </button> - <button - class="btn" - @click="dismissWarning" - > - {{ $t('settings.style.switcher.keep_as_is') }} - </button> - </template> - </div> + <div class="alert warning"> + {{ themeWarningHelp }} </div> - <ExportImport - :export-object="exportedTheme" - :export-label="$t("settings.export_theme")" - :import-label="$t("settings.import_theme")" - :import-failed-text="$t("settings.invalid_theme_imported")" - :on-import="onImport" - :validator="importValidator" - > - <template slot="before"> - <div class="presets"> - {{ $t('settings.presets') }} - <label - for="preset-switcher" - class="select" + <div class="buttons"> + <template v-if="themeWarning.type === 'snapshot_source_mismatch'"> + <button + class="btn" + @click="forceLoad" > - <select - id="preset-switcher" - v-model="selected" - class="preset-switcher" - > - <option - v-for="style in availableStyles" - :key="style.name" - :value="style" - :style="{ - backgroundColor: style[1] || (style.theme || style.source).colors.bg, - color: style[3] || (style.theme || style.source).colors.text - }" - > - {{ style[0] || style.name }} - </option> - </select> - <i class="icon-down-open" /> - </label> - </div> + {{ $t('settings.style.switcher.use_source') }} + </button> + <button + class="btn" + @click="forceSnapshot" + > + {{ $t('settings.style.switcher.use_snapshot') }} + </button> </template> - </ExportImport> + <template v-else-if="themeWarning.noActionsPossible"> + <button + class="btn" + @click="dismissWarning" + > + {{ $t('general.dismiss') }} + </button> + </template> + <template v-else> + <button + class="btn" + @click="forceLoad" + > + {{ $t('settings.style.switcher.load_theme') }} + </button> + <button + class="btn" + @click="dismissWarning" + > + {{ $t('settings.style.switcher.keep_as_is') }} + </button> + </template> + </div> + </div> + <ExportImport + :export-object="exportedTheme" + :export-label="$t("settings.export_theme")" + :import-label="$t("settings.import_theme")" + :import-failed-text="$t("settings.invalid_theme_imported")" + :on-import="onImport" + :validator="importValidator" + > + <template slot="before"> + <div class="presets"> + {{ $t('settings.presets') }} + <label + for="preset-switcher" + class="select" + > + <select + id="preset-switcher" + v-model="selected" + class="preset-switcher" + > + <option + v-for="style in availableStyles" + :key="style.name" + :value="style" + :style="{ + backgroundColor: style[1] || (style.theme || style.source).colors.bg, + color: style[3] || (style.theme || style.source).colors.text + }" + > + {{ style[0] || style.name }} + </option> + </select> + <i class="icon-down-open" /> + </label> + </div> + </template> + </ExportImport> </div> <div class="save-load-options"> <span class="keep-option"> @@ -951,6 +951,6 @@ </div> </template> -<script src="./style_switcher.js"></script> +<script src="./theme_tab.js"></script> -<style src="./style_switcher.scss" lang="scss"></style> +<style src="./theme_tab.scss" lang="scss"></style> diff --git a/src/components/settings_modal/tabs/version_tab.js b/src/components/settings_modal/tabs/version_tab.js new file mode 100644 index 00000000..616bdadf --- /dev/null +++ b/src/components/settings_modal/tabs/version_tab.js @@ -0,0 +1,24 @@ +import { extractCommit } from 'src/services/version/version.service' + +const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/' +const pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/' + +const VersionTab = { + data () { + const instance = this.$store.state.instance + return { + backendVersion: instance.backendVersion, + frontendVersion: instance.frontendVersion + } + }, + computed: { + frontendVersionLink () { + return pleromaFeCommitUrl + this.frontendVersion + }, + backendVersionLink () { + return pleromaBeCommitUrl + extractCommit(this.backendVersion) + } + } +} + +export default VersionTab diff --git a/src/components/settings_modal/tabs/version_tab.vue b/src/components/settings_modal/tabs/version_tab.vue new file mode 100644 index 00000000..acc43569 --- /dev/null +++ b/src/components/settings_modal/tabs/version_tab.vue @@ -0,0 +1,31 @@ +<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="backendVersionLink" + 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"> diff --git a/src/components/tab_switcher/tab_switcher.scss b/src/components/tab_switcher/tab_switcher.scss index a443531e..a7b790a3 100644 --- a/src/components/tab_switcher/tab_switcher.scss +++ b/src/components/tab_switcher/tab_switcher.scss @@ -46,8 +46,14 @@ &.side-tabs { flex-direction: row; + @media all and (max-width: 800px) { + overflow-x: auto; + } > .contents { flex: 0 1 80%; + @media all and (max-width: 800px) { + min-width: 96vw; + } } > .tabs { flex: 1 0 auto; diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue index 4ee040e8..b4f275d9 100644 --- a/src/components/user_card/user_card.vue +++ b/src/components/user_card/user_card.vue @@ -50,15 +50,6 @@ > {{ user.name }} </div> - <router-link - v-if="!isOtherUser" - :to="{ name: 'user-settings' }" - > - <i - class="button-icon icon-wrench usersettings" - :title="$t('tool_tip.user_settings')" - /> - </router-link> <a v-if="isOtherUser && !user.is_local" :href="user.statusnet_profile_url" @@ -117,7 +108,7 @@ type="color" > <label - for="style-switcher" + for="theme_tab" class="userHighlightSel select" > <select diff --git a/src/components/user_settings/user_settings.js b/src/components/user_settings/user_settings.js deleted file mode 100644 index e07d4e56..00000000 --- a/src/components/user_settings/user_settings.js +++ /dev/null @@ -1,140 +0,0 @@ -import get from 'lodash/get' -import map from 'lodash/map' -import reject from 'lodash/reject' -import Autosuggest from '../autosuggest/autosuggest.vue' -import TabSwitcher from '../tab_switcher/tab_switcher.js' -import BlockCard from '../block_card/block_card.vue' -import MuteCard from '../mute_card/mute_card.vue' -import DomainMuteCard from '../domain_mute_card/domain_mute_card.vue' -import SelectableList from '../selectable_list/selectable_list.vue' -import ProgressButton from '../progress_button/progress_button.vue' -import Importer from '../importer/importer.vue' -import Exporter from '../exporter/exporter.vue' -import withSubscription from '../../hocs/with_subscription/with_subscription' -import Checkbox from '../checkbox/checkbox.vue' - -const BlockList = withSubscription({ - fetch: (props, $store) => $store.dispatch('fetchBlocks'), - select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []), - childPropName: 'items' -})(SelectableList) - -const MuteList = withSubscription({ - fetch: (props, $store) => $store.dispatch('fetchMutes'), - select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []), - childPropName: 'items' -})(SelectableList) - -const DomainMuteList = withSubscription({ - fetch: (props, $store) => $store.dispatch('fetchDomainMutes'), - select: (props, $store) => get($store.state.users.currentUser, 'domainMutes', []), - childPropName: 'items' -})(SelectableList) - -const UserSettings = { - data () { - return { - activeTab: 'profile', - newDomainToMute: '' - } - }, - created () { - this.$store.dispatch('fetchTokens') - }, - components: { - TabSwitcher, - BlockList, - MuteList, - DomainMuteList, - BlockCard, - MuteCard, - DomainMuteCard, - ProgressButton, - Autosuggest, - Checkbox - }, - computed: { - user () { - return this.$store.state.users.currentUser - }, - pleromaBackend () { - return this.$store.state.instance.pleromaBackend - }, - currentSaveStateNotice () { - return this.$store.state.interface.settings.currentSaveStateNotice - } - }, - methods: { - importFollows (file) { - return this.$store.state.api.backendInteractor.importFollows({ file }) - .then((status) => { - if (!status) { - throw new Error('failed') - } - }) - }, - importBlocks (file) { - return this.$store.state.api.backendInteractor.importBlocks({ file }) - .then((status) => { - if (!status) { - throw new Error('failed') - } - }) - }, - generateExportableUsersContent (users) { - // Get addresses - return users.map((user) => { - // check is it's a local user - if (user && user.is_local) { - // append the instance address - // eslint-disable-next-line no-undef - return user.screen_name + '@' + location.hostname - } - return user.screen_name - }).join('\n') - }, - activateTab (tabName) { - this.activeTab = tabName - }, - filterUnblockedUsers (userIds) { - return reject(userIds, (userId) => { - const user = this.$store.getters.findUser(userId) - return !user || user.statusnet_blocking || user.id === this.$store.state.users.currentUser.id - }) - }, - filterUnMutedUsers (userIds) { - return reject(userIds, (userId) => { - const user = this.$store.getters.findUser(userId) - return !user || user.muted || user.id === this.$store.state.users.currentUser.id - }) - }, - queryUserIds (query) { - return this.$store.dispatch('searchUsers', query) - .then((users) => map(users, 'id')) - }, - blockUsers (ids) { - return this.$store.dispatch('blockUsers', ids) - }, - unblockUsers (ids) { - return this.$store.dispatch('unblockUsers', ids) - }, - muteUsers (ids) { - return this.$store.dispatch('muteUsers', ids) - }, - unmuteUsers (ids) { - return this.$store.dispatch('unmuteUsers', ids) - }, - unmuteDomains (domains) { - return this.$store.dispatch('unmuteDomains', domains) - }, - muteDomain () { - return this.$store.dispatch('muteDomain', this.newDomainToMute) - .then(() => { this.newDomainToMute = '' }) - }, - identity (value) { - return value - } - } -} - -export default UserSettings diff --git a/src/components/user_settings/user_settings.vue b/src/components/user_settings/user_settings.vue deleted file mode 100644 index 2a88714f..00000000 --- a/src/components/user_settings/user_settings.vue +++ /dev/null @@ -1,119 +0,0 @@ -<template> - <div class="settings panel panel-default"> - <div class="panel-heading"> - <div class="title"> - {{ $t('settings.user_settings') }} - </div> - <transition name="fade"> - <template v-if="currentSaveStateNotice"> - <div - v-if="currentSaveStateNotice.error" - class="alert error" - @click.prevent - > - {{ $t('settings.saving_err') }} - </div> - - <div - v-if="!currentSaveStateNotice.error" - class="alert transparent" - @click.prevent - > - {{ $t('settings.saving_ok') }} - </div> - </template> - </transition> - </div> - <div class="panel-body profile-edit"> - </div> - </div> -</template> - -<script src="./user_settings.js"> -</script> - -<style lang="scss"> -@import '../../_variables.scss'; - -.profile-edit { - .bio { - margin: 0; - } - - .visibility-tray { - padding-top: 5px; - } - - input[type=file] { - padding: 5px; - height: auto; - } - - .banner { - max-width: 100%; - } - - .uploading { - font-size: 1.5em; - margin: 0.25em; - } - - .name-changer { - width: 100%; - } - - .bg { - max-width: 100%; - } - - .current-avatar { - display: block; - width: 150px; - height: 150px; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); - } - - .oauth-tokens { - width: 100%; - - th { - text-align: left; - } - - .actions { - text-align: right; - } - } - - &-usersearch-wrapper { - padding: 1em; - } - - &-bulk-actions { - text-align: right; - padding: 0 1em; - min-height: 28px; - - button { - width: 10em; - } - } - - &-domain-mute-form { - padding: 1em; - display: flex; - flex-direction: column; - - button { - align-self: flex-end; - margin-top: 1em; - width: 10em; - } - } - - .setting-subitem { - margin-left: 1.75em; - } -} -</style> diff --git a/src/i18n/en.json b/src/i18n/en.json index 312f7283..d42be00f 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -68,7 +68,9 @@ "disable": "Disable", "enable": "Enable", "confirm": "Confirm", - "verify": "Verify" + "verify": "Verify", + "close": "Close", + "peek": "Peek" }, "image_cropper": { "crop_picture": "Crop picture",