Merge remote-tracking branch 'origin/develop' into shigusegubu
* origin/develop: (23 commits) change icon to a megaphone change side drawer to use shoutbox name update changelog Fix chat messages being missed when the streaming is disabled and the messages are sent by both participants simultaneously Fix the chat list order and last message timestamp updates change a eslint disable to nextline only fix vue warnings and errors Translated using Weblate (Basque) Translated using Weblate (Spanish) Translated using Weblate (Spanish) Translated using Weblate (Spanish) Do not show desktop notifications for your own chat messages Add hacky functionality to open specific settings tabs Password reset no longer informs user of errors or account existence add changelog entry hide poll when subject collapsed, but show poll icon update changelog for autocomplete fixes update changelog with 2.1.0, fix Add -> Added in older releases Rewrite word split imperatively for control wip start ...
This commit is contained in:
commit
8e76a1dc53
39 changed files with 447 additions and 172 deletions
48
CHANGELOG.md
48
CHANGELOG.md
|
@ -2,21 +2,26 @@
|
|||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## [Unreleased]
|
||||
### Changed
|
||||
- Greentext now has separate color slot for it
|
||||
- Removed the use of with_move parameters when fetching notifications
|
||||
- Push notifications now are the same as normal notfication, and are localized.
|
||||
- Updated Notification Settings to match new BE API
|
||||
## [Unreleased patch]
|
||||
|
||||
### Fixed
|
||||
- Weird bug related to post being sent seemingly after pasting with keyboard (hopefully)
|
||||
- Multiple issues with muted statuses/notifications
|
||||
- Fixed chats list not updating its order when new messages come in
|
||||
- Fixed chat messages sometimes getting lost when you receive a message at the same time
|
||||
|
||||
## [Unreleased patch]
|
||||
### Add
|
||||
- Added private notifications option for push notifications
|
||||
- 'Copy link' button for statuses (in the ellipsis menu)
|
||||
|
||||
## [2.1.1] - 2020-09-08
|
||||
### Changed
|
||||
- Polls will be hidden with status content if "Collapse posts with subjects" is enabled and the post is collapsed.
|
||||
|
||||
### Fixed
|
||||
- Autocomplete won't stop at the second @, so it'll still work with "@lain@l" and not start over.
|
||||
- Fixed weird autocomplete behavior when you write ":custom_emoji: ?"
|
||||
|
||||
|
||||
## [2.1.0] - 2020-08-28
|
||||
### Added
|
||||
- Autocomplete domains from list of known instances
|
||||
- 'Bot' settings option and badge
|
||||
- Added profile meta data fields that can be set in profile settings
|
||||
|
@ -25,15 +30,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Added status preview option to preview your statuses before posting
|
||||
- When a post is a reply to an unavailable post, the 'Reply to'-text has a strike-through style
|
||||
- Added ability to see all favoriting or repeating users when hovering the number on highlighted statuses
|
||||
- Bookmarks
|
||||
|
||||
### Changed
|
||||
- Registration page no longer requires email if the server is configured not to require it
|
||||
- Change heart to thumbs up in reaction picker
|
||||
- Close the media modal on navigation events
|
||||
- Add colons to the emoji alt text, to make them copyable
|
||||
- Add better visual indication for drag-and-drop for files
|
||||
- When disabling attachments, the placeholder links now show an icon and the description instead of just IMAGE or VIDEO etc
|
||||
- Remove unnecessary options for 'automatic loading when loading older' and 'reply previews'
|
||||
- Greentext now has separate color slot for it
|
||||
- Removed the use of with_move parameters when fetching notifications
|
||||
- Push notifications now are the same as normal notfication, and are localized.
|
||||
- Updated Notification Settings to match new BE API
|
||||
|
||||
### Fixed
|
||||
- Custom Emoji will display in poll options now.
|
||||
|
@ -52,6 +61,19 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
- Reply filtering options in Settings -> Filtering now work again using filtering on server
|
||||
- Don't show just blank-screen when cookies are disabled
|
||||
- Add status idempotency to prevent accidental double posting when posting returns an error
|
||||
- Weird bug related to post being sent seemingly after pasting with keyboard (hopefully)
|
||||
- Multiple issues with muted statuses/notifications
|
||||
|
||||
## [2.0.5] - 2020-05-12
|
||||
### Added
|
||||
- Added private notifications option for push notifications
|
||||
- 'Copy link' button for statuses (in the ellipsis menu)
|
||||
|
||||
### Changed
|
||||
- Registration page no longer requires email if the server is configured not to require it
|
||||
|
||||
### Fixed
|
||||
- Status ellipsis menu closes properly when selecting certain options
|
||||
|
||||
## [2.0.3] - 2020-05-02
|
||||
### Fixed
|
||||
|
@ -61,7 +83,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
### Changed
|
||||
- Emoji autocomplete will match any part of the word and not just start, for example :drool will now helpfully suggest :blobcatdrool: and :blobcatdroolreach:
|
||||
|
||||
### Add
|
||||
### Added
|
||||
- Follow request notification support
|
||||
|
||||
## [2.0.2] - 2020-04-08
|
||||
|
|
|
@ -204,9 +204,9 @@ const Chat = {
|
|||
}
|
||||
},
|
||||
readChat () {
|
||||
if (!(this.currentChatMessageService && this.currentChatMessageService.lastMessage)) { return }
|
||||
if (!(this.currentChatMessageService && this.currentChatMessageService.maxId)) { return }
|
||||
if (document.hidden) { return }
|
||||
const lastReadId = this.currentChatMessageService.lastMessage.id
|
||||
const lastReadId = this.currentChatMessageService.maxId
|
||||
this.$store.dispatch('readChat', { id: this.currentChat.id, lastReadId })
|
||||
},
|
||||
bottomedOut (offset) {
|
||||
|
@ -244,7 +244,7 @@ const Chat = {
|
|||
|
||||
const chatId = chatMessageService.chatId
|
||||
const fetchOlderMessages = !!maxId
|
||||
const sinceId = fetchLatest && chatMessageService.lastMessage && chatMessageService.lastMessage.id
|
||||
const sinceId = fetchLatest && chatMessageService.maxId
|
||||
|
||||
this.backendInteractor.chatMessages({ id: chatId, maxId, sinceId })
|
||||
.then((messages) => {
|
||||
|
@ -303,7 +303,11 @@ const Chat = {
|
|||
|
||||
return this.backendInteractor.sendChatMessage(params)
|
||||
.then(data => {
|
||||
this.$store.dispatch('addChatMessages', { chatId: this.currentChat.id, messages: [data] }).then(() => {
|
||||
this.$store.dispatch('addChatMessages', {
|
||||
chatId: this.currentChat.id,
|
||||
messages: [data],
|
||||
updateMaxId: false
|
||||
}).then(() => {
|
||||
this.$nextTick(() => {
|
||||
this.handleResize()
|
||||
// When the posting form size changes because of a media attachment, we need an extra resize
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
.chat-message-wrapper {
|
||||
&.hovered-message-chain {
|
||||
.animated.avatar {
|
||||
.animated.Avatar {
|
||||
canvas {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@
|
|||
@click.stop.prevent="togglePanel"
|
||||
>
|
||||
<div class="title">
|
||||
<i class="icon-comment-empty" />
|
||||
<i class="icon-megaphone" />
|
||||
{{ $t('shoutbox.title') }}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -39,13 +39,16 @@
|
|||
export default {
|
||||
props: {
|
||||
large: {
|
||||
required: false
|
||||
required: false,
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// TODO: Make theme switcher compute theme initially so that contrast
|
||||
// component won't be called without contrast data
|
||||
contrast: {
|
||||
required: false,
|
||||
type: Object
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
word-wrap: break-word;
|
||||
word-break: break-word;
|
||||
|
||||
&:hover .animated.avatar {
|
||||
&:hover .animated.Avatar {
|
||||
canvas {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -47,11 +47,6 @@ const passwordReset = {
|
|||
if (status === 204) {
|
||||
this.success = true
|
||||
this.error = null
|
||||
} else if (status === 404 || status === 400) {
|
||||
this.error = this.$t('password_reset.not_found')
|
||||
this.$nextTick(() => {
|
||||
this.$refs.email.focus()
|
||||
})
|
||||
} else if (status === 429) {
|
||||
this.throttled = true
|
||||
this.error = this.$t('password_reset.too_many_requests')
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
<span class="result-percentage">
|
||||
{{ percentageForOption(option.votes_count) }}%
|
||||
</span>
|
||||
<!-- eslint-disable-next-line vue/no-v-html -->
|
||||
<span v-html="option.title_html" />
|
||||
</div>
|
||||
<div
|
||||
|
@ -96,6 +97,7 @@
|
|||
align-items: center;
|
||||
padding: 0.1em 0.25em;
|
||||
z-index: 1;
|
||||
word-break: break-word;
|
||||
}
|
||||
.result-percentage {
|
||||
width: 3.5em;
|
||||
|
|
|
@ -555,6 +555,9 @@ const PostStatusForm = {
|
|||
},
|
||||
updateIdempotencyKey () {
|
||||
this.idempotencyKey = Date.now().toString()
|
||||
},
|
||||
openProfileTab () {
|
||||
this.$store.dispatch('openSettingsModalTab', 'profile')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,12 @@
|
|||
tag="p"
|
||||
class="visibility-notice"
|
||||
>
|
||||
<router-link :to="{ name: 'user-settings' }">
|
||||
<a
|
||||
href="#"
|
||||
@click="openProfileTab"
|
||||
>
|
||||
{{ $t('post_status.account_not_locked_warning_link') }}
|
||||
</router-link>
|
||||
</a>
|
||||
</i18n>
|
||||
<p
|
||||
v-if="!hideScopeNotice && newStatus.visibility === 'public'"
|
||||
|
|
|
@ -27,6 +27,34 @@ const SettingsModalContent = {
|
|||
computed: {
|
||||
isLoggedIn () {
|
||||
return !!this.$store.state.users.currentUser
|
||||
},
|
||||
open () {
|
||||
return this.$store.state.interface.settingsModalState !== 'hidden'
|
||||
}
|
||||
},
|
||||
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.data && elm.data.attrs['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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
<div
|
||||
:label="$t('settings.general')"
|
||||
icon="wrench"
|
||||
data-tab-name="general"
|
||||
>
|
||||
<GeneralTab />
|
||||
</div>
|
||||
|
@ -15,6 +16,7 @@
|
|||
v-if="isLoggedIn"
|
||||
:label="$t('settings.profile_tab')"
|
||||
icon="user"
|
||||
data-tab-name="profile"
|
||||
>
|
||||
<ProfileTab />
|
||||
</div>
|
||||
|
@ -22,18 +24,21 @@
|
|||
v-if="isLoggedIn"
|
||||
:label="$t('settings.security_tab')"
|
||||
icon="lock"
|
||||
data-tab-name="security"
|
||||
>
|
||||
<SecurityTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.filtering')"
|
||||
icon="filter"
|
||||
data-tab-name="filtering"
|
||||
>
|
||||
<FilteringTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.theme')"
|
||||
icon="brush"
|
||||
data-tab-name="theme"
|
||||
>
|
||||
<ThemeTab />
|
||||
</div>
|
||||
|
@ -41,6 +46,7 @@
|
|||
v-if="isLoggedIn"
|
||||
:label="$t('settings.notifications')"
|
||||
icon="bell-ringing-o"
|
||||
data-tab-name="notifications"
|
||||
>
|
||||
<NotificationsTab />
|
||||
</div>
|
||||
|
@ -48,6 +54,7 @@
|
|||
v-if="isLoggedIn"
|
||||
:label="$t('settings.data_import_export_tab')"
|
||||
icon="download"
|
||||
data-tab-name="dataImportExport"
|
||||
>
|
||||
<DataImportExportTab />
|
||||
</div>
|
||||
|
@ -56,12 +63,14 @@
|
|||
:label="$t('settings.mutes_and_blocks')"
|
||||
:fullHeight="true"
|
||||
icon="eye-off"
|
||||
data-tab-name="mutesAndBlocks"
|
||||
>
|
||||
<MutesAndBlocksTab />
|
||||
</div>
|
||||
<div
|
||||
:label="$t('settings.version.title')"
|
||||
icon="info-circled"
|
||||
data-tab-name="version"
|
||||
>
|
||||
<VersionTab />
|
||||
</div>
|
||||
|
|
|
@ -278,7 +278,7 @@
|
|||
/>
|
||||
<ContrastRatio
|
||||
:contrast="previewContrast.alertErrorText"
|
||||
large="true"
|
||||
large
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="alertWarningColorLocal"
|
||||
|
@ -294,7 +294,7 @@
|
|||
/>
|
||||
<ContrastRatio
|
||||
:contrast="previewContrast.alertWarningText"
|
||||
large="true"
|
||||
large
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="alertNeutralColorLocal"
|
||||
|
@ -310,7 +310,7 @@
|
|||
/>
|
||||
<ContrastRatio
|
||||
:contrast="previewContrast.alertNeutralText"
|
||||
large="true"
|
||||
large
|
||||
/>
|
||||
<OpacityInput
|
||||
v-model="alertOpacityLocal"
|
||||
|
@ -334,7 +334,7 @@
|
|||
/>
|
||||
<ContrastRatio
|
||||
:contrast="previewContrast.badgeNotificationText"
|
||||
large="true"
|
||||
large
|
||||
/>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
|
@ -359,7 +359,7 @@
|
|||
/>
|
||||
<ContrastRatio
|
||||
:contrast="previewContrast.panelText"
|
||||
large="true"
|
||||
large
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="panelLinkColorLocal"
|
||||
|
@ -369,7 +369,7 @@
|
|||
/>
|
||||
<ContrastRatio
|
||||
:contrast="previewContrast.panelLink"
|
||||
large="true"
|
||||
large
|
||||
/>
|
||||
</div>
|
||||
<div class="color-item">
|
||||
|
@ -740,57 +740,57 @@
|
|||
<ColorInput
|
||||
v-model="chatBgColorLocal"
|
||||
name="chatBgColor"
|
||||
:fallback="previewTheme.colors.bg || 1"
|
||||
:fallback="previewTheme.colors.bg"
|
||||
:label="$t('settings.background')"
|
||||
/>
|
||||
<h5>{{ $t('settings.style.advanced_colors.chat.incoming') }}</h5>
|
||||
<ColorInput
|
||||
v-model="chatMessageIncomingBgColorLocal"
|
||||
name="chatMessageIncomingBgColor"
|
||||
:fallback="previewTheme.colors.bg || 1"
|
||||
:fallback="previewTheme.colors.bg"
|
||||
:label="$t('settings.background')"
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="chatMessageIncomingTextColorLocal"
|
||||
name="chatMessageIncomingTextColor"
|
||||
:fallback="previewTheme.colors.text || 1"
|
||||
:fallback="previewTheme.colors.text"
|
||||
:label="$t('settings.text')"
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="chatMessageIncomingLinkColorLocal"
|
||||
name="chatMessageIncomingLinkColor"
|
||||
:fallback="previewTheme.colors.link || 1"
|
||||
:fallback="previewTheme.colors.link"
|
||||
:label="$t('settings.links')"
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="chatMessageIncomingBorderColorLocal"
|
||||
name="chatMessageIncomingBorderLinkColor"
|
||||
:fallback="previewTheme.colors.fg || 1"
|
||||
:fallback="previewTheme.colors.fg"
|
||||
:label="$t('settings.style.advanced_colors.chat.border')"
|
||||
/>
|
||||
<h5>{{ $t('settings.style.advanced_colors.chat.outgoing') }}</h5>
|
||||
<ColorInput
|
||||
v-model="chatMessageOutgoingBgColorLocal"
|
||||
name="chatMessageOutgoingBgColor"
|
||||
:fallback="previewTheme.colors.bg || 1"
|
||||
:fallback="previewTheme.colors.bg"
|
||||
:label="$t('settings.background')"
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="chatMessageOutgoingTextColorLocal"
|
||||
name="chatMessageOutgoingTextColor"
|
||||
:fallback="previewTheme.colors.text || 1"
|
||||
:fallback="previewTheme.colors.text"
|
||||
:label="$t('settings.text')"
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="chatMessageOutgoingLinkColorLocal"
|
||||
name="chatMessageOutgoingLinkColor"
|
||||
:fallback="previewTheme.colors.link || 1"
|
||||
:fallback="previewTheme.colors.link"
|
||||
:label="$t('settings.links')"
|
||||
/>
|
||||
<ColorInput
|
||||
v-model="chatMessageOutgoingBorderColorLocal"
|
||||
name="chatMessageOutgoingBorderLinkColor"
|
||||
:fallback="previewTheme.colors.bg || 1"
|
||||
:fallback="previewTheme.colors.bg"
|
||||
:label="$t('settings.style.advanced_colors.chat.border')"
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
@click="toggleDrawer"
|
||||
>
|
||||
<router-link :to="{ name: 'chat' }">
|
||||
<i class="button-icon icon-chat" /> {{ $t("nav.chat") }}
|
||||
<i class="button-icon icon-megaphone" /> {{ $t("shoutbox.title") }}
|
||||
</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -71,6 +71,10 @@
|
|||
v-if="attachmentTypes.includes('unknown')"
|
||||
class="icon-doc"
|
||||
/>
|
||||
<span
|
||||
v-if="status.poll && status.poll.options"
|
||||
class="icon-chart-bar"
|
||||
/>
|
||||
<span
|
||||
v-if="status.card"
|
||||
class="icon-link"
|
||||
|
@ -86,7 +90,7 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<div v-if="status.poll && status.poll.options">
|
||||
<div v-if="status.poll && status.poll.options && !hideSubjectStatus">
|
||||
<poll :base-poll="status.poll" />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -60,16 +60,19 @@ export default Vue.component('tab-switcher', {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
activateTab (index) {
|
||||
clickTab (index) {
|
||||
return (e) => {
|
||||
e.preventDefault()
|
||||
if (typeof this.onSwitch === 'function') {
|
||||
this.onSwitch.call(null, this.$slots.default[index].key)
|
||||
}
|
||||
this.active = index
|
||||
if (this.scrollableTabs) {
|
||||
this.$refs.contents.scrollTop = 0
|
||||
}
|
||||
this.setTab(index)
|
||||
}
|
||||
},
|
||||
setTab (index) {
|
||||
if (typeof this.onSwitch === 'function') {
|
||||
this.onSwitch.call(null, this.$slots.default[index].key)
|
||||
}
|
||||
this.active = index
|
||||
if (this.scrollableTabs) {
|
||||
this.$refs.contents.scrollTop = 0
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -88,7 +91,7 @@ export default Vue.component('tab-switcher', {
|
|||
<div class={classesWrapper.join(' ')}>
|
||||
<button
|
||||
disabled={slot.data.attrs.disabled}
|
||||
onClick={this.activateTab(index)}
|
||||
onClick={this.clickTab(index)}
|
||||
class={classesTab.join(' ')}>
|
||||
<img src={slot.data.attrs.image} title={slot.data.attrs['image-tooltip']}/>
|
||||
{slot.data.attrs.label ? '' : slot.data.attrs.label}
|
||||
|
@ -100,7 +103,7 @@ export default Vue.component('tab-switcher', {
|
|||
<div class={classesWrapper.join(' ')}>
|
||||
<button
|
||||
disabled={slot.data.attrs.disabled}
|
||||
onClick={this.activateTab(index)}
|
||||
onClick={this.clickTab(index)}
|
||||
class={classesTab.join(' ')}
|
||||
type="button"
|
||||
>
|
||||
|
|
|
@ -354,7 +354,7 @@
|
|||
align-items: flex-start;
|
||||
max-height: 56px;
|
||||
|
||||
.avatar {
|
||||
.Avatar {
|
||||
flex: 1 0 100%;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
|
@ -364,7 +364,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
&:hover .avatar {
|
||||
&:hover .Avatar {
|
||||
--still-image-img: visible;
|
||||
--still-image-canvas: hidden;
|
||||
}
|
||||
|
|
|
@ -478,7 +478,6 @@
|
|||
"placeholder": "Dein Benutzername oder die zugehörige E-Mail-Adresse",
|
||||
"check_email": "Im E-Mail-Posteingang des angebenen Kontos müsste sich jetzt (oder zumindest in Kürze) die E-Mail mit dem Link zum Passwortzurücksetzen befinden.",
|
||||
"return_home": "Zurück zur Heimseite",
|
||||
"not_found": "Benutzername/E-Mail-Adresse nicht gefunden. Vertippt?",
|
||||
"too_many_requests": "Kurze Pause. Zu viele Versuche. Bitte, später nochmal probieren.",
|
||||
"password_reset_disabled": "Passwortzurücksetzen deaktiviert. Bitte Administrator kontaktieren.",
|
||||
"password_reset_required": "Passwortzurücksetzen erforderlich.",
|
||||
|
|
|
@ -113,7 +113,6 @@
|
|||
"about": "About",
|
||||
"administration": "Administration",
|
||||
"back": "Back",
|
||||
"chat": "Local Chat",
|
||||
"friend_requests": "Follow Requests",
|
||||
"mentions": "Mentions",
|
||||
"interactions": "Interactions",
|
||||
|
@ -775,7 +774,6 @@
|
|||
"placeholder": "Your email or username",
|
||||
"check_email": "Check your email for a link to reset your password.",
|
||||
"return_home": "Return to the home page",
|
||||
"not_found": "We couldn't find that email or username.",
|
||||
"too_many_requests": "You have reached the limit of attempts, try again later.",
|
||||
"password_reset_disabled": "Password reset is disabled. Please contact your instance administrator.",
|
||||
"password_reset_required": "You must reset your password to log in.",
|
||||
|
|
|
@ -776,7 +776,6 @@
|
|||
"password_reset_required": "Vi devas restarigi vian pasvorton por saluti.",
|
||||
"password_reset_disabled": "Restarigado de pasvortoj estas malŝaltita. Bonvolu kontakti la administranton de via nodo.",
|
||||
"too_many_requests": "Vi atingis la limon de provoj, reprovu pli poste.",
|
||||
"not_found": "Ni ne trovis tiun retpoŝtadreson aŭ uzantonomon.",
|
||||
"return_home": "Reiri al la hejmpaĝo",
|
||||
"check_email": "Kontrolu vian retpoŝton pro ligilo por restarigi vian pasvorton.",
|
||||
"placeholder": "Via retpoŝtadreso aŭ uzantonomo",
|
||||
|
|
244
src/i18n/es.json
244
src/i18n/es.json
|
@ -13,7 +13,8 @@
|
|||
"scope_options": "Opciones del alcance de la visibilidad",
|
||||
"text_limit": "Límite de caracteres",
|
||||
"title": "Características",
|
||||
"who_to_follow": "A quién seguir"
|
||||
"who_to_follow": "A quién seguir",
|
||||
"pleroma_chat_messages": "Chat de Pleroma"
|
||||
},
|
||||
"finder": {
|
||||
"error_fetching_user": "Error al buscar usuario",
|
||||
|
@ -31,7 +32,13 @@
|
|||
"disable": "Inhabilitar",
|
||||
"enable": "Habilitar",
|
||||
"confirm": "Confirmar",
|
||||
"verify": "Verificar"
|
||||
"verify": "Verificar",
|
||||
"peek": "Ojear",
|
||||
"close": "Cerrar",
|
||||
"dismiss": "Descartar",
|
||||
"retry": "Inténtalo de nuevo",
|
||||
"error_retry": "Por favor, inténtalo de nuevo",
|
||||
"loading": "Cargando…"
|
||||
},
|
||||
"image_cropper": {
|
||||
"crop_picture": "Recortar la foto",
|
||||
|
@ -41,7 +48,7 @@
|
|||
},
|
||||
"importer": {
|
||||
"submit": "Enviar",
|
||||
"success": "Importado con éxito",
|
||||
"success": "Importado con éxito.",
|
||||
"error": "Se ha producido un error al importar el archivo."
|
||||
},
|
||||
"login": {
|
||||
|
@ -77,21 +84,27 @@
|
|||
"dms": "Mensajes Directos",
|
||||
"public_tl": "Línea Temporal Pública",
|
||||
"timeline": "Línea Temporal",
|
||||
"twkn": "Toda La Red Conocida",
|
||||
"twkn": "Red Conocida",
|
||||
"user_search": "Búsqueda de Usuarios",
|
||||
"search": "Buscar",
|
||||
"who_to_follow": "A quién seguir",
|
||||
"preferences": "Preferencias"
|
||||
"preferences": "Preferencias",
|
||||
"chats": "Chats",
|
||||
"timelines": "Líneas de Tiempo",
|
||||
"bookmarks": "Marcadores"
|
||||
},
|
||||
"notifications": {
|
||||
"broken_favorite": "Estado desconocido, buscándolo...",
|
||||
"broken_favorite": "Estado desconocido, buscándolo…",
|
||||
"favorited_you": "le gusta tu estado",
|
||||
"followed_you": "empezó a seguirte",
|
||||
"load_older": "Cargar notificaciones antiguas",
|
||||
"notifications": "Notificaciones",
|
||||
"read": "¡Leído!",
|
||||
"repeated_you": "repitió tu estado",
|
||||
"no_more_notifications": "No hay más notificaciones"
|
||||
"no_more_notifications": "No hay más notificaciones",
|
||||
"reacted_with": "reaccionó con {0}",
|
||||
"migrated_to": "migrado a",
|
||||
"follow_request": "quiere seguirte"
|
||||
},
|
||||
"polls": {
|
||||
"add_poll": "Añadir encuesta",
|
||||
|
@ -114,7 +127,9 @@
|
|||
"search_emoji": "Buscar un emoji",
|
||||
"add_emoji": "Insertar un emoji",
|
||||
"custom": "Emojis personalizados",
|
||||
"unicode": "Emojis unicode"
|
||||
"unicode": "Emojis unicode",
|
||||
"load_all": "Cargando todos los {emojiAmount} emoji",
|
||||
"load_all_hint": "Cargado el primer emoji {saneAmount}, cargar todos los emoji puede causar problemas de rendimiento."
|
||||
},
|
||||
"stickers": {
|
||||
"add_sticker": "Añadir Pegatina"
|
||||
|
@ -122,7 +137,8 @@
|
|||
"interactions": {
|
||||
"favs_repeats": "Favoritos y Repetidos",
|
||||
"follows": "Nuevos seguidores",
|
||||
"load_older": "Cargar interacciones más antiguas"
|
||||
"load_older": "Cargar interacciones más antiguas",
|
||||
"moves": "Usuario Migrado"
|
||||
},
|
||||
"post_status": {
|
||||
"new_status": "Publicar un nuevo estado",
|
||||
|
@ -142,7 +158,7 @@
|
|||
"posting": "Publicando",
|
||||
"scope_notice": {
|
||||
"public": "Esta publicación será visible para todo el mundo",
|
||||
"private": "Esta publicación solo será visible para tus seguidores.",
|
||||
"private": "Esta publicación solo será visible para tus seguidores",
|
||||
"unlisted": "Esta publicación no será visible en la Línea Temporal Pública ni en Toda La Red Conocida"
|
||||
},
|
||||
"scope": {
|
||||
|
@ -150,7 +166,12 @@
|
|||
"private": "Solo-seguidores - Solo tus seguidores leerán la publicación",
|
||||
"public": "Público - Entradas visibles en las Líneas Temporales Públicas",
|
||||
"unlisted": "Sin listar - Entradas no visibles en las Líneas Temporales Públicas"
|
||||
}
|
||||
},
|
||||
"media_description_error": "Error al actualizar el archivo, inténtalo de nuevo",
|
||||
"empty_status_error": "No se puede publicar un estado vacío y sin archivos adjuntos",
|
||||
"preview_empty": "Vacío",
|
||||
"preview": "Vista previa",
|
||||
"media_description": "Descripción multimedia"
|
||||
},
|
||||
"registration": {
|
||||
"bio": "Biografía",
|
||||
|
@ -189,7 +210,7 @@
|
|||
"generate_new_recovery_codes": "Generar códigos de recuperación nuevos",
|
||||
"warning_of_generate_new_codes": "Cuando generas nuevos códigos de recuperación, los antiguos dejarán de funcionar.",
|
||||
"recovery_codes": "Códigos de recuperación.",
|
||||
"waiting_a_recovery_codes": "Recibiendo códigos de respaldo",
|
||||
"waiting_a_recovery_codes": "Recibiendo códigos de respaldo…",
|
||||
"recovery_codes_warning": "Anote los códigos o guárdelos en un lugar seguro, de lo contrario no los volverá a ver. Si pierde el acceso a su aplicación 2FA y los códigos de recuperación, su cuenta quedará bloqueada.",
|
||||
"authentication_methods": "Métodos de autentificación",
|
||||
"scan": {
|
||||
|
@ -232,7 +253,7 @@
|
|||
"default_vis": "Alcance de visibilidad por defecto",
|
||||
"delete_account": "Eliminar la cuenta",
|
||||
"discoverable": "Permitir la aparición de esta cuenta en los resultados de búsqueda y otros servicios",
|
||||
"delete_account_description": "Eliminar para siempre la cuenta y todos los mensajes.",
|
||||
"delete_account_description": "Eliminar para siempre los datos y desactivar la cuenta.",
|
||||
"pad_emoji": "Rellenar con espacios al agregar emojis desde el selector",
|
||||
"delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el administrador de tu instancia.",
|
||||
"delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.",
|
||||
|
@ -253,7 +274,7 @@
|
|||
"max_thumbnails": "Cantidad máxima de miniaturas por publicación",
|
||||
"hide_isp": "Ocultar el panel específico de la instancia",
|
||||
"preload_images": "Precargar las imágenes",
|
||||
"use_one_click_nsfw": "Abrir los adjuntos NSFW con un solo click.",
|
||||
"use_one_click_nsfw": "Abrir los adjuntos NSFW con un solo click",
|
||||
"hide_post_stats": "Ocultar las estadísticas de las entradas (p.ej. el número de favoritos)",
|
||||
"hide_user_stats": "Ocultar las estadísticas del usuario (p.ej. el número de seguidores)",
|
||||
"hide_filtered_statuses": "Ocultar estados filtrados",
|
||||
|
@ -299,7 +320,7 @@
|
|||
"valid_until": "Válido hasta",
|
||||
"revoke_token": "Revocar",
|
||||
"panelRadius": "Paneles",
|
||||
"pause_on_unfocused": "Parar la transmisión cuando no estés en foco.",
|
||||
"pause_on_unfocused": "Parar la transmisión cuando no estés en foco",
|
||||
"presets": "Por defecto",
|
||||
"profile_background": "Fondo del Perfil",
|
||||
"profile_banner": "Cabecera del Perfil",
|
||||
|
@ -355,7 +376,24 @@
|
|||
"save_load_hint": "Las opciones \"Mantener\" conservan las opciones configuradas actualmente al seleccionar o cargar temas, también almacena dichas opciones al exportar un tema. Cuando se desactiven todas las casillas de verificación, el tema de exportación lo guardará todo.",
|
||||
"reset": "Reiniciar",
|
||||
"clear_all": "Limpiar todo",
|
||||
"clear_opacity": "Limpiar opacidad"
|
||||
"clear_opacity": "Limpiar opacidad",
|
||||
"help": {
|
||||
"snapshot_source_mismatch": "Conflicto de versiones: lo más probable es que el frontend se haya revertido y actualizado nuevamente, si cambió el tema con una versión anterior del frontend, lo más probable es que desee usar la versión anterior; de lo contrario, use la nueva versión.",
|
||||
"migration_napshot_gone": "Por alguna razón, faltaba la instantánea, algunas cosas podrían verse diferentes de lo que recuerdas.",
|
||||
"migration_snapshot_ok": "Solo para estar seguro, se cargó la instantánea del tema. Puede intentar cargar los datos del tema.",
|
||||
"fe_downgraded": "Versión de PleromaFE revertida.",
|
||||
"fe_upgraded": "El creador de temas de PleromaFE se actualizó después de la actualización de la versión.",
|
||||
"snapshot_missing": "No había ninguna instantánea del tema en el archivo, por lo que podría verse diferente de lo previsto originalmente.",
|
||||
"snapshot_present": "Se ha cargado una instantánea del tema, por lo que todos los valores se sobrescriben. De lo contrario, puede cargar el tema por completo.",
|
||||
"older_version_imported": "El archivo que ha importado se creó en una versión anterior del frontend actual.",
|
||||
"v2_imported": "El archivo que ha importado fue creado para un frontend más antiguo. Intentamos maximizar la compatibilidad, pero aún podría haber inconsistencias.",
|
||||
"future_version_imported": "El archivo que ha importado se creó para una versión más reciente del frontend.",
|
||||
"upgraded_from_v2": "PleromaFE se ha actualizado, el tema podría verse un poco diferente de lo que recuerdas."
|
||||
},
|
||||
"use_source": "Nueva versión",
|
||||
"use_snapshot": "Versión antigua",
|
||||
"keep_as_is": "Mantener como está",
|
||||
"load_theme": "Cargar tema"
|
||||
},
|
||||
"common": {
|
||||
"color": "Color",
|
||||
|
@ -390,7 +428,26 @@
|
|||
"borders": "Bordes",
|
||||
"buttons": "Botones",
|
||||
"inputs": "Campos de entrada",
|
||||
"faint_text": "Texto desvanecido"
|
||||
"faint_text": "Texto desvanecido",
|
||||
"alert_neutral": "Neutral",
|
||||
"chat": {
|
||||
"border": "Borde",
|
||||
"outgoing": "Salientes",
|
||||
"incoming": "Entrantes"
|
||||
},
|
||||
"tabs": "Pestañas",
|
||||
"toggled": "Intercambiado",
|
||||
"disabled": "Deshabilitado",
|
||||
"selectedMenu": "Elemento del menú seleccionado",
|
||||
"selectedPost": "Publicación seleccionada",
|
||||
"pressed": "Presionado",
|
||||
"highlight": "Elementos destacados",
|
||||
"icons": "Iconos",
|
||||
"poll": "Gráfico de la encuesta",
|
||||
"underlay": "Subrayado",
|
||||
"popover": "Sugerencias, menús, superposiciones",
|
||||
"post": "Publicaciones/Biografías de Usuarios",
|
||||
"alert_warning": "Precaución"
|
||||
},
|
||||
"radii": {
|
||||
"_tab_label": "Redondez"
|
||||
|
@ -423,7 +480,8 @@
|
|||
"buttonPressed": "Botón (presionado)",
|
||||
"buttonPressedHover": "Botón (presionado+encima)",
|
||||
"input": "Campo de entrada"
|
||||
}
|
||||
},
|
||||
"hintV3": "Para las sombras, también puede usar la notación {0} para usar otro espacio de color."
|
||||
},
|
||||
"fonts": {
|
||||
"_tab_label": "Fuentes",
|
||||
|
@ -458,7 +516,42 @@
|
|||
"title": "Versión",
|
||||
"backend_version": "Versión del Backend",
|
||||
"frontend_version": "Versión del Frontend"
|
||||
}
|
||||
},
|
||||
"notification_visibility_moves": "Usuario Migrado",
|
||||
"greentext": "Texto verde (meme arrows)",
|
||||
"notification_setting_hide_notification_contents": "Ocultar el remitente y el contenido de las notificaciones push",
|
||||
"notification_setting_privacy": "Privacidad",
|
||||
"notification_setting_block_from_strangers": "Bloquea las notificaciones de los usuarios que no sigues",
|
||||
"notification_setting_filters": "Filtros",
|
||||
"fun": "Divertido",
|
||||
"type_domains_to_mute": "Buscar dominios para silenciar",
|
||||
"useStreamingApiWarning": "(no recomendado, experimental, puede omitir publicaciones)",
|
||||
"useStreamingApi": "Recibir entradas y notificaciones en tiempo real",
|
||||
"user_mutes": "Usuarios",
|
||||
"reset_profile_background": "Restablecer el fondo de pantalla",
|
||||
"reset_background_confirm": "¿Estás seguro de restablecer el fondo de pantalla?",
|
||||
"reset_banner_confirm": "¿Estás seguro de restablecer la imagen del banner?",
|
||||
"reset_avatar_confirm": "¿Estás seguro de restablecer la imagen de avatar?",
|
||||
"reset_profile_banner": "Restabler imagen del banner del perfil",
|
||||
"reset_avatar": "Restablecer avatar",
|
||||
"notification_visibility_emoji_reactions": "Reacciones",
|
||||
"new_email": "Nuevo correo electrónico",
|
||||
"profile_fields": {
|
||||
"value": "Contenido",
|
||||
"name": "Etiqueta",
|
||||
"add_field": "Añadir un campo",
|
||||
"label": "Metadatos del perfil"
|
||||
},
|
||||
"accent": "Acento",
|
||||
"emoji_reactions_on_timeline": "Mostrar las reacciones de emoji en la línea de tiempo",
|
||||
"domain_mutes": "Dominios",
|
||||
"mutes_and_blocks": "Silenciado y Bloqueados",
|
||||
"chatMessageRadius": "Mensaje de chat",
|
||||
"changed_email": "¡Correo electrónico modificado correctamente!",
|
||||
"change_email_error": "Ha ocurrido un error al intentar modificar tu correo electrónico.",
|
||||
"change_email": "Modificar el correo electrónico",
|
||||
"bot": "Esta cuenta es un bot",
|
||||
"allow_following_move": "Permitir el seguimiento automático, cuando la cuenta que sigues se traslada a otra instancia"
|
||||
},
|
||||
"time": {
|
||||
"day": "{0} día",
|
||||
|
@ -504,7 +597,8 @@
|
|||
"show_new": "Mostrar lo nuevo",
|
||||
"up_to_date": "Actualizado",
|
||||
"no_more_statuses": "No hay más estados",
|
||||
"no_statuses": "Sin estados"
|
||||
"no_statuses": "Sin estados",
|
||||
"reload": "Recargar"
|
||||
},
|
||||
"status": {
|
||||
"favorites": "Favoritos",
|
||||
|
@ -517,7 +611,17 @@
|
|||
"reply_to": "Respondiendo a",
|
||||
"replies_list": "Respuestas:",
|
||||
"mute_conversation": "Silenciar la conversación",
|
||||
"unmute_conversation": "Mostrar la conversación"
|
||||
"unmute_conversation": "Mostrar la conversación",
|
||||
"hide_content": "Ocultar el contenido",
|
||||
"show_content": "Mostrar el contenido",
|
||||
"hide_full_subject": "Ocultar el tema completo",
|
||||
"show_full_subject": "Mostrar el tema completo",
|
||||
"thread_muted_and_words": ", contiene:",
|
||||
"thread_muted": "Conversación silenciada",
|
||||
"copy_link": "Copiar el enlace al estado",
|
||||
"status_unavailable": "Estado no disponible",
|
||||
"bookmark": "Marcar",
|
||||
"unbookmark": "Desmarcar"
|
||||
},
|
||||
"user_card": {
|
||||
"approve": "Aprobar",
|
||||
|
@ -546,11 +650,11 @@
|
|||
"subscribe": "Suscribirse",
|
||||
"unsubscribe": "Desuscribirse",
|
||||
"unblock": "Desbloquear",
|
||||
"unblock_progress": "Desbloqueando...",
|
||||
"block_progress": "Bloqueando...",
|
||||
"unmute": "Quitar silencio",
|
||||
"unmute_progress": "Quitando silencio...",
|
||||
"mute_progress": "Silenciando...",
|
||||
"unblock_progress": "Desbloqueando…",
|
||||
"block_progress": "Bloqueando…",
|
||||
"unmute": "Dejar de silenciar",
|
||||
"unmute_progress": "Quitando silencio…",
|
||||
"mute_progress": "Silenciando…",
|
||||
"admin_menu": {
|
||||
"moderation": "Moderación",
|
||||
"grant_admin": "Conceder permisos de Administrador",
|
||||
|
@ -564,12 +668,16 @@
|
|||
"strip_media": "Eliminar archivos multimedia de las publicaciones",
|
||||
"force_unlisted": "Forzar que se publique en el modo -Sin Listar-",
|
||||
"sandbox": "Forzar que se publique solo para tus seguidores",
|
||||
"disable_remote_subscription": "No permitir que usuarios de instancias remotas te siga.",
|
||||
"disable_remote_subscription": "No permitir que usuarios de instancias remotas te siga",
|
||||
"disable_any_subscription": "No permitir que ningún usuario te siga",
|
||||
"quarantine": "No permitir publicaciones de usuarios de instancias remotas",
|
||||
"delete_user": "Eliminar usuario",
|
||||
"delete_user_confirmation": "¿Estás completamente seguro? Esta acción no se puede deshacer."
|
||||
}
|
||||
},
|
||||
"show_repeats": "Mostrar repetidos",
|
||||
"hide_repeats": "Ocultar repetidos",
|
||||
"message": "Mensaje",
|
||||
"hidden": "Oculto"
|
||||
},
|
||||
"user_profile": {
|
||||
"timeline_title": "Linea Temporal del Usuario",
|
||||
|
@ -594,7 +702,11 @@
|
|||
"repeat": "Repetir",
|
||||
"reply": "Contestar",
|
||||
"favorite": "Favorito",
|
||||
"user_settings": "Ajustes de usuario"
|
||||
"user_settings": "Ajustes de usuario",
|
||||
"bookmark": "Marcador",
|
||||
"reject_follow_request": "Rechazar la solicitud de seguimiento",
|
||||
"accept_follow_request": "Aceptar la solicitud de seguimiento",
|
||||
"add_reaction": "Añadir Reacción"
|
||||
},
|
||||
"upload": {
|
||||
"error": {
|
||||
|
@ -624,8 +736,78 @@
|
|||
"placeholder": "Su correo electrónico o nombre de usuario",
|
||||
"check_email": "Revise su correo electrónico para obtener un enlace para restablecer su contraseña.",
|
||||
"return_home": "Volver a la página de inicio",
|
||||
"not_found": "No pudimos encontrar ese correo electrónico o nombre de usuario.",
|
||||
"too_many_requests": "Has alcanzado el límite de intentos, vuelve a intentarlo más tarde.",
|
||||
"password_reset_disabled": "El restablecimiento de contraseñas está deshabilitado. Póngase en contacto con el administrador de su instancia."
|
||||
"password_reset_disabled": "El restablecimiento de contraseñas está deshabilitado. Póngase en contacto con el administrador de su instancia.",
|
||||
"password_reset_required_but_mailer_is_disabled": "Debes restablecer la contraseña, pero el restablecimiento de contraseñas está deshabilitado. Por favor contacta con el administrador de la instancia.",
|
||||
"password_reset_required": "Debes restablecer la contraseña para iniciar sesión."
|
||||
},
|
||||
"errors": {
|
||||
"storage_unavailable": "Pleroma no pudo acceder al almacenamiento del navegador. Su inicio de sesión o su configuración local no se guardarán y puede encontrar problemas inesperados. Intente habilitar las cookies."
|
||||
},
|
||||
"domain_mute_card": {
|
||||
"unmute_progress": "Quitando silencio…",
|
||||
"unmute": "Dejar de silenciar",
|
||||
"mute_progress": "Silenciando…",
|
||||
"mute": "Silenciar"
|
||||
},
|
||||
"about": {
|
||||
"mrf": {
|
||||
"simple": {
|
||||
"accept_desc": "Esta instancia solo acepta mensajes de las siguientes instancias:",
|
||||
"media_nsfw_desc": "Esta instancia obliga a que los archivos multimedia se establezcan como sensibles en las publicaciones de las siguientes instancias:",
|
||||
"media_nsfw": "Forzar Multimedia Como Sensible",
|
||||
"media_removal_desc": "Esta instancia elimina los archivos multimedia de las publicaciones de las siguientes instancias:",
|
||||
"media_removal": "Eliminar Multimedia",
|
||||
"quarantine": "Cuarentena",
|
||||
"ftl_removal_desc": "Esta instancia elimina las siguientes instancias de la línea de tiempo \"Toda la red conocida\":",
|
||||
"ftl_removal": "Eliminar de la línea de tiempo \"Toda La Red Conocida\"",
|
||||
"quarantine_desc": "Esta instancia enviará solo publicaciones públicas a las siguientes instancias:",
|
||||
"simple_policies": "Políticas sobre instancias específicas",
|
||||
"reject_desc": "Esta instancia no aceptará mensajes de las siguientes instancias:",
|
||||
"reject": "Rechazar",
|
||||
"accept": "Aceptar"
|
||||
},
|
||||
"mrf_policies_desc": "Las políticas MRF manipulan la federación de esta instancia con el resto del fediverso. Las siguientes políticas están habilitadas:",
|
||||
"mrf_policies": "Habilitar políticas MRF",
|
||||
"keyword": {
|
||||
"ftl_removal": "Eliminar de la línea de tiempo \"Toda La Red Conocida\"",
|
||||
"keyword_policies": "Política de Palabras Clave",
|
||||
"is_replaced_by": "→",
|
||||
"replace": "Reemplazar",
|
||||
"reject": "Rechazar"
|
||||
},
|
||||
"federation": "Federación"
|
||||
},
|
||||
"staff": "Equipo"
|
||||
},
|
||||
"shoutbox": {
|
||||
"title": "Jaula de Grillos"
|
||||
},
|
||||
"remote_user_resolver": {
|
||||
"remote_user_resolver": "Resolución de usuario remoto",
|
||||
"error": "No encontrado.",
|
||||
"searching_for": "Buscando"
|
||||
},
|
||||
"chats": {
|
||||
"chats": "Chats",
|
||||
"empty_chat_list_placeholder": "Aún no tienes ninguna conversación. ¡Inicia una nueva conversación!",
|
||||
"error_sending_message": "Algo salió mal al enviar el mensaje.",
|
||||
"error_loading_chat": "Algo salió mal al cargar el chat.",
|
||||
"delete_confirm": "¿Realmente quieres borrar este mensaje?",
|
||||
"more": "Más",
|
||||
"empty_message_error": "No puedes publicar un mensaje vacío",
|
||||
"new": "Nueva conversación",
|
||||
"delete": "Borrar",
|
||||
"message_user": "Mensaje de {nickname}",
|
||||
"you": "Tú:"
|
||||
},
|
||||
"display_date": {
|
||||
"today": "Hoy"
|
||||
},
|
||||
"file_type": {
|
||||
"file": "Archivo",
|
||||
"image": "Imagen",
|
||||
"video": "Vídeo",
|
||||
"audio": "Audio"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@
|
|||
"default_vis": "Lehenetsitako ikusgaitasunak",
|
||||
"delete_account": "Ezabatu kontua",
|
||||
"discoverable": "Baimendu zure kontua kanpo bilaketa-emaitzetan eta bestelako zerbitzuetan agertzea",
|
||||
"delete_account_description": "Betirako ezabatu zure kontua eta zure mezu guztiak",
|
||||
"delete_account_description": "Betirako ezabatu zure datuak eta desaktibatu kontua.",
|
||||
"pad_emoji": "Zuriuneak gehitu emoji bat aukeratzen denean",
|
||||
"delete_account_error": "Arazo bat gertatu da zure kontua ezabatzerakoan. Arazoa jarraitu eskero, administratzailearekin harremanetan jarri.",
|
||||
"delete_account_instructions": "Idatzi zure pasahitza kontua ezabatzeko.",
|
||||
|
@ -626,10 +626,17 @@
|
|||
"placeholder": "Zure e-posta edo erabiltzaile izena",
|
||||
"check_email": "Begiratu zure posta elektronikoa pasahitza berrezarri ahal izateko.",
|
||||
"return_home": "Itzuli hasierara",
|
||||
"not_found": "Ezin izan dugu helbide elektroniko edo erabiltzaile hori aurkitu.",
|
||||
"too_many_requests": "Saiakera gehiegi burutu ditzu, saiatu berriro geroxeago.",
|
||||
"password_reset_disabled": "Pasahitza berrezartzea debekatuta dago. Mesedez, jarri harremanetan instantzia administratzailearekin.",
|
||||
"password_reset_required": "Pasahitza berrezarri behar duzu saioa hasteko.",
|
||||
"password_reset_required_but_mailer_is_disabled": "Pasahitza berrezarri behar duzu, baina pasahitza berrezartzeko aukera desgaituta dago. Mesedez, jarri harremanetan instantziaren administratzailearekin."
|
||||
},
|
||||
"about": {
|
||||
"mrf": {
|
||||
"keyword": {
|
||||
"keyword_policies": "Gako-hitz politika"
|
||||
},
|
||||
"federation": "Federazioa"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -752,7 +752,6 @@
|
|||
"password_reset": "Salasanan nollaus",
|
||||
"placeholder": "Sähköpostiosoite tai käyttäjänimi",
|
||||
"return_home": "Palaa etusivulle",
|
||||
"not_found": "Sähköpostiosoitetta tai käyttäjänimeä ei löytynyt.",
|
||||
"too_many_requests": "Olet käyttänyt kaikki yritykset, yritä uudelleen myöhemmin.",
|
||||
"password_reset_required": "Sinun täytyy vaihtaa salasana kirjautuaksesi."
|
||||
},
|
||||
|
|
|
@ -730,7 +730,6 @@
|
|||
"instruction": "Entrer votre address de courriel ou votre nom utilisateur. Nous enverrons un lien pour changer votre mot de passe.",
|
||||
"placeholder": "Votre email ou nom d'utilisateur",
|
||||
"return_home": "Retourner à la page d'accueil",
|
||||
"not_found": "Email ou nom d'utilisateur inconnu.",
|
||||
"too_many_requests": "Vos avez atteint la limite d'essais, essayez plus tard.",
|
||||
"password_reset_required": "Vous devez changer votre mot de passe pour vous authentifier."
|
||||
}
|
||||
|
|
|
@ -745,7 +745,6 @@
|
|||
"password_reset_required": "Devi reimpostare la tua password per poter continuare.",
|
||||
"password_reset_disabled": "Non puoi azzerare la tua password. Contatta il tuo amministratore.",
|
||||
"too_many_requests": "Hai raggiunto il numero massimo di tentativi, riprova più tardi.",
|
||||
"not_found": "Non ho trovato questa email o nome utente.",
|
||||
"return_home": "Torna alla pagina principale",
|
||||
"check_email": "Controlla la tua posta elettronica.",
|
||||
"placeholder": "La tua email o nome utente",
|
||||
|
|
|
@ -666,7 +666,6 @@
|
|||
"placeholder": "あなたのメールアドレスかユーザーめい",
|
||||
"check_email": "パスワードをリセットするためのリンクがかかれたメールが、とどいているかどうか、みてください。",
|
||||
"return_home": "ホームページにもどる",
|
||||
"not_found": "そのメールアドレスまたはユーザーめいを、みつけることができませんでした。",
|
||||
"too_many_requests": "パスワードリセットを、ためすことが、おおすぎます。しばらくしてから、ためしてください。",
|
||||
"password_reset_disabled": "このインスタンスでは、パスワードリセットは、できません。インスタンスのアドミニストレーターに、おといあわせください。",
|
||||
"password_reset_required": "ログインするには、パスワードをリセットしてください。",
|
||||
|
|
|
@ -625,7 +625,6 @@
|
|||
"placeholder": "メールアドレスまたはユーザー名",
|
||||
"check_email": "パスワードをリセットするためのリンクが記載されたメールが届いているか確認してください。",
|
||||
"return_home": "ホームページに戻る",
|
||||
"not_found": "メールアドレスまたはユーザー名が見つかりませんでした。",
|
||||
"too_many_requests": "試行回数の制限に達しました。しばらく時間を置いてから再試行してください。",
|
||||
"password_reset_disabled": "このインスタンスではパスワードリセットは無効になっています。インスタンスの管理者に連絡してください。"
|
||||
}
|
||||
|
|
|
@ -677,7 +677,6 @@
|
|||
"password_reset_required": "Je dient je wachtwoord opnieuw in te stellen om in te kunnen loggen.",
|
||||
"password_reset_disabled": "Wachtwoord reset is uitgeschakeld. Neem contact op met de beheerder van deze instantie.",
|
||||
"too_many_requests": "Je hebt het maximaal aantal pogingen bereikt, probeer het later opnieuw.",
|
||||
"not_found": "We kunnen die email of gebruikersnaam niet vinden.",
|
||||
"return_home": "Terugkeren naar de home pagina",
|
||||
"check_email": "Controleer je email inbox voor een link om je wachtwoord opnieuw in te stellen.",
|
||||
"placeholder": "Je email of gebruikersnaam",
|
||||
|
|
|
@ -753,7 +753,6 @@
|
|||
"placeholder": "Twój email lub nazwa użytkownika",
|
||||
"check_email": "Sprawdź pocztę, aby uzyskać link do zresetowania hasła.",
|
||||
"return_home": "Wróć do strony głównej",
|
||||
"not_found": "Nie mogliśmy znaleźć tego emaila lub nazwy użytkownika.",
|
||||
"too_many_requests": "Przekroczyłeś(-aś) limit prób, spróbuj ponownie później.",
|
||||
"password_reset_disabled": "Resetowanie hasła jest wyłączone. Proszę skontaktuj się z administratorem tej instancji.",
|
||||
"password_reset_required": "Musisz zresetować hasło, by się zalogować.",
|
||||
|
|
|
@ -420,7 +420,6 @@
|
|||
"placeholder": "Ваш email или имя пользователя",
|
||||
"check_email": "Проверьте ваш email и перейдите по ссылке для сброса пароля.",
|
||||
"return_home": "Вернуться на главную страницу",
|
||||
"not_found": "Мы не смогли найти аккаунт с таким email-ом или именем пользователя.",
|
||||
"too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.",
|
||||
"password_reset_disabled": "Сброс пароля отключен. Cвяжитесь с администратором вашего сервера."
|
||||
},
|
||||
|
|
|
@ -640,7 +640,6 @@
|
|||
"placeholder": "你的电邮地址或者用户名",
|
||||
"check_email": "检查你的邮箱,会有一个链接用于重置密码。",
|
||||
"return_home": "回到首页",
|
||||
"not_found": "我们无法找到匹配的邮箱地址或者用户名。",
|
||||
"too_many_requests": "你触发了尝试的限制,请稍后再试。",
|
||||
"password_reset_disabled": "密码重置已经被禁用。请联系你的实例管理员。"
|
||||
},
|
||||
|
|
|
@ -143,6 +143,7 @@ const chats = {
|
|||
const isNewMessage = (chat.lastMessage && chat.lastMessage.id) !== (updatedChat.lastMessage && updatedChat.lastMessage.id)
|
||||
chat.lastMessage = updatedChat.lastMessage
|
||||
chat.unread = updatedChat.unread
|
||||
chat.updated_at = updatedChat.updated_at
|
||||
if (isNewMessage && chat.unread) {
|
||||
newChatMessageSideEffects(updatedChat)
|
||||
}
|
||||
|
@ -181,30 +182,16 @@ const chats = {
|
|||
setChatsLoading (state, { value }) {
|
||||
state.chats.loading = value
|
||||
},
|
||||
addChatMessages (state, { commit, chatId, messages }) {
|
||||
addChatMessages (state, { chatId, messages, updateMaxId }) {
|
||||
const chatMessageService = state.openedChatMessageServices[chatId]
|
||||
if (chatMessageService) {
|
||||
chatService.add(chatMessageService, { messages: messages.map(parseChatMessage) })
|
||||
commit('refreshLastMessage', { chatId })
|
||||
chatService.add(chatMessageService, { messages: messages.map(parseChatMessage), updateMaxId })
|
||||
}
|
||||
},
|
||||
refreshLastMessage (state, { chatId }) {
|
||||
const chatMessageService = state.openedChatMessageServices[chatId]
|
||||
if (chatMessageService) {
|
||||
const chat = getChatById(state, chatId)
|
||||
if (chat) {
|
||||
chat.lastMessage = chatMessageService.lastMessage
|
||||
if (chatMessageService.lastMessage) {
|
||||
chat.updated_at = chatMessageService.lastMessage.created_at
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
deleteChatMessage (state, { commit, chatId, messageId }) {
|
||||
deleteChatMessage (state, { chatId, messageId }) {
|
||||
const chatMessageService = state.openedChatMessageServices[chatId]
|
||||
if (chatMessageService) {
|
||||
chatService.deleteMessage(chatMessageService, messageId)
|
||||
commit('refreshLastMessage', { chatId })
|
||||
}
|
||||
},
|
||||
resetChatNewMessageCount (state, _value) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { set, delete as del } from 'vue'
|
|||
const defaultState = {
|
||||
settingsModalState: 'hidden',
|
||||
settingsModalLoaded: false,
|
||||
settingsModalTargetTab: null,
|
||||
settings: {
|
||||
currentSaveStateNotice: null,
|
||||
noticeClearTimeout: null,
|
||||
|
@ -62,6 +63,9 @@ const interfaceMod = {
|
|||
state.settingsModalLoaded = true
|
||||
}
|
||||
},
|
||||
setSettingsModalTargetTab (state, value) {
|
||||
state.settingsModalTargetTab = value
|
||||
},
|
||||
pushGlobalNotice (state, notice) {
|
||||
state.globalNotices.push(notice)
|
||||
},
|
||||
|
@ -97,6 +101,13 @@ const interfaceMod = {
|
|||
togglePeekSettingsModal ({ commit }) {
|
||||
commit('togglePeekSettingsModal')
|
||||
},
|
||||
clearSettingsModalTargetTab ({ commit }) {
|
||||
commit('setSettingsModalTargetTab', null)
|
||||
},
|
||||
openSettingsModalTab ({ commit }, value) {
|
||||
commit('setSettingsModalTargetTab', value)
|
||||
commit('openSettingsModal')
|
||||
},
|
||||
pushGlobalNotice (
|
||||
{ commit, dispatch },
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ const empty = (chatId) => {
|
|||
lastSeenTimestamp: 0,
|
||||
chatId: chatId,
|
||||
minId: undefined,
|
||||
lastMessage: undefined
|
||||
maxId: undefined
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ const clear = (storage) => {
|
|||
storage.newMessageCount = 0
|
||||
storage.lastSeenTimestamp = 0
|
||||
storage.minId = undefined
|
||||
storage.lastMessage = undefined
|
||||
storage.maxId = undefined
|
||||
}
|
||||
|
||||
const deleteMessage = (storage, messageId) => {
|
||||
|
@ -26,8 +26,9 @@ const deleteMessage = (storage, messageId) => {
|
|||
storage.messages = storage.messages.filter(m => m.id !== messageId)
|
||||
delete storage.idIndex[messageId]
|
||||
|
||||
if (storage.lastMessage && (storage.lastMessage.id === messageId)) {
|
||||
storage.lastMessage = _.maxBy(storage.messages, 'id')
|
||||
if (storage.maxId === messageId) {
|
||||
const lastMessage = _.maxBy(storage.messages, 'id')
|
||||
storage.maxId = lastMessage.id
|
||||
}
|
||||
|
||||
if (storage.minId === messageId) {
|
||||
|
@ -36,7 +37,7 @@ const deleteMessage = (storage, messageId) => {
|
|||
}
|
||||
}
|
||||
|
||||
const add = (storage, { messages: newMessages }) => {
|
||||
const add = (storage, { messages: newMessages, updateMaxId = true }) => {
|
||||
if (!storage) { return }
|
||||
for (let i = 0; i < newMessages.length; i++) {
|
||||
const message = newMessages[i]
|
||||
|
@ -48,8 +49,10 @@ const add = (storage, { messages: newMessages }) => {
|
|||
storage.minId = message.id
|
||||
}
|
||||
|
||||
if (!storage.lastMessage || message.id > storage.lastMessage.id) {
|
||||
storage.lastMessage = message
|
||||
if (!storage.maxId || message.id > storage.maxId) {
|
||||
if (updateMaxId) {
|
||||
storage.maxId = message.id
|
||||
}
|
||||
}
|
||||
|
||||
if (!storage.idIndex[message.id]) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { showDesktopNotification } from '../desktop_notification_utils/desktop_n
|
|||
export const maybeShowChatNotification = (store, chat) => {
|
||||
if (!chat.lastMessage) return
|
||||
if (store.rootState.chats.currentChatId === chat.id && !document.hidden) return
|
||||
if (store.rootState.users.currentUser.id === chat.lastMessage.account.id) return
|
||||
|
||||
const opts = {
|
||||
tag: chat.lastMessage.id,
|
||||
|
|
|
@ -5,7 +5,7 @@ export const replaceWord = (str, toReplace, replacement) => {
|
|||
}
|
||||
|
||||
export const wordAtPosition = (str, pos) => {
|
||||
const words = splitIntoWords(str)
|
||||
const words = splitByWhitespaceBoundary(str)
|
||||
const wordsWithPosition = addPositionToWords(words)
|
||||
|
||||
return find(wordsWithPosition, ({ start, end }) => start <= pos && end > pos)
|
||||
|
@ -34,36 +34,36 @@ export const addPositionToWords = (words) => {
|
|||
}, [])
|
||||
}
|
||||
|
||||
export const splitIntoWords = (str) => {
|
||||
// Split at word boundaries
|
||||
const regex = /\b/
|
||||
const triggers = /[@#:]+$/
|
||||
|
||||
let split = str.split(regex)
|
||||
|
||||
// Add trailing @ and # to the following word.
|
||||
const words = reduce(split, (result, word) => {
|
||||
if (result.length > 0) {
|
||||
let previous = result.pop()
|
||||
const matches = previous.match(triggers)
|
||||
if (matches) {
|
||||
previous = previous.replace(triggers, '')
|
||||
word = matches[0] + word
|
||||
}
|
||||
result.push(previous)
|
||||
export const splitByWhitespaceBoundary = (str) => {
|
||||
let result = []
|
||||
let currentWord = ''
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const currentChar = str[i]
|
||||
// Starting a new word
|
||||
if (!currentWord) {
|
||||
currentWord = currentChar
|
||||
continue
|
||||
}
|
||||
result.push(word)
|
||||
|
||||
return result
|
||||
}, [])
|
||||
|
||||
return words
|
||||
// current character is whitespace while word isn't, or vice versa:
|
||||
// add our current word to results, start over the current word.
|
||||
if (!!currentChar.trim() !== !!currentWord.trim()) {
|
||||
result.push(currentWord)
|
||||
currentWord = currentChar
|
||||
continue
|
||||
}
|
||||
currentWord += currentChar
|
||||
}
|
||||
// Add the last word we were working on
|
||||
if (currentWord) {
|
||||
result.push(currentWord)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const completion = {
|
||||
wordAtPosition,
|
||||
addPositionToWords,
|
||||
splitIntoWords,
|
||||
splitByWhitespaceBoundary,
|
||||
replaceWord
|
||||
}
|
||||
|
||||
|
|
|
@ -405,6 +405,12 @@
|
|||
"css": "block",
|
||||
"code": 59434,
|
||||
"src": "fontawesome"
|
||||
},
|
||||
{
|
||||
"uid": "3e674995cacc2b09692c096ea7eb6165",
|
||||
"css": "megaphone",
|
||||
"code": 59435,
|
||||
"src": "fontawesome"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -33,12 +33,12 @@ describe('chatService', () => {
|
|||
const chat = chatService.empty()
|
||||
|
||||
chatService.add(chat, { messages: [ message1 ] })
|
||||
expect(chat.lastMessage.id).to.eql(message1.id)
|
||||
expect(chat.maxId).to.eql(message1.id)
|
||||
expect(chat.minId).to.eql(message1.id)
|
||||
expect(chat.newMessageCount).to.eql(1)
|
||||
|
||||
chatService.add(chat, { messages: [ message2 ] })
|
||||
expect(chat.lastMessage.id).to.eql(message2.id)
|
||||
expect(chat.maxId).to.eql(message2.id)
|
||||
expect(chat.minId).to.eql(message1.id)
|
||||
expect(chat.newMessageCount).to.eql(2)
|
||||
|
||||
|
@ -60,15 +60,15 @@ describe('chatService', () => {
|
|||
chatService.add(chat, { messages: [ message2 ] })
|
||||
chatService.add(chat, { messages: [ message3 ] })
|
||||
|
||||
expect(chat.lastMessage.id).to.eql(message3.id)
|
||||
expect(chat.maxId).to.eql(message3.id)
|
||||
expect(chat.minId).to.eql(message1.id)
|
||||
|
||||
chatService.deleteMessage(chat, message3.id)
|
||||
expect(chat.lastMessage.id).to.eql(message2.id)
|
||||
expect(chat.maxId).to.eql(message2.id)
|
||||
expect(chat.minId).to.eql(message1.id)
|
||||
|
||||
chatService.deleteMessage(chat, message1.id)
|
||||
expect(chat.lastMessage.id).to.eql(message2.id)
|
||||
expect(chat.maxId).to.eql(message2.id)
|
||||
expect(chat.minId).to.eql(message2.id)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { replaceWord, addPositionToWords, wordAtPosition, splitIntoWords } from '../../../../../src/services/completion/completion.js'
|
||||
import { replaceWord, addPositionToWords, wordAtPosition, splitByWhitespaceBoundary } from '../../../../../src/services/completion/completion.js'
|
||||
|
||||
describe('addPositiontoWords', () => {
|
||||
it('adds the position to a word list', () => {
|
||||
const words = ['hey', 'this', 'is', 'fun']
|
||||
const words = ['hey', ' ', 'this', ' ', 'is', ' ', 'fun']
|
||||
|
||||
const expected = [
|
||||
{
|
||||
|
@ -11,19 +11,34 @@ describe('addPositiontoWords', () => {
|
|||
end: 3
|
||||
},
|
||||
{
|
||||
word: 'this',
|
||||
word: ' ',
|
||||
start: 3,
|
||||
end: 7
|
||||
end: 4
|
||||
},
|
||||
{
|
||||
word: 'is',
|
||||
start: 7,
|
||||
word: 'this',
|
||||
start: 4,
|
||||
end: 8
|
||||
},
|
||||
{
|
||||
word: ' ',
|
||||
start: 8,
|
||||
end: 9
|
||||
},
|
||||
{
|
||||
word: 'fun',
|
||||
word: 'is',
|
||||
start: 9,
|
||||
end: 11
|
||||
},
|
||||
{
|
||||
word: ' ',
|
||||
start: 11,
|
||||
end: 12
|
||||
},
|
||||
{
|
||||
word: 'fun',
|
||||
start: 12,
|
||||
end: 15
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -33,11 +48,11 @@ describe('addPositiontoWords', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('splitIntoWords', () => {
|
||||
describe('splitByWhitespaceBoundary', () => {
|
||||
it('splits at whitespace boundaries', () => {
|
||||
const str = 'This is a #nice @test for you, @idiot.'
|
||||
const expected = ['This', ' ', 'is', ' ', 'a', ' ', '#nice', ' ', '@test', ' ', 'for', ' ', 'you', ', ', '@idiot', '.']
|
||||
const res = splitIntoWords(str)
|
||||
const str = 'This is a #nice @test for you, @idiot@idiot.com'
|
||||
const expected = ['This', ' ', 'is', ' ', 'a', ' ', '#nice', ' ', '@test', ' ', 'for', ' ', 'you,', ' ', '@idiot@idiot.com']
|
||||
const res = splitByWhitespaceBoundary(str)
|
||||
|
||||
expect(res).to.eql(expected)
|
||||
})
|
||||
|
@ -57,13 +72,13 @@ describe('wordAtPosition', () => {
|
|||
|
||||
describe('replaceWord', () => {
|
||||
it('replaces a word (with start and end) with another word in a given string', () => {
|
||||
const str = 'hey @take, how are you'
|
||||
const wordsWithPosition = addPositionToWords(splitIntoWords(str))
|
||||
const str = 'hey @take , how are you'
|
||||
const wordsWithPosition = addPositionToWords(splitByWhitespaceBoundary(str))
|
||||
const toReplace = wordsWithPosition[2]
|
||||
|
||||
expect(toReplace.word).to.eql('@take')
|
||||
|
||||
const expected = 'hey @takeshitakenji, how are you'
|
||||
const expected = 'hey @takeshitakenji , how are you'
|
||||
const res = replaceWord(str, toReplace, '@takeshitakenji')
|
||||
expect(res).to.eql(expected)
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue