Merge remote-tracking branch 'origin/develop' into harden-parser
This commit is contained in:
commit
5e656cc0b4
124 changed files with 5370 additions and 1409 deletions
37
src/App.scss
37
src/App.scss
|
|
@ -580,8 +580,6 @@ textarea,
|
|||
}
|
||||
|
||||
&[type="checkbox"] {
|
||||
display: none;
|
||||
|
||||
&:checked + label::before {
|
||||
color: $fallback--text;
|
||||
color: var(--inputText, $fallback--text);
|
||||
|
|
@ -647,6 +645,20 @@ option {
|
|||
}
|
||||
}
|
||||
|
||||
.cards-list {
|
||||
list-style: none;
|
||||
display: grid;
|
||||
grid-auto-flow: row dense;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
|
||||
li {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--inputRadius);
|
||||
padding: 0.5em;
|
||||
margin: 0.25em;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
|
@ -657,16 +669,19 @@ option {
|
|||
display: inline-flex;
|
||||
vertical-align: middle;
|
||||
|
||||
button {
|
||||
button,
|
||||
.button-dropdown {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
|
||||
&:not(:last-child) {
|
||||
&:not(:last-child),
|
||||
&:not(:last-child) .button-default {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
&:not(:first-child) {
|
||||
&:not(:first-child),
|
||||
&:not(:first-child) .button-default {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
|
@ -887,3 +902,15 @@ option {
|
|||
opacity: 0;
|
||||
}
|
||||
/* stylelint-enable no-descending-specificity */
|
||||
|
||||
.visible-for-screenreader-only {
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
visibility: visible;
|
||||
clip: rect(0 0 0 0);
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,6 +253,7 @@ const getNodeInfo = async ({ store }) => {
|
|||
store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') })
|
||||
store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') })
|
||||
store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') })
|
||||
store.dispatch('setInstanceOption', { name: 'pleromaCustomEmojiReactionsAvailable', value: features.includes('pleroma_custom_emoji_reactions') })
|
||||
store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') })
|
||||
store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') })
|
||||
store.dispatch('setInstanceOption', { name: 'editingAvailable', value: features.includes('editing') })
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
<template>
|
||||
<label
|
||||
class="checkbox"
|
||||
:class="{ disabled, indeterminate }"
|
||||
:class="{ disabled, indeterminate, 'indeterminate-fix': indeterminateTransitionFix }"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
class="visible-for-screenreader-only"
|
||||
:disabled="disabled"
|
||||
:checked="modelValue"
|
||||
:indeterminate="indeterminate"
|
||||
@change="$emit('update:modelValue', $event.target.checked)"
|
||||
>
|
||||
<i class="checkbox-indicator" />
|
||||
<i
|
||||
class="checkbox-indicator"
|
||||
:aria-hidden="true"
|
||||
@transitionend.capture="onTransitionEnd"
|
||||
/>
|
||||
<span
|
||||
v-if="!!$slots.default"
|
||||
class="label"
|
||||
|
|
@ -27,12 +32,30 @@ export default {
|
|||
'indeterminate',
|
||||
'disabled'
|
||||
],
|
||||
emits: ['update:modelValue']
|
||||
emits: ['update:modelValue'],
|
||||
data: (vm) => ({
|
||||
indeterminateTransitionFix: vm.indeterminate
|
||||
}),
|
||||
watch: {
|
||||
indeterminate (e) {
|
||||
if (e) {
|
||||
this.indeterminateTransitionFix = true
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onTransitionEnd (e) {
|
||||
if (!this.indeterminate) {
|
||||
this.indeterminateTransitionFix = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "../../variables";
|
||||
@import "../../mixins";
|
||||
|
||||
.checkbox {
|
||||
position: relative;
|
||||
|
|
@ -81,8 +104,6 @@ export default {
|
|||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
display: none;
|
||||
|
||||
&:checked + .checkbox-indicator::before {
|
||||
color: $fallback--text;
|
||||
color: var(--inputText, $fallback--text);
|
||||
|
|
@ -95,6 +116,12 @@ export default {
|
|||
}
|
||||
}
|
||||
|
||||
&.indeterminate-fix {
|
||||
input[type="checkbox"] + .checkbox-indicator::before {
|
||||
content: "–";
|
||||
}
|
||||
}
|
||||
|
||||
& > span {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,10 @@ export default {
|
|||
this.searchBarHidden = hidden
|
||||
},
|
||||
openSettingsModal () {
|
||||
this.$store.dispatch('openSettingsModal')
|
||||
this.$store.dispatch('openSettingsModal', 'user')
|
||||
},
|
||||
openAdminModal () {
|
||||
this.$store.dispatch('openSettingsModal', 'admin')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
class="logo"
|
||||
:to="{ name: 'root' }"
|
||||
:style="logoBgStyle"
|
||||
:title="sitename"
|
||||
>
|
||||
<div
|
||||
class="mask"
|
||||
|
|
@ -38,40 +39,39 @@
|
|||
/>
|
||||
<button
|
||||
class="button-unstyled nav-icon"
|
||||
:title="$t('nav.preferences')"
|
||||
@click.stop="openSettingsModal"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="cog"
|
||||
:title="$t('nav.preferences')"
|
||||
/>
|
||||
</button>
|
||||
<a
|
||||
<button
|
||||
v-if="currentUser && currentUser.role === 'admin'"
|
||||
href="/pleroma/admin/#/login-pleroma"
|
||||
class="nav-icon"
|
||||
class="button-unstyled nav-icon"
|
||||
target="_blank"
|
||||
@click.stop
|
||||
:title="$t('nav.administration')"
|
||||
@click.stop="openAdminModal"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="tachometer-alt"
|
||||
:title="$t('nav.administration')"
|
||||
/>
|
||||
</a>
|
||||
</button>
|
||||
<span class="spacer" />
|
||||
<button
|
||||
v-if="currentUser"
|
||||
class="button-unstyled nav-icon"
|
||||
:title="$t('login.logout')"
|
||||
@click.stop.prevent="logout"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="sign-out-alt"
|
||||
:title="$t('login.logout')"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import Completion from '../../services/completion/completion.js'
|
||||
import EmojiPicker from '../emoji_picker/emoji_picker.vue'
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
import ScreenReaderNotice from 'src/components/screen_reader_notice/screen_reader_notice.vue'
|
||||
import UnicodeDomainIndicator from '../unicode_domain_indicator/unicode_domain_indicator.vue'
|
||||
import { take } from 'lodash'
|
||||
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
||||
|
|
@ -109,9 +110,10 @@ const EmojiInput = {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
randomSeed: `${Math.random()}`.replace('.', '-'),
|
||||
input: undefined,
|
||||
caretEl: undefined,
|
||||
highlighted: 0,
|
||||
highlighted: -1,
|
||||
caret: 0,
|
||||
focused: false,
|
||||
blurTimeout: null,
|
||||
|
|
@ -125,12 +127,16 @@ const EmojiInput = {
|
|||
components: {
|
||||
Popover,
|
||||
EmojiPicker,
|
||||
UnicodeDomainIndicator
|
||||
UnicodeDomainIndicator,
|
||||
ScreenReaderNotice
|
||||
},
|
||||
computed: {
|
||||
padEmoji () {
|
||||
return this.$store.getters.mergedConfig.padEmoji
|
||||
},
|
||||
defaultCandidateIndex () {
|
||||
return this.$store.getters.mergedConfig.autocompleteSelect ? 0 : -1
|
||||
},
|
||||
preText () {
|
||||
return this.modelValue.slice(0, this.caret)
|
||||
},
|
||||
|
|
@ -203,6 +209,12 @@ const EmojiInput = {
|
|||
top: this.input.scrollTop,
|
||||
left: this.input.scrollLeft
|
||||
})
|
||||
},
|
||||
suggestionListId () {
|
||||
return `suggestions-${this.randomSeed}`
|
||||
},
|
||||
suggestionItemId () {
|
||||
return (index) => `suggestion-item-${index}-${this.randomSeed}`
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
|
|
@ -278,6 +290,11 @@ const EmojiInput = {
|
|||
...rest,
|
||||
img: imageUrl || ''
|
||||
}))
|
||||
this.highlighted = this.defaultCandidateIndex
|
||||
this.$refs.screenReaderNotice.announce(
|
||||
this.$tc('tool_tip.autocomplete_available',
|
||||
this.suggestions.length,
|
||||
{ number: this.suggestions.length }))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -374,26 +391,27 @@ const EmojiInput = {
|
|||
},
|
||||
cycleBackward (e) {
|
||||
const len = this.suggestions.length || 0
|
||||
if (len > 1) {
|
||||
this.highlighted -= 1
|
||||
if (this.highlighted < 0) {
|
||||
this.highlighted = this.suggestions.length - 1
|
||||
}
|
||||
|
||||
this.highlighted -= 1
|
||||
if (this.highlighted === -1) {
|
||||
this.input.focus()
|
||||
} else if (this.highlighted < -1) {
|
||||
this.highlighted = len - 1
|
||||
}
|
||||
if (len > 0) {
|
||||
e.preventDefault()
|
||||
} else {
|
||||
this.highlighted = 0
|
||||
}
|
||||
},
|
||||
cycleForward (e) {
|
||||
const len = this.suggestions.length || 0
|
||||
if (len > 1) {
|
||||
this.highlighted += 1
|
||||
if (this.highlighted >= len) {
|
||||
this.highlighted = 0
|
||||
}
|
||||
|
||||
this.highlighted += 1
|
||||
if (this.highlighted >= len) {
|
||||
this.highlighted = -1
|
||||
this.input.focus()
|
||||
}
|
||||
if (len > 0) {
|
||||
e.preventDefault()
|
||||
} else {
|
||||
this.highlighted = 0
|
||||
}
|
||||
},
|
||||
scrollIntoView () {
|
||||
|
|
@ -540,6 +558,13 @@ const EmojiInput = {
|
|||
})
|
||||
},
|
||||
resize () {
|
||||
},
|
||||
autoCompleteItemLabel (suggestion) {
|
||||
if (suggestion.user) {
|
||||
return suggestion.displayText + ' ' + suggestion.detailText
|
||||
} else {
|
||||
return this.maybeLocalizedEmojiName(suggestion)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,19 @@
|
|||
class="emoji-input"
|
||||
:class="{ 'with-picker': !hideEmojiButton }"
|
||||
>
|
||||
<slot />
|
||||
<slot
|
||||
:id="'textbox-' + randomSeed"
|
||||
:aria-owns="suggestionListId"
|
||||
aria-autocomplete="both"
|
||||
:aria-expanded="showSuggestions"
|
||||
:aria-activedescendant="(!showSuggestions || highlighted === -1) ? '' : suggestionItemId(highlighted)"
|
||||
/>
|
||||
<!-- TODO: make the 'x' disappear if at the end maybe? -->
|
||||
<div
|
||||
ref="hiddenOverlay"
|
||||
class="hidden-overlay"
|
||||
:style="overlayStyle"
|
||||
:aria-hidden="true"
|
||||
>
|
||||
<span>{{ preText }}</span>
|
||||
<span
|
||||
|
|
@ -18,11 +25,16 @@
|
|||
>x</span>
|
||||
<span>{{ postText }}</span>
|
||||
</div>
|
||||
<screen-reader-notice
|
||||
ref="screenReaderNotice"
|
||||
aria-live="assertive"
|
||||
/>
|
||||
<template v-if="enableEmojiPicker">
|
||||
<button
|
||||
v-if="!hideEmojiButton"
|
||||
class="button-unstyled emoji-picker-icon"
|
||||
type="button"
|
||||
:title="$t('emoji.add_emoji')"
|
||||
@click.prevent="togglePicker"
|
||||
>
|
||||
<FAIcon :icon="['far', 'smile-beam']" />
|
||||
|
|
@ -43,17 +55,24 @@
|
|||
ref="suggestorPopover"
|
||||
class="autocomplete-panel"
|
||||
placement="bottom"
|
||||
:trigger-attrs="{ 'aria-hidden': true }"
|
||||
>
|
||||
<template #content>
|
||||
<div
|
||||
:id="suggestionListId"
|
||||
ref="panel-body"
|
||||
class="autocomplete-panel-body"
|
||||
role="listbox"
|
||||
>
|
||||
<div
|
||||
v-for="(suggestion, index) in suggestions"
|
||||
:id="suggestionItemId(index)"
|
||||
:key="index"
|
||||
class="autocomplete-item"
|
||||
role="option"
|
||||
:class="{ highlighted: index === highlighted }"
|
||||
:aria-label="autoCompleteItemLabel(suggestion)"
|
||||
:aria-selected="index === highlighted"
|
||||
@click.stop.prevent="onClick($event, suggestion)"
|
||||
>
|
||||
<span class="image">
|
||||
|
|
|
|||
|
|
@ -94,8 +94,9 @@ export const suggestUsers = ({ dispatch, state }) => {
|
|||
|
||||
const newSuggestions = state.users.users.filter(
|
||||
user =>
|
||||
user.screen_name.toLowerCase().startsWith(noPrefix) ||
|
||||
user.name.toLowerCase().startsWith(noPrefix)
|
||||
user.screen_name && user.name && (
|
||||
user.screen_name.toLowerCase().startsWith(noPrefix) ||
|
||||
user.name.toLowerCase().startsWith(noPrefix))
|
||||
).slice(0, 20).sort((a, b) => {
|
||||
let aScore = 0
|
||||
let bScore = 0
|
||||
|
|
|
|||
|
|
@ -98,6 +98,11 @@ const EmojiPicker = {
|
|||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hideCustomEmoji: {
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
|
@ -280,6 +285,9 @@ const EmojiPicker = {
|
|||
return 0
|
||||
},
|
||||
allCustomGroups () {
|
||||
if (this.hideCustomEmoji) {
|
||||
return {}
|
||||
}
|
||||
const emojis = this.$store.getters.groupedCustomEmojis
|
||||
if (emojis.unpacked) {
|
||||
emojis.unpacked.text = this.$t('emoji.unpacked')
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
ref="popover"
|
||||
trigger="click"
|
||||
popover-class="emoji-picker popover-default"
|
||||
:trigger-attrs="{ 'aria-hidden': true }"
|
||||
@show="onPopoverShown"
|
||||
@close="onPopoverClosed"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,17 @@
|
|||
import UserAvatar from '../user_avatar/user_avatar.vue'
|
||||
import UserListPopover from '../user_list_popover/user_list_popover.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faPlus,
|
||||
faMinus,
|
||||
faCheck
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faPlus,
|
||||
faMinus,
|
||||
faCheck
|
||||
)
|
||||
|
||||
const EMOJI_REACTION_COUNT_CUTOFF = 12
|
||||
|
||||
|
|
@ -33,6 +45,9 @@ const EmojiReactions = {
|
|||
},
|
||||
loggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
remoteInteractionLink () {
|
||||
return this.$store.getters.remoteInteractionLink({ statusId: this.status.id })
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -62,6 +77,17 @@ const EmojiReactions = {
|
|||
} else {
|
||||
this.reactWith(emoji)
|
||||
}
|
||||
},
|
||||
counterTriggerAttrs (reaction) {
|
||||
return {
|
||||
class: [
|
||||
'btn',
|
||||
'button-default',
|
||||
'emoji-reaction-count-button',
|
||||
{ '-picked-reaction': this.reactedWith(reaction.name) }
|
||||
],
|
||||
'aria-label': this.$tc('status.reaction_count_label', reaction.count, { num: reaction.count })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,64 @@
|
|||
<template>
|
||||
<div class="EmojiReactions">
|
||||
<UserListPopover
|
||||
<span
|
||||
v-for="(reaction) in emojiReactions"
|
||||
:key="reaction.name"
|
||||
:users="accountsForEmoji[reaction.name]"
|
||||
:key="reaction.url || reaction.name"
|
||||
class="emoji-reaction-container btn-group"
|
||||
>
|
||||
<button
|
||||
<component
|
||||
:is="loggedIn ? 'button' : 'a'"
|
||||
v-bind="!loggedIn ? { href: remoteInteractionLink } : {}"
|
||||
role="button"
|
||||
class="emoji-reaction btn button-default"
|
||||
:class="{ '-picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }"
|
||||
:class="{ '-picked-reaction': reactedWith(reaction.name) }"
|
||||
:title="reaction.url ? reaction.name : undefined"
|
||||
:aria-pressed="reactedWith(reaction.name)"
|
||||
@click="emojiOnClick(reaction.name, $event)"
|
||||
@mouseenter="fetchEmojiReactionsByIfMissing()"
|
||||
>
|
||||
<span class="reaction-emoji">{{ reaction.name }}</span>
|
||||
<span>{{ reaction.count }}</span>
|
||||
</button>
|
||||
</UserListPopover>
|
||||
<span
|
||||
class="reaction-emoji"
|
||||
>
|
||||
<img
|
||||
v-if="reaction.url"
|
||||
:src="reaction.url"
|
||||
class="reaction-emoji-content"
|
||||
width="1em"
|
||||
>
|
||||
<span
|
||||
v-else
|
||||
class="reaction-emoji reaction-emoji-content"
|
||||
>{{ reaction.name }}</span>
|
||||
</span>
|
||||
<FALayers>
|
||||
<FAIcon
|
||||
v-if="reactedWith(reaction.name)"
|
||||
class="active-marker"
|
||||
transform="shrink-6 up-9"
|
||||
icon="check"
|
||||
/>
|
||||
<FAIcon
|
||||
v-if="!reactedWith(reaction.name)"
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9"
|
||||
icon="plus"
|
||||
/>
|
||||
<FAIcon
|
||||
v-else
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9"
|
||||
icon="minus"
|
||||
/>
|
||||
</FALayers>
|
||||
</component>
|
||||
<UserListPopover
|
||||
:users="accountsForEmoji[reaction.name]"
|
||||
class="emoji-reaction-popover"
|
||||
:trigger-attrs="counterTriggerAttrs(reaction)"
|
||||
@show="fetchEmojiReactionsByIfMissing()"
|
||||
>
|
||||
<span class="emoji-reaction-counts">{{ reaction.count }}</span>
|
||||
</UserListPopover>
|
||||
</span>
|
||||
<a
|
||||
v-if="tooManyReactions"
|
||||
class="emoji-reaction-expand faint"
|
||||
|
|
@ -29,43 +73,118 @@
|
|||
<script src="./emoji_reactions.js"></script>
|
||||
<style lang="scss">
|
||||
@import "../../variables";
|
||||
@import "../../mixins";
|
||||
|
||||
.EmojiReactions {
|
||||
display: flex;
|
||||
margin-top: 0.25em;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.emoji-reaction {
|
||||
padding: 0 0.5em;
|
||||
margin-right: 0.5em;
|
||||
--emoji-size: calc(1.25em * var(--emojiReactionsScale, 1));
|
||||
|
||||
.emoji-reaction-container {
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
margin-top: 0.5em;
|
||||
margin-right: 0.5em;
|
||||
|
||||
.emoji-reaction-popover {
|
||||
padding: 0;
|
||||
|
||||
.emoji-reaction-count-button {
|
||||
background-color: var(--btn);
|
||||
height: 100%;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
box-sizing: border-box;
|
||||
min-width: 2em;
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: $fallback--text;
|
||||
color: var(--btnText, $fallback--text);
|
||||
|
||||
&.-picked-reaction {
|
||||
border: 1px solid var(--accent, $fallback--link);
|
||||
margin-right: -1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.emoji-reaction {
|
||||
padding-left: 0.5em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
|
||||
.reaction-emoji {
|
||||
width: 1.25em;
|
||||
width: var(--emoji-size);
|
||||
height: var(--emoji-size);
|
||||
margin-right: 0.25em;
|
||||
line-height: var(--emoji-size);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.reaction-emoji-content {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
line-height: inherit;
|
||||
overflow: hidden;
|
||||
font-size: calc(var(--emoji-size) * 0.8);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.not-clickable {
|
||||
cursor: default;
|
||||
|
||||
&:hover {
|
||||
box-shadow: $fallback--buttonShadow;
|
||||
box-shadow: var(--buttonShadow);
|
||||
}
|
||||
.svg-inline--fa {
|
||||
color: $fallback--text;
|
||||
color: var(--btnText, $fallback--text);
|
||||
}
|
||||
|
||||
&.-picked-reaction {
|
||||
border: 1px solid var(--accent, $fallback--link);
|
||||
margin-left: -1px; // offset the border, can't use inset shadows either
|
||||
margin-right: calc(0.5em - 1px);
|
||||
margin-right: -1px;
|
||||
|
||||
.svg-inline--fa {
|
||||
color: $fallback--link;
|
||||
color: var(--accent, $fallback--link);
|
||||
}
|
||||
}
|
||||
|
||||
@include unfocused-style {
|
||||
.focus-marker {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.active-marker {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@include focused-style {
|
||||
.svg-inline--fa {
|
||||
color: $fallback--link;
|
||||
color: var(--accent, $fallback--link);
|
||||
}
|
||||
|
||||
.focus-marker {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.active-marker {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,13 +38,20 @@
|
|||
class="button-unstyled interactive"
|
||||
target="_blank"
|
||||
role="button"
|
||||
:title="$t('tool_tip.favorite')"
|
||||
:href="remoteInteractionLink"
|
||||
>
|
||||
<FAIcon
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
:title="$t('tool_tip.favorite')"
|
||||
:icon="['far', 'star']"
|
||||
/>
|
||||
<FALayers class="fa-scale-110 fa-old-padding-layer">
|
||||
<FAIcon
|
||||
class="fa-scale-110"
|
||||
:icon="['far', 'star']"
|
||||
/>
|
||||
<FAIcon
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9 right-12"
|
||||
icon="plus"
|
||||
/>
|
||||
</FALayers>
|
||||
</a>
|
||||
<span
|
||||
v-if="!mergedConfig.hidePostStats && status.fave_num > 0"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
:class="{ custom: isCustom }"
|
||||
>
|
||||
<label
|
||||
:id="name + '-label'"
|
||||
:for="preset === 'custom' ? name : name + '-font-switcher'"
|
||||
class="label"
|
||||
>
|
||||
|
|
@ -12,7 +13,8 @@
|
|||
<input
|
||||
v-if="typeof fallback !== 'undefined'"
|
||||
:id="name + '-o'"
|
||||
class="opt exlcude-disabled"
|
||||
:aria-labelledby="name + '-label'"
|
||||
class="opt exlcude-disabled visible-for-screenreader-only"
|
||||
type="checkbox"
|
||||
:checked="present"
|
||||
@change="$emit('update:modelValue', typeof modelValue === 'undefined' ? fallback : undefined)"
|
||||
|
|
@ -21,6 +23,7 @@
|
|||
v-if="typeof fallback !== 'undefined'"
|
||||
class="opt-l"
|
||||
:for="name + '-o'"
|
||||
:aria-hidden="true"
|
||||
/>
|
||||
{{ ' ' }}
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -36,7 +36,9 @@
|
|||
<button
|
||||
class="button-default btn"
|
||||
@click="addLanguage"
|
||||
>{{ $t('settings.add_language') }}</button>
|
||||
>
|
||||
{{ $t('settings.add_language') }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -102,7 +104,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import '../../_variables.scss';
|
||||
@import "../../variables";
|
||||
|
||||
.interface-language-switcher {
|
||||
.language-select {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,13 @@
|
|||
<template>
|
||||
<div class="list">
|
||||
<div
|
||||
class="list"
|
||||
role="list"
|
||||
>
|
||||
<div
|
||||
v-for="item in items"
|
||||
:key="getKey(item)"
|
||||
class="list-item"
|
||||
role="listitem"
|
||||
>
|
||||
<slot
|
||||
name="item"
|
||||
|
|
|
|||
|
|
@ -23,6 +23,11 @@ const mediaUpload = {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
onClick () {
|
||||
if (this.uploadReady) {
|
||||
this.$refs.input.click()
|
||||
}
|
||||
},
|
||||
uploadFile (file) {
|
||||
const self = this
|
||||
const store = this.$store
|
||||
|
|
@ -69,10 +74,15 @@ const mediaUpload = {
|
|||
this.multiUpload(target.files)
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'dropFiles',
|
||||
'disabled'
|
||||
],
|
||||
props: {
|
||||
dropFiles: Object,
|
||||
disabled: Boolean,
|
||||
normalButton: Boolean,
|
||||
acceptTypes: {
|
||||
type: String,
|
||||
default: '*/*'
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dropFiles: function (fileInfos) {
|
||||
if (!this.uploading) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
<template>
|
||||
<label
|
||||
<button
|
||||
class="media-upload"
|
||||
:class="{ disabled: disabled }"
|
||||
:class="[normalButton ? 'button-default btn' : 'button-unstyled', { disabled }]"
|
||||
:title="$t('tool_tip.media_upload')"
|
||||
@click="onClick"
|
||||
>
|
||||
<FAIcon
|
||||
v-if="uploading"
|
||||
|
|
@ -15,15 +16,21 @@
|
|||
class="new-icon"
|
||||
icon="upload"
|
||||
/>
|
||||
<template v-if="normalButton">
|
||||
{{ ' ' }}
|
||||
{{ uploading ? $t('general.loading') : $t('tool_tip.media_upload') }}
|
||||
</template>
|
||||
<input
|
||||
v-if="uploadReady"
|
||||
ref="input"
|
||||
class="hidden-input-file"
|
||||
:disabled="disabled"
|
||||
type="file"
|
||||
multiple="true"
|
||||
:accept="acceptTypes"
|
||||
@change="change"
|
||||
>
|
||||
</label>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script src="./media_upload.js"></script>
|
||||
|
|
@ -32,10 +39,12 @@
|
|||
@import "../../variables";
|
||||
|
||||
.media-upload {
|
||||
cursor: pointer; // We use <label> for interactivity... i wonder if it's fine
|
||||
|
||||
.hidden-input-file {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
label.media-upload {
|
||||
cursor: pointer; // We use <label> for interactivity... i wonder if it's fine
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -80,3 +80,21 @@ export const ROOT_ITEMS = {
|
|||
criteria: ['announcements']
|
||||
}
|
||||
}
|
||||
|
||||
export function routeTo (item, currentUser) {
|
||||
if (!item.route && !item.routeObject) return null
|
||||
|
||||
let route
|
||||
|
||||
if (item.routeObject) {
|
||||
route = item.routeObject
|
||||
} else {
|
||||
route = { name: (item.anon || currentUser) ? item.route : item.anonRoute }
|
||||
}
|
||||
|
||||
if (USERNAME_ROUTES.has(route.name)) {
|
||||
route.params = { username: currentUser.screen_name, name: currentUser.screen_name }
|
||||
}
|
||||
|
||||
return route
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { USERNAME_ROUTES } from 'src/components/navigation/navigation.js'
|
||||
import { routeTo } from 'src/components/navigation/navigation.js'
|
||||
import OptionalRouterLink from 'src/components/optional_router_link/optional_router_link.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faThumbtack } from '@fortawesome/free-solid-svg-icons'
|
||||
|
|
@ -26,17 +26,7 @@ const NavigationEntry = {
|
|||
},
|
||||
computed: {
|
||||
routeTo () {
|
||||
if (!this.item.route && !this.item.routeObject) return null
|
||||
let route
|
||||
if (this.item.routeObject) {
|
||||
route = this.item.routeObject
|
||||
} else {
|
||||
route = { name: (this.item.anon || this.currentUser) ? this.item.route : this.item.anonRoute }
|
||||
}
|
||||
if (USERNAME_ROUTES.has(route.name)) {
|
||||
route.params = { username: this.currentUser.screen_name, name: this.currentUser.screen_name }
|
||||
}
|
||||
return route
|
||||
return routeTo(this.item, this.currentUser)
|
||||
},
|
||||
getters () {
|
||||
return this.$store.getters
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { mapState } from 'vuex'
|
||||
import { TIMELINES, ROOT_ITEMS, USERNAME_ROUTES } from 'src/components/navigation/navigation.js'
|
||||
import { TIMELINES, ROOT_ITEMS, routeTo } from 'src/components/navigation/navigation.js'
|
||||
import { getListEntries, filterNavigation } from 'src/components/navigation/filter.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
|
|
@ -31,14 +31,7 @@ const NavPanel = {
|
|||
props: ['limit'],
|
||||
methods: {
|
||||
getRouteTo (item) {
|
||||
if (item.routeObject) {
|
||||
return item.routeObject
|
||||
}
|
||||
const route = { name: (item.anon || this.currentUser) ? item.route : item.anonRoute }
|
||||
if (USERNAME_ROUTES.has(route.name)) {
|
||||
route.params = { username: this.currentUser.screen_name }
|
||||
}
|
||||
return route
|
||||
return routeTo(item, this.currentUser)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -52,6 +45,7 @@ const NavPanel = {
|
|||
privateMode: state => state.instance.private,
|
||||
federating: state => state.instance.federating,
|
||||
pleromaChatMessagesAvailable: state => state.instance.pleromaChatMessagesAvailable,
|
||||
supportsAnnouncements: state => state.announcements.supportsAnnouncements,
|
||||
pinnedItems: state => new Set(state.serverSideStorage.prefsStorage.collections.pinnedNavItems)
|
||||
}),
|
||||
pinnedList () {
|
||||
|
|
@ -63,6 +57,7 @@ const NavPanel = {
|
|||
],
|
||||
{
|
||||
hasChats: this.pleromaChatMessagesAvailable,
|
||||
hasAnnouncements: this.supportsAnnouncements,
|
||||
isFederating: this.federating,
|
||||
isPrivate: this.privateMode,
|
||||
currentUser: this.currentUser
|
||||
|
|
@ -82,6 +77,7 @@ const NavPanel = {
|
|||
],
|
||||
{
|
||||
hasChats: this.pleromaChatMessagesAvailable,
|
||||
hasAnnouncements: this.supportsAnnouncements,
|
||||
isFederating: this.federating,
|
||||
isPrivate: this.privateMode,
|
||||
currentUser: this.currentUser
|
||||
|
|
|
|||
|
|
@ -121,7 +121,17 @@
|
|||
scope="global"
|
||||
keypath="notifications.reacted_with"
|
||||
>
|
||||
<span class="emoji-reaction-emoji">{{ notification.emoji }}</span>
|
||||
<img
|
||||
v-if="notification.emoji_url"
|
||||
class="emoji-reaction-emoji emoji-reaction-emoji-image"
|
||||
:src="notification.emoji_url"
|
||||
:alt="notification.emoji"
|
||||
:title="notification.emoji"
|
||||
>
|
||||
<span
|
||||
v-else
|
||||
class="emoji-reaction-emoji"
|
||||
>{{ notification.emoji }}</span>
|
||||
</i18n-t>
|
||||
</small>
|
||||
</span>
|
||||
|
|
@ -153,9 +163,9 @@
|
|||
</router-link>
|
||||
<button
|
||||
class="button-unstyled expand-icon"
|
||||
@click.prevent="toggleStatusExpanded"
|
||||
:title="$t('tool_tip.toggle_expand')"
|
||||
:aria-expanded="statusExpanded"
|
||||
@click.prevent="toggleStatusExpanded"
|
||||
>
|
||||
<FAIcon
|
||||
class="fa-scale-110"
|
||||
|
|
|
|||
|
|
@ -129,6 +129,13 @@
|
|||
|
||||
.emoji-reaction-emoji {
|
||||
font-size: 1.3em;
|
||||
max-width: 1.25em;
|
||||
height: 1.25em;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.emoji-reaction-emoji-image {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.notification-details {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ export default {
|
|||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
choices: []
|
||||
choices: [],
|
||||
randomSeed: `${Math.random()}`.replace('.', '-')
|
||||
}
|
||||
},
|
||||
created () {
|
||||
|
|
|
|||
|
|
@ -4,53 +4,63 @@
|
|||
:class="containerClass"
|
||||
>
|
||||
<div
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
class="poll-option"
|
||||
:role="showResults ? 'section' : (poll.multiple ? 'group' : 'radiogroup')"
|
||||
>
|
||||
<div
|
||||
v-if="showResults"
|
||||
:title="resultTitle(option)"
|
||||
class="option-result"
|
||||
v-for="(option, index) in options"
|
||||
:key="index"
|
||||
class="poll-option"
|
||||
>
|
||||
<div class="option-result-label">
|
||||
<span class="result-percentage">
|
||||
{{ percentageForOption(option.votes_count) }}%
|
||||
</span>
|
||||
<RichContent
|
||||
:html="option.title_html"
|
||||
:handle-links="false"
|
||||
:emoji="emoji"
|
||||
<div
|
||||
v-if="showResults"
|
||||
:title="resultTitle(option)"
|
||||
class="option-result"
|
||||
>
|
||||
<div class="option-result-label">
|
||||
<span class="result-percentage">
|
||||
{{ percentageForOption(option.votes_count) }}%
|
||||
</span>
|
||||
<RichContent
|
||||
:html="option.title_html"
|
||||
:handle-links="false"
|
||||
:emoji="emoji"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="result-fill"
|
||||
:style="{ 'width': `${percentageForOption(option.votes_count)}%` }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
class="result-fill"
|
||||
:style="{ 'width': `${percentageForOption(option.votes_count)}%` }"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
@click="activateOption(index)"
|
||||
>
|
||||
<input
|
||||
v-if="poll.multiple"
|
||||
type="checkbox"
|
||||
:disabled="loading"
|
||||
:value="index"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
type="radio"
|
||||
:disabled="loading"
|
||||
:value="index"
|
||||
tabindex="0"
|
||||
:role="poll.multiple ? 'checkbox' : 'radio'"
|
||||
:aria-labelledby="`option-vote-${randomSeed}-${index}`"
|
||||
:aria-checked="choices[index]"
|
||||
@click="activateOption(index)"
|
||||
>
|
||||
<label class="option-vote">
|
||||
<RichContent
|
||||
:html="option.title_html"
|
||||
:handle-links="false"
|
||||
:emoji="emoji"
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
v-if="poll.multiple"
|
||||
type="checkbox"
|
||||
class="poll-checkbox"
|
||||
:disabled="loading"
|
||||
:value="index"
|
||||
>
|
||||
<input
|
||||
v-else
|
||||
type="radio"
|
||||
:disabled="loading"
|
||||
:value="index"
|
||||
>
|
||||
<label class="option-vote">
|
||||
<RichContent
|
||||
:id="`option-vote-${randomSeed}-${index}`"
|
||||
:html="option.title_html"
|
||||
:handle-links="false"
|
||||
:emoji="emoji"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer faint">
|
||||
|
|
@ -161,5 +171,9 @@
|
|||
padding: 0 0.5em;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
.poll-checkbox {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,9 @@ const Popover = {
|
|||
// Lets hover popover stay when clicking inside of it
|
||||
stayOnClick: Boolean,
|
||||
|
||||
// Use styled button (to avoid nested buttons)
|
||||
normalButton: Boolean,
|
||||
|
||||
triggerAttrs: {
|
||||
type: Object,
|
||||
default: {}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
>
|
||||
<button
|
||||
ref="trigger"
|
||||
class="button-unstyled popover-trigger-button"
|
||||
class="popover-trigger-button"
|
||||
:class="normalButton ? 'button-default btn' : 'button-unstyled'"
|
||||
type="button"
|
||||
v-bind="triggerAttrs"
|
||||
@click="onClick"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import Gallery from 'src/components/gallery/gallery.vue'
|
|||
import StatusContent from '../status_content/status_content.vue'
|
||||
import fileTypeService from '../../services/file_type/file_type.service.js'
|
||||
import { findOffset } from '../../services/offset_finder/offset_finder.service.js'
|
||||
import { propsToNative } from '../../services/attributes_helper/attributes_helper.service.js'
|
||||
import { reject, map, uniqBy, debounce } from 'lodash'
|
||||
import suggestor from '../emoji_input/suggestor.js'
|
||||
import { mapGetters, mapState } from 'vuex'
|
||||
|
|
@ -629,6 +630,9 @@ const PostStatusForm = {
|
|||
},
|
||||
openProfileTab () {
|
||||
this.$store.dispatch('openSettingsModalTab', 'profile')
|
||||
},
|
||||
propsToNative (props) {
|
||||
return propsToNative(props)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@
|
|||
<span>{{ $t('post_status.scope_notice.public') }}</span>
|
||||
<a
|
||||
class="fa-scale-110 fa-old-padding dismiss"
|
||||
:title="$t('post_status.scope_notice_dismiss')"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click.prevent="dismissScopeNotice()"
|
||||
>
|
||||
<FAIcon icon="times" />
|
||||
|
|
@ -42,6 +45,9 @@
|
|||
<span>{{ $t('post_status.scope_notice.unlisted') }}</span>
|
||||
<a
|
||||
class="fa-scale-110 fa-old-padding dismiss"
|
||||
:title="$t('post_status.scope_notice_dismiss')"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click.prevent="dismissScopeNotice()"
|
||||
>
|
||||
<FAIcon icon="times" />
|
||||
|
|
@ -54,6 +60,9 @@
|
|||
<span>{{ $t('post_status.scope_notice.private') }}</span>
|
||||
<a
|
||||
class="fa-scale-110 fa-old-padding dismiss"
|
||||
:title="$t('post_status.scope_notice_dismiss')"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
@click.prevent="dismissScopeNotice()"
|
||||
>
|
||||
<FAIcon icon="times" />
|
||||
|
|
@ -124,14 +133,17 @@
|
|||
:suggest="emojiSuggestor"
|
||||
class="form-control"
|
||||
>
|
||||
<input
|
||||
v-model="newStatus.spoilerText"
|
||||
type="text"
|
||||
:placeholder="$t('post_status.content_warning')"
|
||||
:disabled="posting && !optimisticPosting"
|
||||
size="1"
|
||||
class="form-post-subject"
|
||||
>
|
||||
<template #default="inputProps">
|
||||
<input
|
||||
v-model="newStatus.spoilerText"
|
||||
type="text"
|
||||
:placeholder="$t('post_status.content_warning')"
|
||||
:disabled="posting && !optimisticPosting"
|
||||
v-bind="propsToNative(inputProps)"
|
||||
size="1"
|
||||
class="form-post-subject"
|
||||
>
|
||||
</template>
|
||||
</EmojiInput>
|
||||
<EmojiInput
|
||||
ref="emoji-input"
|
||||
|
|
@ -148,29 +160,32 @@
|
|||
@sticker-upload-failed="uploadFailed"
|
||||
@shown="handleEmojiInputShow"
|
||||
>
|
||||
<textarea
|
||||
ref="textarea"
|
||||
v-model="newStatus.status"
|
||||
:placeholder="placeholder || $t('post_status.default')"
|
||||
rows="1"
|
||||
cols="1"
|
||||
:disabled="posting && !optimisticPosting"
|
||||
class="form-post-body"
|
||||
:class="{ 'scrollable-form': !!maxHeight }"
|
||||
@keydown.exact.enter="submitOnEnter && postStatus($event, newStatus)"
|
||||
@keydown.meta.enter="postStatus($event, newStatus)"
|
||||
@keydown.ctrl.enter="!submitOnEnter && postStatus($event, newStatus)"
|
||||
@input="resize"
|
||||
@compositionupdate="resize"
|
||||
@paste="paste"
|
||||
/>
|
||||
<p
|
||||
v-if="hasStatusLengthLimit"
|
||||
class="character-counter faint"
|
||||
:class="{ error: isOverLengthLimit }"
|
||||
>
|
||||
{{ charactersLeft }}
|
||||
</p>
|
||||
<template #default="inputProps">
|
||||
<textarea
|
||||
ref="textarea"
|
||||
v-model="newStatus.status"
|
||||
:placeholder="placeholder || $t('post_status.default')"
|
||||
rows="1"
|
||||
cols="1"
|
||||
:disabled="posting && !optimisticPosting"
|
||||
class="form-post-body"
|
||||
:class="{ 'scrollable-form': !!maxHeight }"
|
||||
v-bind="propsToNative(inputProps)"
|
||||
@keydown.exact.enter="submitOnEnter && postStatus($event, newStatus)"
|
||||
@keydown.meta.enter="postStatus($event, newStatus)"
|
||||
@keydown.ctrl.enter="!submitOnEnter && postStatus($event, newStatus)"
|
||||
@input="resize"
|
||||
@compositionupdate="resize"
|
||||
@paste="paste"
|
||||
/>
|
||||
<p
|
||||
v-if="hasStatusLengthLimit"
|
||||
class="character-counter faint"
|
||||
:class="{ error: isOverLengthLimit }"
|
||||
>
|
||||
{{ charactersLeft }}
|
||||
</p>
|
||||
</template>
|
||||
</EmojiInput>
|
||||
<div
|
||||
v-if="!disableScopeSelector"
|
||||
|
|
@ -193,6 +208,7 @@
|
|||
id="post-content-type"
|
||||
v-model="newStatus.contentType"
|
||||
class="form-control"
|
||||
:attrs="{ 'aria-label': $t('post_status.content_type_selection') }"
|
||||
>
|
||||
<option
|
||||
v-for="postFormat in postFormats"
|
||||
|
|
@ -265,12 +281,10 @@
|
|||
>
|
||||
{{ $t('post_status.post') }}
|
||||
</button>
|
||||
<!-- touchstart is used to keep the OSK at the same position after a message send -->
|
||||
<button
|
||||
v-else
|
||||
:disabled="uploadingFiles || disableSubmit"
|
||||
class="btn button-default"
|
||||
@touchstart.stop.prevent="postStatus($event, newStatus)"
|
||||
@click.stop.prevent="postStatus($event, newStatus)"
|
||||
>
|
||||
{{ $t('post_status.post') }}
|
||||
|
|
|
|||
|
|
@ -6,36 +6,51 @@
|
|||
:trigger-attrs="{ title: $t('timeline.quick_filter_settings') }"
|
||||
>
|
||||
<template #content>
|
||||
<div class="dropdown-menu">
|
||||
<div v-if="loggedIn">
|
||||
<div
|
||||
class="dropdown-menu"
|
||||
role="menu"
|
||||
>
|
||||
<div
|
||||
v-if="loggedIn"
|
||||
role="group"
|
||||
>
|
||||
<button
|
||||
v-if="!conversation"
|
||||
class="button-default dropdown-item"
|
||||
:aria-checked="replyVisibilityAll"
|
||||
role="menuitemradio"
|
||||
@click="replyVisibilityAll = true"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox -radio"
|
||||
:class="{ 'menu-checkbox-checked': replyVisibilityAll }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.reply_visibility_all') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!conversation"
|
||||
class="button-default dropdown-item"
|
||||
:aria-checked="replyVisibilityFollowing"
|
||||
role="menuitemradio"
|
||||
@click="replyVisibilityFollowing = true"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox -radio"
|
||||
:class="{ 'menu-checkbox-checked': replyVisibilityFollowing }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.reply_visibility_following_short') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!conversation"
|
||||
class="button-default dropdown-item"
|
||||
:aria-checked="replyVisibilitySelf"
|
||||
role="menuitemradio"
|
||||
@click="replyVisibilitySelf = true"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox -radio"
|
||||
:class="{ 'menu-checkbox-checked': replyVisibilitySelf }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.reply_visibility_self_short') }}
|
||||
</button>
|
||||
<div
|
||||
|
|
@ -46,33 +61,43 @@
|
|||
</div>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
role="menuitemcheckbox"
|
||||
:aria-checked="muteBotStatuses"
|
||||
@click="muteBotStatuses = !muteBotStatuses"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': muteBotStatuses }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.mute_bot_posts') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
role="menuitemcheckbox"
|
||||
:aria-checked="hideMedia"
|
||||
@click="hideMedia = !hideMedia"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': hideMedia }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.hide_media_previews') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
role="menuitemcheckbox"
|
||||
:aria-checked="hideMutedPosts"
|
||||
@click="hideMutedPosts = !hideMutedPosts"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': hideMutedPosts }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.hide_all_muted_posts') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
role="menuitem"
|
||||
@click="openTab('filtering')"
|
||||
>
|
||||
<FAIcon icon="font" />{{ $t('settings.word_filter_and_more') }}
|
||||
|
|
|
|||
|
|
@ -6,60 +6,87 @@
|
|||
:trigger-attrs="{ title: $t('timeline.quick_view_settings') }"
|
||||
>
|
||||
<template #content>
|
||||
<div class="dropdown-menu">
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="conversationDisplay = 'tree'"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox -radio"
|
||||
:class="{ 'menu-checkbox-checked': conversationDisplay === 'tree' }"
|
||||
/><FAIcon icon="folder-tree" /> {{ $t('settings.conversation_display_tree_quick') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
@click="conversationDisplay = 'linear'"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox -radio"
|
||||
:class="{ 'menu-checkbox-checked': conversationDisplay === 'linear' }"
|
||||
/><FAIcon icon="list" /> {{ $t('settings.conversation_display_linear_quick') }}
|
||||
</button>
|
||||
<div
|
||||
class="dropdown-menu"
|
||||
role="menu"
|
||||
>
|
||||
<div role="group">
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
:aria-checked="conversationDisplay === 'tree'"
|
||||
role="menuitemradio"
|
||||
@click="conversationDisplay = 'tree'"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox -radio"
|
||||
:aria-hidden="true"
|
||||
:class="{ 'menu-checkbox-checked': conversationDisplay === 'tree' }"
|
||||
/><FAIcon
|
||||
icon="folder-tree"
|
||||
:aria-hidden="true"
|
||||
/> {{ $t('settings.conversation_display_tree_quick') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
:aria-checked="conversationDisplay === 'linear'"
|
||||
role="menuitemradio"
|
||||
@click="conversationDisplay = 'linear'"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox -radio"
|
||||
:class="{ 'menu-checkbox-checked': conversationDisplay === 'linear' }"
|
||||
:aria-hidden="true"
|
||||
/><FAIcon
|
||||
icon="list"
|
||||
:aria-hidden="true"
|
||||
/> {{ $t('settings.conversation_display_linear_quick') }}
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
role="separator"
|
||||
class="dropdown-divider"
|
||||
/>
|
||||
<button
|
||||
class="button-default dropdown-item"
|
||||
role="menuitemcheckbox"
|
||||
:aria-checked="showUserAvatars"
|
||||
@click="showUserAvatars = !showUserAvatars"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': showUserAvatars }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.mention_link_show_avatar_quick') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!conversation"
|
||||
class="button-default dropdown-item"
|
||||
role="menuitemcheckbox"
|
||||
:aria-checked="autoUpdate"
|
||||
@click="autoUpdate = !autoUpdate"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': autoUpdate }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.auto_update') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="!conversation"
|
||||
class="button-default dropdown-item"
|
||||
role="menuitemcheckbox"
|
||||
:aria-checked="collapseWithSubjects"
|
||||
@click="collapseWithSubjects = !collapseWithSubjects"
|
||||
>
|
||||
<span
|
||||
class="menu-checkbox"
|
||||
:class="{ 'menu-checkbox-checked': collapseWithSubjects }"
|
||||
:aria-hidden="true"
|
||||
/>{{ $t('settings.collapse_subject') }}
|
||||
</button>
|
||||
<button
|
||||
class="button-default dropdown-item dropdown-item-icon"
|
||||
role="menuitem"
|
||||
@click="openTab('general')"
|
||||
>
|
||||
<FAIcon icon="wrench" />{{ $t('settings.more_settings') }}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
:class="{ disabled: !present || disabled }"
|
||||
>
|
||||
<label
|
||||
:id="name + '-label'"
|
||||
:for="name"
|
||||
class="label"
|
||||
>
|
||||
|
|
@ -12,7 +13,8 @@
|
|||
<input
|
||||
v-if="typeof fallback !== 'undefined'"
|
||||
:id="name + '-o'"
|
||||
class="opt"
|
||||
:aria-labelledby="name + '-label'"
|
||||
class="opt visible-for-screenreader-only"
|
||||
type="checkbox"
|
||||
:checked="present"
|
||||
@change="$emit('update:modelValue', !present ? fallback : undefined)"
|
||||
|
|
@ -21,6 +23,7 @@
|
|||
v-if="typeof fallback !== 'undefined'"
|
||||
class="opt-l"
|
||||
:for="name + '-o'"
|
||||
:aria-hidden="true"
|
||||
/>
|
||||
<input
|
||||
:id="name"
|
||||
|
|
@ -34,9 +37,10 @@
|
|||
@input="$emit('update:modelValue', $event.target.value)"
|
||||
>
|
||||
<input
|
||||
:id="name"
|
||||
:id="name + '-numeric'"
|
||||
class="input-number"
|
||||
type="number"
|
||||
:aria-labelledby="name + '-label'"
|
||||
:value="modelValue || fallback"
|
||||
:disabled="!present || disabled"
|
||||
:max="hardMax"
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import Popover from '../popover/popover.vue'
|
||||
import { ensureFinalFallback } from '../../i18n/languages.js'
|
||||
import EmojiPicker from '../emoji_picker/emoji_picker.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faPlus, faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
import { faSmileBeam } from '@fortawesome/free-regular-svg-icons'
|
||||
import { trim } from 'lodash'
|
||||
|
||||
library.add(
|
||||
faPlus,
|
||||
|
|
@ -20,105 +19,34 @@ const ReactButton = {
|
|||
}
|
||||
},
|
||||
components: {
|
||||
Popover
|
||||
Popover,
|
||||
EmojiPicker
|
||||
},
|
||||
methods: {
|
||||
addReaction (event, emoji, close) {
|
||||
addReaction (event) {
|
||||
const emoji = event.insertion
|
||||
const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji)
|
||||
if (existingReaction && existingReaction.me) {
|
||||
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
|
||||
} else {
|
||||
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
|
||||
}
|
||||
close()
|
||||
},
|
||||
show () {
|
||||
if (!this.expanded) {
|
||||
this.$refs.picker.showPicker()
|
||||
}
|
||||
},
|
||||
onShow () {
|
||||
this.expanded = true
|
||||
this.focusInput()
|
||||
},
|
||||
onClose () {
|
||||
this.expanded = false
|
||||
},
|
||||
focusInput () {
|
||||
this.$nextTick(() => {
|
||||
const input = document.querySelector('.reaction-picker-filter > input')
|
||||
if (input) input.focus()
|
||||
})
|
||||
},
|
||||
// Vaguely adjusted copypaste from emoji_input and emoji_picker!
|
||||
maybeLocalizedEmojiNamesAndKeywords (emoji) {
|
||||
const names = [emoji.displayText]
|
||||
const keywords = []
|
||||
|
||||
if (emoji.displayTextI18n) {
|
||||
names.push(this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args))
|
||||
}
|
||||
|
||||
if (emoji.annotations) {
|
||||
this.languages.forEach(lang => {
|
||||
names.push(emoji.annotations[lang]?.name)
|
||||
|
||||
keywords.push(...(emoji.annotations[lang]?.keywords || []))
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
names: names.filter(k => k),
|
||||
keywords: keywords.filter(k => k)
|
||||
}
|
||||
},
|
||||
maybeLocalizedEmojiName (emoji) {
|
||||
if (!emoji.annotations) {
|
||||
return emoji.displayText
|
||||
}
|
||||
|
||||
if (emoji.displayTextI18n) {
|
||||
return this.$t(emoji.displayTextI18n.key, emoji.displayTextI18n.args)
|
||||
}
|
||||
|
||||
for (const lang of this.languages) {
|
||||
if (emoji.annotations[lang]?.name) {
|
||||
return emoji.annotations[lang].name
|
||||
}
|
||||
}
|
||||
|
||||
return emoji.displayText
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
commonEmojis () {
|
||||
const hardcodedSet = new Set(['👍', '😠', '👀', '😂', '🔥'])
|
||||
return this.$store.getters.standardEmojiList.filter(emoji => hardcodedSet.has(emoji.replacement))
|
||||
},
|
||||
languages () {
|
||||
return ensureFinalFallback(this.$store.getters.mergedConfig.interfaceLanguage)
|
||||
},
|
||||
emojis () {
|
||||
if (this.filterWord !== '') {
|
||||
const keywordLowercase = trim(this.filterWord.toLowerCase())
|
||||
|
||||
const orderedEmojiList = []
|
||||
for (const emoji of this.$store.getters.standardEmojiList) {
|
||||
const indices = this.maybeLocalizedEmojiNamesAndKeywords(emoji)
|
||||
.keywords
|
||||
.map(k => k.toLowerCase().indexOf(keywordLowercase))
|
||||
.filter(k => k > -1)
|
||||
|
||||
const indexOfKeyword = indices.length ? Math.min(...indices) : -1
|
||||
|
||||
if (indexOfKeyword > -1) {
|
||||
if (!Array.isArray(orderedEmojiList[indexOfKeyword])) {
|
||||
orderedEmojiList[indexOfKeyword] = []
|
||||
}
|
||||
orderedEmojiList[indexOfKeyword].push(emoji)
|
||||
}
|
||||
}
|
||||
return orderedEmojiList.flat()
|
||||
}
|
||||
return this.$store.getters.standardEmojiList || []
|
||||
},
|
||||
mergedConfig () {
|
||||
return this.$store.getters.mergedConfig
|
||||
hideCustomEmoji () {
|
||||
return !this.$store.state.instance.pleromaCustomEmojiReactionsAvailable
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,73 +1,39 @@
|
|||
<template>
|
||||
<Popover
|
||||
trigger="click"
|
||||
class="ReactButton"
|
||||
placement="top"
|
||||
:offset="{ y: 5 }"
|
||||
:bound-to="{ x: 'container' }"
|
||||
remove-padding
|
||||
popover-class="ReactButton popover-default"
|
||||
@show="onShow"
|
||||
@close="onClose"
|
||||
>
|
||||
<template #content="{close}">
|
||||
<div class="reaction-picker-filter">
|
||||
<input
|
||||
v-model="filterWord"
|
||||
size="1"
|
||||
:placeholder="$t('emoji.search_emoji')"
|
||||
@input="$event.target.composing = false"
|
||||
>
|
||||
</div>
|
||||
<div class="reaction-picker">
|
||||
<span
|
||||
v-for="emoji in commonEmojis"
|
||||
:key="emoji.replacement"
|
||||
class="emoji-button"
|
||||
:title="maybeLocalizedEmojiName(emoji)"
|
||||
@click="addReaction($event, emoji.replacement, close)"
|
||||
>
|
||||
{{ emoji.replacement }}
|
||||
</span>
|
||||
<div class="reaction-picker-divider" />
|
||||
<span
|
||||
v-for="(emoji, key) in emojis"
|
||||
:key="key"
|
||||
class="emoji-button"
|
||||
:title="maybeLocalizedEmojiName(emoji)"
|
||||
@click="addReaction($event, emoji.replacement, close)"
|
||||
>
|
||||
{{ emoji.replacement }}
|
||||
</span>
|
||||
<div class="reaction-bottom-fader" />
|
||||
</div>
|
||||
</template>
|
||||
<template #trigger>
|
||||
<span
|
||||
class="button-unstyled popover-trigger"
|
||||
:title="$t('tool_tip.add_reaction')"
|
||||
>
|
||||
<FALayers>
|
||||
<FAIcon
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
:icon="['far', 'smile-beam']"
|
||||
/>
|
||||
<FAIcon
|
||||
v-show="!expanded"
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9 right-17"
|
||||
icon="plus"
|
||||
/>
|
||||
<FAIcon
|
||||
v-show="expanded"
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9 right-17"
|
||||
icon="times"
|
||||
/>
|
||||
</FALayers>
|
||||
</span>
|
||||
</template>
|
||||
</Popover>
|
||||
<span class="ReactButton">
|
||||
<EmojiPicker
|
||||
ref="picker"
|
||||
:enable-sticker-picker="enableStickerPicker"
|
||||
:hide-custom-emoji="hideCustomEmoji"
|
||||
class="emoji-picker-panel"
|
||||
@emoji="addReaction"
|
||||
@show="onShow"
|
||||
@close="onClose"
|
||||
/>
|
||||
<span
|
||||
class="button-unstyled popover-trigger"
|
||||
:title="$t('tool_tip.add_reaction')"
|
||||
@click.stop.prevent="show"
|
||||
>
|
||||
<FALayers>
|
||||
<FAIcon
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
:icon="['far', 'smile-beam']"
|
||||
/>
|
||||
<FAIcon
|
||||
v-show="!expanded"
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9 right-17"
|
||||
icon="plus"
|
||||
/>
|
||||
<FAIcon
|
||||
v-show="expanded"
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9 right-17"
|
||||
icon="times"
|
||||
/>
|
||||
</FALayers>
|
||||
</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script src="./react_button.js"></script>
|
||||
|
|
@ -135,11 +101,6 @@
|
|||
color: $fallback--text;
|
||||
color: var(--text, $fallback--text);
|
||||
}
|
||||
}
|
||||
|
||||
.popover-trigger-button {
|
||||
/* override of popover internal stuff */
|
||||
width: auto;
|
||||
|
||||
@include unfocused-style {
|
||||
.focus-marker {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const registration = {
|
|||
confirm: '',
|
||||
birthday: '',
|
||||
reason: '',
|
||||
language: ''
|
||||
language: ['']
|
||||
},
|
||||
captcha: {}
|
||||
}),
|
||||
|
|
@ -100,7 +100,7 @@ const registration = {
|
|||
this.user.captcha_token = this.captcha.token
|
||||
this.user.captcha_answer_data = this.captcha.answer_data
|
||||
if (this.user.language) {
|
||||
this.user.language = localeService.internalToBackendLocale(this.user.language)
|
||||
this.user.language = localeService.internalToBackendLocaleMulti(this.user.language.filter(k => k))
|
||||
}
|
||||
|
||||
this.v$.$touch()
|
||||
|
|
|
|||
|
|
@ -210,6 +210,7 @@
|
|||
:prompt-text="$t('registration.email_language')"
|
||||
:language="v$.user.language.$model"
|
||||
:set-language="val => v$.user.language.$model = val"
|
||||
@click.stop.prevent
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -32,12 +32,20 @@
|
|||
target="_blank"
|
||||
role="button"
|
||||
:href="remoteInteractionLink"
|
||||
:title="$t('tool_tip.reply')"
|
||||
>
|
||||
<FAIcon
|
||||
icon="reply"
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
:title="$t('tool_tip.reply')"
|
||||
/>
|
||||
<FALayers class="fa-old-padding-layer">
|
||||
<FAIcon
|
||||
class="fa-scale-110"
|
||||
icon="reply"
|
||||
/>
|
||||
<FAIcon
|
||||
v-if="!replying"
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-8 right-16"
|
||||
icon="plus"
|
||||
/>
|
||||
</FALayers>
|
||||
</a>
|
||||
<span
|
||||
v-if="status.replies_count > 0"
|
||||
|
|
|
|||
|
|
@ -45,13 +45,20 @@
|
|||
class="button-unstyled interactive"
|
||||
target="_blank"
|
||||
role="button"
|
||||
:title="$t('tool_tip.repeat')"
|
||||
:href="remoteInteractionLink"
|
||||
>
|
||||
<FAIcon
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="retweet"
|
||||
:title="$t('tool_tip.repeat')"
|
||||
/>
|
||||
<FALayers class="fa-old-padding-layer">
|
||||
<FAIcon
|
||||
class="fa-scale-110"
|
||||
icon="retweet"
|
||||
/>
|
||||
<FAIcon
|
||||
class="focus-marker"
|
||||
transform="shrink-6 up-9 right-12"
|
||||
icon="plus"
|
||||
/>
|
||||
</FALayers>
|
||||
</a>
|
||||
<span
|
||||
v-if="!mergedConfig.hidePostStats && status.repeat_num > 0"
|
||||
|
|
|
|||
21
src/components/screen_reader_notice/screen_reader_notice.js
Normal file
21
src/components/screen_reader_notice/screen_reader_notice.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
const ScreenReaderNotice = {
|
||||
props: {
|
||||
ariaLive: {
|
||||
type: String,
|
||||
defualt: 'assertive'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
currentText: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
announce (text) {
|
||||
this.currentText = text
|
||||
setTimeout(() => { this.currentText = '' }, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default ScreenReaderNotice
|
||||
10
src/components/screen_reader_notice/screen_reader_notice.vue
Normal file
10
src/components/screen_reader_notice/screen_reader_notice.vue
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<template>
|
||||
<div
|
||||
class="visible-for-screenreader-only"
|
||||
:aria-live="ariaLive"
|
||||
>
|
||||
{{ currentText }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./screen_reader_notice.js"></script>
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
class="button-unstyled nav-icon"
|
||||
:title="$t('nav.search')"
|
||||
type="button"
|
||||
:aria-expanded="!hidden"
|
||||
@click.prevent.stop="toggleHidden"
|
||||
>
|
||||
<FAIcon
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
<button
|
||||
class="button-default search-button"
|
||||
type="submit"
|
||||
:title="$t('nav.search')"
|
||||
@click="find(searchTerm)"
|
||||
>
|
||||
<FAIcon
|
||||
|
|
@ -39,6 +41,8 @@
|
|||
<button
|
||||
class="button-unstyled cancel-search"
|
||||
type="button"
|
||||
:title="$t('nav.search_close')"
|
||||
:aria-expanded="!hidden"
|
||||
@click.prevent.stop="toggleHidden"
|
||||
>
|
||||
<FAIcon
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export default {
|
|||
'modelValue',
|
||||
'disabled',
|
||||
'unstyled',
|
||||
'kind'
|
||||
'kind',
|
||||
'attrs'
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
<select
|
||||
:disabled="disabled"
|
||||
:value="modelValue"
|
||||
v-bind="attrs"
|
||||
@change="$emit('update:modelValue', $event.target.value)"
|
||||
>
|
||||
<slot />
|
||||
|
|
|
|||
64
src/components/settings_modal/admin_tabs/frontends_tab.js
Normal file
64
src/components/settings_modal/admin_tabs/frontends_tab.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import StringSetting from '../helpers/string_setting.vue'
|
||||
import GroupSetting from '../helpers/group_setting.vue'
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe
|
||||
)
|
||||
|
||||
const FrontendsTab = {
|
||||
provide () {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
StringSetting,
|
||||
GroupSetting,
|
||||
Popover
|
||||
},
|
||||
created () {
|
||||
if (this.user.rights.admin) {
|
||||
this.$store.dispatch('loadFrontendsStuff')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
frontends () {
|
||||
return this.$store.state.adminSettings.frontends
|
||||
},
|
||||
...SharedComputedObject()
|
||||
},
|
||||
methods: {
|
||||
update (frontend, suggestRef) {
|
||||
const ref = suggestRef || frontend.refs[0]
|
||||
const { name } = frontend
|
||||
const payload = { name, ref }
|
||||
|
||||
this.$store.state.api.backendInteractor.installFrontend({ payload })
|
||||
.then((externalUser) => {
|
||||
this.$store.dispatch('loadFrontendsStuff')
|
||||
})
|
||||
},
|
||||
setDefault (frontend, suggestRef) {
|
||||
const ref = suggestRef || frontend.refs[0]
|
||||
const { name } = frontend
|
||||
|
||||
this.$store.commit('updateAdminDraft', { path: [':pleroma', ':frontends', ':primary'], value: { name, ref } })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default FrontendsTab
|
||||
13
src/components/settings_modal/admin_tabs/frontends_tab.scss
Normal file
13
src/components/settings_modal/admin_tabs/frontends_tab.scss
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
.frontends-tab {
|
||||
.cards-list {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
dd {
|
||||
text-overflow: ellipsis;
|
||||
word-wrap: nowrap;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
max-width: 10em;
|
||||
}
|
||||
}
|
||||
184
src/components/settings_modal/admin_tabs/frontends_tab.vue
Normal file
184
src/components/settings_modal/admin_tabs/frontends_tab.vue
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
<template>
|
||||
<div
|
||||
class="frontends-tab"
|
||||
:label="$t('admin_dash.tabs.frontends')"
|
||||
>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('admin_dash.tabs.frontends') }}</h2>
|
||||
<p>{{ $t('admin_dash.frontend.wip_notice') }}</p>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<h3>{{ $t('admin_dash.frontend.default_frontend') }}</h3>
|
||||
<p>{{ $t('admin_dash.frontend.default_frontend_tip') }}</p>
|
||||
<p>{{ $t('admin_dash.frontend.default_frontend_tip2') }}</p>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<StringSetting path=":pleroma.:frontends.:primary.name" />
|
||||
</li>
|
||||
<li>
|
||||
<StringSetting path=":pleroma.:frontends.:primary.ref" />
|
||||
</li>
|
||||
<li>
|
||||
<GroupSetting path=":pleroma.:frontends.:primary" />
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="setting-list">
|
||||
<h3>{{ $t('admin_dash.frontend.available_frontends') }}</h3>
|
||||
<ul class="cards-list">
|
||||
<li
|
||||
v-for="frontend in frontends"
|
||||
:key="frontend.name"
|
||||
>
|
||||
<strong>{{ frontend.name }}</strong>
|
||||
{{ ' ' }}
|
||||
<span v-if="adminDraft[':pleroma'][':frontends'][':primary'].name === frontend.name">
|
||||
<i18n-t
|
||||
v-if="adminDraft[':pleroma'][':frontends'][':primary'].ref === frontend.refs[0]"
|
||||
keypath="admin_dash.frontend.is_default"
|
||||
/>
|
||||
<i18n-t
|
||||
v-else
|
||||
keypath="admin_dash.frontend.is_default_custom"
|
||||
>
|
||||
<template #version>
|
||||
<code>{{ adminDraft[':pleroma'][':frontends'][':primary'].ref }}</code>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</span>
|
||||
<dl>
|
||||
<dt>{{ $t('admin_dash.frontend.repository') }}</dt>
|
||||
<dd>
|
||||
<a
|
||||
:href="frontend.git"
|
||||
target="_blank"
|
||||
>{{ frontend.git }}</a>
|
||||
</dd>
|
||||
<template v-if="expertLevel">
|
||||
<dt>{{ $t('admin_dash.frontend.versions') }}</dt>
|
||||
<dd
|
||||
v-for="ref in frontend.refs"
|
||||
:key="ref"
|
||||
>
|
||||
<code>{{ ref }}</code>
|
||||
</dd>
|
||||
</template>
|
||||
<dt v-if="expertLevel">
|
||||
{{ $t('admin_dash.frontend.build_url') }}
|
||||
</dt>
|
||||
<dd v-if="expertLevel">
|
||||
<a
|
||||
:href="frontend.build_url"
|
||||
target="_blank"
|
||||
>{{ frontend.build_url }}</a>
|
||||
</dd>
|
||||
</dl>
|
||||
<div>
|
||||
<span class="btn-group">
|
||||
<button
|
||||
class="button button-default btn"
|
||||
type="button"
|
||||
@click="update(frontend)"
|
||||
>
|
||||
{{
|
||||
frontend.installed
|
||||
? $t('admin_dash.frontend.reinstall')
|
||||
: $t('admin_dash.frontend.install')
|
||||
}}
|
||||
</button>
|
||||
<Popover
|
||||
v-if="frontend.refs.length > 1"
|
||||
trigger="click"
|
||||
class="button-dropdown"
|
||||
placement="bottom"
|
||||
>
|
||||
<template #content>
|
||||
<div class="dropdown-menu">
|
||||
<button
|
||||
v-for="ref in frontend.refs"
|
||||
:key="ref"
|
||||
class="button-default dropdown-item"
|
||||
@click="update(frontend, ref)"
|
||||
>
|
||||
<i18n-t keypath="admin_dash.frontend.install_version">
|
||||
<template #version>
|
||||
<code>{{ ref }}</code>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template #trigger>
|
||||
<button
|
||||
class="button button-default btn dropdown-button"
|
||||
type="button"
|
||||
:title="$t('admin_dash.frontend.more_install_options')"
|
||||
>
|
||||
<FAIcon icon="chevron-down" />
|
||||
</button>
|
||||
</template>
|
||||
</Popover>
|
||||
</span>
|
||||
<span
|
||||
v-if="frontend.installed && frontend.name !== 'admin-fe'"
|
||||
class="btn-group"
|
||||
>
|
||||
<button
|
||||
class="button button-default btn"
|
||||
type="button"
|
||||
:disabled="
|
||||
adminDraft[':pleroma'][':frontends'][':primary'].name === frontend.name &&
|
||||
adminDraft[':pleroma'][':frontends'][':primary'].ref === frontend.refs[0]
|
||||
"
|
||||
@click="setDefault(frontend)"
|
||||
>
|
||||
{{
|
||||
$t('admin_dash.frontend.set_default')
|
||||
}}
|
||||
</button>
|
||||
{{ ' ' }}
|
||||
<Popover
|
||||
v-if="frontend.refs.length > 1"
|
||||
trigger="click"
|
||||
class="button-dropdown"
|
||||
placement="bottom"
|
||||
>
|
||||
<template #content>
|
||||
<div class="dropdown-menu">
|
||||
<button
|
||||
v-for="ref in frontend.refs.slice(1)"
|
||||
:key="ref"
|
||||
class="button-default dropdown-item"
|
||||
@click="setDefault(frontend, ref)"
|
||||
>
|
||||
<i18n-t keypath="admin_dash.frontend.set_default_version">
|
||||
<template #version>
|
||||
<code>{{ ref }}</code>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<template #trigger>
|
||||
<button
|
||||
class="button button-default btn dropdown-button"
|
||||
type="button"
|
||||
:title="$t('admin_dash.frontend.more_default_options')"
|
||||
>
|
||||
<FAIcon icon="chevron-down" />
|
||||
</button>
|
||||
</template>
|
||||
</Popover>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./frontends_tab.js"></script>
|
||||
|
||||
<style lang="scss" src="./frontends_tab.scss"></style>
|
||||
38
src/components/settings_modal/admin_tabs/instance_tab.js
Normal file
38
src/components/settings_modal/admin_tabs/instance_tab.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import StringSetting from '../helpers/string_setting.vue'
|
||||
import GroupSetting from '../helpers/group_setting.vue'
|
||||
import AttachmentSetting from '../helpers/attachment_setting.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe
|
||||
)
|
||||
|
||||
const InstanceTab = {
|
||||
provide () {
|
||||
return {
|
||||
defaultDraftMode: true,
|
||||
defaultSource: 'admin'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
StringSetting,
|
||||
AttachmentSetting,
|
||||
GroupSetting
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
}
|
||||
|
||||
export default InstanceTab
|
||||
196
src/components/settings_modal/admin_tabs/instance_tab.vue
Normal file
196
src/components/settings_modal/admin_tabs/instance_tab.vue
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
<template>
|
||||
<div :label="$t('admin_dash.tabs.instance')">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('admin_dash.instance.instance') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<StringSetting path=":pleroma.:instance.:name" />
|
||||
</li>
|
||||
<li>
|
||||
<StringSetting path=":pleroma.:instance.:email" />
|
||||
</li>
|
||||
<li>
|
||||
<StringSetting path=":pleroma.:instance.:description" />
|
||||
</li>
|
||||
<li>
|
||||
<StringSetting path=":pleroma.:instance.:short_description" />
|
||||
</li>
|
||||
<li>
|
||||
<AttachmentSetting path=":pleroma.:instance.:instance_thumbnail" />
|
||||
</li>
|
||||
<li>
|
||||
<AttachmentSetting path=":pleroma.:instance.:background_image" />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('admin_dash.instance.registrations') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path=":pleroma.:instance.:registrations_open" />
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path=":pleroma.:instance.:invites_enabled"
|
||||
parent-path=":pleroma.:instance.:registrations_open"
|
||||
parent-invert
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path=":pleroma.:instance.:birthday_required" />
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<IntegerSetting
|
||||
path=":pleroma.:instance.:birthday_min_age"
|
||||
parent-path=":pleroma.:instance.:birthday_required"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path=":pleroma.:instance.:account_activation_required" />
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path=":pleroma.:instance.:account_approval_required" />
|
||||
</li>
|
||||
<li>
|
||||
<h3>{{ $t('admin_dash.instance.captcha_header') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting :path="[':pleroma', 'Pleroma.Captcha', ':enabled']" />
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
:path="[':pleroma', 'Pleroma.Captcha', ':method']"
|
||||
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
|
||||
:option-label-map="{
|
||||
'Pleroma.Captcha.Native': $t('admin_dash.captcha.native'),
|
||||
'Pleroma.Captcha.Kocaptcha': $t('admin_dash.captcha.kocaptcha')
|
||||
}"
|
||||
/>
|
||||
<IntegerSetting
|
||||
:path="[':pleroma', 'Pleroma.Captcha', ':seconds_valid']"
|
||||
:parent-path="[':pleroma', 'Pleroma.Captcha', ':enabled']"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
v-if="adminDraft[':pleroma']['Pleroma.Captcha'][':enabled'] && adminDraft[':pleroma']['Pleroma.Captcha'][':method'] === 'Pleroma.Captcha.Kocaptcha'"
|
||||
>
|
||||
<h4>{{ $t('admin_dash.instance.kocaptcha') }}</h4>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<StringSetting :path="[':pleroma', 'Pleroma.Captcha.Kocaptcha', ':endpoint']" />
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('admin_dash.instance.access') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
override-backend-description
|
||||
override-backend-description-label
|
||||
path=":pleroma.:instance.:public"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<ChoiceSetting
|
||||
override-backend-description
|
||||
override-backend-description-label
|
||||
path=":pleroma.:instance.:limit_to_local_content"
|
||||
/>
|
||||
</li>
|
||||
<li v-if="expertLevel">
|
||||
<h3>{{ $t('admin_dash.instance.restrict.header') }}</h3>
|
||||
<p>
|
||||
{{ $t('admin_dash.instance.restrict.description') }}
|
||||
</p>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<h4>{{ $t('admin_dash.instance.restrict.timelines') }}</h4>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path=":pleroma.:restrict_unauthenticated.:timelines.:local"
|
||||
indeterminate-state=":if_instance_is_private"
|
||||
swap-description-and-label
|
||||
hide-description
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path=":pleroma.:restrict_unauthenticated.:timelines.:federated"
|
||||
indeterminate-state=":if_instance_is_private"
|
||||
swap-description-and-label
|
||||
hide-description
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<GroupSetting path=":pleroma.:restrict_unauthenticated.:timelines" />
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4>{{ $t('admin_dash.instance.restrict.profiles') }}</h4>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path=":pleroma.:restrict_unauthenticated.:profiles.:local"
|
||||
indeterminate-state=":if_instance_is_private"
|
||||
swap-description-and-label
|
||||
hide-description
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path=":pleroma.:restrict_unauthenticated.:profiles.:remote"
|
||||
indeterminate-state=":if_instance_is_private"
|
||||
swap-description-and-label
|
||||
hide-description
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<GroupSetting path=":pleroma.:restrict_unauthenticated.:profiles" />
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4>{{ $t('admin_dash.instance.restrict.activities') }}</h4>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path=":pleroma.:restrict_unauthenticated.:activities.:local"
|
||||
indeterminate-state=":if_instance_is_private"
|
||||
swap-description-and-label
|
||||
hide-description
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path=":pleroma.:restrict_unauthenticated.:activities.:remote"
|
||||
indeterminate-state=":if_instance_is_private"
|
||||
swap-description-and-label
|
||||
hide-description
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<GroupSetting path=":pleroma.:restrict_unauthenticated.:activities" />
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./instance_tab.js"></script>
|
||||
29
src/components/settings_modal/admin_tabs/limits_tab.js
Normal file
29
src/components/settings_modal/admin_tabs/limits_tab.js
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import StringSetting from '../helpers/string_setting.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faGlobe
|
||||
)
|
||||
|
||||
const LimitsTab = {
|
||||
data () {},
|
||||
components: {
|
||||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
StringSetting
|
||||
},
|
||||
computed: {
|
||||
...SharedComputedObject()
|
||||
}
|
||||
}
|
||||
|
||||
export default LimitsTab
|
||||
136
src/components/settings_modal/admin_tabs/limits_tab.vue
Normal file
136
src/components/settings_modal/admin_tabs/limits_tab.vue
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
<template>
|
||||
<div :label="$t('admin_dash.tabs.limits')">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('admin_dash.limits.arbitrary_limits') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<h3>{{ $t('admin_dash.limits.posts') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:limit"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:remote_limit"
|
||||
expert="1"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h3>{{ $t('admin_dash.limits.uploads') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:description_limit"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:upload_limit"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:max_media_attachments"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h3>{{ $t('admin_dash.limits.users') }}</h3>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:max_pinned_statuses"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:user_bio_length"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:user_name_length"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<h4>{{ $t('admin_dash.limits.profile_fields') }}</h4>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:max_account_fields"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:max_remote_account_fields"
|
||||
draft-mode
|
||||
expert="1"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:account_field_name_length"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:account_field_value_length"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<h4>{{ $t('admin_dash.limits.user_uploads') }}</h4>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:avatar_upload_limit"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<IntegerSetting
|
||||
source="admin"
|
||||
path=":pleroma.:instance.:banner_upload_limit"
|
||||
draft-mode
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script src="./limits_tab.js"></script>
|
||||
43
src/components/settings_modal/helpers/attachment_setting.js
Normal file
43
src/components/settings_modal/helpers/attachment_setting.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import Setting from './setting.js'
|
||||
import { fileTypeExt } from 'src/services/file_type/file_type.service.js'
|
||||
import MediaUpload from 'src/components/media_upload/media_upload.vue'
|
||||
import Attachment from 'src/components/attachment/attachment.vue'
|
||||
|
||||
export default {
|
||||
...Setting,
|
||||
props: {
|
||||
...Setting.props,
|
||||
acceptTypes: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'image/*'
|
||||
}
|
||||
},
|
||||
components: {
|
||||
...Setting.components,
|
||||
MediaUpload,
|
||||
Attachment
|
||||
},
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
attachment () {
|
||||
const path = this.realDraftMode ? this.draft : this.state
|
||||
// The "server" part is primarily for local dev, but could be useful for alt-domain or multiuser usage.
|
||||
const url = path.includes('://') ? path : this.$store.state.instance.server + path
|
||||
return {
|
||||
mimetype: fileTypeExt(url),
|
||||
url
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
setMediaFile (fileInfo) {
|
||||
if (this.realDraftMode) {
|
||||
this.draft = fileInfo.url
|
||||
} else {
|
||||
this.configSink(this.path, fileInfo.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
96
src/components/settings_modal/helpers/attachment_setting.vue
Normal file
96
src/components/settings_modal/helpers/attachment_setting.vue
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<template>
|
||||
<span
|
||||
v-if="matchesExpertLevel"
|
||||
class="AttachmentSetting"
|
||||
>
|
||||
<label
|
||||
:for="path"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
<template v-if="backendDescriptionLabel">
|
||||
{{ backendDescriptionLabel + ' ' }}
|
||||
</template>
|
||||
<template v-else-if="source === 'admin'">
|
||||
MISSING LABEL FOR {{ path }}
|
||||
</template>
|
||||
<slot v-else />
|
||||
|
||||
</label>
|
||||
<p
|
||||
v-if="backendDescriptionDescription"
|
||||
class="setting-description"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
{{ backendDescriptionDescription + ' ' }}
|
||||
</p>
|
||||
<div class="attachment-input">
|
||||
<div>{{ $t('settings.url') }}</div>
|
||||
<div class="controls">
|
||||
<input
|
||||
:id="path"
|
||||
class="string-input"
|
||||
:disabled="shouldBeDisabled"
|
||||
:value="realDraftMode ? draft : state"
|
||||
@change="update"
|
||||
>
|
||||
{{ ' ' }}
|
||||
<ModifiedIndicator
|
||||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||
</div>
|
||||
<div>{{ $t('settings.preview') }}</div>
|
||||
<Attachment
|
||||
class="attachment"
|
||||
:compact="compact"
|
||||
:attachment="attachment"
|
||||
size="small"
|
||||
hide-description
|
||||
@setMedia="onMedia"
|
||||
@naturalSizeLoad="onNaturalSizeLoad"
|
||||
/>
|
||||
<div class="controls">
|
||||
<MediaUpload
|
||||
ref="mediaUpload"
|
||||
class="media-upload-icon"
|
||||
:drop-files="dropFiles"
|
||||
normal-button
|
||||
:accept-types="acceptTypes"
|
||||
@uploaded="setMediaFile"
|
||||
@upload-failed="uploadFailed"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<DraftButtons />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script src="./attachment_setting.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
.AttachmentSetting {
|
||||
.attachment {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 15em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.attachment-input {
|
||||
margin-left: 1em;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 20em;
|
||||
}
|
||||
|
||||
.controls {
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
input,
|
||||
button {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,56 +1,31 @@
|
|||
import { get, set } from 'lodash'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import ModifiedIndicator from './modified_indicator.vue'
|
||||
import ServerSideIndicator from './server_side_indicator.vue'
|
||||
import Setting from './setting.js'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
Checkbox,
|
||||
ModifiedIndicator,
|
||||
ServerSideIndicator
|
||||
...Setting,
|
||||
props: {
|
||||
...Setting.props,
|
||||
indeterminateState: [String, Object]
|
||||
},
|
||||
components: {
|
||||
...Setting.components,
|
||||
Checkbox
|
||||
},
|
||||
props: [
|
||||
'path',
|
||||
'disabled',
|
||||
'expert'
|
||||
],
|
||||
computed: {
|
||||
pathDefault () {
|
||||
const [firstSegment, ...rest] = this.path.split('.')
|
||||
return [firstSegment + 'DefaultValue', ...rest].join('.')
|
||||
},
|
||||
state () {
|
||||
const value = get(this.$parent, this.path)
|
||||
if (value === undefined) {
|
||||
return this.defaultState
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
defaultState () {
|
||||
return get(this.$parent, this.pathDefault)
|
||||
},
|
||||
isServerSide () {
|
||||
return this.path.startsWith('serverSide_')
|
||||
},
|
||||
isChanged () {
|
||||
return !this.path.startsWith('serverSide_') && this.state !== this.defaultState
|
||||
},
|
||||
matchesExpertLevel () {
|
||||
return (this.expert || 0) <= this.$parent.expertLevel
|
||||
...Setting.computed,
|
||||
isIndeterminate () {
|
||||
return this.visibleState === this.indeterminateState
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update (e) {
|
||||
const [firstSegment, ...rest] = this.path.split('.')
|
||||
set(this.$parent, this.path, e)
|
||||
// Updating nested properties does not trigger update on its parent.
|
||||
// probably still not as reliable, but works for depth=1 at least
|
||||
if (rest.length > 0) {
|
||||
set(this.$parent, firstSegment, { ...get(this.$parent, firstSegment) })
|
||||
...Setting.methods,
|
||||
getValue (e) {
|
||||
// Basic tri-state toggle implementation
|
||||
if (!!this.indeterminateState && !e && this.visibleState === true) {
|
||||
// If we have indeterminate state, switching from true to false first goes through indeterminate
|
||||
return this.indeterminateState
|
||||
}
|
||||
},
|
||||
reset () {
|
||||
set(this.$parent, this.path, this.defaultState)
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,23 +4,37 @@
|
|||
class="BooleanSetting"
|
||||
>
|
||||
<Checkbox
|
||||
:model-value="state"
|
||||
:disabled="disabled"
|
||||
:model-value="visibleState"
|
||||
:disabled="shouldBeDisabled"
|
||||
:indeterminate="isIndeterminate"
|
||||
@update:modelValue="update"
|
||||
>
|
||||
<span
|
||||
v-if="!!$slots.default"
|
||||
class="label"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
<slot />
|
||||
<template v-if="backendDescriptionLabel">
|
||||
{{ backendDescriptionLabel }}
|
||||
</template>
|
||||
<template v-else-if="source === 'admin'">
|
||||
MISSING LABEL FOR {{ path }}
|
||||
</template>
|
||||
<slot v-else />
|
||||
</span>
|
||||
{{ ' ' }}
|
||||
<ModifiedIndicator
|
||||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
<ServerSideIndicator :server-side="isServerSide" />
|
||||
</Checkbox>
|
||||
<ModifiedIndicator
|
||||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||
<DraftButtons />
|
||||
<p
|
||||
v-if="backendDescriptionDescription"
|
||||
class="setting-description"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
{{ backendDescriptionDescription + ' ' }}
|
||||
</p>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,51 +1,41 @@
|
|||
import { get, set } from 'lodash'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
import ModifiedIndicator from './modified_indicator.vue'
|
||||
import ServerSideIndicator from './server_side_indicator.vue'
|
||||
import Setting from './setting.js'
|
||||
|
||||
export default {
|
||||
...Setting,
|
||||
components: {
|
||||
Select,
|
||||
ModifiedIndicator,
|
||||
ServerSideIndicator
|
||||
...Setting.components,
|
||||
Select
|
||||
},
|
||||
props: {
|
||||
...Setting.props,
|
||||
options: {
|
||||
type: Array,
|
||||
required: false
|
||||
},
|
||||
optionLabelMap: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
props: [
|
||||
'path',
|
||||
'disabled',
|
||||
'options',
|
||||
'expert'
|
||||
],
|
||||
computed: {
|
||||
pathDefault () {
|
||||
const [firstSegment, ...rest] = this.path.split('.')
|
||||
return [firstSegment + 'DefaultValue', ...rest].join('.')
|
||||
},
|
||||
state () {
|
||||
const value = get(this.$parent, this.path)
|
||||
if (value === undefined) {
|
||||
return this.defaultState
|
||||
} else {
|
||||
return value
|
||||
...Setting.computed,
|
||||
realOptions () {
|
||||
if (this.realSource === 'admin') {
|
||||
return this.backendDescriptionSuggestions.map(x => ({
|
||||
key: x,
|
||||
value: x,
|
||||
label: this.optionLabelMap[x] || x
|
||||
}))
|
||||
}
|
||||
},
|
||||
defaultState () {
|
||||
return get(this.$parent, this.pathDefault)
|
||||
},
|
||||
isServerSide () {
|
||||
return this.path.startsWith('serverSide_')
|
||||
},
|
||||
isChanged () {
|
||||
return !this.path.startsWith('serverSide_') && this.state !== this.defaultState
|
||||
},
|
||||
matchesExpertLevel () {
|
||||
return (this.expert || 0) <= this.$parent.expertLevel
|
||||
return this.options
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update (e) {
|
||||
set(this.$parent, this.path, e)
|
||||
},
|
||||
reset () {
|
||||
set(this.$parent, this.path, this.defaultState)
|
||||
...Setting.methods,
|
||||
getValue (e) {
|
||||
return e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,15 +3,20 @@
|
|||
v-if="matchesExpertLevel"
|
||||
class="ChoiceSetting"
|
||||
>
|
||||
<slot />
|
||||
<template v-if="backendDescriptionLabel">
|
||||
{{ backendDescriptionLabel }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot />
|
||||
</template>
|
||||
{{ ' ' }}
|
||||
<Select
|
||||
:model-value="state"
|
||||
:model-value="realDraftMode ? draft :state"
|
||||
:disabled="disabled"
|
||||
@update:modelValue="update"
|
||||
>
|
||||
<option
|
||||
v-for="option in options"
|
||||
v-for="option in realOptions"
|
||||
:key="option.key"
|
||||
:value="option.value"
|
||||
>
|
||||
|
|
@ -23,7 +28,14 @@
|
|||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
<ServerSideIndicator :server-side="isServerSide" />
|
||||
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||
<DraftButtons />
|
||||
<p
|
||||
v-if="backendDescriptionDescription"
|
||||
class="setting-description"
|
||||
>
|
||||
{{ backendDescriptionDescription + ' ' }}
|
||||
</p>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
88
src/components/settings_modal/helpers/draft_buttons.vue
Normal file
88
src/components/settings_modal/helpers/draft_buttons.vue
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
<!-- this is a helper exclusive to Setting components -->
|
||||
<!-- TODO make it reusable -->
|
||||
<template>
|
||||
<span
|
||||
class="DraftButtons"
|
||||
>
|
||||
<Popover
|
||||
v-if="$parent.isDirty"
|
||||
trigger="hover"
|
||||
normal-button
|
||||
:trigger-attrs="{ 'aria-label': $t('settings.commit_value_tooltip') }"
|
||||
@click="$parent.commitDraft"
|
||||
>
|
||||
<template #trigger>
|
||||
{{ $t('settings.commit_value') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="modified-tooltip">
|
||||
{{ $t('settings.commit_value_tooltip') }}
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
<Popover
|
||||
v-if="$parent.isDirty"
|
||||
trigger="hover"
|
||||
normal-button
|
||||
:trigger-attrs="{ 'aria-label': $t('settings.reset_value_tooltip') }"
|
||||
@click="$parent.reset"
|
||||
>
|
||||
<template #trigger>
|
||||
{{ $t('settings.reset_value') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="modified-tooltip">
|
||||
{{ $t('settings.reset_value_tooltip') }}
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
<Popover
|
||||
v-if="$parent.canHardReset"
|
||||
trigger="hover"
|
||||
normal-button
|
||||
:trigger-attrs="{ 'aria-label': $t('settings.hard_reset_value_tooltip') }"
|
||||
@click="$parent.hardReset"
|
||||
>
|
||||
<template #trigger>
|
||||
{{ $t('settings.hard_reset_value') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="modified-tooltip">
|
||||
{{ $t('settings.hard_reset_value_tooltip') }}
|
||||
</div>
|
||||
</template>
|
||||
</Popover>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Popover from 'src/components/popover/popover.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { faWrench } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faWrench
|
||||
)
|
||||
|
||||
export default {
|
||||
components: { Popover },
|
||||
props: ['changed']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.DraftButtons {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
|
||||
.button-default {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.draft-tooltip {
|
||||
margin: 0.5em 1em;
|
||||
min-width: 10em;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
16
src/components/settings_modal/helpers/float_setting.vue
Normal file
16
src/components/settings_modal/helpers/float_setting.vue
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<template>
|
||||
<NumberSetting
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot />
|
||||
</NumberSetting>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NumberSetting from './number_setting.vue'
|
||||
export default {
|
||||
components: {
|
||||
NumberSetting
|
||||
}
|
||||
}
|
||||
</script>
|
||||
13
src/components/settings_modal/helpers/group_setting.js
Normal file
13
src/components/settings_modal/helpers/group_setting.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { isEqual } from 'lodash'
|
||||
|
||||
import Setting from './setting.js'
|
||||
|
||||
export default {
|
||||
...Setting,
|
||||
computed: {
|
||||
...Setting.computed,
|
||||
isDirty () {
|
||||
return !isEqual(this.state, this.draft)
|
||||
}
|
||||
}
|
||||
}
|
||||
15
src/components/settings_modal/helpers/group_setting.vue
Normal file
15
src/components/settings_modal/helpers/group_setting.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<span
|
||||
v-if="matchesExpertLevel"
|
||||
class="GroupSetting"
|
||||
>
|
||||
<ModifiedIndicator
|
||||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||
<DraftButtons />
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script src="./group_setting.js"></script>
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import { get, set } from 'lodash'
|
||||
import ModifiedIndicator from './modified_indicator.vue'
|
||||
export default {
|
||||
components: {
|
||||
ModifiedIndicator
|
||||
},
|
||||
props: {
|
||||
path: String,
|
||||
disabled: Boolean,
|
||||
min: Number,
|
||||
expert: [Number, String]
|
||||
},
|
||||
computed: {
|
||||
pathDefault () {
|
||||
const [firstSegment, ...rest] = this.path.split('.')
|
||||
return [firstSegment + 'DefaultValue', ...rest].join('.')
|
||||
},
|
||||
state () {
|
||||
const value = get(this.$parent, this.path)
|
||||
if (value === undefined) {
|
||||
return this.defaultState
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
defaultState () {
|
||||
return get(this.$parent, this.pathDefault)
|
||||
},
|
||||
isChanged () {
|
||||
return this.state !== this.defaultState
|
||||
},
|
||||
matchesExpertLevel () {
|
||||
return (this.expert || 0) <= this.$parent.expertLevel
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update (e) {
|
||||
set(this.$parent, this.path, parseInt(e.target.value))
|
||||
},
|
||||
reset () {
|
||||
set(this.$parent, this.path, this.defaultState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,17 @@
|
|||
<template>
|
||||
<span
|
||||
v-if="matchesExpertLevel"
|
||||
class="IntegerSetting"
|
||||
<NumberSetting
|
||||
v-bind="$attrs"
|
||||
truncate="1"
|
||||
>
|
||||
<label :for="path">
|
||||
<slot />
|
||||
</label>
|
||||
<input
|
||||
:id="path"
|
||||
class="number-input"
|
||||
type="number"
|
||||
step="1"
|
||||
:disabled="disabled"
|
||||
:min="min || 0"
|
||||
:value="state"
|
||||
@change="update"
|
||||
>
|
||||
{{ ' ' }}
|
||||
<ModifiedIndicator
|
||||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
</span>
|
||||
<slot />
|
||||
</NumberSetting>
|
||||
</template>
|
||||
|
||||
<script src="./integer_setting.js"></script>
|
||||
<script>
|
||||
import NumberSetting from './number_setting.vue'
|
||||
export default {
|
||||
components: {
|
||||
NumberSetting
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
>
|
||||
<Popover
|
||||
trigger="hover"
|
||||
:trigger-attrs="{ 'aria-label': $t('settings.setting_changed') }"
|
||||
>
|
||||
<template #trigger>
|
||||
|
||||
<FAIcon
|
||||
icon="wrench"
|
||||
:aria-label="$t('settings.setting_changed')"
|
||||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
|
|
|
|||
24
src/components/settings_modal/helpers/number_setting.js
Normal file
24
src/components/settings_modal/helpers/number_setting.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import Setting from './setting.js'
|
||||
|
||||
export default {
|
||||
...Setting,
|
||||
props: {
|
||||
...Setting.props,
|
||||
truncate: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...Setting.methods,
|
||||
getValue (e) {
|
||||
if (!this.truncate === 1) {
|
||||
return parseInt(e.target.value)
|
||||
} else if (this.truncate > 1) {
|
||||
return Math.trunc(e.target.value / this.truncate) * this.truncate
|
||||
}
|
||||
return parseFloat(e.target.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
45
src/components/settings_modal/helpers/number_setting.vue
Normal file
45
src/components/settings_modal/helpers/number_setting.vue
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<span
|
||||
v-if="matchesExpertLevel"
|
||||
class="NumberSetting"
|
||||
>
|
||||
<label
|
||||
:for="path"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
<template v-if="backendDescriptionLabel">
|
||||
{{ backendDescriptionLabel + ' ' }}
|
||||
</template>
|
||||
<template v-else-if="source === 'admin'">
|
||||
MISSING LABEL FOR {{ path }}
|
||||
</template>
|
||||
<slot v-else />
|
||||
</label>
|
||||
<input
|
||||
:id="path"
|
||||
class="number-input"
|
||||
type="number"
|
||||
:step="step || 1"
|
||||
:disabled="shouldBeDisabled"
|
||||
:min="min || 0"
|
||||
:value="realDraftMode ? draft :state"
|
||||
@change="update"
|
||||
>
|
||||
{{ ' ' }}
|
||||
<ModifiedIndicator
|
||||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||
<DraftButtons />
|
||||
<p
|
||||
v-if="backendDescriptionDescription"
|
||||
class="setting-description"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
{{ backendDescriptionDescription + ' ' }}
|
||||
</p>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script src="./number_setting.js"></script>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<span
|
||||
v-if="serverSide"
|
||||
class="ServerSideIndicator"
|
||||
v-if="isProfile"
|
||||
class="ProfileSettingIndicator"
|
||||
>
|
||||
<Popover
|
||||
trigger="hover"
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
/>
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="serverside-tooltip">
|
||||
<div class="profilesetting-tooltip">
|
||||
{{ $t('settings.setting_server_side') }}
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -33,17 +33,17 @@ library.add(
|
|||
|
||||
export default {
|
||||
components: { Popover },
|
||||
props: ['serverSide']
|
||||
props: ['isProfile']
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.ServerSideIndicator {
|
||||
.ProfileSettingIndicator {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.serverside-tooltip {
|
||||
.profilesetting-tooltip {
|
||||
margin: 0.5em 1em;
|
||||
min-width: 10em;
|
||||
text-align: center;
|
||||
237
src/components/settings_modal/helpers/setting.js
Normal file
237
src/components/settings_modal/helpers/setting.js
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
import ModifiedIndicator from './modified_indicator.vue'
|
||||
import ProfileSettingIndicator from './profile_setting_indicator.vue'
|
||||
import DraftButtons from './draft_buttons.vue'
|
||||
import { get, set, cloneDeep } from 'lodash'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ModifiedIndicator,
|
||||
DraftButtons,
|
||||
ProfileSettingIndicator
|
||||
},
|
||||
props: {
|
||||
path: {
|
||||
type: [String, Array],
|
||||
required: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
parentPath: {
|
||||
type: [String, Array]
|
||||
},
|
||||
parentInvert: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
expert: {
|
||||
type: [Number, String],
|
||||
default: 0
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
default: undefined
|
||||
},
|
||||
hideDescription: {
|
||||
type: Boolean
|
||||
},
|
||||
swapDescriptionAndLabel: {
|
||||
type: Boolean
|
||||
},
|
||||
overrideBackendDescription: {
|
||||
type: Boolean
|
||||
},
|
||||
overrideBackendDescriptionLabel: {
|
||||
type: Boolean
|
||||
},
|
||||
draftMode: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
inject: {
|
||||
defaultSource: {
|
||||
default: 'default'
|
||||
},
|
||||
defaultDraftMode: {
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
localDraft: null
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.realDraftMode && this.realSource !== 'admin') {
|
||||
this.draft = this.state
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
draft: {
|
||||
// TODO allow passing shared draft object?
|
||||
get () {
|
||||
if (this.realSource === 'admin') {
|
||||
return get(this.$store.state.adminSettings.draft, this.canonPath)
|
||||
} else {
|
||||
return this.localDraft
|
||||
}
|
||||
},
|
||||
set (value) {
|
||||
if (this.realSource === 'admin') {
|
||||
this.$store.commit('updateAdminDraft', { path: this.canonPath, value })
|
||||
} else {
|
||||
this.localDraft = value
|
||||
}
|
||||
}
|
||||
},
|
||||
state () {
|
||||
const value = get(this.configSource, this.canonPath)
|
||||
if (value === undefined) {
|
||||
return this.defaultState
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
visibleState () {
|
||||
return this.realDraftMode ? this.draft : this.state
|
||||
},
|
||||
realSource () {
|
||||
return this.source || this.defaultSource
|
||||
},
|
||||
realDraftMode () {
|
||||
return typeof this.draftMode === 'undefined' ? this.defaultDraftMode : this.draftMode
|
||||
},
|
||||
backendDescription () {
|
||||
return get(this.$store.state.adminSettings.descriptions, this.path)
|
||||
},
|
||||
backendDescriptionLabel () {
|
||||
if (this.realSource !== 'admin') return ''
|
||||
if (!this.backendDescription || this.overrideBackendDescriptionLabel) {
|
||||
return this.$t([
|
||||
'admin_dash',
|
||||
'temp_overrides',
|
||||
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
|
||||
'label'
|
||||
].join('.'))
|
||||
} else {
|
||||
return this.swapDescriptionAndLabel
|
||||
? this.backendDescription?.description
|
||||
: this.backendDescription?.label
|
||||
}
|
||||
},
|
||||
backendDescriptionDescription () {
|
||||
if (this.realSource !== 'admin') return ''
|
||||
if (this.hideDescription) return null
|
||||
if (!this.backendDescription || this.overrideBackendDescription) {
|
||||
return this.$t([
|
||||
'admin_dash',
|
||||
'temp_overrides',
|
||||
...this.canonPath.map(p => p.replace(/\./g, '_DOT_')),
|
||||
'description'
|
||||
].join('.'))
|
||||
} else {
|
||||
return this.swapDescriptionAndLabel
|
||||
? this.backendDescription?.label
|
||||
: this.backendDescription?.description
|
||||
}
|
||||
},
|
||||
backendDescriptionSuggestions () {
|
||||
return this.backendDescription?.suggestions
|
||||
},
|
||||
shouldBeDisabled () {
|
||||
const parentValue = this.parentPath !== undefined ? get(this.configSource, this.parentPath) : null
|
||||
return this.disabled || (parentValue !== null ? (this.parentInvert ? parentValue : !parentValue) : false)
|
||||
},
|
||||
configSource () {
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return this.$store.state.profileConfig
|
||||
case 'admin':
|
||||
return this.$store.state.adminSettings.config
|
||||
default:
|
||||
return this.$store.getters.mergedConfig
|
||||
}
|
||||
},
|
||||
configSink () {
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return (k, v) => this.$store.dispatch('setProfileOption', { name: k, value: v })
|
||||
case 'admin':
|
||||
return (k, v) => this.$store.dispatch('pushAdminSetting', { path: k, value: v })
|
||||
default:
|
||||
return (k, v) => this.$store.dispatch('setOption', { name: k, value: v })
|
||||
}
|
||||
},
|
||||
defaultState () {
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
return {}
|
||||
default:
|
||||
return get(this.$store.getters.defaultConfig, this.path)
|
||||
}
|
||||
},
|
||||
isProfileSetting () {
|
||||
return this.realSource === 'profile'
|
||||
},
|
||||
isChanged () {
|
||||
switch (this.realSource) {
|
||||
case 'profile':
|
||||
case 'admin':
|
||||
return false
|
||||
default:
|
||||
return this.state !== this.defaultState
|
||||
}
|
||||
},
|
||||
canonPath () {
|
||||
return Array.isArray(this.path) ? this.path : this.path.split('.')
|
||||
},
|
||||
isDirty () {
|
||||
if (this.realSource === 'admin' && this.canonPath.length > 3) {
|
||||
return false // should not show draft buttons for "grouped" values
|
||||
} else {
|
||||
return this.realDraftMode && this.draft !== this.state
|
||||
}
|
||||
},
|
||||
canHardReset () {
|
||||
return this.realSource === 'admin' && this.$store.state.adminSettings.modifiedPaths.has(this.canonPath.join(' -> '))
|
||||
},
|
||||
matchesExpertLevel () {
|
||||
return (this.expert || 0) <= this.$store.state.config.expertLevel > 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getValue (e) {
|
||||
return e.target.value
|
||||
},
|
||||
update (e) {
|
||||
if (this.realDraftMode) {
|
||||
this.draft = this.getValue(e)
|
||||
} else {
|
||||
this.configSink(this.path, this.getValue(e))
|
||||
}
|
||||
},
|
||||
commitDraft () {
|
||||
if (this.realDraftMode) {
|
||||
this.configSink(this.path, this.draft)
|
||||
}
|
||||
},
|
||||
reset () {
|
||||
if (this.realDraftMode) {
|
||||
this.draft = cloneDeep(this.state)
|
||||
} else {
|
||||
set(this.$store.getters.mergedConfig, this.path, cloneDeep(this.defaultState))
|
||||
}
|
||||
},
|
||||
hardReset () {
|
||||
switch (this.realSource) {
|
||||
case 'admin':
|
||||
return this.$store.dispatch('resetAdminSetting', { path: this.path })
|
||||
.then(() => { this.draft = this.state })
|
||||
default:
|
||||
console.warn('Hard reset not implemented yet!')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,52 +1,18 @@
|
|||
import { defaultState as configDefaultState } from 'src/modules/config.js'
|
||||
import { defaultState as serverSideConfigDefaultState } from 'src/modules/serverSideConfig.js'
|
||||
|
||||
const SharedComputedObject = () => ({
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
// Getting values for default properties
|
||||
...Object.keys(configDefaultState)
|
||||
.map(key => [
|
||||
key + 'DefaultValue',
|
||||
function () {
|
||||
return this.$store.getters.defaultConfig[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 }), {}),
|
||||
...Object.keys(serverSideConfigDefaultState)
|
||||
.map(key => ['serverSide_' + key, {
|
||||
get () { return this.$store.state.serverSideConfig[key] },
|
||||
set (value) {
|
||||
this.$store.dispatch('setServerSideOption', { 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 })
|
||||
})
|
||||
}
|
||||
expertLevel () {
|
||||
return this.$store.getters.mergedConfig.expertLevel > 0
|
||||
},
|
||||
mergedConfig () {
|
||||
return this.$store.getters.mergedConfig
|
||||
},
|
||||
adminConfig () {
|
||||
return this.$store.state.adminSettings.config
|
||||
},
|
||||
adminDraft () {
|
||||
return this.$store.state.adminSettings.draft
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -1,67 +1,40 @@
|
|||
import { get, set } from 'lodash'
|
||||
import ModifiedIndicator from './modified_indicator.vue'
|
||||
import Select from 'src/components/select/select.vue'
|
||||
import Setting from './setting.js'
|
||||
|
||||
export const allCssUnits = ['cm', 'mm', 'in', 'px', 'pt', 'pc', 'em', 'ex', 'ch', 'rem', 'vw', 'vh', 'vmin', 'vmax', '%']
|
||||
export const defaultHorizontalUnits = ['px', 'rem', 'vw']
|
||||
export const defaultVerticalUnits = ['px', 'rem', 'vh']
|
||||
|
||||
export default {
|
||||
...Setting,
|
||||
components: {
|
||||
ModifiedIndicator,
|
||||
...Setting.components,
|
||||
Select
|
||||
},
|
||||
props: {
|
||||
path: String,
|
||||
disabled: Boolean,
|
||||
...Setting.props,
|
||||
min: Number,
|
||||
units: {
|
||||
type: [String],
|
||||
type: Array,
|
||||
default: () => allCssUnits
|
||||
},
|
||||
expert: [Number, String]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pathDefault () {
|
||||
const [firstSegment, ...rest] = this.path.split('.')
|
||||
return [firstSegment + 'DefaultValue', ...rest].join('.')
|
||||
},
|
||||
...Setting.computed,
|
||||
stateUnit () {
|
||||
return (this.state || '').replace(/\d+/, '')
|
||||
return this.state.replace(/\d+/, '')
|
||||
},
|
||||
stateValue () {
|
||||
return (this.state || '').replace(/\D+/, '')
|
||||
},
|
||||
state () {
|
||||
const value = get(this.$parent, this.path)
|
||||
if (value === undefined) {
|
||||
return this.defaultState
|
||||
} else {
|
||||
return value
|
||||
}
|
||||
},
|
||||
defaultState () {
|
||||
return get(this.$parent, this.pathDefault)
|
||||
},
|
||||
isChanged () {
|
||||
return this.state !== this.defaultState
|
||||
},
|
||||
matchesExpertLevel () {
|
||||
return (this.expert || 0) <= this.$parent.expertLevel
|
||||
return this.state.replace(/\D+/, '')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
update (e) {
|
||||
set(this.$parent, this.path, e)
|
||||
},
|
||||
reset () {
|
||||
set(this.$parent, this.path, this.defaultState)
|
||||
},
|
||||
...Setting.methods,
|
||||
updateValue (e) {
|
||||
set(this.$parent, this.path, parseInt(e.target.value) + this.stateUnit)
|
||||
this.configSink(this.path, parseInt(e.target.value) + this.stateUnit)
|
||||
},
|
||||
updateUnit (e) {
|
||||
set(this.$parent, this.path, this.stateValue + e.target.value)
|
||||
this.configSink(this.path, this.stateValue + e.target.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,11 +45,18 @@
|
|||
<script src="./size_setting.js"></script>
|
||||
|
||||
<style lang="scss">
|
||||
.css-unit-input,
|
||||
.css-unit-input select {
|
||||
margin-left: 0.5em;
|
||||
width: 4em;
|
||||
max-width: 4em;
|
||||
min-width: 4em;
|
||||
.SizeSetting {
|
||||
.number-input {
|
||||
max-width: 6.5em;
|
||||
}
|
||||
|
||||
.css-unit-input,
|
||||
.css-unit-input select {
|
||||
margin-left: 0.5em;
|
||||
width: 4em;
|
||||
max-width: 4em;
|
||||
min-width: 4em;
|
||||
}
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
5
src/components/settings_modal/helpers/string_setting.js
Normal file
5
src/components/settings_modal/helpers/string_setting.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import Setting from './setting.js'
|
||||
|
||||
export default {
|
||||
...Setting
|
||||
}
|
||||
42
src/components/settings_modal/helpers/string_setting.vue
Normal file
42
src/components/settings_modal/helpers/string_setting.vue
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<label
|
||||
v-if="matchesExpertLevel"
|
||||
class="StringSetting"
|
||||
>
|
||||
<label
|
||||
:for="path"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
<template v-if="backendDescriptionLabel">
|
||||
{{ backendDescriptionLabel + ' ' }}
|
||||
</template>
|
||||
<template v-else-if="source === 'admin'">
|
||||
MISSING LABEL FOR {{ path }}
|
||||
</template>
|
||||
<slot v-else />
|
||||
</label>
|
||||
<input
|
||||
:id="path"
|
||||
class="string-input"
|
||||
:disabled="shouldBeDisabled"
|
||||
:value="realDraftMode ? draft : state"
|
||||
@change="update"
|
||||
>
|
||||
{{ ' ' }}
|
||||
<ModifiedIndicator
|
||||
:changed="isChanged"
|
||||
:onclick="reset"
|
||||
/>
|
||||
<ProfileSettingIndicator :is-profile="isProfileSetting" />
|
||||
<DraftButtons />
|
||||
<p
|
||||
v-if="backendDescriptionDescription"
|
||||
class="setting-description"
|
||||
:class="{ 'faint': shouldBeDisabled }"
|
||||
>
|
||||
{{ backendDescriptionDescription + ' ' }}
|
||||
</p>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<script src="./string_setting.js"></script>
|
||||
|
|
@ -5,7 +5,7 @@ import getResettableAsyncComponent from 'src/services/resettable_async_component
|
|||
import Popover from '../popover/popover.vue'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import { cloneDeep } from 'lodash'
|
||||
import { cloneDeep, isEqual } from 'lodash'
|
||||
import {
|
||||
newImporter,
|
||||
newExporter
|
||||
|
|
@ -53,8 +53,16 @@ const SettingsModal = {
|
|||
Modal,
|
||||
Popover,
|
||||
Checkbox,
|
||||
SettingsModalContent: getResettableAsyncComponent(
|
||||
() => import('./settings_modal_content.vue'),
|
||||
SettingsModalUserContent: getResettableAsyncComponent(
|
||||
() => import('./settings_modal_user_content.vue'),
|
||||
{
|
||||
loadingComponent: PanelLoading,
|
||||
errorComponent: AsyncComponentError,
|
||||
delay: 0
|
||||
}
|
||||
),
|
||||
SettingsModalAdminContent: getResettableAsyncComponent(
|
||||
() => import('./settings_modal_admin_content.vue'),
|
||||
{
|
||||
loadingComponent: PanelLoading,
|
||||
errorComponent: AsyncComponentError,
|
||||
|
|
@ -147,6 +155,12 @@ const SettingsModal = {
|
|||
PLEROMAFE_SETTINGS_MINOR_VERSION
|
||||
]
|
||||
return clone
|
||||
},
|
||||
resetAdminDraft () {
|
||||
this.$store.commit('resetAdminDraft')
|
||||
},
|
||||
pushAdminDraft () {
|
||||
this.$store.dispatch('pushAdminDraft')
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -156,8 +170,14 @@ const SettingsModal = {
|
|||
modalActivated () {
|
||||
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||
},
|
||||
modalOpenedOnce () {
|
||||
return this.$store.state.interface.settingsModalLoaded
|
||||
modalMode () {
|
||||
return this.$store.state.interface.settingsModalMode
|
||||
},
|
||||
modalOpenedOnceUser () {
|
||||
return this.$store.state.interface.settingsModalLoadedUser
|
||||
},
|
||||
modalOpenedOnceAdmin () {
|
||||
return this.$store.state.interface.settingsModalLoadedAdmin
|
||||
},
|
||||
modalPeeked () {
|
||||
return this.$store.state.interface.settingsModalState === 'minimized'
|
||||
|
|
@ -167,9 +187,14 @@ const SettingsModal = {
|
|||
return this.$store.state.config.expertLevel > 0
|
||||
},
|
||||
set (value) {
|
||||
console.log(value)
|
||||
this.$store.dispatch('setOption', { name: 'expertLevel', value: value ? 1 : 0 })
|
||||
}
|
||||
},
|
||||
adminDraftAny () {
|
||||
return !isEqual(
|
||||
this.$store.state.adminSettings.config,
|
||||
this.$store.state.adminSettings.draft
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.setting-description {
|
||||
margin-top: 0.2em;
|
||||
margin-bottom: 2em;
|
||||
font-size: 70%;
|
||||
}
|
||||
|
||||
.settings-modal-panel {
|
||||
overflow: hidden;
|
||||
transition: transform;
|
||||
|
|
@ -37,7 +43,9 @@
|
|||
|
||||
.btn {
|
||||
min-height: 2em;
|
||||
min-width: 10em;
|
||||
}
|
||||
|
||||
.btn:not(.dropdown-button) {
|
||||
padding: 0 2em;
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +53,8 @@
|
|||
|
||||
.settings-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
line-height: 2;
|
||||
|
||||
>* {
|
||||
margin-right: 0.5em;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
<div class="settings-modal-panel panel">
|
||||
<div class="panel-heading">
|
||||
<span class="title">
|
||||
{{ $t('settings.settings') }}
|
||||
{{ modalMode === 'user' ? $t('settings.settings') : $t('admin_dash.window_title') }}
|
||||
</span>
|
||||
<transition name="fade">
|
||||
<div
|
||||
|
|
@ -42,10 +42,12 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<SettingsModalContent v-if="modalOpenedOnce" />
|
||||
<SettingsModalUserContent v-if="modalMode === 'user' && modalOpenedOnceUser" />
|
||||
<SettingsModalAdminContent v-if="modalMode === 'admin' && modalOpenedOnceAdmin" />
|
||||
</div>
|
||||
<div class="panel-footer settings-footer">
|
||||
<div class="panel-footer settings-footer -flexible-height">
|
||||
<Popover
|
||||
v-if="modalMode === 'user'"
|
||||
class="export"
|
||||
trigger="click"
|
||||
placement="top"
|
||||
|
|
@ -107,10 +109,42 @@
|
|||
>
|
||||
{{ $t("settings.expert_mode") }}
|
||||
</Checkbox>
|
||||
<span v-if="modalMode === 'admin'">
|
||||
<i18n-t keypath="admin_dash.wip_notice">
|
||||
<template #adminFeLink>
|
||||
<a
|
||||
href="/pleroma/admin/#/login-pleroma"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t("admin_dash.old_ui_link") }}
|
||||
</a>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</span>
|
||||
<span
|
||||
id="unscrolled-content"
|
||||
class="extra-content"
|
||||
/>
|
||||
<span
|
||||
v-if="modalMode === 'admin'"
|
||||
class="admin-buttons"
|
||||
>
|
||||
<button
|
||||
class="button-default btn"
|
||||
:disabled="!adminDraftAny"
|
||||
@click="resetAdminDraft"
|
||||
>
|
||||
{{ $t("admin_dash.reset_all") }}
|
||||
</button>
|
||||
{{ ' ' }}
|
||||
<button
|
||||
class="button-default btn"
|
||||
:disabled="!adminDraftAny"
|
||||
@click="pushAdminDraft"
|
||||
>
|
||||
{{ $t("admin_dash.commit_all") }}
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
|
||||
|
||||
import InstanceTab from './admin_tabs/instance_tab.vue'
|
||||
import LimitsTab from './admin_tabs/limits_tab.vue'
|
||||
import FrontendsTab from './admin_tabs/frontends_tab.vue'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faWrench,
|
||||
faHand,
|
||||
faLaptopCode,
|
||||
faPaintBrush,
|
||||
faBell,
|
||||
faDownload,
|
||||
faEyeSlash,
|
||||
faInfo
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
library.add(
|
||||
faWrench,
|
||||
faHand,
|
||||
faLaptopCode,
|
||||
faPaintBrush,
|
||||
faBell,
|
||||
faDownload,
|
||||
faEyeSlash,
|
||||
faInfo
|
||||
)
|
||||
|
||||
const SettingsModalAdminContent = {
|
||||
components: {
|
||||
TabSwitcher,
|
||||
|
||||
InstanceTab,
|
||||
LimitsTab,
|
||||
FrontendsTab
|
||||
},
|
||||
computed: {
|
||||
user () {
|
||||
return this.$store.state.users.currentUser
|
||||
},
|
||||
isLoggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
open () {
|
||||
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||
},
|
||||
bodyLock () {
|
||||
return this.$store.state.interface.settingsModalState === 'visible'
|
||||
},
|
||||
adminDbLoaded () {
|
||||
return this.$store.state.adminSettings.loaded
|
||||
},
|
||||
adminDescriptionsLoaded () {
|
||||
return this.$store.state.adminSettings.descriptions !== null
|
||||
},
|
||||
noDb () {
|
||||
return this.$store.state.adminSettings.dbConfigEnabled === false
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (this.user.rights.admin) {
|
||||
this.$store.dispatch('loadAdminStuff')
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen () {
|
||||
const targetTab = this.$store.state.interface.settingsModalTargetTab
|
||||
// We're being told to open in specific tab
|
||||
if (targetTab) {
|
||||
const tabIndex = this.$refs.tabSwitcher.$slots.default().findIndex(elm => {
|
||||
return elm.props && elm.props['data-tab-name'] === targetTab
|
||||
})
|
||||
if (tabIndex >= 0) {
|
||||
this.$refs.tabSwitcher.setTab(tabIndex)
|
||||
}
|
||||
}
|
||||
// Clear the state of target tab, so that next time settings is opened
|
||||
// it doesn't force it.
|
||||
this.$store.dispatch('clearSettingsModalTargetTab')
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.onOpen()
|
||||
},
|
||||
watch: {
|
||||
open: function (value) {
|
||||
if (value) this.onOpen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsModalAdminContent
|
||||
|
|
@ -48,9 +48,5 @@
|
|||
color: var(--cRed, $fallback--cRed);
|
||||
color: $fallback--cRed;
|
||||
}
|
||||
|
||||
.number-input {
|
||||
max-width: 6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
<template>
|
||||
<tab-switcher
|
||||
v-if="adminDescriptionsLoaded && (noDb || adminDbLoaded)"
|
||||
ref="tabSwitcher"
|
||||
class="settings_tab-switcher"
|
||||
:side-tab-bar="true"
|
||||
:scrollable-tabs="true"
|
||||
:render-only-focused="true"
|
||||
:body-scroll-lock="bodyLock"
|
||||
>
|
||||
<div
|
||||
v-if="noDb"
|
||||
:label="$t('admin_dash.tabs.nodb')"
|
||||
icon="exclamation-triangle"
|
||||
data-tab-name="nodb-notice"
|
||||
>
|
||||
<div :label="$t('admin_dash.tabs.nodb')">
|
||||
<div class="setting-item">
|
||||
<h2>{{ $t('admin_dash.nodb.heading') }}</h2>
|
||||
<i18n-t keypath="admin_dash.nodb.text">
|
||||
<template #documentation>
|
||||
<a
|
||||
href="https://docs-develop.pleroma.social/backend/configuration/howto_database_config/"
|
||||
target="_blank"
|
||||
>
|
||||
{{ $t("admin_dash.nodb.documentation") }}
|
||||
</a>
|
||||
</template>
|
||||
<template #property>
|
||||
<code>config :pleroma, configurable_from_database</code>
|
||||
</template>
|
||||
<template #value>
|
||||
<code>true</code>
|
||||
</template>
|
||||
</i18n-t>
|
||||
<p>{{ $t('admin_dash.nodb.text2') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="adminDbLoaded"
|
||||
:label="$t('admin_dash.tabs.instance')"
|
||||
icon="wrench"
|
||||
data-tab-name="general"
|
||||
>
|
||||
<InstanceTab />
|
||||
</div>
|
||||
<div
|
||||
v-if="adminDbLoaded"
|
||||
:label="$t('admin_dash.tabs.limits')"
|
||||
icon="hand"
|
||||
data-tab-name="limits"
|
||||
>
|
||||
<LimitsTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('admin_dash.tabs.frontends')"
|
||||
icon="laptop-code"
|
||||
data-tab-name="frontends"
|
||||
>
|
||||
<FrontendsTab />
|
||||
</div>
|
||||
</tab-switcher>
|
||||
</template>
|
||||
|
||||
<script src="./settings_modal_admin_content.js"></script>
|
||||
|
||||
<style src="./settings_modal_admin_content.scss" lang="scss"></style>
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
@import "src/variables";
|
||||
|
||||
.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,
|
||||
> label {
|
||||
display: block;
|
||||
margin-bottom: 0.5em;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.select-multiple {
|
||||
display: flex;
|
||||
|
||||
.option-list {
|
||||
margin: 0;
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
&: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 svg {
|
||||
color: var(--cRed, $fallback--cRed);
|
||||
color: $fallback--cRed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -78,6 +78,6 @@
|
|||
</tab-switcher>
|
||||
</template>
|
||||
|
||||
<script src="./settings_modal_content.js"></script>
|
||||
<script src="./settings_modal_user_content.js"></script>
|
||||
|
||||
<style src="./settings_modal_content.scss" lang="scss"></style>
|
||||
<style src="./settings_modal_user_content.scss" lang="scss"></style>
|
||||
|
|
@ -7,13 +7,11 @@
|
|||
<BooleanSetting path="hideFilteredStatuses">
|
||||
{{ $t('settings.hide_filtered_statuses') }}
|
||||
</BooleanSetting>
|
||||
<ul
|
||||
class="setting-list suboptions"
|
||||
:class="[{disabled: !streaming}]"
|
||||
>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
:disabled="hideFilteredStatuses"
|
||||
parent-path="hideFilteredStatuses"
|
||||
:parent-invert="true"
|
||||
path="hideWordFilteredPosts"
|
||||
>
|
||||
{{ $t('settings.hide_wordfiltered_statuses') }}
|
||||
|
|
@ -22,7 +20,8 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
v-if="user"
|
||||
:disabled="hideFilteredStatuses"
|
||||
parent-path="hideFilteredStatuses"
|
||||
:parent-invert="true"
|
||||
path="hideMutedThreads"
|
||||
>
|
||||
{{ $t('settings.hide_muted_threads') }}
|
||||
|
|
@ -31,7 +30,8 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
v-if="user"
|
||||
:disabled="hideFilteredStatuses"
|
||||
parent-path="hideFilteredStatuses"
|
||||
:parent-invert="true"
|
||||
path="hideMutedPosts"
|
||||
>
|
||||
{{ $t('settings.hide_muted_posts') }}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,12 @@ import BooleanSetting from '../helpers/boolean_setting.vue'
|
|||
import ChoiceSetting from '../helpers/choice_setting.vue'
|
||||
import ScopeSelector from 'src/components/scope_selector/scope_selector.vue'
|
||||
import IntegerSetting from '../helpers/integer_setting.vue'
|
||||
import FloatSetting from '../helpers/float_setting.vue'
|
||||
import SizeSetting, { defaultHorizontalUnits } from '../helpers/size_setting.vue'
|
||||
import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue'
|
||||
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import ServerSideIndicator from '../helpers/server_side_indicator.vue'
|
||||
import ProfileSettingIndicator from '../helpers/profile_setting_indicator.vue'
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
faGlobe
|
||||
|
|
@ -62,10 +63,11 @@ const GeneralTab = {
|
|||
BooleanSetting,
|
||||
ChoiceSetting,
|
||||
IntegerSetting,
|
||||
FloatSetting,
|
||||
SizeSetting,
|
||||
InterfaceLanguageSwitcher,
|
||||
ScopeSelector,
|
||||
ServerSideIndicator
|
||||
ProfileSettingIndicator
|
||||
},
|
||||
computed: {
|
||||
horizontalUnits () {
|
||||
|
|
@ -108,7 +110,7 @@ const GeneralTab = {
|
|||
},
|
||||
methods: {
|
||||
changeDefaultScope (value) {
|
||||
this.$store.dispatch('setServerSideOption', { name: 'defaultScope', value })
|
||||
this.$store.dispatch('setProfileOption', { name: 'defaultScope', value })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,14 +29,11 @@
|
|||
<BooleanSetting path="streaming">
|
||||
{{ $t('settings.streaming') }}
|
||||
</BooleanSetting>
|
||||
<ul
|
||||
class="setting-list suboptions"
|
||||
:class="[{disabled: !streaming}]"
|
||||
>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="pauseOnUnfocused"
|
||||
:disabled="!streaming"
|
||||
parent-path="streaming"
|
||||
>
|
||||
{{ $t('settings.pause_on_unfocused') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -213,7 +210,7 @@
|
|||
</ChoiceSetting>
|
||||
</li>
|
||||
<ul
|
||||
v-if="conversationDisplay !== 'linear'"
|
||||
v-if="mergedConfig.conversationDisplay !== 'linear'"
|
||||
class="setting-list suboptions"
|
||||
>
|
||||
<li>
|
||||
|
|
@ -265,12 +262,22 @@
|
|||
<li>
|
||||
<BooleanSetting
|
||||
v-if="user"
|
||||
path="serverSide_stripRichContent"
|
||||
source="profile"
|
||||
path="stripRichContent"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.no_rich_text_description') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<FloatSetting
|
||||
v-if="user"
|
||||
path="emojiReactionsScale"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.emoji_reactions_scale') }}
|
||||
</FloatSetting>
|
||||
</li>
|
||||
<h3>{{ $t('settings.attachments') }}</h3>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
|
|
@ -290,7 +297,7 @@
|
|||
<BooleanSetting
|
||||
path="preloadImage"
|
||||
expert="1"
|
||||
:disabled="!hideNsfw"
|
||||
parent-path="hideNsfw"
|
||||
>
|
||||
{{ $t('settings.preload_images') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -299,7 +306,7 @@
|
|||
<BooleanSetting
|
||||
path="useOneClickNsfw"
|
||||
expert="1"
|
||||
:disabled="!hideNsfw"
|
||||
parent-path="hideNsfw"
|
||||
>
|
||||
{{ $t('settings.use_one_click_nsfw') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -312,15 +319,13 @@
|
|||
>
|
||||
{{ $t('settings.loop_video') }}
|
||||
</BooleanSetting>
|
||||
<ul
|
||||
class="setting-list suboptions"
|
||||
:class="[{disabled: !streaming}]"
|
||||
>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="loopVideoSilentOnly"
|
||||
expert="1"
|
||||
:disabled="!loopVideo || !loopSilentAvailable"
|
||||
parent-path="loopVideo"
|
||||
:disabled="!loopSilentAvailable"
|
||||
>
|
||||
{{ $t('settings.loop_video_silent_only') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -418,18 +423,18 @@
|
|||
<ul class="setting-list">
|
||||
<li>
|
||||
<label for="default-vis">
|
||||
{{ $t('settings.default_vis') }} <ServerSideIndicator :server-side="true" />
|
||||
{{ $t('settings.default_vis') }} <ProfileSettingIndicator :is-profile="true" />
|
||||
<ScopeSelector
|
||||
class="scope-selector"
|
||||
:show-all="true"
|
||||
:user-default="serverSide_defaultScope"
|
||||
:initial-scope="serverSide_defaultScope"
|
||||
:user-default="$store.state.profileConfig.defaultScope"
|
||||
:initial-scope="$store.state.profileConfig.defaultScope"
|
||||
:on-scope-change="changeDefaultScope"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
<li>
|
||||
<!-- <BooleanSetting path="serverSide_defaultNSFW"> -->
|
||||
<!-- <BooleanSetting source="profile" path="defaultNSFW"> -->
|
||||
<BooleanSetting path="sensitiveByDefault">
|
||||
{{ $t('settings.sensitive_by_default') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -501,6 +506,14 @@
|
|||
{{ $t('settings.pad_emoji') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="autocompleteSelect"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.autocomplete_select_first') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,17 +9,20 @@ 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 withLoadMore from 'src/components/../hocs/with_load_more/with_load_more'
|
||||
import Checkbox from 'src/components/checkbox/checkbox.vue'
|
||||
|
||||
const BlockList = withSubscription({
|
||||
const BlockList = withLoadMore({
|
||||
fetch: (props, $store) => $store.dispatch('fetchBlocks'),
|
||||
select: (props, $store) => get($store.state.users.currentUser, 'blockIds', []),
|
||||
destroy: () => {},
|
||||
childPropName: 'items'
|
||||
})(SelectableList)
|
||||
|
||||
const MuteList = withSubscription({
|
||||
const MuteList = withLoadMore({
|
||||
fetch: (props, $store) => $store.dispatch('fetchMutes'),
|
||||
select: (props, $store) => get($store.state.users.currentUser, 'muteIds', []),
|
||||
destroy: () => {},
|
||||
childPropName: 'items'
|
||||
})(SelectableList)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,10 @@
|
|||
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_blockNotificationsFromStrangers">
|
||||
<BooleanSetting
|
||||
source="profile"
|
||||
path="blockNotificationsFromStrangers"
|
||||
>
|
||||
{{ $t('settings.notification_setting_block_from_strangers') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
|
|
@ -67,7 +70,8 @@
|
|||
</li>
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="serverSide_webPushHideContents"
|
||||
source="profile"
|
||||
path="webPushHideContents"
|
||||
expert="1"
|
||||
>
|
||||
{{ $t('settings.notification_setting_hide_notification_contents') }}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import InterfaceLanguageSwitcher from 'src/components/interface_language_switche
|
|||
import BooleanSetting from '../helpers/boolean_setting.vue'
|
||||
import SharedComputedObject from '../helpers/shared_computed_object.js'
|
||||
import localeService from 'src/services/locale/locale.service.js'
|
||||
import { propsToNative } from 'src/services/attributes_helper/attributes_helper.service.js'
|
||||
|
||||
import { library } from '@fortawesome/fontawesome-svg-core'
|
||||
import {
|
||||
|
|
@ -261,6 +262,9 @@ const ProfileTab = {
|
|||
messageArgs: [error.message],
|
||||
level: 'error'
|
||||
})
|
||||
},
|
||||
propsToNative (props) {
|
||||
return propsToNative(props)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,11 +8,14 @@
|
|||
enable-emoji-picker
|
||||
:suggest="emojiSuggestor"
|
||||
>
|
||||
<input
|
||||
id="username"
|
||||
v-model="newName"
|
||||
class="name-changer"
|
||||
>
|
||||
<template #default="inputProps">
|
||||
<input
|
||||
id="username"
|
||||
v-model="newName"
|
||||
class="name-changer"
|
||||
v-bind="propsToNative(inputProps)"
|
||||
>
|
||||
</template>
|
||||
</EmojiInput>
|
||||
<p>{{ $t('settings.bio') }}</p>
|
||||
<EmojiInput
|
||||
|
|
@ -20,10 +23,13 @@
|
|||
enable-emoji-picker
|
||||
:suggest="emojiUserSuggestor"
|
||||
>
|
||||
<textarea
|
||||
v-model="newBio"
|
||||
class="bio resize-height"
|
||||
/>
|
||||
<template #default="inputProps">
|
||||
<textarea
|
||||
v-model="newBio"
|
||||
class="bio resize-height"
|
||||
v-bind="propsToNative(inputProps)"
|
||||
/>
|
||||
</template>
|
||||
</EmojiInput>
|
||||
<p v-if="role === 'admin' || role === 'moderator'">
|
||||
<Checkbox v-model="showRole">
|
||||
|
|
@ -60,10 +66,13 @@
|
|||
hide-emoji-button
|
||||
:suggest="userSuggestor"
|
||||
>
|
||||
<input
|
||||
v-model="newFields[i].name"
|
||||
:placeholder="$t('settings.profile_fields.name')"
|
||||
>
|
||||
<template #default="inputProps">
|
||||
<input
|
||||
v-model="newFields[i].name"
|
||||
:placeholder="$t('settings.profile_fields.name')"
|
||||
v-bind="propsToNative(inputProps)"
|
||||
>
|
||||
</template>
|
||||
</EmojiInput>
|
||||
<EmojiInput
|
||||
v-model="newFields[i].value"
|
||||
|
|
@ -71,10 +80,13 @@
|
|||
hide-emoji-button
|
||||
:suggest="userSuggestor"
|
||||
>
|
||||
<input
|
||||
v-model="newFields[i].value"
|
||||
:placeholder="$t('settings.profile_fields.value')"
|
||||
>
|
||||
<template #default="inputProps">
|
||||
<input
|
||||
v-model="newFields[i].value"
|
||||
:placeholder="$t('settings.profile_fields.value')"
|
||||
v-bind="propsToNative(inputProps)"
|
||||
>
|
||||
</template>
|
||||
</EmojiInput>
|
||||
<button
|
||||
class="delete-field button-unstyled -hover-highlight"
|
||||
|
|
@ -242,37 +254,50 @@
|
|||
<h2>{{ $t('settings.account_privacy') }}</h2>
|
||||
<ul class="setting-list">
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_locked">
|
||||
<BooleanSetting
|
||||
source="profile"
|
||||
path="locked"
|
||||
>
|
||||
{{ $t('settings.lock_account_description') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_discoverable">
|
||||
<BooleanSetting
|
||||
source="profile"
|
||||
path="discoverable"
|
||||
>
|
||||
{{ $t('settings.discoverable') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_allowFollowingMove">
|
||||
<BooleanSetting
|
||||
source="profile"
|
||||
path="allowFollowingMove"
|
||||
>
|
||||
{{ $t('settings.allow_following_move') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_hideFavorites">
|
||||
<BooleanSetting
|
||||
source="profile"
|
||||
path="hideFavorites"
|
||||
>
|
||||
{{ $t('settings.hide_favorites_description') }}
|
||||
</BooleanSetting>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_hideFollowers">
|
||||
<BooleanSetting
|
||||
source="profile"
|
||||
path="hideFollowers"
|
||||
>
|
||||
{{ $t('settings.hide_followers_description') }}
|
||||
</BooleanSetting>
|
||||
<ul
|
||||
class="setting-list suboptions"
|
||||
:class="[{disabled: !serverSide_hideFollowers}]"
|
||||
>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="serverSide_hideFollowersCount"
|
||||
:disabled="!serverSide_hideFollowers"
|
||||
source="profile"
|
||||
path="hideFollowersCount"
|
||||
parent-path="hideFollowers"
|
||||
>
|
||||
{{ $t('settings.hide_followers_count_description') }}
|
||||
</BooleanSetting>
|
||||
|
|
@ -280,17 +305,18 @@
|
|||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<BooleanSetting path="serverSide_hideFollows">
|
||||
<BooleanSetting
|
||||
source="profile"
|
||||
path="hideFollows"
|
||||
>
|
||||
{{ $t('settings.hide_follows_description') }}
|
||||
</BooleanSetting>
|
||||
<ul
|
||||
class="setting-list suboptions"
|
||||
:class="[{disabled: !serverSide_hideFollows}]"
|
||||
>
|
||||
<ul class="setting-list suboptions">
|
||||
<li>
|
||||
<BooleanSetting
|
||||
path="serverSide_hideFollowsCount"
|
||||
:disabled="!serverSide_hideFollows"
|
||||
source="profile"
|
||||
path="hideFollowsCount"
|
||||
parent-path="hideFollows"
|
||||
>
|
||||
{{ $t('settings.hide_follows_count_description') }}
|
||||
</BooleanSetting>
|
||||
|
|
|
|||
|
|
@ -143,8 +143,8 @@
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<i18n
|
||||
path="settings.new_alias_target"
|
||||
<i18n-t
|
||||
keypath="settings.new_alias_target"
|
||||
tag="p"
|
||||
>
|
||||
<code
|
||||
|
|
@ -152,7 +152,7 @@
|
|||
>
|
||||
foo@example.org
|
||||
</code>
|
||||
</i18n>
|
||||
</i18n-t>
|
||||
<input
|
||||
v-model="addAliasTarget"
|
||||
>
|
||||
|
|
@ -175,16 +175,16 @@
|
|||
<h2>{{ $t('settings.move_account') }}</h2>
|
||||
<p>{{ $t('settings.move_account_notes') }}</p>
|
||||
<div>
|
||||
<i18n
|
||||
path="settings.move_account_target"
|
||||
<i18n-t
|
||||
keypath="settings.move_account_target"
|
||||
tag="p"
|
||||
>
|
||||
<code
|
||||
place="example"
|
||||
>
|
||||
foo@example.org
|
||||
</code>
|
||||
</i18n>
|
||||
<template #example>
|
||||
<code>
|
||||
foo@example.org
|
||||
</code>
|
||||
</template>
|
||||
</i18n-t>
|
||||
<input
|
||||
v-model="moveAccountTarget"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -129,12 +129,13 @@
|
|||
v-model="selected.inset"
|
||||
:disabled="!present"
|
||||
name="inset"
|
||||
class="input-inset"
|
||||
class="input-inset visible-for-screenreader-only"
|
||||
type="checkbox"
|
||||
>
|
||||
<label
|
||||
class="checkbox-label"
|
||||
for="inset"
|
||||
:aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -115,7 +115,10 @@ const SideDrawer = {
|
|||
GestureService.updateSwipe(e, this.closeGesture)
|
||||
},
|
||||
openSettingsModal () {
|
||||
this.$store.dispatch('openSettingsModal')
|
||||
this.$store.dispatch('openSettingsModal', 'user')
|
||||
},
|
||||
openAdminModal () {
|
||||
this.$store.dispatch('openSettingsModal', 'admin')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -180,16 +180,16 @@
|
|||
v-if="currentUser && currentUser.role === 'admin'"
|
||||
@click="toggleDrawer"
|
||||
>
|
||||
<a
|
||||
href="/pleroma/admin/#/login-pleroma"
|
||||
target="_blank"
|
||||
<button
|
||||
class="button-unstyled -link -fullwidth"
|
||||
@click.stop="openAdminModal"
|
||||
>
|
||||
<FAIcon
|
||||
fixed-width
|
||||
class="fa-scale-110 fa-old-padding"
|
||||
icon="tachometer-alt"
|
||||
/> {{ $t("nav.administration") }}
|
||||
</a>
|
||||
</button>
|
||||
</li>
|
||||
<li
|
||||
v-if="currentUser && supportsAnnouncements"
|
||||
|
|
|
|||
|
|
@ -60,13 +60,7 @@ export default {
|
|||
const isWanted = slot => slot.props && slot.props['data-tab-name'] === tabName
|
||||
return this.$slots.default().findIndex(isWanted) === this.activeIndex
|
||||
}
|
||||
},
|
||||
settingsModalVisible () {
|
||||
return this.settingsModalState === 'visible'
|
||||
},
|
||||
...mapState({
|
||||
settingsModalState: state => state.interface.settingsModalState
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeUpdate () {
|
||||
const currentSlot = this.slots()[this.active]
|
||||
|
|
@ -117,6 +111,7 @@ export default {
|
|||
onClick={this.clickTab(index)}
|
||||
class={classesTab.join(' ')}
|
||||
type="button"
|
||||
role="tab"
|
||||
>
|
||||
<img src={props.image} title={props['image-tooltip']}/>
|
||||
{props.label ? '' : props.label}
|
||||
|
|
@ -131,6 +126,7 @@ export default {
|
|||
onClick={this.clickTab(index)}
|
||||
class={classesTab.join(' ')}
|
||||
type="button"
|
||||
role="tab"
|
||||
>
|
||||
{!props.icon ? '' : (<FAIcon class="tab-icon" size="2x" fixed-width icon={props.icon}/>)}
|
||||
<span class="text">
|
||||
|
|
@ -167,11 +163,15 @@ export default {
|
|||
|
||||
return (
|
||||
<div class={'tab-switcher ' + (this.sideTabBar ? 'side-tabs' : 'top-tabs')}>
|
||||
<div class="tabs">
|
||||
<div
|
||||
class="tabs"
|
||||
role="tablist"
|
||||
>
|
||||
{tabs}
|
||||
</div>
|
||||
<div
|
||||
ref="contents"
|
||||
role="tabpanel"
|
||||
class={'contents' + (this.scrollableTabs ? ' scrollable-tabs' : '')}
|
||||
v-body-scroll-lock={this.bodyScrollLock}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ const withLoadMore = ({
|
|||
</button>
|
||||
}
|
||||
{!this.error && this.loading && <FAIcon spin icon="circle-notch"/>}
|
||||
{!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries}>{this.$t('general.more')}</a>}
|
||||
{!this.error && !this.loading && !this.bottomedOut && <a onClick={this.fetchEntries} role="button" tabindex="0">{this.$t('general.more')}</a>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
380
src/i18n/ar.json
380
src/i18n/ar.json
|
|
@ -9,7 +9,8 @@
|
|||
"scope_options": "",
|
||||
"text_limit": "الحد الأقصى للنص",
|
||||
"title": "الميّزات",
|
||||
"who_to_follow": "للمتابعة"
|
||||
"who_to_follow": "للمتابعة",
|
||||
"upload_limit": "حد الرفع"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "خطأ أثناء جلب صفحة المستخدم",
|
||||
|
|
@ -17,7 +18,35 @@
|
|||
},
|
||||
"general": {
|
||||
"apply": "تطبيق",
|
||||
"submit": "إرسال"
|
||||
"submit": "إرسال",
|
||||
"error_retry": "حاول مجددًا",
|
||||
"retry": "حاول مجدداً",
|
||||
"optional": "اختياري",
|
||||
"show_more": "اعرض المزيد",
|
||||
"show_less": "اعرض أقل",
|
||||
"cancel": "ألغ",
|
||||
"disable": "عطّل",
|
||||
"enable": "فعّل",
|
||||
"confirm": "تأكيد",
|
||||
"close": "أغلق",
|
||||
"role": {
|
||||
"admin": "مدير",
|
||||
"moderator": "مشرف"
|
||||
},
|
||||
"generic_error_message": "حدث خطأ: {0}",
|
||||
"never_show_again": "لا تظهره مجددًا",
|
||||
"yes": "نعم",
|
||||
"no": "لا",
|
||||
"unpin": "ألغ تثبيت العنصر",
|
||||
"undo": "تراجع",
|
||||
"more": "المزيد",
|
||||
"loading": "يحمل…",
|
||||
"generic_error": "حدث خطأ",
|
||||
"scope_in_timeline": {
|
||||
"private": "المتابِعون فقط"
|
||||
},
|
||||
"scroll_to_top": "مرر لأعلى",
|
||||
"pin": "ثبت العنصر"
|
||||
},
|
||||
"login": {
|
||||
"login": "تسجيل الدخول",
|
||||
|
|
@ -25,7 +54,19 @@
|
|||
"password": "الكلمة السرية",
|
||||
"placeholder": "مثال lain",
|
||||
"register": "انشاء حساب",
|
||||
"username": "إسم المستخدم"
|
||||
"username": "إسم المستخدم",
|
||||
"logout_confirm_title": "تأكيد الخروج",
|
||||
"logout_confirm": "أتريد الخروج؟",
|
||||
"logout_confirm_accept_button": "خروج",
|
||||
"logout_confirm_cancel_button": "لا تخرج",
|
||||
"hint": "لِج للانضمام للمناقشة",
|
||||
"authentication_code": "رمز الاستيثاق",
|
||||
"enter_recovery_code": "أدخل رمز التأكيد",
|
||||
"enter_two_factor_code": "أدخل رمز الاستيثاق بعاملين",
|
||||
"recovery_code": "رمز الاستعادة",
|
||||
"heading": {
|
||||
"totp": "الاستيثاق بعاملين"
|
||||
}
|
||||
},
|
||||
"nav": {
|
||||
"chat": "الدردشة المحلية",
|
||||
|
|
@ -33,23 +74,48 @@
|
|||
"mentions": "الإشارات",
|
||||
"public_tl": "الخيط الزمني العام",
|
||||
"timeline": "الخيط الزمني",
|
||||
"twkn": "كافة الشبكة المعروفة"
|
||||
"twkn": "كافة الشبكة المعروفة",
|
||||
"search_close": "أغلق شربط البحث",
|
||||
"back": "للخلف",
|
||||
"administration": "الإدارة",
|
||||
"preferences": "التفضيلات",
|
||||
"chats": "المحادثات",
|
||||
"lists": "القوائم",
|
||||
"edit_nav_mobile": "خصص شريط التنقل",
|
||||
"edit_pinned": "حرر العناصر المثبتة",
|
||||
"mobile_notifications_close": "أغلق الاشعارات",
|
||||
"announcements": "إعلانات",
|
||||
"home_timeline": "الخط الزمني الرئيس",
|
||||
"search": "بحث",
|
||||
"who_to_follow": "للمتابعة",
|
||||
"dms": "رسالة شخصية",
|
||||
"edit_finish": "تم التحرير",
|
||||
"timelines": "الخيوط الزمنية",
|
||||
"mobile_notifications": "افتح الإشعارات (تتواجد اشعارات غير مقروءة)"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "منشور مجهول، جارٍ البحث عنه…",
|
||||
"favorited_you": "أعجِب بمنشورك",
|
||||
"followed_you": "يُتابعك",
|
||||
"load_older": "تحميل الإشعارات الأقدم",
|
||||
"notifications": "الإخطارات",
|
||||
"notifications": "الاشعارات",
|
||||
"read": "مقروء!",
|
||||
"repeated_you": "شارَك منشورك"
|
||||
"repeated_you": "شارَك منشورك",
|
||||
"error": "خطأ أثناء جلب الاشعارات: {0}",
|
||||
"follow_request": "يريد متابعتك",
|
||||
"poll_ended": "انتهى الاستطلاع",
|
||||
"no_more_notifications": "لا مزيد من الإشعارات",
|
||||
"reacted_with": "تفاعل بـ{0}",
|
||||
"submitted_report": "أرسل بلاغًا"
|
||||
},
|
||||
"post_status": {
|
||||
"account_not_locked_warning": "",
|
||||
"account_not_locked_warning_link": "مقفل",
|
||||
"attachments_sensitive": "اعتبر المرفقات كلها كمحتوى حساس",
|
||||
"content_type": {
|
||||
"text/plain": "نص صافٍ"
|
||||
"text/plain": "نص صِرف",
|
||||
"text/html": "HTML",
|
||||
"text/markdown": "ماركداون"
|
||||
},
|
||||
"content_warning": "الموضوع (اختياري)",
|
||||
"default": "وصلت للتوّ إلى لوس أنجلس.",
|
||||
|
|
@ -60,15 +126,47 @@
|
|||
"private": "",
|
||||
"public": "علني - يُنشر على الخيوط الزمنية العمومية",
|
||||
"unlisted": "غير مُدرَج - لا يُنشَر على الخيوط الزمنية العمومية"
|
||||
}
|
||||
},
|
||||
"media_description": "وصف الوسائط",
|
||||
"direct_warning_to_all": "سيكون عذا المنشور مرئيًا لكل المستخدمين المذكورين.",
|
||||
"post": "انشر",
|
||||
"preview": "معاينة",
|
||||
"preview_empty": "فارغ",
|
||||
"scope_notice": {
|
||||
"public": "سيكون هذا المنشور مرئيًا للجميع",
|
||||
"private": "سيكون هذا المنشور مرئيا لمتابِعيك فقط"
|
||||
},
|
||||
"direct_warning_to_first_only": "سيكون عذا المنشور مرئيًا للمستخدمين المذكورين في أول الرسالة.",
|
||||
"edit_unsupported_warning": "بليروما لا يدعم تعديل الذكر والاستطلاع.",
|
||||
"empty_status_error": "يتعذر نشر منشور فارغ دون ملفات"
|
||||
},
|
||||
"registration": {
|
||||
"bio": "السيرة الذاتية",
|
||||
"email": "عنوان البريد الإلكتروني",
|
||||
"fullname": "الإسم المعروض",
|
||||
"fullname": "الاسم العلني",
|
||||
"password_confirm": "تأكيد الكلمة السرية",
|
||||
"registration": "التسجيل",
|
||||
"token": "رمز الدعوة"
|
||||
"token": "رمز الدعوة",
|
||||
"bio_optional": "سيرة (اختيارية)",
|
||||
"email_optional": "بيرد إلكتروني (اختياري)",
|
||||
"username_placeholder": "مثل lain",
|
||||
"reason": "سبب التسجيل",
|
||||
"register": "سجل",
|
||||
"validations": {
|
||||
"username_required": "لايمكن تركه فارغًا",
|
||||
"email_required": "لايمكن تركه فارغًا",
|
||||
"password_required": "لايمكن تركه فارغًا",
|
||||
"password_confirmation_required": "لايمكن تركه فارغًا",
|
||||
"fullname_required": "لايمكن تركه فارغًا",
|
||||
"password_confirmation_match": "يلزم أن يطابق كلمة السر",
|
||||
"birthday_required": "لايمكن تركه فارغًا",
|
||||
"birthday_min_age": "يلزم أن يكون في {date} أو قبله"
|
||||
},
|
||||
"fullname_placeholder": "مثل Lain Iwakura",
|
||||
"reason_placeholder": "قبول التسجيل في هذا المثيل يستلزم موافقة المدير\nلهذا يجب عليك إعلامه بسبب التسجيل.",
|
||||
"birthday_optional": "تاريخ الميلاد (اختياري):",
|
||||
"email_language": "بأي لغة تريد استلام رسائل البريد الإلكتروني؟",
|
||||
"birthday": "تاريخ الميلاد:"
|
||||
},
|
||||
"settings": {
|
||||
"attachmentRadius": "المُرفَقات",
|
||||
|
|
@ -83,9 +181,9 @@
|
|||
"cGreen": "أخضر (إعادة النشر)",
|
||||
"cOrange": "برتقالي (مفضلة)",
|
||||
"cRed": "أحمر (إلغاء)",
|
||||
"change_password": "تغيير كلمة السر",
|
||||
"change_password_error": "وقع هناك خلل أثناء تعديل كلمتك السرية.",
|
||||
"changed_password": "تم تغيير كلمة المرور بنجاح!",
|
||||
"change_password": "غيّر كلمة السر",
|
||||
"change_password_error": "حدث خلل أثناء تعديل كلمتك السرية.",
|
||||
"changed_password": "نجح تغيير كلمة السر!",
|
||||
"collapse_subject": "",
|
||||
"confirm_new_password": "تأكيد كلمة السر الجديدة",
|
||||
"current_avatar": "صورتك الرمزية الحالية",
|
||||
|
|
@ -94,11 +192,11 @@
|
|||
"data_import_export_tab": "تصدير واستيراد البيانات",
|
||||
"default_vis": "أسلوب العرض الافتراضي",
|
||||
"delete_account": "حذف الحساب",
|
||||
"delete_account_description": "حذف حسابك و كافة منشوراتك نهائيًا.",
|
||||
"delete_account_error": "",
|
||||
"delete_account_description": "حذف حسابك و كافة بياناتك نهائيًا.",
|
||||
"delete_account_error": "حدثة مشكلة اثناء حذف حسابك، إذا استمرت تواصل مع مدير المثيل.",
|
||||
"delete_account_instructions": "يُرجى إدخال كلمتك السرية أدناه لتأكيد عملية حذف الحساب.",
|
||||
"export_theme": "حفظ النموذج",
|
||||
"filtering": "التصفية",
|
||||
"filtering": "الترشيح",
|
||||
"filtering_explanation": "سيتم إخفاء كافة المنشورات التي تحتوي على هذه الكلمات، كلمة واحدة في كل سطر",
|
||||
"follow_export": "تصدير الاشتراكات",
|
||||
"follow_export_button": "تصدير الاشتراكات كملف csv",
|
||||
|
|
@ -108,30 +206,30 @@
|
|||
"follows_imported": "",
|
||||
"foreground": "الأمامية",
|
||||
"general": "الإعدادات العامة",
|
||||
"hide_attachments_in_convo": "إخفاء المرفقات على المحادثات",
|
||||
"hide_attachments_in_tl": "إخفاء المرفقات على الخيط الزمني",
|
||||
"hide_post_stats": "",
|
||||
"hide_user_stats": "",
|
||||
"import_followers_from_a_csv_file": "",
|
||||
"hide_attachments_in_convo": "اخف المرفقات من المحادثات",
|
||||
"hide_attachments_in_tl": "اخف المرفقات من الخيط الزمني",
|
||||
"hide_post_stats": "اخف احصائيات المنشور (مثل عدد التفضيلات)",
|
||||
"hide_user_stats": "اخف احصائيات المستخدم (مثل عدد المتابِعين)",
|
||||
"import_followers_from_a_csv_file": "استورد المتابِعين من ملف csv",
|
||||
"import_theme": "تحميل نموذج",
|
||||
"inputRadius": "",
|
||||
"instance_default": "",
|
||||
"instance_default": "(الافتراضي: {value})",
|
||||
"interfaceLanguage": "لغة الواجهة",
|
||||
"invalid_theme_imported": "",
|
||||
"invalid_theme_imported": "الملف المختار ليس سمة تدعمها بليروما.لن تطرأ تغييرات على سمتك.",
|
||||
"limited_availability": "غير متوفر على متصفحك",
|
||||
"links": "الروابط",
|
||||
"lock_account_description": "",
|
||||
"loop_video": "",
|
||||
"loop_video_silent_only": "",
|
||||
"loop_video": "كرر الفيديوهات",
|
||||
"loop_video_silent_only": "كرر فيديوهات بدون صوت (مثل gif في ماستودون)",
|
||||
"name": "الاسم",
|
||||
"name_bio": "الاسم والسيرة الذاتية",
|
||||
"new_password": "كلمة السر الجديدة",
|
||||
"no_rich_text_description": "",
|
||||
"notification_visibility": "نوع الإشعارات التي تريد عرضها",
|
||||
"notification_visibility_follows": "يتابع",
|
||||
"notification_visibility_likes": "الإعجابات",
|
||||
"notification_visibility_mentions": "الإشارات",
|
||||
"notification_visibility_repeats": "",
|
||||
"notification_visibility_likes": "المفضلة",
|
||||
"notification_visibility_mentions": "ذِكر",
|
||||
"notification_visibility_repeats": "مشاركات",
|
||||
"nsfw_clickthrough": "",
|
||||
"oauth_tokens": "رموز OAuth",
|
||||
"token": "رمز",
|
||||
|
|
@ -141,16 +239,16 @@
|
|||
"panelRadius": "",
|
||||
"pause_on_unfocused": "",
|
||||
"presets": "النماذج",
|
||||
"profile_background": "خلفية الصفحة الشخصية",
|
||||
"profile_background": "خلفية الملف التعريفي",
|
||||
"profile_banner": "رأسية الصفحة الشخصية",
|
||||
"profile_tab": "الملف الشخصي",
|
||||
"profile_tab": "الملف التعريفي",
|
||||
"radii_help": "",
|
||||
"replies_in_timeline": "الردود على الخيط الزمني",
|
||||
"reply_visibility_all": "عرض كافة الردود",
|
||||
"replies_in_timeline": "المشاركات في الخيط الزمني",
|
||||
"reply_visibility_all": "أظهر كل المشاركات",
|
||||
"reply_visibility_following": "",
|
||||
"reply_visibility_self": "",
|
||||
"saving_err": "خطأ أثناء حفظ الإعدادات",
|
||||
"saving_ok": "تم حفظ الإعدادات",
|
||||
"saving_ok": "حُفظت الإعدادات",
|
||||
"security_tab": "الأمان",
|
||||
"set_new_avatar": "اختيار صورة رمزية جديدة",
|
||||
"set_new_profile_background": "اختيار خلفية جديدة للملف الشخصي",
|
||||
|
|
@ -166,7 +264,121 @@
|
|||
"values": {
|
||||
"false": "لا",
|
||||
"true": "نعم"
|
||||
}
|
||||
},
|
||||
"emoji_reactions_scale": "معامل تحجيم التفاعلات",
|
||||
"app_name": "اسم تطبيق",
|
||||
"security": "الأمن",
|
||||
"enter_current_password_to_confirm": "أدخل كلمة السر الحالية لتيقن من هويتك",
|
||||
"mfa": {
|
||||
"title": "الاستيثاق بعاملين",
|
||||
"generate_new_recovery_codes": "ولّد رموز استعادة جديدة",
|
||||
"warning_of_generate_new_codes": "عند توليد رموز استعادة جديدة ستزال القديمة.",
|
||||
"recovery_codes": "رموز الاستعادة.",
|
||||
"recovery_codes_warning": "خزن هذه الرموز في مكان آمن. إذا فقدت هذه الرموز وتعذر عليك الوصول إلى تطبيق الاستيثاق بعاملين، لن تتمكن من الوصول لحسابك.",
|
||||
"authentication_methods": "طرق الاستيثاق",
|
||||
"scan": {
|
||||
"title": "مسح",
|
||||
"desc": "امسح رمز الاستجابة السريعة QR من تطبيق الاستيثاق أو أدخل المفتاح:",
|
||||
"secret_code": "مفتاح"
|
||||
},
|
||||
"verify": {
|
||||
"desc": "لتفعيل الاستيثاق بعاملين أدخل الرمز من تطبيق الاستيثاق:"
|
||||
}
|
||||
},
|
||||
"block_import": "استيراد المحجوبين",
|
||||
"import_mutes_from_a_csv_file": "استورد قائمة المكتومين من ملف csv",
|
||||
"account_backup": "نسخ احتياطي للحساب",
|
||||
"download_backup": "نزّل",
|
||||
"account_backup_table_head": "نسخ احتياطي",
|
||||
"backup_not_ready": "هذا النسخ الاحتياطي ليس جاهزًا.",
|
||||
"backup_failed": "فشل النسخ الاحتياطي.",
|
||||
"remove_backup": "أزل",
|
||||
"list_backups_error": "خطأ أثناء حلب قائمة النُسخ الاحتياطية: {error}",
|
||||
"added_backup": "أُضيفت نسخة احتياطية جديدة.",
|
||||
"blocks_tab": "المحجوبون",
|
||||
"confirm_dialogs_block": "حجب مستخدم",
|
||||
"confirm_dialogs_mute": "كتم مستخدم",
|
||||
"confirm_dialogs_delete": "حذف حالة",
|
||||
"confirm_dialogs_logout": "خروج",
|
||||
"confirm_dialogs_approve_follow": "قبول متابِع",
|
||||
"confirm_dialogs_deny_follow": "رفض متابِع",
|
||||
"list_aliases_error": "خطأ أثناء جلب الكنيات: {error}",
|
||||
"hide_list_aliases_error_action": "أغلق",
|
||||
"remove_alias": "أزل هذه الكنية",
|
||||
"add_alias_error": "حدث خطأ أثناء إضافة الكنية: {error}",
|
||||
"confirm_dialogs": "أطلب تأكيدًا عند",
|
||||
"confirm_dialogs_repeat": "مشاركة حالة",
|
||||
"mutes_and_blocks": "المكتومون والمحجوبون",
|
||||
"move_account_target": "الحساب المستهدف (مثل {example})",
|
||||
"wordfilter": "ترشيح الكلمات",
|
||||
"always_show_post_button": "أظهر الزر العائم لإنشاء منشور جديد دائمًا",
|
||||
"hide_wallpaper": "اخف خلفية المثيل",
|
||||
"save": "احفظ التعديلات",
|
||||
"lists_navigation": "أظهر القوائم في شريط التنقل",
|
||||
"mute_export_button": "صدّر قائمة المكتومين إلى ملف csv",
|
||||
"blocks_imported": "اُستورد المحجوبون! معالجة القائمة ستستغرق وقتًا.",
|
||||
"mute_export": "تصدير المكتومين",
|
||||
"mute_import": "استيراد المكتومين",
|
||||
"mute_import_error": "خطأ أثناء استيراد المكتومين",
|
||||
"change_email_error": "حدثت خلل أثناء تغيير بريدك الإلكتروني.",
|
||||
"change_email": "غيّر البريد الإلكتروني",
|
||||
"changed_email": "نجح تغيير البريد الإلكتروني!",
|
||||
"account_alias_table_head": "الكنية",
|
||||
"account_alias": "كنيات الحساب",
|
||||
"move_account": "أنقل الحساب",
|
||||
"moved_account": "نُقل الحساب.",
|
||||
"hide_media_previews": "اخف معاينات الوسائط",
|
||||
"hide_muted_posts": "اخف منشورات المستخدمين المكتومين",
|
||||
"confirm_dialogs_unfollow": "الغاء متابعة مستخدم",
|
||||
"confirm_dialogs_remove_follower": "إزالة متابع",
|
||||
"new_alias_target": "أضف كنية جديدة (مثل {example})",
|
||||
"added_alias": "أُضيفت الكنية.",
|
||||
"move_account_error": "خطأ أثناء نقل الحساب: {error}",
|
||||
"emoji_reactions_on_timeline": "أظهر التفاعلات في الخط الزمني",
|
||||
"mutes_imported": "اُستورد المكتومون! معالجة القئمة ستستغرق وقتًا.",
|
||||
"remove_language": "أزل",
|
||||
"primary_language": "اللغة الرئيسية:",
|
||||
"expert_mode": "أظهر الإعدادات المتقدمة",
|
||||
"block_import_error": "خطأ أثناء استيراد قائمة المحجوبين",
|
||||
"add_backup": "أنشئ نسخة احتياطية جديدة",
|
||||
"add_backup_error": "خطأ أثناء إضافة نسخ احتياطي جديد: {error}",
|
||||
"move_account_notes": "إذا أردت نقل حسابك عليك إضافة كنية تشير إلى هنا في الحساب المستهدف.",
|
||||
"avatar_size_instruction": "أدنى حجم مستحسن للصورة الرمزية هو 150x150 بيكسل.",
|
||||
"word_filter_and_more": "مرشح الكلمات والمزيد...",
|
||||
"hide_all_muted_posts": "اخف المنشورات المكتومة",
|
||||
"max_thumbnails": "أقصى عدد للصور المصغرة لكل منشور (فارغ = غير محدود)",
|
||||
"block_export_button": "صدّر قائمة المحجوبين إلى ملف csv",
|
||||
"block_export": "تصدير المحجوبين",
|
||||
"use_one_click_nsfw": "افتح المرفقات ذات المحتوى الحساس NSFW بنقرة واحدة",
|
||||
"account_privacy": "خصوصية",
|
||||
"use_contain_fit": "لا تقتص الصور المصغرة للمرفقات",
|
||||
"import_blocks_from_a_csv_file": "استورد المحجوبين من ملف csv",
|
||||
"instance_default_simple": "(افتراضي)",
|
||||
"interface": "واجهة",
|
||||
"birthday": {
|
||||
"label": "تاريخ الميلاد",
|
||||
"show_birthday": "اظهر تاريخ ميلادي"
|
||||
},
|
||||
"profile_fields": {
|
||||
"add_field": "أضف حقل",
|
||||
"value": "محتوى"
|
||||
},
|
||||
"posts": "منشورات",
|
||||
"user_profiles": "ملفات تعريفية للمستخدمين",
|
||||
"notification_visibility_emoji_reactions": "تفاعلات",
|
||||
"notification_visibility_polls": "انتهاء استطلاعات اشتركت بها",
|
||||
"file_export_import": {
|
||||
"restore_settings": "استرجع الإعدادات من ملف",
|
||||
"backup_restore": "نسخ احتياطي للإعدادات"
|
||||
},
|
||||
"mutes_tab": "مكتومون",
|
||||
"no_mutes": "لا يوجد مكتومون",
|
||||
"hide_followers_count_description": "لا تظهر عدد المتابِعين",
|
||||
"show_moderator_badge": "أظهر شارة \"مشرف\" في ملفي التعريفي",
|
||||
"hide_follows_count_description": "لا تظهر عدد المتابَعين",
|
||||
"hide_muted_threads": "اخف النقاشات المكتومة",
|
||||
"no_blocks": "لا يوجد محجوبون",
|
||||
"show_admin_badge": "أظهر شارة \"مدير\" في ملفي التعريفي"
|
||||
},
|
||||
"timeline": {
|
||||
"collapse": "",
|
||||
|
|
@ -211,11 +423,109 @@
|
|||
"keyword_policies": "سياسة الكلمات الدلالية"
|
||||
},
|
||||
"simple": {
|
||||
"simple_policies": "سياسات الخادم"
|
||||
"simple_policies": "سياسات الخادم",
|
||||
"instance": "مثيل",
|
||||
"reason": "السبب",
|
||||
"accept": "قبول",
|
||||
"reject": "رفض"
|
||||
},
|
||||
"federation": "الاتحاد",
|
||||
"mrf_policies": "تفعيل سياسات إعادة كتابة المنشور",
|
||||
"mrf_policies_desc": "خاصية إعادة كتابة المناشير تقوم بتعديل تفاعل الاتحاد مع هذا الخادم. السياسات التالية مفعّلة:"
|
||||
}
|
||||
},
|
||||
"announcements": {
|
||||
"page_header": "إعلانات",
|
||||
"title": "إعلان",
|
||||
"mark_as_read_action": "علّمه كمقروء",
|
||||
"post_form_header": "انشر إعلانًا",
|
||||
"post_placeholder": "اكتب محتوى الاعلان هنا...",
|
||||
"post_action": "انشر",
|
||||
"post_error": "خطأ: {error}",
|
||||
"close_error": "أغلاق",
|
||||
"delete_action": "احذف",
|
||||
"start_time_prompt": "وقت البدأ: ",
|
||||
"end_time_prompt": "وقت النهاية: ",
|
||||
"all_day_prompt": "هذا حدث يوم كامل",
|
||||
"start_time_display": "يبدأ في {time}",
|
||||
"end_time_display": "ينتهي في {time}",
|
||||
"edit_action": "حرر",
|
||||
"submit_edit_action": "أرسل",
|
||||
"cancel_edit_action": "ألغِ",
|
||||
"inactive_message": "هذا الاعلان غير نشط",
|
||||
"published_time_display": "نُشر في {time}"
|
||||
},
|
||||
"polls": {
|
||||
"votes": "أصوات",
|
||||
"vote": "صوّت",
|
||||
"type": "نوع الاستطلاع",
|
||||
"single_choice": "خيار واحد",
|
||||
"multiple_choices": "متعدد الخيارات",
|
||||
"expiry": "عمر الاستطلاع",
|
||||
"expires_in": "ينتهي الاستطلاع في {0}",
|
||||
"expired": "انتهى الاستطلاع منذ {0}",
|
||||
"add_poll": "أضف استطلاعًا",
|
||||
"add_option": "أضف خيارًا",
|
||||
"option": "خيار"
|
||||
},
|
||||
"emoji": {
|
||||
"stickers": "ملصقات",
|
||||
"emoji": "إيموجي",
|
||||
"search_emoji": "ابحث عن إيموجي",
|
||||
"unicode_groups": {
|
||||
"animals-and-nature": "حيوانات وطبيعة",
|
||||
"food-and-drink": "أطعمة ومشروبات",
|
||||
"symbols": "رموز",
|
||||
"activities": "نشاطات",
|
||||
"flags": "أعلام"
|
||||
},
|
||||
"add_emoji": "أدخل إيموجي",
|
||||
"custom": "إيموجي مخصص"
|
||||
},
|
||||
"interactions": {
|
||||
"emoji_reactions": "تفاعلات بالإيموجي",
|
||||
"reports": "البلاغات",
|
||||
"follows": "المتابعات الجديدة"
|
||||
},
|
||||
"report": {
|
||||
"state_closed": "مغلق",
|
||||
"state_resolved": "عولج",
|
||||
"reported_statuses": "الحالة المبلغة عنها:",
|
||||
"state_open": "مفتوح",
|
||||
"notes": "ملاحظة:",
|
||||
"state": "الحالة:",
|
||||
"reporter": "المبلِّغ:",
|
||||
"reported_user": "المُبلغ عنه:"
|
||||
},
|
||||
"selectable_list": {
|
||||
"select_all": "اختر الكل"
|
||||
},
|
||||
"image_cropper": {
|
||||
"save": "احفظ",
|
||||
"cancel": "ألغ"
|
||||
},
|
||||
"importer": {
|
||||
"submit": "أرسل",
|
||||
"success": "نجح الاستيراد.",
|
||||
"error": "حدث خطأ أثناء الاستيراد."
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"mute": "اكتم",
|
||||
"mute_progress": "يكتم…",
|
||||
"unmute": "ارفع الكتم",
|
||||
"unmute_progress": "يرفع الكتم…"
|
||||
},
|
||||
"exporter": {
|
||||
"export": "صدر",
|
||||
"processing": "يُعالج. سيُطلب منك تنزيل الملف قريباً"
|
||||
},
|
||||
"media_modal": {
|
||||
"previous": "السابق",
|
||||
"next": "التالي",
|
||||
"hide": "أغلق عارض الوسائط"
|
||||
},
|
||||
"remote_user_resolver": {
|
||||
"searching_for": "يبحث عن",
|
||||
"error": "لم يُعثر عليه."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
106
src/i18n/en.json
106
src/i18n/en.json
|
|
@ -176,6 +176,7 @@
|
|||
"bookmarks": "Bookmarks",
|
||||
"user_search": "User Search",
|
||||
"search": "Search",
|
||||
"search_close": "Close search bar",
|
||||
"who_to_follow": "Who to follow",
|
||||
"preferences": "Preferences",
|
||||
"timelines": "Timelines",
|
||||
|
|
@ -270,6 +271,7 @@
|
|||
"text/markdown": "Markdown",
|
||||
"text/bbcode": "BBCode"
|
||||
},
|
||||
"content_type_selection": "Post format",
|
||||
"content_warning": "Subject (optional)",
|
||||
"default": "Just landed in L.A.",
|
||||
"direct_warning_to_all": "This post will be visible to all the mentioned users.",
|
||||
|
|
@ -287,6 +289,7 @@
|
|||
"private": "This post will be visible to your followers only",
|
||||
"unlisted": "This post will not be visible in Public Timeline and The Whole Known Network"
|
||||
},
|
||||
"scope_notice_dismiss": "Close this notice",
|
||||
"scope": {
|
||||
"direct": "Direct - post to mentioned users only",
|
||||
"private": "Followers-only - post to followers only",
|
||||
|
|
@ -462,7 +465,9 @@
|
|||
"domain_mutes": "Domains",
|
||||
"avatar_size_instruction": "The recommended minimum size for avatar images is 150x150 pixels.",
|
||||
"pad_emoji": "Pad emoji with spaces when adding from picker",
|
||||
"autocomplete_select_first": "Automatically select the first candidate when autocomplete results are available",
|
||||
"emoji_reactions_on_timeline": "Show emoji reactions on timeline",
|
||||
"emoji_reactions_scale": "Reactions scale factor",
|
||||
"export_theme": "Save preset",
|
||||
"filtering": "Filtering",
|
||||
"wordfilter": "Wordfilter",
|
||||
|
|
@ -514,6 +519,8 @@
|
|||
"loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")",
|
||||
"mutes_tab": "Mutes",
|
||||
"play_videos_in_modal": "Play videos in a popup frame",
|
||||
"url": "URL",
|
||||
"preview": "Preview",
|
||||
"file_export_import": {
|
||||
"backup_restore": "Settings backup",
|
||||
"backup_settings": "Backup settings to file",
|
||||
|
|
@ -825,6 +832,98 @@
|
|||
"title": "Version",
|
||||
"backend_version": "Backend version",
|
||||
"frontend_version": "Frontend version"
|
||||
},
|
||||
"commit_value": "Save",
|
||||
"commit_value_tooltip": "Value is not saved, press this button to commit your changes",
|
||||
"reset_value": "Reset",
|
||||
"reset_value_tooltip": "Reset draft",
|
||||
"hard_reset_value": "Hard reset",
|
||||
"hard_reset_value_tooltip": "Remove setting from storage, forcing use of default value"
|
||||
},
|
||||
"admin_dash": {
|
||||
"window_title": "Administration",
|
||||
"wip_notice": "This admin dashboard is experimental and WIP, {adminFeLink}.",
|
||||
"old_ui_link": "old admin UI available here",
|
||||
"reset_all": "Reset all",
|
||||
"commit_all": "Save all",
|
||||
"tabs": {
|
||||
"nodb": "No DB Config",
|
||||
"instance": "Instance",
|
||||
"limits": "Limits",
|
||||
"frontends": "Front-ends"
|
||||
},
|
||||
"nodb": {
|
||||
"heading": "Database config is disabled",
|
||||
"text": "You need to change backend config files so that {property} is set to {value}, see more in {documentation}.",
|
||||
"documentation": "documentation",
|
||||
"text2": "Most configuration options will be unavailable."
|
||||
},
|
||||
"captcha": {
|
||||
"native": "Native",
|
||||
"kocaptcha": "KoCaptcha"
|
||||
},
|
||||
"instance": {
|
||||
"instance": "Instance information",
|
||||
"registrations": "User sign-ups",
|
||||
"captcha_header": "CAPTCHA",
|
||||
"kocaptcha": "KoCaptcha settings",
|
||||
"access": "Instance access",
|
||||
"restrict": {
|
||||
"header": "Restrict access for anonymous visitors",
|
||||
"description": "Detailed setting for allowing/disallowing access to certain aspects of API. By default (indeterminate state) it will disallow if instance is not public, ticked checkbox means disallow access even if instance is public, unticked means allow access even if instance is private. Please note that unexpected behavior might happen if some settings are set, i.e. if profile access is disabled posts will show without profile information.",
|
||||
"timelines": "Timelines access",
|
||||
"profiles": "User profiles access",
|
||||
"activities": "Statues/activities access"
|
||||
}
|
||||
},
|
||||
"limits": {
|
||||
"arbitrary_limits": "Arbitrary limits",
|
||||
"posts": "Post limits",
|
||||
"uploads": "Attachments limits",
|
||||
"users": "User profile limits",
|
||||
"profile_fields": "Profile fields limits",
|
||||
"user_uploads": "Profile media limits"
|
||||
},
|
||||
"frontend": {
|
||||
"repository": "Repository link",
|
||||
"versions": "Available versions",
|
||||
"build_url": "Build URL",
|
||||
"reinstall": "Reinstall",
|
||||
"is_default": "(Default)",
|
||||
"is_default_custom": "(Default, version: {version})",
|
||||
"install": "Install",
|
||||
"install_version": "Install version {version}",
|
||||
"more_install_options": "More install options",
|
||||
"more_default_options": "More default setting options",
|
||||
"set_default": "Set default",
|
||||
"set_default_version": "Set version {version} as default",
|
||||
"wip_notice": "Please note that this section is a WIP and lacks certain features as backend implementation of front-end management is incomplete.",
|
||||
"default_frontend": "Default front-end",
|
||||
"default_frontend_tip": "Default front-end will be shown to all users. Currently there's no way to for a user to select personal front-end. If you switch away from PleromaFE you'll most likely have to use old and buggy AdminFE to do instance configuration until we replace it.",
|
||||
"default_frontend_tip2": "WIP: Since Pleroma backend doesn't properly list all installed frontends you'll have to enter name and reference manually. List below provides shortcuts to fill the values.",
|
||||
"available_frontends": "Available for install"
|
||||
},
|
||||
"temp_overrides": {
|
||||
":pleroma": {
|
||||
":instance": {
|
||||
":public": {
|
||||
"label": "Instance is public",
|
||||
"description": "Disabling this will make all API accessible only for logged-in users, this will make Public and Federated timelines inaccessible to anonymous visitors."
|
||||
},
|
||||
":limit_to_local_content": {
|
||||
"label": "Limit search to local content",
|
||||
"description": "Disables global network search for unauthenticated (default), all users or none"
|
||||
},
|
||||
":description_limit": {
|
||||
"label": "Limit",
|
||||
"description": "Character limit for attachment descriptions"
|
||||
},
|
||||
":background_image": {
|
||||
"label": "Background image",
|
||||
"description": "Background image (primarily used by PleromaFE)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"time": {
|
||||
|
|
@ -874,6 +973,7 @@
|
|||
"repeat_confirm_accept_button": "Repeat",
|
||||
"repeat_confirm_cancel_button": "Do not repeat",
|
||||
"delete": "Delete status",
|
||||
"delete_error": "Error deleting status: {0}",
|
||||
"edit": "Edit status",
|
||||
"edited_at": "(last edited {time})",
|
||||
"pin": "Pin on profile",
|
||||
|
|
@ -927,7 +1027,8 @@
|
|||
"show_all_conversation_with_icon": "{icon} {text}",
|
||||
"show_all_conversation": "Show full conversation ({numStatus} other status) | Show full conversation ({numStatus} other statuses)",
|
||||
"show_only_conversation_under_this": "Only show replies to this status",
|
||||
"status_history": "Status history"
|
||||
"status_history": "Status history",
|
||||
"reaction_count_label": "{num} person reacted | {num} people reacted"
|
||||
},
|
||||
"user_card": {
|
||||
"approve": "Approve",
|
||||
|
|
@ -1055,7 +1156,8 @@
|
|||
"reject_follow_request": "Reject follow request",
|
||||
"bookmark": "Bookmark",
|
||||
"toggle_expand": "Expand or collapse notification to show post in full",
|
||||
"toggle_mute": "Expand or collapse notification to reveal muted content"
|
||||
"toggle_mute": "Expand or collapse notification to reveal muted content",
|
||||
"autocomplete_available": "{number} result is available. Use up and down keys to navigate through them. | {number} results are available. Use up and down keys to navigate through them."
|
||||
},
|
||||
"upload": {
|
||||
"error": {
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@
|
|||
"undo": "Malfari",
|
||||
"yes": "Jes",
|
||||
"no": "Ne",
|
||||
"unpin": "Malfiksi eron",
|
||||
"pin": "Fiksi eron",
|
||||
"unpin": "Malfiksi",
|
||||
"pin": "Fiksi",
|
||||
"scroll_to_top": "Rulumi supren"
|
||||
},
|
||||
"image_cropper": {
|
||||
|
|
@ -81,7 +81,11 @@
|
|||
"recovery_code": "Rehava kodo",
|
||||
"enter_two_factor_code": "Enigu kodon de duobla aŭtentikigo",
|
||||
"enter_recovery_code": "Enigu rehavan kodon",
|
||||
"authentication_code": "Aŭtentikiga kodo"
|
||||
"authentication_code": "Aŭtentikiga kodo",
|
||||
"logout_confirm_title": "Konfirmo de adiaŭo",
|
||||
"logout_confirm": "Ĉu vi certe volas adiaŭi?",
|
||||
"logout_confirm_accept_button": "Adiaŭi",
|
||||
"logout_confirm_cancel_button": "Ne adiaŭi"
|
||||
},
|
||||
"media_modal": {
|
||||
"previous": "Antaŭa",
|
||||
|
|
@ -90,7 +94,7 @@
|
|||
"hide": "Fermi vidilon de vidaŭdaĵoj"
|
||||
},
|
||||
"nav": {
|
||||
"about": "Pri",
|
||||
"about": "Prio",
|
||||
"back": "Reen",
|
||||
"chat": "Loka babilejo",
|
||||
"friend_requests": "Petoj pri abono",
|
||||
|
|
@ -115,7 +119,9 @@
|
|||
"edit_finish": "Fini redakton",
|
||||
"mobile_notifications": "Malfermi sciigojn (estas nelegitaj)",
|
||||
"mobile_notifications_close": "Fermi sciigojn",
|
||||
"announcements": "Anoncoj"
|
||||
"announcements": "Anoncoj",
|
||||
"search_close": "Fermi serĉujon",
|
||||
"mobile_sidebar": "(Mal)ŝalti flankan breton por telefonoj"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "Nekonata afiŝo, serĉante ĝin…",
|
||||
|
|
@ -169,7 +175,9 @@
|
|||
"post": "Afiŝo",
|
||||
"edit_remote_warning": "Aliaj foraj nodoj eble ne subtenas redaktadon, kaj ne povos ricevi pli novan version de via afiŝo.",
|
||||
"edit_unsupported_warning": "Pleroma ne subtenas redaktadon de mencioj aŭ enketoj.",
|
||||
"edit_status": "Redakti afiŝon"
|
||||
"edit_status": "Redakti afiŝon",
|
||||
"content_type_selection": "Formo de afiŝo",
|
||||
"scope_notice_dismiss": "Fermi ĉi tiun avizon"
|
||||
},
|
||||
"registration": {
|
||||
"bio": "Priskribo",
|
||||
|
|
@ -189,14 +197,18 @@
|
|||
"email_required": "ne povas resti malplena",
|
||||
"password_required": "ne povas resti malplena",
|
||||
"password_confirmation_required": "ne povas resti malplena",
|
||||
"password_confirmation_match": "samu la pasvorton"
|
||||
"password_confirmation_match": "samu la pasvorton",
|
||||
"birthday_min_age": "ne povas esti post {date}",
|
||||
"birthday_required": "ne povas resti malplena"
|
||||
},
|
||||
"reason_placeholder": "Ĉi-node oni aprobas registriĝojn permane.\nSciigu la administrantojn kial vi volas registriĝi.",
|
||||
"reason": "Kialo registriĝi",
|
||||
"register": "Registriĝi",
|
||||
"bio_optional": "Prio (malnepra)",
|
||||
"email_optional": "Retpoŝtadreso (malnepra)",
|
||||
"email_language": "En kiu lingvo vi volus ricevi retleterojn de la servilo?"
|
||||
"email_language": "En kiu lingvo vi volus ricevi retleterojn de la servilo?",
|
||||
"birthday": "Naskiĝtago:",
|
||||
"birthday_optional": "Naskiĝtago (malnepra):"
|
||||
},
|
||||
"settings": {
|
||||
"app_name": "Nomo de aplikaĵo",
|
||||
|
|
@ -666,7 +678,28 @@
|
|||
"user_popover_avatar_overlay": "Aperigi ŝprucaĵon pri uzanto sur profilbildo",
|
||||
"show_yous": "Montri la markon «(Vi)»",
|
||||
"user_popover_avatar_action_zoom": "Zomi la profilbildon",
|
||||
"third_column_mode": "Kun sufiĉo da spaco, montri trian kolumnon kun"
|
||||
"third_column_mode": "Kun sufiĉo da spaco, montri trian kolumnon kun",
|
||||
"birthday": {
|
||||
"show_birthday": "Montri mian naskiĝtagon",
|
||||
"label": "Naskiĝtago"
|
||||
},
|
||||
"confirm_dialogs_delete": "forigo de afiŝo",
|
||||
"backup_running": "Ĉi tiu savkopiado progresas, traktis {number} datumon. | Ĉi tiu savkopiado progresas, traktis {number} datumojn.",
|
||||
"backup_failed": "Ĉi tiu savkopiado malsukcesis.",
|
||||
"autocomplete_select_first": "Memage elekti unuan kandidaton kiam rezultoj de memaga konjektado disponeblas",
|
||||
"confirm_dialogs_logout": "adiaŭo",
|
||||
"user_popover_avatar_action": "Post klako sur profilbildon en ŝprucaĵo",
|
||||
"remove_language": "Forigi",
|
||||
"primary_language": "Ĉefa lingvo:",
|
||||
"confirm_dialogs": "Peti konfirmon je",
|
||||
"confirm_dialogs_repeat": "ripeto de afiŝo",
|
||||
"confirm_dialogs_unfollow": "malabono de uzanto",
|
||||
"confirm_dialogs_block": "blokado de uzanto",
|
||||
"confirm_dialogs_mute": "silentigo de uzanto",
|
||||
"confirm_dialogs_approve_follow": "aprobo de abonanto",
|
||||
"confirm_dialogs_deny_follow": "malaprobo de abonanto",
|
||||
"confirm_dialogs_remove_follower": "forigo de abonanto",
|
||||
"tree_fade_ancestors": "Montri responditojn de la nuna afiŝo per teksto malvigla"
|
||||
},
|
||||
"timeline": {
|
||||
"collapse": "Maletendi",
|
||||
|
|
@ -753,7 +786,33 @@
|
|||
"note_blank": "(Neniu)",
|
||||
"edit_note_apply": "Apliki",
|
||||
"edit_note_cancel": "Nuligi",
|
||||
"edit_note": "Redakti noton"
|
||||
"edit_note": "Redakti noton",
|
||||
"block_confirm": "Ĉu vi certe volas bloki uzanton {user}?",
|
||||
"block_confirm_accept_button": "Bloki",
|
||||
"remove_follower_confirm": "Ĉu vi certe volas forigi uzanton {user} de viaj abonantoj?",
|
||||
"approve_confirm_accept_button": "Aprobi",
|
||||
"approve_confirm_cancel_button": "Ne aprobi",
|
||||
"approve_confirm": "Ĉu vi certe volas aprobi abonan peton de {user}?",
|
||||
"block_confirm_title": "Konfirmo de blokado",
|
||||
"approve_confirm_title": "Konfirmo de aprobo",
|
||||
"block_confirm_cancel_button": "Ne bloki",
|
||||
"deny_confirm_accept_button": "Malaprobi",
|
||||
"deny_confirm_cancel_button": "Ne malaprobi",
|
||||
"mute_confirm_title": "Silentigi konfirmon",
|
||||
"deny_confirm_title": "Konfirmo de malaprobo",
|
||||
"mute_confirm": "Ĉu vi certe volas silentigi uzanton {user}?",
|
||||
"mute_confirm_accept_button": "Silentigi",
|
||||
"mute_confirm_cancel_button": "Ne silentigi",
|
||||
"mute_duration_prompt": "Silentigi ĉi tiun uzanton por (0 signifas senliman silentigon):",
|
||||
"remove_follower_confirm_accept_button": "Forigi",
|
||||
"remove_follower_confirm_title": "Konfirmo de forigo de abonanto",
|
||||
"birthday": "Naskita je {birthday}",
|
||||
"deny_confirm": "Ĉu vi certe volas malaprobi abonan peton de {user}?",
|
||||
"unfollow_confirm_cancel_button": "Ne malaboni",
|
||||
"unfollow_confirm_title": "Konfirmo de malabono",
|
||||
"unfollow_confirm": "Ĉu vi certe volas malaboni uzanton {user}?",
|
||||
"unfollow_confirm_accept_button": "Malaboni",
|
||||
"remove_follower_confirm_cancel_button": "Ne forigi"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "Historio de uzanto",
|
||||
|
|
@ -775,7 +834,8 @@
|
|||
"accept_follow_request": "Akcepti abonpeton",
|
||||
"add_reaction": "Aldoni reagon",
|
||||
"toggle_expand": "Etendi aŭ maletendi sciigon por montri plenan afiŝon",
|
||||
"toggle_mute": "Etendi aŭ maletendi afiŝon por montri silentigitan enhavon"
|
||||
"toggle_mute": "Etendi aŭ maletendi afiŝon por montri silentigitan enhavon",
|
||||
"autocomplete_available": "{number} rezulto disponeblas. Uzu la sagajn klavojn supren kaj suben por foliumi ilin. | {number} rezulto disponeblas. Uzu la sagajn klavojn supren kaj suben por foliumi ilin."
|
||||
},
|
||||
"upload": {
|
||||
"error": {
|
||||
|
|
@ -951,7 +1011,14 @@
|
|||
"show_all_conversation_with_icon": "{icon} {text}",
|
||||
"show_only_conversation_under_this": "Montri nur respondojn al ĉi tiu afiŝo",
|
||||
"status_history": "Historio de afiŝo",
|
||||
"open_gallery": "Malfermi galerion"
|
||||
"open_gallery": "Malfermi galerion",
|
||||
"delete_confirm_title": "Konfirmo de forigo",
|
||||
"delete_confirm_accept_button": "Forigi",
|
||||
"repeat_confirm": "Ĉu vi certe volas ripeti ĉi tiun afiŝon?",
|
||||
"repeat_confirm_title": "Konfirmo de ripeto",
|
||||
"repeat_confirm_accept_button": "Ripeti",
|
||||
"repeat_confirm_cancel_button": "Ne ripeti",
|
||||
"delete_confirm_cancel_button": "Ne forigi"
|
||||
},
|
||||
"time": {
|
||||
"years_short": "{0}j",
|
||||
|
|
@ -1119,6 +1186,7 @@
|
|||
"edit_action": "Redakti",
|
||||
"submit_edit_action": "Afiŝi",
|
||||
"cancel_edit_action": "Nuligi",
|
||||
"inactive_message": "Ĉi tiu anonco estas neaktiva"
|
||||
"inactive_message": "Ĉi tiu anonco estas neaktiva",
|
||||
"post_form_header": "Afiŝi anoncon"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,17 @@
|
|||
"media_removal": "メディアをのぞく",
|
||||
"media_removal_desc": "このインスタンスは、これらのインスタンスからおくられてきたメディアを、とりのぞきます:",
|
||||
"media_nsfw": "メディアをすべてセンシティブにする",
|
||||
"media_nsfw_desc": "このインスタンスは、これらのインスタンスからおくられてきたメディアを、すべて、センシティブにマークします:"
|
||||
"media_nsfw_desc": "このインスタンスは、これらのインスタンスからおくられてきたメディアを、すべて、センシティブにマークします:",
|
||||
"reason": "りゆう",
|
||||
"instance": "インスタンス",
|
||||
"not_applicable": "なし"
|
||||
},
|
||||
"keyword": {
|
||||
"keyword_policies": "キーワードポリシー",
|
||||
"reject": "おことわり",
|
||||
"replace": "おきかえ",
|
||||
"ftl_removal": "「つながっているすべてのネットワーク」タイムラインからのぞく",
|
||||
"is_replaced_by": "→"
|
||||
}
|
||||
},
|
||||
"staff": "スタッフ"
|
||||
|
|
@ -36,7 +46,10 @@
|
|||
"scope_options": "こうかいはんいせんたく",
|
||||
"text_limit": "もじのかず",
|
||||
"title": "ゆうこうなきのう",
|
||||
"who_to_follow": "おすすめユーザー"
|
||||
"who_to_follow": "おすすめユーザー",
|
||||
"pleroma_chat_messages": "Pleroma チャット",
|
||||
"upload_limit": "アップロードできるファイルのおおきさ",
|
||||
"shout": "Shoutbox"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "ユーザーけんさくがエラーになりました",
|
||||
|
|
@ -54,7 +67,34 @@
|
|||
"disable": "なし",
|
||||
"enable": "あり",
|
||||
"confirm": "たしかめる",
|
||||
"verify": "たしかめる"
|
||||
"verify": "たしかめる",
|
||||
"retry": "もういちど、ためしてください",
|
||||
"loading": "よみこんでいます…",
|
||||
"undo": "もとにもどす",
|
||||
"yes": "はい",
|
||||
"no": "いいえ",
|
||||
"unpin": "ピンどめするのをやめる",
|
||||
"scroll_to_top": "いちばんうえにもどる",
|
||||
"role": {
|
||||
"moderator": "モデレーター",
|
||||
"admin": "かんりするひと"
|
||||
},
|
||||
"flash_security": "Flash コンテンツはどんなコードでもじっこうできるので、あぶないかもしれません。",
|
||||
"flash_fail": "Flash コンテンツをよみこむことに、しっぱいしました。コンソールで、くわしいないようを、よむことができます。",
|
||||
"scope_in_timeline": {
|
||||
"private": "フォロワーげんてい",
|
||||
"public": "パブリック",
|
||||
"unlisted": "アンリステッド",
|
||||
"direct": "ダイレクト"
|
||||
},
|
||||
"pin": "ピンどめする",
|
||||
"flash_content": "Flash コンテンツを、 Ruffle をつかってひょうじする (うごかないかもしれません)。",
|
||||
"generic_error_message": "エラーになりました: {0}",
|
||||
"error_retry": "もういちど、ためしてください",
|
||||
"never_show_again": "にどとひょうじしない",
|
||||
"close": "とじる",
|
||||
"dismiss": "むしする",
|
||||
"peek": "かくす"
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop_picture": "がぞうをきりぬく",
|
||||
|
|
@ -83,11 +123,17 @@
|
|||
"heading": {
|
||||
"totp": "2-ファクターにんしょう",
|
||||
"recovery": "2-ファクターリカバリー"
|
||||
}
|
||||
},
|
||||
"logout_confirm_title": "ログアウトのかくにん",
|
||||
"logout_confirm": "ほんとうに、ログアウトしますか?",
|
||||
"logout_confirm_accept_button": "ログアウトする",
|
||||
"logout_confirm_cancel_button": "ログアウトしない"
|
||||
},
|
||||
"media_modal": {
|
||||
"previous": "まえ",
|
||||
"next": "つぎ"
|
||||
"next": "つぎ",
|
||||
"counter": "{current} / {total}",
|
||||
"hide": "メディアビューアーをとじる"
|
||||
},
|
||||
"nav": {
|
||||
"about": "これはなに?",
|
||||
|
|
@ -104,7 +150,20 @@
|
|||
"user_search": "ユーザーをさがす",
|
||||
"search": "さがす",
|
||||
"who_to_follow": "おすすめユーザー",
|
||||
"preferences": "せってい"
|
||||
"preferences": "せってい",
|
||||
"home_timeline": "ホームタイムライン",
|
||||
"bookmarks": "ブックマーク",
|
||||
"timelines": "タイムライン",
|
||||
"chats": "チャット",
|
||||
"lists": "リスト",
|
||||
"mobile_notifications": "つうちをひらく (よんでないものがあります)",
|
||||
"mobile_notifications_close": "つうちをとじる",
|
||||
"announcements": "おしらせ",
|
||||
"edit_pinned": "ピンどめをへんしゅう",
|
||||
"search_close": "けんさくバーをとじる",
|
||||
"edit_nav_mobile": "ナビゲーションバーのせっていをかえる",
|
||||
"mobile_sidebar": "モバイルのサイドバーをきりかえる",
|
||||
"edit_finish": "へんしゅうをおわりにする"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "ステータスがみつかりません。さがしています…",
|
||||
|
|
@ -114,21 +173,29 @@
|
|||
"notifications": "つうち",
|
||||
"read": "よんだ!",
|
||||
"repeated_you": "あなたのステータスがリピートされました",
|
||||
"no_more_notifications": "つうちはありません"
|
||||
"no_more_notifications": "つうちはありません",
|
||||
"error": "つうちをとりにいくことに、しっぱいしました: {0}",
|
||||
"follow_request": "あなたをフォローしたいです",
|
||||
"migrated_to": "インスタンスを、ひっこしました",
|
||||
"reacted_with": "{0} でリアクションしました",
|
||||
"poll_ended": "とうひょうが、おわりました",
|
||||
"submitted_report": "つうほうしました"
|
||||
},
|
||||
"polls": {
|
||||
"add_poll": "いれふだをはじめる",
|
||||
"add_poll": "とうひょうをはじめる",
|
||||
"add_option": "オプションをふやす",
|
||||
"option": "オプション",
|
||||
"votes": "いれふだ",
|
||||
"vote": "ふだをいれる",
|
||||
"type": "いれふだのかた",
|
||||
"votes": "ひょう",
|
||||
"vote": "とうひょうする",
|
||||
"type": "とうひょうのけいしき",
|
||||
"single_choice": "ひとつえらぶ",
|
||||
"multiple_choices": "いくつでもえらべる",
|
||||
"expiry": "いれふだのながさ",
|
||||
"expires_in": "いれふだは {0} で、おわります",
|
||||
"expired": "いれふだは {0} まえに、おわりました",
|
||||
"not_enough_options": "ユニークなオプションが、たりません"
|
||||
"expiry": "とうひょうのながさ",
|
||||
"expires_in": "とうひょうは {0} で、おわります",
|
||||
"expired": "とうひょうは {0} まえに、おわりました",
|
||||
"not_enough_options": "ユニークなオプションが、たりません",
|
||||
"people_voted_count": "{count} にんが、とうひょうしました",
|
||||
"votes_count": "{count} ひょう"
|
||||
},
|
||||
"emoji": {
|
||||
"stickers": "ステッカー",
|
||||
|
|
@ -139,7 +206,19 @@
|
|||
"custom": "カスタムえもじ",
|
||||
"unicode": "ユニコードえもじ",
|
||||
"load_all_hint": "はじめの {saneAmount} このえもじだけがロードされています。すべてのえもじをロードすると、パフォーマンスがわるくなるかもしれません。",
|
||||
"load_all": "すべてのえもじをロード ({emojiAmount} こあります)"
|
||||
"load_all": "すべてのえもじをロード ({emojiAmount} こあります)",
|
||||
"unicode_groups": {
|
||||
"flags": "はた",
|
||||
"activities": "かつどう",
|
||||
"animals-and-nature": "どうぶつ・しぜん",
|
||||
"food-and-drink": "たべもの・のみもの",
|
||||
"objects": "もの",
|
||||
"people-and-body": "ひと・からだ",
|
||||
"smileys-and-emotion": "えがお・きもち",
|
||||
"symbols": "きごう",
|
||||
"travel-and-places": "りょこう・ばしょ"
|
||||
},
|
||||
"regional_indicator": "ばしょをしめすきごう {letter}"
|
||||
},
|
||||
"stickers": {
|
||||
"add_sticker": "ステッカーをふやす"
|
||||
|
|
@ -147,7 +226,10 @@
|
|||
"interactions": {
|
||||
"favs_repeats": "リピートとおきにいり",
|
||||
"follows": "あたらしいフォロー",
|
||||
"load_older": "ふるいやりとりをみる"
|
||||
"load_older": "ふるいやりとりをみる",
|
||||
"emoji_reactions": "えもじリアクション",
|
||||
"moves": "ユーザーのひっこし",
|
||||
"reports": "つうほう"
|
||||
},
|
||||
"post_status": {
|
||||
"new_status": "とうこうする",
|
||||
|
|
@ -176,7 +258,18 @@
|
|||
"private": "フォロワーげんてい: フォロワーのみにとどきます",
|
||||
"public": "パブリック: パブリックタイムラインにとどきます",
|
||||
"unlisted": "アンリステッド: パブリックタイムラインにとどきません"
|
||||
}
|
||||
},
|
||||
"media_description_error": "メディアのアップロードにしっぱいしました。もういちどためしてください",
|
||||
"edit_status": "ステータスをへんしゅうする",
|
||||
"media_description": "メディアのせつめい",
|
||||
"content_type_selection": "とうこうのけいしき",
|
||||
"edit_remote_warning": "ほかのリモートインスタンスは、へんしゅうをサポートしていないかもしれません。そして、へんしゅうされたとうこうをうけとることができないかもしれません。",
|
||||
"post": "とうこう",
|
||||
"edit_unsupported_warning": "Pleroma は、メンションやとうひょうのへんしゅうを、サポートしていません。",
|
||||
"preview": "プレビュー",
|
||||
"preview_empty": "なにもありません",
|
||||
"empty_status_error": "とうこうないようを、にゅうりょくしてください",
|
||||
"scope_notice_dismiss": "このつうちをとじる"
|
||||
},
|
||||
"registration": {
|
||||
"bio": "プロフィール",
|
||||
|
|
@ -196,8 +289,18 @@
|
|||
"email_required": "なにかかいてください",
|
||||
"password_required": "なにかかいてください",
|
||||
"password_confirmation_required": "なにかかいてください",
|
||||
"password_confirmation_match": "パスワードがちがいます"
|
||||
}
|
||||
"password_confirmation_match": "パスワードがちがいます",
|
||||
"birthday_required": "なにかかいてください",
|
||||
"birthday_min_age": "{date} か、それよりまえにしてください"
|
||||
},
|
||||
"reason_placeholder": "このインスタンスでは、ひとがかくにんして、とうろくをうけいれています。\nなぜあなたがとうろくしたいのかを、かんりしているひとに、おしえてください。",
|
||||
"bio_optional": "プロフィール (かかなくてもよい)",
|
||||
"reason": "とうろくするりゆう",
|
||||
"email_optional": "Eメール (かかなくてもよい)",
|
||||
"register": "とうろくする",
|
||||
"email_language": "サーバーからのメールは、なにご(どのことば)がいいですか?",
|
||||
"birthday": "たんじょうび:",
|
||||
"birthday_optional": "たんじょうび (かかなくてもよい):"
|
||||
},
|
||||
"remote_user_resolver": {
|
||||
"remote_user_resolver": "リモートユーザーリゾルバー",
|
||||
|
|
@ -393,7 +496,24 @@
|
|||
"save_load_hint": "「のこす」オプションをONにすると、テーマをえらんだときとロードしたとき、いまのせっていをのこします。また、テーマをエクスポートするとき、これらのオプションをストアします。すべてのチェックボックスをOFFにすると、テーマをエクスポートしたとき、すべてのせっていをセーブします。",
|
||||
"reset": "リセット",
|
||||
"clear_all": "すべてクリア",
|
||||
"clear_opacity": "とうめいどをクリア"
|
||||
"clear_opacity": "とうめいどをクリア",
|
||||
"help": {
|
||||
"older_version_imported": "ふるいバージョンのフロントエンドでつくられたファイルをインポートしました。",
|
||||
"snapshot_missing": "ファイルにはテーマのスナップショットがありません。おもっていたみためと、ちがうかもしれません。",
|
||||
"migration_snapshot_ok": "あんぜんのため、テーマのスナップショットがよみこまれました。テーマのデータをよみこむことができます。",
|
||||
"snapshot_source_mismatch": "バージョンがただしくないです。フロントエンドのバージョンをもとにもどしたあと、あたらしくしたことが、りゆうかもしれません。ふるいフロントエンドでテーマをへんこうしていたばあい、ふるいバージョンをつかうのがいいです。そうでないばあい、あたらしいバージョンをつかってください。",
|
||||
"snapshot_present": "テーマのスナップショットをよみこみました。せっていはうわがきされました。かわりに、テーマのじっさいのデータをよみこむことができます。",
|
||||
"fe_upgraded": "フロントエンドといっしょに、テーマエンジンもあたらしくなりました。",
|
||||
"fe_downgraded": "フロントエンドが、まえのバージョンにもどりました。",
|
||||
"migration_napshot_gone": "スナップショットがありません。おぼえているみためと、ちがうかもしれません。",
|
||||
"upgraded_from_v2": "PleromaFEがあたらしくなったので、いままでのみためとすこしちがうかもしれません。",
|
||||
"v2_imported": "ふるいフロントエンドのためのファイルをインポートしました。せっていしたのとは、すこしちがうかもしれません。",
|
||||
"future_version_imported": "あたらしいフロントエンドでつくられたファイルをインポートしました。"
|
||||
},
|
||||
"load_theme": "テーマをよみこむ",
|
||||
"keep_as_is": "そのままにする",
|
||||
"use_snapshot": "ふるいバージョン",
|
||||
"use_source": "あたらしいバージョン"
|
||||
},
|
||||
"common": {
|
||||
"color": "いろ",
|
||||
|
|
@ -429,7 +549,26 @@
|
|||
"borders": "さかいめ",
|
||||
"buttons": "ボタン",
|
||||
"inputs": "インプットフィールド",
|
||||
"faint_text": "うすいテキスト"
|
||||
"faint_text": "うすいテキスト",
|
||||
"post": "とうこう / プロフィール",
|
||||
"wallpaper": "かべがみ",
|
||||
"icons": "アイコン",
|
||||
"highlight": "よくみえるようにした、ようそ",
|
||||
"pressed": "おしたとき",
|
||||
"chat": {
|
||||
"border": "さかいめ",
|
||||
"incoming": "うけとったもの",
|
||||
"outgoing": "おくったもの"
|
||||
},
|
||||
"underlay": "アンダーレイ",
|
||||
"alert_neutral": "それいがい",
|
||||
"popover": "ツールチップ、メニュー、ポップオーバー",
|
||||
"poll": "とうひょうのグラフ",
|
||||
"selectedPost": "えらんだとうこう",
|
||||
"selectedMenu": "えらんだメニューアイテム",
|
||||
"disabled": "つかえないとき",
|
||||
"toggled": "きりかえたとき",
|
||||
"tabs": "タブ"
|
||||
},
|
||||
"radii": {
|
||||
"_tab_label": "まるさ"
|
||||
|
|
@ -462,7 +601,8 @@
|
|||
"buttonPressed": "ボタン (おされているとき)",
|
||||
"buttonPressedHover": "ボタン (ホバー、かつ、おされているとき)",
|
||||
"input": "インプットフィールド"
|
||||
}
|
||||
},
|
||||
"hintV3": "かげのばあいは、 {0} というかきかたをつかうことができます。そうすると、ほかのいろのスロットをつかうことができます。"
|
||||
},
|
||||
"fonts": {
|
||||
"_tab_label": "フォント",
|
||||
|
|
@ -497,7 +637,167 @@
|
|||
"title": "バージョン",
|
||||
"backend_version": "バックエンドのバージョン",
|
||||
"frontend_version": "フロントエンドのバージョン"
|
||||
}
|
||||
},
|
||||
"notification_visibility_polls": "あなたがさんかしたとうひょうが、おわりました",
|
||||
"setting_server_side": "このせっていは、あなたのプロフィールについてのものです。へんこうすると、すべてのセッションとクライアントにえいきょうします",
|
||||
"mute_import_error": "ミュートのインポートが、エラーになりました",
|
||||
"account_backup_description": "あなたのアカウントじょうほうや、とうこうのアーカイブを、ダウンロードすることができます。しかし、 Pleroma アカウントにインポートすることはまだできません。",
|
||||
"list_backups_error": "バックアップリストをとりにいくことが、エラーになりました: {error}",
|
||||
"list_aliases_error": "エイリアスをとりにいくときに、エラーになりました: {error}",
|
||||
"added_alias": "エイリアスをつくりました。",
|
||||
"move_account_notes": "もしあなたがアカウントをほかのインスタンスにひっこしたいのなら、ひっこすさきのアカウントからここへのエイリアスをつくってください。",
|
||||
"file_export_import": {
|
||||
"backup_settings_theme": "せっていとテーマをファイルにバックアップする",
|
||||
"restore_settings": "ファイルからせっていをもとにもどす",
|
||||
"errors": {
|
||||
"file_too_new": "メジャーバージョン({fileMajor})がちがいます。この PleromaFE (せっていのバージョン {feMajor}) はふるいので、つかうことができません",
|
||||
"file_slightly_new": "ファイルのマイナーバージョンがちがっています。いくつかのせっていは、よみこまれないかもしれません",
|
||||
"invalid_file": "これは Pleroma のせっていをバックアップしたファイルではありません。",
|
||||
"file_too_old": "メジャーバージョン({fileMajor})がちがいます。ファイルのバージョンが古いので、使うことができません(バージョン {feMajor} いじょうのせっていバージョンをつかってください)"
|
||||
},
|
||||
"backup_settings": "せっていをファイルにバックアップする",
|
||||
"backup_restore": "せっていのバックアップ"
|
||||
},
|
||||
"hide_wallpaper": "このインスタンスのバックグラウンドをかくす",
|
||||
"reply_visibility_following_short": "わたしのフォローしているひとにあてられたリプライをみる",
|
||||
"reply_visibility_self_short": "じぶんにあてられたリプライだけをみる",
|
||||
"save": "へんこうをほぞんする",
|
||||
"reset_banner_confirm": "ほんとうに、バナーをリセットしますか?",
|
||||
"tree_advanced": "ツリービューで、ナビゲーションをもっとじゅうなんにする",
|
||||
"third_column_mode": "じゅうぶんなくうかんがあれば、3ばんめのれつをひょうじする",
|
||||
"conversation_other_replies_button": "「ほかのリプライ」ボタンをひょうじするばしょ",
|
||||
"user_popover_avatar_action_open": "プロフィールをひらく",
|
||||
"notification_setting_filters": "フィルター",
|
||||
"notification_setting_hide_notification_contents": "おくったひとと、ないようを、プッシュつうちにひょうじしない",
|
||||
"backup_running": "バックアップしています。{number}このデータをしょりしました。",
|
||||
"word_filter_and_more": "ことばのフィルターと、そのほか…",
|
||||
"account_privacy": "プライバシー",
|
||||
"posts": "とうこう",
|
||||
"move_account": "アカウントをひっこす",
|
||||
"move_account_target": "ひっこしさきのアカウント (れい: {example})",
|
||||
"mute_bot_posts": "Bot のとうこうをミュートする",
|
||||
"hide_bot_indication": "Bot によるとうこうであることを、とうこうにひょうじしない",
|
||||
"hide_all_muted_posts": "ミュートしたとうこうをかくす",
|
||||
"hide_shoutbox": "Shoutbox をかくす",
|
||||
"conversation_display_tree": "ツリーけいしき",
|
||||
"mention_link_display_full_for_remote": "リモートユーザーだけ、ながいなまえでひょうじする (れい: {'@'}hoge{'@'}example.org)",
|
||||
"mention_link_bolden_you": "あなたがメンションされたとき、あなたへのメンションを、よくみえるようにする",
|
||||
"user_popover_avatar_action": "ポップオーバーのアバターをクリックしたとき",
|
||||
"user_popover_avatar_action_zoom": "アバターをおおきくする",
|
||||
"user_popover_avatar_action_close": "ポップオーバーをとじる",
|
||||
"always_show_post_button": "とうこうボタンをいつもひょうじする",
|
||||
"auto_update": "あたらしいとうこうを、じどうてきにみせる",
|
||||
"user_mutes": "ユーザー",
|
||||
"useStreamingApi": "とうこうとつうちを、リアルタイムにうけとる",
|
||||
"use_websockets": "Websockets をつかう (リアルタイムアップデート)",
|
||||
"mutes_and_blocks": "ミュートとブロック",
|
||||
"emoji_reactions_on_timeline": "えもじリアクションをタイムラインにひょうじする",
|
||||
"accent": "アクセント",
|
||||
"domain_mutes": "ドメイン",
|
||||
"import_mutes_from_a_csv_file": "CSVファイルからミュートをインポートする",
|
||||
"reset_avatar": "アバターをリセットする",
|
||||
"remove_language": "とりのぞく",
|
||||
"primary_language": "いちばんわかることば:",
|
||||
"add_language": "よびとしてつかうことばを、ついかする",
|
||||
"fallback_language": "よびとしてつかうことば {index}:",
|
||||
"lists_navigation": "ナビゲーションにリストをひょうじする",
|
||||
"account_alias": "アカウントのエイリアス",
|
||||
"mention_link_display_full": "いつも、ながいなまえをひょうじする (れい: {'@'}hoge{'@'}example.org)",
|
||||
"setting_changed": "せっていは、デフォルトとちがっています",
|
||||
"email_language": "サーバーからうけとるEメールのことば",
|
||||
"mute_export": "ミュートのエクスポート",
|
||||
"mute_export_button": "あなたのミュートを、 CSV ファイルにエクスポートします",
|
||||
"mute_import": "ミュートのインポート",
|
||||
"mutes_imported": "ミュートをインポートしました!すこしじかんがかかるかもしれません。",
|
||||
"account_backup": "アカウントのバックアップ",
|
||||
"account_backup_table_head": "バックアップ",
|
||||
"download_backup": "ダウンロード",
|
||||
"backup_not_ready": "バックアップのじゅんびが、まだできていません。",
|
||||
"backup_failed": "バックアップにしっぱいしました。",
|
||||
"remove_backup": "とりのぞく",
|
||||
"add_backup": "あたらしいバックアップをつくる",
|
||||
"added_backup": "あたらしいバックアップをつくりました。",
|
||||
"add_backup_error": "あたらしいバックアップをつくるときに、エラーになりました: {error}",
|
||||
"bot": "これは bot アカウントです",
|
||||
"account_alias_table_head": "エイリアス",
|
||||
"hide_list_aliases_error_action": "とじる",
|
||||
"remove_alias": "このエイリアスをけす",
|
||||
"add_alias_error": "エイリアスをつくるときに、エラーになりました: {error}",
|
||||
"new_alias_target": "あたらしいエイリアスをつくる (れい: {example})",
|
||||
"moved_account": "アカウントをひっこしました。",
|
||||
"move_account_error": "アカウントをひっこしているときに、エラーになりました: {error}",
|
||||
"wordfilter": "ことばのフィルター",
|
||||
"hide_media_previews": "メディアのプレビューをかくす",
|
||||
"right_sidebar": "サイドバーをみぎにひょうじする",
|
||||
"hide_wordfiltered_statuses": "ことばのフィルターでフィルターされたステータスをかくす",
|
||||
"hide_muted_threads": "ミュートされたスレッドをかくす",
|
||||
"navbar_column_stretch": "ナビゲーションバーをれつのはばまでのばす",
|
||||
"birthday": {
|
||||
"label": "たんじょうび",
|
||||
"show_birthday": "たんじょうびを、ひょうじする"
|
||||
},
|
||||
"profile_fields": {
|
||||
"label": "プロフィールのメタデータ",
|
||||
"add_field": "フィールドをふやす",
|
||||
"name": "ラベル",
|
||||
"value": "ないよう"
|
||||
},
|
||||
"user_profiles": "ユーザープロフィール",
|
||||
"notification_visibility_moves": "ユーザーのひっこし",
|
||||
"notification_visibility_emoji_reactions": "リアクション",
|
||||
"hide_favorites_description": "おきにいりのリストをみせない (つうちはおくられます)",
|
||||
"reset_profile_background": "プロフィールバックグラウンドをリセットする",
|
||||
"reset_profile_banner": "プロフィールバナーをリセットする",
|
||||
"reset_avatar_confirm": "ほんとうに、アバターをリセットしますか?",
|
||||
"reset_background_confirm": "ほんとうに、バックグラウンドをリセットしますか?",
|
||||
"column_sizes_sidebar": "サイドバー",
|
||||
"column_sizes_notifs": "つうち",
|
||||
"columns": "れつ",
|
||||
"column_sizes": "れつのおおきさ",
|
||||
"column_sizes_content": "コンテンツ",
|
||||
"conversation_display": "スレッドのひょうじけいしき",
|
||||
"conversation_display_linear": "リニアけいしき",
|
||||
"conversation_display_linear_quick": "リニアビュー",
|
||||
"show_scrollbars": "よこのれつにスクロールバーをひょうじする",
|
||||
"third_column_mode_none": "3ばんめのれつをひょうじしない",
|
||||
"third_column_mode_postform": "とうこうフォームとナビゲーション",
|
||||
"third_column_mode_notifications": "つうちのれつをひょうじする",
|
||||
"tree_fade_ancestors": "げんざいのステータスのおやを、うすいいろのもじでひょうじする",
|
||||
"conversation_other_replies_button_below": "ステータスのした",
|
||||
"conversation_other_replies_button_inside": "ステータスのなか",
|
||||
"max_depth_in_thread": "デフォルトでひょうじするスレッドのふかさ",
|
||||
"sensitive_by_default": "デフォルトで、とうこうをNSFWにする",
|
||||
"type_domains_to_mute": "ミュートしたいドメインを、ここでけんさくできます",
|
||||
"mention_link_use_tooltip": "メンションのリンクをクリックしたとき、ユーザーカードをみせる",
|
||||
"mention_link_show_avatar": "ユーザーのアバターをリンクのよこにひょうじする",
|
||||
"mention_link_show_avatar_quick": "ユーザーのアバターをメンションのとなりにひょうじする",
|
||||
"mention_link_fade_domain": "ドメイン(れい: {'@'}hoge{'@'}example.org のなかの {'@'}example.org)を、うすいいろにする",
|
||||
"user_popover_avatar_overlay": "ユーザーのポップオーバーを、ユーザーのアバターのうえにひょうじする",
|
||||
"show_yous": "(あなた)をひょうじする",
|
||||
"notification_setting_block_from_strangers": "フォローしていないユーザーからのつうちをブロックする",
|
||||
"notification_setting_privacy": "プライバシー",
|
||||
"more_settings": "そのたのせってい",
|
||||
"expert_mode": "くわしいせっていを、ひょうじする",
|
||||
"mention_links": "メンションのリンク",
|
||||
"post_look_feel": "とうこうのみためとかんかく",
|
||||
"allow_following_move": "フォローしているアカウントがインスタンスをひっこしたばあい、じどうでフォローしてもよい",
|
||||
"chatMessageRadius": "チャットメッセージ",
|
||||
"confirm_dialogs": "つぎのばあいに、かくにんをする",
|
||||
"confirm_dialogs_repeat": "ステータスをリピートするとき",
|
||||
"confirm_dialogs_unfollow": "ユーザーのフォローをはずすとき",
|
||||
"confirm_dialogs_block": "ユーザーをブロックするとき",
|
||||
"confirm_dialogs_mute": "ユーザーをミュートするとき",
|
||||
"confirm_dialogs_delete": "ステータスをけすとき",
|
||||
"confirm_dialogs_logout": "ログアウトするとき",
|
||||
"confirm_dialogs_approve_follow": "フォローをうけいれるとき",
|
||||
"confirm_dialogs_deny_follow": "フォローをことわるとき",
|
||||
"confirm_dialogs_remove_follower": "フォロワーをとりのぞくとき",
|
||||
"conversation_display_tree_quick": "ツリービュー",
|
||||
"disable_sticky_headers": "れつのヘッダーを、がめんのいちばんうえにこていしない",
|
||||
"virtual_scrolling": "タイムラインのレンダリングをよくする",
|
||||
"use_at_icon": "{'@'} きごうを、もじのかわりに、アイコンでひょうじする",
|
||||
"mention_link_display_short": "いつも、みじかいなまえにする (れい: {'@'}hoge)",
|
||||
"mention_link_display": "メンションのリンクをひょうじするけいしき"
|
||||
},
|
||||
"time": {
|
||||
"day": "{0}日",
|
||||
|
|
@ -531,7 +831,23 @@
|
|||
"year": "{0}年",
|
||||
"years": "{0}年",
|
||||
"year_short": "{0}年",
|
||||
"years_short": "{0}年"
|
||||
"years_short": "{0}年",
|
||||
"unit": {
|
||||
"minutes": "{0}ふん",
|
||||
"seconds_short": "{0}びょう",
|
||||
"weeks": "{0}しゅうかん",
|
||||
"weeks_short": "{0}しゅう",
|
||||
"years": "{0}ねん",
|
||||
"years_short": "{0}ねん",
|
||||
"days": "{0}にち",
|
||||
"days_short": "{0}にち",
|
||||
"hours": "{0}じかん",
|
||||
"hours_short": "{0}じかん",
|
||||
"minutes_short": "{0}ふん",
|
||||
"months": "{0}かげつ",
|
||||
"months_short": "{0}かげつ",
|
||||
"seconds": "{0}びょう"
|
||||
}
|
||||
},
|
||||
"timeline": {
|
||||
"collapse": "たたむ",
|
||||
|
|
@ -543,7 +859,11 @@
|
|||
"show_new": "よみこみ",
|
||||
"up_to_date": "さいしん",
|
||||
"no_more_statuses": "これでおわりです",
|
||||
"no_statuses": "ありません"
|
||||
"no_statuses": "ありません",
|
||||
"socket_broke": "コード{0}により、リアルタイムでつながることがなくなりました",
|
||||
"socket_reconnected": "リアルタイムでつながることを、つくりました",
|
||||
"reload": "もういちど、よみこむ",
|
||||
"error": "タイムラインをとりにいくときに、エラーになりました: {0}"
|
||||
},
|
||||
"status": {
|
||||
"favorites": "おきにいり",
|
||||
|
|
@ -556,7 +876,57 @@
|
|||
"reply_to": "へんしん:",
|
||||
"replies_list": "へんしん:",
|
||||
"mute_conversation": "スレッドをミュートする",
|
||||
"unmute_conversation": "スレッドをミュートするのをやめる"
|
||||
"unmute_conversation": "スレッドをミュートするのをやめる",
|
||||
"repeat_confirm_title": "リピートのかくにん",
|
||||
"mentions": "メンション",
|
||||
"thread_muted": "ミュートされたスレッド",
|
||||
"collapse_attachments": "ファイルをかくす",
|
||||
"remove_attachment": "ファイルをとりのぞく",
|
||||
"thread_show_full": "このスレッドのすべてのとうこうをみる (ぜんぶで{numStatus}このステータス、ふかさ{depth})",
|
||||
"show_all_attachments": "すべてのファイルをみる",
|
||||
"hide_full_subject": "かくす",
|
||||
"nsfw": "NSFW",
|
||||
"hide_content": "かくす",
|
||||
"status_deleted": "このとうこうは、けされました",
|
||||
"you": "(あなた)",
|
||||
"expand": "ひろげる",
|
||||
"repeat_confirm_accept_button": "リピートする",
|
||||
"repeat_confirm_cancel_button": "リピートしない",
|
||||
"edited_at": "({time} まえにへんしゅう)",
|
||||
"delete_confirm_title": "けすことのかくにん",
|
||||
"delete_confirm_accept_button": "けす",
|
||||
"delete_confirm_cancel_button": "のこす",
|
||||
"edit": "ステータスをへんしゅうする",
|
||||
"bookmark": "ブックマークする",
|
||||
"unbookmark": "ブックマークをはずす",
|
||||
"replies_list_with_others": "へんしん (ほかに +{numReplies}こ):",
|
||||
"status_unavailable": "ステータスがありません",
|
||||
"copy_link": "リンクをコピー",
|
||||
"external_source": "そとにあるソース",
|
||||
"thread_muted_and_words": "つぎのことばをふくむので:",
|
||||
"show_content": "みる",
|
||||
"plus_more": "あと {number}こ",
|
||||
"many_attachments": "とうこうには、{number}このファイルがついています",
|
||||
"show_attachment_in_modal": "メディアモーダルでみる",
|
||||
"show_attachment_description": "せつめいのプレビュー (ぜんぶみるには、ファイルをひらいてください)",
|
||||
"hide_attachment": "ファイルをかくす",
|
||||
"attachment_stop_flash": "Flash プレーヤーをとめる",
|
||||
"move_up": "ファイルをひだりにうごかす",
|
||||
"move_down": "ファイルをみぎにうごかす",
|
||||
"open_gallery": "ギャラリーをひらく",
|
||||
"thread_hide": "スレッドをかくす",
|
||||
"thread_show": "スレッドをみる",
|
||||
"show_full_subject": "すべてをみる",
|
||||
"repeat_confirm": "ほんとうに、このステータスをリピートしますか?",
|
||||
"show_all_conversation": "このスレッドをぜんぶみる (あと {numStatus}このステータス)",
|
||||
"show_only_conversation_under_this": "このステータスへのへんしんだけをみる",
|
||||
"status_history": "ステータスのれきし",
|
||||
"thread_show_full_with_icon": "{icon} {text}",
|
||||
"thread_follow": "のこりのとうこうをみる (ぜんぶで {numStatus}このステータス)",
|
||||
"thread_follow_with_icon": "{icon} {text}",
|
||||
"ancestor_follow": "このステータスよりしたの、{numReplies}このへんしんをみる",
|
||||
"ancestor_follow_with_icon": "{icon} {text}",
|
||||
"show_all_conversation_with_icon": "{icon} {text}"
|
||||
},
|
||||
"user_card": {
|
||||
"approve": "うけいれ",
|
||||
|
|
@ -577,7 +947,7 @@
|
|||
"media": "メディア",
|
||||
"mention": "メンション",
|
||||
"mute": "ミュート",
|
||||
"muted": "ミュートしています!",
|
||||
"muted": "ミュートしています",
|
||||
"per_day": "/日",
|
||||
"remote_follow": "リモートフォロー",
|
||||
"report": "つうほう",
|
||||
|
|
@ -608,8 +978,52 @@
|
|||
"disable_remote_subscription": "ほかのインスタンスからフォローされないようにする",
|
||||
"disable_any_subscription": "フォローされないようにする",
|
||||
"quarantine": "ほかのインスタンスのユーザーのとうこうをとめる",
|
||||
"delete_user": "ユーザーをけす"
|
||||
}
|
||||
"delete_user": "ユーザーをけす",
|
||||
"delete_user_data_and_deactivate_confirmation": "これをすると、このアカウントのデータがきえて、にどとつかえなくなります。ほんとうに、していいですか?"
|
||||
},
|
||||
"mute_confirm_accept_button": "ミュートする",
|
||||
"unfollow_confirm_title": "フォローをやめることのかくにん",
|
||||
"mute_confirm": "ほんとうに、 {user} をミュートしますか?",
|
||||
"mute_duration_prompt": "このユーザーをつぎのじかんだけミュートする (0にすると、おわりがありません):",
|
||||
"edit_note_apply": "てきよう",
|
||||
"block_confirm": "ほんとうに、 {user} をブロックしますか?",
|
||||
"deactivated": "つかえない",
|
||||
"remove_follower": "フォロワーをとりのぞく",
|
||||
"highlight": {
|
||||
"solid": "バッググラウンドをひとつのいろにする",
|
||||
"striped": "しまもようのバックグラウンドにする",
|
||||
"side": "はじにせんをつける",
|
||||
"disabled": "めだたせない"
|
||||
},
|
||||
"mute_confirm_cancel_button": "ミュートしない",
|
||||
"unfollow_confirm_accept_button": "フォローをやめる",
|
||||
"unfollow_confirm": "ほんとうに、 {user} のフォローをやめますか?",
|
||||
"unfollow_confirm_cancel_button": "フォローしたままにする",
|
||||
"mute_confirm_title": "ミュートのかくにん",
|
||||
"block_confirm_accept_button": "ブロックする",
|
||||
"block_confirm_cancel_button": "ブロックしない",
|
||||
"deny_confirm_title": "おことわりのかくにん",
|
||||
"deny_confirm_accept_button": "ことわる",
|
||||
"deny_confirm_cancel_button": "ことわらない",
|
||||
"deny_confirm": "{user} のフォローリクエストをことわりますか?",
|
||||
"follow_cancel": "リクエストをキャンセル",
|
||||
"birthday": "{birthday} に、うまれました",
|
||||
"remove_follower_confirm_title": "フォロワーをとりのぞくことのかくにん",
|
||||
"remove_follower_confirm_accept_button": "とりのぞく",
|
||||
"remove_follower_confirm_cancel_button": "のこす",
|
||||
"remove_follower_confirm": "ほんとうに、 {user} をあなたのフォロワーからとりのぞきますか?",
|
||||
"edit_note": "メモをへんしゅうする",
|
||||
"edit_note_cancel": "キャンセル",
|
||||
"message": "メッセージ",
|
||||
"bot": "bot",
|
||||
"approve_confirm_title": "うけいれのかくにん",
|
||||
"approve_confirm_accept_button": "うけいれる",
|
||||
"approve_confirm_cancel_button": "うけいれない",
|
||||
"approve_confirm": "{user} のフォローリクエストをうけいれますか?",
|
||||
"edit_profile": "プロフィールをへんしゅう",
|
||||
"block_confirm_title": "ブロックのかくにん",
|
||||
"note_blank": "(なし)",
|
||||
"note": "メモ"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "ユーザータイムライン",
|
||||
|
|
@ -634,13 +1048,21 @@
|
|||
"repeat": "リピート",
|
||||
"reply": "リプライ",
|
||||
"favorite": "おきにいり",
|
||||
"user_settings": "ユーザーせってい"
|
||||
"user_settings": "ユーザーせってい",
|
||||
"accept_follow_request": "フォローのおねがいを、うけいれる",
|
||||
"toggle_mute": "ミュートされたないようをみるために、つうちをひらくか、とじる",
|
||||
"autocomplete_available": "{number}このけっかが、あります。うえとしたのキーをつかって、けっかをみることができます。",
|
||||
"add_reaction": "リアクションをつける",
|
||||
"reject_follow_request": "フォローのおねがいを、ことわる",
|
||||
"bookmark": "ブックマーク",
|
||||
"toggle_expand": "とうこうをすべてみるために、つうちをひらくか、とじる"
|
||||
},
|
||||
"upload": {
|
||||
"error": {
|
||||
"base": "アップロードにしっぱいしました。",
|
||||
"file_too_big": "ファイルがおおきすぎます [{filesize} {filesizeunit} / {allowedsize} {allowedsizeunit}]",
|
||||
"default": "しばらくしてから、ためしてください"
|
||||
"default": "しばらくしてから、ためしてください",
|
||||
"message": "アップロードにしっぱいしました: {0}"
|
||||
},
|
||||
"file_size_units": {
|
||||
"B": "B",
|
||||
|
|
@ -655,7 +1077,9 @@
|
|||
"hashtags": "ハッシュタグ",
|
||||
"person_talking": "{count} にんが、はなしています",
|
||||
"people_talking": "{count} にんが、はなしています",
|
||||
"no_results": "みつかりませんでした"
|
||||
"no_results": "みつかりませんでした",
|
||||
"no_more_results": "これでおわりです",
|
||||
"load_more": "もっとみる"
|
||||
},
|
||||
"password_reset": {
|
||||
"forgot_password": "パスワードを、わすれましたか?",
|
||||
|
|
@ -668,5 +1092,103 @@
|
|||
"password_reset_disabled": "このインスタンスでは、パスワードリセットは、できません。インスタンスのアドミニストレーターに、おといあわせください。",
|
||||
"password_reset_required": "ログインするには、パスワードをリセットしてください。",
|
||||
"password_reset_required_but_mailer_is_disabled": "あなたはパスワードのリセットがひつようです。しかし、まずいことに、このインスタンスでは、パスワードのリセットができなくなっています。このインスタンスのアドミニストレーターに、おといあわせください。"
|
||||
},
|
||||
"announcements": {
|
||||
"post_placeholder": "おしらせのないようを、にゅうりょくしてください。",
|
||||
"end_time_prompt": "おわるじかん: ",
|
||||
"inactive_message": "このおしらせは、つかわれていません",
|
||||
"page_header": "おしらせ",
|
||||
"title": "おしらせ",
|
||||
"post_action": "とうこう",
|
||||
"post_form_header": "おしらせをとうこう",
|
||||
"mark_as_read_action": "よんだことにする",
|
||||
"post_error": "エラー: {error}",
|
||||
"close_error": "とじる",
|
||||
"delete_action": "けす",
|
||||
"start_time_display": "{time}にはじまります",
|
||||
"end_time_display": "{time}におわります",
|
||||
"edit_action": "へんしゅう",
|
||||
"start_time_prompt": "はじまるじかん: ",
|
||||
"all_day_prompt": "このイベントはいちにちじゅうやります",
|
||||
"published_time_display": "{time}にこうかいされました",
|
||||
"submit_edit_action": "そうしん",
|
||||
"cancel_edit_action": "キャンセル"
|
||||
},
|
||||
"report": {
|
||||
"reported_statuses": "つうほうされたステータス:",
|
||||
"reporter": "つうほうしたひと:",
|
||||
"state_closed": "クローズ",
|
||||
"state_resolved": "かいけつしました",
|
||||
"reported_user": "つうほうされたユーザー:",
|
||||
"notes": "メモ:",
|
||||
"state": "じょうたい:",
|
||||
"state_open": "オープン"
|
||||
},
|
||||
"update": {
|
||||
"update_bugs": "もんだいや、バグがあれば、 {pleromaGitlab} でおしえてください。ちゃんとテストはしているのですが、たくさんのことをかえているので、そしてかいはつバージョンをつかっているので、もんだいやバグに、きづかないことがあります。あなたがきづいたもんだいについての、フィードバックやていあんを、まっています。 Pleroma や Pleroma-FE をよくするやりかたについても、おしえてください。",
|
||||
"update_changelog_here": "すべてのかわったことのきろく",
|
||||
"art_by": "{linkToArtist}によるさくひん",
|
||||
"big_update_title": "すこし、まってください",
|
||||
"big_update_content": "しばらくリリースがありませんでした。おもっていたみためと、ちがうかもしれません。",
|
||||
"update_bugs_gitlab": "Pleroma GitLab",
|
||||
"update_changelog": "かわったことをすべてみるには、{theFullChangelog}をみてください。"
|
||||
},
|
||||
"chats": {
|
||||
"new": "あたらしいチャット",
|
||||
"chats": "チャット",
|
||||
"you": "あなた:",
|
||||
"message_user": "{nickname} にメッセージ",
|
||||
"delete": "けす",
|
||||
"empty_message_error": "なにかかいてください",
|
||||
"more": "もっとみる",
|
||||
"delete_confirm": "ほんとうに、このメッセージをけしますか?",
|
||||
"error_loading_chat": "チャットをよみこむことに、しっぱいしました。",
|
||||
"error_sending_message": "メッセージをおくることに、しっぱいしました。",
|
||||
"empty_chat_list_placeholder": "チャットがありません。あたらしいチャットボタンをおして、はじめてください!"
|
||||
},
|
||||
"shoutbox": {
|
||||
"title": "Shoutbox"
|
||||
},
|
||||
"errors": {
|
||||
"storage_unavailable": "Pleroma はブラウザーのストレージにアクセスすることができません。あなたがログインしたことと、あなたのローカルのせっていは、ほぞんされません。ほかにももんだいがおきるかもしれません。 Cookie をゆうこうにしてください。"
|
||||
},
|
||||
"lists": {
|
||||
"lists": "リスト",
|
||||
"new": "あたらしいリスト",
|
||||
"search": "ユーザーをさがす",
|
||||
"title": "リストのなまえ",
|
||||
"create": "つくる",
|
||||
"save": "へんこうをほぞんする",
|
||||
"delete": "リストをけす",
|
||||
"following_only": "フォローしているひとげんていにする",
|
||||
"manage_lists": "リストをかんりする",
|
||||
"manage_members": "リストにふくまれるひとを、かんりする",
|
||||
"add_members": "もっとユーザーをさがす",
|
||||
"remove_from_list": "リストからとりのぞく",
|
||||
"add_to_list": "リストにいれる",
|
||||
"editing_list": "リスト {listTitle} をへんしゅうしています",
|
||||
"creating_list": "あたらしいリストをつくっています",
|
||||
"update_title": "なまえをほぞんする",
|
||||
"really_delete": "ほんとうに、リストをけしますか?",
|
||||
"is_in_list": "すでにリストのなかにあります",
|
||||
"error": "リストをへんしゅうするときに、エラーになりました: {0}"
|
||||
},
|
||||
"file_type": {
|
||||
"audio": "オーディオ",
|
||||
"video": "ビデオ",
|
||||
"image": "がぞう",
|
||||
"file": "ファイル"
|
||||
},
|
||||
"display_date": {
|
||||
"today": "きょう"
|
||||
},
|
||||
"unicode_domain_indicator": {
|
||||
"tooltip": "このドメインは、ASCIIいがいのもじをふくんでいます。"
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"mute": "ミュート",
|
||||
"mute_progress": "ミュートしています…",
|
||||
"unmute": "ミュートをやめる",
|
||||
"unmute_progress": "ミュートをやめています…"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,11 @@
|
|||
"enter_two_factor_code": "2단계인증 코드를 입력하십시오",
|
||||
"enter_recovery_code": "복구 코드를 입력하십시오",
|
||||
"authentication_code": "인증 코드",
|
||||
"hint": "로그인해서 대화에 참여"
|
||||
"hint": "로그인해서 대화에 참여",
|
||||
"logout_confirm_title": "로그아웃 확인",
|
||||
"logout_confirm": "정말 로그아웃 하시겠습니까?",
|
||||
"logout_confirm_accept_button": "로그아웃",
|
||||
"logout_confirm_cancel_button": "로그아웃 안 함"
|
||||
},
|
||||
"nav": {
|
||||
"about": "인스턴스 소개",
|
||||
|
|
@ -104,7 +108,8 @@
|
|||
"edit_finish": "편집 종료",
|
||||
"mobile_notifications_close": "알림 닫기",
|
||||
"mobile_sidebar": "모바일 사이드바 토글",
|
||||
"announcements": "공지사항"
|
||||
"announcements": "공지사항",
|
||||
"search_close": "검색 바 닫기"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "알 수 없는 게시물입니다, 검색합니다…",
|
||||
|
|
@ -158,7 +163,9 @@
|
|||
"edit_status": "수정",
|
||||
"edit_remote_warning": "수정 기능이 없는 다른 인스턴스에서는 수정한 사항이 반영되지 않을 수 있습니다.",
|
||||
"post": "게시",
|
||||
"direct_warning_to_first_only": "맨 앞에 멘션한 사용자들에게만 보여집니다."
|
||||
"direct_warning_to_first_only": "맨 앞에 멘션한 사용자들에게만 보여집니다.",
|
||||
"content_type_selection": "게시물 형태",
|
||||
"scope_notice_dismiss": "알림 닫기"
|
||||
},
|
||||
"registration": {
|
||||
"bio": "소개",
|
||||
|
|
@ -175,7 +182,9 @@
|
|||
"email_required": "공백으로 둘 수 없습니다",
|
||||
"password_required": "공백으로 둘 수 없습니다",
|
||||
"password_confirmation_required": "공백으로 둘 수 없습니다",
|
||||
"password_confirmation_match": "패스워드와 일치해야 합니다"
|
||||
"password_confirmation_match": "패스워드와 일치해야 합니다",
|
||||
"birthday_required": "공백으로 둘 수 없습니다",
|
||||
"birthday_min_age": "{date} 또는 그 이전 출생만 가능합니다"
|
||||
},
|
||||
"fullname_placeholder": "예: 김례인",
|
||||
"username_placeholder": "예: lain",
|
||||
|
|
@ -185,7 +194,9 @@
|
|||
"reason": "가입하려는 이유",
|
||||
"reason_placeholder": "이 인스턴스는 수동으로 가입을 승인하고 있습니다.\n왜 가입하고 싶은지 관리자에게 알려주세요.",
|
||||
"register": "가입",
|
||||
"email_language": "무슨 언어로 이메일을 받길 원하시나요?"
|
||||
"email_language": "무슨 언어로 이메일을 받길 원하시나요?",
|
||||
"birthday": "생일:",
|
||||
"birthday_optional": "생일 (선택):"
|
||||
},
|
||||
"settings": {
|
||||
"attachmentRadius": "첨부물",
|
||||
|
|
@ -383,7 +394,8 @@
|
|||
"highlight": "강조 요소",
|
||||
"pressed": "눌렸을 때",
|
||||
"toggled": "토글됨",
|
||||
"tabs": "탭"
|
||||
"tabs": "탭",
|
||||
"underlay": "밑배경"
|
||||
},
|
||||
"radii": {
|
||||
"_tab_label": "둥글기"
|
||||
|
|
@ -652,7 +664,29 @@
|
|||
"post_status_content_type": "게시물 내용 형식",
|
||||
"list_aliases_error": "별칭을 가져오는 중 에러 발생: {error}",
|
||||
"add_alias_error": "별칭을 추가하는 중 에러 발생: {error}",
|
||||
"mention_link_show_avatar_quick": "멘션 옆에 유저 프로필 사진을 보임"
|
||||
"mention_link_show_avatar_quick": "멘션 옆에 유저 프로필 사진을 보임",
|
||||
"backup_running": "백업 중입니다, {number}개 처리 완료. | 백업 중입니다, {number}개 처리 완료.",
|
||||
"confirm_dialogs": "하기 전에 다시 물어보기",
|
||||
"autocomplete_select_first": "자동완성이 가능하면 자동으로 첫 번째 후보를 선택",
|
||||
"backup_failed": "백업에 실패했습니다.",
|
||||
"emoji_reactions_scale": "리액션 크기",
|
||||
"birthday": {
|
||||
"label": "생일",
|
||||
"show_birthday": "내 생일 보여주기"
|
||||
},
|
||||
"add_language": "보조 언어 추가",
|
||||
"confirm_dialogs_repeat": "리핏",
|
||||
"confirm_dialogs_unfollow": "언팔로우",
|
||||
"confirm_dialogs_block": "차단",
|
||||
"confirm_dialogs_mute": "뮤트",
|
||||
"confirm_dialogs_delete": "게시물 삭제",
|
||||
"confirm_dialogs_approve_follow": "팔로워 승인",
|
||||
"confirm_dialogs_deny_follow": "팔로워 거절",
|
||||
"confirm_dialogs_remove_follower": "팔로워 제거",
|
||||
"remove_language": "삭제",
|
||||
"primary_language": "주 언어:",
|
||||
"fallback_language": "보조 언어 {index}:",
|
||||
"confirm_dialogs_logout": "로그아웃"
|
||||
},
|
||||
"timeline": {
|
||||
"collapse": "접기",
|
||||
|
|
@ -735,7 +769,12 @@
|
|||
"striped": "줄무늬 배경",
|
||||
"solid": "단색 배경",
|
||||
"side": "옆트임"
|
||||
}
|
||||
},
|
||||
"approve_confirm_title": "승인 확인",
|
||||
"approve_confirm_accept_button": "승인",
|
||||
"approve_confirm_cancel_button": "승인 안 함",
|
||||
"approve_confirm": "{user}의 팔로우 요청을 승인할까요?",
|
||||
"block_confirm_title": "차단 확인"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "사용자 타임라인",
|
||||
|
|
@ -1069,7 +1108,14 @@
|
|||
"ancestor_follow_with_icon": "{icon} {text}",
|
||||
"show_all_conversation_with_icon": "{icon} {text}",
|
||||
"ancestor_follow": "이 게시물 아래 {numReplies}개 답글 더 보기 | 이 게시물 아래 {numReplies}개 답글 더 보기",
|
||||
"show_only_conversation_under_this": "이 게시물의 답글만 보기"
|
||||
"show_only_conversation_under_this": "이 게시물의 답글만 보기",
|
||||
"repeat_confirm": "리핏할까요?",
|
||||
"repeat_confirm_title": "리핏 확인",
|
||||
"repeat_confirm_accept_button": "리핏",
|
||||
"repeat_confirm_cancel_button": "리핏 안 함",
|
||||
"delete_confirm_title": "삭제 확인",
|
||||
"delete_confirm_accept_button": "삭제",
|
||||
"delete_confirm_cancel_button": "냅두기"
|
||||
},
|
||||
"errors": {
|
||||
"storage_unavailable": "Pleroma가 브라우저 저장소에 접근할 수 없습니다. 로그인이 풀리거나 로컬 설정이 초기화 되는 등 예상치 못한 문제를 겪을 수 있습니다. 쿠키를 활성화 해보세요."
|
||||
|
|
|
|||
|
|
@ -118,7 +118,10 @@
|
|||
"totp": "Двофакторна автентифікація"
|
||||
},
|
||||
"enter_two_factor_code": "Введіть двофакторний код автентифікації",
|
||||
"placeholder": "напр. stepan"
|
||||
"placeholder": "напр. stepan",
|
||||
"logout_confirm": "Ви дійсно хочете вийти?",
|
||||
"logout_confirm_accept_button": "Вийти",
|
||||
"logout_confirm_cancel_button": "Ні, хочу назад!"
|
||||
},
|
||||
"importer": {
|
||||
"error": "Під час імпортування файлу сталася помилка.",
|
||||
|
|
@ -189,7 +192,8 @@
|
|||
"mobile_notifications": "Відкрити сповіщення (є непрочитані)",
|
||||
"mobile_notifications_close": "Закрити сповіщення",
|
||||
"edit_nav_mobile": "Редагувати панель навігації",
|
||||
"announcements": "Анонси"
|
||||
"announcements": "Анонси",
|
||||
"search_close": "Закрити панель пошуку"
|
||||
},
|
||||
"media_modal": {
|
||||
"next": "Наступна",
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue