commit
199fc9351d
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="async-component-error">
|
||||
<div>
|
||||
<h4>
|
||||
{{ $t('general.generic_error') }}
|
||||
</h4>
|
||||
<p>
|
||||
{{ $t('general.error_retry') }}
|
||||
</p>
|
||||
<button
|
||||
class="btn"
|
||||
@click="retry"
|
||||
>
|
||||
{{ $t('general.retry') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
retry () {
|
||||
this.$emit('resetAsyncComponent')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.async-component-error {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.btn {
|
||||
margin: .5em;
|
||||
padding: .5em 2em;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="panel-loading">
|
||||
<span class="loading-text">
|
||||
<i class="icon-spin4 animate-spin" />
|
||||
{{ $t('general.loading') }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'src/_variables.scss';
|
||||
|
||||
.panel-loading {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 2em;
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
.loading-text i {
|
||||
font-size: 3em;
|
||||
line-height: 0;
|
||||
vertical-align: middle;
|
||||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -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
|
@ -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>
|
@ -0,0 +1,58 @@
|
||||
import {
|
||||
instanceDefaultProperties,
|
||||
multiChoiceProperties,
|
||||
defaultState as configDefaultState
|
||||
} from 'src/modules/config.js'
|
||||
|
||||
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)
|
||||
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
|
@ -0,0 +1,42 @@
|
||||
import Modal from 'src/components/modal/modal.vue'
|
||||
import PanelLoading from 'src/components/panel_loading/panel_loading.vue'
|
||||
import AsyncComponentError from 'src/components/async_component_error/async_component_error.vue'
|
||||
import getResettableAsyncComponent from 'src/services/resettable_async_component.js'
|
||||
|
||||
const SettingsModal = {
|
||||
components: {
|
||||
Modal,
|
||||
SettingsModalContent: getResettableAsyncComponent(
|
||||
() => import('./settings_modal_content.vue'),
|
||||
{
|
||||
loading: PanelLoading,
|
||||
error: AsyncComponentError,
|
||||
delay: 0
|
||||
}
|
||||
)
|
||||
},
|
||||
methods: {
|
||||
closeModal () {
|
||||
this.$store.dispatch('closeSettingsModal')
|
||||
},
|
||||
peekModal () {
|
||||
this.$store.dispatch('togglePeekSettingsModal')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentSaveStateNotice () {
|
||||
return this.$store.state.interface.settings.currentSaveStateNotice
|
||||
},
|
||||
modalActivated () {
|
||||
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||
},
|
||||
modalOpenedOnce () {
|
||||
return this.$store.state.interface.settingsModalLoaded
|
||||
},
|
||||
modalPeeked () {
|
||||
return this.$store.state.interface.settingsModalState === 'minimized'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsModal
|
@ -0,0 +1,44 @@
|
||||
@import 'src/_variables.scss';
|
||||
.settings-modal {
|
||||
overflow: hidden;
|
||||
|
||||
&.peek {
|
||||
.settings-modal-panel {
|
||||
/* Explanation:
|
||||
* Modal is positioned vertically centered.
|
||||
* 100vh - 100% = Distance between modal's top+bottom boundaries and screen
|
||||
* (100vh - 100%) / 2 = Distance between bottom (or top) boundary and screen
|
||||
* + 100% - we move modal completely off-screen, it's top boundary touches
|
||||
* bottom of the screen
|
||||
* - 50px - leaving tiny amount of space so that titlebar + tiny amount of modal is visible
|
||||
*/
|
||||
transform: translateY(calc(((100vh - 100%) / 2 + 100%) - 50px));
|
||||
}
|
||||
}
|
||||
|
||||
.settings-modal-panel {
|
||||
overflow: hidden;
|
||||
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;
|
||||
|
||||
.btn {
|
||||
min-height: 28px;
|
||||
min-width: 10em;
|
||||
padding: 0 2em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
<template>
|
||||
<Modal
|
||||
:is-open="modalActivated"
|
||||
class="settings-modal"
|
||||
:class="{ peek: modalPeeked }"
|
||||
:no-background="modalPeeked"
|
||||
>
|
||||
<div class="settings-modal-panel panel">
|
||||
<div class="panel-heading">
|
||||
<span class="title">
|
||||
{{ $t('settings.settings') }}
|
||||
</span>
|
||||
<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>
|
||||
<button
|
||||
class="btn"
|
||||
@click="peekModal"
|
||||
>
|
||||
{{ $t('general.peek') }}
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="closeModal"
|
||||
>
|
||||
{{ $t('general.close') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<SettingsModalContent v-if="modalOpenedOnce" />
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script src="./settings_modal.js"></script>
|
||||
|
||||
<style src="./settings_modal.scss" lang="scss"></style>
|
@ -0,0 +1,34 @@
|
||||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js'
|
||||
|
||||
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 SettingsModalContent = {
|
||||
components: {
|
||||
TabSwitcher,
|
||||
|
||||
DataImportExportTab,
|
||||
MutesAndBlocksTab,
|
||||
NotificationsTab,
|
||||
FilteringTab,
|
||||
SecurityTab,
|
||||
ProfileTab,
|
||||
GeneralTab,
|
||||
VersionTab,
|
||||
ThemeTab
|
||||
},
|
||||
computed: {
|
||||
isLoggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsModalContent
|
@ -0,0 +1,43 @@
|
||||
@import 'src/_variables.scss';
|
||||
.settings_tab-switcher {
|
||||
height: 100%;
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.number-input {
|
||||
max-width: 6em;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<tab-switcher
|
||||
ref="tabSwitcher"
|
||||
class="settings_tab-switcher"
|
||||
:side-tab-bar="true"
|
||||
:scrollable-tabs="true"
|
||||
>
|
||||
<div
|
||||
:label="$t('settings.general')"
|
||||
icon="wrench"
|
||||
>
|
||||
<GeneralTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.profile_tab')"
|
||||
icon="user"
|
||||
>
|
||||
<ProfileTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.security_tab')"
|
||||
icon="lock"
|
||||
>
|
||||
<SecurityTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.filtering')"
|
||||
icon="filter"
|
||||
>
|
||||
<FilteringTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.theme')"
|
||||
icon="brush"
|
||||
>
|
||||
<ThemeTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.notifications')"
|
||||
icon="bell-ringing-o"
|
||||
>
|
||||
<NotificationsTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.data_import_export_tab')"
|
||||
icon="download"
|
||||
>
|
||||
<DataImportExportTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="isLoggedIn"
|
||||
:label="$t('settings.mutes_and_blocks')"
|
||||
:fullHeight="true"
|
||||
icon="eye-off"
|
||||
>
|
||||
<MutesAndBlocksTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.version.title')"
|
||||
icon="info-circled"
|
||||
>
|
||||
<VersionTab />
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</template>
|
||||
|
||||
<script src="./settings_modal_content.js"></script>
|
||||
|
||||
<style src="./settings_modal_content.scss" lang="scss"></style>
|
@ -0,0 +1,65 @@
|
||||
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 DataImportExportTab = {
|
||||
data () {
|
||||
return {
|
||||
activeTab: 'profile',
|
||||
newDomainToMute: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchTokens')
|
||||
},
|
||||
components: {
|
||||
Importer,
|
||||
Exporter,
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getFollowsContent () {
|
||||
return this.$store.state.api.backendInteractor.exportFriends({ id: this.$store.state.users.currentUser.id })
|
||||
.then(this.generateExportableUsersContent)
|
||||
},
|
||||
getBlocksContent () {
|
||||
return this.$store.state.api.backendInteractor.fetchBlocks()
|
||||
.then(this.generateExportableUsersContent)
|
||||
},
|
||||
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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DataImportExportTab
|
@ -0,0 +1,43 @@
|
||||
<template>
|
||||
<div
|
||||
:label="$t('settings.data_import_export_tab')"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.follow_import') }}</h2>
|
||||
<p>{{ $t('settings.import_followers_from_a_csv_file') }}</p>
|
||||
<Importer
|
||||
:submit-handler="importFollows"
|
||||
:success-message="$t('settings.follows_imported')"
|
||||
:error-message="$t('settings.follow_import_error')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.follow_export') }}</h2>
|
||||
<Exporter
|
||||
:get-content="getFollowsContent"
|
||||
filename="friends.csv"
|
||||
:export-button-label="$t('settings.follow_export_button')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.block_import') }}</h2>
|
||||
<p>{{ $t('settings.import_blocks_from_a_csv_file') }}</p>
|
||||
<Importer
|
||||
:submit-handler="importBlocks"
|
||||
:success-message="$t('settings.blocks_imported')"
|
||||
:error-message="$t('settings.block_import_error')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.block_export') }}</h2>
|
||||
<Exporter
|
||||
:get-content="getBlocksContent"
|
||||
filename="blocks.csv"
|
||||
:export-button-label="$t('settings.block_export_button')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./data_import_export_tab.js"></script>
|
||||
<!-- <style lang="scss" src="./profile.scss"></style> -->
|
@ -0,0 +1,44 @@
|
||||
import { filter, trim } from 'lodash'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
|
||||
const FilteringTab = {
|
||||
data () {
|
||||
return {
|
||||
muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n')
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject(),
|
||||
muteWordsString: {
|
||||
get () {
|
||||
return this.muteWordsStringLocal
|
||||
},
|
||||
set (value) {
|
||||
this.muteWordsStringLocal = value
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'muteWords',
|
||||
value: filter(value.split('\n'), (word) => trim(word).length > 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
// Updating nested properties
|
||||
watch: {
|
||||
notificationVisibility: {
|
||||
handler (value) {
|
||||
this.$store.dispatch('setOption', {
|
||||
name: 'notificationVisibility',
|
||||
value: this.$store.getters.mergedConfig.notificationVisibility
|
||||
})
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FilteringTab
|
@ -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>
|
@ -0,0 +1,31 @@
|
||||
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 () {
|
||||
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
|
@ -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>
|
@ -0,0 +1,136 @@
|
||||
import get from 'lodash/get'
|
||||
import map from 'lodash/map'
|
||||
import reject from 'lodash/reject'
|
||||
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'),
|
||||
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 MutesAndBlocks = {
|
||||
data () {
|
||||
return {
|
||||
activeTab: 'profile'
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchTokens')
|
||||
this.$store.dispatch('getKnownDomains')
|
||||
},
|
||||
components: {
|
||||
TabSwitcher,
|
||||
BlockList,
|
||||
MuteList,
|
||||
DomainMuteList,
|
||||
BlockCard,
|
||||
MuteCard,
|
||||
DomainMuteCard,
|
||||
ProgressButton,
|
||||
Autosuggest,
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
knownDomains () {
|
||||
return this.$store.state.instance.knownDomains
|
||||
},
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
}
|
||||
},
|
||||
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 relationship = this.$store.getters.relationship(this.userId)
|
||||
return relationship.blocking || userId === this.user.id
|
||||
})
|
||||
},
|
||||
filterUnMutedUsers (userIds) {
|
||||
return reject(userIds, (userId) => {
|
||||
const relationship = this.$store.getters.relationship(this.userId)
|
||||
return relationship.muting || userId === this.user.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)
|
||||
},
|
||||
filterUnMutedDomains (urls) {
|
||||
return urls.filter(url => !this.user.domainMutes.includes(url))
|
||||
},
|
||||
queryKnownDomains (query) {
|
||||
return new Promise((resolve, reject) => {
|
||||
resolve(this.knownDomains.filter(url => url.toLowerCase().includes(query)))
|
||||
})
|
||||
},
|
||||
unmuteDomains (domains) {
|
||||
return this.$store.dispatch('unmuteDomains', domains)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default MutesAndBlocks
|
@ -0,0 +1,29 @@
|
||||
.mutes-and-blocks-tab {
|
||||
height: 100%;
|
||||
|
||||
.usersearch-wrapper {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.bulk-actions {
|
||||
text-align: right;
|
||||
padding: 0 1em;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.bulk-action-button {
|
||||
width: 10em
|
||||
}
|
||||
|
||||
.domain-mute-form {
|
||||
padding: 1em;
|
||||
display: flex;
|
||||
flex-direction: column
|
||||
}
|
||||
|
||||
.domain-mute-button {
|
||||
align-self: flex-end;
|
||||
margin-top: 1em;
|
||||
width: 10em
|
||||
}
|
||||
}
|
@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<tab-switcher
|
||||
:scrollable-tabs="true"
|
||||
class="mutes-and-blocks-tab"
|
||||
>
|
||||
<div :label="$t('settings.blocks_tab')">
|
||||
<div class="usersearch-wrapper">
|
||||
<Autosuggest
|
||||
:filter="filterUnblockedUsers"
|
||||
:query="queryUserIds"
|
||||
:placeholder="$t('settings.search_user_to_block')"
|
||||
>
|
||||
<BlockCard
|
||||
slot-scope="row"
|
||||
:user-id="row.item"
|
||||
/>
|
||||
</Autosuggest>
|
||||
</div>
|
||||
<BlockList
|
||||
:refresh="true"
|
||||
:get-key="i => i"
|
||||
>
|
||||
<template
|
||||
slot="header"
|
||||
slot-scope="{selected}"
|
||||
>
|
||||
<div class="bulk-actions">
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default bulk-action-button"
|
||||
:click="() => blockUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.block') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.block_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => unblockUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.unblock') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.unblock_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
slot="item"
|
||||
slot-scope="{item}"
|
||||
>
|
||||
<BlockCard :user-id="item" />
|
||||
</template>
|
||||
<template slot="empty">
|
||||
{{ $t('settings.no_blocks') }}
|
||||
</template>
|
||||
</BlockList>
|
||||
</div>
|
||||
|
||||
<div :label="$t('settings.mutes_tab')">
|
||||
<tab-switcher>
|
||||
<div label="Users">
|
||||
<div class="usersearch-wrapper">
|
||||
<Autosuggest
|
||||
:filter="filterUnMutedUsers"
|
||||
:query="queryUserIds"
|
||||
:placeholder="$t('settings.search_user_to_mute')"
|
||||
>
|
||||
<MuteCard
|
||||
slot-scope="row"
|
||||
:user-id="row.item"
|
||||
/>
|
||||
</Autosuggest>
|
||||
</div>
|
||||
<MuteList
|
||||
:refresh="true"
|
||||
:get-key="i => i"
|
||||
>
|
||||
<template
|
||||
slot="header"
|
||||
slot-scope="{selected}"
|
||||
>
|
||||
<div class="bulk-actions">
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => muteUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.mute') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.mute_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => unmuteUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.unmute') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.unmute_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
slot="item"
|
||||
slot-scope="{item}"
|
||||
>
|
||||
<MuteCard :user-id="item" />
|
||||
</template>
|
||||
<template slot="empty">
|
||||
{{ $t('settings.no_mutes') }}
|
||||
</template>
|
||||
</MuteList>
|
||||
</div>
|
||||
|
||||
<div :label="$t('settings.domain_mutes')">
|
||||
<div class="domain-mute-form">
|
||||
<Autosuggest
|
||||
:filter="filterUnMutedDomains"
|
||||
:query="queryKnownDomains"
|
||||
:placeholder="$t('settings.type_domains_to_mute')"
|
||||
>
|
||||
<DomainMuteCard
|
||||
slot-scope="row"
|
||||
:domain="row.item"
|
||||
/>
|
||||
</Autosuggest>
|
||||
</div>
|
||||
<DomainMuteList
|
||||
:refresh="true"
|
||||
:get-key="i => i"
|
||||
>
|
||||
<template
|
||||
slot="header"
|
||||
slot-scope="{selected}"
|
||||
>
|
||||
<div class="bulk-actions">
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => unmuteDomains(selected)"
|
||||
>
|
||||
{{ $t('domain_mute_card.unmute') }}
|
||||
<template slot="progress">
|
||||
{{ $t('domain_mute_card.unmute_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
slot="item"
|
||||
slot-scope="{item}"
|
||||
>
|
||||
<DomainMuteCard :domain="item" />
|
||||
</template>
|
||||
<template slot="empty">
|
||||
{{ $t('settings.no_mutes') }}
|
||||
</template>
|
||||
</DomainMuteList>
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</template>
|
||||
|
||||
<script src="./mutes_and_blocks_tab.js"></script>
|
||||
<style lang="scss" src="./mutes_and_blocks_tab.scss"></style>
|
@ -0,0 +1,27 @@
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
const NotificationsTab = {
|
||||
data () {
|
||||
return {
|
||||
activeTab: 'profile',
|
||||
notificationSettings: this.$store.state.users.currentUser.notification_settings,
|
||||
newDomainToMute: ''
|
||||
}
|
||||
},
|
||||
components: {
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateNotificationSettings () {
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateNotificationSettings({ settings: this.notificationSettings })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default NotificationsTab
|
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div :label="$t('settings.notifications')">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
|
||||
<div class="select-multiple">
|
||||
<span class="label">{{ $t('settings.notification_setting') }}</span>
|
||||
<ul class="option-list">
|
||||
<li>
|
||||
<Checkbox v-model="notificationSettings.from_following">
|
||||
{{ $t('settings.notification_setting_from_following') }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
<li>
|
||||
<Checkbox v-model="notificationSettings.from_followers">
|
||||
{{ $t('settings.notification_setting_from_followers') }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
<li>
|
||||
<Checkbox v-model="notificationSettings.from_strangers">
|
||||
{{ $t('settings.notification_setting_from_strangers') }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.notification_setting_privacy') }}</h2>
|
||||
<p>
|
||||
<Checkbox v-model="notificationSettings.privacy_option">
|
||||
{{ $t('settings.notification_setting_privacy_option') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<p>{{ $t('settings.notification_mutes') }}</p>
|
||||
<p>{{ $t('settings.notification_blocks') }}</p>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="updateNotificationSettings"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./notifications_tab.js"></script>
|
||||
<!-- <style lang="scss" src="./profile.scss"></style> -->
|
@ -0,0 +1,181 @@
|
||||
import unescape from 'lodash/unescape'
|
||||
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 () {
|
||||
return {
|
||||
newName: this.$store.state.users.currentUser.name,
|
||||
newBio: unescape(this.$store.state.users.currentUser.description),
|
||||
newLocked: this.$store.state.users.currentUser.locked,
|
||||
newNoRichText: this.$store.state.users.currentUser.no_rich_text,
|
||||
newDefaultScope: this.$store.state.users.currentUser.default_scope,
|
||||
hideFollows: this.$store.state.users.currentUser.hide_follows,
|
||||
hideFollowers: this.$store.state.users.currentUser.hide_followers,
|
||||
hideFollowsCount: this.$store.state.users.currentUser.hide_follows_count,
|
||||
hideFollowersCount: this.$store.state.users.currentUser.hide_followers_count,
|
||||
showRole: this.$store.state.users.currentUser.show_role,
|
||||
role: this.$store.state.users.currentUser.role,
|
||||
discoverable: this.$store.state.users.currentUser.discoverable,
|
||||
bot: this.$store.state.users.currentUser.bot,
|
||||
allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
|
||||
pickAvatarBtnVisible: true,
|
||||
bannerUploading: false,
|
||||
backgroundUploading: false,
|
||||
banner: null,
|
||||
bannerPreview: null,
|
||||
background: null,
|
||||
backgroundPreview: null,
|
||||
bannerUploadError: null,
|
||||
backgroundUploadError: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
ScopeSelector,
|
||||
ImageCropper,
|
||||
EmojiInput,
|
||||
Autosuggest,
|
||||
ProgressButton,
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
emojiUserSuggestor () {
|
||||
return suggestor({
|
||||
emoji: [
|
||||
...this.$store.state.instance.emoji,
|
||||
...this.$store.state.instance.customEmoji
|
||||
],
|
||||
users: this.$store.state.users.users,
|
||||
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
|
||||
})
|
||||
},
|
||||
emojiSuggestor () {
|
||||
return suggestor({ emoji: [
|
||||
...this.$store.state.instance.emoji,
|
||||
...this.$store.state.instance.customEmoji
|
||||
] })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateProfile () {
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateProfile({
|
||||
params: {
|
||||
note: this.newBio,
|
||||
locked: this.newLocked,
|
||||
// Backend notation.
|
||||
/* eslint-disable camelcase */
|
||||
display_name: this.newName,
|
||||
default_scope: this.newDefaultScope,
|
||||
no_rich_text: this.newNoRichText,
|
||||
hide_follows: this.hideFollows,
|
||||
hide_followers: this.hideFollowers,
|
||||
discoverable: this.discoverable,
|
||||
bot: this.bot,
|
||||
allow_following_move: this.allowFollowingMove,
|
||||
hide_follows_count: this.hideFollowsCount,
|
||||
hide_followers_count: this.hideFollowersCount,
|
||||
show_role: this.showRole
|
||||
/* eslint-enable camelcase */
|
||||
} }).then((user) => {
|
||||
this.$store.commit('addNewUsers', [user])
|
||||
this.$store.commit('setCurrentUser', user)
|
||||
})
|
||||
},
|
||||
changeVis (visibility) {
|
||||
this.newDefaultScope = visibility
|
||||
},
|
||||
uploadFile (slot, e) {
|
||||
const file = e.target.files[0]
|
||||
if (!file) { return }
|
||||
if (file.size > this.$store.state.instance[slot + 'limit']) {
|
||||
const filesize = fileSizeFormatService.fileSizeFormat(file.size)
|
||||
const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
|
||||
this[slot + 'UploadError'] = [
|
||||
this.$t('upload.error.base'),
|
||||
this.$t(
|
||||
'upload.error.file_too_big',
|
||||
{
|
||||
filesize: filesize.num,
|
||||
filesizeunit: filesize.unit,
|
||||
allowedsize: allowedsize.num,
|
||||
allowedsizeunit: allowedsize.unit
|
||||
}
|
||||
)
|
||||
].join(' ')
|
||||
return
|
||||
}
|
||||
// eslint-disable-next-line no-undef
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
const img = target.result
|
||||
this[slot + 'Preview'] = img
|
||||
this[slot] = file
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
},
|
||||
submitAvatar (cropper, file) {
|
||||
const that = this
|
||||
return new Promise((resolve, reject) => {
|
||||
function updateAvatar (avatar) {
|
||||
that.$store.state.api.backendInteractor.updateAvatar({ avatar })
|
||||
.then((user) => {
|
||||
that.$store.commit('addNewUsers', [user])
|
||||
that.$store.commit('setCurrentUser', user)
|
||||
resolve()
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(new Error(that.$t('upload.error.base') + ' ' + err.message))
|
||||
})
|
||||
}
|
||||
|
||||
if (cropper) {
|
||||
cropper.getCroppedCanvas().toBlob(updateAvatar, file.type)
|
||||
} else {
|
||||
updateAvatar(file)
|
||||
}
|
||||
})
|
||||
},
|
||||
submitBanner () {
|
||||
if (!this.bannerPreview) { return }
|
||||
|
||||
this.bannerUploading = true
|
||||
this.$store.state.api.backendInteractor.updateBanner({ banner: this.banner })
|
||||
.then((user) => {
|
||||
this.$store.commit('addNewUsers', [user])
|
||||
this.$store.commit('setCurrentUser', user)
|
||||
this.bannerPreview = null
|
||||
})
|
||||
.catch((err) => {
|
||||
this.bannerUploadError = this.$t('upload.error.base') + ' ' + err.message
|
||||
})
|
||||
.then(() => { this.bannerUploading = false })
|
||||
},
|
||||
submitBg () {
|
||||
if (!this.backgroundPreview) { return }
|
||||
let background = this.background
|
||||
this.backgroundUploading = true
|
||||
this.$store.state.api.backendInteractor.updateBg({ background }).then((data) => {
|
||||
if (!data.error) {
|
||||
this.$store.commit('addNewUsers', [data])
|
||||
this.$store.commit('setCurrentUser', data)
|
||||
this.backgroundPreview = null
|
||||
} else {
|
||||
this.backgroundUploadError = this.$t('upload.error.base') + data.error
|
||||
}
|
||||
this.backgroundUploading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ProfileTab
|
@ -0,0 +1,82 @@
|
||||
@import '../../../_variables.scss';
|
||||
.profile-tab {
|
||||
.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;
|
||||
}
|
||||
}
|
@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="profile-tab">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.name_bio') }}</h2>
|
||||
<p>{{ $t('settings.name') }}</p>
|
||||
<EmojiInput
|
||||
v-model="newName"
|
||||
enable-emoji-picker
|
||||
:suggest="emojiSuggestor"
|
||||
>
|
||||
<input
|
||||
id="username"
|
||||
v-model="newName"
|
||||
classname="name-changer"
|
||||
>
|
||||
</EmojiInput>
|
||||
<p>{{ $t('settings.bio') }}</p>
|
||||
<EmojiInput
|
||||
v-model="newBio"
|
||||
enable-emoji-picker
|
||||
:suggest="emojiUserSuggestor"
|
||||
>
|
||||
<textarea
|
||||
v-model="newBio"
|
||||
classname="bio"
|
||||
/>
|
||||
</EmojiInput>
|
||||
<p>
|
||||
<Checkbox v-model="newLocked">
|
||||
{{ $t('settings.lock_account_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<div>
|
||||
<label for="default-vis">{{ $t('settings.default_vis') }}</label>
|
||||
<div
|
||||
id="default-vis"
|
||||
class="visibility-tray"
|
||||
>
|
||||
<scope-selector
|
||||
:show-all="true"
|
||||
:user-default="newDefaultScope"
|
||||
:initial-scope="newDefaultScope"
|
||||
:on-scope-change="changeVis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<Checkbox v-model="newNoRichText">
|
||||
{{ $t('settings.no_rich_text_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="hideFollows">
|
||||
{{ $t('settings.hide_follows_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p class="setting-subitem">
|
||||
<Checkbox
|
||||
v-model="hideFollowsCount"
|
||||
:disabled="!hideFollows"
|
||||
>
|
||||
{{ $t('settings.hide_follows_count_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="hideFollowers">
|
||||
{{ $t('settings.hide_followers_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p class="setting-subitem">
|
||||
<Checkbox
|
||||
v-model="hideFollowersCount"
|
||||
:disabled="!hideFollowers"
|
||||
>
|
||||
{{ $t('settings.hide_followers_count_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="allowFollowingMove">
|
||||
{{ $t('settings.allow_following_move') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p v-if="role === 'admin' || role === 'moderator'">
|
||||
<Checkbox v-model="showRole">
|
||||
<template v-if="role === 'admin'">
|
||||
{{ $t('settings.show_admin_badge') }}
|
||||
</template>
|
||||
<template v-if="role === 'moderator'">
|
||||
{{ $t('settings.show_moderator_badge') }}
|
||||
</template>
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="discoverable">
|
||||
{{ $t('settings.discoverable') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="bot">
|
||||
{{ $t('settings.bot') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<button
|
||||
:disabled="newName && newName.length === 0"
|
||||
class="btn btn-default"
|
||||
@click="updateProfile"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.avatar') }}</h2>
|
||||
<p class="visibility-notice">
|
||||
{{ $t('settings.avatar_size_instruction') }}
|
||||
</p>
|
||||
<p>{{ $t('settings.current_avatar') }}</p>
|
||||
<img
|
||||
:src="user.profile_image_url_original"
|
||||
class="current-avatar"
|
||||
>
|
||||
<p>{{ $t('settings.set_new_avatar') }}</p>
|
||||
<button
|
||||
v-show="pickAvatarBtnVisible"
|
||||
id="pick-avatar"
|
||||
class="btn"
|
||||
type="button"
|
||||
>
|
||||
{{ $t('settings.upload_a_photo') }}
|
||||
</button>
|
||||
<image-cropper
|
||||
trigger="#pick-avatar"
|
||||
:submit-handler="submitAvatar"
|
||||
@open="pickAvatarBtnVisible=false"
|
||||
@close="pickAvatarBtnVisible=true"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.profile_banner') }}</h2>
|
||||
<p>{{ $t('settings.current_profile_banner') }}</p>
|
||||
<img
|
||||
:src="user.cover_photo"
|
||||
class="banner"
|
||||
>
|
||||
<p>{{ $t('settings.set_new_profile_banner') }}</p>
|
||||
<img
|
||||
v-if="bannerPreview"
|
||||
class="banner"
|
||||
:src="bannerPreview"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
type="file"
|
||||
@change="uploadFile('banner', $event)"
|
||||
>
|
||||
</div>
|
||||
<i
|
||||
v-if="bannerUploading"
|
||||
class=" icon-spin4 animate-spin uploading"
|
||||
/>
|
||||
<button
|
||||
v-else-if="bannerPreview"
|
||||
class="btn btn-default"
|
||||
@click="submitBanner"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<div
|
||||
v-if="bannerUploadError"
|
||||
class="alert error"
|
||||
>
|
||||
Error: {{ bannerUploadError }}
|
||||
<i
|
||||
class="button-icon icon-cancel"
|
||||
@click="clearUploadError('banner')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.profile_background') }}</h2>
|
||||
<p>{{ $t('settings.set_new_profile_background') }}</p>
|
||||
<img
|
||||
v-if="backgroundPreview"
|
||||
class="bg"
|
||||
:src="backgroundPreview"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
type="file"
|
||||
@change="uploadFile('background', $event)"
|
||||
>
|
||||
</div>
|
||||
<i
|
||||
v-if="backgroundUploading"
|
||||
class=" icon-spin4 animate-spin uploading"
|
||||
/>
|
||||
<button
|
||||
v-else-if="backgroundPreview"
|
||||
class="btn btn-default"
|
||||
@click="submitBg"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<div
|
||||
v-if="backgroundUploadError"
|
||||
class="alert error"
|
||||
>
|
||||
Error: {{ backgroundUploadError }}
|
||||
<i
|
||||
class="button-icon icon-cancel"
|
||||
@click="clearUploadError('background')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./profile_tab.js"></script>
|
||||
<style lang="scss" src="./profile_tab.scss"></style>
|
@ -0,0 +1,106 @@
|
||||
import ProgressButton from 'src/components/progress_button/progress_button.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import Mfa from './mfa.vue'
|
||||
|
||||
const SecurityTab = {
|
||||
data () {
|
||||
return {
|
||||
newEmail: '',
|
||||
changeEmailError: false,
|
||||
changeEmailPassword: '',
|
||||
changedEmail: false,
|
||||
deletingAccount: false,
|
||||
deleteAccountConfirmPasswordInput: '',
|
||||
deleteAccountError: false,
|
||||
changePasswordInputs: [ '', '', '' ],
|
||||
changedPassword: false,
|
||||
changePasswordError: false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchTokens')
|
||||
},
|
||||
components: {
|
||||
ProgressButton,
|
||||
Mfa,
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
pleromaBackend () {
|
||||
return this.$store.state.instance.pleromaBackend
|
||||
},
|
||||
oauthTokens () {
|
||||
return this.$store.state.oauthTokens.tokens.map(oauthToken => {
|
||||
return {
|
||||
id: oauthToken.id,
|
||||
appName: oauthToken.app_name,
|
||||
validUntil: new Date(oauthToken.valid_until).toLocaleDateString()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
confirmDelete () {
|
||||
this.deletingAccount = true
|
||||
},
|
||||
deleteAccount () {
|
||||
this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.$store.dispatch('logout')
|
||||
this.$router.push({ name: 'root' })
|
||||
} else {
|
||||
this.deleteAccountError = res.error
|
||||
}
|
||||
})
|
||||
},
|
||||
changePassword () {
|
||||
const params = {
|
||||
password: this.changePasswordInputs[0],
|
||||
newPassword: this.changePasswordInputs[1],
|
||||
newPasswordConfirmation: this.changePasswordInputs[2]
|
||||
}
|
||||
this.$store.state.api.backendInteractor.changePassword(params)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.changedPassword = true
|
||||
this.changePasswordError = false
|
||||
this.logout()
|
||||
} else {
|
||||
this.changedPassword = false
|
||||
this.changePasswordError = res.error
|
||||
}
|
||||
})
|
||||
},
|
||||
changeEmail () {
|
||||
const params = {
|
||||
email: this.newEmail,
|
||||
password: this.changeEmailPassword
|
||||
}
|
||||
this.$store.state.api.backendInteractor.changeEmail(params)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.changedEmail = true
|
||||
this.changeEmailError = false
|
||||
} else {
|
||||
this.changedEmail = false
|
||||
this.changeEmailError = res.error
|
||||
}
|
||||
})
|
||||
},
|
||||
logout () {
|
||||
this.$store.dispatch('logout')
|
||||
this.$router.replace('/')
|
||||
},
|
||||
revokeToken (id) {
|
||||
if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {
|
||||
this.$store.dispatch('revokeToken', id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SecurityTab
|
@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div :label="$t('settings.security_tab')">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.change_email') }}</h2>
|
||||
<div>
|
||||
<p>{{ $t('settings.new_email') }}</p>
|
||||
<input
|
||||
v-model="newEmail"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('settings.current_password') }}</p>
|
||||
<input
|
||||
v-model="changeEmailPassword"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="changeEmail"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<p v-if="changedEmail">
|
||||
{{ $t('settings.changed_email') }}
|
||||
</p>
|
||||
<template v-if="changeEmailError !== false">
|
||||
<p>{{ $t('settings.change_email_error') }}</p>
|
||||
<p>{{ changeEmailError }}</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.change_password') }}</h2>
|
||||
<div>
|
||||
<p>{{ $t('settings.current_password') }}</p>
|
||||
<input
|
||||
v-model="changePasswordInputs[0]"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('settings.new_password') }}</p>
|
||||
<input
|
||||
v-model="changePasswordInputs[1]"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('settings.confirm_new_password') }}</p>
|
||||
<input
|
||||
v-model="changePasswordInputs[2]"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="changePassword"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<p v-if="changedPassword">
|
||||
{{ $t('settings.changed_password') }}
|
||||
</p>
|
||||
<p v-else-if="changePasswordError !== false">
|
||||
{{ $t('settings.change_password_error') }}
|
||||
</p>
|
||||
<p v-if="changePasswordError">
|
||||
{{ changePasswordError }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.oauth_tokens') }}</h2>
|
||||
<table class="oauth-tokens">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('settings.app_name') }}</th>
|
||||
<th>{{ $t('settings.valid_until') }}</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="oauthToken in oauthTokens"
|
||||
:key="oauthToken.id"
|
||||
>
|
||||
<td>{{ oauthToken.appName }}</td>
|
||||
<td>{{ oauthToken.validUntil }}</td>
|
||||
<td class="actions">
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="revokeToken(oauthToken.id)"
|
||||
>
|
||||
{{ $t('settings.revoke_token') }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<mfa />
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.delete_account') }}</h2>
|
||||
<p v-if="!deletingAccount">
|
||||
{{ $t('settings.delete_account_description') }}
|
||||
</p>
|
||||
<div v-if="deletingAccount">
|
||||
<p>{{ $t('settings.delete_account_instructions') }}</p>
|
||||
<p>{{ $t('login.password') }}</p>
|
||||
<input
|
||||
v-model="deleteAccountConfirmPasswordInput"
|
||||
type="password"
|
||||
>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="deleteAccount"
|
||||
>
|
||||
{{ $t('settings.delete_account') }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="deleteAccountError !== false">
|
||||
{{ $t('settings.delete_account_error') }}
|
||||
</p>
|
||||
<p v-if="deleteAccountError">
|
||||
{{ deleteAccountError }}
|
||||
</p>
|
||||
<button
|
||||
v-if="!deletingAccount"
|
||||
class="btn btn-default"
|
||||
@click="confirmDelete"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./security_tab.js"></script>
|
||||
<!-- <style lang="scss" src="./profile.scss"></style> -->
|
@ -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
|
@ -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">
|
@ -1,393 +0,0 @@
|
||||
import unescape from 'lodash/unescape'
|
||||
import get from 'lodash/get'
|
||||
import map from 'lodash/map'
|
||||
import reject from 'lodash/reject'
|
||||
import TabSwitcher from '../tab_switcher/tab_switcher.js'
|
||||
import ImageCropper from '../image_cropper/image_cropper.vue'
|
||||
import StyleSwitcher from '../style_switcher/style_switcher.vue'
|
||||
import ScopeSelector from '../scope_selector/scope_selector.vue'
|
||||
import fileSizeFormatService from '../../services/file_size_format/file_size_format.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 EmojiInput from '../emoji_input/emoji_input.vue'
|
||||
import suggestor from '../emoji_input/suggestor.js'
|
||||
import Autosuggest from '../autosuggest/autosuggest.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'
|
||||
import Mfa from './mfa.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 {
|
||||
newEmail: '',
|
||||
newName: this.$store.state.users.currentUser.name,
|
||||
newBio: unescape(this.$store.state.users.currentUser.description),
|
||||
newLocked: this.$store.state.users.currentUser.locked,
|
||||
newNoRichText: this.$store.state.users.currentUser.no_rich_text,
|
||||
newDefaultScope: this.$store.state.users.currentUser.default_scope,
|
||||
hideFollows: this.$store.state.users.currentUser.hide_follows,
|
||||
hideFollowers: this.$store.state.users.currentUser.hide_followers,
|
||||
hideFollowsCount: this.$store.state.users.currentUser.hide_follows_count,
|
||||
hideFollowersCount: this.$store.state.users.currentUser.hide_followers_count,
|
||||
showRole: this.$store.state.users.currentUser.show_role,
|
||||
role: this.$store.state.users.currentUser.role,
|
||||
discoverable: this.$store.state.users.currentUser.discoverable,
|
||||
allowFollowingMove: this.$store.state.users.currentUser.allow_following_move,
|
||||
pickAvatarBtnVisible: true,
|
||||
bannerUploading: false,
|
||||
backgroundUploading: false,
|
||||
banner: null,
|
||||
bannerPreview: null,
|
||||
background: null,
|
||||
backgroundPreview: null,
|
||||
bannerUploadError: null,
|
||||
backgroundUploadError: null,
|
||||
changeEmailError: false,
|
||||
changeEmailPassword: '',
|
||||
changedEmail: false,
|
||||
deletingAccount: false,
|
||||
deleteAccountConfirmPasswordInput: '',
|
||||
deleteAccountError: false,
|
||||
changePasswordInputs: [ '', '', '' ],
|
||||
changedPassword: false,
|
||||
changePasswordError: false,
|
||||
activeTab: 'profile',
|
||||
notificationSettings: this.$store.state.users.currentUser.notification_settings,
|
||||
newDomainToMute: ''
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('fetchTokens')
|
||||
},
|
||||
components: {
|
||||
StyleSwitcher,
|
||||
ScopeSelector,
|
||||
TabSwitcher,
|
||||
ImageCropper,
|
||||
BlockList,
|
||||
MuteList,
|
||||
DomainMuteList,
|
||||
EmojiInput,
|
||||
Autosuggest,
|
||||
BlockCard,
|
||||
MuteCard,
|
||||
DomainMuteCard,
|
||||
ProgressButton,
|
||||
Importer,
|
||||
Exporter,
|
||||
Mfa,
|
||||
Checkbox
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
emojiUserSuggestor () {
|
||||
return suggestor({
|
||||
emoji: [
|
||||
...this.$store.state.instance.emoji,
|
||||
...this.$store.state.instance.customEmoji
|
||||
],
|
||||
users: this.$store.state.users.users,
|
||||
updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
|
||||
})
|
||||
},
|
||||
emojiSuggestor () {
|
||||
return suggestor({ emoji: [
|
||||
...this.$store.state.instance.emoji,
|
||||
...this.$store.state.instance.customEmoji
|
||||
] })
|
||||
},
|
||||
pleromaBackend () {
|
||||
return this.$store.state.instance.pleromaBackend
|
||||
},
|
||||
minimalScopesMode () {
|
||||
return this.$store.state.instance.minimalScopesMode
|
||||
},
|
||||
vis () {
|
||||
return {
|
||||
public: { selected: this.newDefaultScope === 'public' },
|
||||
unlisted: { selected: this.newDefaultScope === 'unlisted' },
|
||||
private: { selected: this.newDefaultScope === 'private' },
|
||||
direct: { selected: this.newDefaultScope === 'direct' }
|
||||
}
|
||||
},
|
||||
currentSaveStateNotice () {
|
||||
return this.$store.state.interface.settings.currentSaveStateNotice
|
||||
},
|
||||
oauthTokens () {
|
||||
return this.$store.state.oauthTokens.tokens.map(oauthToken => {
|
||||
return {
|
||||
id: oauthToken.id,
|
||||
appName: oauthToken.app_name,
|
||||
validUntil: new Date(oauthToken.valid_until).toLocaleDateString()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
updateProfile () {
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateProfile({
|
||||
params: {
|
||||
note: this.newBio,
|
||||
locked: this.newLocked,
|
||||
// Backend notation.
|
||||
/* eslint-disable camelcase */
|
||||
display_name: this.newName,
|
||||
default_scope: this.newDefaultScope,
|
||||
no_rich_text: this.newNoRichText,
|
||||
hide_follows: this.hideFollows,
|
||||
hide_followers: this.hideFollowers,
|
||||
discoverable: this.discoverable,
|
||||
allow_following_move: this.allowFollowingMove,
|
||||
hide_follows_count: this.hideFollowsCount,
|
||||
hide_followers_count: this.hideFollowersCount,
|
||||
show_role: this.showRole
|
||||
/* eslint-enable camelcase */
|
||||
} }).then((user) => {
|
||||
this.$store.commit('addNewUsers', [user])
|
||||
this.$store.commit('setCurrentUser', user)
|
||||
})
|
||||
},
|
||||
updateNotificationSettings () {
|
||||
this.$store.state.api.backendInteractor
|
||||
.updateNotificationSettings({ settings: this.notificationSettings })
|
||||
},
|
||||
changeVis (visibility) {
|
||||
this.newDefaultScope = visibility
|
||||
},
|
||||
uploadFile (slot, e) {
|
||||
const file = e.target.files[0]
|
||||
if (!file) { return }
|
||||
if (file.size > this.$store.state.instance[slot + 'limit']) {
|
||||
const filesize = fileSizeFormatService.fileSizeFormat(file.size)
|
||||
const allowedsize = fileSizeFormatService.fileSizeFormat(this.$store.state.instance[slot + 'limit'])
|
||||
this[slot + 'UploadError'] = this.$t('upload.error.base') + ' ' + this.$t('upload.error.file_too_big', { filesize: filesize.num, filesizeunit: filesize.unit, allowedsize: allowedsize.num, allowedsizeunit: allowedsize.unit })
|
||||
return
|
||||
}
|
||||
// eslint-disable-next-line no-undef
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
const img = target.result
|
||||
this[slot + 'Preview'] = img
|
||||
this[slot] = file
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
},
|
||||
submitAvatar (cropper, file) {
|
||||
const that = this
|
||||
return new Promise((resolve, reject) => {
|
||||
function updateAvatar (avatar) {
|
||||
that.$store.state.api.backendInteractor.updateAvatar({ avatar })
|
||||
.then((user) => {
|
||||
that.$store.commit('addNewUsers', [user])
|
||||
that.$store.commit('setCurrentUser', user)
|
||||
resolve()
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(new Error(that.$t('upload.error.base') + ' ' + err.message))
|
||||
})
|
||||
}
|
||||
|
||||
if (cropper) {
|
||||
cropper.getCroppedCanvas().toBlob(updateAvatar, file.type)
|
||||
} else {
|
||||
updateAvatar(file)
|
||||
}
|
||||
})
|
||||
},
|
||||
clearUploadError (slot) {
|
||||
this[slot + 'UploadError'] = null
|
||||
},
|
||||
submitBanner () {
|
||||
if (!this.bannerPreview) { return }
|
||||
|
||||
this.bannerUploading = true
|
||||
this.$store.state.api.backendInteractor.updateBanner({ banner: this.banner })
|
||||
.then((user) => {
|
||||
this.$store.commit('addNewUsers', [user])
|
||||
this.$store.commit('setCurrentUser', user)
|
||||
this.bannerPreview = null
|
||||
})
|
||||
.catch((err) => {
|
||||
this.bannerUploadError = this.$t('upload.error.base') + ' ' + err.message
|
||||
})
|
||||
.then(() => { this.bannerUploading = false })
|
||||
},
|
||||
submitBg () {
|
||||
if (!this.backgroundPreview) { return }
|
||||
let background = this.background
|
||||
this.backgroundUploading = true
|
||||
this.$store.state.api.backendInteractor.updateBg({ background }).then((data) => {
|
||||
if (!data.error) {
|
||||
this.$store.commit('addNewUsers', [data])
|
||||
this.$store.commit('setCurrentUser', data)
|
||||
this.backgroundPreview = null
|
||||
} else {
|
||||
this.backgroundUploadError = this.$t('upload.error.base') + data.error
|
||||
}
|
||||
this.backgroundUploading = false
|
||||
})
|
||||
},
|
||||
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')
|
||||
},
|
||||
getFollowsContent () {
|
||||
return this.$store.state.api.backendInteractor.exportFriends({ id: this.$store.state.users.currentUser.id })
|
||||
.then(this.generateExportableUsersContent)
|
||||
},
|
||||
getBlocksContent () {
|
||||
return this.$store.state.api.backendInteractor.fetchBlocks()
|
||||
.then(this.generateExportableUsersContent)
|
||||
},
|
||||
confirmDelete () {
|
||||
this.deletingAccount = true
|
||||
},
|
||||
deleteAccount () {
|
||||
this.$store.state.api.backendInteractor.deleteAccount({ password: this.deleteAccountConfirmPasswordInput })
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.$store.dispatch('logout')
|
||||
this.$router.push({ name: 'root' })
|
||||
} else {
|
||||
this.deleteAccountError = res.error
|
||||
}
|
||||
})
|
||||
},
|
||||
changePassword () {
|
||||
const params = {
|
||||
password: this.changePasswordInputs[0],
|
||||
newPassword: this.changePasswordInputs[1],
|
||||
newPasswordConfirmation: this.changePasswordInputs[2]
|
||||
}
|
||||
this.$store.state.api.backendInteractor.changePassword(params)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.changedPassword = true
|
||||
this.changePasswordError = false
|
||||
this.logout()
|
||||
} else {
|
||||
this.changedPassword = false
|
||||
this.changePasswordError = res.error
|
||||
}
|
||||
})
|
||||
},
|
||||
changeEmail () {
|
||||
const params = {
|
||||
email: this.newEmail,
|
||||
password: this.changeEmailPassword
|
||||
}
|
||||
this.$store.state.api.backendInteractor.changeEmail(params)
|
||||
.then((res) => {
|
||||
if (res.status === 'success') {
|
||||
this.changedEmail = true
|
||||
this.changeEmailError = false
|
||||
} else {
|
||||
this.changedEmail = false
|
||||
this.changeEmailError = res.error
|
||||
}
|
||||
})
|
||||
},
|
||||
activateTab (tabName) {
|
||||
this.activeTab = tabName
|
||||
},
|
||||
logout () {
|
||||
this.$store.dispatch('logout')
|
||||
this.$router.replace('/')
|
||||
},
|
||||
revokeToken (id) {
|
||||
if (window.confirm(`${this.$i18n.t('settings.revoke_token')}?`)) {
|
||||
this.$store.dispatch('revokeToken', id)
|
||||
}
|
||||
},
|
||||
filterUnblockedUsers (userIds) {
|
||||
return reject(userIds, (userId) => {
|
||||
const relationship = this.$store.getters.relationship(this.userId)
|
||||
return relationship.blocking || userId === this.$store.state.users.currentUser.id
|
||||
})
|
||||
},
|
||||
filterUnMutedUsers (userIds) {
|
||||
return reject(userIds, (userId) => {
|
||||
const relationship = this.$store.getters.relationship(this.userId)
|
||||
return relationship.muting || userId === 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
|
@ -1,723 +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">
|
||||
<tab-switcher>
|
||||
<div :label="$t('settings.profile_tab')">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.name_bio') }}</h2>
|
||||
<p>{{ $t('settings.name') }}</p>
|
||||
<EmojiInput
|
||||
v-model="newName"
|
||||
enable-emoji-picker
|
||||
:suggest="emojiSuggestor"
|
||||
>
|
||||
<input
|
||||
id="username"
|
||||
v-model="newName"
|
||||
classname="name-changer"
|
||||
>
|
||||
</EmojiInput>
|
||||
<p>{{ $t('settings.bio') }}</p>
|
||||
<EmojiInput
|
||||
v-model="newBio"
|
||||
enable-emoji-picker
|
||||
:suggest="emojiUserSuggestor"
|
||||
>
|
||||
<textarea
|
||||
v-model="newBio"
|
||||
classname="bio"
|
||||
/>
|
||||
</EmojiInput>
|
||||
<p>
|
||||
<Checkbox v-model="newLocked">
|
||||
{{ $t('settings.lock_account_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<div>
|
||||
<label for="default-vis">{{ $t('settings.default_vis') }}</label>
|
||||
<div
|
||||
id="default-vis"
|
||||
class="visibility-tray"
|
||||
>
|
||||
<scope-selector
|
||||
:show-all="true"
|
||||
:user-default="newDefaultScope"
|
||||
:initial-scope="newDefaultScope"
|
||||
:on-scope-change="changeVis"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<Checkbox v-model="newNoRichText">
|
||||
{{ $t('settings.no_rich_text_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="hideFollows">
|
||||
{{ $t('settings.hide_follows_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p class="setting-subitem">
|
||||
<Checkbox
|
||||
v-model="hideFollowsCount"
|
||||
:disabled="!hideFollows"
|
||||
>
|
||||
{{ $t('settings.hide_follows_count_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="hideFollowers">
|
||||
{{ $t('settings.hide_followers_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p class="setting-subitem">
|
||||
<Checkbox
|
||||
v-model="hideFollowersCount"
|
||||
:disabled="!hideFollowers"
|
||||
>
|
||||
{{ $t('settings.hide_followers_count_description') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="allowFollowingMove">
|
||||
{{ $t('settings.allow_following_move') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p v-if="role === 'admin' || role === 'moderator'">
|
||||
<Checkbox v-model="showRole">
|
||||
<template v-if="role === 'admin'">
|
||||
{{ $t('settings.show_admin_badge') }}
|
||||
</template>
|
||||
<template v-if="role === 'moderator'">
|
||||
{{ $t('settings.show_moderator_badge') }}
|
||||
</template>
|
||||
</Checkbox>
|
||||
</p>
|
||||
<p>
|
||||
<Checkbox v-model="discoverable">
|
||||
{{ $t('settings.discoverable') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
<button
|
||||
:disabled="newName && newName.length === 0"
|
||||
class="btn btn-default"
|
||||
@click="updateProfile"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.avatar') }}</h2>
|
||||
<p class="visibility-notice">
|
||||
{{ $t('settings.avatar_size_instruction') }}
|
||||
</p>
|
||||
<p>{{ $t('settings.current_avatar') }}</p>
|
||||
<img
|
||||
:src="user.profile_image_url_original"
|
||||
class="current-avatar"
|
||||
>
|
||||
<p>{{ $t('settings.set_new_avatar') }}</p>
|
||||
<button
|
||||
v-show="pickAvatarBtnVisible"
|
||||
id="pick-avatar"
|
||||
class="btn"
|
||||
type="button"
|
||||
>
|
||||
{{ $t('settings.upload_a_photo') }}
|
||||
</button>
|
||||
<image-cropper
|
||||
trigger="#pick-avatar"
|
||||
:submit-handler="submitAvatar"
|
||||
@open="pickAvatarBtnVisible=false"
|
||||
@close="pickAvatarBtnVisible=true"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.profile_banner') }}</h2>
|
||||
<p>{{ $t('settings.current_profile_banner') }}</p>
|
||||
<img
|
||||
:src="user.cover_photo"
|
||||
class="banner"
|
||||
>
|
||||
<p>{{ $t('settings.set_new_profile_banner') }}</p>
|
||||
<img
|
||||
v-if="bannerPreview"
|
||||
class="banner"
|
||||
:src="bannerPreview"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
type="file"
|
||||
@change="uploadFile('banner', $event)"
|
||||
>
|
||||
</div>
|
||||
<i
|
||||
v-if="bannerUploading"
|
||||
class=" icon-spin4 animate-spin uploading"
|
||||
/>
|
||||
<button
|
||||
v-else-if="bannerPreview"
|
||||
class="btn btn-default"
|
||||
@click="submitBanner"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<div
|
||||
v-if="bannerUploadError"
|
||||
class="alert error"
|
||||
>
|
||||
Error: {{ bannerUploadError }}
|
||||
<i
|
||||
class="button-icon icon-cancel"
|
||||
@click="clearUploadError('banner')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.profile_background') }}</h2>
|
||||
<p>{{ $t('settings.set_new_profile_background') }}</p>
|
||||
<img
|
||||
v-if="backgroundPreview"
|
||||
class="bg"
|
||||
:src="backgroundPreview"
|
||||
>
|
||||
<div>
|
||||
<input
|
||||
type="file"
|
||||
@change="uploadFile('background', $event)"
|
||||
>
|
||||
</div>
|
||||
<i
|
||||
v-if="backgroundUploading"
|
||||
class=" icon-spin4 animate-spin uploading"
|
||||
/>
|
||||
<button
|
||||
v-else-if="backgroundPreview"
|
||||
class="btn btn-default"
|
||||
@click="submitBg"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<div
|
||||
v-if="backgroundUploadError"
|
||||
class="alert error"
|
||||
>
|
||||
Error: {{ backgroundUploadError }}
|
||||
<i
|
||||
class="button-icon icon-cancel"
|
||||
@click="clearUploadError('background')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :label="$t('settings.security_tab')">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.change_email') }}</h2>
|
||||
<div>
|
||||
<p>{{ $t('settings.new_email') }}</p>
|
||||
<input
|
||||
v-model="newEmail"
|
||||
type="email"
|
||||
autocomplete="email"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('settings.current_password') }}</p>
|
||||
<input
|
||||
v-model="changeEmailPassword"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="changeEmail"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<p v-if="changedEmail">
|
||||
{{ $t('settings.changed_email') }}
|
||||
</p>
|
||||
<template v-if="changeEmailError !== false">
|
||||
<p>{{ $t('settings.change_email_error') }}</p>
|
||||
<p>{{ changeEmailError }}</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.change_password') }}</h2>
|
||||
<div>
|
||||
<p>{{ $t('settings.current_password') }}</p>
|
||||
<input
|
||||
v-model="changePasswordInputs[0]"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('settings.new_password') }}</p>
|
||||
<input
|
||||
v-model="changePasswordInputs[1]"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
<div>
|
||||
<p>{{ $t('settings.confirm_new_password') }}</p>
|
||||
<input
|
||||
v-model="changePasswordInputs[2]"
|
||||
type="password"
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="changePassword"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
<p v-if="changedPassword">
|
||||
{{ $t('settings.changed_password') }}
|
||||
</p>
|
||||
<p v-else-if="changePasswordError !== false">
|
||||
{{ $t('settings.change_password_error') }}
|
||||
</p>
|
||||
<p v-if="changePasswordError">
|
||||
{{ changePasswordError }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.oauth_tokens') }}</h2>
|
||||
<table class="oauth-tokens">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('settings.app_name') }}</th>
|
||||
<th>{{ $t('settings.valid_until') }}</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="oauthToken in oauthTokens"
|
||||
:key="oauthToken.id"
|
||||
>
|
||||
<td>{{ oauthToken.appName }}</td>
|
||||
<td>{{ oauthToken.validUntil }}</td>
|
||||
<td class="actions">
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="revokeToken(oauthToken.id)"
|
||||
>
|
||||
{{ $t('settings.revoke_token') }}
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<mfa />
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.delete_account') }}</h2>
|
||||
<p v-if="!deletingAccount">
|
||||
{{ $t('settings.delete_account_description') }}
|
||||
</p>
|
||||
<div v-if="deletingAccount">
|
||||
<p>{{ $t('settings.delete_account_instructions') }}</p>
|
||||
<p>{{ $t('login.password') }}</p>
|
||||
<input
|
||||
v-model="deleteAccountConfirmPasswordInput"
|
||||
type="password"
|
||||
>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="deleteAccount"
|
||||
>
|
||||
{{ $t('settings.delete_account') }}
|
||||
</button>
|
||||
</div>
|
||||
<p v-if="deleteAccountError !== false">
|
||||
{{ $t('settings.delete_account_error') }}
|
||||
</p>
|
||||
<p v-if="deleteAccountError">
|
||||
{{ deleteAccountError }}
|
||||
</p>
|
||||
<button
|
||||
v-if="!deletingAccount"
|
||||
class="btn btn-default"
|
||||
@click="confirmDelete"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="pleromaBackend"
|
||||
:label="$t('settings.notifications')"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
|
||||
<div class="select-multiple">
|
||||
<span class="label">{{ $t('settings.notification_setting') }}</span>
|
||||
<ul class="option-list">
|
||||
<li>
|
||||
<Checkbox v-model="notificationSettings.from_following">
|
||||
{{ $t('settings.notification_setting_from_following') }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
<li>
|
||||
<Checkbox v-model="notificationSettings.from_followers">
|
||||
{{ $t('settings.notification_setting_from_followers') }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
<li>
|
||||
<Checkbox v-model="notificationSettings.from_strangers">
|
||||
{{ $t('settings.notification_setting_from_strangers') }}
|
||||
</Checkbox>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.notification_setting_privacy') }}</h2>
|
||||
<p>
|
||||
<Checkbox v-model="notificationSettings.privacy_option">
|
||||
{{ $t('settings.notification_setting_privacy_option') }}
|
||||
</Checkbox>
|
||||
</p>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<p>{{ $t('settings.notification_mutes') }}</p>
|
||||
<p>{{ $t('settings.notification_blocks') }}</p>
|
||||
<button
|
||||
class="btn btn-default"
|
||||
@click="updateNotificationSettings"
|
||||
>
|
||||
{{ $t('general.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="pleromaBackend"
|
||||
:label="$t('settings.data_import_export_tab')"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.follow_import') }}</h2>
|
||||
<p>{{ $t('settings.import_followers_from_a_csv_file') }}</p>
|
||||
<Importer
|
||||
:submit-handler="importFollows"
|
||||
:success-message="$t('settings.follows_imported')"
|
||||
:error-message="$t('settings.follow_import_error')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.follow_export') }}</h2>
|
||||
<Exporter
|
||||
:get-content="getFollowsContent"
|
||||
filename="friends.csv"
|
||||
:export-button-label="$t('settings.follow_export_button')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.block_import') }}</h2>
|
||||
<p>{{ $t('settings.import_blocks_from_a_csv_file') }}</p>
|
||||
<Importer
|
||||
:submit-handler="importBlocks"
|
||||
:success-message="$t('settings.blocks_imported')"
|
||||
:error-message="$t('settings.block_import_error')"
|
||||
/>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('settings.block_export') }}</h2>
|
||||
<Exporter
|
||||
:get-content="getBlocksContent"
|
||||
filename="blocks.csv"
|
||||
:export-button-label="$t('settings.block_export_button')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div :label="$t('settings.blocks_tab')">
|
||||
<div class="profile-edit-usersearch-wrapper">
|
||||
<Autosuggest
|
||||
:filter="filterUnblockedUsers"
|
||||
:query="queryUserIds"
|
||||
:placeholder="$t('settings.search_user_to_block')"
|
||||
>
|
||||
<BlockCard
|
||||
slot-scope="row"
|
||||
:user-id="row.item"
|
||||
/>
|
||||
</Autosuggest>
|
||||
</div>
|
||||
<BlockList
|
||||
:refresh="true"
|
||||
:get-key="identity"
|
||||
>
|
||||
<template
|
||||
slot="header"
|
||||
slot-scope="{selected}"
|
||||
>
|
||||
<div class="profile-edit-bulk-actions">
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => blockUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.block') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.block_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => unblockUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.unblock') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.unblock_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
slot="item"
|
||||
slot-scope="{item}"
|
||||
>
|
||||
<BlockCard :user-id="item" />
|
||||
</template>
|
||||
<template slot="empty">
|
||||
{{ $t('settings.no_blocks') }}
|
||||
</template>
|
||||
</BlockList>
|
||||
</div>
|
||||
|
||||
<div :label="$t('settings.mutes_tab')">
|
||||
<tab-switcher>
|
||||
<div label="Users">
|
||||
<div class="profile-edit-usersearch-wrapper">
|
||||
<Autosuggest
|
||||
:filter="filterUnMutedUsers"
|
||||
:query="queryUserIds"
|
||||
:placeholder="$t('settings.search_user_to_mute')"
|
||||
>
|
||||
<MuteCard
|
||||
slot-scope="row"
|
||||
:user-id="row.item"
|
||||
/>
|
||||
</Autosuggest>
|
||||
</div>
|
||||
<MuteList
|
||||
:refresh="true"
|
||||
:get-key="identity"
|
||||
>
|
||||
<template
|
||||
slot="header"
|
||||
slot-scope="{selected}"
|
||||
>
|
||||
<div class="profile-edit-bulk-actions">
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => muteUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.mute') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.mute_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => unmuteUsers(selected)"
|
||||
>
|
||||
{{ $t('user_card.unmute') }}
|
||||
<template slot="progress">
|
||||
{{ $t('user_card.unmute_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
slot="item"
|
||||
slot-scope="{item}"
|
||||
>
|
||||
<MuteCard :user-id="item" />
|
||||
</template>
|
||||
<template slot="empty">
|
||||
{{ $t('settings.no_mutes') }}
|
||||
</template>
|
||||
</MuteList>
|
||||
</div>
|
||||
|
||||
<div :label="$t('settings.domain_mutes')">
|
||||
<div class="profile-edit-domain-mute-form">
|
||||
<input
|
||||
v-model="newDomainToMute"
|
||||
:placeholder="$t('settings.type_domains_to_mute')"
|
||||
type="text"
|
||||
@keyup.enter="muteDomain"
|
||||
>
|
||||
<ProgressButton
|
||||
class="btn btn-default"
|
||||
:click="muteDomain"
|
||||
>
|
||||
{{ $t('domain_mute_card.mute') }}
|
||||
<template slot="progress">
|
||||
{{ $t('domain_mute_card.mute_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
</div>
|
||||
<DomainMuteList
|
||||
:refresh="true"
|
||||
:get-key="identity"
|
||||
>
|
||||
<template
|
||||
slot="header"
|
||||
slot-scope="{selected}"
|
||||
>
|
||||
<div class="profile-edit-bulk-actions">
|
||||
<ProgressButton
|
||||
v-if="selected.length > 0"
|
||||
class="btn btn-default"
|
||||
:click="() => unmuteDomains(selected)"
|
||||
>
|
||||
{{ $t('domain_mute_card.unmute') }}
|
||||
<template slot="progress">
|
||||
{{ $t('domain_mute_card.unmute_progress') }}
|
||||
</template>
|
||||
</ProgressButton>
|
||||
</div>
|
||||
</template>
|
||||
<template
|
||||
slot="item"
|
||||
slot-scope="{item}"
|
||||
>
|
||||
<DomainMuteCard :domain="item" />
|
||||
</template>
|
||||
<template slot="empty">
|
||||
{{ $t('settings.no_mutes') }}
|
||||
</template>
|
||||
</DomainMuteList>
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</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>
|
@ -1,206 +1,206 @@
|
||||
{
|
||||
"chat": {
|
||||
"title": "الدردشة"
|
||||
"chat": {
|
||||
"title": "الدردشة"
|
||||
},
|
||||
"features_panel": {
|
||||
"chat": "الدردشة",
|
||||
"gopher": "غوفر",
|
||||
"media_proxy": "بروكسي الوسائط",
|
||||
"scope_options": "",
|
||||
"text_limit": "الحد الأقصى للنص",
|
||||
"title": "الميّزات",
|
||||
"who_to_follow": "للمتابعة"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "خطأ أثناء جلب صفحة المستخدم",
|
||||
"find_user": "البحث عن مستخدِم"
|
||||
},
|
||||
"general": {
|
||||
"apply": "تطبيق",
|
||||
"submit": "إرسال"
|
||||
},
|
||||
"login": {
|
||||
"login": "تسجيل الدخول",
|
||||
"logout": "الخروج",
|
||||
"password": "الكلمة السرية",
|
||||
"placeholder": "مثال lain",
|
||||
"register": "انشاء حساب",
|
||||
"username": "إسم المستخدم"
|
||||
},
|
||||
"nav": {
|
||||
"chat": "الدردشة المحلية",
|
||||
"friend_requests": "طلبات المتابَعة",
|
||||
"mentions": "الإشارات",
|
||||
"public_tl": "الخيط الزمني العام",
|
||||
"timeline": "الخيط الزمني",
|
||||
"twkn": "كافة الشبكة المعروفة"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "منشور مجهول، جارٍ البحث عنه…",
|
||||
"favorited_you": "أعجِب بمنشورك",
|
||||
"followed_you": "يُتابعك",
|
||||
"load_older": "تحميل الإشعارات الأقدم",
|
||||
"notifications": "الإخطارات",
|
||||
"read": "مقروء!",
|
||||
"repeated_you": "شارَك منشورك"
|
||||
},
|
||||
"post_status": {
|
||||
"account_not_locked_warning": "",
|
||||
"account_not_locked_warning_link": "مقفل",
|
||||
"attachments_sensitive": "اعتبر المرفقات كلها كمحتوى حساس",
|
||||
"content_type": {
|
||||
"text/plain": "نص صافٍ"
|
||||
},
|
||||
"features_panel": {
|
||||
"chat": "الدردشة",
|
||||
"gopher": "غوفر",
|
||||
"media_proxy": "بروكسي الوسائط",
|
||||
"scope_options": "",
|
||||
"text_limit": "الحد الأقصى للنص",
|
||||
"title": "الميّزات",
|
||||
"who_to_follow": "للمتابعة"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "خطأ أثناء جلب صفحة المستخدم",
|
||||
"find_user": "البحث عن مستخدِم"
|
||||
},
|
||||
"general": {
|
||||
"apply": "تطبيق",
|
||||
"submit": "إرسال"
|
||||
},
|
||||
"login": {
|
||||
"login": "تسجيل الدخول",
|
||||
"logout": "الخروج",
|
||||
"password": "الكلمة السرية",
|
||||
"placeholder": "مثال lain",
|
||||
"register": "انشاء حساب",
|
||||
"username": "إسم المستخدم"
|
||||
},
|
||||
"nav": {
|
||||
"chat": "الدردشة المحلية",
|
||||
"friend_requests": "طلبات المتابَعة",
|
||||
"mentions": "الإشارات",
|
||||
"public_tl": "الخيط الزمني العام",
|
||||
"timeline": "الخيط الزمني",
|
||||
"twkn": "كافة الشبكة المعروفة"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "منشور مجهول، جارٍ البحث عنه…",
|
||||
"favorited_you": "أعجِب بمنشورك",
|
||||
"followed_you": "يُتابعك",
|
||||
"load_older": "تحميل الإشعارات الأقدم",
|
||||
"notifications": "الإخطارات",
|
||||
"read": "مقروء!",
|
||||
"repeated_you": "شارَك منشورك"
|
||||
},
|
||||
"post_status": {
|
||||
"account_not_locked_warning": "",
|
||||
"account_not_locked_warning_link": "مقفل",
|
||||
"attachments_sensitive": "اعتبر المرفقات كلها كمحتوى حساس",
|
||||
"content_type": {
|
||||
"text/plain": "نص صافٍ"
|
||||
},
|
||||
"content_warning": "الموضوع (اختياري)",
|
||||
"default": "وصلت للتوّ إلى لوس أنجلس.",
|
||||
"direct_warning": "",
|
||||
"posting": "النشر",
|
||||
"scope": {
|
||||
"direct": "",
|
||||
"private": "",
|
||||
"public": "علني - يُنشر على الخيوط الزمنية العمومية",
|
||||
"unlisted": "غير مُدرَج - لا يُنشَر على الخيوط الزمنية العمومية"
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"bio": "السيرة الذاتية",
|
||||
"email": "عنوان البريد الإلكتروني",
|
||||
"fullname": "الإسم المعروض",
|
||||
"password_confirm": "تأكيد الكلمة السرية",
|
||||
"registration": "التسجيل",
|
||||
"token": "رمز الدعوة"
|
||||
},
|
||||
"settings": {
|
||||
"attachmentRadius": "المُرفَقات",
|
||||
"attachments": "المُرفَقات",
|
||||
"autoload": "",
|
||||
"avatar": "الصورة الرمزية",
|
||||
"avatarAltRadius": "الصور الرمزية (الإشعارات)",
|
||||
"avatarRadius": "الصور الرمزية",
|
||||
"background": "الخلفية",
|
||||
"bio": "السيرة الذاتية",
|
||||
"btnRadius": "الأزرار",
|
||||
"cBlue": "أزرق (الرد، المتابَعة)",
|
||||
"cGreen": "أخضر (إعادة النشر)",
|
||||
"cOrange": "برتقالي (مفضلة)",
|
||||
"cRed": "أحمر (إلغاء)",
|
||||
"change_password": "تغيير كلمة السر",
|
||||
"change_password_error": "وقع هناك خلل أثناء تعديل كلمتك السرية.",
|
||||
"changed_password": "تم تغيير كلمة المرور بنجاح!",
|
||||
"collapse_subject": "",
|
||||
"confirm_new_password": "تأكيد كلمة السر الجديدة",
|
||||
"current_avatar": "صورتك الرمزية الحالية",
|
||||
"current_password": "كلمة السر الحالية",
|
||||
"current_profile_banner": "الرأسية الحالية لصفحتك الشخصية",
|
||||
"data_import_export_tab": "تصدير واستيراد البيانات",
|
||||
"default_vis": "أسلوب العرض الافتراضي",
|
||||
"delete_account": "حذف الحساب",
|
||||
"delete_account_description": "حذف حسابك و كافة منشوراتك نهائيًا.",
|
||||
"delete_account_error": "",
|
||||
"delete_account_instructions": "يُرجى إدخال كلمتك السرية أدناه لتأكيد عملية حذف الحساب.",
|
||||
"export_theme": "حفظ النموذج",
|
||||
"filtering": "التصفية",
|
||||
"filtering_explanation": "سيتم إخفاء كافة المنشورات التي تحتوي على هذه الكلمات، كلمة واحدة في كل سطر",
|
||||
"follow_export": "تصدير الاشتراكات",
|
||||
"follow_export_button": "تصدير الاشتراكات كملف csv",
|
||||
"follow_export_processing": "التصدير جارٍ، سوف يُطلَب منك تنزيل ملفك بعد حين",
|
||||
"follow_import": "استيراد الاشتراكات",
|
||||
"follow_import_error": "خطأ أثناء استيراد المتابِعين",
|
||||
"follows_imported": "",
|
||||
"foreground": "الأمامية",
|
||||
"general": "الإعدادات العامة",
|
||||
"hide_attachments_in_convo": "إخفاء المرفقات على المحادثات",
|
||||
"hide_attachments_in_tl": "إخفاء المرفقات على الخيط الزمني",
|
||||
"hide_post_stats": "",
|
||||
"hide_user_stats": "",
|
||||
"import_followers_from_a_csv_file": "",
|
||||
"import_theme": "تحميل نموذج",
|
||||
"inputRadius": "",
|
||||
"instance_default": "",
|
||||
"interfaceLanguage": "لغة الواجهة",
|
||||
"invalid_theme_imported": "",
|
||||
"limited_availability": "غير متوفر على متصفحك",
|
||||
"links": "الروابط",
|
||||
"lock_account_description": "",
|
||||
"loop_video": "",
|
||||
"loop_video_silent_only": "",
|
||||
"name": "الاسم",
|
||||
"name_bio": "الاسم والسيرة الذاتية",
|
||||
"new_password": "كلمة السر الجديدة",
|
||||
"no_rich_text_description": "",
|
||||
"notification_visibility": "نوع الإشعارات التي تريد عرضها",
|
||||
"notification_visibility_follows": "يتابع",
|
||||
"notification_visibility_likes": "الإعجابات",
|
||||
"notification_visibility_mentions": "الإشارات",
|
||||
"notification_visibility_repeats": "",
|
||||
"nsfw_clickthrough": "",
|
||||
"oauth_tokens": "رموز OAuth",
|
||||
"token": "رمز",
|
||||
"refresh_token": "رمز التحديث",
|
||||
"valid_until": "صالح حتى",
|
||||
"revoke_token": "سحب",
|
||||
"panelRadius": "",
|
||||
"pause_on_unfocused": "",
|
||||
"presets": "النماذج",
|
||||
"profile_background": "خلفية الصفحة الشخصية",
|
||||
"profile_banner": "رأسية الصفحة الشخصية",
|
||||
"profile_tab": "الملف الشخصي",
|
||||
"radii_help": "",
|
||||
"replies_in_timeline": "الردود على الخيط الزمني",
|
||||
"reply_link_preview": "",
|
||||
"reply_visibility_all": "عرض كافة الردود",
|
||||
"reply_visibility_following": "",
|
||||
"reply_visibility_self": "",
|
||||
"saving_err": "خطأ أثناء حفظ الإعدادات",
|
||||
"saving_ok": "تم حفظ الإعدادات",
|
||||
"security_tab": "الأمان",
|
||||
"set_new_avatar": "اختيار صورة رمزية جديدة",
|
||||
"set_new_profile_background": "اختيار خلفية جديدة للملف الشخصي",
|
||||
"set_new_profile_banner": "اختيار رأسية جديدة للصفحة الشخصية",
|
||||
"settings": "الإعدادات",
|
||||
"stop_gifs": "",
|
||||
"streaming": "",
|
||||
"text": "النص",
|
||||
"theme": "المظهر",
|
||||
"theme_help": "",
|
||||
"tooltipRadius": "",
|
||||
"user_settings": "إعدادات المستخدم",
|
||||
"values": {
|
||||
"false": "لا",
|
||||
"true": "نعم"
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"collapse": "",
|
||||
"conversation": "محادثة",
|
||||
"error_fetching": "خطأ أثناء جلب التحديثات",
|
||||
"load_older": "تحميل المنشورات القديمة",
|
||||
"no_retweet_hint": "",
|
||||
"repeated": "",
|
||||
"show_new": "عرض الجديد",
|
||||
"up_to_date": "تم تحديثه"
|
||||
},
|
||||
"user_card": {
|
||||
"approve": "قبول",
|
||||
"block": "حظر",
|
||||
"blocked": "تم حظره!",
|
||||
"deny": "رفض",
|
||||
"follow": "اتبع",
|
||||
"followees": "",
|
||||
"followers": "مُتابِعون",
|
||||
"following": "",
|
||||
"follows_you": "يتابعك!",
|
||||
"mute": "كتم",
|
||||
"muted": "تم كتمه",
|
||||
"per_day": "في اليوم",
|
||||
"remote_follow": "مُتابَعة عن بُعد",
|
||||
"statuses": "المنشورات"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "الخيط الزمني للمستخدم"
|
||||
},
|
||||
"who_to_follow": {
|
||||
"more": "المزيد",
|
||||
"who_to_follow": "للمتابعة"
|
||||
"content_warning": "الموضوع (اختياري)",
|
||||
"default": "وصلت للتوّ إلى لوس أنجلس.",
|
||||
"direct_warning": "",
|
||||
"posting": "النشر",
|
||||
"scope": {
|
||||
"direct": "",
|
||||
"private": "",
|
||||
"public": "علني - يُنشر على الخيوط الزمنية العمومية",
|
||||
"unlisted": "غير مُدرَج - لا يُنشَر على الخيوط الزمنية العمومية"
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"bio": "السيرة الذاتية",
|
||||
"email": "عنوان البريد الإلكتروني",
|
||||
"fullname": "الإسم المعروض",
|
||||
"password_confirm": "تأكيد الكلمة السرية",
|
||||
"registration": "التسجيل",
|
||||
"token": "رمز الدعوة"
|
||||
},
|
||||
"settings": {
|
||||
"attachmentRadius": "المُرفَقات",
|
||||
"attachments": "المُرفَقات",
|
||||
"autoload": "",
|
||||
"avatar": "الصورة الرمزية",
|
||||
"avatarAltRadius": "الصور الرمزية (الإشعارات)",
|
||||
"avatarRadius": "الصور الرمزية",
|
||||
"background": "الخلفية",
|
||||
"bio": "السيرة الذاتية",
|
||||
"btnRadius": "الأزرار",
|
||||
"cBlue": "أزرق (الرد، المتابَعة)",
|
||||
"cGreen": "أخضر (إعادة النشر)",
|
||||
"cOrange": "برتقالي (مفضلة)",
|
||||
"cRed": "أحمر (إلغاء)",
|
||||
"change_password": "تغيير كلمة السر",
|
||||
"change_password_error": "وقع هناك خلل أثناء تعديل كلمتك السرية.",
|
||||
"changed_password": "تم تغيير كلمة المرور بنجاح!",
|
||||
"collapse_subject": "",
|
||||
"confirm_new_password": "تأكيد كلمة السر الجديدة",
|
||||
"current_avatar": "صورتك الرمزية الحالية",
|
||||
"current_password": "كلمة السر الحالية",
|
||||
"current_profile_banner": "الرأسية الحالية لصفحتك الشخصية",
|
||||
"data_import_export_tab": "تصدير واستيراد البيانات",
|
||||
"default_vis": "أسلوب العرض الافتراضي",
|
||||
"delete_account": "حذف الحساب",
|
||||
"delete_account_description": "حذف حسابك و كافة منشوراتك نهائيًا.",
|
||||
"delete_account_error": "",
|
||||
"delete_account_instructions": "يُرجى إدخال كلمتك السرية أدناه لتأكيد عملية حذف الحساب.",
|
||||
"export_theme": "حفظ النموذج",
|
||||
"filtering": "التصفية",
|
||||
"filtering_explanation": "سيتم إخفاء كافة المنشورات التي تحتوي على هذه الكلمات، كلمة واحدة في كل سطر",
|
||||
"follow_export": "تصدير الاشتراكات",
|
||||
"follow_export_button": "تصدير الاشتراكات كملف csv",
|
||||
"follow_export_processing": "التصدير جارٍ، سوف يُطلَب منك تنزيل ملفك بعد حين",
|
||||
"follow_import": "استيراد الاشتراكات",
|
||||
"follow_import_error": "خطأ أثناء استيراد المتابِعين",
|
||||
"follows_imported": "",
|
||||
"foreground": "الأمامية",
|
||||
"general": "الإعدادات العامة",
|
||||
"hide_attachments_in_convo": "إخفاء المرفقات على المحادثات",
|
||||
"hide_attachments_in_tl": "إخفاء المرفقات على الخيط الزمني",
|
||||
"hide_post_stats": "",
|
||||
"hide_user_stats": "",
|
||||
"import_followers_from_a_csv_file": "",
|
||||
"import_theme": "تحميل نموذج",
|
||||
"inputRadius": "",
|
||||
"instance_default": "",
|
||||
"interfaceLanguage": "لغة الواجهة",
|
||||
"invalid_theme_imported": "",
|
||||
"limited_availability": "غير متوفر على متصفحك",
|
||||
"links": "الروابط",
|
||||
"lock_account_description": "",
|
||||
"loop_video": "",
|
||||
"loop_video_silent_only": "",
|
||||
"name": "الاسم",
|
||||
"name_bio": "الاسم والسيرة الذاتية",
|
||||
"new_password": "كلمة السر الجديدة",
|
||||
"no_rich_text_description": "",
|
||||
"notification_visibility": "نوع الإشعارات التي تريد عرضها",
|
||||
"notification_visibility_follows": "يتابع",
|
||||
"notification_visibility_likes": "الإعجابات",
|
||||
"notification_visibility_mentions": "الإشارات",
|
||||
"notification_visibility_repeats": "",
|
||||
"nsfw_clickthrough": "",
|
||||
"oauth_tokens": "رموز OAuth",
|
||||
"token": "رمز",
|
||||
"refresh_token": "رمز التحديث",
|
||||
"valid_until": "صالح حتى",
|
||||
"revoke_token": "سحب",
|
||||
"panelRadius": "",
|
||||
"pause_on_unfocused": "",
|
||||
"presets": "النماذج",
|
||||
"profile_background": "خلفية الصفحة الشخصية",
|
||||
"profile_banner": "رأسية الصفحة الشخصية",
|
||||
"profile_tab": "الملف الشخصي",
|
||||
"radii_help": "",
|
||||
"replies_in_timeline": "الردود على الخيط الزمني",
|
||||
"reply_link_preview": "",
|
||||
"reply_visibility_all": "عرض كافة الردود",
|
||||
"reply_visibility_following": "",
|
||||
"reply_visibility_self": "",
|
||||
"saving_err": "خطأ أثناء حفظ الإعدادات",
|
||||
"saving_ok": "تم حفظ الإعدادات",
|
||||
"security_tab": "الأمان",
|
||||
"set_new_avatar": "اختيار صورة رمزية جديدة",
|
||||
"set_new_profile_background": "اختيار خلفية جديدة للملف الشخصي",
|
||||
"set_new_profile_banner": "اختيار رأسية جديدة للصفحة الشخصية",
|
||||
"settings": "الإعدادات",
|
||||
"stop_gifs": "",
|
||||
"streaming": "",
|
||||
"text": "النص",
|
||||
"theme": "المظهر",
|
||||
"theme_help": "",
|
||||
"tooltipRadius": "",
|
||||
"user_settings": "إعدادات المستخدم",
|
||||
"values": {
|
||||
"false": "لا",
|
||||
"true": "نعم"
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"collapse": "",
|
||||
"conversation": "محادثة",
|
||||
"error_fetching": "خطأ أثناء جلب التحديثات",
|
||||
"load_older": "تحميل المنشورات القديمة",
|
||||
"no_retweet_hint": "",
|
||||
"repeated": "",
|
||||
"show_new": "عرض الجديد",
|
||||
"up_to_date": "تم تحديثه"
|
||||
},
|
||||
"user_card": {
|
||||
"approve": "قبول",
|
||||
"block": "حظر",
|
||||
"blocked": "تم حظره!",
|
||||
"deny": "رفض",
|
||||
"follow": "اتبع",
|
||||
"followees": "",
|
||||
"followers": "مُتابِعون",
|
||||
"following": "",
|
||||
"follows_you": "يتابعك!",
|
||||
"mute": "كتم",
|
||||
"muted": "تم كتمه",
|
||||
"per_day": "في اليوم",
|
||||
"remote_follow": "مُتابَعة عن بُعد",
|
||||
"statuses": "المنشورات"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "الخيط الزمني للمستخدم"
|
||||
},
|
||||
"who_to_follow": {
|
||||
"more": "المزيد",
|
||||
"who_to_follow": "للمتابعة"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,323 +1,551 @@
|
||||
{
|
||||
"general": {
|
||||
"submit": "Invia",
|
||||
"apply": "Applica",
|
||||
"more": "Altro",
|
||||
"generic_error": "Errore",
|
||||
"optional": "facoltativo",
|
||||
"show_more": "Mostra tutto",
|
||||
"show_less": "Ripiega",
|
||||
"dismiss": "Chiudi",
|
||||
"cancel": "Annulla",
|
||||
"disable": "Disabilita",
|
||||
"enable": "Abilita",
|
||||
"confirm": "Conferma",
|
||||
"verify": "Verifica"
|
||||
"general": {
|
||||
"submit": "Invia",
|
||||
"apply": "Applica",
|
||||
"more": "Altro",
|
||||
"generic_error": "Errore",
|
||||
"optional": "facoltativo",
|
||||
"show_more": "Mostra tutto",
|
||||
"show_less": "Ripiega",
|
||||
"dismiss": "Chiudi",
|
||||
"cancel": "Annulla",
|
||||
"disable": "Disabilita",
|
||||
"enable": "Abilita",
|
||||
"confirm": "Conferma",
|
||||
"verify": "Verifica",
|
||||
"peek": "Anteprima",
|
||||
"close": "Chiudi",
|
||||
"retry": "Riprova",
|
||||
"error_retry": "Per favore, riprova",
|
||||
"loading": "Carico…"
|
||||
},
|
||||
"nav": {
|
||||
"mentions": "Menzioni",
|
||||
"public_tl": "Sequenza pubblica",
|
||||
"timeline": "Sequenza personale",
|
||||
"twkn": "Sequenza globale",
|
||||
"chat": "Chat della stanza",
|
||||
"friend_requests": "Vogliono seguirti",
|
||||
"about": "Informazioni",
|
||||
"administration": "Amministrazione",
|
||||
"back": "Indietro",
|
||||
"interactions": "Interazioni",
|
||||
"dms": "Messaggi diretti",
|
||||
"user_search": "Ricerca utenti",
|
||||
"search": "Ricerca",
|
||||
"who_to_follow": "Chi seguire",
|
||||
"preferences": "Preferenze"
|
||||
},
|
||||
"notifications": {
|
||||
"followed_you": "ti segue",
|
||||
"notifications": "Notifiche",
|
||||
"read": "Letto!",
|
||||
"broken_favorite": "Stato sconosciuto, lo sto cercando…",
|
||||
"favorited_you": "ha gradito il tuo messaggio",
|
||||
"load_older": "Carica notifiche precedenti",
|
||||
"repeated_you": "ha condiviso il tuo messaggio",
|
||||
"follow_request": "vuole seguirti",
|
||||
"no_more_notifications": "Fine delle notifiche",
|
||||
"migrated_to": "è migrato verso",
|
||||
"reacted_with": "ha reagito con {0}"
|
||||
},
|
||||
"settings": {
|
||||
"attachments": "Allegati",
|
||||
"autoload": "Abilita caricamento automatico quando raggiungi il fondo pagina",
|
||||
"avatar": "Icona utente",
|
||||
"bio": "Introduzione",
|
||||
"current_avatar": "La tua icona attuale",
|
||||
"current_profile_banner": "Il tuo stendardo attuale",
|
||||
"filtering": "Filtri",
|
||||
"filtering_explanation": "Tutti i post contenenti queste parole saranno silenziati, una per riga",
|
||||
"hide_attachments_in_convo": "Nascondi gli allegati presenti nelle conversazioni",
|
||||
"hide_attachments_in_tl": "Nascondi gli allegati presenti nelle sequenze",
|
||||
"name": "Nome",
|
||||
"name_bio": "Nome ed introduzione",
|
||||
"nsfw_clickthrough": "Fai click per visualizzare gli allegati offuscati",
|
||||
"profile_background": "Sfondo della tua pagina",
|
||||
"profile_banner": "Stendardo del tuo profilo",
|
||||
"reply_link_preview": "Visualizza le risposte al passaggio del cursore",
|
||||
"set_new_avatar": "Scegli una nuova icona",
|
||||
"set_new_profile_background": "Scegli un nuovo sfondo per la tua pagina",
|
||||
"set_new_profile_banner": "Scegli un nuovo stendardo per il tuo profilo",
|
||||
"settings": "Impostazioni",
|
||||
"theme": "Tema",
|
||||
"user_settings": "Impostazioni Utente",
|
||||
"attachmentRadius": "Allegati",
|
||||
"avatarAltRadius": "Icone utente (Notifiche)",
|
||||
"avatarRadius": "Icone utente",
|
||||
"background": "Sfondo",
|
||||
"btnRadius": "Pulsanti",
|
||||
"cBlue": "Blu (risposte, seguire)",
|
||||
"cGreen": "Verde (ripeti)",
|
||||
"cOrange": "Arancione (gradire)",
|
||||
"cRed": "Rosso (annulla)",
|
||||
"change_password": "Cambia password",
|
||||
"change_password_error": "C'è stato un problema durante il cambiamento della password.",
|
||||
"changed_password": "Password cambiata correttamente!",
|
||||
"collapse_subject": "Ripiega messaggi con Oggetto",
|
||||
"confirm_new_password": "Conferma la nuova password",
|
||||
"current_password": "La tua password attuale",
|
||||
"data_import_export_tab": "Importa o esporta dati",
|
||||
"default_vis": "Visibilità predefinita dei messaggi",
|
||||
"delete_account": "Elimina profilo",
|
||||
"delete_account_description": "Elimina definitivamente i tuoi dati e disattiva il tuo profilo.",
|
||||
"delete_account_error": "C'è stato un problema durante l'eliminazione del tuo profilo. Se il problema persiste contatta l'amministratore della tua stanza.",
|
||||
"delete_account_instructions": "Digita la tua password nel campo sottostante per confermare l'eliminazione del tuo profilo.",
|
||||
"export_theme": "Salva impostazioni",
|
||||
"follow_export": "Esporta la lista di chi segui",
|
||||
"follow_export_button": "Esporta la lista di chi segui in un file CSV",
|
||||
"follow_export_processing": "Sto elaborando, presto ti sarà chiesto di scaricare il tuo file",
|
||||
"follow_import": "Importa la lista di chi segui",
|
||||
"follow_import_error": "Errore nell'importazione della lista di chi segui",
|
||||
"follows_imported": "Importazione riuscita! L'elaborazione richiederà un po' di tempo.",
|
||||
"foreground": "Primo piano",
|
||||
"general": "Generale",
|
||||
"hide_post_stats": "Nascondi statistiche dei messaggi (es. il numero di preferenze)",
|
||||
"hide_user_stats": "Nascondi statistiche dell'utente (es. il numero dei tuoi seguaci)",
|
||||
"import_followers_from_a_csv_file": "Importa una lista di chi segui da un file CSV",
|
||||
"import_theme": "Carica impostazioni",
|
||||
"inputRadius": "Campi di testo",
|
||||
"instance_default": "(predefinito: {value})",
|
||||
"interfaceLanguage": "Lingua dell'interfaccia",
|
||||
"invalid_theme_imported": "Il file selezionato non è un tema supportato da Pleroma. Il tuo tema non è stato modificato.",
|
||||
"limited_availability": "Non disponibile nel tuo browser",
|
||||
"links": "Collegamenti",
|
||||
"lock_account_description": "Limita il tuo account solo a seguaci approvati",
|
||||
"loop_video": "Riproduci video in ciclo continuo",
|
||||
"loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le \"gif\" di Mastodon)",
|
||||
"new_password": "Nuova password",
|
||||
"notification_visibility": "Tipi di notifiche da mostrare",
|
||||
"notification_visibility_follows": "Nuove persone ti seguono",
|
||||
"notification_visibility_likes": "Preferiti",
|
||||
"notification_visibility_mentions": "Menzioni",
|
||||
"notification_visibility_repeats": "Condivisioni",
|
||||
"no_rich_text_description": "Togli la formattazione del testo da tutti i messaggi",
|
||||
"oauth_tokens": "Token OAuth",
|
||||
"token": "Token",
|
||||
"refresh_token": "Aggiorna token",
|
||||
"valid_until": "Valido fino a",
|
||||
"revoke_token": "Revoca",
|
||||
"panelRadius": "Pannelli",
|
||||
"pause_on_unfocused": "Interrompi l'aggiornamento continuo mentre la scheda è in secondo piano",
|
||||
"presets": "Valori predefiniti",
|
||||
"profile_tab": "Profilo",
|
||||
"radii_help": "Imposta il raggio degli angoli (in pixel)",
|
||||
"replies_in_timeline": "Risposte nella sequenza personale",
|
||||
"reply_visibility_all": "Mostra tutte le risposte",
|
||||
"reply_visibility_following": "Mostra solo le risposte rivolte a me o agli utenti che seguo",
|
||||
"reply_visibility_self": "Mostra solo risposte rivolte a me",
|
||||
"saving_err": "Errore nel salvataggio delle impostazioni",
|
||||
"saving_ok": "Impostazioni salvate",
|
||||
"security_tab": "Sicurezza",
|
||||
"stop_gifs": "Riproduci GIF al passaggio del cursore",
|
||||
"streaming": "Mostra automaticamente i nuovi messaggi quando sei in cima alla pagina",
|
||||
"text": "Testo",
|
||||
"theme_help": "Usa codici colore esadecimali (#rrggbb) per personalizzare il tuo schema di colori.",
|
||||
"tooltipRadius": "Suggerimenti/avvisi",
|
||||
"values": {
|
||||
"false": "no",
|
||||
"true": "sì"
|
||||
},
|
||||
"nav": {
|
||||
"mentions": "Menzioni",
|
||||
"public_tl": "Sequenza pubblica",
|
||||
"timeline": "Sequenza personale",
|
||||
"twkn": "Sequenza globale",
|
||||
"chat": "Chat della stanza",
|
||||
"friend_requests": "Vogliono seguirti",
|
||||
"about": "Informazioni",
|
||||
"administration": "Amministrazione",
|
||||
"back": "Indietro",
|
||||
"interactions": "Interazioni",
|
||||
"dms": "Messaggi diretti",
|
||||
"user_search": "Ricerca utenti",
|
||||
"search": "Ricerca",
|
||||
"who_to_follow": "Chi seguire",
|
||||
"preferences": "Preferenze"
|
||||
"avatar_size_instruction": "La taglia minima per l'icona personale è 150x150 pixel.",
|
||||
"domain_mutes": "Domini",
|
||||
"discoverable": "Permetti la scoperta di questo profilo da servizi di ricerca ed altro",
|
||||
"composing": "Composizione",
|
||||
"changed_email": "Email cambiata con successo!",
|
||||
"change_email_error": "C'è stato un problema nel cambiare la tua email.",
|
||||
"change_email": "Cambia email",
|
||||
"blocks_tab": "Bloccati",
|
||||
"blocks_imported": "Blocchi importati! Saranno elaborati a breve.",
|
||||
"block_import_error": "Errore nell'importazione",
|
||||
"block_import": "Importa blocchi",
|
||||
"block_export_button": "Esporta i tuoi blocchi in un file CSV",
|
||||
"block_export": "Esporta blocchi",
|
||||
"allow_following_move": "Consenti",
|
||||
"mfa": {
|
||||
"verify": {
|
||||
"desc": "Per abilitare l'autenticazione bifattoriale, inserisci il codice fornito dalla tua applicazione:"
|
||||
},
|
||||
"scan": {
|
||||
"secret_code": "Codice",
|
||||
"desc": "Con la tua applicazione bifattoriale, acquisisci questo QR o inserisci il codice manualmente:",
|
||||
"title": "Acquisisci"
|
||||
},
|
||||
"authentication_methods": "Metodi di accesso",
|
||||
"recovery_codes_warning": "Appuntati i codici o salvali in un posto sicuro, altrimenti rischi di non rivederli mai più. Se perderai l'accesso sia alla tua applicazione bifattoriale che ai codici di recupero non potrai più accedere al tuo profilo.",
|
||||
"waiting_a_recovery_codes": "Ricevo codici di recupero…",
|
||||
"recovery_codes": "Codici di recupero.",
|
||||
"warning_of_generate_new_codes": "Alla generazione di nuovi codici di recupero, quelli vecchi saranno disattivati.",
|
||||
"generate_new_recovery_codes": "Genera nuovi codici di recupero",
|
||||
"title": "Accesso bifattoriale",
|
||||
"confirm_and_enable": "Conferma ed abilita OTP",
|
||||
"wait_pre_setup_otp": "preimposto OTP",
|
||||
"setup_otp": "Imposta OTP",
|
||||
"otp": "OTP"
|
||||
},
|
||||
"notifications": {
|
||||
"followed_you": "ti segue",
|
||||
"notifications": "Notifiche",
|
||||
"read": "Letto!",
|
||||
"broken_favorite": "Stato sconosciuto, lo sto cercando...",
|
||||
"favorited_you": "ha gradito il tuo messaggio",
|
||||
"load_older": "Carica notifiche precedenti",
|
||||
"repeated_you": "ha condiviso il tuo messaggio",
|
||||
"follow_request": "vuole seguirti",
|
||||
"no_more_notifications": "Fine delle notifiche",
|
||||
"migrated_to": "è migrato verso",
|
||||
"reacted_with": "ha reagito con"
|
||||
},
|
||||
"settings": {
|
||||
"attachments": "Allegati",
|
||||
"autoload": "Abilita caricamento automatico quando raggiungi il fondo pagina",
|
||||
"avatar": "Icona utente",
|
||||
"bio": "Introduzione",
|
||||
"current_avatar": "La tua icona attuale",
|
||||
"current_profile_banner": "Il tuo stendardo attuale",
|
||||
"filtering": "Filtri",
|
||||
"filtering_explanation": "Tutti i post contenenti queste parole saranno silenziati, una per riga",
|
||||
"hide_attachments_in_convo": "Nascondi gli allegati presenti nelle conversazioni",
|
||||
"hide_attachments_in_tl": "Nascondi gli allegati presenti nelle sequenze",
|
||||
"name": "Nome",
|
||||
"name_bio": "Nome ed introduzione",
|
||||
"nsfw_clickthrough": "Fai click per visualizzare gli allegati nascosti",
|
||||
"profile_background": "Sfondo della tua pagina",
|
||||
"profile_banner": "Stendardo del tuo profilo",
|
||||
"reply_link_preview": "Visualizza le risposte al passaggio del cursore",
|
||||
"set_new_avatar": "Scegli una nuova icona",
|
||||
"set_new_profile_background": "Scegli un nuovo sfondo per la tua pagina",
|
||||
"set_new_profile_banner": "Scegli un nuovo stendardo per il tuo profilo",
|
||||
"settings": "Impostazioni",
|
||||
"theme": "Tema",
|
||||
"user_settings": "Impostazioni Utente",
|
||||
"attachmentRadius": "Allegati",
|
||||
"avatarAltRadius": "Icone utente (Notifiche)",
|
||||
"avatarRadius": "Icone utente",
|
||||
"background": "Sfondo",
|
||||
"btnRadius": "Pulsanti",
|
||||
"cBlue": "Blu (risposte, seguire)",
|
||||
"cGreen": "Verde (ripeti)",
|
||||
"cOrange": "Arancione (gradire)",
|
||||
"cRed": "Rosso (annulla)",
|
||||
"change_password": "Cambia password",
|
||||
"change_password_error": "C'è stato un problema durante il cambiamento della password.",
|
||||
"changed_password": "Password cambiata correttamente!",
|
||||
"collapse_subject": "Ripiega messaggi con Oggetto",
|
||||
"confirm_new_password": "Conferma la nuova password",
|
||||
"current_password": "La tua password attuale",
|
||||
"data_import_export_tab": "Importa o esporta dati",
|
||||
"default_vis": "Visibilità predefinita dei messaggi",
|
||||
"delete_account": "Elimina profilo",
|
||||
"delete_account_description": "Elimina definitivamente i tuoi dati e disattiva il tuo profilo.",
|
||||
"delete_account_error": "C'è stato un problema durante l'eliminazione del tuo profilo. Se il problema persiste contatta l'amministratore della tua stanza.",
|
||||
"delete_account_instructions": "Digita la tua password nel campo sottostante per confermare l'eliminazione del tuo profilo.",
|
||||
"export_theme": "Salva impostazioni",
|
||||
"follow_export": "Esporta la lista di chi segui",
|
||||
"follow_export_button": "Esporta la lista di chi segui in un file CSV",
|
||||
"follow_export_processing": "Sto elaborando, presto ti sarà chiesto di scaricare il tuo file",
|
||||
"follow_import": "Importa la lista di chi segui",
|
||||
"follow_import_error": "Errore nell'importazione della lista di chi segui",
|
||||
"follows_imported": "Importazione riuscita! L'elaborazione richiederà un po' di tempo.",
|
||||
"foreground": "Primo piano",
|
||||
"general": "Generale",
|
||||
"hide_post_stats": "Nascondi statistiche dei messaggi (es. il numero di preferenze)",
|
||||
"hide_user_stats": "Nascondi statistiche dell'utente (es. il numero dei tuoi seguaci)",
|
||||
"import_followers_from_a_csv_file": "Importa una lista di chi segui da un file CSV",
|
||||
"import_theme": "Carica impostazioni",
|
||||
"inputRadius": "Campi di testo",
|
||||
"instance_default": "(predefinito: {value})",
|
||||
"interfaceLanguage": "Lingua dell'interfaccia",
|
||||
"invalid_theme_imported": "Il file selezionato non è un tema supportato da Pleroma. Il tuo tema non è stato modificato.",
|
||||
"limited_availability": "Non disponibile nel tuo browser",
|
||||
"links": "Collegamenti",
|
||||
"lock_account_description": "Limita il tuo account solo a seguaci approvati",
|
||||
"loop_video": "Riproduci video in ciclo continuo",
|
||||
"loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le \"gif\" di Mastodon)",
|
||||
"new_password": "Nuova password",
|
||||
"notification_visibility": "Tipi di notifiche da mostrare",
|
||||
"notification_visibility_follows": "Nuove persone ti seguono",
|
||||
"notification_visibility_likes": "Preferiti",
|
||||
"notification_visibility_mentions": "Menzioni",
|
||||
"notification_visibility_repeats": "Condivisioni",
|
||||
"no_rich_text_description": "Togli la formattazione del testo da tutti i messaggi",
|
||||
"oauth_tokens": "Token OAuth",
|
||||
"token": "Token",
|
||||
"refresh_token": "Aggiorna token",
|
||||
"valid_until": "Valido fino a",
|
||||
"revoke_token": "Revoca",
|
||||
"panelRadius": "Pannelli",
|
||||
"pause_on_unfocused": "Interrompi l'aggiornamento continuo mentre la scheda è in secondo piano",
|
||||
"presets": "Valori predefiniti",
|
||||
"profile_tab": "Profilo",
|
||||
"radii_help": "Imposta il raggio degli angoli (in pixel)",
|
||||
"replies_in_timeline": "Risposte nella sequenza personale",
|
||||
"reply_visibility_all": "Mostra tutte le risposte",
|
||||
"reply_visibility_following": "Mostra solo le risposte rivolte a me o agli utenti che seguo",
|
||||
"reply_visibility_self": "Mostra solo risposte rivolte a me",
|
||||
"saving_err": "Errore nel salvataggio delle impostazioni",
|
||||
"saving_ok": "Impostazioni salvate",
|
||||
"security_tab": "Sicurezza",
|
||||
"stop_gifs": "Riproduci GIF al passaggio del cursore",
|
||||
"streaming": "Mostra automaticamente i nuovi messaggi quando sei in cima alla pagina",
|
||||
"text": "Testo",
|
||||
"theme_help": "Usa codici colore esadecimali (#rrggbb) per personalizzare il tuo schema di colori.",
|
||||
"tooltipRadius": "Descrizioni/avvisi",
|
||||
"values": {
|
||||
"false": "no",
|
||||
"true": "sì"
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"error_fetching": "Errore nell'aggiornamento",
|
||||
"load_older": "Carica messaggi più vecchi",
|
||||
"show_new": "Mostra nuovi",
|
||||
"up_to_date": "Aggiornato",
|
||||
"collapse": "Riduci",
|
||||
"conversation": "Conversazione",
|
||||
"no_retweet_hint": "Il messaggio è diretto o solo per seguaci e non può essere condiviso",
|
||||
"repeated": "condiviso"
|
||||
},
|
||||
"user_card": {
|
||||
"follow": "Segui",
|
||||
"followees": "Chi stai seguendo",
|
||||
"followers": "Seguaci",
|
||||
"following": "Seguìto!",
|
||||
"follows_you": "Ti segue!",
|
||||
"mute": "Silenzia",
|
||||
"muted": "Silenziato",
|
||||
"per_day": "al giorno",
|
||||
"statuses": "Messaggi",
|
||||
"approve": "Approva",
|
||||
"block": "Blocca",
|
||||
"blocked": "Bloccato!",
|
||||
"deny": "Nega",
|
||||
"remote_follow": "Segui da remoto"
|
||||
},
|
||||
"chat": {
|
||||
"title": "Chat"
|
||||
},
|
||||
"features_panel": {
|
||||
"chat": "Chat",
|
||||
"gopher": "Gopher",
|
||||
"media_proxy": "Proxy multimedia",
|
||||
"scope_options": "Opzioni visibilità",
|
||||
"text_limit": "Lunghezza massima",
|
||||
"title": "Caratteristiche",
|
||||
"who_to_follow": "Chi seguire"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "Errore nel recupero dell'utente",
|
||||
"find_user": "Trova utente"
|
||||
},
|
||||
"login": {
|
||||
"login": "Accedi",
|
||||
"logout": "Disconnettiti",
|
||||
"password": "Password",
|
||||
"placeholder": "es. Lupo Lucio",
|
||||
"register": "Registrati",
|
||||
"username": "Nome utente",
|
||||
"description": "Accedi con OAuth",
|
||||
"hint": "Accedi per partecipare alla discussione",
|
||||
"authentication_code": "Codice di autenticazione",
|
||||
"enter_recovery_code": "Inserisci un codice di recupero",
|
||||
"enter_two_factor_code": "Inserisci un codice two-factor",
|
||||
"recovery_code": "Codice di recupero",
|
||||
"heading": {
|
||||
"totp": "Autenticazione two-factor",
|
||||
"recovery": "Recupero two-factor"
|
||||
}
|
||||
},
|
||||
"post_status": {
|
||||
"account_not_locked_warning": "Il tuo profilo non è {0}. Chiunque può seguirti e vedere i tuoi messaggi riservati ai tuoi seguaci.",
|
||||
"account_not_locked_warning_link": "protetto",
|
||||
"attachments_sensitive": "Nascondi gli allegati",
|
||||
"content_type": {
|
||||
"text/plain": "Testo normale"
|
||||
"enter_current_password_to_confirm": "Inserisci la tua password per identificarti",
|
||||
"security": "Sicurezza",
|
||||
"app_name": "Nome applicazione",
|
||||
"style": {
|
||||
"switcher": {
|
||||
"help": {
|
||||
"older_version_imported": "Il tema importato è stato creato per una versione precedente dell'interfaccia.",
|
||||
"future_version_imported": "Il tema importato è stato creato per una versione più recente dell'interfaccia.",
|
||||
"v2_imported": "Il tema importato è stato creato per una vecchia interfaccia. Non tutto potrebbe essere come prima.",
|
||||
"upgraded_from_v2": "L'interfaccia è stata aggiornata, il tema potrebbe essere diverso da come lo intendevi.",
|
||||
"migration_snapshot_ok": "Ho caricato l'anteprima del tema. Puoi provare a caricarne i contenuti.",
|
||||
"fe_downgraded": "L'interfaccia è stata portata ad una versione precedente.",
|
||||
"fe_upgraded": "Lo schema dei temi è stato aggiornato insieme all'interfaccia.",
|
||||
"snapshot_missing": "Il tema non è provvisto di anteprima, quindi potrebbe essere diverso da come appare.",
|
||||
"snapshot_present": "Tutti i valori sono sostituiti dall'anteprima del tema. Puoi invece caricare i suoi contenuti.",
|
||||
"snapshot_source_mismatch": "Conflitto di versione: probabilmente l'interfaccia è stata portata ad una versione precedente e poi aggiornata di nuovo. Se hai modificato il tema con una versione precedente dell'interfaccia, usa la vecchia versione del tema, altrimenti puoi usare la nuova.",
|
||||
"migration_napshot_gone": "Anteprima del tema non trovata, non tutto potrebbe essere come ricordi."
|
||||
},
|
||||
"content_warning": "Oggetto (facoltativo)",
|
||||
"default": "Sono appena atterrato a Fiumicino.",
|
||||
"direct_warning": "Questo post sarà visibile solo dagli utenti menzionati.",
|
||||
"posting": "Sto pubblicando",
|
||||
"scope": {
|
||||
"direct": "Diretto - Visibile solo agli utenti menzionati",
|
||||
"private": "Solo per seguaci - Visibile solo dai tuoi seguaci",
|
||||
"public": "Pubblico - Visibile sulla sequenza pubblica",
|
||||
"unlisted": "Non elencato - Non visibile sulla sequenza pubblica"
|
||||
"use_source": "Nuova versione",
|
||||
"use_snapshot": "Versione precedente",
|
||||
"keep_as_is": "Mantieni tal quale",
|
||||
"load_theme": "Carica tema",
|
||||
"clear_opacity": "Rimuovi opacità",
|
||||
"clear_all": "Azzera tutto",
|
||||
"reset": "Reimposta",
|
||||
"save_load_hint": "Le opzioni \"mantieni\" conservano le impostazioni correnti quando selezioni o carichi un tema, e le salvano quando ne esporti uno. Quando nessuna casella è selezionata, tutte le impostazioni correnti saranno salvate nel tema.",
|
||||
"keep_fonts": "Mantieni font",
|
||||
"keep_roundness": "Mantieni vertici",
|
||||
"keep_opacity": "Mantieni opacità",
|
||||
"keep_shadows": "Mantieni ombre",
|
||||
"keep_color": "Mantieni colori"
|
||||
},
|
||||
"common": {
|
||||
"opacity": "Opacità",
|
||||
"color": "Colore",
|
||||
"contrast": {
|
||||
"context": {
|
||||
"text": "per il testo",
|
||||
"18pt": "per il testo grande (oltre 17pt)"
|
||||
},
|
||||
"level": {
|
||||
"bad": "non soddisfa le linee guida di alcun livello",
|
||||
"aaa": "soddisfa le linee guida di livello AAA (ottimo)",
|
||||
"aa": "soddisfa le linee guida di livello AA (sufficiente)"
|
||||
},
|
||||
"hint": "Il rapporto di contrasto è {ratio}, e {level} {context}"
|
||||
}
|
||||
},
|
||||
"advanced_colors": {
|
||||
"badge": "Sfondo medaglie",
|
||||
"post": "Messaggi / Biografie",
|
||||
"alert_neutral": "Neutro",
|
||||
"alert_warning": "Attenzione",
|
||||
"alert_error": "Errore",
|
||||
"alert": "Sfondo degli avvertimenti",
|
||||
"_tab_label": "Avanzate",
|
||||
"tabs": "Etichette",
|
||||
"disabled": "Disabilitato",
|
||||
"selectedMenu": "Voce menù selezionata",
|
||||
"selectedPost": "Messaggio selezionato",
|
||||
"pressed": "Premuto",
|
||||
"highlight": "Elementi evidenziati",
|
||||
"icons": "Icone",
|
||||
"poll": "Grafico sondaggi",
|
||||
"underlay": "Sottostante",
|
||||
"faint_text": "Testo sbiadito",
|
||||
"inputs": "Campi d'immissione",
|
||||
"buttons": "Pulsanti",
|
||||
"borders": "Bordi",
|
||||
"top_bar": "Barra superiore",
|
||||
"panel_header": "Titolo pannello",
|
||||
"badge_notification": "Notifica",
|
||||
"popover": "Suggerimenti, menù, sbalzi"
|
||||
},
|
||||
"common_colors": {
|
||||
"rgbo": "Icone, accenti, medaglie",
|
||||
"foreground_hint": "Seleziona l'etichetta \"Avanzate\" per controlli più fini",
|
||||
"main": "Colori comuni",
|
||||
"_tab_label": "Comuni"
|
||||
},
|
||||
"shadows": {
|
||||
"inset": "Includi",
|
||||
"spread": "Spandi",
|
||||
"blur": "Sfoca",
|
||||
"shadow_id": "Ombra numero {value}",
|
||||
"override": "Sostituisci",
|
||||
"component": "Componente",
|
||||
"_tab_label": "Luci ed ombre"
|
||||
},
|
||||
"radii": {
|
||||
"_tab_label": "Raggio"
|
||||
}
|
||||
},
|
||||
"registration": {
|
||||
"bio": "Introduzione",
|
||||
"email": "Email",
|
||||
"fullname": "Nome visualizzato",
|
||||
"password_confirm": "Conferma password",
|
||||
"registration": "Registrazione",
|
||||
"token": "Codice d'invito"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "Sequenza dell'Utente"
|
||||
},
|
||||
"who_to_follow": {
|
||||
"more": "Altro",
|
||||
"who_to_follow": "Chi seguire"
|
||||
},
|
||||
"about": {
|
||||
"mrf": {
|
||||
"federation": "Federazione",
|
||||
"keyword": {
|
||||
"reject": "Rifiuta",
|
||||
"replace": "Sostituisci",
|
||||
"is_replaced_by": "→",
|
||||
"keyword_policies": "Regole per parole chiave",
|
||||
"ftl_removal": "Rimozione dalla sequenza globale"
|
||||
},
|
||||
"simple": {
|
||||
"reject": "Rifiuta",
|
||||
"accept": "Accetta",
|
||||
"simple_policies": "Regole specifiche alla stanza",
|
||||
"accept_desc": "Questa stanza accetta messaggi solo dalle seguenti stanze:",
|
||||
"reject_desc": "Questa stanza non accetterà messaggi dalle stanze seguenti:",
|
||||
"quarantine": "Quarantena",
|
||||
"quarantine_desc": "Questa stanza inoltrerà solo messaggi pubblici alle seguenti stanze:",
|
||||
"ftl_removal": "Rimozione dalla sequenza globale",
|
||||
"ftl_removal_desc": "Questa stanza rimuove le seguenti stanze dalla sequenza globale:",
|
||||
"media_removal": "Rimozione multimedia",
|
||||
"media_removal_desc": "Questa istanza rimuove gli allegati dalle seguenti stanze:",
|
||||
"media_nsfw": "Allegati oscurati forzatamente",
|
||||
"media_nsfw_desc": "Questa stanza oscura gli allegati dei messaggi provenienti da queste stanze:"
|
||||
},
|
||||
"mrf_policies": "Regole RM abilitate",
|
||||
"mrf_policies_desc": "Le regole RM cambiano il comportamento federativo della stanza. Vigono le seguenti regole:"
|
||||
},
|
||||
"staff": "Equipaggio"
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"mute": "Zittisci",
|
||||
"mute_progress": "Zittisco...",
|
||||
"unmute": "Ascolta",
|
||||
"unmute_progress": "Procedo..."
|
||||
},
|
||||
"exporter": {
|
||||
"export": "Esporta",
|
||||
"processing": "In elaborazione, il tuo file sarà scaricabile a breve"
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop_picture": "Ritaglia immagine",
|
||||
"save": "Salva",
|
||||
"save_without_cropping": "Salva senza ritagliare",
|
||||
"cancel": "Annulla"
|
||||
"enable_web_push_notifications": "Abilita notifiche web push",
|
||||
"fun": "Divertimento",
|
||||
"notification_mutes": "Per non ricevere notifiche da uno specifico utente, zittiscilo.",
|
||||
"notification_setting_privacy_option": "Nascondi mittente e contenuti delle notifiche push",
|
||||
"notification_setting_privacy": "Privacy",
|
||||
"notification_setting_followers": "Utenti che ti seguono",
|
||||
"notification_setting_non_followers": "Utenti che non ti seguono",
|
||||
"notification_setting_non_follows": "Utenti che non segui",
|
||||
"notification_setting_follows": "Utenti che segui",
|
||||
"notification_setting": "Ricevi notifiche da:",
|
||||
"notification_setting_filters": "Filtri",
|
||||
"notifications": "Notifiche",
|
||||
"greentext": "Frecce da meme",
|
||||
"upload_a_photo": "Carica un'immagine",
|
||||
"type_domains_to_mute": "Cerca domini da zittire",
|
||||
"theme_help_v2_2": "Le icone dietro alcuni elementi sono indicatori del contrasto fra testo e sfondo, passaci sopra col puntatore per ulteriori informazioni. Se si usano delle trasparenze, questi indicatori mostrano il peggior caso possibile.",
|
||||
"theme_help_v2_1": "Puoi anche forzare colore ed opacità di alcuni elementi selezionando la casella. Usa il pulsante \"Azzera\" per azzerare tutte le forzature.",
|
||||
"useStreamingApiWarning": "(Sconsigliato, sperimentale, può saltare messaggi)",
|
||||
"useStreamingApi": "Ricevi messaggi e notifiche in tempo reale",
|
||||
"user_mutes": "Utenti",
|
||||
"post_status_content_type": "Tipo di contenuto dei messaggi",
|
||||
"subject_line_noop": "Non copiare",
|
||||
"subject_line_mastodon": "Come in Mastodon: copia tal quale",
|
||||
"subject_line_email": "Come nelle email: \"re: oggetto\"",
|
||||
"subject_line_behavior": "Copia oggetto quando rispondi",
|
||||
"subject_input_always_show": "Mostra sempre il campo Oggetto",
|
||||
"minimal_scopes_mode": "Riduci opzioni di visibilità",
|
||||
"scope_copy": "Risposte ereditano la visibilità (messaggi privati lo fanno sempre)",
|
||||
"search_user_to_mute": "Cerca utente da zittire",
|
||||
"search_user_to_block": "Cerca utente da bloccare",
|
||||
"autohide_floating_post_button": "Nascondi automaticamente il pulsante di composizione (mobile)",
|
||||
"show_moderator_badge": "Mostra l'insegna di moderatore sulla mia pagina",
|
||||
"show_admin_badge": "Mostra l'insegna di amministratore sulla mia pagina",
|
||||
"hide_followers_count_description": "Non mostrare quanti seguaci ho",
|
||||
"hide_follows_count_description": "Non mostrare quanti utenti seguo",
|
||||
"hide_followers_description": "Non mostrare i miei seguaci",
|
||||
"hide_follows_description": "Non mostrare chi seguo",
|
||||
"no_mutes": "Nessun utente zittito",
|
||||
"no_blocks": "Nessun utente bloccato",
|
||||
"notification_visibility_emoji_reactions": "Reazioni",
|
||||
"notification_visibility_moves": "Migrazioni utenti",
|
||||
"new_email": "Nuova email",
|
||||
"use_contain_fit": "Non ritagliare le anteprime degli allegati",
|
||||
"play_videos_in_modal": "Riproduci video in un riquadro a sbalzo",
|
||||
"mutes_tab": "Zittiti",
|
||||
"interface": "Interfaccia",
|
||||
"instance_default_simple": "(predefinito)",
|
||||
"checkboxRadius": "Caselle di selezione",
|
||||
"import_blocks_from_a_csv_file": "Importa blocchi da un file CSV",
|
||||
"hide_filtered_statuses": "Nascondi messaggi filtrati",
|
||||
"use_one_click_nsfw": "Apri media offuscati con un solo click",
|
||||
"preload_images": "Precarica immagini",
|
||||
"hide_isp": "Nascondi pannello della stanza",
|
||||
"max_thumbnails": "Numero massimo di anteprime per messaggio",
|
||||
"hide_muted_posts": "Nascondi messaggi degli utenti zittiti",
|
||||
"accent": "Accento",
|
||||
"emoji_reactions_on_timeline": "Mostra emoji di reazione sulle sequenze",
|
||||
"pad_emoji": "Affianca spazi agli emoji inseriti tramite selettore",
|
||||
"notification_blocks": "Bloccando un utente non riceverai più le sue notifiche né lo seguirai più.",
|
||||
"mutes_and_blocks": "Zittiti e bloccati"
|
||||
},
|
||||
"timeline": {
|
||||
"error_fetching": "Errore nell'aggiornamento",
|
||||
"load_older": "Carica messaggi più vecchi",
|
||||
"show_new": "Mostra nuovi",
|
||||
"up_to_date": "Aggiornato",
|
||||
"collapse": "Riduci",
|
||||
"conversation": "Conversazione",
|
||||
"no_retweet_hint": "Il messaggio è diretto o solo per seguaci e non può essere condiviso",
|
||||
"repeated": "condiviso"
|
||||
},
|
||||
"user_card": {
|
||||
"follow": "Segui",
|
||||
"followees": "Chi stai seguendo",
|
||||
"followers": "Seguaci",
|
||||
"following": "Seguìto!",
|
||||
"follows_you": "Ti segue!",
|
||||
"mute": "Silenzia",
|
||||
"muted": "Silenziato",
|
||||
"per_day": "al giorno",
|
||||
"statuses": "Messaggi",
|
||||
"approve": "Approva",
|
||||
"block": "Blocca",
|
||||
"blocked": "Bloccato!",
|
||||
"deny": "Nega",
|
||||
"remote_follow": "Segui da remoto"
|
||||
},
|
||||
"chat": {
|
||||
"title": "Chat"
|
||||
},
|
||||
"features_panel": {
|
||||
"chat": "Chat",
|
||||
"gopher": "Gopher",
|
||||
"media_proxy": "Proxy multimedia",
|
||||
"scope_options": "Opzioni visibilità",
|
||||
"text_limit": "Lunghezza massima",
|
||||
"title": "Caratteristiche",
|
||||
"who_to_follow": "Chi seguire"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "Errore nel recupero dell'utente",
|
||||
"find_user": "Trova utente"
|
||||
},
|
||||
"login": {
|
||||
"login": "Accedi",
|
||||
"logout": "Disconnettiti",
|
||||
"password": "Password",
|
||||
"placeholder": "es. Lupo Lucio",
|
||||
"register": "Registrati",
|
||||
"username": "Nome utente",
|
||||
"description": "Accedi con OAuth",
|
||||
"hint": "Accedi per partecipare alla discussione",
|
||||
"authentication_code": "Codice di autenticazione",
|
||||
"enter_recovery_code": "Inserisci un codice di recupero",
|
||||
"enter_two_factor_code": "Inserisci un codice two-factor",
|
||||
"recovery_code": "Codice di recupero",
|
||||
"heading": {
|
||||
"totp": "Autenticazione two-factor",
|
||||
"recovery": "Recupero two-factor"
|
||||
}
|
||||
},
|
||||
"post_status": {
|
||||
"account_not_locked_warning": "Il tuo profilo non è {0}. Chiunque può seguirti e vedere i tuoi messaggi riservati ai tuoi seguaci.",
|
||||
"account_not_locked_warning_link": "protetto",
|
||||
"attachments_sensitive": "Nascondi gli allegati",
|
||||
"content_type": {
|
||||
"text/plain": "Testo normale",
|
||||
"text/bbcode": "BBCode",
|
||||
"text/markdown": "Markdown",
|
||||
"text/html": "HTML"
|
||||
},
|
||||
"importer": {
|
||||
"submit": "Invia",
|
||||
"success": "Importato.",
|
||||
"error": "L'importazione non è andata a buon fine."
|
||||
"content_warning": "Oggetto (facoltativo)",
|
||||
"default": "Sono appena atterrato a Fiumicino.",
|
||||
"direct_warning": "Questo post sarà visibile solo dagli utenti menzionati.",
|
||||
"posting": "Sto pubblicando",
|
||||
"scope": {
|
||||
"direct": "Diretto - Visibile solo agli utenti menzionati",
|
||||
"private": "Solo per seguaci - Visibile solo dai tuoi seguaci",
|
||||
"public": "Pubblico - Visibile sulla sequenza pubblica",
|
||||
"unlisted": "Non elencato - Non visibile sulla sequenza pubblica"
|
||||
},
|
||||
"media_modal": {
|
||||
"previous": "Precedente",
|
||||
"next": "Prossimo"
|
||||
"scope_notice": {
|
||||
"unlisted": "Questo messaggio non sarà visibile sulla sequenza locale né su quella pubblica",
|
||||
"private": "Questo messaggio sarà visibile solo ai tuoi seguaci",
|
||||
"public": "Questo messaggio sarà visibile a tutti"
|
||||
},
|
||||
"polls": {
|
||||
"add_poll": "Sondaggio",
|
||||
"add_option": "Alternativa",
|
||||
"option": "Opzione",
|
||||
"votes": "voti",
|
||||
"vote": "Vota",
|
||||
"type": "Tipo di sondaggio",
|
||||
"single_choice": "Scelta singola",
|
||||
"multiple_choices": "Scelta multipla",
|
||||
"expiry": "Scadenza",
|
||||
"expires_in": "Scade fra {0}",
|
||||
"expired": "Scaduto {0} fa",
|
||||
"not_enough_options": "Aggiungi altre risposte"
|
||||
"direct_warning_to_first_only": "Questo messaggio sarà visibile solo agli utenti menzionati all'inizio.",
|
||||
"direct_warning_to_all": "Questo messaggio sarà visibile a tutti i menzionati.",
|
||||
"new_status": "Nuovo messaggio"
|
||||
},
|
||||
"registration": {
|
||||
"bio": "Introduzione",
|
||||
"email": "Email",
|
||||
"fullname": "Nome visualizzato",
|
||||
"password_confirm": "Conferma password",
|
||||
"registration": "Registrazione",
|
||||
"token": "Codice d'invito",
|
||||
"validations": {
|
||||
"password_confirmation_match": "dovrebbe essere uguale alla password",
|
||||
"password_confirmation_required": "non può essere vuoto",
|
||||
"password_required": "non può essere vuoto",
|
||||
"email_required": "non può essere vuoto",
|
||||
"fullname_required": "non può essere vuoto",
|
||||
"username_required": "non può essere vuoto"
|
||||
},
|
||||
"interactions": {
|
||||
"favs_repeats": "Condivisi e preferiti"
|
||||
"bio_placeholder": "es.\nCiao, sono Lupo Lucio.\nSono un lupo fantastico che vive nel Fantabosco. Forse mi hai visto alla Melevisione.",
|
||||
"fullname_placeholder": "es. Lupo Lucio",
|
||||
"username_placeholder": "es. mister_wolf",
|
||||
"new_captcha": "Clicca l'immagine per avere un altro captcha",
|
||||
"captcha": "CAPTCHA"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "Sequenza dell'Utente"
|
||||
},
|
||||
"who_to_follow": {
|
||||
"more": "Altro",
|
||||
"who_to_follow": "Chi seguire"
|
||||
},
|
||||
"about": {
|
||||
"mrf": {
|
||||
"federation": "Federazione",
|
||||
"keyword": {
|
||||
"reject": "Rifiuta",
|
||||
"replace": "Sostituisci",
|
||||
"is_replaced_by": "→",
|
||||
"keyword_policies": "Regole per parole chiave",
|
||||
"ftl_removal": "Rimozione dalla sequenza globale"
|
||||
},
|
||||
"simple": {
|
||||
"reject": "Rifiuta",
|
||||
"accept": "Accetta",
|
||||
"simple_policies": "Regole specifiche alla stanza",
|
||||
"accept_desc": "Questa stanza accetta messaggi solo dalle seguenti stanze:",
|
||||
"reject_desc": "Questa stanza non accetterà messaggi dalle stanze seguenti:",
|
||||
"quarantine": "Quarantena",
|
||||
"quarantine_desc": "Questa stanza inoltrerà solo messaggi pubblici alle seguenti stanze:",
|
||||
"ftl_removal": "Rimozione dalla sequenza globale",
|
||||
"ftl_removal_desc": "Questa stanza rimuove le seguenti stanze dalla sequenza globale:",
|
||||
"media_removal": "Rimozione multimedia",
|
||||
"media_removal_desc": "Questa istanza rimuove gli allegati dalle seguenti stanze:",
|
||||
"media_nsfw": "Allegati oscurati forzatamente",
|
||||
"media_nsfw_desc": "Questa stanza oscura gli allegati dei messaggi provenienti da queste stanze:"
|
||||
},
|
||||
"mrf_policies": "Regole RM abilitate",
|
||||
"mrf_policies_desc": "Le regole RM cambiano il comportamento federativo della stanza. Vigono le seguenti regole:"
|
||||
},
|
||||
"emoji": {
|
||||
"load_all": "Carico tutti i {emojiAmount} emoji",
|
||||
"load_all_hint": "Primi {saneAmount} emoji caricati, caricarli tutti potrebbe causare rallentamenti.",
|
||||
"unicode": "Emoji Unicode",
|
||||
"custom": "Emoji personale",
|
||||
"add_emoji": "Inserisci Emoji",
|
||||
"search_emoji": "Cerca un emoji",
|
||||
"keep_open": "Tieni aperto il menù",
|
||||
"emoji": "Emoji",
|
||||
"stickers": "Adesivi"
|
||||
}
|
||||
"staff": "Equipaggio"
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"mute": "Zittisci",
|
||||
"mute_progress": "Zittisco…",
|
||||
"unmute": "Ascolta",
|
||||
"unmute_progress": "Procedo…"
|
||||
},
|
||||
"exporter": {
|
||||
"export": "Esporta",
|
||||
"processing": "In elaborazione, il tuo file sarà scaricabile a breve"
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop_picture": "Ritaglia immagine",
|
||||
"save": "Salva",
|
||||
"save_without_cropping": "Salva senza ritagliare",
|
||||
"cancel": "Annulla"
|
||||
},
|
||||
"importer": {
|
||||
"submit": "Invia",
|
||||
"success": "Importato.",
|
||||
"error": "L'importazione non è andata a buon fine."
|
||||
},
|
||||
"media_modal": {
|
||||
"previous": "Precedente",
|
||||
"next": "Prossimo"
|
||||
},
|
||||
"polls": {
|
||||
"add_poll": "Sondaggio",
|
||||
"add_option": "Alternativa",
|
||||
"option": "Opzione",
|
||||
"votes": "voti",
|
||||
"vote": "Vota",
|
||||
"type": "Tipo di sondaggio",
|
||||
"single_choice": "Scelta singola",
|
||||
"multiple_choices": "Scelta multipla",
|
||||
"expiry": "Scadenza",
|
||||
"expires_in": "Scade fra {0}",
|
||||
"expired": "Scaduto {0} fa",
|
||||
"not_enough_options": "Aggiungi altre risposte"
|
||||
},
|
||||
"interactions": {
|
||||
"favs_repeats": "Condivisi e preferiti",
|
||||
"load_older": "Carica vecchie interazioni",
|
||||
"moves": "Utenti migrati",
|
||||
"follows": "Nuovi seguìti"
|
||||
},
|
||||
"emoji": {
|
||||
"load_all": "Carico tutti i {emojiAmount} emoji",
|
||||
"load_all_hint": "Primi {saneAmount} emoji caricati, caricarli tutti potrebbe causare rallentamenti.",
|
||||
"unicode": "Emoji Unicode",
|
||||
"custom": "Emoji personale",
|
||||
"add_emoji": "Inserisci Emoji",
|
||||
"search_emoji": "Cerca un emoji",
|
||||
"keep_open": "Tieni aperto il menù",
|
||||
"emoji": "Emoji",
|
||||
"stickers": "Adesivi"
|
||||
},
|
||||
"selectable_list": {
|
||||
"select_all": "Seleziona tutto"
|
||||
},
|
||||
"remote_user_resolver": {
|
||||
"error": "Non trovato.",
|
||||
"searching_for": "Cerco",
|
||||
"remote_user_resolver": "Cerca utenti remoti"
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,35 @@
|
||||
/* eslint-disable import/no-webpack-loader-syntax */
|
||||
// This module exports only the notification part of the i18n,
|
||||
// which is useful for the service worker
|
||||
|
||||
const messages = {
|
||||
ar: require('../lib/notification-i18n-loader.js!./ar.json'),
|
||||
ca: require('../lib/notification-i18n-loader.js!./ca.json'),
|
||||
cs: require('../lib/notification-i18n-loader.js!./cs.json'),
|
||||
de: require('../lib/notification-i18n-loader.js!./de.json'),
|
||||
eo: require('../lib/notification-i18n-loader.js!./eo.json'),
|
||||
es: require('../lib/notification-i18n-loader.js!./es.json'),
|
||||
et: require('../lib/notification-i18n-loader.js!./et.json'),
|
||||
eu: require('../lib/notification-i18n-loader.js!./eu.json'),
|
||||
fi: require('../lib/notification-i18n-loader.js!./fi.json'),
|
||||
fr: require('../lib/notification-i18n-loader.js!./fr.json'),
|
||||
ga: require('../lib/notification-i18n-loader.js!./ga.json'),
|
||||
he: require('../lib/notification-i18n-loader.js!./he.json'),
|
||||
hu: require('../lib/notification-i18n-loader.js!./hu.json'),
|
||||
it: require('../lib/notification-i18n-loader.js!./it.json'),
|
||||
ja: require('../lib/notification-i18n-loader.js!./ja_pedantic.json'),
|
||||
ja_easy: require('../lib/notification-i18n-loader.js!./ja_easy.json'),
|
||||
ko: require('../lib/notification-i18n-loader.js!./ko.json'),
|
||||
nb: require('../lib/notification-i18n-loader.js!./nb.json'),
|
||||
nl: require('../lib/notification-i18n-loader.js!./nl.json'),
|
||||
oc: require('../lib/notification-i18n-loader.js!./oc.json'),
|
||||
pl: require('../lib/notification-i18n-loader.js!./pl.json'),
|
||||
pt: require('../lib/notification-i18n-loader.js!./pt.json'),
|
||||
ro: require('../lib/notification-i18n-loader.js!./ro.json'),
|
||||
ru: require('../lib/notification-i18n-loader.js!./ru.json'),
|
||||
te: require('../lib/notification-i18n-loader.js!./te.json'),
|
||||
zh: require('../lib/notification-i18n-loader.js!./zh.json'),
|
||||
en: require('../lib/notification-i18n-loader.js!./en.json')
|
||||
}
|
||||
|
||||
export default messages
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,12 @@
|
||||
// This somewhat mysterious module will load a json string
|
||||
// and then extract only the 'notifications' part. This is
|
||||
// meant to be used to load the partial i18n we need for
|
||||
// the service worker.
|
||||
module.exports = function (source) {
|
||||
var object = JSON.parse(source)
|
||||
var smol = {
|
||||
notifications: object.notifications || {}
|
||||
}
|
||||
|
||||
return JSON.stringify(smol)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue