fix conflicts

This commit is contained in:
Shpuld Shpuldson 2020-06-05 12:06:42 +03:00
commit 32b1ae15d2
79 changed files with 6406 additions and 3830 deletions

View file

@ -2,8 +2,42 @@
All notable changes to this project will be documented in this file. 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/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Changed
- Removed the use of with_move parameters when fetching notifications
## [Unreleased patch]
### Add
- 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
- Show more/less works correctly with auto-collapsed subjects and long posts
- RTL characters won't look messed up in notifications
### 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
- Follow request notification support
## [2.0.2] - 2020-04-08
### Fixed
- Favorite/Repeat avatars not showing up on private instances/non-public posts
- Autocorrect getting triggered in the captcha field
- Overflow on long domains in follow/move notifications
### Changed
- Polish translation updated
## [2.0.0] - 2020-02-28
### Added ### Added
- Tons of color slots including ones for hover/pressed/toggled buttons - Tons of color slots including ones for hover/pressed/toggled buttons
- Experimental `--variable[,mod]` syntax support for color slots in themes. the `mod` makes color brighter/darker depending on background color (makes darker color brighter/darker depending on background color) - Experimental `--variable[,mod]` syntax support for color slots in themes. the `mod` makes color brighter/darker depending on background color (makes darker color brighter/darker depending on background color)
@ -16,6 +50,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Emoji reactions for statuses - Emoji reactions for statuses
- MRF keyword policy disclosure - MRF keyword policy disclosure
### Changed ### Changed
- Updated Pleroma default themes
- theme engine update to 3 (themes v2.1 introduction) - theme engine update to 3 (themes v2.1 introduction)
- massive internal changes in theme engine - slowly away from "generate things separately with spaghetti code" towards "feed all data into single 'generateTheme' function and declare slot inheritance and all in a separate file" - massive internal changes in theme engine - slowly away from "generate things separately with spaghetti code" towards "feed all data into single 'generateTheme' function and declare slot inheritance and all in a separate file"
- Breezy theme updates to make it closer to actual Breeze in some aspects - Breezy theme updates to make it closer to actual Breeze in some aspects
@ -25,11 +60,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Notifications column now cleans itself up to optimize performance when tab is left open for a long time - Notifications column now cleans itself up to optimize performance when tab is left open for a long time
- 403 messaging - 403 messaging
### Fixed ### Fixed
- Fixed loader-spinner not disappearing when a status preview fails to load
- anon viewers won't get theme data saved to local storage, so admin changing default theme will have an effect for users coming back to instance. - anon viewers won't get theme data saved to local storage, so admin changing default theme will have an effect for users coming back to instance.
- Single notifications left unread when hitting read on another device/tab - Single notifications left unread when hitting read on another device/tab
- Registration fixed - Registration fixed
- Deactivation of remote accounts from frontend - Deactivation of remote accounts from frontend
- Fixed NSFW unhiding not working with videos when using one-click unhiding/displaying - Fixed NSFW unhiding not working with videos when using one-click unhiding/displaying
- Improved performance of anything that uses popovers (most notably statuses)
## [1.1.7 and earlier] - 2019-12-14 ## [1.1.7 and earlier] - 2019-12-14
### Added ### Added

View file

@ -1,6 +1,6 @@
# pleroma_fe # Pleroma-FE
> A single column frontend for both Pleroma and GS servers. > A single column frontend designed for Pleroma.
![screenshot](https://i.imgur.com/DJVqSJ0.png) ![screenshot](https://i.imgur.com/DJVqSJ0.png)
@ -11,7 +11,6 @@ To translate Pleroma-FE, add your language to [src/i18n/messages.js](https://git
# FOR ADMINS # FOR ADMINS
You don't need to build Pleroma-FE yourself. Those using the Pleroma backend will be able to use it out of the box. You don't need to build Pleroma-FE yourself. Those using the Pleroma backend will be able to use it out of the box.
For the GNU social backend, check out https://git.pleroma.social/pleroma/pleroma-fe/wikis/dual-boot-with-qvitter to see how to run Pleroma-FE and Qvitter at the same time.
## Build Setup ## Build Setup

View file

@ -19,32 +19,69 @@ There's currently no mechanism for user-settings synchronization across several
## Options ## Options
### `theme` ### `alwaysShowSubjectInput`
Default theme used for new users. De-facto instance-default, user can change theme. `true` - will always show subject line input, `false` - only show when it's not empty (i.e. replying). To hide subject line input completely, set it to `false` and `subjectLineBehavior` to `"noop"`
### `background` ### `background`
Default image background. Be aware of using too big images as they may take longer to load. Currently image is fitted with `background-size: cover` which means "scaled and cropped", currently left-aligned. De-facto instance default, user can choose their own background, if they remove their own background, instance default will be used instead. Default image background. Be aware of using too big images as they may take longer to load. Currently image is fitted with `background-size: cover` which means "scaled and cropped", currently left-aligned. De-facto instance default, user can choose their own background, if they remove their own background, instance default will be used instead.
### `collapseMessageWithSubject`
Collapse post content when post has a subject line (content warning). Instance-default.
### `disableChat`
hides the chat (TODO: even if it's enabled on backend)
### `greentext`
Changes lines prefixed with the `>` character to have a green text color
### `hideFilteredStatuses`
Removes filtered statuses from timelines.
### `hideMutedPosts`
Removes muted statuses from timelines.
### `hidePostStats`
Hide repeats/favorites counters for posts.
### `hideSitename`
Hide instance name in header.
### `hideUserStats`
Hide followers/friends counters for users.
### `loginMethod`
`"password"` - show simple password field
`"token"` - show button to log in with external method (will redirect to login form, more details in BE documentation)
### `logo`, `logoMask`, `logoMargin` ### `logo`, `logoMask`, `logoMargin`
Instance `logo`, could be any image, including svg. By default it assumes logo used will be monochrome-with-alpha one, this is done to be compatible with both light and dark themes, so that white logo designed with dark theme in mind won't be invisible over light theme, this is done via [CSS3 Masking](https://www.html5rocks.com/en/tutorials/masking/adobe/). Basically - it will take alpha channel of the image and fill non-transparent areas of it with solid color. If you really want colorful logo - it can be done by setting `logoMask` to `false`. Instance `logo`, could be any image, including svg. By default it assumes logo used will be monochrome-with-alpha one, this is done to be compatible with both light and dark themes, so that white logo designed with dark theme in mind won't be invisible over light theme, this is done via [CSS3 Masking](https://www.html5rocks.com/en/tutorials/masking/adobe/). Basically - it will take alpha channel of the image and fill non-transparent areas of it with solid color. If you really want colorful logo - it can be done by setting `logoMask` to `false`.
`logoMargin` allows you to adjust vertical margins between logo boundary and navbar borders. The idea is that to have logo's image without any extra margins and instead adjust them to your need in layout. `logoMargin` allows you to adjust vertical margins between logo boundary and navbar borders. The idea is that to have logo's image without any extra margins and instead adjust them to your need in layout.
### `minimalScopesMode`
Limit scope selection to *Direct*, *User default* and *Scope of post replying to*. This also makes it impossible to reply to a DM with a non-DM post from PleromaFE.
### `nsfwCensorImage`
Use custom image for NSFW'd images
### `postContentType`
Default post formatting option (markdown/bbcode/plaintext/etc...)
### `redirectRootNoLogin`, `redirectRootLogin` ### `redirectRootNoLogin`, `redirectRootLogin`
These two settings should point to where FE should redirect visitor when they login/open up website root These two settings should point to where FE should redirect visitor when they login/open up website root
### `chatDisabled` ### `scopeCopy`
hides the chat (TODO: even if it's enabled on backend) Copy post scope (visibility) when replying to a post. Instance-default.
### `sidebarRight`
Change alignment of sidebar and panels to the right. Defaults to `false`.
### `showFeaturesPanel`
Show panel showcasing instance features/settings to logged-out visitors
### `showInstanceSpecificPanel` ### `showInstanceSpecificPanel`
This allows you to include arbitrary HTML content in a panel below navigation menu. PleromaFE looks for an html page `instance/panel.html`, by default it's not provided in FE, but BE bundles some [default one](https://git.pleroma.social/pleroma/pleroma/blob/develop/priv/static/instance/panel.html). De-facto instance-defaults, since user can hide instance-specific panel. This allows you to include arbitrary HTML content in a panel below navigation menu. PleromaFE looks for an html page `instance/panel.html`, by default it's not provided in FE, but BE bundles some [default one](https://git.pleroma.social/pleroma/pleroma/blob/develop/priv/static/instance/panel.html). De-facto instance-defaults, since user can hide instance-specific panel.
### `collapseMessageWithSubject`
Collapse post content when post has a subject line (content warning). Instance-default.
### `scopeCopy`
Copy post scope (visibility) when replying to a post. Instance-default.
### `subjectLineBehavior` ### `subjectLineBehavior`
How to handle subject line (CW) when replying to a post. How to handle subject line (CW) when replying to a post.
* `"email"` - like EMail - prepend `re: ` to subject line if it doesn't already start with it. * `"email"` - like EMail - prepend `re: ` to subject line if it doesn't already start with it.
@ -52,39 +89,22 @@ How to handle subject line (CW) when replying to a post.
* `"noop"` - do not copy * `"noop"` - do not copy
Instance-default. Instance-default.
### `postContentType` ### `theme`
Default post formatting option (markdown/bbcode/plaintext/etc...) Default theme used for new users. De-facto instance-default, user can change theme.
### `alwaysShowSubjectInput`
`true` - will always show subject line input, `false` - only show when it's not empty (i.e. replying). To hide subject line input completely, set it to `false` and `subjectLineBehavior` to `"noop"`
### `hidePostStats` and `hideUserStats`
Hide counters for posts and users respectively, i.e. hiding repeats/favorites counts for posts, hiding followers/friends counts for users. This is just cosmetic and aimed to ease pressure and bias imposed by stat numbers of people and/or posts. (as an example: so that people care less about how many followers someone has since they can't see that info)
### `loginMethod`
`"password"` - show simple password field
`"token"` - show button to log in with external method (will redirect to login form, more details in BE documentation)
### `webPushNotifications` ### `webPushNotifications`
Enables [PushAPI](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) - based notifications for users. Instance-default. Enables [PushAPI](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) - based notifications for users. Instance-default.
### `noAttachmentLinks`
**TODO Currently doesn't seem to be doing anything code-wise**, but implication is to disable adding links for attachments, which looks nicer but breaks compatibility with old GNU/Social servers.
### `nsfwCensorImage`
Use custom image for NSFW'd images
### `showFeaturesPanel`
Show panel showcasing instance features/settings to logged-out visitors
### `hideSitename`
Hide instance name in header
## Indirect configuration ## Indirect configuration
Some features are configured depending on how backend is configured. In general the approach is "if backend allows it there's no need to hide it, if backend doesn't allow it there's no need to show it. Some features are configured depending on how backend is configured. In general the approach is "if backend allows it there's no need to hide it, if backend doesn't allow it there's no need to show it.
### Chat ### Chat
**TODO somewhat broken, see: chatDisabled** chat can be disabled by disabling it in backend **TODO somewhat broken, see: disableChat** chat can be disabled by disabling it in backend
### Private Mode
If the `private` instance setting is enabled in the backend, features that are not accessible without authentication, such as the timelines and search will be disabled for unauthenticated users.
### Rich text formatting in post formatting ### Rich text formatting in post formatting
Rich text formatting options are displayed depending on how many formatting options are enabled on backend, if you don't want your users to use rich text at all you can only allow "text/plain" one, frontend then will only display post text format as a label instead of dropdown (just so that users know for example if you only allow Markdown, only BBCode or only Plain text) Rich text formatting options are displayed depending on how many formatting options are enabled on backend, if you don't want your users to use rich text at all you can only allow "text/plain" one, frontend then will only display post text format as a label instead of dropdown (just so that users know for example if you only allow Markdown, only BBCode or only Plain text)
@ -92,13 +112,3 @@ Rich text formatting options are displayed depending on how many formatting opti
### Who to follow ### Who to follow
This is a panel intended for users to find people to follow based on randomness or on post contents. Being potentially privacy unfriendly feature it needs to be enabled and configured in backend to be enabled. This is a panel intended for users to find people to follow based on randomness or on post contents. Being potentially privacy unfriendly feature it needs to be enabled and configured in backend to be enabled.
### Safe DM message display
Setting this will change the warning text that is displayed for direct messages.
ATTENTION: If you actually want the behavior to change. You will need to set the appropriate option at the backend. See the backend documentation for information about that.
DO NOT activate this without checking the backend configuration first!
### Private Mode
If the `private` instance setting is enabled in the backend, features that are not accessible without authentication, such as the timelines and search will be disabled for unauthenticated users.

View file

@ -33,7 +33,7 @@ will become
Note that you can only use emoji defined on your instance, you cannot "copy" someone else's emoji, and will have to ask your administrator to copy emoji from other instance to yours. Note that you can only use emoji defined on your instance, you cannot "copy" someone else's emoji, and will have to ask your administrator to copy emoji from other instance to yours.
Lastly, there's two convenience options for emoji: an emoji picker (smiley face to the right of "submit" button) and autocomplete suggestions - when you start typing :shortcode: it will automatically try to suggest you emoj and complete the shortcode for you if you select one. **Note** that if emoji doesn't show up in suggestions nor in emoji picker it means there's no such emoji on your instance, if shortcode doesn't match any defined emoji it will appear as text. Lastly, there's two convenience options for emoji: an emoji picker (smiley face to the right of "submit" button) and autocomplete suggestions - when you start typing :shortcode: it will automatically try to suggest you emoj and complete the shortcode for you if you select one. **Note** that if emoji doesn't show up in suggestions nor in emoji picker it means there's no such emoji on your instance, if shortcode doesn't match any defined emoji it will appear as text.
* **Attachments** are fairly simple - you can attach any file to a post as long as the file is within maximum size limits. If you're uploading explicit material you can mark all of your attachments as sensitive (or add `#nsfw` tag) - it will hide the images and videos behind a warning so that it won't be displayed instantly. * **Attachments** are fairly simple - you can attach any file to a post as long as the file is within maximum size limits. If you're uploading explicit material you can mark all of your attachments as sensitive (or add `#nsfw` tag) - it will hide the images and videos behind a warning so that it won't be displayed instantly.
* **Subject line** also known as **CW** (Content Warning) could be used as a header to the post and/or to warn others about contents of the post having something that might upset somebody or something among those lines. Several applications allow to hide post content leaving only subject line visible. As a side-effect using subject line will also mark your images as sensitive (see above). * **Subject line** also known as **CW** (Content Warning) could be used as a header to the post and/or to warn others about contents of the post having something that might upset somebody or something among those lines. Several applications allow to hide post content leaving only subject line visible. Using a subject line will not mark your images as sensitive, you will have to do that explicitly (see above).
* **Visiblity scope** controls who will be able to see your posts. There are four scopes available: * **Visiblity scope** controls who will be able to see your posts. There are four scopes available:
1. `Public`: This is the default, and some fediverse software like GNU Social only supports this. This means that your post is accessible by anyone and will be shown in the public timelines. 1. `Public`: This is the default, and some fediverse software like GNU Social only supports this. This means that your post is accessible by anyone and will be shown in the public timelines.

View file

@ -29,7 +29,6 @@
"portal-vue": "^2.1.4", "portal-vue": "^2.1.4",
"sanitize-html": "^1.13.0", "sanitize-html": "^1.13.0",
"v-click-outside": "^2.1.1", "v-click-outside": "^2.1.1",
"v-tooltip": "^2.0.2",
"vue": "^2.5.13", "vue": "^2.5.13",
"vue-chat-scroll": "^1.2.1", "vue-chat-scroll": "^1.2.1",
"vue-i18n": "^7.3.2", "vue-i18n": "^7.3.2",

View file

@ -99,7 +99,12 @@ export default {
}, },
showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }, showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel },
isMobileLayout () { return this.$store.state.interface.mobileLayout }, isMobileLayout () { return this.$store.state.interface.mobileLayout },
privateMode () { return this.$store.state.instance.private } privateMode () { return this.$store.state.instance.private },
sidebarAlign () {
return {
'order': this.$store.state.instance.sidebarRight ? 99 : 0
}
}
}, },
methods: { methods: {
scrollToTop () { scrollToTop () {

View file

@ -80,7 +80,10 @@
id="content" id="content"
class="container underlay" class="container underlay"
> >
<div class="sidebar-flexer mobile-hidden"> <div
class="sidebar-flexer mobile-hidden"
:style="sidebarAlign"
>
<div class="sidebar-bounds"> <div class="sidebar-bounds">
<div class="sidebar-scroller"> <div class="sidebar-scroller">
<div class="sidebar"> <div class="sidebar">

View file

@ -108,9 +108,9 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => {
copyInstanceOption('subjectLineBehavior') copyInstanceOption('subjectLineBehavior')
copyInstanceOption('postContentType') copyInstanceOption('postContentType')
copyInstanceOption('alwaysShowSubjectInput') copyInstanceOption('alwaysShowSubjectInput')
copyInstanceOption('noAttachmentLinks')
copyInstanceOption('showFeaturesPanel') copyInstanceOption('showFeaturesPanel')
copyInstanceOption('hideSitename') copyInstanceOption('hideSitename')
copyInstanceOption('sidebarRight')
return store.dispatch('setTheme', config['theme']) return store.dispatch('setTheme', config['theme'])
} }
@ -241,6 +241,9 @@ const getNodeInfo = async ({ store }) => {
: federation.enabled : federation.enabled
}) })
const accountActivationRequired = metadata.accountActivationRequired
store.dispatch('setInstanceOption', { name: 'accountActivationRequired', value: accountActivationRequired })
const accounts = metadata.staffAccounts const accounts = metadata.staffAccounts
resolveStaffAccounts({ store, accounts }) resolveStaffAccounts({ store, accounts })
} else { } else {
@ -304,6 +307,9 @@ const afterStoreSetup = async ({ store, i18n }) => {
getNodeInfo({ store }) getNodeInfo({ store })
]) ])
// Start fetching things that don't need to block the UI
store.dispatch('fetchMutes')
const router = new VueRouter({ const router = new VueRouter({
mode: 'history', mode: 'history',
routes: routes(store), routes: routes(store),

View file

@ -1,14 +1,16 @@
import ProgressButton from '../progress_button/progress_button.vue' import ProgressButton from '../progress_button/progress_button.vue'
import Popover from '../popover/popover.vue'
const AccountActions = { const AccountActions = {
props: [ props: [
'user' 'user', 'relationship'
], ],
data () { data () {
return { } return { }
}, },
components: { components: {
ProgressButton ProgressButton,
Popover
}, },
methods: { methods: {
showRepeats () { showRepeats () {

View file

@ -1,24 +1,24 @@
<template> <template>
<div class="account-actions"> <div class="account-actions">
<v-popover <Popover
trigger="click" trigger="click"
class="account-tools-popover" placement="bottom"
:container="false" >
placement="bottom-end" <div
:offset="5" slot="content"
class="account-tools-popover"
> >
<div slot="popover">
<div class="dropdown-menu"> <div class="dropdown-menu">
<template v-if="user.following"> <template v-if="relationship.following">
<button <button
v-if="user.showing_reblogs" v-if="relationship.showing_reblogs"
class="btn btn-default dropdown-item" class="btn btn-default dropdown-item"
@click="hideRepeats" @click="hideRepeats"
> >
{{ $t('user_card.hide_repeats') }} {{ $t('user_card.hide_repeats') }}
</button> </button>
<button <button
v-if="!user.showing_reblogs" v-if="!relationship.showing_reblogs"
class="btn btn-default dropdown-item" class="btn btn-default dropdown-item"
@click="showRepeats" @click="showRepeats"
> >
@ -30,7 +30,7 @@
/> />
</template> </template>
<button <button
v-if="user.statusnet_blocking" v-if="relationship.blocking"
class="btn btn-default btn-block dropdown-item" class="btn btn-default btn-block dropdown-item"
@click="unblockUser" @click="unblockUser"
> >
@ -51,10 +51,13 @@
</button> </button>
</div> </div>
</div> </div>
<div class="btn btn-default ellipsis-button"> <div
slot="trigger"
class="btn btn-default ellipsis-button"
>
<i class="icon-ellipsis trigger-button" /> <i class="icon-ellipsis trigger-button" />
</div> </div>
</v-popover> </Popover>
</div> </div>
</template> </template>
@ -62,7 +65,6 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../popper/popper.scss';
.account-actions { .account-actions {
margin: 0 .8em; margin: 0 .8em;
} }
@ -70,6 +72,7 @@
.account-actions button.dropdown-item { .account-actions button.dropdown-item {
margin-left: 0; margin-left: 0;
} }
.account-actions .trigger-button { .account-actions .trigger-button {
color: $fallback--lightText; color: $fallback--lightText;
color: var(--lightText, $fallback--lightText); color: var(--lightText, $fallback--lightText);

View file

@ -12,7 +12,7 @@
class="basic-user-card-expanded-content" class="basic-user-card-expanded-content"
> >
<UserCard <UserCard
:user="user" :user-id="user.id"
:rounded="true" :rounded="true"
:bordered="true" :bordered="true"
/> />

View file

@ -11,8 +11,11 @@ const BlockCard = {
user () { user () {
return this.$store.getters.findUser(this.userId) return this.$store.getters.findUser(this.userId)
}, },
relationship () {
return this.$store.getters.relationship(this.userId)
},
blocked () { blocked () {
return this.user.statusnet_blocking return this.relationship.blocking
} }
}, },
components: { components: {

View file

@ -29,17 +29,29 @@ export default data => input => {
export const suggestEmoji = emojis => input => { export const suggestEmoji = emojis => input => {
const noPrefix = input.toLowerCase().substr(1) const noPrefix = input.toLowerCase().substr(1)
return emojis return emojis
.filter(({ displayText }) => displayText.toLowerCase().startsWith(noPrefix)) .filter(({ displayText }) => displayText.toLowerCase().match(noPrefix))
.sort((a, b) => { .sort((a, b) => {
let aScore = 0 let aScore = 0
let bScore = 0 let bScore = 0
// Make custom emojis a priority // An exact match always wins
aScore += a.imageUrl ? 10 : 0 aScore += a.displayText.toLowerCase() === noPrefix ? 200 : 0
bScore += b.imageUrl ? 10 : 0 bScore += b.displayText.toLowerCase() === noPrefix ? 200 : 0
// Sort alphabetically // Prioritize custom emoji a lot
const alphabetically = a.displayText > b.displayText ? 1 : -1 aScore += a.imageUrl ? 100 : 0
bScore += b.imageUrl ? 100 : 0
// Prioritize prefix matches somewhat
aScore += a.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0
bScore += b.displayText.toLowerCase().startsWith(noPrefix) ? 10 : 0
// Sort by length
aScore -= a.displayText.length
bScore -= b.displayText.length
// Break ties alphabetically
const alphabetically = a.displayText > b.displayText ? 0.5 : -0.5
return bScore - aScore + alphabetically return bScore - aScore + alphabetically
}) })

View file

@ -1,20 +1,17 @@
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import Popover from '../popover/popover.vue'
const EMOJI_REACTION_COUNT_CUTOFF = 12 const EMOJI_REACTION_COUNT_CUTOFF = 12
const EmojiReactions = { const EmojiReactions = {
name: 'EmojiReactions', name: 'EmojiReactions',
components: { components: {
UserAvatar UserAvatar,
Popover
}, },
props: ['status'], props: ['status'],
data: () => ({ data: () => ({
showAll: false, showAll: false
popperOptions: {
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
}), }),
computed: { computed: {
tooManyReactions () { tooManyReactions () {

View file

@ -1,15 +1,14 @@
<template> <template>
<div class="emoji-reactions"> <div class="emoji-reactions">
<v-popover <Popover
v-for="(reaction) in emojiReactions" v-for="(reaction) in emojiReactions"
:key="reaction.name" :key="reaction.name"
:popper-options="popperOptions"
trigger="hover" trigger="hover"
placement="top" placement="top"
:offset="{ y: 5 }"
> >
<div <div
slot="popover" slot="content"
class="reacted-users" class="reacted-users"
> >
<div v-if="accountsForEmoji[reaction.name].length"> <div v-if="accountsForEmoji[reaction.name].length">
@ -24,7 +23,12 @@
:compact="true" :compact="true"
/> />
<div class="reacted-user-names"> <div class="reacted-user-names">
<span class="reacted-user-name" v-html="account.name_html" /> <!-- eslint-disable vue/no-v-html -->
<span
class="reacted-user-name"
v-html="account.name_html"
/>
<!-- eslint-enable vue/no-v-html -->
<span class="reacted-user-screen-name">{{ account.screen_name }}</span> <span class="reacted-user-screen-name">{{ account.screen_name }}</span>
</div> </div>
</div> </div>
@ -34,6 +38,7 @@
</div> </div>
</div> </div>
<button <button
slot="trigger"
class="emoji-reaction btn btn-default" class="emoji-reaction btn btn-default"
:class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }" :class="{ 'picked-reaction': reactedWith(reaction.name), 'not-clickable': !loggedIn }"
@click="emojiOnClick(reaction.name, $event)" @click="emojiOnClick(reaction.name, $event)"
@ -42,17 +47,16 @@
<span class="reaction-emoji">{{ reaction.name }}</span> <span class="reaction-emoji">{{ reaction.name }}</span>
<span>{{ reaction.count }}</span> <span>{{ reaction.count }}</span>
</button> </button>
</v-popover> </Popover>
<a <a
v-if="tooManyReactions" v-if="tooManyReactions"
@click="toggleShowAll"
class="emoji-reaction-expand faint" class="emoji-reaction-expand faint"
href='javascript:void(0)' href="javascript:void(0)"
@click="toggleShowAll"
> >
{{ showAll ? $t('general.show_less') : showMoreString }} {{ showAll ? $t('general.show_less') : showMoreString }}
</a> </a>
</div> </div>
</template> </template>
<script src="./emoji_reactions.js" ></script> <script src="./emoji_reactions.js" ></script>
@ -78,6 +82,7 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
margin-left: 0.5em; margin-left: 0.5em;
min-width: 5em;
img { img {
width: 1em; width: 1em;

View file

@ -1,5 +1,8 @@
import Popover from '../popover/popover.vue'
const ExtraButtons = { const ExtraButtons = {
props: [ 'status' ], props: [ 'status' ],
components: { Popover },
methods: { methods: {
deleteStatus () { deleteStatus () {
const confirmed = window.confirm(this.$t('status.delete_confirm')) const confirmed = window.confirm(this.$t('status.delete_confirm'))
@ -26,6 +29,11 @@ const ExtraButtons = {
this.$store.dispatch('unmuteConversation', this.status.id) this.$store.dispatch('unmuteConversation', this.status.id)
.then(() => this.$emit('onSuccess')) .then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error)) .catch(err => this.$emit('onError', err.error.error))
},
copyLink () {
navigator.clipboard.writeText(this.statusLink)
.then(() => this.$emit('onSuccess'))
.catch(err => this.$emit('onError', err.error.error))
} }
}, },
computed: { computed: {
@ -43,6 +51,9 @@ const ExtraButtons = {
}, },
canMute () { canMute () {
return !!this.currentUser return !!this.currentUser
},
statusLink () {
return `${this.$store.state.instance.server}${this.$router.resolve({ name: 'conversation', params: { id: this.status.id } }).href}`
} }
} }
} }

View file

@ -1,11 +1,13 @@
<template> <template>
<v-popover <Popover
v-if="canDelete || canMute || canPin"
trigger="click" trigger="click"
placement="top" placement="top"
class="extra-button-popover" class="extra-button-popover"
> >
<div slot="popover"> <div
slot="content"
slot-scope="{close}"
>
<div class="dropdown-menu"> <div class="dropdown-menu">
<button <button
v-if="canMute && !status.thread_muted" v-if="canMute && !status.thread_muted"
@ -23,41 +25,48 @@
</button> </button>
<button <button
v-if="!status.pinned && canPin" v-if="!status.pinned && canPin"
v-close-popover
class="dropdown-item dropdown-item-icon" class="dropdown-item dropdown-item-icon"
@click.prevent="pinStatus" @click.prevent="pinStatus"
@click="close"
> >
<i class="icon-pin" /><span>{{ $t("status.pin") }}</span> <i class="icon-pin" /><span>{{ $t("status.pin") }}</span>
</button> </button>
<button <button
v-if="status.pinned && canPin" v-if="status.pinned && canPin"
v-close-popover
class="dropdown-item dropdown-item-icon" class="dropdown-item dropdown-item-icon"
@click.prevent="unpinStatus" @click.prevent="unpinStatus"
@click="close"
> >
<i class="icon-pin" /><span>{{ $t("status.unpin") }}</span> <i class="icon-pin" /><span>{{ $t("status.unpin") }}</span>
</button> </button>
<button <button
v-if="canDelete" v-if="canDelete"
v-close-popover
class="dropdown-item dropdown-item-icon" class="dropdown-item dropdown-item-icon"
@click.prevent="deleteStatus" @click.prevent="deleteStatus"
@click="close"
> >
<i class="icon-cancel" /><span>{{ $t("status.delete") }}</span> <i class="icon-cancel" /><span>{{ $t("status.delete") }}</span>
</button> </button>
<button
class="dropdown-item dropdown-item-icon"
@click.prevent="copyLink"
@click="close"
>
<i class="icon-share" /><span>{{ $t("status.copy_link") }}</span>
</button>
</div> </div>
</div> </div>
<div class="button-icon"> <i
<i class="icon-ellipsis" /> slot="trigger"
</div> class="icon-ellipsis button-icon"
</v-popover> />
</Popover>
</template> </template>
<script src="./extra_buttons.js" ></script> <script src="./extra_buttons.js" ></script>
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../popper/popper.scss';
.icon-ellipsis { .icon-ellipsis {
cursor: pointer; cursor: pointer;

View file

@ -1,6 +1,6 @@
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate' import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
export default { export default {
props: ['user', 'labelFollowing', 'buttonClass'], props: ['relationship', 'labelFollowing', 'buttonClass'],
data () { data () {
return { return {
inProgress: false inProgress: false
@ -8,12 +8,12 @@ export default {
}, },
computed: { computed: {
isPressed () { isPressed () {
return this.inProgress || this.user.following return this.inProgress || this.relationship.following
}, },
title () { title () {
if (this.inProgress || this.user.following) { if (this.inProgress || this.relationship.following) {
return this.$t('user_card.follow_unfollow') return this.$t('user_card.follow_unfollow')
} else if (this.user.requested) { } else if (this.relationship.requested) {
return this.$t('user_card.follow_again') return this.$t('user_card.follow_again')
} else { } else {
return this.$t('user_card.follow') return this.$t('user_card.follow')
@ -22,9 +22,9 @@ export default {
label () { label () {
if (this.inProgress) { if (this.inProgress) {
return this.$t('user_card.follow_progress') return this.$t('user_card.follow_progress')
} else if (this.user.following) { } else if (this.relationship.following) {
return this.labelFollowing || this.$t('user_card.following') return this.labelFollowing || this.$t('user_card.following')
} else if (this.user.requested) { } else if (this.relationship.requested) {
return this.$t('user_card.follow_sent') return this.$t('user_card.follow_sent')
} else { } else {
return this.$t('user_card.follow') return this.$t('user_card.follow')
@ -33,20 +33,20 @@ export default {
}, },
methods: { methods: {
onClick () { onClick () {
this.user.following ? this.unfollow() : this.follow() this.relationship.following ? this.unfollow() : this.follow()
}, },
follow () { follow () {
this.inProgress = true this.inProgress = true
requestFollow(this.user, this.$store).then(() => { requestFollow(this.relationship.id, this.$store).then(() => {
this.inProgress = false this.inProgress = false
}) })
}, },
unfollow () { unfollow () {
const store = this.$store const store = this.$store
this.inProgress = true this.inProgress = true
requestUnfollow(this.user, store).then(() => { requestUnfollow(this.relationship.id, store).then(() => {
this.inProgress = false this.inProgress = false
store.commit('removeStatus', { timeline: 'friends', userId: this.user.id }) store.commit('removeStatus', { timeline: 'friends', userId: this.relationship.id })
}) })
} }
} }

View file

@ -18,6 +18,9 @@ const FollowCard = {
}, },
loggedIn () { loggedIn () {
return this.$store.state.users.currentUser return this.$store.state.users.currentUser
},
relationship () {
return this.$store.getters.relationship(this.user.id)
} }
} }
} }

View file

@ -2,24 +2,24 @@
<basic-user-card :user="user"> <basic-user-card :user="user">
<div class="follow-card-content-container"> <div class="follow-card-content-container">
<span <span
v-if="!noFollowsYou && user.follows_you" v-if="isMe || (!noFollowsYou && relationship.followed_by)"
class="faint" class="faint"
> >
{{ isMe ? $t('user_card.its_you') : $t('user_card.follows_you') }} {{ isMe ? $t('user_card.its_you') : $t('user_card.follows_you') }}
</span> </span>
<template v-if="!loggedIn"> <template v-if="!loggedIn">
<div <div
v-if="!user.following" v-if="!relationship.following"
class="follow-card-follow-button" class="follow-card-follow-button"
> >
<RemoteFollow :user="user" /> <RemoteFollow :user="user" />
</div> </div>
</template> </template>
<template v-else> <template v-else-if="!isMe">
<FollowButton <FollowButton
:user="user" :relationship="relationship"
class="follow-card-follow-button"
:label-following="$t('user_card.follow_unfollow')" :label-following="$t('user_card.follow_unfollow')"
class="follow-card-follow-button"
/> />
</template> </template>
</div> </div>

View file

@ -1,4 +1,5 @@
import BasicUserCard from '../basic_user_card/basic_user_card.vue' import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import { notificationsFromStore } from '../../services/notification_utils/notification_utils.js'
const FollowRequestCard = { const FollowRequestCard = {
props: ['user'], props: ['user'],
@ -6,13 +7,32 @@ const FollowRequestCard = {
BasicUserCard BasicUserCard
}, },
methods: { methods: {
findFollowRequestNotificationId () {
const notif = notificationsFromStore(this.$store).find(
(notif) => notif.from_profile.id === this.user.id && notif.type === 'follow_request'
)
return notif && notif.id
},
approveUser () { approveUser () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id }) this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('removeFollowRequest', this.user)
const notifId = this.findFollowRequestNotificationId()
this.$store.dispatch('markSingleNotificationAsSeen', { id: notifId })
this.$store.dispatch('updateNotification', {
id: notifId,
updater: notification => {
notification.type = 'follow'
}
})
}, },
denyUser () { denyUser () {
const notifId = this.findFollowRequestNotificationId()
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id }) this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
this.$store.dispatch('removeFollowRequest', this.user) this.$store.dispatch('removeFollowRequest', this.user)
})
} }
} }
} }

View file

@ -84,10 +84,12 @@ const MediaModal = {
} }
}, },
mounted () { mounted () {
window.addEventListener('popstate', this.hide)
document.addEventListener('keyup', this.handleKeyupEvent) document.addEventListener('keyup', this.handleKeyupEvent)
document.addEventListener('keydown', this.handleKeydownEvent) document.addEventListener('keydown', this.handleKeydownEvent)
}, },
destroyed () { destroyed () {
window.removeEventListener('popstate', this.hide)
document.removeEventListener('keyup', this.handleKeyupEvent) document.removeEventListener('keyup', this.handleKeyupEvent)
document.removeEventListener('keydown', this.handleKeydownEvent) document.removeEventListener('keydown', this.handleKeydownEvent)
} }

View file

@ -1,4 +1,5 @@
import DialogModal from '../dialog_modal/dialog_modal.vue' import DialogModal from '../dialog_modal/dialog_modal.vue'
import Popover from '../popover/popover.vue'
const FORCE_NSFW = 'mrf_tag:media-force-nsfw' const FORCE_NSFW = 'mrf_tag:media-force-nsfw'
const STRIP_MEDIA = 'mrf_tag:media-strip' const STRIP_MEDIA = 'mrf_tag:media-strip'
@ -14,7 +15,6 @@ const ModerationTools = {
], ],
data () { data () {
return { return {
showDropDown: false,
tags: { tags: {
FORCE_NSFW, FORCE_NSFW,
STRIP_MEDIA, STRIP_MEDIA,
@ -24,11 +24,13 @@ const ModerationTools = {
SANDBOX, SANDBOX,
QUARANTINE QUARANTINE
}, },
showDeleteUserDialog: false showDeleteUserDialog: false,
toggled: false
} }
}, },
components: { components: {
DialogModal DialogModal,
Popover
}, },
computed: { computed: {
tagsSet () { tagsSet () {
@ -89,6 +91,9 @@ const ModerationTools = {
window.history.back() window.history.back()
} }
}) })
},
setToggled (value) {
this.toggled = value
} }
} }
} }

View file

@ -1,13 +1,14 @@
<template> <template>
<div> <div>
<v-popover <Popover
trigger="click" trigger="click"
class="moderation-tools-popover" class="moderation-tools-popover"
placement="bottom-end" placement="bottom"
@show="showDropDown = true" :offset="{ y: 5 }"
@hide="showDropDown = false" @show="setToggled(true)"
@close="setToggled(false)"
> >
<div slot="popover"> <div slot="content">
<div class="dropdown-menu"> <div class="dropdown-menu">
<span v-if="user.is_local"> <span v-if="user.is_local">
<button <button
@ -122,12 +123,13 @@
</div> </div>
</div> </div>
<button <button
slot="trigger"
class="btn btn-default btn-block" class="btn btn-default btn-block"
:class="{ toggled: showDropDown }" :class="{ toggled }"
> >
{{ $t('user_card.admin_menu.moderation') }} {{ $t('user_card.admin_menu.moderation') }}
</button> </button>
</v-popover> </Popover>
<portal to="modal"> <portal to="modal">
<DialogModal <DialogModal
v-if="showDeleteUserDialog" v-if="showDeleteUserDialog"
@ -160,7 +162,6 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
@import '../popper/popper.scss';
.menu-checkbox { .menu-checkbox {
float: right; float: right;

View file

@ -11,8 +11,11 @@ const MuteCard = {
user () { user () {
return this.$store.getters.findUser(this.userId) return this.$store.getters.findUser(this.userId)
}, },
relationship () {
return this.$store.getters.relationship(this.userId)
},
muted () { muted () {
return this.user.muted return this.relationship.muting
} }
}, },
components: { components: {
@ -21,13 +24,13 @@ const MuteCard = {
methods: { methods: {
unmuteUser () { unmuteUser () {
this.progress = true this.progress = true
this.$store.dispatch('unmuteUser', this.user.id).then(() => { this.$store.dispatch('unmuteUser', this.userId).then(() => {
this.progress = false this.progress = false
}) })
}, },
muteUser () { muteUser () {
this.progress = true this.progress = true
this.$store.dispatch('muteUser', this.user.id).then(() => { this.$store.dispatch('muteUser', this.userId).then(() => {
this.progress = false this.progress = false
}) })
} }

View file

@ -2,6 +2,7 @@ import Status from '../status/status.vue'
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import UserCard from '../user_card/user_card.vue' import UserCard from '../user_card/user_card.vue'
import Timeago from '../timeago/timeago.vue' import Timeago from '../timeago/timeago.vue'
import { isStatusNotification } from '../../services/notification_utils/notification_utils.js'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@ -32,6 +33,24 @@ const Notification = {
}, },
toggleMute () { toggleMute () {
this.unmuted = !this.unmuted this.unmuted = !this.unmuted
},
approveUser () {
this.$store.state.api.backendInteractor.approveUser({ id: this.user.id })
this.$store.dispatch('removeFollowRequest', this.user)
this.$store.dispatch('markSingleNotificationAsSeen', { id: this.notification.id })
this.$store.dispatch('updateNotification', {
id: this.notification.id,
updater: notification => {
notification.type = 'follow'
}
})
},
denyUser () {
this.$store.state.api.backendInteractor.denyUser({ id: this.user.id })
.then(() => {
this.$store.dispatch('dismissNotificationLocal', { id: this.notification.id })
this.$store.dispatch('removeFollowRequest', this.user)
})
} }
}, },
computed: { computed: {
@ -56,7 +75,10 @@ const Notification = {
return this.generateUserProfileLink(this.targetUser) return this.generateUserProfileLink(this.targetUser)
}, },
needMute () { needMute () {
return this.user.muted return this.$store.getters.relationship(this.user.id).muting
},
isStatusNotification () {
return isStatusNotification(this.notification.type)
} }
} }
} }

View file

@ -40,14 +40,14 @@
<div class="notification-right"> <div class="notification-right">
<UserCard <UserCard
v-if="userExpanded" v-if="userExpanded"
:user="getUser(notification)" :user-id="getUser(notification).id"
:rounded="true" :rounded="true"
:bordered="true" :bordered="true"
/> />
<span class="notification-details"> <span class="notification-details">
<div class="name-and-action"> <div class="name-and-action">
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<span <bdi
v-if="!!notification.from_profile.name_html" v-if="!!notification.from_profile.name_html"
class="username" class="username"
:title="'@'+notification.from_profile.screen_name" :title="'@'+notification.from_profile.screen_name"
@ -74,6 +74,10 @@
<i class="fa icon-user-plus lit" /> <i class="fa icon-user-plus lit" />
<small>{{ $t('notifications.followed_you') }}</small> <small>{{ $t('notifications.followed_you') }}</small>
</span> </span>
<span v-if="notification.type === 'follow_request'">
<i class="fa icon-user lit" />
<small>{{ $t('notifications.follow_request') }}</small>
</span>
<span v-if="notification.type === 'pleroma:move'"> <span v-if="notification.type === 'pleroma:move'">
<i class="fa icon-arrow-curved lit" /> <i class="fa icon-arrow-curved lit" />
<small>{{ $t('notifications.migrated_to') }}</small> <small>{{ $t('notifications.migrated_to') }}</small>
@ -87,18 +91,7 @@
</span> </span>
</div> </div>
<div <div
v-if="notification.type === 'follow' || notification.type === 'pleroma:move'" v-if="isStatusNotification"
class="timeago"
>
<span class="faint">
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</span>
</div>
<div
v-else
class="timeago" class="timeago"
> >
<router-link <router-link
@ -112,6 +105,17 @@
/> />
</router-link> </router-link>
</div> </div>
<div
v-else
class="timeago"
>
<span class="faint">
<Timeago
:time="notification.created_at"
:auto-update="240"
/>
</span>
</div>
<a <a
v-if="needMute" v-if="needMute"
href="#" href="#"
@ -119,12 +123,30 @@
><i class="button-icon icon-eye-off" /></a> ><i class="button-icon icon-eye-off" /></a>
</span> </span>
<div <div
v-if="notification.type === 'follow'" v-if="notification.type === 'follow' || notification.type === 'follow_request'"
class="follow-text" class="follow-text"
> >
<router-link :to="userProfileLink"> <router-link
:to="userProfileLink"
class="follow-name"
>
@{{ notification.from_profile.screen_name }} @{{ notification.from_profile.screen_name }}
</router-link> </router-link>
<div
v-if="notification.type === 'follow_request'"
style="white-space: nowrap;"
>
<i
class="icon-ok button-icon follow-request-accept"
:title="$t('tool_tip.accept_follow_request')"
@click="approveUser()"
/>
<i
class="icon-cancel button-icon follow-request-reject"
:title="$t('tool_tip.reject_follow_request')"
@click="denyUser()"
/>
</div>
</div> </div>
<div <div
v-else-if="notification.type === 'pleroma:move'" v-else-if="notification.type === 'pleroma:move'"

View file

@ -79,8 +79,38 @@
} }
} }
.follow-request-accept {
cursor: pointer;
&:hover {
color: $fallback--text;
color: var(--text, $fallback--text);
}
}
.follow-request-reject {
cursor: pointer;
&:hover {
color: $fallback--cRed;
color: var(--cRed, $fallback--cRed);
}
}
.follow-text, .move-text { .follow-text, .move-text {
padding: 0.5em 0; padding: 0.5em 0;
overflow-wrap: break-word;
display: flex;
justify-content: space-between;
.follow-name {
display: block;
max-width: 100%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
} }
.status-el { .status-el {
@ -142,6 +172,11 @@
color: var(--cGreen, $fallback--cGreen); color: var(--cGreen, $fallback--cGreen);
} }
.icon-user.lit {
color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue);
}
.icon-user-plus.lit { .icon-user-plus.lit {
color: $fallback--cBlue; color: $fallback--cBlue;
color: var(--cBlue, $fallback--cBlue); color: var(--cBlue, $fallback--cBlue);

View file

@ -0,0 +1,156 @@
const Popover = {
name: 'Popover',
props: {
// Action to trigger popover: either 'hover' or 'click'
trigger: String,
// Either 'top' or 'bottom'
placement: String,
// Takes object with properties 'x' and 'y', values of these can be
// 'container' for using offsetParent as boundaries for either axis
// or 'viewport'
boundTo: Object,
// Takes a top/bottom/left/right object, how much space to leave
// between boundary and popover element
margin: Object,
// Takes a x/y object and tells how many pixels to offset from
// anchor point on either axis
offset: Object,
// Additional styles you may want for the popover container
popoverClass: String
},
data () {
return {
hidden: true,
styles: { opacity: 0 },
oldSize: { width: 0, height: 0 }
}
},
methods: {
updateStyles () {
if (this.hidden) {
this.styles = {
opacity: 0
}
return
}
// Popover will be anchored around this element, trigger ref is the container, so
// its children are what are inside the slot. Expect only one slot="trigger".
const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el
const screenBox = anchorEl.getBoundingClientRect()
// Screen position of the origin point for popover
const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top }
const content = this.$refs.content
// Minor optimization, don't call a slow reflow call if we don't have to
const parentBounds = this.boundTo &&
(this.boundTo.x === 'container' || this.boundTo.y === 'container') &&
this.$el.offsetParent.getBoundingClientRect()
const margin = this.margin || {}
// What are the screen bounds for the popover? Viewport vs container
// when using viewport, using default margin values to dodge the navbar
const xBounds = this.boundTo && this.boundTo.x === 'container' ? {
min: parentBounds.left + (margin.left || 0),
max: parentBounds.right - (margin.right || 0)
} : {
min: 0 + (margin.left || 10),
max: window.innerWidth - (margin.right || 10)
}
const yBounds = this.boundTo && this.boundTo.y === 'container' ? {
min: parentBounds.top + (margin.top || 0),
max: parentBounds.bottom - (margin.bottom || 0)
} : {
min: 0 + (margin.top || 50),
max: window.innerHeight - (margin.bottom || 5)
}
let horizOffset = 0
// If overflowing from left, move it so that it doesn't
if ((origin.x - content.offsetWidth * 0.5) < xBounds.min) {
horizOffset += -(origin.x - content.offsetWidth * 0.5) + xBounds.min
}
// If overflowing from right, move it so that it doesn't
if ((origin.x + horizOffset + content.offsetWidth * 0.5) > xBounds.max) {
horizOffset -= (origin.x + horizOffset + content.offsetWidth * 0.5) - xBounds.max
}
// Default to whatever user wished with placement prop
let usingTop = this.placement !== 'bottom'
// Handle special cases, first force to displaying on top if there's not space on bottom,
// regardless of what placement value was. Then check if there's not space on top, and
// force to bottom, again regardless of what placement value was.
if (origin.y + content.offsetHeight > yBounds.max) usingTop = true
if (origin.y - content.offsetHeight < yBounds.min) usingTop = false
const yOffset = (this.offset && this.offset.y) || 0
const translateY = usingTop
? -anchorEl.offsetHeight - yOffset - content.offsetHeight
: yOffset
const xOffset = (this.offset && this.offset.x) || 0
const translateX = (anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset + xOffset
// Note, separate translateX and translateY avoids blurry text on chromium,
// single translate or translate3d resulted in blurry text.
this.styles = {
opacity: 1,
transform: `translateX(${Math.floor(translateX)}px) translateY(${Math.floor(translateY)}px)`
}
},
showPopover () {
if (this.hidden) this.$emit('show')
this.hidden = false
this.$nextTick(this.updateStyles)
},
hidePopover () {
if (!this.hidden) this.$emit('close')
this.hidden = true
this.styles = { opacity: 0 }
},
onMouseenter (e) {
if (this.trigger === 'hover') this.showPopover()
},
onMouseleave (e) {
if (this.trigger === 'hover') this.hidePopover()
},
onClick (e) {
if (this.trigger === 'click') {
if (this.hidden) {
this.showPopover()
} else {
this.hidePopover()
}
}
},
onClickOutside (e) {
if (this.hidden) return
if (this.$el.contains(e.target)) return
this.hidePopover()
}
},
updated () {
// Monitor changes to content size, update styles only when content sizes have changed,
// that should be the only time we need to move the popover box if we don't care about scroll
// or resize
const content = this.$refs.content
if (!content) return
if (this.oldSize.width !== content.offsetWidth || this.oldSize.height !== content.offsetHeight) {
this.updateStyles()
this.oldSize = { width: content.offsetWidth, height: content.offsetHeight }
}
},
created () {
document.addEventListener('click', this.onClickOutside)
},
destroyed () {
document.removeEventListener('click', this.onClickOutside)
this.hidePopover()
}
}
export default Popover

View file

@ -0,0 +1,118 @@
<template>
<div
@mouseenter="onMouseenter"
@mouseleave="onMouseleave"
>
<div
ref="trigger"
@click="onClick"
>
<slot name="trigger" />
</div>
<div
v-if="!hidden"
ref="content"
:style="styles"
class="popover"
:class="popoverClass"
>
<slot
name="content"
class="popover-inner"
:close="hidePopover"
/>
</div>
</div>
</template>
<script src="./popover.js" />
<style lang=scss>
@import '../../_variables.scss';
.popover {
z-index: 8;
position: absolute;
min-width: 0;
transition: opacity 0.3s;
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
box-shadow: var(--panelShadow);
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--text;
color: var(--popoverText, $fallback--text);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--postLink: var(--popoverPostLink, $fallback--link);
--postFaintLink: var(--popoverPostFaintLink, $fallback--link);
--icon: var(--popoverIcon, $fallback--icon);
}
.dropdown-menu {
display: block;
padding: .5rem 0;
font-size: 1rem;
text-align: left;
list-style: none;
max-width: 100vw;
z-index: 10;
white-space: nowrap;
.dropdown-divider {
height: 0;
margin: .5rem 0;
overflow: hidden;
border-top: 1px solid $fallback--border;
border-top: 1px solid var(--border, $fallback--border);
}
.dropdown-item {
line-height: 21px;
margin-right: 5px;
overflow: auto;
display: block;
padding: .25rem 1.0rem .25rem 1.5rem;
clear: both;
font-weight: 400;
text-align: inherit;
white-space: nowrap;
border: none;
border-radius: 0px;
background-color: transparent;
box-shadow: none;
width: 100%;
height: 100%;
--btnText: var(--popoverText, $fallback--text);
&-icon {
padding-left: 0.5rem;
i {
margin-right: 0.25rem;
color: var(--menuPopoverIcon, $fallback--icon)
}
}
&:active, &:hover {
background-color: $fallback--lightBg;
background-color: var(--selectedMenuPopover, $fallback--lightBg);
color: $fallback--link;
color: var(--selectedMenuPopoverText, $fallback--link);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
i {
color: var(--selectedMenuPopoverIcon, $fallback--icon);
}
}
}
}
</style>

View file

@ -1,164 +0,0 @@
@import '../../_variables.scss';
.tooltip.popover {
z-index: 8;
.popover-inner {
box-shadow: 1px 1px 4px rgba(0,0,0,.6);
box-shadow: var(--panelShadow);
border-radius: $fallback--btnRadius;
border-radius: var(--btnRadius, $fallback--btnRadius);
background-color: $fallback--bg;
background-color: var(--popover, $fallback--bg);
color: $fallback--text;
color: var(--popoverText, $fallback--text);
--faint: var(--popoverFaintText, $fallback--faint);
--faintLink: var(--popoverFaintLink, $fallback--faint);
--lightText: var(--popoverLightText, $fallback--lightText);
--postLink: var(--popoverPostLink, $fallback--link);
--postFaintLink: var(--popoverPostFaintLink, $fallback--link);
--icon: var(--popoverIcon, $fallback--icon);
}
.popover-arrow {
width: 0;
height: 0;
border-style: solid;
position: absolute;
margin: 5px;
border-color: $fallback--bg;
border-color: var(--bg, $fallback--bg);
}
&[x-placement^="top"] {
margin-bottom: 5px;
.popover-arrow {
border-width: 5px 5px 0 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
bottom: -4px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="bottom"] {
margin-top: 5px;
.popover-arrow {
border-width: 0 5px 5px 5px;
border-left-color: transparent !important;
border-right-color: transparent !important;
border-top-color: transparent !important;
top: -4px;
left: calc(50% - 5px);
margin-top: 0;
margin-bottom: 0;
}
}
&[x-placement^="right"] {
margin-left: 5px;
.popover-arrow {
border-width: 5px 5px 5px 0;
border-left-color: transparent !important;
border-top-color: transparent !important;
border-bottom-color: transparent !important;
left: -4px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[x-placement^="left"] {
margin-right: 5px;
.popover-arrow {
border-width: 5px 0 5px 5px;
border-top-color: transparent !important;
border-right-color: transparent !important;
border-bottom-color: transparent !important;
right: -4px;
top: calc(50% - 5px);
margin-left: 0;
margin-right: 0;
}
}
&[aria-hidden='true'] {
visibility: hidden;
opacity: 0;
transition: opacity .15s, visibility .15s;
}
&[aria-hidden='false'] {
visibility: visible;
opacity: 1;
transition: opacity .15s;
}
}
.dropdown-menu {
display: block;
padding: .5rem 0;
font-size: 1rem;
text-align: left;
list-style: none;
max-width: 100vw;
z-index: 10;
.dropdown-divider {
height: 0;
margin: .5rem 0;
overflow: hidden;
border-top: 1px solid $fallback--border;
border-top: 1px solid var(--border, $fallback--border);
}
.dropdown-item {
line-height: 21px;
margin-right: 5px;
overflow: auto;
display: block;
padding: .25rem 1.0rem .25rem 1.5rem;
clear: both;
font-weight: 400;
text-align: inherit;
white-space: normal;
border: none;
border-radius: 0px;
background-color: transparent;
box-shadow: none;
width: 100%;
height: 100%;
--btnText: var(--popoverText, $fallback--text);
&-icon {
padding-left: 0.5rem;
i {
margin-right: 0.25rem;
}
}
&:active, &:hover {
background-color: $fallback--lightBg;
background-color: var(--selectedMenuPopover, $fallback--lightBg);
color: $fallback--link;
color: var(--selectedMenuPopoverText, $fallback--link);
--faint: var(--selectedMenuPopoverFaintText, $fallback--faint);
--faintLink: var(--selectedMenuPopoverFaintLink, $fallback--faint);
--lightText: var(--selectedMenuPopoverLightText, $fallback--lightText);
--icon: var(--selectedMenuPopoverIcon, $fallback--icon);
i {
color: var(--selectedMenuPopoverIcon, $fallback--icon);
}
}
}
}

View file

@ -102,7 +102,7 @@ const PostStatusForm = {
...this.$store.state.instance.customEmoji ...this.$store.state.instance.customEmoji
], ],
users: this.$store.state.users.users, users: this.$store.state.users.users,
updateUsersList: (input) => this.$store.dispatch('searchUsers', input) updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
}) })
}, },
emojiSuggestor () { emojiSuggestor () {

View file

@ -1,34 +1,25 @@
import Popover from '../popover/popover.vue'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
const ReactButton = { const ReactButton = {
props: ['status', 'loggedIn'], props: ['status'],
data () { data () {
return { return {
showTooltip: false, filterWord: ''
filterWord: '',
popperOptions: {
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
} }
}, },
components: {
Popover
},
methods: { methods: {
openReactionSelect () { addReaction (event, emoji, close) {
this.showTooltip = true
this.filterWord = ''
},
closeReactionSelect () {
this.showTooltip = false
},
addReaction (event, emoji) {
const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji) const existingReaction = this.status.emoji_reactions.find(r => r.name === emoji)
if (existingReaction && existingReaction.me) { if (existingReaction && existingReaction.me) {
this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('unreactWithEmoji', { id: this.status.id, emoji })
} else { } else {
this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji }) this.$store.dispatch('reactWithEmoji', { id: this.status.id, emoji })
} }
this.closeReactionSelect() close()
} }
}, },
computed: { computed: {

View file

@ -1,13 +1,14 @@
<template> <template>
<v-popover <Popover
:popper-options="popperOptions" trigger="click"
:open="showTooltip"
trigger="manual"
placement="top" placement="top"
:offset="{ y: 5 }"
class="react-button-popover" class="react-button-popover"
@hide="closeReactionSelect"
> >
<div slot="popover"> <div
slot="content"
slot-scope="{close}"
>
<div class="reaction-picker-filter"> <div class="reaction-picker-filter">
<input <input
v-model="filterWord" v-model="filterWord"
@ -19,7 +20,7 @@
v-for="emoji in commonEmojis" v-for="emoji in commonEmojis"
:key="emoji" :key="emoji"
class="emoji-button" class="emoji-button"
@click="addReaction($event, emoji)" @click="addReaction($event, emoji, close)"
> >
{{ emoji }} {{ emoji }}
</span> </span>
@ -28,23 +29,19 @@
v-for="(emoji, key) in emojis" v-for="(emoji, key) in emojis"
:key="key" :key="key"
class="emoji-button" class="emoji-button"
@click="addReaction($event, emoji.replacement)" @click="addReaction($event, emoji.replacement, close)"
> >
{{ emoji.replacement }} {{ emoji.replacement }}
</span> </span>
<div class="reaction-bottom-fader" /> <div class="reaction-bottom-fader" />
</div> </div>
</div> </div>
<div
v-if="loggedIn"
@click.prevent="openReactionSelect"
>
<i <i
slot="trigger"
class="icon-smile button-icon add-reaction-button" class="icon-smile button-icon add-reaction-button"
:title="$t('tool_tip.add_reaction')" :title="$t('tool_tip.add_reaction')"
/> />
</div> </Popover>
</v-popover>
</template> </template>
<script src="./react_button.js" ></script> <script src="./react_button.js" ></script>

View file

@ -1,5 +1,5 @@
import { validationMixin } from 'vuelidate' import { validationMixin } from 'vuelidate'
import { required, sameAs } from 'vuelidate/lib/validators' import { required, requiredIf, sameAs } from 'vuelidate/lib/validators'
import { mapActions, mapState } from 'vuex' import { mapActions, mapState } from 'vuex'
const registration = { const registration = {
@ -14,9 +14,10 @@ const registration = {
}, },
captcha: {} captcha: {}
}), }),
validations: { validations () {
return {
user: { user: {
email: { required }, email: { required: requiredIf(() => this.accountActivationRequired) },
username: { required }, username: { required },
fullname: { required }, fullname: { required },
password: { required }, password: { required },
@ -25,6 +26,7 @@ const registration = {
sameAsPassword: sameAs('password') sameAsPassword: sameAs('password')
} }
} }
}
}, },
created () { created () {
if ((!this.registrationOpen && !this.token) || this.signedIn) { if ((!this.registrationOpen && !this.token) || this.signedIn) {
@ -43,7 +45,8 @@ const registration = {
signedIn: (state) => !!state.users.currentUser, signedIn: (state) => !!state.users.currentUser,
isPending: (state) => state.users.signUpPending, isPending: (state) => state.users.signUpPending,
serverValidationErrors: (state) => state.users.signUpErrors, serverValidationErrors: (state) => state.users.signUpErrors,
termsOfService: (state) => state.instance.tos termsOfService: (state) => state.instance.tos,
accountActivationRequired: (state) => state.instance.accountActivationRequired
}) })
}, },
methods: { methods: {

View file

@ -187,6 +187,9 @@
class="form-control" class="form-control"
type="text" type="text"
autocomplete="off" autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"
> >
</template> </template>
</div> </div>

View file

@ -19,7 +19,7 @@
> >
<UserCard <UserCard
v-if="currentUser" v-if="currentUser"
:user="currentUser" :user-id="currentUser.id"
:hide-bio="true" :hide-bio="true"
/> />
<div <div

View file

@ -1,23 +1,17 @@
import Attachment from '../attachment/attachment.vue'
import FavoriteButton from '../favorite_button/favorite_button.vue' import FavoriteButton from '../favorite_button/favorite_button.vue'
import ReactButton from '../react_button/react_button.vue' import ReactButton from '../react_button/react_button.vue'
import RetweetButton from '../retweet_button/retweet_button.vue' import RetweetButton from '../retweet_button/retweet_button.vue'
import Poll from '../poll/poll.vue'
import ExtraButtons from '../extra_buttons/extra_buttons.vue' import ExtraButtons from '../extra_buttons/extra_buttons.vue'
import PostStatusForm from '../post_status_form/post_status_form.vue' import PostStatusForm from '../post_status_form/post_status_form.vue'
import UserCard from '../user_card/user_card.vue' import UserCard from '../user_card/user_card.vue'
import UserAvatar from '../user_avatar/user_avatar.vue' import UserAvatar from '../user_avatar/user_avatar.vue'
import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue'
import AvatarList from '../avatar_list/avatar_list.vue' import AvatarList from '../avatar_list/avatar_list.vue'
import Timeago from '../timeago/timeago.vue' import Timeago from '../timeago/timeago.vue'
import StatusContent from '../status_content/status_content.vue'
import StatusPopover from '../status_popover/status_popover.vue' import StatusPopover from '../status_popover/status_popover.vue'
import EmojiReactions from '../emoji_reactions/emoji_reactions.vue' import EmojiReactions from '../emoji_reactions/emoji_reactions.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import fileType from 'src/services/file_type/file_type.service'
import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js'
import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js' import { highlightClass, highlightStyle } from '../../services/user_highlighter/user_highlighter.js'
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
import { filter, unescape, uniqBy } from 'lodash' import { filter, unescape, uniqBy } from 'lodash'
import { mapGetters, mapState } from 'vuex' import { mapGetters, mapState } from 'vuex'
@ -43,17 +37,10 @@ const Status = {
replying: false, replying: false,
unmuted: false, unmuted: false,
userExpanded: false, userExpanded: false,
showingTall: this.inConversation && this.focused, error: null
showingLongSubject: false,
error: null,
// not as computed because it sets the initial state which will be changed later
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject
} }
}, },
computed: { computed: {
localCollapseSubjectDefault () {
return this.mergedConfig.collapseMessageWithSubject
},
muteWords () { muteWords () {
return this.mergedConfig.muteWords return this.mergedConfig.muteWords
}, },
@ -79,10 +66,6 @@ const Status = {
const highlight = this.mergedConfig.highlight const highlight = this.mergedConfig.highlight
return highlightStyle(highlight[user.screen_name]) return highlightStyle(highlight[user.screen_name])
}, },
hideAttachments () {
return (this.mergedConfig.hideAttachments && !this.inConversation) ||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation)
},
userProfileLink () { userProfileLink () {
return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name) return this.generateUserProfileLink(this.status.user.id, this.status.user.screen_name)
}, },
@ -118,7 +101,13 @@ const Status = {
return hits return hits
}, },
muted () { return !this.unmuted && ((!(this.inProfile && this.status.user.id === this.profileUserId) && this.status.user.muted) || (!this.inConversation && this.status.thread_muted) || this.muteWordHits.length > 0) }, muted () {
const relationship = this.$store.getters.relationship(this.status.user.id)
return !this.unmuted && (
(!(this.inProfile && this.status.user.id === this.profileUserId) && relationship.muting) ||
(!this.inConversation && this.status.thread_muted) ||
this.muteWordHits.length > 0)
},
hideFilteredStatuses () { hideFilteredStatuses () {
return this.mergedConfig.hideFilteredStatuses return this.mergedConfig.hideFilteredStatuses
}, },
@ -135,20 +124,6 @@ const Status = {
// use conversation highlight only when in conversation // use conversation highlight only when in conversation
return this.status.id === this.highlight return this.status.id === this.highlight
}, },
// This is a bit hacky, but we want to approximate post height before rendering
// so we count newlines (masto uses <p> for paragraphs, GS uses <br> between them)
// as well as approximate line count by counting characters and approximating ~80
// per line.
//
// Using max-height + overflow: auto for status components resulted in false positives
// very often with japanese characters, and it was very annoying.
tallStatus () {
const lengthScore = this.status.statusnet_html.split(/<p|<br/).length + this.status.text.length / 80
return lengthScore > 20
},
longSubject () {
return this.status.summary.length > 900
},
isReply () { isReply () {
return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id) return !!(this.status.in_reply_to_status_id && this.status.in_reply_to_user_id)
}, },
@ -178,8 +153,11 @@ const Status = {
if (this.status.user.id === this.status.attentions[i].id) { if (this.status.user.id === this.status.attentions[i].id) {
continue continue
} }
const taggedUser = this.$store.getters.findUser(this.status.attentions[i].id) // There's zero guarantee of this working. If we happen to have that user and their
if (checkFollowing && taggedUser && taggedUser.following) { // relationship in store then it will work, but there's kinda little chance of having
// them for people you're not following.
const relationship = this.$store.state.users.relationships[this.status.attentions[i].id]
if (checkFollowing && relationship && relationship.following) {
return false return false
} }
if (this.status.attentions[i].id === this.currentUser.id) { if (this.status.attentions[i].id === this.currentUser.id) {
@ -188,33 +166,6 @@ const Status = {
} }
return this.status.attentions.length > 0 return this.status.attentions.length > 0
}, },
hideSubjectStatus () {
if (this.tallStatus && !this.localCollapseSubjectDefault) {
return false
}
return !this.expandingSubject && this.status.summary
},
hideTallStatus () {
if (this.status.summary && this.localCollapseSubjectDefault) {
return false
}
if (this.showingTall) {
return false
}
return this.tallStatus
},
showingMore () {
return (this.tallStatus && this.showingTall) || (this.status.summary && this.expandingSubject)
},
nsfwClickthrough () {
if (!this.status.nsfw) {
return false
}
if (this.status.summary && this.localCollapseSubjectDefault) {
return false
}
return true
},
replySubject () { replySubject () {
if (!this.status.summary) return '' if (!this.status.summary) return ''
const decodedSummary = unescape(this.status.summary) const decodedSummary = unescape(this.status.summary)
@ -228,83 +179,6 @@ const Status = {
return '' return ''
} }
}, },
attachmentSize () {
if ((this.mergedConfig.hideAttachments && !this.inConversation) ||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation) ||
(this.status.attachments.length > this.maxThumbnails)) {
return 'hide'
} else if (this.compact) {
return 'small'
}
return 'normal'
},
galleryTypes () {
if (this.attachmentSize === 'hide') {
return []
}
return this.mergedConfig.playVideosInModal
? ['image', 'video']
: ['image']
},
galleryAttachments () {
return this.status.attachments.filter(
file => fileType.fileMatchesSomeType(this.galleryTypes, file)
)
},
nonGalleryAttachments () {
return this.status.attachments.filter(
file => !fileType.fileMatchesSomeType(this.galleryTypes, file)
)
},
hasImageAttachments () {
return this.status.attachments.some(
file => fileType.fileType(file.mimetype) === 'image'
)
},
hasVideoAttachments () {
return this.status.attachments.some(
file => fileType.fileType(file.mimetype) === 'video'
)
},
maxThumbnails () {
return this.mergedConfig.maxThumbnails
},
postBodyHtml () {
const html = this.status.statusnet_html
if (this.mergedConfig.greentext) {
try {
if (html.includes('&gt;')) {
// This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works
return processHtml(html, (string) => {
if (string.includes('&gt;') &&
string
.replace(/<[^>]+?>/gi, '') // remove all tags
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
.trim()
.startsWith('&gt;')) {
return `<span class='greentext'>${string}</span>`
} else {
return string
}
})
} else {
return html
}
} catch (e) {
console.err('Failed to process status html', e)
return html
}
} else {
return html
}
},
contentHtml () {
if (!this.status.summary_html) {
return this.postBodyHtml
}
return this.status.summary_html + '<br />' + this.postBodyHtml
},
combinedFavsAndRepeatsUsers () { combinedFavsAndRepeatsUsers () {
// Use the status from the global status repository since favs and repeats are saved in it // Use the status from the global status repository since favs and repeats are saved in it
const combinedUsers = [].concat( const combinedUsers = [].concat(
@ -313,9 +187,6 @@ const Status = {
) )
return uniqBy(combinedUsers, 'id') return uniqBy(combinedUsers, 'id')
}, },
ownStatus () {
return this.status.user.id === this.currentUser.id
},
tags () { tags () {
return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ') return this.status.tags.filter(tagObj => tagObj.hasOwnProperty('name')).map(tagObj => tagObj.name).join(' ')
}, },
@ -329,21 +200,18 @@ const Status = {
}) })
}, },
components: { components: {
Attachment,
FavoriteButton, FavoriteButton,
ReactButton, ReactButton,
RetweetButton, RetweetButton,
ExtraButtons, ExtraButtons,
PostStatusForm, PostStatusForm,
Poll,
UserCard, UserCard,
UserAvatar, UserAvatar,
Gallery,
LinkPreview,
AvatarList, AvatarList,
Timeago, Timeago,
StatusPopover, StatusPopover,
EmojiReactions EmojiReactions,
StatusContent
}, },
methods: { methods: {
visibilityIcon (visibility) { visibilityIcon (visibility) {
@ -364,32 +232,6 @@ const Status = {
clearError () { clearError () {
this.error = undefined this.error = undefined
}, },
linkClicked (event) {
const target = event.target.closest('.status-content a')
if (target) {
if (target.className.match(/mention/)) {
const href = target.href
const attn = this.status.attentions.find(attn => mentionMatchesUrl(attn, href))
if (attn) {
event.stopPropagation()
event.preventDefault()
const link = this.generateUserProfileLink(attn.id, attn.screen_name)
this.$router.push(link)
return
}
}
if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) {
// Extract tag name from link url
const tag = extractTagFromUrl(target.href)
if (tag) {
const link = this.generateTagLink(tag)
this.$router.push(link)
return
}
}
window.open(target.href, '_blank')
}
},
toggleReplying () { toggleReplying () {
this.replying = !this.replying this.replying = !this.replying
}, },
@ -407,26 +249,8 @@ const Status = {
toggleUserExpanded () { toggleUserExpanded () {
this.userExpanded = !this.userExpanded this.userExpanded = !this.userExpanded
}, },
toggleShowMore () {
if (this.showingTall) {
this.showingTall = false
} else if (this.expandingSubject && this.status.summary) {
this.expandingSubject = false
} else if (this.hideTallStatus) {
this.showingTall = true
} else if (this.hideSubjectStatus && this.status.summary) {
this.expandingSubject = true
}
},
generateUserProfileLink (id, name) { generateUserProfileLink (id, name) {
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames) return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
},
generateTagLink (tag) {
return `/tag/${tag}`
},
setMedia () {
const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments
return () => this.$store.dispatch('setMedia', attachments)
} }
}, },
watch: { watch: {

View file

@ -94,7 +94,7 @@
<div class="status-body"> <div class="status-body">
<UserCard <UserCard
v-if="userExpanded" v-if="userExpanded"
:user="status.user" :user-id="status.user.id"
:rounded="true" :rounded="true"
:bordered="true" :bordered="true"
class="status-usercard" class="status-usercard"
@ -177,6 +177,8 @@
<StatusPopover <StatusPopover
v-if="!isPreview" v-if="!isPreview"
:status-id="status.in_reply_to_status_id" :status-id="status.in_reply_to_status_id"
class="reply-to-popover"
style="min-width: 0"
> >
<a <a
class="reply-to" class="reply-to"
@ -224,118 +226,12 @@
</div> </div>
</div> </div>
<div <StatusContent
v-if="longSubject" :status="status"
class="status-content-wrapper" :no-heading="noHeading"
:class="{ 'tall-status': !showingLongSubject }" :highlight="highlight"
> :focused="isFocused"
<a
v-if="!showingLongSubject"
class="tall-status-hider"
:class="{ 'tall-status-hider_focused': isFocused }"
href="#"
@click.prevent="showingLongSubject=true"
>{{ $t("general.show_more") }}</a>
<div
class="status-content media-body"
@click.prevent="linkClicked"
v-html="contentHtml"
/> />
<a
v-if="showingLongSubject"
href="#"
class="status-unhider"
@click.prevent="showingLongSubject=false"
>{{ $t("general.show_less") }}</a>
</div>
<div
v-else
:class="{'tall-status': hideTallStatus}"
class="status-content-wrapper"
>
<a
v-if="hideTallStatus"
class="tall-status-hider"
:class="{ 'tall-status-hider_focused': isFocused }"
href="#"
@click.prevent="toggleShowMore"
>{{ $t("general.show_more") }}</a>
<div
v-if="!hideSubjectStatus"
class="status-content media-body"
@click.prevent="linkClicked"
v-html="contentHtml"
/>
<div
v-else
class="status-content media-body"
@click.prevent="linkClicked"
v-html="status.summary_html"
/>
<a
v-if="hideSubjectStatus"
href="#"
class="cw-status-hider"
@click.prevent="toggleShowMore"
>
{{ $t("general.show_more") }}
<span
v-if="hasImageAttachments"
class="icon-picture"
/>
<span
v-if="hasVideoAttachments"
class="icon-video"
/>
<span
v-if="status.card"
class="icon-link"
/>
</a>
<a
v-if="showingMore"
href="#"
class="status-unhider"
@click.prevent="toggleShowMore"
>{{ $t("general.show_less") }}</a>
</div>
<div v-if="status.poll && status.poll.options">
<poll :base-poll="status.poll" />
</div>
<div
v-if="status.attachments && (!hideSubjectStatus || showingLongSubject)"
class="attachments media-body"
>
<attachment
v-for="attachment in nonGalleryAttachments"
:key="attachment.id"
class="non-gallery"
:size="attachmentSize"
:nsfw="nsfwClickthrough"
:attachment="attachment"
:allow-play="true"
:set-media="setMedia()"
/>
<gallery
v-if="galleryAttachments.length > 0"
:nsfw="nsfwClickthrough"
:attachments="galleryAttachments"
:set-media="setMedia()"
/>
</div>
<div
v-if="status.card && !hideSubjectStatus && !noHeading"
class="link-preview media-body"
>
<link-preview
:card="status.card"
:size="attachmentSize"
:nsfw="nsfwClickthrough"
/>
</div>
<transition name="fade"> <transition name="fade">
<div <div
@ -402,7 +298,7 @@
:status="status" :status="status"
/> />
<ReactButton <ReactButton
:logged-in="loggedIn" v-if="loggedIn"
:status="status" :status="status"
/> />
<extra-buttons <extra-buttons
@ -572,11 +468,10 @@ $status-margin: 0.75em;
align-items: stretch; align-items: stretch;
> .reply-to-and-accountname > a { > .reply-to-and-accountname > a {
overflow: hidden;
max-width: 100%; max-width: 100%;
text-overflow: ellipsis; text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap; white-space: nowrap;
display: inline-block;
word-break: break-all; word-break: break-all;
} }
} }
@ -585,7 +480,6 @@ $status-margin: 0.75em;
display: flex; display: flex;
height: 18px; height: 18px;
margin-right: 0.5em; margin-right: 0.5em;
overflow: hidden;
max-width: 100%; max-width: 100%;
.icon-reply { .icon-reply {
transform: scaleX(-1); transform: scaleX(-1);
@ -596,6 +490,10 @@ $status-margin: 0.75em;
display: flex; display: flex;
} }
.reply-to-popover {
min-width: 0;
}
.reply-to { .reply-to {
display: flex; display: flex;
} }
@ -603,6 +501,7 @@ $status-margin: 0.75em;
.reply-to-text { .reply-to-text {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap;
margin: 0 0.4em 0 0.2em; margin: 0 0.4em 0 0.2em;
} }
@ -625,105 +524,6 @@ $status-margin: 0.75em;
} }
} }
.tall-status {
position: relative;
height: 220px;
overflow-x: hidden;
overflow-y: hidden;
z-index: 1;
.status-content {
height: 100%;
mask: linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat,
linear-gradient(to top, white, white);
/* Autoprefixed seem to ignore this one, and also syntax is different */
-webkit-mask-composite: xor;
mask-composite: exclude;
}
}
.tall-status-hider {
display: inline-block;
word-break: break-all;
position: absolute;
height: 70px;
margin-top: 150px;
width: 100%;
text-align: center;
line-height: 110px;
z-index: 2;
}
.status-unhider, .cw-status-hider {
width: 100%;
text-align: center;
display: inline-block;
word-break: break-all;
}
.status-content {
font-family: var(--postFont, sans-serif);
line-height: 1.4em;
white-space: pre-wrap;
a {
color: $fallback--link;
color: var(--postLink, $fallback--link);
}
img, video {
max-width: 100%;
max-height: 400px;
vertical-align: middle;
object-fit: contain;
&.emoji {
width: 32px;
height: 32px;
}
}
blockquote {
margin: 0.2em 0 0.2em 2em;
font-style: italic;
}
pre {
overflow: auto;
}
code, samp, kbd, var, pre {
font-family: var(--postCodeFont, monospace);
}
p {
margin: 0 0 1em 0;
}
p:last-child {
margin: 0 0 0 0;
}
h1 {
font-size: 1.1em;
line-height: 1.2em;
margin: 1.4em 0;
}
h2 {
font-size: 1.1em;
margin: 1.0em 0;
}
h3 {
font-size: 1em;
margin: 1.2em 0;
}
h4 {
margin: 1.1em 0;
}
}
.retweet-info { .retweet-info {
padding: 0.4em $status-margin; padding: 0.4em $status-margin;
margin: 0; margin: 0;
@ -785,11 +585,6 @@ $status-margin: 0.75em;
} }
} }
.greentext {
color: $fallback--cGreen;
color: var(--cGreen, $fallback--cGreen);
}
.status-conversation { .status-conversation {
border-left-style: solid; border-left-style: solid;
} }
@ -861,14 +656,6 @@ a.unmute {
flex: 1; flex: 1;
} }
.timeline :not(.panel-disabled) > {
.status-el:last-child {
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
border-bottom: none;
}
}
.favs-repeated-users { .favs-repeated-users {
margin-top: $status-margin; margin-top: $status-margin;

View file

@ -0,0 +1,210 @@
import Attachment from '../attachment/attachment.vue'
import Poll from '../poll/poll.vue'
import Gallery from '../gallery/gallery.vue'
import LinkPreview from '../link-preview/link-preview.vue'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import fileType from 'src/services/file_type/file_type.service'
import { processHtml } from 'src/services/tiny_post_html_processor/tiny_post_html_processor.service.js'
import { mentionMatchesUrl, extractTagFromUrl } from 'src/services/matcher/matcher.service.js'
import { mapGetters, mapState } from 'vuex'
const StatusContent = {
name: 'StatusContent',
props: [
'status',
'focused',
'noHeading',
'fullContent'
],
data () {
return {
showingTall: this.inConversation && this.focused,
showingLongSubject: false,
// not as computed because it sets the initial state which will be changed later
expandingSubject: !this.$store.getters.mergedConfig.collapseMessageWithSubject
}
},
computed: {
localCollapseSubjectDefault () {
return this.mergedConfig.collapseMessageWithSubject
},
hideAttachments () {
return (this.mergedConfig.hideAttachments && !this.inConversation) ||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation)
},
// This is a bit hacky, but we want to approximate post height before rendering
// so we count newlines (masto uses <p> for paragraphs, GS uses <br> between them)
// as well as approximate line count by counting characters and approximating ~80
// per line.
//
// Using max-height + overflow: auto for status components resulted in false positives
// very often with japanese characters, and it was very annoying.
tallStatus () {
const lengthScore = this.status.statusnet_html.split(/<p|<br/).length + this.status.text.length / 80
return lengthScore > 20
},
longSubject () {
return this.status.summary.length > 900
},
// When a status has a subject and is also tall, we should only have one show more/less button. If the default is to collapse statuses with subjects, we just treat it like a status with a subject; otherwise, we just treat it like a tall status.
mightHideBecauseSubject () {
return this.status.summary && (!this.tallStatus || this.localCollapseSubjectDefault)
},
mightHideBecauseTall () {
return this.tallStatus && (!this.status.summary || !this.localCollapseSubjectDefault)
},
hideSubjectStatus () {
return this.mightHideBecauseSubject && !this.expandingSubject
},
hideTallStatus () {
return this.mightHideBecauseTall && !this.showingTall
},
showingMore () {
return (this.mightHideBecauseTall && this.showingTall) || (this.mightHideBecauseSubject && this.expandingSubject)
},
nsfwClickthrough () {
if (!this.status.nsfw) {
return false
}
if (this.status.summary && this.localCollapseSubjectDefault) {
return false
}
return true
},
attachmentSize () {
if ((this.mergedConfig.hideAttachments && !this.inConversation) ||
(this.mergedConfig.hideAttachmentsInConv && this.inConversation) ||
(this.status.attachments.length > this.maxThumbnails)) {
return 'hide'
} else if (this.compact) {
return 'small'
}
return 'normal'
},
galleryTypes () {
if (this.attachmentSize === 'hide') {
return []
}
return this.mergedConfig.playVideosInModal
? ['image', 'video']
: ['image']
},
galleryAttachments () {
return this.status.attachments.filter(
file => fileType.fileMatchesSomeType(this.galleryTypes, file)
)
},
nonGalleryAttachments () {
return this.status.attachments.filter(
file => !fileType.fileMatchesSomeType(this.galleryTypes, file)
)
},
hasImageAttachments () {
return this.status.attachments.some(
file => fileType.fileType(file.mimetype) === 'image'
)
},
hasVideoAttachments () {
return this.status.attachments.some(
file => fileType.fileType(file.mimetype) === 'video'
)
},
maxThumbnails () {
return this.mergedConfig.maxThumbnails
},
postBodyHtml () {
const html = this.status.statusnet_html
if (this.mergedConfig.greentext) {
try {
if (html.includes('&gt;')) {
// This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works
return processHtml(html, (string) => {
if (string.includes('&gt;') &&
string
.replace(/<[^>]+?>/gi, '') // remove all tags
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
.trim()
.startsWith('&gt;')) {
return `<span class='greentext'>${string}</span>`
} else {
return string
}
})
} else {
return html
}
} catch (e) {
console.err('Failed to process status html', e)
return html
}
} else {
return html
}
},
contentHtml () {
if (!this.status.summary_html) {
return this.postBodyHtml
}
return this.status.summary_html + '<br />' + this.postBodyHtml
},
...mapGetters(['mergedConfig']),
...mapState({
betterShadow: state => state.interface.browserSupport.cssFilter,
currentUser: state => state.users.currentUser
})
},
components: {
Attachment,
Poll,
Gallery,
LinkPreview
},
methods: {
linkClicked (event) {
const target = event.target.closest('.status-content a')
if (target) {
if (target.className.match(/mention/)) {
const href = target.href
const attn = this.status.attentions.find(attn => mentionMatchesUrl(attn, href))
if (attn) {
event.stopPropagation()
event.preventDefault()
const link = this.generateUserProfileLink(attn.id, attn.screen_name)
this.$router.push(link)
return
}
}
if (target.rel.match(/(?:^|\s)tag(?:$|\s)/) || target.className.match(/hashtag/)) {
// Extract tag name from dataset or link url
const tag = target.dataset.tag || extractTagFromUrl(target.href)
if (tag) {
const link = this.generateTagLink(tag)
this.$router.push(link)
return
}
}
window.open(target.href, '_blank')
}
},
toggleShowMore () {
if (this.mightHideBecauseTall) {
this.showingTall = !this.showingTall
} else if (this.mightHideBecauseSubject) {
this.expandingSubject = !this.expandingSubject
}
},
generateUserProfileLink (id, name) {
return generateProfileLink(id, name, this.$store.state.instance.restrictedNicknames)
},
generateTagLink (tag) {
return `/tag/${tag}`
},
setMedia () {
const attachments = this.attachmentSize === 'hide' ? this.status.attachments : this.galleryAttachments
return () => this.$store.dispatch('setMedia', attachments)
}
}
}
export default StatusContent

View file

@ -0,0 +1,240 @@
<template>
<!-- eslint-disable vue/no-v-html -->
<div class="status-body">
<slot name="header" />
<div
v-if="longSubject"
class="status-content-wrapper"
:class="{ 'tall-status': !showingLongSubject }"
>
<a
v-if="!showingLongSubject"
class="tall-status-hider"
:class="{ 'tall-status-hider_focused': focused }"
href="#"
@click.prevent="showingLongSubject=true"
>
{{ $t("general.show_more") }}
<span
v-if="hasImageAttachments"
class="icon-picture"
/>
<span
v-if="hasVideoAttachments"
class="icon-video"
/>
<span
v-if="status.card"
class="icon-link"
/>
</a>
<div
class="status-content media-body"
@click.prevent="linkClicked"
v-html="contentHtml"
/>
<a
v-if="showingLongSubject"
href="#"
class="status-unhider"
@click.prevent="showingLongSubject=false"
>{{ $t("general.show_less") }}</a>
</div>
<div
v-else
:class="{'tall-status': hideTallStatus}"
class="status-content-wrapper"
>
<a
v-if="hideTallStatus"
class="tall-status-hider"
:class="{ 'tall-status-hider_focused': focused }"
href="#"
@click.prevent="toggleShowMore"
>{{ $t("general.show_more") }}</a>
<div
v-if="!hideSubjectStatus"
class="status-content media-body"
@click.prevent="linkClicked"
v-html="contentHtml"
/>
<div
v-else
class="status-content media-body"
@click.prevent="linkClicked"
v-html="status.summary_html"
/>
<a
v-if="hideSubjectStatus"
href="#"
class="cw-status-hider"
@click.prevent="toggleShowMore"
>{{ $t("general.show_more") }}</a>
<a
v-if="showingMore"
href="#"
class="status-unhider"
@click.prevent="toggleShowMore"
>{{ $t("general.show_less") }}</a>
</div>
<div v-if="status.poll && status.poll.options">
<poll :base-poll="status.poll" />
</div>
<div
v-if="status.attachments.length !== 0 && (!hideSubjectStatus || showingLongSubject)"
class="attachments media-body"
>
<attachment
v-for="attachment in nonGalleryAttachments"
:key="attachment.id"
class="non-gallery"
:size="attachmentSize"
:nsfw="nsfwClickthrough"
:attachment="attachment"
:allow-play="true"
:set-media="setMedia()"
/>
<gallery
v-if="galleryAttachments.length > 0"
:nsfw="nsfwClickthrough"
:attachments="galleryAttachments"
:set-media="setMedia()"
/>
</div>
<div
v-if="status.card && !hideSubjectStatus && !noHeading"
class="link-preview media-body"
>
<link-preview
:card="status.card"
:size="attachmentSize"
:nsfw="nsfwClickthrough"
/>
</div>
<slot name="footer" />
</div>
<!-- eslint-enable vue/no-v-html -->
</template>
<script src="./status_content.js" ></script>
<style lang="scss">
@import '../../_variables.scss';
$status-margin: 0.75em;
.status-body {
flex: 1;
min-width: 0;
.tall-status {
position: relative;
height: 220px;
overflow-x: hidden;
overflow-y: hidden;
z-index: 1;
.status-content {
height: 100%;
mask: linear-gradient(to top, white, transparent) bottom/100% 70px no-repeat,
linear-gradient(to top, white, white);
/* Autoprefixed seem to ignore this one, and also syntax is different */
-webkit-mask-composite: xor;
mask-composite: exclude;
}
}
.tall-status-hider {
display: inline-block;
word-break: break-all;
position: absolute;
height: 70px;
margin-top: 150px;
width: 100%;
text-align: center;
line-height: 110px;
z-index: 2;
}
.status-unhider, .cw-status-hider {
width: 100%;
text-align: center;
display: inline-block;
word-break: break-all;
}
.status-content {
font-family: var(--postFont, sans-serif);
line-height: 1.4em;
white-space: pre-wrap;
img, video {
max-width: 100%;
max-height: 400px;
vertical-align: middle;
object-fit: contain;
&.emoji {
width: 32px;
height: 32px;
}
}
blockquote {
margin: 0.2em 0 0.2em 2em;
font-style: italic;
}
pre {
overflow: auto;
}
code, samp, kbd, var, pre {
font-family: var(--postCodeFont, monospace);
}
p {
margin: 0 0 1em 0;
}
p:last-child {
margin: 0 0 0 0;
}
h1 {
font-size: 1.1em;
line-height: 1.2em;
margin: 1.4em 0;
}
h2 {
font-size: 1.1em;
margin: 1.0em 0;
}
h3 {
font-size: 1em;
margin: 1.2em 0;
}
h4 {
margin: 1.1em 0;
}
}
}
.greentext {
color: $fallback--cGreen;
color: var(--cGreen, $fallback--cGreen);
}
.timeline :not(.panel-disabled) > {
.status-el:last-child {
border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius;
border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius);
border-bottom: none;
}
}
</style>

View file

@ -7,11 +7,7 @@ const StatusPopover = {
], ],
data () { data () {
return { return {
popperOptions: { error: false
modifiers: {
preventOverflow: { padding: { top: 50 }, boundariesElement: 'viewport' }
}
}
} }
}, },
computed: { computed: {
@ -20,12 +16,15 @@ const StatusPopover = {
} }
}, },
components: { components: {
Status: () => import('../status/status.vue') Status: () => import('../status/status.vue'),
Popover: () => import('../popover/popover.vue')
}, },
methods: { methods: {
enter () { enter () {
if (!this.status) { if (!this.status) {
this.$store.dispatch('fetchStatus', this.statusId) this.$store.dispatch('fetchStatus', this.statusId)
.then(data => (this.error = false))
.catch(e => (this.error = true))
} }
} }
} }

View file

@ -1,27 +1,36 @@
<template> <template>
<v-popover <Popover
trigger="hover"
popover-class="status-popover" popover-class="status-popover"
placement="top-start" :bound-to="{ x: 'container' }"
:popper-options="popperOptions" @show="enter"
@show="enter()" >
<template slot="trigger">
<slot />
</template>
<div
slot="content"
> >
<template slot="popover">
<Status <Status
v-if="status" v-if="status"
:is-preview="true" :is-preview="true"
:statusoid="status" :statusoid="status"
:compact="true" :compact="true"
/> />
<div
v-else-if="error"
class="status-preview-no-content faint"
>
{{ $t('status.status_unavailable') }}
</div>
<div <div
v-else v-else
class="status-preview-loading" class="status-preview-no-content"
> >
<i class="icon-spin4 animate-spin" /> <i class="icon-spin4 animate-spin" />
</div> </div>
</template> </div>
</Popover>
<slot />
</v-popover>
</template> </template>
<script src="./status_popover.js" ></script> <script src="./status_popover.js" ></script>
@ -29,13 +38,11 @@
<style lang="scss"> <style lang="scss">
@import '../../_variables.scss'; @import '../../_variables.scss';
.tooltip.popover.status-popover { .status-popover {
font-size: 1rem; font-size: 1rem;
min-width: 15em; min-width: 15em;
max-width: 95%; max-width: 95%;
margin-left: 0.5em;
.popover-inner {
border-color: $fallback--border; border-color: $fallback--border;
border-color: var(--border, $fallback--border); border-color: var(--border, $fallback--border);
border-style: solid; border-style: solid;
@ -44,35 +51,12 @@
border-radius: var(--tooltipRadius, $fallback--tooltipRadius); border-radius: var(--tooltipRadius, $fallback--tooltipRadius);
box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5); box-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
box-shadow: var(--popupShadow); box-shadow: var(--popupShadow);
}
.popover-arrow::before {
position: absolute;
content: '';
left: -7px;
border: solid 7px transparent;
z-index: -1;
}
&[x-placement^="bottom-start"] .popover-arrow::before {
top: -2px;
border-top-width: 0;
border-bottom-color: $fallback--border;
border-bottom-color: var(--border, $fallback--border);
}
&[x-placement^="top-start"] .popover-arrow::before {
bottom: -2px;
border-bottom-width: 0;
border-top-color: $fallback--border;
border-top-color: var(--border, $fallback--border);
}
.status-el.status-el { .status-el.status-el {
border: none; border: none;
} }
.status-preview-loading { .status-preview-no-content {
padding: 1em; padding: 1em;
text-align: center; text-align: center;

View file

@ -4,13 +4,12 @@ import ProgressButton from '../progress_button/progress_button.vue'
import FollowButton from '../follow_button/follow_button.vue' import FollowButton from '../follow_button/follow_button.vue'
import ModerationTools from '../moderation_tools/moderation_tools.vue' import ModerationTools from '../moderation_tools/moderation_tools.vue'
import AccountActions from '../account_actions/account_actions.vue' import AccountActions from '../account_actions/account_actions.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
export default { export default {
props: [ props: [
'user', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar' 'userId', 'switcher', 'selected', 'hideBio', 'rounded', 'bordered', 'allowZoomingAvatar'
], ],
data () { data () {
return { return {
@ -22,6 +21,12 @@ export default {
this.$store.dispatch('fetchUserRelationship', this.user.id) this.$store.dispatch('fetchUserRelationship', this.user.id)
}, },
computed: { computed: {
user () {
return this.$store.getters.findUser(this.userId)
},
relationship () {
return this.$store.getters.relationship(this.userId)
},
classes () { classes () {
return [{ return [{
'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius 'user-card-rounded-t': this.rounded === 'top', // set border-top-left-radius and border-top-right-radius
@ -30,22 +35,12 @@ export default {
}] }]
}, },
style () { style () {
const color = this.$store.getters.mergedConfig.customTheme.colors
? this.$store.getters.mergedConfig.customTheme.colors.bg // v2
: this.$store.getters.mergedConfig.colors.bg // v1
if (color) {
const rgb = (typeof color === 'string') ? hex2rgb(color) : color
const tintColor = `rgba(${Math.floor(rgb.r)}, ${Math.floor(rgb.g)}, ${Math.floor(rgb.b)}, .5)`
return { return {
backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`,
backgroundImage: [ backgroundImage: [
`linear-gradient(to bottom, ${tintColor}, ${tintColor})`, `linear-gradient(to bottom, var(--profileTint), var(--profileTint))`,
`url(${this.user.cover_photo})` `url(${this.user.cover_photo})`
].join(', ') ].join(', ')
} }
}
}, },
isOtherUser () { isOtherUser () {
return this.user.id !== this.$store.state.users.currentUser.id return this.user.id !== this.$store.state.users.currentUser.id

View file

@ -69,6 +69,7 @@
<AccountActions <AccountActions
v-if="isOtherUser && loggedIn" v-if="isOtherUser && loggedIn"
:user="user" :user="user"
:relationship="relationship"
/> />
</div> </div>
<div class="bottom-line"> <div class="bottom-line">
@ -92,7 +93,7 @@
</div> </div>
<div class="user-meta"> <div class="user-meta">
<div <div
v-if="user.follows_you && loggedIn && isOtherUser" v-if="relationship.followed_by && loggedIn && isOtherUser"
class="following" class="following"
> >
{{ $t('user_card.follows_you') }} {{ $t('user_card.follows_you') }}
@ -139,10 +140,10 @@
class="user-interactions" class="user-interactions"
> >
<div class="btn-group"> <div class="btn-group">
<FollowButton :user="user" /> <FollowButton :relationship="relationship" />
<template v-if="user.following"> <template v-if="relationship.following">
<ProgressButton <ProgressButton
v-if="!user.subscribed" v-if="!relationship.subscribing"
class="btn btn-default" class="btn btn-default"
:click="subscribeUser" :click="subscribeUser"
:title="$t('user_card.subscribe')" :title="$t('user_card.subscribe')"
@ -161,7 +162,7 @@
</div> </div>
<div> <div>
<button <button
v-if="user.muted" v-if="relationship.muting"
class="btn btn-default btn-block toggled" class="btn btn-default btn-block toggled"
@click="unmuteUser" @click="unmuteUser"
> >
@ -286,6 +287,7 @@
mask-size: 100% 60%; mask-size: 100% 60%;
border-top-left-radius: calc(var(--panelRadius) - 1px); border-top-left-radius: calc(var(--panelRadius) - 1px);
border-top-right-radius: calc(var(--panelRadius) - 1px); border-top-right-radius: calc(var(--panelRadius) - 1px);
background-color: var(--profileBg);
&.hide-bio { &.hide-bio {
mask-size: 100% 40px; mask-size: 100% 40px;

View file

@ -6,7 +6,7 @@
class="panel panel-default signed-in" class="panel panel-default signed-in"
> >
<UserCard <UserCard
:user="user" :user-id="user.id"
:hide-bio="true" :hide-bio="true"
rounded="top" rounded="top"
/> />

View file

@ -5,7 +5,7 @@
class="user-profile panel panel-default" class="user-profile panel panel-default"
> >
<UserCard <UserCard
:user="user" :user-id="userId"
:switcher="true" :switcher="true"
:selected="timeline.viewing" :selected="timeline.viewing"
:allow-zooming-avatar="true" :allow-zooming-avatar="true"

View file

@ -112,7 +112,7 @@ const UserSettings = {
...this.$store.state.instance.customEmoji ...this.$store.state.instance.customEmoji
], ],
users: this.$store.state.users.users, users: this.$store.state.users.users,
updateUsersList: (input) => this.$store.dispatch('searchUsers', input) updateUsersList: (query) => this.$store.dispatch('searchUsers', { query })
}) })
}, },
emojiSuggestor () { emojiSuggestor () {
@ -351,18 +351,18 @@ const UserSettings = {
}, },
filterUnblockedUsers (userIds) { filterUnblockedUsers (userIds) {
return reject(userIds, (userId) => { return reject(userIds, (userId) => {
const user = this.$store.getters.findUser(userId) const relationship = this.$store.getters.relationship(this.userId)
return !user || user.statusnet_blocking || user.id === this.$store.state.users.currentUser.id return relationship.blocking || userId === this.$store.state.users.currentUser.id
}) })
}, },
filterUnMutedUsers (userIds) { filterUnMutedUsers (userIds) {
return reject(userIds, (userId) => { return reject(userIds, (userId) => {
const user = this.$store.getters.findUser(userId) const relationship = this.$store.getters.relationship(this.userId)
return !user || user.muted || user.id === this.$store.state.users.currentUser.id return relationship.muting || userId === this.$store.state.users.currentUser.id
}) })
}, },
queryUserIds (query) { queryUserIds (query) {
return this.$store.dispatch('searchUsers', query) return this.$store.dispatch('searchUsers', { query })
.then((users) => map(users, 'id')) .then((users) => map(users, 'id'))
}, },
blockUsers (ids) { blockUsers (ids) {

View file

@ -379,6 +379,7 @@
:label="$t('settings.notifications')" :label="$t('settings.notifications')"
> >
<div class="setting-item"> <div class="setting-item">
<h2>{{ $t('settings.notification_setting_filters') }}</h2>
<div class="select-multiple"> <div class="select-multiple">
<span class="label">{{ $t('settings.notification_setting') }}</span> <span class="label">{{ $t('settings.notification_setting') }}</span>
<ul class="option-list"> <ul class="option-list">
@ -404,6 +405,17 @@
</li> </li>
</ul> </ul>
</div> </div>
</div>
<div class="setting-item">
<h2>{{ $t('settings.notification_setting_privacy') }}</h2>
<p>
<Checkbox v-model="notificationSettings.privacy_option">
{{ $t('settings.notification_setting_privacy_option') }}
</Checkbox>
</p>
</div>
<div class="setting-item">
<p>{{ $t('settings.notification_mutes') }}</p> <p>{{ $t('settings.notification_mutes') }}</p>
<p>{{ $t('settings.notification_blocks') }}</p> <p>{{ $t('settings.notification_blocks') }}</p>
<button <button

View file

@ -7,8 +7,8 @@
"gopher": "Gopher", "gopher": "Gopher",
"media_proxy": "Medienproxy", "media_proxy": "Medienproxy",
"scope_options": "Reichweitenoptionen", "scope_options": "Reichweitenoptionen",
"text_limit": "Textlimit", "text_limit": "Zeichenlimit",
"title": "Features", "title": "Funktionen",
"who_to_follow": "Wem folgen?" "who_to_follow": "Wem folgen?"
}, },
"finder": { "finder": {
@ -17,7 +17,18 @@
}, },
"general": { "general": {
"apply": "Anwenden", "apply": "Anwenden",
"submit": "Absenden" "submit": "Absenden",
"more": "Mehr",
"generic_error": "Ein Fehler ist aufgetreten",
"optional": "Optional",
"show_more": "Zeige mehr",
"show_less": "Zeige weniger",
"dismiss": "Ablehnen",
"cancel": "Abbrechen",
"disable": "Deaktivieren",
"enable": "Aktivieren",
"confirm": "Bestätigen",
"verify": "Verifizieren"
}, },
"login": { "login": {
"login": "Anmelden", "login": "Anmelden",
@ -26,7 +37,16 @@
"password": "Passwort", "password": "Passwort",
"placeholder": "z.B. lain", "placeholder": "z.B. lain",
"register": "Registrieren", "register": "Registrieren",
"username": "Benutzername" "username": "Benutzername",
"authentication_code": "Authentifizierungscode",
"enter_recovery_code": "Gebe einen Wiederherstellungscode ein",
"recovery_code": "Wiederherstellungscode",
"heading": {
"totp": "Zwei-Faktor Authentifizierung",
"recovery": "Zwei-Faktor Wiederherstellung"
},
"hint": "Anmelden um an der Diskussion teilzunehmen",
"enter_two_factor_code": "Gebe einen Zwei-Faktor-Code ein"
}, },
"nav": { "nav": {
"about": "Über", "about": "Über",
@ -41,7 +61,9 @@
"twkn": "Das gesamte bekannte Netzwerk", "twkn": "Das gesamte bekannte Netzwerk",
"user_search": "Benutzersuche", "user_search": "Benutzersuche",
"search": "Suche", "search": "Suche",
"preferences": "Voreinstellungen" "preferences": "Voreinstellungen",
"administration": "Administration",
"who_to_follow": "Wem folgen"
}, },
"notifications": { "notifications": {
"broken_favorite": "Unbekannte Nachricht, suche danach...", "broken_favorite": "Unbekannte Nachricht, suche danach...",
@ -50,7 +72,11 @@
"load_older": "Ältere Benachrichtigungen laden", "load_older": "Ältere Benachrichtigungen laden",
"notifications": "Benachrichtigungen", "notifications": "Benachrichtigungen",
"read": "Gelesen!", "read": "Gelesen!",
"repeated_you": "wiederholte deine Nachricht" "repeated_you": "wiederholte deine Nachricht",
"follow_request": "möchte dir folgen",
"migrated_to": "migrierte zu",
"reacted_with": "reagierte mit {0}",
"no_more_notifications": "Keine Benachrichtigungen mehr"
}, },
"post_status": { "post_status": {
"new_status": "Neuen Status veröffentlichen", "new_status": "Neuen Status veröffentlichen",
@ -58,7 +84,10 @@
"account_not_locked_warning_link": "gesperrt", "account_not_locked_warning_link": "gesperrt",
"attachments_sensitive": "Anhänge als heikel markieren", "attachments_sensitive": "Anhänge als heikel markieren",
"content_type": { "content_type": {
"text/plain": "Nur Text" "text/plain": "Nur Text",
"text/bbcode": "BBCode",
"text/markdown": "Markdown",
"text/html": "HTML"
}, },
"content_warning": "Betreff (optional)", "content_warning": "Betreff (optional)",
"default": "Sitze gerade im Hofbräuhaus.", "default": "Sitze gerade im Hofbräuhaus.",
@ -69,6 +98,13 @@
"private": "Nur Follower - Beitrag nur für Follower sichtbar", "private": "Nur Follower - Beitrag nur für Follower sichtbar",
"public": "Öffentlich - Beitrag an öffentliche Zeitleisten", "public": "Öffentlich - Beitrag an öffentliche Zeitleisten",
"unlisted": "Nicht gelistet - Nicht in öffentlichen Zeitleisten anzeigen" "unlisted": "Nicht gelistet - Nicht in öffentlichen Zeitleisten anzeigen"
},
"direct_warning_to_all": "Dieser Beitrag wird für alle erwähnten Benutzer sichtbar sein.",
"direct_warning_to_first_only": "Dieser Beitrag wird für alle Benutzer, die am Anfang der Nachricht erwähnt wurden, sichtbar sein.",
"scope_notice": {
"public": "Dieser Beitrag wird für alle sichtbar sein",
"private": "Dieser Beitrag wird nur für deine Follower sichtbar sein",
"unlisted": "Dieser Beitrag wird weder in der öffentlichen Zeitleiste noch im gesamten bekannten Netzwerk sichtbar sein"
} }
}, },
"registration": { "registration": {
@ -86,8 +122,11 @@
"email_required": "darf nicht leer sein", "email_required": "darf nicht leer sein",
"password_required": "darf nicht leer sein", "password_required": "darf nicht leer sein",
"password_confirmation_required": "darf nicht leer sein", "password_confirmation_required": "darf nicht leer sein",
"password_confirmation_match": "sollte mit dem Passwort identisch sein." "password_confirmation_match": "sollte mit dem Passwort identisch sein"
} },
"bio_placeholder": "z.B.\nHallo, ich bin Lain.\nIch bin ein Anime Mödchen aus dem vorstädtischen Japan. Du kennst mich vielleicht vom Wired.",
"fullname_placeholder": "z.B. Lain Iwakura",
"username_placeholder": "z.B. lain"
}, },
"settings": { "settings": {
"attachmentRadius": "Anhänge", "attachmentRadius": "Anhänge",
@ -99,7 +138,7 @@
"background": "Hintergrund", "background": "Hintergrund",
"bio": "Bio", "bio": "Bio",
"btnRadius": "Buttons", "btnRadius": "Buttons",
"cBlue": "Blau (Antworten, Folgt dir)", "cBlue": "Blau (Antworten, folgt dir)",
"cGreen": "Grün (Retweet)", "cGreen": "Grün (Retweet)",
"cOrange": "Orange (Favorisieren)", "cOrange": "Orange (Favorisieren)",
"cRed": "Rot (Abbrechen)", "cRed": "Rot (Abbrechen)",
@ -115,21 +154,21 @@
"data_import_export_tab": "Datenimport/-export", "data_import_export_tab": "Datenimport/-export",
"default_vis": "Standard-Sichtbarkeitsumfang", "default_vis": "Standard-Sichtbarkeitsumfang",
"delete_account": "Account löschen", "delete_account": "Account löschen",
"delete_account_description": "Lösche deinen Account und alle deine Nachrichten unwiderruflich.", "delete_account_description": "Lösche deine Daten und deaktiviere deinen Account unwiderruflich.",
"delete_account_error": "Es ist ein Fehler beim Löschen deines Accounts aufgetreten. Tritt dies weiterhin auf, wende dich an den Administrator der Instanz.", "delete_account_error": "Es ist ein Fehler beim Löschen deines Accounts aufgetreten. Tritt dies weiterhin auf, wende dich an den Administrator der Instanz.",
"delete_account_instructions": "Tippe dein Passwort unten in das Feld ein, um die Löschung deines Accounts zu bestätigen.", "delete_account_instructions": "Tippe dein Passwort unten in das Feld ein, um die Löschung deines Accounts zu bestätigen.",
"discoverable": "Erlaubnis für automatisches Suchen nach diesem Account", "discoverable": "Erlaube, dass dieser Account in Suchergebnissen auftaucht",
"avatar_size_instruction": "Die empfohlene minimale Größe für Avatare ist 150x150 Pixel.", "avatar_size_instruction": "Die empfohlene minimale Größe für Avatare ist 150x150 Pixel.",
"pad_emoji": "Emojis mit Leerzeichen umrahmen", "pad_emoji": "Emojis mit Leerzeichen umrahmen",
"export_theme": "Farbschema speichern", "export_theme": "Farbschema speichern",
"filtering": "Filtern", "filtering": "Filtern",
"filtering_explanation": "Alle Beiträge die diese Wörter enthalten werden ausgeblendet. Ein Wort pro Zeile.", "filtering_explanation": "Alle Beiträge, welche diese Wörter enthalten, werden ausgeblendet. Ein Wort pro Zeile.",
"follow_export": "Follower exportieren", "follow_export": "Follower exportieren",
"follow_export_button": "Exportiere deine Follows in eine csv-Datei", "follow_export_button": "Exportiere deine Follows in eine csv-Datei",
"follow_export_processing": "In Bearbeitung. Die Liste steht gleich zum herunterladen bereit.", "follow_export_processing": "In Bearbeitung. Die Liste steht gleich zum herunterladen bereit.",
"follow_import": "Followers importieren", "follow_import": "Follower importieren",
"follow_import_error": "Fehler beim importieren der Follower", "follow_import_error": "Fehler beim Importieren der Follower",
"follows_imported": "Followers importiert! Die Bearbeitung kann eine Zeit lang dauern.", "follows_imported": "Follower importiert! Die Bearbeitung kann einen Moment dauern.",
"foreground": "Vordergrund", "foreground": "Vordergrund",
"general": "Allgemein", "general": "Allgemein",
"hide_attachments_in_convo": "Anhänge in Unterhaltungen ausblenden", "hide_attachments_in_convo": "Anhänge in Unterhaltungen ausblenden",
@ -142,7 +181,7 @@
"hide_post_stats": "Beitragsstatistiken verbergen (z.B. die Anzahl der Favoriten)", "hide_post_stats": "Beitragsstatistiken verbergen (z.B. die Anzahl der Favoriten)",
"hide_user_stats": "Benutzerstatistiken verbergen (z.B. die Anzahl der Follower)", "hide_user_stats": "Benutzerstatistiken verbergen (z.B. die Anzahl der Follower)",
"hide_filtered_statuses": "Gefilterte Beiträge verbergen", "hide_filtered_statuses": "Gefilterte Beiträge verbergen",
"import_followers_from_a_csv_file": "Importiere Follower, denen du folgen möchtest, aus einer CSV-Datei", "import_followers_from_a_csv_file": "Importiere Follower aus einer CSV-Datei",
"import_theme": "Farbschema laden", "import_theme": "Farbschema laden",
"inputRadius": "Eingabefelder", "inputRadius": "Eingabefelder",
"checkboxRadius": "Auswahlfelder", "checkboxRadius": "Auswahlfelder",
@ -156,7 +195,7 @@
"lock_account_description": "Sperre deinen Account, um neue Follower zu genehmigen oder abzulehnen", "lock_account_description": "Sperre deinen Account, um neue Follower zu genehmigen oder abzulehnen",
"loop_video": "Videos wiederholen", "loop_video": "Videos wiederholen",
"loop_video_silent_only": "Nur Videos ohne Ton wiederholen (z.B. Mastodons \"gifs\")", "loop_video_silent_only": "Nur Videos ohne Ton wiederholen (z.B. Mastodons \"gifs\")",
"mutes_tab": "Mutes", "mutes_tab": "Stummschaltungen",
"play_videos_in_modal": "Videos in größerem Medienfenster abspielen", "play_videos_in_modal": "Videos in größerem Medienfenster abspielen",
"use_contain_fit": "Vorschaubilder nicht zuschneiden", "use_contain_fit": "Vorschaubilder nicht zuschneiden",
"name": "Name", "name": "Name",
@ -329,8 +368,44 @@
"checkbox": "Ich habe die Allgemeinen Geschäftsbedingungen überflogen", "checkbox": "Ich habe die Allgemeinen Geschäftsbedingungen überflogen",
"link": "ein netter kleiner Link" "link": "ein netter kleiner Link"
} }
},
"app_name": "Anwendungsname",
"mfa": {
"otp": "OTP",
"recovery_codes_warning": "Schreibe dir die Codes auf oder speichere sie an einem sicheren Ort - ansonsten wirst du sie nicht wiederfinden. Wenn du den Zugriff zu deiner 2FA App und die Wiederherstellungs-Codes verlierst, wirst du aus deinem Account ausgeschlossen sein.",
"recovery_codes": "Wiederherstellungs-Codes.",
"warning_of_generate_new_codes": "Wenn du neue Wiederherstellungs-Codes generierst, werden die alten Codes nicht mehr funktionieren.",
"generate_new_recovery_codes": "Generiere neue Wiederherstellungs-Codes",
"title": "Zwei-Faktor Authentifizierung",
"waiting_a_recovery_codes": "Erhalte Wiederherstellungscodes...",
"authentication_methods": "Authentifizierungsmethoden",
"scan": {
"title": "Scan",
"secret_code": "Schlüssel",
"desc": "Wenn du deine 2FA App verwendest, scanne diesen QR Code oder gebe den Schlüssel ein:"
},
"verify": {
"desc": "Um 2FA zu aktivieren, gib den Code von deiner 2FA-App ein:"
} }
}, },
"enter_current_password_to_confirm": "Gib dein aktuelles Passwort ein, um deine Identität zu bestätigen",
"security": "Sicherheit",
"allow_following_move": "Erlaube automatisches Folgen, sobald ein gefolgter Nutzer umzieht",
"blocks_imported": "Blocks importiert! Die Verarbeitung wird einen Moment brauchen.",
"block_import_error": "Fehler beim Importieren der Blocks",
"block_import": "Block Import",
"block_export_button": "Exportiere deine Blocks in eine csv Datei",
"block_export": "Block Export",
"emoji_reactions_on_timeline": "Zeige Emoji-Reaktionen auf der Zeitleiste",
"domain_mutes": "Domains",
"changed_email": "Email Adresse erfolgreich geändert!",
"change_email_error": "Es trat ein Problem auf beim Versuch, deine Email Adresse zu ändern.",
"change_email": "Ändere Email",
"notification_setting_non_followers": "Nutzer, die dir nicht folgen",
"notification_setting_followers": "Nutzer, die dir folgen",
"import_blocks_from_a_csv_file": "Importiere Blocks von einer CSV Datei",
"accent": "Akzent"
},
"timeline": { "timeline": {
"collapse": "Einklappen", "collapse": "Einklappen",
"conversation": "Unterhaltung", "conversation": "Unterhaltung",
@ -352,7 +427,7 @@
"follow_again": "Anfrage erneut senden?", "follow_again": "Anfrage erneut senden?",
"follow_unfollow": "Folgen beenden", "follow_unfollow": "Folgen beenden",
"followees": "Folgt", "followees": "Folgt",
"followers": "Followers", "followers": "Folgende",
"following": "Folgst du!", "following": "Folgst du!",
"follows_you": "Folgt dir!", "follows_you": "Folgt dir!",
"its_you": "Das bist du!", "its_you": "Das bist du!",
@ -360,7 +435,10 @@
"muted": "Stummgeschaltet", "muted": "Stummgeschaltet",
"per_day": "pro Tag", "per_day": "pro Tag",
"remote_follow": "Folgen", "remote_follow": "Folgen",
"statuses": "Beiträge" "statuses": "Beiträge",
"admin_menu": {
"sandbox": "Erzwinge Beiträge nur für Follower sichtbar zu sein"
}
}, },
"user_profile": { "user_profile": {
"timeline_title": "Beiträge" "timeline_title": "Beiträge"
@ -376,7 +454,7 @@
"favorite": "Favorisieren", "favorite": "Favorisieren",
"user_settings": "Benutzereinstellungen" "user_settings": "Benutzereinstellungen"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "Hochladen fehlgeschlagen.", "base": "Hochladen fehlgeschlagen.",
"file_too_big": "Datei ist zu groß [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "Datei ist zu groß [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
@ -409,5 +487,98 @@
"password_reset_disabled": "Passwortzurücksetzen deaktiviert. Bitte Administrator kontaktieren.", "password_reset_disabled": "Passwortzurücksetzen deaktiviert. Bitte Administrator kontaktieren.",
"password_reset_required": "Passwortzurücksetzen erforderlich", "password_reset_required": "Passwortzurücksetzen erforderlich",
"password_reset_required_but_mailer_is_disabled": "Passwortzurücksetzen wäre erforderlich, ist aber deaktiviert. Bitte Administrator kontaktieren." "password_reset_required_but_mailer_is_disabled": "Passwortzurücksetzen wäre erforderlich, ist aber deaktiviert. Bitte Administrator kontaktieren."
},
"about": {
"mrf": {
"federation": "Föderation",
"mrf_policies": "Aktivierte MRF Richtlinien",
"simple": {
"simple_policies": "Instanzspezifische Richtlinien",
"accept": "Akzeptieren",
"reject": "Ablehnen",
"reject_desc": "Diese Instanz akzeptiert keine Nachrichten der folgenden Instanzen:",
"quarantine": "Quarantäne",
"ftl_removal": "Von der Zeitleiste \"Das gesamte bekannte Netzwerk\" entfernen",
"media_removal": "Medienentfernung",
"media_removal_desc": "Diese Instanz entfernt Medien von den Beiträgen der folgenden Instanzen:",
"media_nsfw": "Erzwingen Medien als heikel zu makieren",
"media_nsfw_desc": "Diese Instanz makiert die Medien in Beiträgen der folgenden Instanzen als heikel:",
"accept_desc": "Diese Instanz akzeptiert nur Nachrichten von den folgenden Instanzen:",
"quarantine_desc": "Diese Instanz sendet nur öffentliche Beiträge zu den folgenden Instanzen:",
"ftl_removal_desc": "Dieser Instanz entfernt folgende Instanzen von der \"Das gesamte bekannte Netzwerk\" Zeitleiste:"
},
"keyword": {
"keyword_policies": "Keyword Richtlinien",
"reject": "Ablehnen",
"replace": "Ersetzen",
"is_replaced_by": "→",
"ftl_removal": "Von der Zeitleiste \"Das gesamte bekannte Netzwerk\" entfernen"
},
"mrf_policies_desc": "MRF Richtlinien manipulieren das Föderationsverhalten dieser Instanz. Die folgenden Richtlinien sind aktiv:"
},
"staff": "Mitarbeiter"
},
"domain_mute_card": {
"mute": "Stummschalten",
"mute_progress": "Wird stummgeschaltet..",
"unmute": "Stummschaltung aufheben",
"unmute_progress": "Stummschaltung wird aufgehoben.."
},
"exporter": {
"export": "Exportieren",
"processing": "Verarbeitung läuft, bald wird Du dazu aufgefordert, deine Datei herunterzuladen"
},
"image_cropper": {
"crop_picture": "Bild zuschneiden",
"save": "Speichern",
"cancel": "Abbrechen",
"save_without_cropping": "Ohne Zuschneiden speichern"
},
"importer": {
"submit": "Absenden",
"success": "Erfolgreich importiert.",
"error": "Ein Fehler ist beim Verabeiten der Datei aufgetreten."
},
"media_modal": {
"previous": "Zurück",
"next": "Weiter"
},
"polls": {
"add_poll": "Umfrage hinzufügen",
"add_option": "Option hinzufügen",
"option": "Option",
"votes": "Stimmen",
"vote": "Abstimmen",
"type": "Umfragetyp",
"multiple_choices": "Mehrere Auswahlmöglichkeiten",
"single_choice": "Eine Auswahlmöglichkeit",
"expiry": "Alter der Umfrage",
"expired": "Die Umfrage endete vor {0}",
"not_enough_options": "Zu wenig einzigartige Auswahlmöglichkeiten in der Umfrage",
"expires_in": "Die Umfrage endet in {0}"
},
"emoji": {
"stickers": "Sticker",
"emoji": "Emoji",
"search_emoji": "Nach einem Emoji suchen",
"custom": "Benutzerdefinierter Emoji",
"keep_open": "Auswahlfenster offen halten",
"add_emoji": "Emoji einfügen",
"load_all": "Lade alle {emojiAmount} Emoji",
"load_all_hint": "Erfolgreich erste {saneAmount} Emoji geladen, alle Emojis zu laden würde Leistungsprobleme hervorrufen.",
"unicode": "Unicode Emoji"
},
"interactions": {
"load_older": "Lade ältere Interaktionen",
"follows": "Neue Follows",
"favs_repeats": "Wiederholungen und Favoriten",
"moves": "Benutzer migriert zu"
},
"selectable_list": {
"select_all": "Wähle alle"
},
"remote_user_resolver": {
"searching_for": "Suche nach",
"error": "Nicht gefunden."
} }
} }

View file

@ -124,6 +124,7 @@
"broken_favorite": "Unknown status, searching for it...", "broken_favorite": "Unknown status, searching for it...",
"favorited_you": "favorited your status", "favorited_you": "favorited your status",
"followed_you": "followed you", "followed_you": "followed you",
"follow_request": "wants to follow you",
"load_older": "Load older notifications", "load_older": "Load older notifications",
"notifications": "Notifications", "notifications": "Notifications",
"read": "Read!", "read": "Read!",
@ -281,7 +282,7 @@
"data_import_export_tab": "Data Import / Export", "data_import_export_tab": "Data Import / Export",
"default_vis": "Default visibility scope", "default_vis": "Default visibility scope",
"delete_account": "Delete Account", "delete_account": "Delete Account",
"delete_account_description": "Permanently delete your account and all your messages.", "delete_account_description": "Permanently delete your data and deactivate your account.",
"delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.", "delete_account_error": "There was an issue deleting your account. If this persists please contact your instance administrator.",
"delete_account_instructions": "Type your password in the input below to confirm account deletion.", "delete_account_instructions": "Type your password in the input below to confirm account deletion.",
"discoverable": "Allow discovery of this account in search results and other services", "discoverable": "Allow discovery of this account in search results and other services",
@ -405,11 +406,14 @@
"fun": "Fun", "fun": "Fun",
"greentext": "Meme arrows", "greentext": "Meme arrows",
"notifications": "Notifications", "notifications": "Notifications",
"notification_setting_filters": "Filters",
"notification_setting": "Receive notifications from:", "notification_setting": "Receive notifications from:",
"notification_setting_follows": "Users you follow", "notification_setting_follows": "Users you follow",
"notification_setting_non_follows": "Users you do not follow", "notification_setting_non_follows": "Users you do not follow",
"notification_setting_followers": "Users who follow you", "notification_setting_followers": "Users who follow you",
"notification_setting_non_followers": "Users who do not follow you", "notification_setting_non_followers": "Users who do not follow you",
"notification_setting_privacy": "Privacy",
"notification_setting_privacy_option": "Hide the sender and contents of push notifications",
"notification_mutes": "To stop receiving notifications from a specific user, use a mute.", "notification_mutes": "To stop receiving notifications from a specific user, use a mute.",
"notification_blocks": "Blocking a user stops all notifications as well as unsubscribes them.", "notification_blocks": "Blocking a user stops all notifications as well as unsubscribes them.",
"enable_web_push_notifications": "Enable web push notifications", "enable_web_push_notifications": "Enable web push notifications",
@ -430,7 +434,7 @@
"use_source": "New version", "use_source": "New version",
"help": { "help": {
"upgraded_from_v2": "PleromaFE has been upgraded, theme could look a little bit different than you remember.", "upgraded_from_v2": "PleromaFE has been upgraded, theme could look a little bit different than you remember.",
"v2_imported": "File you imported was made for older FE. We try to maximize compatibility but there still could be inconsitencies.", "v2_imported": "File you imported was made for older FE. We try to maximize compatibility but there still could be inconsistencies.",
"future_version_imported": "File you imported was made in newer version of FE.", "future_version_imported": "File you imported was made in newer version of FE.",
"older_version_imported": "File you imported was made in older version of FE.", "older_version_imported": "File you imported was made in older version of FE.",
"snapshot_present": "Theme snapshot is loaded, so all values are overriden. You can load theme's actual data instead.", "snapshot_present": "Theme snapshot is loaded, so all values are overriden. You can load theme's actual data instead.",
@ -616,7 +620,9 @@
"reply_to": "Reply to", "reply_to": "Reply to",
"replies_list": "Replies:", "replies_list": "Replies:",
"mute_conversation": "Mute conversation", "mute_conversation": "Mute conversation",
"unmute_conversation": "Unmute conversation" "unmute_conversation": "Unmute conversation",
"status_unavailable": "Status unavailable",
"copy_link": "Copy link to status"
}, },
"user_card": { "user_card": {
"approve": "Approve", "approve": "Approve",
@ -697,7 +703,9 @@
"reply": "Reply", "reply": "Reply",
"favorite": "Favorite", "favorite": "Favorite",
"add_reaction": "Add Reaction", "add_reaction": "Add Reaction",
"user_settings": "User Settings" "user_settings": "User Settings",
"accept_follow_request": "Accept follow request",
"reject_follow_request": "Reject follow request"
}, },
"upload":{ "upload":{
"error": { "error": {

View file

@ -19,7 +19,16 @@
"apply": "Aseta", "apply": "Aseta",
"submit": "Lähetä", "submit": "Lähetä",
"more": "Lisää", "more": "Lisää",
"generic_error": "Virhe tapahtui" "generic_error": "Virhe tapahtui",
"optional": "valinnainen",
"show_more": "Näytä lisää",
"show_less": "Näytä vähemmän",
"dismiss": "Sulje",
"cancel": "Peruuta",
"disable": "Poista käytöstä",
"confirm": "Hyväksy",
"verify": "Varmenna",
"enable": "Ota käyttöön"
}, },
"login": { "login": {
"login": "Kirjaudu sisään", "login": "Kirjaudu sisään",
@ -28,7 +37,16 @@
"password": "Salasana", "password": "Salasana",
"placeholder": "esim. Seppo", "placeholder": "esim. Seppo",
"register": "Rekisteröidy", "register": "Rekisteröidy",
"username": "Käyttäjänimi" "username": "Käyttäjänimi",
"hint": "Kirjaudu sisään liittyäksesi keskusteluun",
"authentication_code": "Todennuskoodi",
"enter_recovery_code": "Syötä palautuskoodi",
"recovery_code": "Palautuskoodi",
"heading": {
"totp": "Monivaihetodennus",
"recovery": "Monivaihepalautus"
},
"enter_two_factor_code": "Syötä monivaihetodennuskoodi"
}, },
"nav": { "nav": {
"about": "Tietoja", "about": "Tietoja",
@ -43,7 +61,9 @@
"twkn": "Koko Tunnettu Verkosto", "twkn": "Koko Tunnettu Verkosto",
"user_search": "Käyttäjähaku", "user_search": "Käyttäjähaku",
"who_to_follow": "Seurausehdotukset", "who_to_follow": "Seurausehdotukset",
"preferences": "Asetukset" "preferences": "Asetukset",
"administration": "Ylläpito",
"search": "Haku"
}, },
"notifications": { "notifications": {
"broken_favorite": "Viestiä ei löydetty...", "broken_favorite": "Viestiä ei löydetty...",
@ -54,7 +74,9 @@
"read": "Lue!", "read": "Lue!",
"repeated_you": "toisti viestisi", "repeated_you": "toisti viestisi",
"no_more_notifications": "Ei enempää ilmoituksia", "no_more_notifications": "Ei enempää ilmoituksia",
"reacted_with": "lisäsi reaktion {0}" "reacted_with": "lisäsi reaktion {0}",
"migrated_to": "siirtyi sivulle",
"follow_request": "haluaa seurata sinua"
}, },
"polls": { "polls": {
"add_poll": "Lisää äänestys", "add_poll": "Lisää äänestys",
@ -68,12 +90,14 @@
"expiry": "Äänestyksen kesto", "expiry": "Äänestyksen kesto",
"expires_in": "Päättyy {0} päästä", "expires_in": "Päättyy {0} päästä",
"expired": "Päättyi {0} sitten", "expired": "Päättyi {0} sitten",
"not_enough_option": "Liian vähän uniikkeja vaihtoehtoja äänestyksessä" "not_enough_option": "Liian vähän uniikkeja vaihtoehtoja äänestyksessä",
"not_enough_options": "Liian vähän ainutkertaisia vaihtoehtoja"
}, },
"interactions": { "interactions": {
"favs_repeats": "Toistot ja tykkäykset", "favs_repeats": "Toistot ja tykkäykset",
"follows": "Uudet seuraukset", "follows": "Uudet seuraukset",
"load_older": "Lataa vanhempia interaktioita" "load_older": "Lataa vanhempia interaktioita",
"moves": "Käyttäjien siirtymiset"
}, },
"post_status": { "post_status": {
"new_status": "Uusi viesti", "new_status": "Uusi viesti",
@ -81,7 +105,10 @@
"account_not_locked_warning_link": "lukittu", "account_not_locked_warning_link": "lukittu",
"attachments_sensitive": "Merkkaa liitteet arkaluonteisiksi", "attachments_sensitive": "Merkkaa liitteet arkaluonteisiksi",
"content_type": { "content_type": {
"text/plain": "Tavallinen teksti" "text/plain": "Tavallinen teksti",
"text/html": "HTML",
"text/markdown": "Markdown",
"text/bbcode": "BBCode"
}, },
"content_warning": "Aihe (valinnainen)", "content_warning": "Aihe (valinnainen)",
"default": "Tulin juuri saunasta.", "default": "Tulin juuri saunasta.",
@ -92,6 +119,13 @@
"private": "Vain-seuraajille - Näkyy vain seuraajillesi", "private": "Vain-seuraajille - Näkyy vain seuraajillesi",
"public": "Julkinen - Näkyy julkisilla aikajanoilla", "public": "Julkinen - Näkyy julkisilla aikajanoilla",
"unlisted": "Listaamaton - Ei näy julkisilla aikajanoilla" "unlisted": "Listaamaton - Ei näy julkisilla aikajanoilla"
},
"direct_warning_to_all": "Tämä viesti näkyy vain viestissä mainituille käyttäjille.",
"direct_warning_to_first_only": "Tämä viesti näkyy vain viestin alussa mainituille käyttäjille.",
"scope_notice": {
"public": "Tämä viesti näkyy kaikille",
"private": "Tämä viesti näkyy vain sinun seuraajillesi",
"unlisted": "Tämä viesti ei näy Julkisella Aikajanalla tai Koko Tunnettu Verkosto -aikajanalla"
} }
}, },
"registration": { "registration": {
@ -110,7 +144,10 @@
"password_required": "ei voi olla tyhjä", "password_required": "ei voi olla tyhjä",
"password_confirmation_required": "ei voi olla tyhjä", "password_confirmation_required": "ei voi olla tyhjä",
"password_confirmation_match": "pitää vastata salasanaa" "password_confirmation_match": "pitää vastata salasanaa"
} },
"username_placeholder": "esim. peke",
"fullname_placeholder": "esim. Pekka Postaaja",
"bio_placeholder": "esim.\nHei, olen Pekka.\nOlen esimerkkikäyttäjä tässä verkostossa."
}, },
"settings": { "settings": {
"attachmentRadius": "Liitteet", "attachmentRadius": "Liitteet",
@ -151,7 +188,7 @@
"follow_import": "Seurausten tuonti", "follow_import": "Seurausten tuonti",
"follow_import_error": "Virhe tuodessa seuraksia", "follow_import_error": "Virhe tuodessa seuraksia",
"follows_imported": "Seuraukset tuotu! Niiden käsittely vie hetken.", "follows_imported": "Seuraukset tuotu! Niiden käsittely vie hetken.",
"foreground": "Korostus", "foreground": "Etuala",
"general": "Yleinen", "general": "Yleinen",
"hide_attachments_in_convo": "Piilota liitteet keskusteluissa", "hide_attachments_in_convo": "Piilota liitteet keskusteluissa",
"hide_attachments_in_tl": "Piilota liitteet aikajanalla", "hide_attachments_in_tl": "Piilota liitteet aikajanalla",
@ -186,14 +223,14 @@
"notification_visibility_mentions": "Maininnat", "notification_visibility_mentions": "Maininnat",
"notification_visibility_repeats": "Toistot", "notification_visibility_repeats": "Toistot",
"notification_visibility_emoji_reactions": "Reaktiot", "notification_visibility_emoji_reactions": "Reaktiot",
"no_rich_text_description": "Älä näytä tekstin muotoilua.", "no_rich_text_description": "Älä näytä tekstin muotoilua",
"hide_network_description": "Älä näytä seurauksiani tai seuraajiani", "hide_network_description": "Älä näytä seurauksiani tai seuraajiani",
"nsfw_clickthrough": "Piilota NSFW liitteet klikkauksen taakse", "nsfw_clickthrough": "Piilota NSFW liitteet klikkauksen taakse",
"oauth_tokens": "OAuth-merkit", "oauth_tokens": "OAuth-merkit",
"token": "Token", "token": "Token",
"refresh_token": "Päivitä token", "refresh_token": "Päivitä token",
"valid_until": "Voimassa asti", "valid_until": "Voimassa asti",
"revoke_token": "Peruuttaa", "revoke_token": "Peruuta",
"panelRadius": "Ruudut", "panelRadius": "Ruudut",
"pause_on_unfocused": "Pysäytä automaattinen viestien näyttö välilehden ollessa pois fokuksesta", "pause_on_unfocused": "Pysäytä automaattinen viestien näyttö välilehden ollessa pois fokuksesta",
"presets": "Valmiit teemat", "presets": "Valmiit teemat",
@ -231,6 +268,228 @@
"values": { "values": {
"false": "pois päältä", "false": "pois päältä",
"true": "päällä" "true": "päällä"
},
"hide_follows_description": "Älä näytä ketä seuraan",
"show_moderator_badge": "Näytä Moderaattori-merkki profiilissani",
"useStreamingApi": "Vastaanota viestiejä ja ilmoituksia reaaliajassa",
"notification_setting_filters": "Suodattimet",
"notification_setting": "Vastaanota ilmoituksia seuraavista:",
"notification_setting_privacy_option": "Piilota lähettäjä ja sisältö sovelluksen ulkopuolisista ilmoituksista",
"enable_web_push_notifications": "Ota käyttöön sovelluksen ulkopuoliset ilmoitukset",
"app_name": "Sovelluksen nimi",
"security": "Turvallisuus",
"mfa": {
"otp": "OTP",
"setup_otp": "OTP-asetukset",
"wait_pre_setup_otp": "esiasetetaan OTP:ta",
"confirm_and_enable": "Hyväksy ja käytä OTP",
"title": "Monivaihetodennus",
"generate_new_recovery_codes": "Luo uudet palautuskoodit",
"authentication_methods": "Todennus",
"warning_of_generate_new_codes": "Luodessasi uudet palautuskoodit, vanhat koodisi lakkaavat toimimasta.",
"recovery_codes": "Palautuskoodit.",
"waiting_a_recovery_codes": "Odotetaan palautuskoodeja...",
"recovery_codes_warning": "Kirjoita koodit ylös tai tallenna ne turvallisesti, muuten et näe niitä uudestaan. Jos et voi käyttää monivaihetodennusta ja sinulla ei ole palautuskoodeja, et voi enää kirjautua sisään tilillesi.",
"scan": {
"title": "Skannaa",
"secret_code": "Avain",
"desc": "Käytä monivaihetodennus-sovellusta skannakksesi tämän QR-kooding, tai syötä avain:"
},
"verify": {
"desc": "Kytkeäksesi päälle monivaihetodennuksen, syötä koodi monivaihetodennussovellksesta:"
}
},
"allow_following_move": "Salli automaattinen seuraaminen kun käyttäjä siirtää tilinsä",
"block_export": "Estojen vienti",
"block_export_button": "Vie estosi CSV-tiedostoon",
"block_import": "Estojen tuonti",
"block_import_error": "Virhe tuodessa estoja",
"blocks_imported": "Estot tuotu! Käsittely vie hetken.",
"blocks_tab": "Estot",
"change_email": "Vaihda sähköpostiosoite",
"change_email_error": "Virhe vaihtaessa sähköpostiosoitetta.",
"changed_email": "Sähköpostiosoite vaihdettu!",
"domain_mutes": "Sivut",
"avatar_size_instruction": "Suositeltu vähimmäiskoko profiilikuville on 150x150 pikseliä.",
"accent": "Korostus",
"hide_muted_posts": "Piilota mykistettyjen käyttäjien viestit",
"hide_filtered_statuses": "Piilota mykistetyt viestit",
"import_blocks_from_a_csv_file": "Tuo estot CSV-tiedostosta",
"no_blocks": "Ei estoja",
"no_mutes": "Ei mykistyksiä",
"notification_visibility_moves": "Käyttäjien siirtymiset",
"hide_followers_description": "Älä näytä ketkä seuraavat minua",
"hide_follows_count_description": "Älä näytä seurauksien määrää",
"hide_followers_count_description": "Älä näytä seuraajien määrää",
"show_admin_badge": "Näytä Ylläpitäjä-merkki proofilissani",
"autohide_floating_post_button": "Piilota Uusi Viesti -nappi automaattisesti (mobiili)",
"search_user_to_block": "Hae estettäviä käyttäjiä",
"search_user_to_mute": "Hae mykistettäviä käyttäjiä",
"minimal_scopes_mode": "Yksinkertaista näkyvyydenrajauksen vaihtoehdot",
"post_status_content_type": "Uuden viestin sisällön muoto",
"user_mutes": "Käyttäjät",
"useStreamingApiWarning": "(Kokeellinen)",
"type_domains_to_mute": "Syötä mykistettäviä sivustoja",
"upload_a_photo": "Lataa kuva",
"fun": "Hupi",
"greentext": "Meeminuolet",
"notifications": "Ilmoitukset",
"style": {
"switcher": {
"save_load_hint": "\"Säilytä\" asetukset säilyttävät tällä hetkellä asetetut asetukset valittaessa tai ladatessa teemaa, se myös tallentaa kyseiset asetukset viedessä teemaa. Kun kaikki laatikot ovat tyhjänä, viety teema tallentaa kaiken.",
"help": {
"older_version_imported": "Tuomasi tiedosto on luotu vanhemmalla versiolla.",
"fe_upgraded": "PleromaFE:n teemaus päivitetty versiopäivityksen yhteydessä.",
"migration_snapshot_ok": "Varmuuden vuoksi teeman kaappaus ladattu. Voit koittaa ladata teeman sisällön.",
"migration_napshot_gone": "Jostain syystä teeman kaappaus puuttuu, kaikki asiat eivät välttämättä näytä oikealta.",
"snapshot_source_mismatch": "Versiot eivät täsmää: todennäköisesti versio vaihdettu vanhempaan ja päivitetty uudestaan, jos vaihdoit teemaa vanhalla versiolla, sinun tulisi käyttää vanhaa versiota, muutoin uutta.",
"upgraded_from_v2": "PleromaFE on päivitetty, teemasi saattaa näyttää erilaiselta kuin muistat.",
"v2_imported": "Tuomasi tiedosto on luotu vanhemmalla versiolla. Yhteensopivuus ei välttämättä ole täydellinen.",
"future_version_imported": "Tuomasi tiedosto on luotu uudemmalla versiolla.",
"snapshot_present": "Teeman kaappaus ladattu, joten kaikki arvot ovat ylikirjoitettu. Voit sen sijaan ladata teeman sisällön.",
"snapshot_missing": "Teeman kaappausta ei tiedostossa, joten se voi näyttää erilaiselta kuin suunniteltu.",
"fe_downgraded": "PleromaFE:n versio vaihtunut vanhempaan."
},
"keep_color": "Säilytä värit",
"keep_shadows": "Säilytä varjot",
"keep_opacity": "Säilytä läpinäkyvyys",
"keep_roundness": "Säilytä pyöristys",
"keep_fonts": "Säilytä fontit",
"reset": "Palauta",
"clear_all": "Tyhjennä kaikki",
"clear_opacity": "Tyhjennä läpinäkyvyys",
"load_theme": "Lataa teema",
"keep_as_is": "Pidä sellaisenaan",
"use_snapshot": "Vanha",
"use_source": "Uusi"
},
"advanced_colors": {
"selectedPost": "Valittu viesti",
"_tab_label": "Edistynyt",
"alert": "Varoituksen tausta",
"alert_error": "Virhe",
"alert_warning": "Varoitus",
"alert_neutral": "Neutraali",
"post": "Viestit/Käyttäjien kuvaukset",
"badge": "Merkin tausta",
"badge_notification": "Ilmoitus",
"panel_header": "Ruudun otsikko",
"top_bar": "Yläpalkki",
"borders": "Reunat",
"buttons": "Napit",
"inputs": "Syöttökentät",
"faint_text": "Häivytetty teksti",
"underlay": "Taustapeite",
"poll": "Äänestyksen kuvaaja",
"icons": "Ikonit",
"highlight": "Korostetut elementit",
"pressed": "Painettu",
"selectedMenu": "Valikon valinta",
"disabled": "Pois käytöstä",
"toggled": "Kytketty",
"tabs": "Välilehdet",
"popover": "Työkaluvinkit, valikot, ponnahdusviestit"
},
"common": {
"color": "Väri",
"opacity": "Läpinäkyvyys",
"contrast": {
"level": {
"aaa": "saavuttaa AAA-tason (suositeltu)",
"aa": "saavuttaa AA-tason (minimi)",
"bad": "ei saavuta mitään helppokäyttöisyyssuosituksia"
},
"hint": "Kontrastisuhde on {ratio}, se {level} {context}",
"context": {
"18pt": "suurella (18pt+) tekstillä",
"text": "tekstillä"
}
}
},
"common_colors": {
"_tab_label": "Yleinen",
"main": "Yleiset värit",
"foreground_hint": "Löydät \"Edistynyt\"-välilehdeltä tarkemmat asetukset",
"rgbo": "Ikonit, korostukset, merkit"
},
"shadows": {
"filter_hint": {
"always_drop_shadow": "Varoitus, tämä varjo käyttää aina {0} kun selain tukee sitä.",
"avatar_inset": "Huom. sisennettyjen ja ei-sisennettyjen varjojen yhdistelmät saattavat luoda ei-odotettuja lopputuloksia läpinäkyvillä profiilikuvilla.",
"drop_shadow_syntax": "{0} ei tue {1} parametria ja {2} avainsanaa.",
"spread_zero": "Varjot joiden levitys > 0 näyttävät samalta kuin se olisi nolla",
"inset_classic": "Sisennetyt varjot käyttävät {0}"
},
"components": {
"buttonPressedHover": "Nappi (painettu ja kohdistettu)",
"panel": "Ruutu",
"panelHeader": "Ruudun otsikko",
"topBar": "Yläpalkki",
"avatar": "Profiilikuva (profiilinäkymässä)",
"avatarStatus": "Profiilikuva (viestin yhtyedessä)",
"popup": "Ponnahdusviestit ja työkaluvinkit",
"button": "Nappi",
"buttonHover": "Nappi (kohdistus)",
"buttonPressed": "Nappi (painettu)",
"input": "Syöttökenttä"
},
"hintV3": "Voit käyttää {0} merkintää varjoille käyttääksesi väriä toisesta asetuksesta.",
"_tab_label": "Valo ja varjostus",
"component": "Komponentti",
"override": "Ylikirjoita",
"shadow_id": "Varjo #{value}",
"blur": "Sumennus",
"spread": "Levitys",
"inset": "Sisennys"
},
"fonts": {
"help": "Valitse fontti käyttöliittymälle. \"Oma\"-vaihtohdolle on syötettävä fontin nimi tarkalleen samana kuin se on järjestelmässäsi.",
"_tab_label": "Fontit",
"components": {
"interface": "Käyttöliittymä",
"input": "Syöttökentät",
"post": "Viestin teksti",
"postCode": "Tasavälistetty teksti viestissä"
},
"family": "Fontin nimi",
"size": "Koko (pikseleissä)",
"weight": "Painostus (paksuus)",
"custom": "Oma"
},
"preview": {
"input": "Tulin juuri saunasta.",
"header": "Esikatselu",
"content": "Sisältö",
"error": "Esimerkkivirhe",
"button": "Nappi",
"text": "Vähän lisää {0} ja {1}",
"mono": "sisältöä",
"faint_link": "manuaali",
"fine_print": "Lue meidän {0} vaikka huvin vuoksi!",
"header_faint": "Tämä on OK",
"checkbox": "Olen silmäillyt käyttöehdot",
"link": "kiva linkki"
},
"radii": {
"_tab_label": "Pyöristys"
}
},
"enter_current_password_to_confirm": "Syötä nykyinen salasanasi todentaaksesi henkilöllisyytesi",
"discoverable": "Salli tilisi näkyvyys hakukoneisiin ja muihin palveluihin",
"pad_emoji": "Välistä emojit välilyönneillä lisätessäsi niitä valitsimesta",
"mutes_tab": "Mykistykset",
"new_email": "Uusi sähköpostiosoite",
"notification_setting_follows": "Käyttäjät joita seuraat",
"notification_setting_non_follows": "Käyttäjät joita et seuraa",
"notification_setting_followers": "Käyttäjät jotka seuraavat sinua",
"notification_setting_non_followers": "Käyttäjät jotka eivät seuraa sinua",
"notification_setting_privacy": "Yksityisyys",
"notification_mutes": "Jos et halua ilmoituksia joltain käyttäjältä, käytä mykistystä.",
"notification_blocks": "Estäminen pysäyttää kaikki ilmoitukset käyttäjältä ja poistaa seurauksen.",
"version": {
"title": "Versio",
"backend_version": "Palvelimen versio",
"frontend_version": "Käyttöliittymän versio"
} }
}, },
"time": { "time": {
@ -252,8 +511,8 @@
"months": "{0} kuukautta", "months": "{0} kuukautta",
"month_short": "{0}kk", "month_short": "{0}kk",
"months_short": "{0}kk", "months_short": "{0}kk",
"now": "nyt", "now": "juuri nyt",
"now_short": "juuri nyt", "now_short": "nyt",
"second": "{0} sekunti", "second": "{0} sekunti",
"seconds": "{0} sekuntia", "seconds": "{0} sekuntia",
"second_short": "{0}s", "second_short": "{0}s",
@ -276,7 +535,8 @@
"repeated": "toisti", "repeated": "toisti",
"show_new": "Näytä uudet", "show_new": "Näytä uudet",
"up_to_date": "Ajantasalla", "up_to_date": "Ajantasalla",
"no_more_statuses": "Ei enempää viestejä" "no_more_statuses": "Ei enempää viestejä",
"no_statuses": "Ei viestejä"
}, },
"status": { "status": {
"favorites": "Tykkäykset", "favorites": "Tykkäykset",
@ -288,8 +548,10 @@
"delete_confirm": "Haluatko varmasti postaa viestin?", "delete_confirm": "Haluatko varmasti postaa viestin?",
"reply_to": "Vastaus", "reply_to": "Vastaus",
"replies_list": "Vastaukset:", "replies_list": "Vastaukset:",
"mute_conversation": "Hiljennä keskustelu", "mute_conversation": "Mykistä keskustelu",
"unmute_conversation": "Poista hiljennys" "unmute_conversation": "Poista mykistys",
"status_unavailable": "Viesti ei saatavissa",
"copy_link": "Kopioi linkki"
}, },
"user_card": { "user_card": {
"approve": "Hyväksy", "approve": "Hyväksy",
@ -298,7 +560,7 @@
"deny": "Älä hyväksy", "deny": "Älä hyväksy",
"follow": "Seuraa", "follow": "Seuraa",
"follow_sent": "Pyyntö lähetetty!", "follow_sent": "Pyyntö lähetetty!",
"follow_progress": "Pyydetään...", "follow_progress": "Pyydetään",
"follow_again": "Lähetä pyyntö uudestaan", "follow_again": "Lähetä pyyntö uudestaan",
"follow_unfollow": "Älä seuraa", "follow_unfollow": "Älä seuraa",
"followees": "Seuraa", "followees": "Seuraa",
@ -306,14 +568,50 @@
"following": "Seuraat!", "following": "Seuraat!",
"follows_you": "Seuraa sinua!", "follows_you": "Seuraa sinua!",
"its_you": "Sinun tili!", "its_you": "Sinun tili!",
"mute": "Hiljennä", "mute": "Mykistä",
"muted": "Hiljennetty", "muted": "Mykistetty",
"per_day": "päivässä", "per_day": "päivässä",
"remote_follow": "Seuraa muualta", "remote_follow": "Seuraa muualta",
"statuses": "Viestit" "statuses": "Viestit",
"hidden": "Piilotettu",
"media": "Media",
"block_progress": "Estetään...",
"admin_menu": {
"grant_admin": "Anna Ylläpitöoikeudet",
"force_nsfw": "Merkitse kaikki viestit NSFW:nä",
"disable_any_subscription": "Estä käyttäjän seuraaminen",
"moderation": "Moderaatio",
"revoke_admin": "Poista Ylläpitöoikeudet",
"grant_moderator": "Anna Moderaattorioikeudet",
"revoke_moderator": "Poista Moderaattorioikeudet",
"activate_account": "Aktivoi tili",
"deactivate_account": "Deaktivoi tili",
"delete_account": "Poista tili",
"strip_media": "Poista media viesteistä",
"force_unlisted": "Pakota viestit listaamattomiksi",
"sandbox": "Pakota viestit vain seuraajille",
"disable_remote_subscription": "Estä seuraaminen ulkopuolisilta sivuilta",
"quarantine": "Estä käyttäjän viestin federoituminen",
"delete_user": "Poista käyttäjä",
"delete_user_confirmation": "Oletko aivan varma? Tätä ei voi kumota."
},
"favorites": "Tykkäykset",
"mention": "Mainitse",
"report": "Ilmianna",
"subscribe": "Tilaa",
"unsubscribe": "Poista tilaus",
"unblock": "Poista esto",
"unblock_progress": "Postetaan estoa...",
"unmute": "Poista mykistys",
"unmute_progress": "Poistetaan mykistystä...",
"mute_progress": "Mykistetään...",
"hide_repeats": "Piilota toistot",
"show_repeats": "Näytä toistot"
}, },
"user_profile": { "user_profile": {
"timeline_title": "Käyttäjän aikajana" "timeline_title": "Käyttäjän aikajana",
"profile_does_not_exist": "Tätä profiilia ei ole.",
"profile_loading_error": "Virhe ladatessa profiilia."
}, },
"who_to_follow": { "who_to_follow": {
"more": "Lisää", "more": "Lisää",
@ -324,9 +622,12 @@
"repeat": "Toista", "repeat": "Toista",
"reply": "Vastaa", "reply": "Vastaa",
"favorite": "Tykkää", "favorite": "Tykkää",
"user_settings": "Käyttäjäasetukset" "user_settings": "Käyttäjäasetukset",
"add_reaction": "Lisää Reaktio",
"accept_follow_request": "Hyväksy seurauspyyntö",
"reject_follow_request": "Hylkää seurauspyyntö"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "Lataus epäonnistui.", "base": "Lataus epäonnistui.",
"file_too_big": "Tiedosto liian suuri [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "Tiedosto liian suuri [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
@ -339,5 +640,108 @@
"GiB": "Gt", "GiB": "Gt",
"TiB": "Tt" "TiB": "Tt"
} }
},
"about": {
"mrf": {
"keyword": {
"keyword_policies": "Avainsanasäännöt",
"ftl_removal": "Poistettu \"Koko Tunnettu Verkosto\" -aikajanalta",
"reject": "Hylkää",
"replace": "Korvaa",
"is_replaced_by": "→"
},
"simple": {
"accept": "Hyväksy",
"reject": "Hylkää",
"quarantine": "Karanteeni",
"ftl_removal": "Poisto \"Koko Tunnettu Verkosto\" -aikajanalta",
"media_removal": "Media-tiedostojen poisto",
"simple_policies": "Palvelinkohtaiset Säännöt",
"accept_desc": "Tämä palvelin hyväksyy viestit vain seuraavilta palvelimilta:",
"reject_desc": "Tämä palvelin ei hyväksy viestejä seuraavilta palvelimilta:",
"quarantine_desc": "Tämä palvelin lähettää vain julkisia viestejä seuraaville palvelimille:",
"ftl_removal_desc": "Tämä palvelin poistaa nämä palvelimet \"Koko Tunnettu Verkosto\"-aikajanalta:",
"media_removal_desc": "Tämä palvelin postaa mediatiedostot viesteistä seuraavilta palvelimilta:",
"media_nsfw": "Pakota Media Arkaluontoiseksi",
"media_nsfw_desc": "Tämä palvelin pakottaa mediatiedostot arkaluonteisiksi seuraavilta palvelimilta:"
},
"federation": "Federaatio",
"mrf_policies": "Aktivoidut MRF-säännöt",
"mrf_policies_desc": "MRF-säännöt muuttavat federaation toimintaa sivulla. Seuraavat säännöt ovat kytketty päälle:"
},
"staff": "Henkilökunta"
},
"domain_mute_card": {
"mute": "Mykistä",
"unmute": "Poista mykistys",
"mute_progress": "Mykistetään...",
"unmute_progress": "Poistetaan mykistyst..."
},
"exporter": {
"export": "Vie",
"processing": "Käsitellään, hetken päästä voit tallentaa tiedoston"
},
"image_cropper": {
"crop_picture": "Rajaa kuva",
"save": "Tallenna",
"save_without_cropping": "Tallenna rajaamatta",
"cancel": "Peruuta"
},
"importer": {
"submit": "Hyväksy",
"error": "Virhe tapahtui tietoja tuodessa.",
"success": "Tuonti onnistui."
},
"media_modal": {
"previous": "Edellinen",
"next": "Seuraava"
},
"emoji": {
"stickers": "Tarrat",
"emoji": "Emoji",
"keep_open": "Pidä valitsin auki",
"search_emoji": "Hae emojia",
"add_emoji": "Lisää emoji",
"custom": "Custom-emoji",
"load_all": "Ladataan kaikkia {emojiAmount} emojia",
"unicode": "Unicode-emoji",
"load_all_hint": "Ensimmäiset {saneAmount} emojia ladattu, kaikkien emojien lataaminen voi aiheuttaa hidastelua."
},
"remote_user_resolver": {
"remote_user_resolver": "Ulkopuolinen käyttäjä",
"searching_for": "Etsitään käyttäjää",
"error": "Ei löytynyt."
},
"selectable_list": {
"select_all": "Valitse kaikki"
},
"password_reset": {
"check_email": "Tarkista sähköpostisi salasanannollausta varten.",
"instruction": "Syötä sähköpostiosoite tai käyttäjänimi. Lähetämme linkin salasanan nollausta varten.",
"password_reset_disabled": "Salasanan nollaus ei käytössä. Ota yhteyttä sivun ylläpitäjään.",
"password_reset_required_but_mailer_is_disabled": "Sinun täytyy vaihtaa salasana, mutta salasanan nollaus on pois käytöstä. Ota yhteyttä sivun ylläpitäjään.",
"forgot_password": "Unohditko salasanan?",
"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."
},
"user_reporting": {
"add_comment_description": "Tämä raportti lähetetään sivun moderaattoreille. Voit antaa selityksen miksi ilmiannoit tilin:",
"title": "Ilmiannetaan {0}",
"additional_comments": "Lisäkommentit",
"forward_description": "Tämä tili on toiselta palvelimelta. Lähetä kopio ilmiannosta sinnekin?",
"forward_to": "Lähetä eteenpäin: {0}",
"submit": "Lähetä",
"generic_error": "Virhe käsitellessä pyyntöä."
},
"search": {
"people": "Käyttäjät",
"hashtags": "Aihetunnisteet",
"people_talking": "{0} käyttäjää puhuvat",
"person_talking": "{0} käyttäjä puhuu",
"no_results": "Ei tuloksia"
} }
} }

View file

@ -79,7 +79,9 @@
"twkn": "Ensemble du réseau connu", "twkn": "Ensemble du réseau connu",
"user_search": "Recherche d'utilisateur·ice", "user_search": "Recherche d'utilisateur·ice",
"who_to_follow": "Qui suivre", "who_to_follow": "Qui suivre",
"preferences": "Préférences" "preferences": "Préférences",
"search": "Recherche",
"administration": "Administration"
}, },
"notifications": { "notifications": {
"broken_favorite": "Chargement d'un message inconnu…", "broken_favorite": "Chargement d'un message inconnu…",
@ -89,12 +91,16 @@
"notifications": "Notifications", "notifications": "Notifications",
"read": "Lu !", "read": "Lu !",
"repeated_you": "a partagé votre statut", "repeated_you": "a partagé votre statut",
"no_more_notifications": "Aucune notification supplémentaire" "no_more_notifications": "Aucune notification supplémentaire",
"migrated_to": "a migré à",
"reacted_with": "a réagi avec {0}",
"follow_request": "veut vous suivre"
}, },
"interactions": { "interactions": {
"favs_repeats": "Partages et favoris", "favs_repeats": "Partages et favoris",
"follows": "Nouveaux⋅elles abonné⋅e⋅s ?", "follows": "Nouveaux suivis",
"load_older": "Chargez d'anciennes interactions" "load_older": "Chargez d'anciennes interactions",
"moves": "Migrations de comptes"
}, },
"post_status": { "post_status": {
"new_status": "Poster un nouveau statut", "new_status": "Poster un nouveau statut",
@ -170,7 +176,7 @@
"secret_code": "Clé" "secret_code": "Clé"
}, },
"verify": { "verify": {
"desc": "Pour activer la double authentification, entrez le code depuis votre application:" "desc": "Pour activer la double authentification, entrez le code depuis votre application:"
} }
}, },
"attachmentRadius": "Pièces jointes", "attachmentRadius": "Pièces jointes",
@ -185,7 +191,7 @@
"block_export_button": "Export des comptes bloqués vers un fichier csv", "block_export_button": "Export des comptes bloqués vers un fichier csv",
"block_import": "Import des comptes bloqués", "block_import": "Import des comptes bloqués",
"block_import_error": "Erreur lors de l'import des comptes bloqués", "block_import_error": "Erreur lors de l'import des comptes bloqués",
"blocks_imported": "Blocks importés! Le traitement va prendre un moment.", "blocks_imported": "Blocks importés! Le traitement va prendre un moment.",
"blocks_tab": "Bloqué·e·s", "blocks_tab": "Bloqué·e·s",
"btnRadius": "Boutons", "btnRadius": "Boutons",
"cBlue": "Bleu (répondre, suivre)", "cBlue": "Bleu (répondre, suivre)",
@ -233,7 +239,7 @@
"import_theme": "Charger le thème", "import_theme": "Charger le thème",
"inputRadius": "Champs de texte", "inputRadius": "Champs de texte",
"checkboxRadius": "Cases à cocher", "checkboxRadius": "Cases à cocher",
"instance_default": "(default: {value})", "instance_default": "(default: {value})",
"instance_default_simple": "(default)", "instance_default_simple": "(default)",
"interface": "Interface", "interface": "Interface",
"interfaceLanguage": "Langue de l'interface", "interfaceLanguage": "Langue de l'interface",
@ -264,7 +270,7 @@
"nsfw_clickthrough": "Masquer les images marquées comme contenu adulte ou sensible", "nsfw_clickthrough": "Masquer les images marquées comme contenu adulte ou sensible",
"oauth_tokens": "Jetons OAuth", "oauth_tokens": "Jetons OAuth",
"token": "Jeton", "token": "Jeton",
"refresh_token": "Refresh Token", "refresh_token": "Rafraichir le jeton",
"valid_until": "Valable jusque", "valid_until": "Valable jusque",
"revoke_token": "Révoquer", "revoke_token": "Révoquer",
"panelRadius": "Fenêtres", "panelRadius": "Fenêtres",
@ -293,8 +299,8 @@
"settings": "Paramètres", "settings": "Paramètres",
"subject_input_always_show": "Toujours copier le champ de sujet", "subject_input_always_show": "Toujours copier le champ de sujet",
"subject_line_behavior": "Copier le sujet en répondant", "subject_line_behavior": "Copier le sujet en répondant",
"subject_line_email": "Comme les mails: « re: sujet »", "subject_line_email": "Similaire au courriel: « re: sujet »",
"subject_line_mastodon": "Comme mastodon: copier tel quel", "subject_line_mastodon": "Comme mastodon: copier tel quel",
"subject_line_noop": "Ne pas copier", "subject_line_noop": "Ne pas copier",
"post_status_content_type": "Type de contenu du statuts", "post_status_content_type": "Type de contenu du statuts",
"stop_gifs": "N'animer les GIFS que lors du survol du curseur de la souris", "stop_gifs": "N'animer les GIFS que lors du survol du curseur de la souris",
@ -312,7 +318,7 @@
"true": "oui" "true": "oui"
}, },
"notifications": "Notifications", "notifications": "Notifications",
"notification_setting": "Reçevoir les notifications de:", "notification_setting": "Reçevoir les notifications de:",
"notification_setting_follows": "Utilisateurs que vous suivez", "notification_setting_follows": "Utilisateurs que vous suivez",
"notification_setting_non_follows": "Utilisateurs que vous ne suivez pas", "notification_setting_non_follows": "Utilisateurs que vous ne suivez pas",
"notification_setting_followers": "Utilisateurs qui vous suivent", "notification_setting_followers": "Utilisateurs qui vous suivent",
@ -330,7 +336,17 @@
"save_load_hint": "L'option « Garder » préserve les options activés en cours lors de la séléction ou chargement des thèmes, il sauve aussi les dites options lors de l'export d'un thème. Quand toutes les cases sont décochés, exporter un thème sauvera tout.", "save_load_hint": "L'option « Garder » préserve les options activés en cours lors de la séléction ou chargement des thèmes, il sauve aussi les dites options lors de l'export d'un thème. Quand toutes les cases sont décochés, exporter un thème sauvera tout.",
"reset": "Remise à zéro", "reset": "Remise à zéro",
"clear_all": "Tout vider", "clear_all": "Tout vider",
"clear_opacity": "Vider la transparence" "clear_opacity": "Vider la transparence",
"load_theme": "Charger le thème",
"use_snapshot": "Ancienne version",
"help": {
"upgraded_from_v2": "PleromaFE à été mis à jour, le thème peut être un peu différent que dans vos souvenirs.",
"v2_imported": "Le fichier que vous avez importé vient d'un version antérieure. Nous essayons de maximizer la compatibilité mais il peu y avoir quelques incohérences.",
"future_version_imported": "Le fichier importé viens d'une version postérieure de PleromaFE.",
"older_version_imported": "Le fichier importé viens d'une version antérieure de PleromaFE."
},
"keep_as_is": "Garder tel-quel",
"use_source": "Nouvelle version"
}, },
"common": { "common": {
"color": "Couleur", "color": "Couleur",
@ -365,7 +381,18 @@
"borders": "Bordures", "borders": "Bordures",
"buttons": "Boutons", "buttons": "Boutons",
"inputs": "Champs de saisie", "inputs": "Champs de saisie",
"faint_text": "Texte en fondu" "faint_text": "Texte en fondu",
"underlay": "sous-calque",
"pressed": "Appuyé",
"alert_warning": "Avertissement",
"alert_neutral": "Neutre",
"post": "Messages/Bios des comptes",
"poll": "Graphique de Sondage",
"icons": "Icônes",
"selectedPost": "Message sélectionné",
"selectedMenu": "Objet sélectionné du menu",
"disabled": "Désactivé",
"tabs": "Onglets"
}, },
"radii": { "radii": {
"_tab_label": "Rondeur" "_tab_label": "Rondeur"
@ -398,7 +425,8 @@
"buttonPressed": "Bouton (cliqué)", "buttonPressed": "Bouton (cliqué)",
"buttonPressedHover": "Bouton (cliqué+survol)", "buttonPressedHover": "Bouton (cliqué+survol)",
"input": "Champ de saisie" "input": "Champ de saisie"
} },
"hintV3": "Pour les ombres vous pouvez aussi utiliser la notation {0} pour utiliser un autre emplacement de couleur."
}, },
"fonts": { "fonts": {
"_tab_label": "Polices", "_tab_label": "Polices",
@ -433,7 +461,28 @@
"title": "Version", "title": "Version",
"backend_version": "Version du Backend", "backend_version": "Version du Backend",
"frontend_version": "Version du Frontend" "frontend_version": "Version du Frontend"
} },
"change_email": "Changer de courriel",
"domain_mutes": "Domaines",
"pad_emoji": "Rajouter un espace autour de l'émoji après lavoir choisit",
"notification_visibility_emoji_reactions": "Réactions",
"hide_follows_count_description": "Masquer le nombre de suivis",
"useStreamingApiWarning": "(Non recommandé, expérimental, connu pour rater des messages)",
"type_domains_to_mute": "Écrire les domaines à masquer",
"fun": "Rigolo",
"greentext": "greentexting",
"allow_following_move": "Suivre automatiquement quand ce compte migre",
"change_email_error": "Il y a eu un problème pour charger votre courriel.",
"changed_email": "Courriel changé avec succès !",
"discoverable": "Permettre de découvrir ce compte dans les résultats de recherche web et autres services",
"emoji_reactions_on_timeline": "Montrer les émojis-réactions dans le flux",
"new_email": "Nouveau courriel",
"notification_visibility_moves": "Migrations de compte",
"user_mutes": "Comptes",
"useStreamingApi": "Recevoir les messages et notifications en temps réel",
"notification_setting_filters": "Filtres",
"notification_setting_privacy_option": "Masquer l'expéditeur et le contenu des notifications push",
"notification_setting_privacy": "Intimité"
}, },
"timeline": { "timeline": {
"collapse": "Fermer", "collapse": "Fermer",
@ -456,7 +505,11 @@
"pinned": "Agraffé", "pinned": "Agraffé",
"delete_confirm": "Voulez-vous vraiment supprimer ce statuts ?", "delete_confirm": "Voulez-vous vraiment supprimer ce statuts ?",
"reply_to": "Réponse à", "reply_to": "Réponse à",
"replies_list": "Réponses:" "replies_list": "Réponses:",
"mute_conversation": "Masquer la conversation",
"unmute_conversation": "Démasquer la conversation",
"status_unavailable": "Status indisponible",
"copy_link": "Copier le lien au status"
}, },
"user_card": { "user_card": {
"approve": "Accepter", "approve": "Accepter",
@ -505,7 +558,13 @@
"quarantine": "Interdir les statuts de l'utilisateur à fédérer", "quarantine": "Interdir les statuts de l'utilisateur à fédérer",
"delete_user": "Supprimer l'utilisateur", "delete_user": "Supprimer l'utilisateur",
"delete_user_confirmation": "Êtes-vous absolument-sûr⋅e ? Cette action ne peut être annulée." "delete_user_confirmation": "Êtes-vous absolument-sûr⋅e ? Cette action ne peut être annulée."
} },
"mention": "Mention",
"hidden": "Caché",
"subscribe": "Abonner",
"unsubscribe": "Désabonner",
"hide_repeats": "Cacher les partages",
"show_repeats": "Montrer les partages"
}, },
"user_profile": { "user_profile": {
"timeline_title": "Journal de l'utilisateur⋅ice", "timeline_title": "Journal de l'utilisateur⋅ice",
@ -530,7 +589,10 @@
"repeat": "Répéter", "repeat": "Répéter",
"reply": "Répondre", "reply": "Répondre",
"favorite": "Favoriser", "favorite": "Favoriser",
"user_settings": "Paramètres utilisateur" "user_settings": "Paramètres utilisateur",
"add_reaction": "Ajouter une réaction",
"accept_follow_request": "Accepter la demande de suivit",
"reject_follow_request": "Rejeter la demande de suivit"
}, },
"upload": { "upload": {
"error": { "error": {
@ -545,5 +607,122 @@
"GiB": "GiO", "GiB": "GiO",
"TiB": "TiO" "TiB": "TiO"
} }
},
"about": {
"mrf": {
"keyword": {
"reject": "Rejeté",
"replace": "Remplacer",
"keyword_policies": "Politiques par mot-clés",
"ftl_removal": "Suppression du flux \"Ensemble du réseau connu\"",
"is_replaced_by": "→"
},
"simple": {
"simple_policies": "Politiques par instances",
"accept": "Accepter",
"accept_desc": "Cette instance accepte des messages seulement depuis ces instances :",
"reject": "Rejeter",
"reject_desc": "Cette instance n'acceptera pas de message de ces instances :",
"quarantine": "Quarantaine",
"quarantine_desc": "Cette instance enverras seulement des messages publics à ces instances :",
"ftl_removal_desc": "Cette instance supprime ces instance du flux fédéré :",
"media_removal": "Suppression multimédia",
"media_removal_desc": "Cette instance supprime le contenu multimédia des instances suivantes :",
"media_nsfw": "Force le contenu multimédia comme sensible",
"ftl_removal": "Suppression du flux fédéré",
"media_nsfw_desc": "Cette instance force le contenu multimédia comme sensible pour les messages des instances suivantes :"
},
"federation": "Fédération",
"mrf_policies": "Politiques MRF activées",
"mrf_policies_desc": "Les politiques MRF modifient la fédération entre les instances. Les politiques suivantes sont activées :"
},
"staff": "Staff"
},
"domain_mute_card": {
"mute": "Muet",
"mute_progress": "Masquage…",
"unmute": "Démasquer",
"unmute_progress": "Démasquage…"
},
"polls": {
"add_poll": "Ajouter un Sondage",
"add_option": "Ajouter une option",
"option": "Option",
"votes": "votes",
"type": "Type de Sondage",
"single_choice": "Choix unique",
"multiple_choices": "Choix multiples",
"expiry": "Age du sondage",
"expires_in": "Fin du sondage dans {0}",
"not_enough_options": "Trop peu d'options unique au sondage",
"vote": "Voter",
"expired": "Sondage terminé il y a {0}"
},
"emoji": {
"emoji": "Émoji",
"search_emoji": "Rechercher un émoji",
"add_emoji": "Insérer un émoji",
"custom": "émoji personnalisé",
"unicode": "émoji unicode",
"load_all": "Charger tout les {emojiAmount} émojis",
"load_all_hint": "{saneAmount} émojis chargé, charger tout les émojis peuvent causer des problèmes de performances.",
"stickers": "Stickers"
},
"remote_user_resolver": {
"error": "Non trouvé."
},
"time": {
"minutes_short": "{0}min",
"second_short": "{0}s",
"day": "{0} jour",
"days": "{0} jours",
"months": "{0} mois",
"month_short": "{0}m",
"months_short": "{0}m",
"now": "tout de suite",
"now_short": "maintenant",
"second": "{0} seconde",
"seconds": "{0} secondes",
"seconds_short": "{0}s",
"day_short": "{0}j",
"days_short": "{0}j",
"hour": "{0} heure",
"hours": "{0} heures",
"hour_short": "{0}h",
"hours_short": "{0}h",
"in_future": "dans {0}",
"in_past": "il y a {0}",
"minute": "{0} minute",
"minutes": "{0} minutes",
"minute_short": "{0}min",
"month": "{0} mois",
"week": "{0} semaine",
"weeks": "{0} semaines",
"week_short": "{0}s",
"weeks_short": "{0}s",
"year": "{0} année",
"years": "{0} années",
"year_short": "{0}a",
"years_short": "{0}a"
},
"search": {
"people": "Comptes",
"person_talking": "{count} personnes discutant",
"hashtags": "Mot-dièses",
"people_talking": "{count} personnes discutant",
"no_results": "Aucun résultats"
},
"password_reset": {
"forgot_password": "Mot de passe oublié ?",
"check_email": "Vérifiez vos courriels pour le lien permettant de changer votre mot de passe.",
"password_reset_disabled": "Le changement de mot de passe est désactivé. Veuillez contacter l'administration de votre instance.",
"password_reset_required_but_mailer_is_disabled": "Vous devez changer votre mot de passe mais sont changement est désactivé. Veuillez contacter ladministration de votre instance.",
"password_reset": "Nouveau mot de passe",
"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."
} }
} }

View file

@ -1,140 +1,164 @@
{ {
"general": { "general": {
"submit": "Invia", "submit": "Invia",
"apply": "Applica" "apply": "Applica",
"more": "Altro",
"generic_error": "Errore",
"optional": "facoltativo",
"show_more": "Mostra tutto",
"show_less": "Ripiega",
"dismiss": "Chiudi",
"cancel": "Annulla",
"disable": "Disabilita",
"enable": "Abilita",
"confirm": "Conferma",
"verify": "Verifica"
}, },
"nav": { "nav": {
"mentions": "Menzioni", "mentions": "Menzioni",
"public_tl": "Sequenza temporale pubblica", "public_tl": "Sequenza pubblica",
"timeline": "Sequenza temporale", "timeline": "Sequenza personale",
"twkn": "L'intera rete conosciuta", "twkn": "Sequenza globale",
"chat": "Chat Locale", "chat": "Chat della stanza",
"friend_requests": "Richieste di Seguirti" "friend_requests": "Vogliono seguirti",
"about": "Informazioni",
"administration": "Amministrazione",
"back": "Indietro",
"interactions": "Interazioni",
"dms": "Messaggi diretti",
"user_search": "Ricerca utenti",
"search": "Ricerca",
"who_to_follow": "Chi seguire",
"preferences": "Preferenze"
}, },
"notifications": { "notifications": {
"followed_you": "ti segue", "followed_you": "ti segue",
"notifications": "Notifiche", "notifications": "Notifiche",
"read": "Leggi!", "read": "Letto!",
"broken_favorite": "Stato sconosciuto, lo sto cercando...", "broken_favorite": "Stato sconosciuto, lo sto cercando...",
"favorited_you": "ha messo mi piace al tuo stato", "favorited_you": "ha gradito il tuo messaggio",
"load_older": "Carica notifiche più vecchie", "load_older": "Carica notifiche precedenti",
"repeated_you": "ha condiviso il tuo stato" "repeated_you": "ha condiviso il tuo messaggio",
"follow_request": "vuole seguirti",
"no_more_notifications": "Fine delle notifiche",
"migrated_to": "è migrato verso",
"reacted_with": "ha reagito con"
}, },
"settings": { "settings": {
"attachments": "Allegati", "attachments": "Allegati",
"autoload": "Abilita caricamento automatico quando si raggiunge fondo pagina", "autoload": "Abilita caricamento automatico quando raggiungi il fondo pagina",
"avatar": "Avatar", "avatar": "Icona utente",
"bio": "Introduzione", "bio": "Introduzione",
"current_avatar": "Il tuo avatar attuale", "current_avatar": "La tua icona attuale",
"current_profile_banner": "Il tuo banner attuale", "current_profile_banner": "Il tuo stendardo attuale",
"filtering": "Filtri", "filtering": "Filtri",
"filtering_explanation": "Tutti i post contenenti queste parole saranno silenziati, uno per linea", "filtering_explanation": "Tutti i post contenenti queste parole saranno silenziati, una per riga",
"hide_attachments_in_convo": "Nascondi gli allegati presenti nelle conversazioni", "hide_attachments_in_convo": "Nascondi gli allegati presenti nelle conversazioni",
"hide_attachments_in_tl": "Nascondi gli allegati presenti nella sequenza temporale", "hide_attachments_in_tl": "Nascondi gli allegati presenti nelle sequenze",
"name": "Nome", "name": "Nome",
"name_bio": "Nome & Introduzione", "name_bio": "Nome ed introduzione",
"nsfw_clickthrough": "Abilita il click per visualizzare gli allegati segnati come NSFW", "nsfw_clickthrough": "Fai click per visualizzare gli allegati nascosti",
"profile_background": "Sfondo della tua pagina", "profile_background": "Sfondo della tua pagina",
"profile_banner": "Banner del tuo profilo", "profile_banner": "Stendardo del tuo profilo",
"reply_link_preview": "Abilita il link per la risposta al passaggio del mouse", "reply_link_preview": "Visualizza le risposte al passaggio del cursore",
"set_new_avatar": "Scegli un nuovo avatar", "set_new_avatar": "Scegli una nuova icona",
"set_new_profile_background": "Scegli un nuovo sfondo per la tua pagina", "set_new_profile_background": "Scegli un nuovo sfondo per la tua pagina",
"set_new_profile_banner": "Scegli un nuovo banner per il tuo profilo", "set_new_profile_banner": "Scegli un nuovo stendardo per il tuo profilo",
"settings": "Impostazioni", "settings": "Impostazioni",
"theme": "Tema", "theme": "Tema",
"user_settings": "Impostazioni Utente", "user_settings": "Impostazioni Utente",
"attachmentRadius": "Allegati", "attachmentRadius": "Allegati",
"avatarAltRadius": "Avatar (Notifiche)", "avatarAltRadius": "Icone utente (Notifiche)",
"avatarRadius": "Avatar", "avatarRadius": "Icone utente",
"background": "Sfondo", "background": "Sfondo",
"btnRadius": "Pulsanti", "btnRadius": "Pulsanti",
"cBlue": "Blu (Rispondere, seguire)", "cBlue": "Blu (risposte, seguire)",
"cGreen": "Verde (Condividi)", "cGreen": "Verde (ripeti)",
"cOrange": "Arancio (Mi piace)", "cOrange": "Arancione (gradire)",
"cRed": "Rosso (Annulla)", "cRed": "Rosso (annulla)",
"change_password": "Cambia Password", "change_password": "Cambia password",
"change_password_error": "C'è stato un problema durante il cambiamento della password.", "change_password_error": "C'è stato un problema durante il cambiamento della password.",
"changed_password": "Password cambiata correttamente!", "changed_password": "Password cambiata correttamente!",
"collapse_subject": "Riduci post che hanno un oggetto", "collapse_subject": "Ripiega messaggi con Oggetto",
"confirm_new_password": "Conferma la nuova password", "confirm_new_password": "Conferma la nuova password",
"current_password": "Password attuale", "current_password": "La tua password attuale",
"data_import_export_tab": "Importa / Esporta Dati", "data_import_export_tab": "Importa o esporta dati",
"default_vis": "Visibilità predefinita dei post", "default_vis": "Visibilità predefinita dei messaggi",
"delete_account": "Elimina Account", "delete_account": "Elimina profilo",
"delete_account_description": "Elimina definitivamente il tuo account e tutti i tuoi messaggi.", "delete_account_description": "Elimina definitivamente i tuoi dati e disattiva il tuo profilo.",
"delete_account_error": "C'è stato un problema durante l'eliminazione del tuo account. Se il problema persiste contatta l'amministratore della tua istanza.", "delete_account_error": "C'è stato un problema durante l'eliminazione del tuo profilo. Se il problema persiste contatta l'amministratore della tua stanza.",
"delete_account_instructions": "Digita la tua password nel campo sottostante per confermare l'eliminazione dell'account.", "delete_account_instructions": "Digita la tua password nel campo sottostante per confermare l'eliminazione del tuo profilo.",
"export_theme": "Salva settaggi", "export_theme": "Salva impostazioni",
"follow_export": "Esporta la lista di chi segui", "follow_export": "Esporta la lista di chi segui",
"follow_export_button": "Esporta la lista di chi segui in un file csv", "follow_export_button": "Esporta la lista di chi segui in un file CSV",
"follow_export_processing": "Sto elaborando, presto ti sarà chiesto di scaricare il tuo file", "follow_export_processing": "Sto elaborando, presto ti sarà chiesto di scaricare il tuo file",
"follow_import": "Importa la lista di chi segui", "follow_import": "Importa la lista di chi segui",
"follow_import_error": "Errore nell'importazione della lista di chi segui", "follow_import_error": "Errore nell'importazione della lista di chi segui",
"follows_imported": "Importazione riuscita! L'elaborazione richiederà un po' di tempo.", "follows_imported": "Importazione riuscita! L'elaborazione richiederà un po' di tempo.",
"foreground": "In primo piano", "foreground": "Primo piano",
"general": "Generale", "general": "Generale",
"hide_post_stats": "Nascondi statistiche dei post (es. il numero di mi piace)", "hide_post_stats": "Nascondi statistiche dei messaggi (es. il numero di preferenze)",
"hide_user_stats": "Nascondi statistiche dell'utente (es. il numero di chi ti segue)", "hide_user_stats": "Nascondi statistiche dell'utente (es. il numero dei tuoi seguaci)",
"import_followers_from_a_csv_file": "Importa una lista di chi segui da un file csv", "import_followers_from_a_csv_file": "Importa una lista di chi segui da un file CSV",
"import_theme": "Carica settaggi", "import_theme": "Carica impostazioni",
"inputRadius": "Campi di testo", "inputRadius": "Campi di testo",
"instance_default": "(predefinito: {value})", "instance_default": "(predefinito: {value})",
"interfaceLanguage": "Linguaggio dell'interfaccia", "interfaceLanguage": "Lingua dell'interfaccia",
"invalid_theme_imported": "Il file selezionato non è un file di tema per Pleroma supportato. Il tuo tema non è stato modificato.", "invalid_theme_imported": "Il file selezionato non è un tema supportato da Pleroma. Il tuo tema non è stato modificato.",
"limited_availability": "Non disponibile nel tuo browser", "limited_availability": "Non disponibile nel tuo browser",
"links": "Collegamenti", "links": "Collegamenti",
"lock_account_description": "Limita il tuo account solo per contatti approvati", "lock_account_description": "Limita il tuo account solo a seguaci approvati",
"loop_video": "Riproduci video in ciclo continuo", "loop_video": "Riproduci video in ciclo continuo",
"loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le gif di Mastodon)", "loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le \"gif\" di Mastodon)",
"new_password": "Nuova password", "new_password": "Nuova password",
"notification_visibility": "Tipi di notifiche da mostrare", "notification_visibility": "Tipi di notifiche da mostrare",
"notification_visibility_follows": "Nuove persone ti seguono", "notification_visibility_follows": "Nuove persone ti seguono",
"notification_visibility_likes": "Mi piace", "notification_visibility_likes": "Preferiti",
"notification_visibility_mentions": "Menzioni", "notification_visibility_mentions": "Menzioni",
"notification_visibility_repeats": "Condivisioni", "notification_visibility_repeats": "Condivisioni",
"no_rich_text_description": "Togli la formattazione del testo da tutti i post", "no_rich_text_description": "Togli la formattazione del testo da tutti i messaggi",
"oauth_tokens": "Token OAuth", "oauth_tokens": "Token OAuth",
"token": "Token", "token": "Token",
"refresh_token": "Aggiorna token", "refresh_token": "Aggiorna token",
"valid_until": "Valido fino a", "valid_until": "Valido fino a",
"revoke_token": "Revocare", "revoke_token": "Revoca",
"panelRadius": "Pannelli", "panelRadius": "Pannelli",
"pause_on_unfocused": "Metti in pausa l'aggiornamento continuo quando la scheda non è in primo piano", "pause_on_unfocused": "Interrompi l'aggiornamento continuo mentre la scheda è in secondo piano",
"presets": "Valori predefiniti", "presets": "Valori predefiniti",
"profile_tab": "Profilo", "profile_tab": "Profilo",
"radii_help": "Imposta l'arrotondamento dei bordi (in pixel)", "radii_help": "Imposta il raggio degli angoli (in pixel)",
"replies_in_timeline": "Risposte nella sequenza temporale", "replies_in_timeline": "Risposte nella sequenza personale",
"reply_visibility_all": "Mostra tutte le risposte", "reply_visibility_all": "Mostra tutte le risposte",
"reply_visibility_following": "Mostra solo le risposte dirette a me o agli utenti che seguo", "reply_visibility_following": "Mostra solo le risposte rivolte a me o agli utenti che seguo",
"reply_visibility_self": "Mostra solo risposte dirette a me", "reply_visibility_self": "Mostra solo risposte rivolte a me",
"saving_err": "Errore nel salvataggio delle impostazioni", "saving_err": "Errore nel salvataggio delle impostazioni",
"saving_ok": "Impostazioni salvate", "saving_ok": "Impostazioni salvate",
"security_tab": "Sicurezza", "security_tab": "Sicurezza",
"stop_gifs": "Riproduci GIF al passaggio del cursore del mouse", "stop_gifs": "Riproduci GIF al passaggio del cursore",
"streaming": "Abilita aggiornamento automatico dei nuovi post quando si è in alto alla pagina", "streaming": "Mostra automaticamente i nuovi messaggi quando sei in cima alla pagina",
"text": "Testo", "text": "Testo",
"theme_help": "Usa codici colore esadecimali (#rrggbb) per personalizzare il tuo schema di colori.", "theme_help": "Usa codici colore esadecimali (#rrggbb) per personalizzare il tuo schema di colori.",
"tooltipRadius": "Descrizioni/avvisi", "tooltipRadius": "Descrizioni/avvisi",
"values": { "values": {
"false": "no", "false": "no",
"true": "si" "true": "sì"
} }
}, },
"timeline": { "timeline": {
"error_fetching": "Errore nel prelievo aggiornamenti", "error_fetching": "Errore nell'aggiornamento",
"load_older": "Carica messaggi più vecchi", "load_older": "Carica messaggi più vecchi",
"show_new": "Mostra nuovi", "show_new": "Mostra nuovi",
"up_to_date": "Aggiornato", "up_to_date": "Aggiornato",
"collapse": "Riduci", "collapse": "Riduci",
"conversation": "Conversazione", "conversation": "Conversazione",
"no_retweet_hint": "La visibilità del post è impostata solo per chi ti segue o messaggio diretto e non può essere condiviso", "no_retweet_hint": "Il messaggio è diretto o solo per seguaci e non può essere condiviso",
"repeated": "condiviso" "repeated": "condiviso"
}, },
"user_card": { "user_card": {
"follow": "Segui", "follow": "Segui",
"followees": "Chi stai seguendo", "followees": "Chi stai seguendo",
"followers": "Chi ti segue", "followers": "Seguaci",
"following": "Lo stai seguendo!", "following": "Seguìto!",
"follows_you": "Ti segue!", "follows_you": "Ti segue!",
"mute": "Silenzia", "mute": "Silenzia",
"muted": "Silenziato", "muted": "Silenziato",
@ -152,9 +176,9 @@
"features_panel": { "features_panel": {
"chat": "Chat", "chat": "Chat",
"gopher": "Gopher", "gopher": "Gopher",
"media_proxy": "Media proxy", "media_proxy": "Proxy multimedia",
"scope_options": "Opzioni di visibilità", "scope_options": "Opzioni visibilità",
"text_limit": "Lunghezza limite", "text_limit": "Lunghezza massima",
"title": "Caratteristiche", "title": "Caratteristiche",
"who_to_follow": "Chi seguire" "who_to_follow": "Chi seguire"
}, },
@ -166,26 +190,36 @@
"login": "Accedi", "login": "Accedi",
"logout": "Disconnettiti", "logout": "Disconnettiti",
"password": "Password", "password": "Password",
"placeholder": "es. lain", "placeholder": "es. Lupo Lucio",
"register": "Registrati", "register": "Registrati",
"username": "Nome utente" "username": "Nome utente",
"description": "Accedi con OAuth",
"hint": "Accedi per partecipare alla discussione",
"authentication_code": "Codice di autenticazione",
"enter_recovery_code": "Inserisci un codice di recupero",
"enter_two_factor_code": "Inserisci un codice two-factor",
"recovery_code": "Codice di recupero",
"heading": {
"totp": "Autenticazione two-factor",
"recovery": "Recupero two-factor"
}
}, },
"post_status": { "post_status": {
"account_not_locked_warning": "Il tuo account non è {0}. Chiunque può seguirti e vedere i tuoi post riservati a chi ti segue.", "account_not_locked_warning": "Il tuo profilo non è {0}. Chiunque può seguirti e vedere i tuoi messaggi riservati ai tuoi seguaci.",
"account_not_locked_warning_link": "bloccato", "account_not_locked_warning_link": "protetto",
"attachments_sensitive": "Segna allegati come sensibili", "attachments_sensitive": "Nascondi gli allegati",
"content_type": { "content_type": {
"text/plain": "Testo normale" "text/plain": "Testo normale"
}, },
"content_warning": "Oggetto (facoltativo)", "content_warning": "Oggetto (facoltativo)",
"default": "Appena atterrato in L.A.", "default": "Sono appena atterrato a Fiumicino.",
"direct_warning": "Questo post sarà visibile solo dagli utenti menzionati.", "direct_warning": "Questo post sarà visibile solo dagli utenti menzionati.",
"posting": "Pubblica", "posting": "Sto pubblicando",
"scope": { "scope": {
"direct": "Diretto - Pubblicato solo per gli utenti menzionati", "direct": "Diretto - Visibile solo agli utenti menzionati",
"private": "Solo per chi ti segue - Visibile solo da chi ti segue", "private": "Solo per seguaci - Visibile solo dai tuoi seguaci",
"public": "Pubblico - Visibile sulla sequenza temporale pubblica", "public": "Pubblico - Visibile sulla sequenza pubblica",
"unlisted": "Non elencato - Non visibile sulla sequenza temporale pubblica" "unlisted": "Non elencato - Non visibile sulla sequenza pubblica"
} }
}, },
"registration": { "registration": {
@ -197,10 +231,93 @@
"token": "Codice d'invito" "token": "Codice d'invito"
}, },
"user_profile": { "user_profile": {
"timeline_title": "Sequenza Temporale dell'Utente" "timeline_title": "Sequenza dell'Utente"
}, },
"who_to_follow": { "who_to_follow": {
"more": "Più", "more": "Altro",
"who_to_follow": "Chi seguire" "who_to_follow": "Chi seguire"
},
"about": {
"mrf": {
"federation": "Federazione",
"keyword": {
"reject": "Rifiuta",
"replace": "Sostituisci",
"is_replaced_by": "→",
"keyword_policies": "Regole per parole chiave",
"ftl_removal": "Rimozione dalla sequenza globale"
},
"simple": {
"reject": "Rifiuta",
"accept": "Accetta",
"simple_policies": "Regole specifiche alla stanza",
"accept_desc": "Questa stanza accetta messaggi solo dalle seguenti stanze:",
"reject_desc": "Questa stanza non accetterà messaggi dalle stanze seguenti:",
"quarantine": "Quarantena",
"quarantine_desc": "Questa stanza inoltrerà solo messaggi pubblici alle seguenti stanze:",
"ftl_removal": "Rimozione dalla sequenza globale",
"ftl_removal_desc": "Questa stanza rimuove le seguenti stanze dalla sequenza globale:",
"media_removal": "Rimozione multimedia",
"media_removal_desc": "Questa istanza rimuove gli allegati dalle seguenti stanze:",
"media_nsfw": "Allegati oscurati forzatamente",
"media_nsfw_desc": "Questa stanza oscura gli allegati dei messaggi provenienti da queste stanze:"
},
"mrf_policies": "Regole RM abilitate",
"mrf_policies_desc": "Le regole RM cambiano il comportamento federativo della stanza. Vigono le seguenti regole:"
},
"staff": "Equipaggio"
},
"domain_mute_card": {
"mute": "Zittisci",
"mute_progress": "Zittisco...",
"unmute": "Ascolta",
"unmute_progress": "Procedo..."
},
"exporter": {
"export": "Esporta",
"processing": "In elaborazione, il tuo file sarà scaricabile a breve"
},
"image_cropper": {
"crop_picture": "Ritaglia immagine",
"save": "Salva",
"save_without_cropping": "Salva senza ritagliare",
"cancel": "Annulla"
},
"importer": {
"submit": "Invia",
"success": "Importato.",
"error": "L'importazione non è andata a buon fine."
},
"media_modal": {
"previous": "Precedente",
"next": "Prossimo"
},
"polls": {
"add_poll": "Sondaggio",
"add_option": "Alternativa",
"option": "Opzione",
"votes": "voti",
"vote": "Vota",
"type": "Tipo di sondaggio",
"single_choice": "Scelta singola",
"multiple_choices": "Scelta multipla",
"expiry": "Scadenza",
"expires_in": "Scade fra {0}",
"expired": "Scaduto {0} fa",
"not_enough_options": "Aggiungi altre risposte"
},
"interactions": {
"favs_repeats": "Condivisi e preferiti"
},
"emoji": {
"load_all": "Carico tutti i {emojiAmount} emoji",
"load_all_hint": "Primi {saneAmount} emoji caricati, caricarli tutti potrebbe causare rallentamenti.",
"unicode": "Emoji Unicode",
"custom": "Emoji personale",
"add_emoji": "Inserisci Emoji",
"search_emoji": "Cerca un emoji",
"keep_open": "Tieni aperto il menù",
"emoji": "Emoji",
"stickers": "Adesivi"
} }
} }

View file

@ -8,7 +8,7 @@
"media_proxy": "Media proxy", "media_proxy": "Media proxy",
"scope_options": "Zichtbaarheidsopties", "scope_options": "Zichtbaarheidsopties",
"text_limit": "Tekst limiet", "text_limit": "Tekst limiet",
"title": "Features", "title": "Kenmerken",
"who_to_follow": "Wie te volgen" "who_to_follow": "Wie te volgen"
}, },
"finder": { "finder": {
@ -16,31 +16,54 @@
"find_user": "Gebruiker zoeken" "find_user": "Gebruiker zoeken"
}, },
"general": { "general": {
"apply": "toepassen", "apply": "Toepassen",
"submit": "Verzend" "submit": "Verzend",
"more": "Meer",
"optional": "optioneel",
"show_more": "Bekijk meer",
"show_less": "Bekijk minder",
"dismiss": "Opheffen",
"cancel": "Annuleren",
"disable": "Uitschakelen",
"enable": "Inschakelen",
"confirm": "Bevestigen",
"verify": "Verifiëren",
"generic_error": "Er is een fout opgetreden"
}, },
"login": { "login": {
"login": "Log in", "login": "Log in",
"description": "Log in met OAuth", "description": "Log in met OAuth",
"logout": "Log uit", "logout": "Uitloggen",
"password": "Wachtwoord", "password": "Wachtwoord",
"placeholder": "bv. lain", "placeholder": "bijv. lain",
"register": "Registreer", "register": "Registreren",
"username": "Gebruikersnaam" "username": "Gebruikersnaam",
"hint": "Log in om deel te nemen aan de discussie",
"authentication_code": "Authenticatie code",
"enter_recovery_code": "Voer een herstelcode in",
"enter_two_factor_code": "Voer een twee-factor code in",
"recovery_code": "Herstelcode",
"heading": {
"totp": "Twee-factor authenticatie",
"recovery": "Twee-factor herstelling"
}
}, },
"nav": { "nav": {
"about": "Over", "about": "Over",
"back": "Terug", "back": "Terug",
"chat": "Locale Chat", "chat": "Lokale Chat",
"friend_requests": "Volgverzoek", "friend_requests": "Volgverzoeken",
"mentions": "Vermeldingen", "mentions": "Vermeldingen",
"dms": "Directe Berichten", "dms": "Directe Berichten",
"public_tl": "Publieke Tijdlijn", "public_tl": "Publieke Tijdlijn",
"timeline": "Tijdlijn", "timeline": "Tijdlijn",
"twkn": "Het Geheel Gekende Netwerk", "twkn": "Het Geheel Bekende Netwerk",
"user_search": "Zoek Gebruiker", "user_search": "Gebruiker Zoeken",
"who_to_follow": "Wie te volgen", "who_to_follow": "Wie te volgen",
"preferences": "Voorkeuren" "preferences": "Voorkeuren",
"administration": "Administratie",
"search": "Zoeken",
"interactions": "Interacties"
}, },
"notifications": { "notifications": {
"broken_favorite": "Onbekende status, aan het zoeken...", "broken_favorite": "Onbekende status, aan het zoeken...",
@ -49,25 +72,39 @@
"load_older": "Laad oudere meldingen", "load_older": "Laad oudere meldingen",
"notifications": "Meldingen", "notifications": "Meldingen",
"read": "Gelezen!", "read": "Gelezen!",
"repeated_you": "Herhaalde je status" "repeated_you": "Herhaalde je status",
"no_more_notifications": "Geen meldingen meer",
"migrated_to": "is gemigreerd naar",
"follow_request": "wil je volgen",
"reacted_with": "reageerde met {0}"
}, },
"post_status": { "post_status": {
"new_status": "Post nieuwe status", "new_status": "Nieuwe status plaatsen",
"account_not_locked_warning": "Je account is niet {0}. Iedereen die je volgt kan enkel-volgers posts lezen.", "account_not_locked_warning": "Je account is niet {0}. Iedereen kan je volgen om je alleen-volgers berichten te lezen.",
"account_not_locked_warning_link": "gesloten", "account_not_locked_warning_link": "gesloten",
"attachments_sensitive": "Markeer bijlage als gevoelig", "attachments_sensitive": "Markeer bijlagen als gevoelig",
"content_type": { "content_type": {
"text/plain": "Gewone tekst" "text/plain": "Platte tekst",
"text/html": "HTML",
"text/markdown": "Markdown",
"text/bbcode": "BBCode"
}, },
"content_warning": "Onderwerp (optioneel)", "content_warning": "Onderwerp (optioneel)",
"default": "Tijd voor een pauze!", "default": "Zojuist geland in L.A.",
"direct_warning": "Deze post zal enkel zichtbaar zijn voor de personen die genoemd zijn.", "direct_warning": "Deze post zal enkel zichtbaar zijn voor de personen die genoemd zijn.",
"posting": "Plaatsen", "posting": "Plaatsen",
"scope": { "scope": {
"direct": "Direct - Post enkel naar genoemde gebruikers", "direct": "Direct - Post enkel naar vermelde gebruikers",
"private": "Enkel volgers - Post enkel naar volgers", "private": "Enkel volgers - Post enkel naar volgers",
"public": "Publiek - Post op publieke tijdlijnen", "public": "Publiek - Post op publieke tijdlijnen",
"unlisted": "Unlisted - Toon niet op publieke tijdlijnen" "unlisted": "Niet Vermelden - Niet tonen op publieke tijdlijnen"
},
"direct_warning_to_all": "Dit bericht zal zichtbaar zijn voor alle vermelde gebruikers.",
"direct_warning_to_first_only": "Dit bericht zal alleen zichtbaar zijn voor de vermelde gebruikers aan het begin van het bericht.",
"scope_notice": {
"public": "Dit bericht zal voor iedereen zichtbaar zijn",
"unlisted": "Dit bericht zal niet zichtbaar zijn in de Publieke Tijdlijn en Het Geheel Bekende Netwerk",
"private": "Dit bericht zal voor alleen je volgers zichtbaar zijn"
} }
}, },
"registration": { "registration": {
@ -76,7 +113,7 @@
"fullname": "Weergave naam", "fullname": "Weergave naam",
"password_confirm": "Wachtwoord bevestiging", "password_confirm": "Wachtwoord bevestiging",
"registration": "Registratie", "registration": "Registratie",
"token": "Uitnodigingstoken", "token": "Uitnodigings-token",
"captcha": "CAPTCHA", "captcha": "CAPTCHA",
"new_captcha": "Klik op de afbeelding voor een nieuwe captcha", "new_captcha": "Klik op de afbeelding voor een nieuwe captcha",
"validations": { "validations": {
@ -86,141 +123,161 @@
"password_required": "moet ingevuld zijn", "password_required": "moet ingevuld zijn",
"password_confirmation_required": "moet ingevuld zijn", "password_confirmation_required": "moet ingevuld zijn",
"password_confirmation_match": "komt niet overeen met het wachtwoord" "password_confirmation_match": "komt niet overeen met het wachtwoord"
} },
"username_placeholder": "bijv. lain",
"fullname_placeholder": "bijv. Lain Iwakura",
"bio_placeholder": "bijv.\nHallo, ik ben Lain.\nIk ben een anime meisje woonachtig in een buitenwijk in Japan. Je kent me misschien van the Wired."
}, },
"settings": { "settings": {
"attachmentRadius": "Bijlages", "attachmentRadius": "Bijlages",
"attachments": "Bijlages", "attachments": "Bijlages",
"autoload": "Automatisch laden wanneer tot de bodem gescrold inschakelen", "autoload": "Automatisch laden inschakelen wanneer tot de bodem gescrold wordt",
"avatar": "Avatar", "avatar": "Avatar",
"avatarAltRadius": "Avatars (Meldingen)", "avatarAltRadius": "Avatars (Meldingen)",
"avatarRadius": "Avatars", "avatarRadius": "Avatars",
"background": "Achtergrond", "background": "Achtergrond",
"bio": "Bio", "bio": "Bio",
"btnRadius": "Knoppen", "btnRadius": "Knoppen",
"cBlue": "Blauw (Antwoord, volgen)", "cBlue": "Blauw (Beantwoorden, volgen)",
"cGreen": "Groen (Herhaal)", "cGreen": "Groen (Herhalen)",
"cOrange": "Oranje (Vind ik leuk)", "cOrange": "Oranje (Favoriet)",
"cRed": "Rood (Annuleer)", "cRed": "Rood (Annuleren)",
"change_password": "Verander Wachtwoord", "change_password": "Wachtwoord Wijzigen",
"change_password_error": "Er was een probleem bij het aanpassen van je wachtwoord.", "change_password_error": "Er is een fout opgetreden bij het wijzigen van je wachtwoord.",
"changed_password": "Wachtwoord succesvol aangepast!", "changed_password": "Wachtwoord succesvol gewijzigd!",
"collapse_subject": "Klap posts met onderwerp in", "collapse_subject": "Klap berichten met een onderwerp in",
"composing": "Samenstellen", "composing": "Opstellen",
"confirm_new_password": "Bevestig nieuw wachtwoord", "confirm_new_password": "Nieuw wachtwoord bevestigen",
"current_avatar": "Je huidige avatar", "current_avatar": "Je huidige avatar",
"current_password": "Huidig wachtwoord", "current_password": "Huidig wachtwoord",
"current_profile_banner": "Je huidige profiel banner", "current_profile_banner": "Je huidige profiel banner",
"data_import_export_tab": "Data Import / Export", "data_import_export_tab": "Data Import / Export",
"default_vis": "Standaard zichtbaarheidsscope", "default_vis": "Standaard zichtbaarheidsbereik",
"delete_account": "Verwijder Account", "delete_account": "Account Verwijderen",
"delete_account_description": "Verwijder je account en berichten permanent.", "delete_account_description": "Permanent je gegevens verwijderen en account deactiveren.",
"delete_account_error": "Er was een probleem bij het verwijderen van je account. Indien dit probleem blijft, gelieve de administratie van deze instantie te verwittigen.", "delete_account_error": "Er is een fout opgetreden bij het verwijderen van je account. Indien dit probleem zich voor blijft doen, neem dan contact op met de beheerder van deze instantie.",
"delete_account_instructions": "Typ je wachtwoord in de input hieronder om het verwijderen van je account te bevestigen.", "delete_account_instructions": "Voer je wachtwoord in het onderstaande invoerveld in om het verwijderen van je account te bevestigen.",
"export_theme": "Sla preset op", "export_theme": "Preset opslaan",
"filtering": "Filtering", "filtering": "Filtering",
"filtering_explanation": "Alle statussen die deze woorden bevatten worden genegeerd, één filter per lijn.", "filtering_explanation": "Alle statussen die deze woorden bevatten worden genegeerd, één filter per lijn",
"follow_export": "Volgers export", "follow_export": "Volgers exporteren",
"follow_export_button": "Exporteer je volgers naar een csv file", "follow_export_button": "Exporteer je volgers naar een csv bestand",
"follow_export_processing": "Aan het verwerken, binnen enkele ogenblikken wordt je gevraagd je bestand te downloaden", "follow_export_processing": "Aan het verwerken, binnen enkele ogenblikken wordt je gevraagd je bestand te downloaden",
"follow_import": "Volgers import", "follow_import": "Volgers importeren",
"follow_import_error": "Fout bij importeren volgers", "follow_import_error": "Fout bij importeren volgers",
"follows_imported": "Volgers geïmporteerd! Het kan even duren om ze allemaal te verwerken.", "follows_imported": "Volgers geïmporteerd! Het kan even duren voordat deze verwerkt zijn.",
"foreground": "Voorgrond", "foreground": "Voorgrond",
"general": "Algemeen", "general": "Algemeen",
"hide_attachments_in_convo": "Verberg bijlages in conversaties", "hide_attachments_in_convo": "Verberg bijlages in conversaties",
"hide_attachments_in_tl": "Verberg bijlages in de tijdlijn", "hide_attachments_in_tl": "Verberg bijlages in de tijdlijn",
"hide_isp": "Verberg instantie-specifiek paneel", "hide_isp": "Verberg instantie-specifiek paneel",
"preload_images": "Afbeeldingen voorladen", "preload_images": "Afbeeldingen vooraf laden",
"hide_post_stats": "Verberg post statistieken (bv. het aantal vind-ik-leuks)", "hide_post_stats": "Verberg bericht statistieken (bijv. het aantal favorieten)",
"hide_user_stats": "Verberg post statistieken (bv. het aantal volgers)", "hide_user_stats": "Verberg bericht statistieken (bijv. het aantal volgers)",
"import_followers_from_a_csv_file": "Importeer volgers uit een csv file", "import_followers_from_a_csv_file": "Importeer volgers uit een csv bestand",
"import_theme": "Laad preset", "import_theme": "Preset laden",
"inputRadius": "Invoer velden", "inputRadius": "Invoervelden",
"checkboxRadius": "Checkboxen", "checkboxRadius": "Checkboxen",
"instance_default": "(standaard: {value})", "instance_default": "(standaard: {value})",
"instance_default_simple": "(standaard)", "instance_default_simple": "(standaard)",
"interface": "Interface", "interface": "Interface",
"interfaceLanguage": "Interface taal", "interfaceLanguage": "Interface taal",
"invalid_theme_imported": "Het geselecteerde thema is geen door Pleroma ondersteund thema. Er zijn geen aanpassingen gedaan.", "invalid_theme_imported": "Het geselecteerde bestand is geen door Pleroma ondersteund thema. Er zijn geen aanpassingen gedaan.",
"limited_availability": "Onbeschikbaar in je browser", "limited_availability": "Niet beschikbaar in je browser",
"links": "Links", "links": "Links",
"lock_account_description": "Laat volgers enkel toe na expliciete toestemming", "lock_account_description": "Laat volgers enkel toe na expliciete toestemming",
"loop_video": "Speel videos af in een lus", "loop_video": "Herhaal video's",
"loop_video_silent_only": "Speel enkel videos zonder geluid af in een lus (bv. Mastodon's \"gifs\")", "loop_video_silent_only": "Herhaal enkel video's zonder geluid (bijv. Mastodon's \"gifs\")",
"name": "Naam", "name": "Naam",
"name_bio": "Naam & Bio", "name_bio": "Naam & Bio",
"new_password": "Nieuw wachtwoord", "new_password": "Nieuw wachtwoord",
"notification_visibility": "Type meldingen die getoond worden", "notification_visibility": "Type meldingen die getoond worden",
"notification_visibility_follows": "Volgers", "notification_visibility_follows": "Volgingen",
"notification_visibility_likes": "Vind-ik-leuks", "notification_visibility_likes": "Vind-ik-leuks",
"notification_visibility_mentions": "Vermeldingen", "notification_visibility_mentions": "Vermeldingen",
"notification_visibility_repeats": "Herhalingen", "notification_visibility_repeats": "Herhalingen",
"no_rich_text_description": "Strip rich text formattering van alle posts", "no_rich_text_description": "Verwijder rich text formattering van alle berichten",
"hide_network_description": "Toon niet wie mij volgt en wie ik volg.", "hide_network_description": "Toon niet wie mij volgt en wie ik volg.",
"nsfw_clickthrough": "Schakel doorklikbaar verbergen van NSFW bijlages in", "nsfw_clickthrough": "Doorklikbaar verbergen van gevoelige bijlages inschakelen",
"oauth_tokens": "OAuth-tokens", "oauth_tokens": "OAuth-tokens",
"token": "Token", "token": "Token",
"refresh_token": "Token vernieuwen", "refresh_token": "Token Vernieuwen",
"valid_until": "Geldig tot", "valid_until": "Geldig tot",
"revoke_token": "Intrekken", "revoke_token": "Intrekken",
"panelRadius": "Panelen", "panelRadius": "Panelen",
"pause_on_unfocused": "Pauzeer streamen wanneer de tab niet gefocused is", "pause_on_unfocused": "Streamen pauzeren wanneer de tab niet in focus is",
"presets": "Presets", "presets": "Presets",
"profile_background": "Profiel Achtergrond", "profile_background": "Profiel Achtergrond",
"profile_banner": "Profiel Banner", "profile_banner": "Profiel Banner",
"profile_tab": "Profiel", "profile_tab": "Profiel",
"radii_help": "Stel afronding van hoeken in de interface in (in pixels)", "radii_help": "Stel afronding van hoeken in de interface in (in pixels)",
"replies_in_timeline": "Antwoorden in tijdlijn", "replies_in_timeline": "Antwoorden in tijdlijn",
"reply_link_preview": "Schakel antwoordlink preview in bij over zweven met muisaanwijzer", "reply_link_preview": "Antwoord-link weergave inschakelen bij aanwijzen met muisaanwijzer",
"reply_visibility_all": "Toon alle antwoorden", "reply_visibility_all": "Alle antwoorden tonen",
"reply_visibility_following": "Toon enkel antwoorden naar mij of andere gebruikers gericht", "reply_visibility_following": "Enkel antwoorden tonen die aan mij of gevolgde gebruikers gericht zijn",
"reply_visibility_self": "Toon enkel antwoorden naar mij gericht", "reply_visibility_self": "Enkel antwoorden tonen die aan mij gericht zijn",
"saving_err": "Fout tijdens opslaan van instellingen", "saving_err": "Fout tijdens opslaan van instellingen",
"saving_ok": "Instellingen opgeslagen", "saving_ok": "Instellingen opgeslagen",
"security_tab": "Veiligheid", "security_tab": "Beveiliging",
"scope_copy": "Neem scope over bij antwoorden (Directe Berichten blijven altijd Direct)", "scope_copy": "Neem bereik over bij beantwoorden (Directe Berichten blijven altijd Direct)",
"set_new_avatar": "Zet nieuwe avatar", "set_new_avatar": "Nieuwe avatar instellen",
"set_new_profile_background": "Zet nieuwe profiel achtergrond", "set_new_profile_background": "Nieuwe profiel achtergrond instellen",
"set_new_profile_banner": "Zet nieuwe profiel banner", "set_new_profile_banner": "Nieuwe profiel banner instellen",
"settings": "Instellingen", "settings": "Instellingen",
"subject_input_always_show": "Maak onderwerpveld altijd zichtbaar", "subject_input_always_show": "Altijd onderwerpveld tonen",
"subject_line_behavior": "Kopieer onderwerp bij antwoorden", "subject_line_behavior": "Onderwerp kopiëren bij antwoorden",
"subject_line_email": "Zoals email: \"re: onderwerp\"", "subject_line_email": "Zoals email: \"re: onderwerp\"",
"subject_line_mastodon": "Zoals Mastodon: kopieer zoals het is", "subject_line_mastodon": "Zoals mastodon: kopieer zoals het is",
"subject_line_noop": "Kopieer niet", "subject_line_noop": "Niet kopiëren",
"stop_gifs": "Speel GIFs af bij zweven", "stop_gifs": "GIFs afspelen bij zweven",
"streaming": "Schakel automatisch streamen van posts in wanneer tot boven gescrold.", "streaming": "Automatisch streamen van nieuwe berichten inschakelen wanneer tot boven gescrold is",
"text": "Tekst", "text": "Tekst",
"theme": "Thema", "theme": "Thema",
"theme_help": "Gebruik hex color codes (#rrggbb) om je kleurschema te wijzigen.", "theme_help": "Gebruik hex color codes (#rrggbb) om je kleurschema te wijzigen.",
"theme_help_v2_1": "Je kan ook de kleur en transparantie van bepaalde componenten overschrijven door de checkbox aan te vinken, gebruik de \"Wis alles\" knop om alle overschrijvingen te annuleren.", "theme_help_v2_1": "Je kan ook de kleur en transparantie van bepaalde componenten overschrijven door de checkbox aan te vinken, gebruik de \"Alles wissen\" knop om alle overschrijvingen te annuleren.",
"theme_help_v2_2": "Iconen onder sommige items zijn achtergrond/tekst contrast indicators, zweef er over voor gedetailleerde info. Hou er rekening mee dat bij doorzichtigheid de ergst mogelijke situatie wordt weer gegeven.", "theme_help_v2_2": "Iconen onder sommige onderdelen zijn achtergrond/tekst contrast indicatoren, zweef er over voor gedetailleerde info. Hou er rekening mee dat bij doorzichtigheid de ergst mogelijke situatie wordt weer gegeven.",
"tooltipRadius": "Gereedschapstips/alarmen", "tooltipRadius": "Tooltips/alarmen",
"user_settings": "Gebruikers Instellingen", "user_settings": "Gebruikersinstellingen",
"values": { "values": {
"false": "nee", "false": "nee",
"true": "ja" "true": "ja"
}, },
"notifications": "Meldingen", "notifications": "Meldingen",
"enable_web_push_notifications": "Schakel web push meldingen in", "enable_web_push_notifications": "Web push meldingen inschakelen",
"style": { "style": {
"switcher": { "switcher": {
"keep_color": "Behoud kleuren", "keep_color": "Kleuren behouden",
"keep_shadows": "Behoud schaduwen", "keep_shadows": "Schaduwen behouden",
"keep_opacity": "Behoud transparantie", "keep_opacity": "Transparantie behouden",
"keep_roundness": "Behoud afrondingen", "keep_roundness": "Rondingen behouden",
"keep_fonts": "Behoud lettertypes", "keep_fonts": "Lettertypes behouden",
"save_load_hint": "\"Behoud\" opties behouden de momenteel ingestelde opties bij het selecteren of laden van thema's, maar slaan ook de genoemde opties op bij het exporteren van een thema. Wanneer alle selectievakjes zijn uitgeschakeld, zal het exporteren van thema's alles opslaan.", "save_load_hint": "\"Behoud\" opties behouden de momenteel ingestelde opties bij het selecteren of laden van thema's, maar slaan ook de genoemde opties op bij het exporteren van een thema. Wanneer alle selectievakjes zijn uitgeschakeld, zal het exporteren van thema's alles opslaan.",
"reset": "Reset", "reset": "Reset",
"clear_all": "Wis alles", "clear_all": "Alles wissen",
"clear_opacity": "Wis transparantie" "clear_opacity": "Transparantie wissen",
"keep_as_is": "Hou zoals het is",
"use_snapshot": "Oude versie",
"use_source": "Nieuwe versie",
"help": {
"future_version_imported": "Het geïmporteerde bestand is gemaakt voor een nieuwere versie van FE.",
"older_version_imported": "Het geïmporteerde bestand is gemaakt voor een oudere versie van FE.",
"upgraded_from_v2": "PleromaFE is bijgewerkt, het thema kan iets anders uitzien dan dat je gewend bent.",
"v2_imported": "Het geïmporteerde bestand is gemaakt voor een oudere FE. We proberen compatibiliteit te maximaliseren, maar het kan toch voorkomen dat er inconsistenties zijn.",
"snapshot_source_mismatch": "Versie conflict: waarschijnlijk was FE terug gerold en opnieuw bijgewerkt, indien je het thema aangepast hebt met de oudere versie van FE wil je waarschijnlijk de oude versie gebruiken, gebruik anders de nieuwe versie.",
"migration_napshot_gone": "Voor een onduidelijke reden mist de momentopname, dus sommige dingen kunnen anders uitzien dan je gewend bent.",
"migration_snapshot_ok": "Voor de zekerheid is een momentopname van het thema geladen. Je kunt proberen om de thema gegevens te laden.",
"fe_downgraded": "PleromaFE's versie is terug gerold.",
"fe_upgraded": "De thema-engine van PleromaFE is bijgewerkt na de versie update.",
"snapshot_missing": "Het bestand bevat geen thema momentopname, dus het thema kan anders uitzien dan je oorspronkelijk bedacht had.",
"snapshot_present": "Thema momentopname is geladen, alle waarden zijn overschreven. Je kunt in plaats daarvan ook de daadwerkelijke data van het thema laden."
},
"load_theme": "Thema laden"
}, },
"common": { "common": {
"color": "Kleur", "color": "Kleur",
"opacity": "Transparantie", "opacity": "Transparantie",
"contrast": { "contrast": {
"hint": "Contrast ratio is {ratio}, {level} {context}", "hint": "Contrast verhouding is {ratio}, {level} {context}",
"level": { "level": {
"aa": "voldoet aan de richtlijn van niveau AA (minimum)", "aa": "voldoet aan de richtlijn van niveau AA (minimum)",
"aaa": "voldoet aan de richtlijn van niveau AAA (aangeraden)", "aaa": "voldoet aan de richtlijn van niveau AAA (aangeraden)",
@ -233,8 +290,8 @@
} }
}, },
"common_colors": { "common_colors": {
"_tab_label": "Gemeenschappelijk", "_tab_label": "Algemeen",
"main": "Gemeenschappelijke kleuren", "main": "Algemene kleuren",
"foreground_hint": "Zie \"Geavanceerd\" tab voor meer gedetailleerde controle", "foreground_hint": "Zie \"Geavanceerd\" tab voor meer gedetailleerde controle",
"rgbo": "Iconen, accenten, badges" "rgbo": "Iconen, accenten, badges"
}, },
@ -244,58 +301,73 @@
"alert_error": "Fout", "alert_error": "Fout",
"badge": "Badge achtergrond", "badge": "Badge achtergrond",
"badge_notification": "Meldingen", "badge_notification": "Meldingen",
"panel_header": "Paneel hoofding", "panel_header": "Paneel koptekst",
"top_bar": "Top bar", "top_bar": "Top balk",
"borders": "Randen", "borders": "Randen",
"buttons": "Knoppen", "buttons": "Knoppen",
"inputs": "Invoervelden", "inputs": "Invoervelden",
"faint_text": "Vervaagde tekst" "faint_text": "Vervaagde tekst",
"tabs": "Tabbladen",
"toggled": "Geschakeld",
"disabled": "Uitgeschakeld",
"selectedMenu": "Geselecteerd menu item",
"selectedPost": "Geselecteerd bericht",
"pressed": "Ingedrukt",
"highlight": "Gemarkeerde elementen",
"icons": "Iconen",
"poll": "Poll grafiek",
"underlay": "Onderlaag",
"popover": "Tooltips, menu's, popovers",
"post": "Berichten / Gebruiker bios",
"alert_neutral": "Neutraal",
"alert_warning": "Waarschuwing"
}, },
"radii": { "radii": {
"_tab_label": "Rondheid" "_tab_label": "Rondheid"
}, },
"shadows": { "shadows": {
"_tab_label": "Schaduw en belichting", "_tab_label": "Schaduw en belichting",
"component": "Component", "component": "Onderdeel",
"override": "Overschrijven", "override": "Overschrijven",
"shadow_id": "Schaduw #{value}", "shadow_id": "Schaduw #{value}",
"blur": "Vervagen", "blur": "Vervagen",
"spread": "Spreid", "spread": "Spreiding",
"inset": "Inzet", "inset": "Inzet",
"hint": "Voor schaduw kan je ook --variable gebruiken als een kleur waarde om CSS3 variabelen te gebruiken. Houd er rekening mee dat het instellen van opaciteit in dit geval niet werkt.", "hint": "Voor schaduw kan je ook --variable gebruiken als een kleur waarde om CSS3 variabelen te gebruiken. Houd er rekening mee dat het instellen van opaciteit in dit geval niet werkt.",
"filter_hint": { "filter_hint": {
"always_drop_shadow": "Waarschuwing, deze schaduw gebruikt altijd {0} als de browser dit ondersteund.", "always_drop_shadow": "Waarschuwing, deze schaduw gebruikt altijd {0} als de browser dit ondersteund.",
"drop_shadow_syntax": "{0} ondersteund niet de {1} parameter en {2} sleutelwoord.", "drop_shadow_syntax": "{0} ondersteund niet de {1} parameter en {2} sleutelwoord.",
"avatar_inset": "Houd er rekening mee dat het combineren van zowel inzet and niet-inzet schaduwen op transparante avatars onverwachte resultaten kan opleveren.", "avatar_inset": "Houdt er rekening mee dat het combineren van zowel inzet and niet-inzet schaduwen op transparante avatars onverwachte resultaten kan opleveren.",
"spread_zero": "Schaduw met spreiding > 0 worden weergegeven alsof ze op nul staan", "spread_zero": "Schaduw met spreiding > 0 worden weergegeven alsof ze op nul staan",
"inset_classic": "Inzet schaduw zal {0} gebruiken" "inset_classic": "Inzet schaduw zal {0} gebruiken"
}, },
"components": { "components": {
"panel": "Paneel", "panel": "Paneel",
"panelHeader": "Paneel hoofding", "panelHeader": "Paneel koptekst",
"topBar": "Top bar", "topBar": "Top balk",
"avatar": "Gebruiker avatar (in profiel weergave)", "avatar": "Gebruikers avatar (in profiel weergave)",
"avatarStatus": "Gebruiker avatar (in post weergave)", "avatarStatus": "Gebruikers avatar (in bericht weergave)",
"popup": "Popups en gereedschapstips", "popup": "Popups en tooltips",
"button": "Knop", "button": "Knop",
"buttonHover": "Knop (zweven)", "buttonHover": "Knop (zweven)",
"buttonPressed": "Knop (ingedrukt)", "buttonPressed": "Knop (ingedrukt)",
"buttonPressedHover": "Knop (ingedrukt+zweven)", "buttonPressedHover": "Knop (ingedrukt+zweven)",
"input": "Invoerveld" "input": "Invoerveld"
} },
"hintV3": "Voor schaduwen kun je ook de {0} notatie gebruiken om de andere kleur invoer te gebruiken."
}, },
"fonts": { "fonts": {
"_tab_label": "Lettertypes", "_tab_label": "Lettertypes",
"help": "Selecteer het lettertype om te gebruiken voor elementen van de UI.Voor \"aangepast\" moet je de exacte naam van het lettertype invoeren zoals die in het systeem wordt weergegeven.", "help": "Selecteer het lettertype om te gebruiken voor elementen van de UI. Voor \"aangepast\" dien je de exacte naam van het lettertype in te voeren zoals die in het systeem wordt weergegeven.",
"components": { "components": {
"interface": "Interface", "interface": "Interface",
"input": "Invoervelden", "input": "Invoervelden",
"post": "Post tekst", "post": "Bericht tekst",
"postCode": "Monospaced tekst in een post (rich text)" "postCode": "Monospaced tekst in een bericht (rich text)"
}, },
"family": "Naam lettertype", "family": "Lettertype naam",
"size": "Grootte (in px)", "size": "Grootte (in px)",
"weight": "Gewicht (vetheid)", "weight": "Gewicht (dikgedruktheid)",
"custom": "Aangepast" "custom": "Aangepast"
}, },
"preview": { "preview": {
@ -305,31 +377,119 @@
"button": "Knop", "button": "Knop",
"text": "Nog een boel andere {0} en {1}", "text": "Nog een boel andere {0} en {1}",
"mono": "inhoud", "mono": "inhoud",
"input": "Tijd voor een pauze!", "input": "Zojuist geland in L.A.",
"faint_link": "handige gebruikershandleiding", "faint_link": "handige gebruikershandleiding",
"fine_print": "Lees onze {0} om niets nuttig te leren!", "fine_print": "Lees onze {0} om niets nuttig te leren!",
"header_faint": "Alles komt goed", "header_faint": "Alles komt goed",
"checkbox": "Ik heb de gebruikersvoorwaarden eens van ver bekeken", "checkbox": "Ik heb de gebruikersvoorwaarden gelezen",
"link": "een link" "link": "een leuke kleine link"
} }
},
"notification_setting_follows": "Gebruikers die je volgt",
"notification_setting_non_follows": "Gebruikers die je niet volgt",
"notification_setting_followers": "Gebruikers die je volgen",
"notification_setting_privacy": "Privacy",
"notification_setting_privacy_option": "Verberg de afzender en inhoud van push meldingen",
"notification_mutes": "Om niet langer meldingen te ontvangen van een specifieke gebruiker, kun je deze negeren.",
"app_name": "App naam",
"security": "Beveiliging",
"enter_current_password_to_confirm": "Voer je huidige wachtwoord in om je identiteit te bevestigen",
"mfa": {
"otp": "OTP",
"setup_otp": "OTP instellen",
"wait_pre_setup_otp": "OTP voorinstellen",
"confirm_and_enable": "Bevestig en schakel OTP in",
"title": "Twee-factor Authenticatie",
"generate_new_recovery_codes": "Genereer nieuwe herstelcodes",
"recovery_codes": "Herstelcodes.",
"waiting_a_recovery_codes": "Backup codes ontvangen...",
"authentication_methods": "Authenticatie methodes",
"scan": {
"title": "Scannen",
"desc": "Scan de QR code of voer een sleutel in met je twee-factor applicatie:",
"secret_code": "Sleutel"
},
"verify": {
"desc": "Voer de code van je twee-factor applicatie in om twee-factor authenticatie in te schakelen:"
},
"warning_of_generate_new_codes": "Wanneer je nieuwe herstelcodes genereert, zullen je oude code niet langer werken.",
"recovery_codes_warning": "Schrijf de codes op of sla ze op een veilige locatie op - anders kun je ze niet meer inzien. Als je toegang tot je 2FA app en herstelcodes verliest, zal je buitengesloten zijn uit je account."
},
"allow_following_move": "Automatisch volgen toestaan wanneer een gevolgd account migreert",
"block_export": "Blokkades exporteren",
"block_import": "Blokkades importeren",
"blocks_imported": "Blokkades geïmporteerd! Het kan even duren voordat deze verwerkt zijn.",
"blocks_tab": "Blokkades",
"change_email": "Email wijzigen",
"change_email_error": "Er is een fout opgetreden tijdens het wijzigen van je email.",
"changed_email": "Email succesvol gewijzigd!",
"domain_mutes": "Domeinen",
"avatar_size_instruction": "De aangeraden minimale afmeting voor avatar afbeeldingen is 150x150 pixels.",
"pad_emoji": "Vul emoji aan met spaties wanneer deze met de picker ingevoegd worden",
"emoji_reactions_on_timeline": "Toon emoji reacties op de tijdlijn",
"accent": "Accent",
"hide_muted_posts": "Verberg berichten van genegeerde gebruikers",
"max_thumbnails": "Maximaal aantal miniaturen per bericht",
"use_one_click_nsfw": "Open gevoelige bijlagen met slechts één klik",
"hide_filtered_statuses": "Gefilterde statussen verbergen",
"import_blocks_from_a_csv_file": "Importeer blokkades van een csv bestand",
"mutes_tab": "Negeringen",
"play_videos_in_modal": "Speel video's af in een popup frame",
"new_email": "Nieuwe Email",
"notification_visibility_emoji_reactions": "Reacties",
"no_blocks": "Geen blokkades",
"no_mutes": "Geen negeringen",
"hide_followers_description": "Niet tonen wie mij volgt",
"hide_followers_count_description": "Niet mijn volgers aantal tonen",
"hide_follows_count_description": "Niet mijn gevolgde aantal tonen",
"show_admin_badge": "Beheerders badge tonen in mijn profiel",
"autohide_floating_post_button": "Nieuw Bericht knop automatisch verbergen (mobiel)",
"search_user_to_block": "Zoek wie je wilt blokkeren",
"search_user_to_mute": "Zoek wie je wilt negeren",
"minimal_scopes_mode": "Bericht bereik-opties minimaliseren",
"post_status_content_type": "Bericht status content type",
"user_mutes": "Gebruikers",
"useStreamingApi": "Berichten en meldingen in real-time ontvangen",
"useStreamingApiWarning": "(Afgeraden, experimenteel, kan berichten overslaan)",
"type_domains_to_mute": "Voer domeinen in om te negeren",
"upload_a_photo": "Upload een foto",
"fun": "Plezier",
"greentext": "Meme pijlen",
"notification_setting": "Ontvang meldingen van:",
"block_export_button": "Exporteer je geblokkeerde gebruikers naar een csv bestand",
"block_import_error": "Fout bij importeren blokkades",
"discoverable": "Sta toe dat dit account ontdekt kan worden in zoekresultaten en andere diensten",
"use_contain_fit": "Snij bijlage in miniaturen niet bij",
"notification_visibility_moves": "Gebruiker Migraties",
"hide_follows_description": "Niet tonen wie ik volg",
"show_moderator_badge": "Moderators badge tonen in mijn profiel",
"notification_setting_filters": "Filters",
"notification_setting_non_followers": "Gebruikers die je niet volgen",
"notification_blocks": "Door een gebruiker te blokkeren, ontvang je geen meldingen meer van de gebruiker en wordt je abonnement op de gebruiker opgeheven.",
"version": {
"frontend_version": "Frontend Versie",
"backend_version": "Backend Versie",
"title": "Versie"
} }
}, },
"timeline": { "timeline": {
"collapse": "Inklappen", "collapse": "Inklappen",
"conversation": "Conversatie", "conversation": "Conversatie",
"error_fetching": "Fout bij ophalen van updates", "error_fetching": "Fout bij ophalen van updates",
"load_older": "Laad oudere Statussen", "load_older": "Oudere statussen laden",
"no_retweet_hint": "Post is gemarkeerd als enkel volgers of direct en kan niet worden herhaald", "no_retweet_hint": "Bericht is gemarkeerd als enkel volgers of direct en kan niet worden herhaald",
"repeated": "herhaalde", "repeated": "herhaalde",
"show_new": "Toon nieuwe", "show_new": "Nieuwe tonen",
"up_to_date": "Up-to-date" "up_to_date": "Up-to-date",
"no_statuses": "Geen statussen",
"no_more_statuses": "Geen statussen meer"
}, },
"user_card": { "user_card": {
"approve": "Goedkeuren", "approve": "Goedkeuren",
"block": "Blokkeren", "block": "Blokkeren",
"blocked": "Geblokkeerd!", "blocked": "Geblokkeerd!",
"deny": "Ontzeggen", "deny": "Weigeren",
"favorites": "Vind-ik-leuks", "favorites": "Favorieten",
"follow": "Volgen", "follow": "Volgen",
"follow_sent": "Aanvraag verzonden!", "follow_sent": "Aanvraag verzonden!",
"follow_progress": "Aanvragen…", "follow_progress": "Aanvragen…",
@ -340,31 +500,69 @@
"following": "Aan het volgen!", "following": "Aan het volgen!",
"follows_you": "Volgt jou!", "follows_you": "Volgt jou!",
"its_you": "'t is jij!", "its_you": "'t is jij!",
"mute": "Dempen", "mute": "Negeren",
"muted": "Gedempt", "muted": "Genegeerd",
"per_day": "per dag", "per_day": "per dag",
"remote_follow": "Volg vanop afstand", "remote_follow": "Volg vanop afstand",
"statuses": "Statussen" "statuses": "Statussen",
"admin_menu": {
"delete_user_confirmation": "Weet je het heel zeker? Deze uitvoering kan niet ongedaan worden gemaakt.",
"delete_user": "Gebruiker verwijderen",
"quarantine": "Federeren van gebruikers berichten verbieden",
"disable_any_subscription": "Volgen van gebruiker in zijn geheel verbieden",
"disable_remote_subscription": "Volgen van gebruiker vanaf andere instanties verbieden",
"sandbox": "Berichten forceren om alleen voor volgers zichtbaar te zijn",
"force_unlisted": "Berichten forceren om niet publiekelijk getoond te worden",
"strip_media": "Media van berichten verwijderen",
"force_nsfw": "Alle berichten als gevoelig markeren",
"delete_account": "Account verwijderen",
"deactivate_account": "Account deactiveren",
"activate_account": "Account activeren",
"revoke_moderator": "Moderatorsrechten intrekken",
"grant_moderator": "Moderatorsrechten toekennen",
"revoke_admin": "Beheerdersrechten intrekken",
"grant_admin": "Beheerdersrechten toekennen",
"moderation": "Moderatie"
},
"show_repeats": "Herhalingen tonen",
"hide_repeats": "Herhalingen verbergen",
"mute_progress": "Negeren...",
"unmute_progress": "Negering opheffen...",
"unmute": "Negering opheffen",
"block_progress": "Blokkeren...",
"unblock_progress": "Blokkade opheffen...",
"unblock": "Blokkade opheffen",
"unsubscribe": "Abonnement opzeggen",
"subscribe": "Abonneren",
"report": "Aangeven",
"mention": "Vermelding",
"media": "Media",
"hidden": "Verborgen"
}, },
"user_profile": { "user_profile": {
"timeline_title": "Gebruikers Tijdlijn" "timeline_title": "Gebruikers Tijdlijn",
"profile_loading_error": "Sorry, er is een fout opgetreden bij het laden van dit profiel.",
"profile_does_not_exist": "Sorry, dit profiel bestaat niet."
}, },
"who_to_follow": { "who_to_follow": {
"more": "Meer", "more": "Meer",
"who_to_follow": "Wie te volgen" "who_to_follow": "Wie te volgen"
}, },
"tool_tip": { "tool_tip": {
"media_upload": "Upload Media", "media_upload": "Media Uploaden",
"repeat": "Herhaal", "repeat": "Herhalen",
"reply": "Antwoord", "reply": "Beantwoorden",
"favorite": "Vind-ik-leuk", "favorite": "Favoriet maken",
"user_settings": "Gebruikers Instellingen" "user_settings": "Gebruikers Instellingen",
"reject_follow_request": "Volg-verzoek afwijzen",
"accept_follow_request": "Volg-aanvraag accepteren",
"add_reaction": "Reactie toevoegen"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "Upload gefaald.", "base": "Upload mislukt.",
"file_too_big": "Bestand is te groot [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "Bestand is te groot [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
"default": "Probeer later opnieuw" "default": "Probeer het later opnieuw"
}, },
"file_size_units": { "file_size_units": {
"B": "B", "B": "B",
@ -373,5 +571,177 @@
"GiB": "GiB", "GiB": "GiB",
"TiB": "TiB" "TiB": "TiB"
} }
},
"about": {
"mrf": {
"federation": "Federatie",
"keyword": {
"reject": "Afwijzen",
"replace": "Vervangen",
"is_replaced_by": "→",
"keyword_policies": "Zoekwoord Beleid",
"ftl_removal": "Verwijdering van \"Het Geheel Bekende Netwerk\" Tijdlijn"
},
"mrf_policies_desc": "MRF regels beïnvloeden het federatie gedrag van de instantie. De volgende regels zijn ingeschakeld:",
"mrf_policies": "Ingeschakelde MRF Regels",
"simple": {
"simple_policies": "Instantie-specifieke Regels",
"accept": "Accepteren",
"accept_desc": "Deze instantie accepteert alleen berichten van de volgende instanties:",
"reject": "Afwijzen",
"reject_desc": "Deze instantie zal geen berichten accepteren van de volgende instanties:",
"quarantine": "Quarantaine",
"quarantine_desc": "Deze instantie zal alleen publieke berichten sturen naar de volgende instanties:",
"ftl_removal_desc": "Deze instantie verwijdert de volgende instanties van \"Het Geheel Bekende Netwerk\" tijdlijn:",
"media_removal_desc": "Deze instantie verwijdert media van berichten van de volgende instanties:",
"media_nsfw_desc": "Deze instantie stelt media in als gevoelig in berichten van de volgende instanties:",
"ftl_removal": "Verwijderen van \"Het Geheel Bekende Netwerk\" Tijdlijn",
"media_removal": "Media Verwijdering",
"media_nsfw": "Forceer Media als Gevoelig"
}
},
"staff": "Personeel"
},
"domain_mute_card": {
"mute": "Negeren",
"mute_progress": "Negeren...",
"unmute": "Negering opheffen",
"unmute_progress": "Negering wordt opgeheven..."
},
"exporter": {
"export": "Exporteren",
"processing": "Verwerken, er wordt zo gevraagd om je bestand te downloaden"
},
"image_cropper": {
"save": "Opslaan",
"save_without_cropping": "Opslaan zonder bijsnijden",
"cancel": "Annuleren",
"crop_picture": "Afbeelding bijsnijden"
},
"importer": {
"submit": "Verzenden",
"success": "Succesvol geïmporteerd.",
"error": "Er is een fout opgetreden bij het importeren van dit bestand."
},
"media_modal": {
"previous": "Vorige",
"next": "Volgende"
},
"polls": {
"add_poll": "Poll Toevoegen",
"add_option": "Optie Toevoegen",
"option": "Optie",
"votes": "stemmen",
"vote": "Stem",
"single_choice": "Enkele keuze",
"multiple_choices": "Meerkeuze",
"expiry": "Poll leeftijd",
"expires_in": "Poll eindigt in {0}",
"expired": "Poll is {0} geleden beëindigd",
"not_enough_options": "Te weinig opties in poll",
"type": "Poll type"
},
"emoji": {
"emoji": "Emoji",
"keep_open": "Picker openhouden",
"search_emoji": "Zoek voor een emoji",
"add_emoji": "Emoji invoegen",
"unicode": "Unicode emoji",
"load_all": "Alle {emojiAmount} emoji worden geladen",
"stickers": "Stickers",
"load_all_hint": "Eerste {saneAmount} emoji geladen, alle emoji tegelijk laden kan problemen veroorzaken met prestaties.",
"custom": "Gepersonaliseerde emoji"
},
"interactions": {
"favs_repeats": "Herhalingen en Favorieten",
"follows": "Nieuwe volgingen",
"moves": "Gebruiker migreert",
"load_older": "Oudere interacties laden"
},
"remote_user_resolver": {
"searching_for": "Zoeken naar",
"error": "Niet gevonden.",
"remote_user_resolver": "Externe gebruikers zoeker"
},
"selectable_list": {
"select_all": "Alles selecteren"
},
"password_reset": {
"password_reset_required_but_mailer_is_disabled": "Je dient je wachtwoord opnieuw in te stellen, maar wachtwoord reset is uitgeschakeld. Neem contact op met de beheerder van deze instantie.",
"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",
"instruction": "Voer je email adres of gebruikersnaam in. We sturen je een link om je wachtwoord opnieuw in te stellen.",
"password_reset": "Wachtwoord opnieuw instellen",
"forgot_password": "Wachtwoord vergeten?"
},
"search": {
"no_results": "Geen resultaten",
"people_talking": "{count} personen aan het praten",
"person_talking": "{count} persoon aan het praten",
"hashtags": "Hashtags",
"people": "Personen"
},
"user_reporting": {
"generic_error": "Er is een fout opgetreden tijdens het verwerken van je verzoek.",
"submit": "Verzenden",
"forward_to": "Doorsturen naar {0}",
"forward_description": "Dit account hoort bij een andere server. Wil je een kopie van het rapport ook daarheen sturen?",
"additional_comments": "Aanvullende opmerkingen",
"add_comment_description": "Het rapport zal naar de moderators van de instantie worden verstuurd. Je kunt hieronder uitleg bijvoegen waarom je dit account wilt aangeven:",
"title": "{0} aangeven"
},
"status": {
"copy_link": "Link naar status kopiëren",
"status_unavailable": "Status niet beschikbaar",
"unmute_conversation": "Conversatie niet meer negeren",
"mute_conversation": "Conversatie negeren",
"replies_list": "Antwoorden:",
"reply_to": "Antwoorden aan",
"delete_confirm": "Wil je echt deze status verwijderen?",
"pin": "Aan profiel vastmaken",
"pinned": "Vastgezet",
"unpin": "Van profiel losmaken",
"delete": "Status verwijderen",
"repeats": "Herhalingen",
"favorites": "Favorieten"
},
"time": {
"years_short": "{0}j",
"year_short": "{0}j",
"years": "{0} jaren",
"year": "{0} jaar",
"weeks_short": "{0}w",
"week_short": "{0}w",
"weeks": "{0} weken",
"week": "{0} week",
"seconds_short": "{0}s",
"second_short": "{0}s",
"seconds": "{0} seconden",
"second": "{0} seconde",
"now_short": "nu",
"now": "zojuist",
"months_short": "{0}ma",
"month_short": "{0}ma",
"months": "{0} maanden",
"month": "{0} maand",
"minutes_short": "{0}min",
"minute_short": "{0}min",
"minutes": "{0} minuten",
"minute": "{0} minuut",
"in_past": "{0} geleden",
"in_future": "over {0}",
"hours_short": "{0}u",
"hour_short": "{0}u",
"hours": "{0} uren",
"hour": "{0} uur",
"days_short": "{0}d",
"day_short": "{0}d",
"days": "{0} dagen",
"day": "{0} dag"
} }
} }

View file

@ -1,7 +1,47 @@
{ {
"about": {
"mrf": {
"federation": "Federacja",
"keyword": {
"keyword_policies": "Zasady słów kluczowych",
"ftl_removal": "Usunięcie z \"Całej znanej sieci\"",
"reject": "Odrzucanie",
"replace": "Zastąpienie",
"is_replaced_by": "→"
},
"mrf_policies": "Włączone zasady MRF",
"mrf_policies_desc": "Zasady MRF zmieniają zachowanie federowania instancji. Następujące zasady są włączone:",
"simple": {
"simple_policies": "Zasady specyficzne dla instancji",
"accept": "Akceptowanie",
"accept_desc": "Ta instancja akceptuje tylko posty z wymienionych instancji:",
"reject": "Odrzucanie",
"reject_desc": "Ta instancja odrzuca posty z wymienionych instancji:",
"quarantine": "Kwarantanna",
"quarantine_desc": "Ta instancja wysyła tylko publiczne posty do wymienionych instancji:",
"ftl_removal": "Usunięcie z \"Całej znanej sieci\"",
"ftl_removal_desc": "Ta instancja usuwa wymienionych instancje z \"Całej znanej sieci\":",
"media_removal": "Usuwanie multimediów",
"media_removal_desc": "Ta instancja usuwa multimedia z postów od wymienionych instancji:",
"media_nsfw": "Multimedia ustawione jako wrażliwe",
"media_nsfw_desc": "Ta instancja wymusza, by multimedia z wymienionych instancji były ustawione jako wrażliwe:"
}
},
"staff": "Administracja"
},
"chat": { "chat": {
"title": "Czat" "title": "Czat"
}, },
"domain_mute_card": {
"mute": "Wycisz",
"mute_progress": "Wyciszam...",
"unmute": "Odcisz",
"unmute_progress": "Odciszam..."
},
"exporter": {
"export": "Eksportuj",
"processing": "Przetwarzam, za chwilę zostaniesz zapytany(-na) o ściągnięcie pliku"
},
"features_panel": { "features_panel": {
"chat": "Czat", "chat": "Czat",
"gopher": "Gopher", "gopher": "Gopher",
@ -20,7 +60,15 @@
"submit": "Wyślij", "submit": "Wyślij",
"more": "Więcej", "more": "Więcej",
"generic_error": "Wystąpił błąd", "generic_error": "Wystąpił błąd",
"optional": "nieobowiązkowe" "optional": "nieobowiązkowe",
"show_more": "Pokaż więcej",
"show_less": "Pokaż mniej",
"dismiss": "Odrzuć",
"cancel": "Anuluj",
"disable": "Wyłącz",
"enable": "Włącz",
"confirm": "Potwierdź",
"verify": "Zweryfikuj"
}, },
"image_cropper": { "image_cropper": {
"crop_picture": "Przytnij obrazek", "crop_picture": "Przytnij obrazek",
@ -28,6 +76,11 @@
"save_without_cropping": "Zapisz bez przycinania", "save_without_cropping": "Zapisz bez przycinania",
"cancel": "Anuluj" "cancel": "Anuluj"
}, },
"importer": {
"submit": "Wyślij",
"success": "Zaimportowano pomyślnie.",
"error": "Wystąpił błąd podczas importowania pliku."
},
"login": { "login": {
"login": "Zaloguj", "login": "Zaloguj",
"description": "Zaloguj używając OAuth", "description": "Zaloguj używając OAuth",
@ -36,7 +89,15 @@
"placeholder": "n.p. lain", "placeholder": "n.p. lain",
"register": "Zarejestruj", "register": "Zarejestruj",
"username": "Użytkownik", "username": "Użytkownik",
"hint": "Zaloguj się, aby dołączyć do dyskusji" "hint": "Zaloguj się, aby dołączyć do dyskusji",
"authentication_code": "Kod weryfikacyjny",
"enter_recovery_code": "Wprowadź kod zapasowy",
"enter_two_factor_code": "Wprowadź kod weryfikacyjny",
"recovery_code": "Kod zapasowy",
"heading": {
"totp": "Weryfikacja dwuetapowa",
"recovery": "Zapasowa weryfikacja dwuetapowa"
}
}, },
"media_modal": { "media_modal": {
"previous": "Poprzednie", "previous": "Poprzednie",
@ -44,15 +105,18 @@
}, },
"nav": { "nav": {
"about": "O nas", "about": "O nas",
"administration": "Administracja",
"back": "Wróć", "back": "Wróć",
"chat": "Lokalny czat", "chat": "Lokalny czat",
"friend_requests": "Prośby o możliwość obserwacji", "friend_requests": "Prośby o możliwość obserwacji",
"mentions": "Wzmianki", "mentions": "Wzmianki",
"interactions": "Interakcje",
"dms": "Wiadomości prywatne", "dms": "Wiadomości prywatne",
"public_tl": "Publiczna oś czasu", "public_tl": "Publiczna oś czasu",
"timeline": "Oś czasu", "timeline": "Oś czasu",
"twkn": "Cała znana sieć", "twkn": "Cała znana sieć",
"user_search": "Wyszukiwanie użytkowników", "user_search": "Wyszukiwanie użytkowników",
"search": "Wyszukiwanie",
"who_to_follow": "Sugestie obserwacji", "who_to_follow": "Sugestie obserwacji",
"preferences": "Preferencje" "preferences": "Preferencje"
}, },
@ -64,7 +128,41 @@
"notifications": "Powiadomienia", "notifications": "Powiadomienia",
"read": "Przeczytane!", "read": "Przeczytane!",
"repeated_you": "powtórzył(-a) twój status", "repeated_you": "powtórzył(-a) twój status",
"no_more_notifications": "Nie masz więcej powiadomień" "no_more_notifications": "Nie masz więcej powiadomień",
"migrated_to": "wyemigrował do",
"reacted_with": "zareagował z {0}",
"follow_request": "chce ciebie obserwować"
},
"polls": {
"add_poll": "Dodaj ankietę",
"add_option": "Dodaj opcję",
"option": "Opcja",
"votes": "głosów",
"vote": "Głosuj",
"type": "Typ ankiety",
"single_choice": "jednokrotnego wyboru",
"multiple_choices": "wielokrotnego wyboru",
"expiry": "Czas trwania ankiety",
"expires_in": "Ankieta kończy się za {0}",
"expired": "Ankieta skończyła się {0} temu",
"not_enough_options": "Zbyt mało unikalnych opcji w ankiecie"
},
"emoji": {
"stickers": "Naklejki",
"emoji": "Emoji",
"keep_open": "Zostaw selektor otwarty",
"search_emoji": "Wyszukaj emoji",
"add_emoji": "Wstaw emoji",
"custom": "Niestandardowe emoji",
"unicode": "Emoji unicode",
"load_all_hint": "Załadowano pierwsze {saneAmount} emoji, Załadowanie wszystkich emoji może spowodować problemy z wydajnością.",
"load_all": "Ładuję wszystkie {emojiAmount} emoji"
},
"interactions": {
"favs_repeats": "Powtórzenia i ulubione",
"follows": "Nowi obserwujący",
"moves": "Użytkownik migruje",
"load_older": "Załaduj starsze interakcje"
}, },
"post_status": { "post_status": {
"new_status": "Dodaj nowy status", "new_status": "Dodaj nowy status",
@ -79,8 +177,14 @@
}, },
"content_warning": "Temat (nieobowiązkowy)", "content_warning": "Temat (nieobowiązkowy)",
"default": "Właśnie wróciłem z kościoła", "default": "Właśnie wróciłem z kościoła",
"direct_warning": "Ten wpis zobaczą tylko osoby, o których wspomniałeś(-aś).", "direct_warning_to_all": "Ten wpis zobaczą wszystkie osoby, o których wspomniałeś(-aś).",
"direct_warning_to_first_only": "Ten wpis zobaczą tylko te osoby, o których wspomniałeś(-aś) na początku wiadomości.",
"posting": "Wysyłanie", "posting": "Wysyłanie",
"scope_notice": {
"public": "Ten post będzie widoczny dla każdego",
"private": "Ten post będzie widoczny tylko dla twoich obserwujących",
"unlisted": "Ten post nie będzie widoczny na publicznej osi czasu i całej znanej sieci"
},
"scope": { "scope": {
"direct": "Bezpośredni Tylko dla wspomnianych użytkowników", "direct": "Bezpośredni Tylko dla wspomnianych użytkowników",
"private": "Tylko dla obserwujących Umieść dla osób, które cię obserwują", "private": "Tylko dla obserwujących Umieść dla osób, które cię obserwują",
@ -109,8 +213,40 @@
"password_confirmation_match": "musi być takie jak hasło" "password_confirmation_match": "musi być takie jak hasło"
} }
}, },
"remote_user_resolver": {
"remote_user_resolver": "Wyszukiwarka użytkowników nietutejszych",
"searching_for": "Szukam",
"error": "Nie znaleziono."
},
"selectable_list": {
"select_all": "Zaznacz wszystko"
},
"settings": { "settings": {
"app_name": "Nazwa aplikacji", "app_name": "Nazwa aplikacji",
"security": "Bezpieczeństwo",
"enter_current_password_to_confirm": "Wprowadź obecne hasło, by potwierdzić twoją tożsamość",
"mfa": {
"otp": "OTP",
"setup_otp": "Ustaw OTP",
"wait_pre_setup_otp": "początkowe ustawianie OTP",
"confirm_and_enable": "Potwierdź i włącz OTP",
"title": "Weryfikacja dwuetapowa",
"generate_new_recovery_codes": "Wygeneruj nowe kody zapasowe",
"warning_of_generate_new_codes": "Po tym gdy wygenerujesz nowe kody zapasowe, stare przestaną działać.",
"recovery_codes": "Kody zapasowe.",
"waiting_a_recovery_codes": "Otrzymuję kody zapasowe...",
"recovery_codes_warning": "Spisz kody na kartce papieru, albo zapisz je w bezpiecznym miejscu - inaczej nie zobaczysz ich już nigdy. Jeśli stracisz dostęp do twojej aplikacji 2FA i kodów zapasowych, nie będziesz miał(-a) dostępu do swojego konta.",
"authentication_methods": "Metody weryfikacji",
"scan": {
"title": "Skanuj",
"desc": "Zeskanuj ten kod QR używając twojej aplikacji 2FA albo wpisz ten klucz:",
"secret_code": "Klucz"
},
"verify": {
"desc": "By włączyć weryfikację dwuetapową, wpisz kod z twojej aplikacji 2FA:"
}
},
"allow_following_move": "Zezwalaj na automatyczną obserwację gdy obserwowane konto migruje",
"attachmentRadius": "Załączniki", "attachmentRadius": "Załączniki",
"attachments": "Załączniki", "attachments": "Załączniki",
"autoload": "Włącz automatyczne ładowanie po przewinięciu do końca strony", "autoload": "Włącz automatyczne ładowanie po przewinięciu do końca strony",
@ -119,12 +255,20 @@
"avatarRadius": "Awatary", "avatarRadius": "Awatary",
"background": "Tło", "background": "Tło",
"bio": "Bio", "bio": "Bio",
"block_export": "Eksport blokad",
"block_export_button": "Eksportuj twoje blokady do pliku .csv",
"block_import": "Import blokad",
"block_import_error": "Wystąpił błąd podczas importowania blokad",
"blocks_imported": "Zaimportowano blokady, przetwarzanie może zająć trochę czasu.",
"blocks_tab": "Bloki", "blocks_tab": "Bloki",
"btnRadius": "Przyciski", "btnRadius": "Przyciski",
"cBlue": "Niebieski (odpowiedz, obserwuj)", "cBlue": "Niebieski (odpowiedz, obserwuj)",
"cGreen": "Zielony (powtórzenia)", "cGreen": "Zielony (powtórzenia)",
"cOrange": "Pomarańczowy (ulubione)", "cOrange": "Pomarańczowy (ulubione)",
"cRed": "Czerwony (anuluj)", "cRed": "Czerwony (anuluj)",
"change_email": "Zmień email",
"change_email_error": "Wystąpił problem podczas zmiany emaila.",
"changed_email": "Pomyślnie zmieniono email!",
"change_password": "Zmień hasło", "change_password": "Zmień hasło",
"change_password_error": "Podczas zmiany hasła wystąpił problem.", "change_password_error": "Podczas zmiany hasła wystąpił problem.",
"changed_password": "Pomyślnie zmieniono hasło!", "changed_password": "Pomyślnie zmieniono hasło!",
@ -137,19 +281,23 @@
"data_import_export_tab": "Import/eksport danych", "data_import_export_tab": "Import/eksport danych",
"default_vis": "Domyślny zakres widoczności", "default_vis": "Domyślny zakres widoczności",
"delete_account": "Usuń konto", "delete_account": "Usuń konto",
"delete_account_description": "Trwale usuń konto i wszystkie posty.", "delete_account_description": "Trwale usuń dane i zdezaktywuj konto.",
"delete_account_error": "Wystąpił problem z usuwaniem twojego konta. Jeżeli problem powtarza się, poinformuj administratora swojej instancji.", "delete_account_error": "Wystąpił problem z usuwaniem twojego konta. Jeżeli problem powtarza się, poinformuj administratora swojej instancji.",
"delete_account_instructions": "Wprowadź swoje hasło w poniższe pole aby potwierdzić usunięcie konta.", "delete_account_instructions": "Wprowadź swoje hasło w poniższe pole aby potwierdzić usunięcie konta.",
"discoverable": "Zezwól na odkrywanie tego konta w wynikach wyszukiwania i innych usługach",
"domain_mutes": "Domeny",
"avatar_size_instruction": "Zalecany minimalny rozmiar awatarów to 150x150 pikseli.", "avatar_size_instruction": "Zalecany minimalny rozmiar awatarów to 150x150 pikseli.",
"pad_emoji": "Dodaj odstęp z obu stron emoji podczas dodawania selektorem",
"emoji_reactions_on_timeline": "Pokaż reakcje emoji na osi czasu",
"export_theme": "Zapisz motyw", "export_theme": "Zapisz motyw",
"filtering": "Filtrowanie", "filtering": "Filtrowanie",
"filtering_explanation": "Wszystkie statusy zawierające te słowa będą wyciszone. Jedno słowo na linijkę.", "filtering_explanation": "Wszystkie statusy zawierające te słowa będą wyciszone. Jedno słowo na linijkę.",
"follow_export": "Eksport obserwowanych", "follow_export": "Eksport obserwowanych",
"follow_export_button": "Eksportuj swoją listę obserwowanych do pliku CSV", "follow_export_button": "Eksportuj swoją listę obserwowanych do pliku CSV",
"follow_export_processing": "Przetwarzanie, wkrótce twój plik zacznie się ściągać.",
"follow_import": "Import obserwowanych", "follow_import": "Import obserwowanych",
"follow_import_error": "Błąd przy importowaniu obserwowanych", "follow_import_error": "Błąd przy importowaniu obserwowanych",
"follows_imported": "Obserwowani zaimportowani! Przetwarzanie może trochę potrwać.", "follows_imported": "Obserwowani zaimportowani! Przetwarzanie może trochę potrwać.",
"accent": "Akcent",
"foreground": "Pierwszy plan", "foreground": "Pierwszy plan",
"general": "Ogólne", "general": "Ogólne",
"hide_attachments_in_convo": "Ukrywaj załączniki w rozmowach", "hide_attachments_in_convo": "Ukrywaj załączniki w rozmowach",
@ -162,18 +310,19 @@
"hide_post_stats": "Ukrywaj statysyki postów (np. liczbę polubień)", "hide_post_stats": "Ukrywaj statysyki postów (np. liczbę polubień)",
"hide_user_stats": "Ukrywaj statysyki użytkowników (np. liczbę obserwujących)", "hide_user_stats": "Ukrywaj statysyki użytkowników (np. liczbę obserwujących)",
"hide_filtered_statuses": "Ukrywaj filtrowane statusy", "hide_filtered_statuses": "Ukrywaj filtrowane statusy",
"import_blocks_from_a_csv_file": "Importuj blokady z pliku CSV",
"import_followers_from_a_csv_file": "Importuj obserwowanych z pliku CSV", "import_followers_from_a_csv_file": "Importuj obserwowanych z pliku CSV",
"import_theme": "Załaduj motyw", "import_theme": "Załaduj motyw",
"inputRadius": "Pola tekstowe", "inputRadius": "Pola tekstowe",
"checkboxRadius": "Pola wyboru", "checkboxRadius": "Pola wyboru",
"instance_default": "(domyślny: {value})", "instance_default": "(domyślnie: {value})",
"instance_default_simple": "(domyślny)", "instance_default_simple": "(domyślne)",
"interface": "Interfejs", "interface": "Interfejs",
"interfaceLanguage": "Język interfejsu", "interfaceLanguage": "Język interfejsu",
"invalid_theme_imported": "Wybrany plik nie jest obsługiwanym motywem Pleromy. Nie dokonano zmian w twoim motywie.", "invalid_theme_imported": "Wybrany plik nie jest obsługiwanym motywem Pleromy. Nie dokonano zmian w twoim motywie.",
"limited_availability": "Niedostępne w twojej przeglądarce", "limited_availability": "Niedostępne w twojej przeglądarce",
"links": "Łącza", "links": "Łącza",
"lock_account_description": "Ogranicz swoje konto dla zatwierdzonych obserwowanych", "lock_account_description": "Spraw, by konto mogli wyświetlać tylko zatwierdzeni obserwujący",
"loop_video": "Zapętlaj filmy", "loop_video": "Zapętlaj filmy",
"loop_video_silent_only": "Zapętlaj tylko filmy bez dźwięku (np. mastodonowe „gify”)", "loop_video_silent_only": "Zapętlaj tylko filmy bez dźwięku (np. mastodonowe „gify”)",
"mutes_tab": "Wyciszenia", "mutes_tab": "Wyciszenia",
@ -181,17 +330,22 @@
"use_contain_fit": "Nie przycinaj załączników na miniaturach", "use_contain_fit": "Nie przycinaj załączników na miniaturach",
"name": "Imię", "name": "Imię",
"name_bio": "Imię i bio", "name_bio": "Imię i bio",
"new_email": "Nowy email",
"new_password": "Nowe hasło", "new_password": "Nowe hasło",
"notification_visibility": "Rodzaje powiadomień do wyświetlania", "notification_visibility": "Rodzaje powiadomień do wyświetlania",
"notification_visibility_follows": "Obserwacje", "notification_visibility_follows": "Obserwacje",
"notification_visibility_likes": "Ulubione", "notification_visibility_likes": "Ulubione",
"notification_visibility_mentions": "Wzmianki", "notification_visibility_mentions": "Wzmianki",
"notification_visibility_repeats": "Powtórzenia", "notification_visibility_repeats": "Powtórzenia",
"notification_visibility_moves": "Użytkownik migruje",
"notification_visibility_emoji_reactions": "Reakcje",
"no_rich_text_description": "Usuwaj formatowanie ze wszystkich postów", "no_rich_text_description": "Usuwaj formatowanie ze wszystkich postów",
"no_blocks": "Bez blokad", "no_blocks": "Bez blokad",
"no_mutes": "Bez wyciszeń", "no_mutes": "Bez wyciszeń",
"hide_follows_description": "Nie pokazuj kogo obserwuję", "hide_follows_description": "Nie pokazuj kogo obserwuję",
"hide_followers_description": "Nie pokazuj kto mnie obserwuje", "hide_followers_description": "Nie pokazuj kto mnie obserwuje",
"hide_follows_count_description": "Nie pokazuj licznika obserwowanych",
"hide_followers_count_description": "Nie pokazuj licznika obserwujących",
"show_admin_badge": "Pokazuj odznakę Administrator na moim profilu", "show_admin_badge": "Pokazuj odznakę Administrator na moim profilu",
"show_moderator_badge": "Pokazuj odznakę Moderator na moim profilu", "show_moderator_badge": "Pokazuj odznakę Moderator na moim profilu",
"nsfw_clickthrough": "Włącz domyślne ukrywanie załączników o treści nieprzyzwoitej (NSFW)", "nsfw_clickthrough": "Włącz domyślne ukrywanie załączników o treści nieprzyzwoitej (NSFW)",
@ -212,10 +366,14 @@
"reply_visibility_all": "Pokazuj wszystkie odpowiedzi", "reply_visibility_all": "Pokazuj wszystkie odpowiedzi",
"reply_visibility_following": "Pokazuj tylko odpowiedzi skierowane do mnie i osób które obserwuję", "reply_visibility_following": "Pokazuj tylko odpowiedzi skierowane do mnie i osób które obserwuję",
"reply_visibility_self": "Pokazuj tylko odpowiedzi skierowane do mnie", "reply_visibility_self": "Pokazuj tylko odpowiedzi skierowane do mnie",
"autohide_floating_post_button": "Ukryj automatycznie przycisk \"Nowy post\" (mobile)",
"saving_err": "Nie udało się zapisać ustawień", "saving_err": "Nie udało się zapisać ustawień",
"saving_ok": "Zapisano ustawienia", "saving_ok": "Zapisano ustawienia",
"search_user_to_block": "Wyszukaj kogo chcesz zablokować",
"search_user_to_mute": "Wyszukaj kogo chcesz wyciszyć",
"security_tab": "Bezpieczeństwo", "security_tab": "Bezpieczeństwo",
"scope_copy": "Kopiuj zakres podczas odpowiadania (DM-y zawsze są kopiowane)", "scope_copy": "Kopiuj zakres podczas odpowiadania (DM-y zawsze są kopiowane)",
"minimal_scopes_mode": "Zminimalizuj opcje wyboru zakresu postów",
"set_new_avatar": "Ustaw nowy awatar", "set_new_avatar": "Ustaw nowy awatar",
"set_new_profile_background": "Ustaw nowe tło profilu", "set_new_profile_background": "Ustaw nowe tło profilu",
"set_new_profile_banner": "Ustaw nowy banner profilu", "set_new_profile_banner": "Ustaw nowy banner profilu",
@ -228,19 +386,32 @@
"post_status_content_type": "Post status content type", "post_status_content_type": "Post status content type",
"stop_gifs": "Odtwarzaj GIFy po najechaniu kursorem", "stop_gifs": "Odtwarzaj GIFy po najechaniu kursorem",
"streaming": "Włącz automatycznie strumieniowanie nowych postów gdy jesteś na początku strony", "streaming": "Włącz automatycznie strumieniowanie nowych postów gdy jesteś na początku strony",
"user_mutes": "Użytkownicy",
"useStreamingApi": "Otrzymuj posty i powiadomienia w czasie rzeczywistym",
"useStreamingApiWarning": "(Niezalecane, eksperymentalne, pomija posty)",
"text": "Tekst", "text": "Tekst",
"theme": "Motyw", "theme": "Motyw",
"theme_help": "Użyj kolorów w notacji szesnastkowej (#rrggbb), by stworzyć swój motyw.", "theme_help": "Użyj kolorów w notacji szesnastkowej (#rrggbb), by stworzyć swój motyw.",
"theme_help_v2_1": "Możesz też zastąpić kolory i widoczność poszczególnych komponentów przełączając pola wyboru, użyj „Wyczyść wszystko” aby usunąć wszystkie zastąpienia.", "theme_help_v2_1": "Możesz też zastąpić kolory i widoczność poszczególnych komponentów przełączając pola wyboru, użyj „Wyczyść wszystko” aby usunąć wszystkie zastąpienia.",
"theme_help_v2_2": "Ikony pod niektórych wpisami są wskaźnikami kontrastu pomiędzy tłem a tekstem, po najechaniu na nie otrzymasz szczegółowe informacje. Zapamiętaj, że jeżeli używasz przezroczystości, wskaźniki pokazują najgorszy możliwy przypadek.", "theme_help_v2_2": "Ikony pod niektórych wpisami są wskaźnikami kontrastu pomiędzy tłem a tekstem, po najechaniu na nie otrzymasz szczegółowe informacje. Zapamiętaj, że jeżeli używasz przezroczystości, wskaźniki pokazują najgorszy możliwy przypadek.",
"tooltipRadius": "Etykiety/alerty", "tooltipRadius": "Etykiety/alerty",
"type_domains_to_mute": "Wpisz domeny, które chcesz wyciszyć",
"upload_a_photo": "Wyślij zdjęcie", "upload_a_photo": "Wyślij zdjęcie",
"user_settings": "Ustawienia użytkownika", "user_settings": "Ustawienia użytkownika",
"values": { "values": {
"false": "nie", "false": "nie",
"true": "tak" "true": "tak"
}, },
"fun": "Zabawa",
"greentext": "Memiczne strzałki",
"notifications": "Powiadomienia", "notifications": "Powiadomienia",
"notification_setting": "Otrzymuj powiadomienia od:",
"notification_setting_follows": "Ludzi których obserwujesz",
"notification_setting_non_follows": "Ludzi których nie obserwujesz",
"notification_setting_followers": "Ludzi którzy obserwują ciebie",
"notification_setting_non_followers": "Ludzi którzy nie obserwują ciebie",
"notification_mutes": "By przestać otrzymywać powiadomienia od jednego użytkownika, wycisz go.",
"notification_blocks": "Blokowanie uzytkownika zatrzymuje wszystkie powiadomienia i odsubskrybowuje go.",
"enable_web_push_notifications": "Włącz powiadomienia push", "enable_web_push_notifications": "Włącz powiadomienia push",
"style": { "style": {
"switcher": { "switcher": {
@ -249,10 +420,27 @@
"keep_opacity": "Zachowaj widoczność", "keep_opacity": "Zachowaj widoczność",
"keep_roundness": "Zachowaj zaokrąglenie", "keep_roundness": "Zachowaj zaokrąglenie",
"keep_fonts": "Zachowaj czcionki", "keep_fonts": "Zachowaj czcionki",
"save_load_hint": "Opcje „zachowaj” pozwalają na pozostanie przy obecnych opcjach po wybraniu lub załadowaniu motywu, jak i przechowywanie ich podczas eksportowania motywu. Jeżeli wszystkie są odznaczone, eksportowanie motywu spowoduje zapisanie wszystkiego.", "save_load_hint": "Opcje „zachowaj” pozwalają na pozostanie przy obecnych opcjach po wybraniu lub załadowaniu motywu, jak i przechowywanie ich podczas eksportowania motywu. Jeżeli wszystkie opcje są odznaczone, eksportowanie motywu spowoduje zapisanie wszystkiego.",
"reset": "Wyzeruj", "reset": "Wyzeruj",
"clear_all": "Wyczyść wszystko", "clear_all": "Wyczyść wszystko",
"clear_opacity": "Wyczyść widoczność" "clear_opacity": "Wyczyść widoczność",
"load_theme": "Załaduj motyw",
"keep_as_is": "Zostaw po staremu",
"use_snapshot": "Stara wersja",
"use_source": "Nowa wersja",
"help": {
"upgraded_from_v2": "PleromaFE zostało zaaktualizowane, motyw może wyglądać nieco inaczej niż zapamiętałeś(-aś).",
"v2_imported": "Plik który zaimportowałeś(-aś) został stworzony dla starszego FE. Próbujemy zwiększyć kompatybilność, lecz wciąż mogą występować rozbieżności.",
"future_version_imported": "Plik który zaimportowałeś(-aś) został stworzony w nowszej wersji FE.",
"older_version_imported": "Plik który zaimportowałeś(-aś) został stworzony w starszej wersji FE.",
"snapshot_present": "Migawka motywu jest załadowana, więc wszystkie wartości zostały nadpisane. Zamiast tego możesz załadować właściwe dane motywu.",
"snapshot_missing": "Nie znaleziono migawki motywu w pliku, więc motyw może wyglądać inaczej niż pierwotnie zaplanowano.",
"fe_upgraded": "Silnik motywów PleromaFE został zaaktualizowany.",
"fe_downgraded": "Wersja PleromaFE została cofnięta.",
"migration_snapshot_ok": "Żeby być bezpiecznym, migawka motywu została załadowana. Możesz spróbować załadować dane motywu.",
"migration_napshot_gone": "Z jakiegoś powodu migawka zniknęła, niektóre rzeczy mogą wyglądać inaczej niż zapamiętałeś(-aś).",
"snapshot_source_mismatch": "Konflikt wersji: najprawdopodobniej FE zostało cofnięte do poprzedniej wersji i zaktualizowane ponownie, jeśli zmieniłeś(-aś) motyw używając starszej wersji FE, najprawdopodobniej chcesz używać starszej wersji, w przeciwnym razie użyj nowej wersji."
}
}, },
"common": { "common": {
"color": "Kolor", "color": "Kolor",
@ -280,14 +468,28 @@
"_tab_label": "Zaawansowane", "_tab_label": "Zaawansowane",
"alert": "Tło alertu", "alert": "Tło alertu",
"alert_error": "Błąd", "alert_error": "Błąd",
"alert_warning": "Ostrzeżenie",
"alert_neutral": "Neutralne",
"post": "Posty/Bio użytkowników",
"badge": "Tło odznaki", "badge": "Tło odznaki",
"popover": "Etykiety, menu, popovery",
"badge_notification": "Powiadomienie", "badge_notification": "Powiadomienie",
"panel_header": "Nagłówek panelu", "panel_header": "Nagłówek panelu",
"top_bar": "Górny pasek", "top_bar": "Górny pasek",
"borders": "Granice", "borders": "Granice",
"buttons": "Przyciski", "buttons": "Przyciski",
"inputs": "Pola wejścia", "inputs": "Pola wejścia",
"faint_text": "Zanikający tekst" "faint_text": "Zanikający tekst",
"underlay": "Podkład",
"poll": "Wykres ankiety",
"icons": "Ikony",
"highlight": "Podświetlone elementy",
"pressed": "Naciśnięte",
"selectedPost": "Wybrany post",
"selectedMenu": "Wybrany element menu",
"disabled": "Wyłączone",
"toggled": "Przełączone",
"tabs": "Karty"
}, },
"radii": { "radii": {
"_tab_label": "Zaokrąglenie" "_tab_label": "Zaokrąglenie"
@ -300,11 +502,11 @@
"blur": "Rozmycie", "blur": "Rozmycie",
"spread": "Szerokość", "spread": "Szerokość",
"inset": "Inset", "inset": "Inset",
"hint": "Możesz też używać --zmiennych jako kolorów, aby wykorzystać zmienne CSS3. Pamiętaj, że ustawienie widoczności nie będzie wtedy działać.", "hintV3": "Dla cieni możesz również użyć notacji {0} by użyć inny slot koloru.",
"filter_hint": { "filter_hint": {
"always_drop_shadow": "Ostrzeżenie, ten cień zawsze używa {0} jeżeli to obsługiwane przez przeglądarkę.", "always_drop_shadow": "Ostrzeżenie, ten cień zawsze używa {0} jeżeli to obsługiwane przez przeglądarkę.",
"drop_shadow_syntax": "{0} nie obsługuje parametru {1} i słowa kluczowego {2}.", "drop_shadow_syntax": "{0} nie obsługuje parametru {1} i słowa kluczowego {2}.",
"avatar_inset": "Pamiętaj że użycie jednocześnie cieni inset i nie inset na awatarach może daćnieoczekiwane wyniki z przezroczystymi awatarami.", "avatar_inset": "Pamiętaj że użycie jednocześnie cieni inset i nie inset na awatarach może dać nieoczekiwane wyniki z przezroczystymi awatarami.",
"spread_zero": "Cienie o ujemnej szerokości będą widoczne tak, jakby wynosiła ona zero", "spread_zero": "Cienie o ujemnej szerokości będą widoczne tak, jakby wynosiła ona zero",
"inset_classic": "Cienie inset będą używały {0}" "inset_classic": "Cienie inset będą używały {0}"
}, },
@ -347,7 +549,7 @@
"faint_link": "pomocny podręcznik", "faint_link": "pomocny podręcznik",
"fine_print": "Przeczytaj nasz {0}, aby nie nauczyć się niczego przydatnego!", "fine_print": "Przeczytaj nasz {0}, aby nie nauczyć się niczego przydatnego!",
"header_faint": "W porządku", "header_faint": "W porządku",
"checkbox": "Przeleciałem przez zasady użytkowania", "checkbox": "Przeleciałem(-am) przez zasady użytkowania",
"link": "i fajny mały odnośnik" "link": "i fajny mały odnośnik"
} }
}, },
@ -355,7 +557,44 @@
"title": "Wersja", "title": "Wersja",
"backend_version": "Wersja back-endu", "backend_version": "Wersja back-endu",
"frontend_version": "Wersja front-endu" "frontend_version": "Wersja front-endu"
} },
"notification_setting_privacy": "Prywatność",
"notification_setting_filters": "Filtry",
"notification_setting_privacy_option": "Ukryj nadawcę i zawartość powiadomień push"
},
"time": {
"day": "{0} dzień",
"days": "{0} dni",
"day_short": "{0} d",
"days_short": "{0} d",
"hour": "{0} godzina",
"hours": "{0} godzin",
"hour_short": "{0} godz.",
"hours_short": "{0} godz.",
"in_future": "za {0}",
"in_past": "{0} temu",
"minute": "{0} minuta",
"minutes": "{0} minut",
"minute_short": "{0} min",
"minutes_short": "{0} min",
"month": "{0} miesiąc",
"months": "{0} miesięcy",
"month_short": "{0} mies.",
"months_short": "{0} mies.",
"now": "teraz",
"now_short": "teraz",
"second": "{0} sekunda",
"seconds": "{0} sekund",
"second_short": "{0} s",
"seconds_short": "{0} s",
"week": "{0} tydzień",
"weeks": "{0} tygodni",
"week_short": "{0} tydz.",
"weeks_short": "{0} tyg.",
"year": "{0} rok",
"years": "{0} lata",
"year_short": "{0} r.",
"years_short": "{0} lata"
}, },
"timeline": { "timeline": {
"collapse": "Zwiń", "collapse": "Zwiń",
@ -370,8 +609,19 @@
"no_statuses": "Brak statusów" "no_statuses": "Brak statusów"
}, },
"status": { "status": {
"favorites": "Ulubione",
"repeats": "Powtórzenia",
"delete": "Usuń status",
"pin": "Przypnij na profilu",
"unpin": "Odepnij z profilu",
"pinned": "Przypnięte",
"delete_confirm": "Czy naprawdę chcesz usunąć ten status?",
"reply_to": "Odpowiedź dla", "reply_to": "Odpowiedź dla",
"replies_list": "Odpowiedzi:" "replies_list": "Odpowiedzi:",
"mute_conversation": "Wycisz konwersację",
"unmute_conversation": "Odcisz konwersację",
"status_unavailable": "Status niedostępny",
"copy_link": "Kopiuj link do statusu"
}, },
"user_card": { "user_card": {
"approve": "Przyjmij", "approve": "Przyjmij",
@ -388,25 +638,60 @@
"followers": "Obserwujący", "followers": "Obserwujący",
"following": "Obserwowany!", "following": "Obserwowany!",
"follows_you": "Obserwuje cię!", "follows_you": "Obserwuje cię!",
"hidden": "Ukryte",
"its_you": "To ty!", "its_you": "To ty!",
"media": "Media", "media": "Media",
"mention": "Wspomnienie",
"mute": "Wycisz", "mute": "Wycisz",
"muted": "Wyciszony(-a)", "muted": "Wyciszony(-a)",
"per_day": "dziennie", "per_day": "dziennie",
"remote_follow": "Zdalna obserwacja", "remote_follow": "Zdalna obserwacja",
"report": "Zgłoś",
"statuses": "Statusy", "statuses": "Statusy",
"subscribe": "Subskrybuj",
"unsubscribe": "Odsubskrybuj",
"unblock": "Odblokuj", "unblock": "Odblokuj",
"unblock_progress": "Odblokowuję…", "unblock_progress": "Odblokowuję…",
"block_progress": "Blokuję…", "block_progress": "Blokuję…",
"unmute": "Cofnij wyciszenie", "unmute": "Cofnij wyciszenie",
"unmute_progress": "Cofam wyciszenie…", "unmute_progress": "Cofam wyciszenie…",
"mute_progress": "Wyciszam…" "mute_progress": "Wyciszam…",
"hide_repeats": "Ukryj powtórzenia",
"show_repeats": "Pokaż powtórzenia",
"admin_menu": {
"moderation": "Moderacja",
"grant_admin": "Przyznaj admina",
"revoke_admin": "Odwołaj admina",
"grant_moderator": "Przyznaj moderatora",
"revoke_moderator": "Odwołaj moderatora",
"activate_account": "Aktywuj konto",
"deactivate_account": "Dezaktywuj konto",
"delete_account": "Usuń konto",
"force_nsfw": "Oznacz wszystkie posty jako NSFW",
"strip_media": "Usuń multimedia z postów",
"force_unlisted": "Wymuś posty na niepubliczne",
"sandbox": "Wymuś by posty były tylko dla obserwujących",
"disable_remote_subscription": "Zakaż obserwowania użytkownika ze zdalnych instancji",
"disable_any_subscription": "Zakaż całkowicie obserwowania użytkownika",
"quarantine": "Zakaż federowania postów od tego użytkownika",
"delete_user": "Usuń użytkownika",
"delete_user_confirmation": "Czy jesteś absolutnie pewny(-a)? Ta operacja nie może być cofnięta."
}
}, },
"user_profile": { "user_profile": {
"timeline_title": "Oś czasu użytkownika", "timeline_title": "Oś czasu użytkownika",
"profile_does_not_exist": "Przepraszamy, ten profil nie istnieje.", "profile_does_not_exist": "Przepraszamy, ten profil nie istnieje.",
"profile_loading_error": "Przepraszamy, wystąpił błąd podczas ładowania tego profilu." "profile_loading_error": "Przepraszamy, wystąpił błąd podczas ładowania tego profilu."
}, },
"user_reporting": {
"title": "Raportowanie {0}",
"add_comment_description": "Zgłoszenie zostanie wysłane do moderatorów instancji. Możesz dodać powód dlaczego zgłaszasz owe konto poniżej:",
"additional_comments": "Dodatkowe komentarze",
"forward_description": "To konto jest z innego serwera. Wysłać również tam kopię zgłoszenia?",
"forward_to": "Przekaż do {0}",
"submit": "Wyślij",
"generic_error": "Wystąpił błąd podczas przetwarzania twojej prośby."
},
"who_to_follow": { "who_to_follow": {
"more": "Więcej", "more": "Więcej",
"who_to_follow": "Propozycje obserwacji" "who_to_follow": "Propozycje obserwacji"
@ -416,9 +701,12 @@
"repeat": "Powtórz", "repeat": "Powtórz",
"reply": "Odpowiedz", "reply": "Odpowiedz",
"favorite": "Dodaj do ulubionych", "favorite": "Dodaj do ulubionych",
"user_settings": "Ustawienia użytkownika" "add_reaction": "Dodaj reakcję",
"user_settings": "Ustawienia użytkownika",
"accept_follow_request": "Akceptuj prośbę o możliwość obserwacji",
"reject_follow_request": "Odrzuć prośbę o możliwość obserwacji"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "Wysyłanie nie powiodło się.", "base": "Wysyłanie nie powiodło się.",
"file_too_big": "Zbyt duży plik [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "Zbyt duży plik [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
@ -431,5 +719,25 @@
"GiB": "GiB", "GiB": "GiB",
"TiB": "TiB" "TiB": "TiB"
} }
},
"search": {
"people": "Ludzie",
"hashtags": "Hasztagi",
"person_talking": "{count} osoba rozmawia o tym",
"people_talking": "{count} osób rozmawia o tym",
"no_results": "Brak wyników"
},
"password_reset": {
"forgot_password": "Zapomniałeś(-aś) hasła?",
"password_reset": "Reset hasła",
"instruction": "Wprowadź swój adres email lub nazwę użytkownika. Wyślemy ci link z którym możesz zresetować hasło.",
"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ć.",
"password_reset_required_but_mailer_is_disabled": "Musisz zresetować hasło, ale resetowanie hasła jest wyłączone. Proszę skontaktuj się z administratorem tej instancji."
} }
} }

View file

@ -13,7 +13,12 @@
"disable": "Оключить", "disable": "Оключить",
"enable": "Включить", "enable": "Включить",
"confirm": "Подтвердить", "confirm": "Подтвердить",
"verify": "Проверить" "verify": "Проверить",
"more": "Больше",
"generic_error": "Произошла ошибка",
"optional": "не обязательно",
"show_less": "Показать меньше",
"show_more": "Показать больше"
}, },
"login": { "login": {
"login": "Войти", "login": "Войти",
@ -26,9 +31,9 @@
"enter_recovery_code": "Ввести код восстановления", "enter_recovery_code": "Ввести код восстановления",
"enter_two_factor_code": "Ввести код аутентификации", "enter_two_factor_code": "Ввести код аутентификации",
"recovery_code": "Код восстановления", "recovery_code": "Код восстановления",
"heading" : { "heading": {
"TotpForm" : "Двухфакторная аутентификация", "TotpForm": "Двухфакторная аутентификация",
"RecoveryForm" : "Two-factor recovery" "RecoveryForm": "Two-factor recovery"
} }
}, },
"nav": { "nav": {
@ -39,7 +44,8 @@
"public_tl": "Публичная лента", "public_tl": "Публичная лента",
"timeline": "Лента", "timeline": "Лента",
"twkn": "Федеративная лента", "twkn": "Федеративная лента",
"search": "Поиск" "search": "Поиск",
"friend_requests": "Запросы на чтение"
}, },
"notifications": { "notifications": {
"broken_favorite": "Неизвестный статус, ищем...", "broken_favorite": "Неизвестный статус, ищем...",
@ -48,7 +54,8 @@
"load_older": "Загрузить старые уведомления", "load_older": "Загрузить старые уведомления",
"notifications": "Уведомления", "notifications": "Уведомления",
"read": "Прочесть", "read": "Прочесть",
"repeated_you": "повторил(а) ваш статус" "repeated_you": "повторил(а) ваш статус",
"follow_request": "хочет читать вас"
}, },
"interactions": { "interactions": {
"favs_repeats": "Повторы и фавориты", "favs_repeats": "Повторы и фавориты",
@ -56,7 +63,7 @@
"load_older": "Загрузить старые взаимодействия" "load_older": "Загрузить старые взаимодействия"
}, },
"post_status": { "post_status": {
"account_not_locked_warning": "Ваш аккаунт не {0}. Кто угодно может зафоловить вас чтобы прочитать посты только для подписчиков", "account_not_locked_warning": "Ваш аккаунт не {0}. Кто угодно может начать читать вас чтобы видеть посты только для подписчиков.",
"account_not_locked_warning_link": "залочен", "account_not_locked_warning_link": "залочен",
"attachments_sensitive": "Вложения содержат чувствительный контент", "attachments_sensitive": "Вложения содержат чувствительный контент",
"content_warning": "Тема (не обязательно)", "content_warning": "Тема (не обязательно)",
@ -94,17 +101,17 @@
"settings": { "settings": {
"enter_current_password_to_confirm": "Введите свой текущий пароль", "enter_current_password_to_confirm": "Введите свой текущий пароль",
"mfa": { "mfa": {
"otp" : "OTP", "otp": "OTP",
"setup_otp" : "Настройка OTP", "setup_otp": "Настройка OTP",
"wait_pre_setup_otp" : "предварительная настройка OTP", "wait_pre_setup_otp": "предварительная настройка OTP",
"confirm_and_enable" : "Подтвердить и включить OTP", "confirm_and_enable": "Подтвердить и включить OTP",
"title": "Двухфакторная аутентификация", "title": "Двухфакторная аутентификация",
"generate_new_recovery_codes" : "Получить новые коды востановления", "generate_new_recovery_codes": "Получить новые коды востановления",
"warning_of_generate_new_codes" : "После получения новых кодов восстановления, старые больше не будут работать.", "warning_of_generate_new_codes": "После получения новых кодов восстановления, старые больше не будут работать.",
"recovery_codes" : "Коды восстановления.", "recovery_codes": "Коды восстановления.",
"waiting_a_recovery_codes": "Получение кодов восстановления ...", "waiting_a_recovery_codes": "Получение кодов восстановления ...",
"recovery_codes_warning" : "Запишите эти коды и держите в безопасном месте - иначе вы их больше не увидите. Если вы потеряете доступ к OTP приложению - без резервных кодов вы больше не сможете залогиниться.", "recovery_codes_warning": "Запишите эти коды и держите в безопасном месте - иначе вы их больше не увидите. Если вы потеряете доступ к OTP приложению - без резервных кодов вы больше не сможете залогиниться.",
"authentication_methods" : "Методы аутентификации", "authentication_methods": "Методы аутентификации",
"scan": { "scan": {
"title": "Сканирование", "title": "Сканирование",
"desc": "Используйте приложение для двухэтапной аутентификации для сканирования этого QR-код или введите текстовый ключ:", "desc": "Используйте приложение для двухэтапной аутентификации для сканирования этого QR-код или введите текстовый ключ:",
@ -129,10 +136,10 @@
"cRed": "Отменить", "cRed": "Отменить",
"change_email": "Сменить email", "change_email": "Сменить email",
"change_email_error": "Произошла ошибка при попытке изменить email.", "change_email_error": "Произошла ошибка при попытке изменить email.",
"changed_email": "Email изменён успешно.", "changed_email": "Email изменён успешно!",
"change_password": "Сменить пароль", "change_password": "Сменить пароль",
"change_password_error": "Произошла ошибка при попытке изменить пароль.", "change_password_error": "Произошла ошибка при попытке изменить пароль.",
"changed_password": "Пароль изменён успешно.", "changed_password": "Пароль изменён успешно!",
"collapse_subject": "Сворачивать посты с темой", "collapse_subject": "Сворачивать посты с темой",
"confirm_new_password": "Подтверждение нового пароля", "confirm_new_password": "Подтверждение нового пароля",
"current_avatar": "Текущий аватар", "current_avatar": "Текущий аватар",
@ -150,7 +157,7 @@
"follow_export_button": "Экспортировать читаемых в файл .csv", "follow_export_button": "Экспортировать читаемых в файл .csv",
"follow_export_processing": "Ведётся обработка, скоро вам будет предложено загрузить файл", "follow_export_processing": "Ведётся обработка, скоро вам будет предложено загрузить файл",
"follow_import": "Импортировать читаемых", "follow_import": "Импортировать читаемых",
"follow_import_error": "Ошибка при импортировании читаемых.", "follow_import_error": "Ошибка при импортировании читаемых",
"follows_imported": "Список читаемых импортирован. Обработка займёт некоторое время..", "follows_imported": "Список читаемых импортирован. Обработка займёт некоторое время..",
"foreground": "Передний план", "foreground": "Передний план",
"general": "Общие", "general": "Общие",
@ -204,7 +211,7 @@
"replies_in_timeline": "Ответы в ленте", "replies_in_timeline": "Ответы в ленте",
"reply_link_preview": "Включить предварительный просмотр ответа при наведении мыши", "reply_link_preview": "Включить предварительный просмотр ответа при наведении мыши",
"reply_visibility_all": "Показывать все ответы", "reply_visibility_all": "Показывать все ответы",
"reply_visibility_following": "Показывать только ответы мне и тех на кого я подписан", "reply_visibility_following": "Показывать только ответы мне или тех на кого я подписан",
"reply_visibility_self": "Показывать только ответы мне", "reply_visibility_self": "Показывать только ответы мне",
"autohide_floating_post_button": "Автоматически скрывать кнопку постинга (в мобильной версии)", "autohide_floating_post_button": "Автоматически скрывать кнопку постинга (в мобильной версии)",
"saving_err": "Не удалось сохранить настройки", "saving_err": "Не удалось сохранить настройки",
@ -224,7 +231,7 @@
"text": "Текст", "text": "Текст",
"theme": "Тема", "theme": "Тема",
"theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.", "theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.",
"theme_help_v2_1": "Вы так же можете перепоределить цвета определенных компонентов нажав соотв. галочку. Используйте кнопку \"Очистить всё\" чтобы снять все переопределения", "theme_help_v2_1": "Вы так же можете перепоределить цвета определенных компонентов нажав соотв. галочку. Используйте кнопку \"Очистить всё\" чтобы снять все переопределения.",
"theme_help_v2_2": "Под некоторыми полями ввода это идикаторы контрастности, наведите на них мышью чтобы узнать больше. Приспользовании прозрачности контраст расчитывается для наихудшего варианта.", "theme_help_v2_2": "Под некоторыми полями ввода это идикаторы контрастности, наведите на них мышью чтобы узнать больше. Приспользовании прозрачности контраст расчитывается для наихудшего варианта.",
"tooltipRadius": "Всплывающие подсказки/уведомления", "tooltipRadius": "Всплывающие подсказки/уведомления",
"user_settings": "Настройки пользователя", "user_settings": "Настройки пользователя",
@ -292,9 +299,9 @@
"inset": "Внутренняя", "inset": "Внутренняя",
"hint": "Для теней вы так же можете использовать --variable в качестве цвета чтобы использовать CSS3-переменные. В таком случае прозрачность работать не будет.", "hint": "Для теней вы так же можете использовать --variable в качестве цвета чтобы использовать CSS3-переменные. В таком случае прозрачность работать не будет.",
"filter_hint": { "filter_hint": {
"always_drop_shadow": "Внимание, эта тень всегда использует {0} когда браузер поддерживает это", "always_drop_shadow": "Внимание, эта тень всегда использует {0} когда браузер поддерживает это.",
"drop_shadow_syntax": "{0} не поддерживает параметр {1} и ключевое слово {2}", "drop_shadow_syntax": "{0} не поддерживает параметр {1} и ключевое слово {2}.",
"avatar_inset": "Одновременное использование внутренних и внешних теней на (прозрачных) аватарках может дать не те результаты что вы ожидаете", "avatar_inset": "Одновременное использование внутренних и внешних теней на (прозрачных) аватарках может дать не те результаты что вы ожидаете.",
"spread_zero": "Тени с разбросом > 0 будут выглядеть как если бы разброс установлен в 0", "spread_zero": "Тени с разбросом > 0 будут выглядеть как если бы разброс установлен в 0",
"inset_classic": "Внутренние тени будут использовать {0}" "inset_classic": "Внутренние тени будут использовать {0}"
}, },
@ -340,7 +347,13 @@
"checkbox": "Я подтверждаю что не было ни единого разрыва", "checkbox": "Я подтверждаю что не было ни единого разрыва",
"link": "ссылка" "link": "ссылка"
} }
} },
"notification_setting_non_followers": "Не читающие вас",
"allow_following_move": "Разрешить автоматически читать новый аккаунт при перемещении на другой сервер",
"hide_user_stats": "Не показывать статистику пользователей (например количество читателей)",
"notification_setting_followers": "Читающие вас",
"notification_setting_follows": "Читаемые вами",
"notification_setting_non_follows": "Не читаемые вами"
}, },
"timeline": { "timeline": {
"collapse": "Свернуть", "collapse": "Свернуть",
@ -359,12 +372,12 @@
"follow": "Читать", "follow": "Читать",
"follow_sent": "Запрос отправлен!", "follow_sent": "Запрос отправлен!",
"follow_progress": "Запрашиваем…", "follow_progress": "Запрашиваем…",
"follow_again": "Запросить еще заново?", "follow_again": "Запросить еще раз?",
"follow_unfollow": "Перестать читать", "follow_unfollow": "Перестать читать",
"followees": "Читаемые", "followees": "Читаемые",
"followers": "Читатели", "followers": "Читатели",
"following": "Читаю", "following": "Читаю!",
"follows_you": "Читает вас", "follows_you": "Читает вас!",
"mute": "Игнорировать", "mute": "Игнорировать",
"muted": "Игнорирую", "muted": "Игнорирую",
"per_day": "в день", "per_day": "в день",
@ -382,9 +395,9 @@
"force_nsfw": "Отмечать посты пользователя как NSFW", "force_nsfw": "Отмечать посты пользователя как NSFW",
"strip_media": "Убирать вложения из постов пользователя", "strip_media": "Убирать вложения из постов пользователя",
"force_unlisted": "Не добавлять посты в публичные ленты", "force_unlisted": "Не добавлять посты в публичные ленты",
"sandbox": "Посты доступны только для подписчиков", "sandbox": "Принудить видимость постов только читателям",
"disable_remote_subscription": "Запретить подписываться с удаленных серверов", "disable_remote_subscription": "Запретить читать с удаленных серверов",
"disable_any_subscription": "Запретить подписываться на пользователя", "disable_any_subscription": "Запретить читать пользователя",
"quarantine": "Не федерировать посты пользователя", "quarantine": "Не федерировать посты пользователя",
"delete_user": "Удалить пользователя", "delete_user": "Удалить пользователя",
"delete_user_confirmation": "Вы уверены? Это действие нельзя отменить." "delete_user_confirmation": "Вы уверены? Это действие нельзя отменить."
@ -410,5 +423,56 @@
"not_found": "Мы не смогли найти аккаунт с таким email-ом или именем пользователя.", "not_found": "Мы не смогли найти аккаунт с таким email-ом или именем пользователя.",
"too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.", "too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.",
"password_reset_disabled": "Сброс пароля отключен. Cвяжитесь с администратором вашего сервера." "password_reset_disabled": "Сброс пароля отключен. Cвяжитесь с администратором вашего сервера."
},
"about": {
"mrf": {
"federation": "Федерация",
"simple": {
"accept_desc": "Данный сервер принимает сообщения только со следующих серверов:",
"ftl_removal_desc": "Данный сервер скрывает следующие сервера с федеративной ленты:",
"media_nsfw_desc": "Данный сервер принужденно помечает вложения со следущих серверов как NSFW:",
"simple_policies": "Правила для определенных серверов",
"accept": "Принимаемые сообщения",
"reject": "Отклоняемые сообщения",
"reject_desc": "Данный сервер не принимает сообщения со следующих серверов:",
"quarantine": "Зона карантина",
"quarantine_desc": "Данный сервер отправляет только публичные посты следующим серверам:",
"ftl_removal": "Скрытие с федеративной ленты",
"media_removal": "Удаление вложений",
"media_removal_desc": "Данный сервер удаляет вложения со следующих серверов:",
"media_nsfw": "Принужденно помеченно как NSFW"
},
"keyword": {
"ftl_removal": "Убрать из федеративной ленты",
"reject": "Отклонить",
"keyword_policies": "Действия на ключевые слова",
"replace": "Заменить",
"is_replaced_by": "→"
},
"mrf_policies": "Активные правила MRF (модуль переписывания сообщений)",
"mrf_policies_desc": "Правила MRF (модуль переписывания сообщений) влияют на федерацию данного сервера. Следующие правила активны:"
},
"staff": "Администрация"
},
"domain_mute_card": {
"mute": "Игнорировать",
"mute_progress": "В процессе...",
"unmute": "Прекратить игнорирование",
"unmute_progress": "В процессе..."
},
"exporter": {
"export": "Экспорт",
"processing": "Запрос в обработке, вам скоро будет предложено загрузить файл"
},
"features_panel": {
"chat": "Чат",
"media_proxy": "Прокси для внешних вложений",
"text_limit": "Лимит символов",
"title": "Особенности",
"gopher": "Gopher"
},
"tool_tip": {
"accept_follow_request": "Принять запрос на чтение",
"reject_follow_request": "Отклонить запрос на чтение"
} }
} }

View file

@ -25,13 +25,14 @@
"more": "更多", "more": "更多",
"generic_error": "发生一个错误", "generic_error": "发生一个错误",
"optional": "可选项", "optional": "可选项",
"show_more": "显示更多", "show_more": "展开",
"show_less": "显示更少", "show_less": "收起",
"cancel": "取消", "cancel": "取消",
"disable": "禁用", "disable": "禁用",
"enable": "启用", "enable": "启用",
"confirm": "确认", "confirm": "确认",
"verify": "验证" "verify": "验证",
"dismiss": "忽略"
}, },
"image_cropper": { "image_cropper": {
"crop_picture": "裁剪图片", "crop_picture": "裁剪图片",
@ -57,9 +58,9 @@
"enter_recovery_code": "输入一个恢复码", "enter_recovery_code": "输入一个恢复码",
"enter_two_factor_code": "输入一个双重因素验证码", "enter_two_factor_code": "输入一个双重因素验证码",
"recovery_code": "恢复码", "recovery_code": "恢复码",
"heading" : { "heading": {
"totp" : "双重因素验证", "totp": "双重因素验证",
"recovery" : "双重因素恢复" "recovery": "双重因素恢复"
} }
}, },
"media_modal": { "media_modal": {
@ -68,8 +69,8 @@
}, },
"nav": { "nav": {
"about": "关于", "about": "关于",
"back": "Back", "back": "后退",
"chat": "本地聊天", "chat": "本站聊天",
"friend_requests": "关注请求", "friend_requests": "关注请求",
"mentions": "提及", "mentions": "提及",
"interactions": "互动", "interactions": "互动",
@ -80,7 +81,8 @@
"user_search": "用户搜索", "user_search": "用户搜索",
"search": "搜索", "search": "搜索",
"who_to_follow": "推荐关注", "who_to_follow": "推荐关注",
"preferences": "偏好设置" "preferences": "偏好设置",
"administration": "管理员"
}, },
"notifications": { "notifications": {
"broken_favorite": "未知的状态,正在搜索中...", "broken_favorite": "未知的状态,正在搜索中...",
@ -90,7 +92,10 @@
"notifications": "通知", "notifications": "通知",
"read": "阅读!", "read": "阅读!",
"repeated_you": "转发了你的状态", "repeated_you": "转发了你的状态",
"no_more_notifications": "没有更多的通知" "no_more_notifications": "没有更多的通知",
"reacted_with": "和 {0} 互动过",
"migrated_to": "迁移到",
"follow_request": "想要关注你"
}, },
"polls": { "polls": {
"add_poll": "增加问卷调查", "add_poll": "增加问卷调查",
@ -112,7 +117,8 @@
"interactions": { "interactions": {
"favs_repeats": "转发和收藏", "favs_repeats": "转发和收藏",
"follows": "新的关注者", "follows": "新的关注者",
"load_older": "加载更早的互动" "load_older": "加载更早的互动",
"moves": "用户迁移"
}, },
"post_status": { "post_status": {
"new_status": "发布新状态", "new_status": "发布新状态",
@ -151,9 +157,9 @@
"token": "邀请码", "token": "邀请码",
"captcha": "CAPTCHA", "captcha": "CAPTCHA",
"new_captcha": "点击图片获取新的验证码", "new_captcha": "点击图片获取新的验证码",
"username_placeholder": "例如: lain", "username_placeholder": "例如:lain",
"fullname_placeholder": "例如: Lain Iwakura", "fullname_placeholder": "例如:岩仓玲音",
"bio_placeholder": "例如:\n你好 我是 Lain.\n我是一个住在上海的宅男。你可能在某处见过我。", "bio_placeholder": "例如:\n你好我是玲音。\n我是一个住在日本郊区的动画少女。你可能在 Wired 见过我。",
"validations": { "validations": {
"username_required": "不能留空", "username_required": "不能留空",
"fullname_required": "不能留空", "fullname_required": "不能留空",
@ -171,17 +177,17 @@
"security": "安全", "security": "安全",
"enter_current_password_to_confirm": "输入你当前密码来确认你的身份", "enter_current_password_to_confirm": "输入你当前密码来确认你的身份",
"mfa": { "mfa": {
"otp" : "OTP", "otp": "OTP",
"setup_otp" : "设置 OTP", "setup_otp": "设置 OTP",
"wait_pre_setup_otp" : "预设 OTP", "wait_pre_setup_otp": "预设 OTP",
"confirm_and_enable" : "确认并启用 OTP", "confirm_and_enable": "确认并启用 OTP",
"title": "双因素验证", "title": "双因素验证",
"generate_new_recovery_codes" : "生成新的恢复码", "generate_new_recovery_codes": "生成新的恢复码",
"warning_of_generate_new_codes" : "当你生成新的恢复码时,你的恢复码就失效了。", "warning_of_generate_new_codes": "当你生成新的恢复码时,你的恢复码就失效了。",
"recovery_codes" : "恢复码。", "recovery_codes": "恢复码。",
"waiting_a_recovery_codes": "接受备份码。。。", "waiting_a_recovery_codes": "正在接收备份码……",
"recovery_codes_warning" : "抄写这些号码,或者保存在安全的地方。这些号码不会再次显示。如果你无法访问你的 2FA app也丢失了你的恢复码你的账号就再也无法登录了。", "recovery_codes_warning": "抄写这些号码,或者保存在安全的地方。这些号码不会再次显示。如果你无法访问你的 2FA app也丢失了你的恢复码你的账号就再也无法登录了。",
"authentication_methods" : "身份验证方法", "authentication_methods": "身份验证方法",
"scan": { "scan": {
"title": "扫一下", "title": "扫一下",
"desc": "使用你的双因素验证 app扫描这个二维码或者输入这些文字密钥", "desc": "使用你的双因素验证 app扫描这个二维码或者输入这些文字密钥",
@ -222,7 +228,7 @@
"data_import_export_tab": "数据导入/导出", "data_import_export_tab": "数据导入/导出",
"default_vis": "默认可见范围", "default_vis": "默认可见范围",
"delete_account": "删除账户", "delete_account": "删除账户",
"delete_account_description": "永久删除你的帐号和所有消息。", "delete_account_description": "永久删除你的帐号和所有数据。",
"delete_account_error": "删除账户时发生错误,如果一直删除不了,请联系实例管理员。", "delete_account_error": "删除账户时发生错误,如果一直删除不了,请联系实例管理员。",
"delete_account_instructions": "在下面输入你的密码来确认删除账户", "delete_account_instructions": "在下面输入你的密码来确认删除账户",
"avatar_size_instruction": "推荐的头像图片最小的尺寸是 150x150 像素。", "avatar_size_instruction": "推荐的头像图片最小的尺寸是 150x150 像素。",
@ -263,7 +269,7 @@
"loop_video_silent_only": "只循环没有声音的视频例如Mastodon 里的“GIF”", "loop_video_silent_only": "只循环没有声音的视频例如Mastodon 里的“GIF”",
"mutes_tab": "隐藏", "mutes_tab": "隐藏",
"play_videos_in_modal": "在弹出框内播放视频", "play_videos_in_modal": "在弹出框内播放视频",
"use_contain_fit": "生成缩略图时不要裁剪附件", "use_contain_fit": "生成缩略图时不要裁剪附件",
"name": "名字", "name": "名字",
"name_bio": "名字及简介", "name_bio": "名字及简介",
"new_password": "新密码", "new_password": "新密码",
@ -348,7 +354,14 @@
"save_load_hint": "\"保留\" 选项在选择或加载主题时保留当前设置的选项,在导出主题时还会存储上述选项。当所有复选框未设置时,导出主题将保存所有内容。", "save_load_hint": "\"保留\" 选项在选择或加载主题时保留当前设置的选项,在导出主题时还会存储上述选项。当所有复选框未设置时,导出主题将保存所有内容。",
"reset": "重置", "reset": "重置",
"clear_all": "清除全部", "clear_all": "清除全部",
"clear_opacity": "清除透明度" "clear_opacity": "清除透明度",
"load_theme": "加载主题",
"help": {
"upgraded_from_v2": "PleromaFE 已升级,主题会和你记忆中的不太一样。"
},
"use_source": "新版本",
"use_snapshot": "老版本",
"keep_as_is": "保持原状"
}, },
"common": { "common": {
"color": "颜色", "color": "颜色",
@ -441,7 +454,7 @@
"mono": "内容", "mono": "内容",
"input": "刚刚抵达上海", "input": "刚刚抵达上海",
"faint_link": "帮助菜单", "faint_link": "帮助菜单",
"fine_print": "阅读我们的 {0} 学不到什么东东", "fine_print": "阅读我们的 {0} ,然而什么也学不到!",
"header_faint": "这很正常", "header_faint": "这很正常",
"checkbox": "我已经浏览了 TOC", "checkbox": "我已经浏览了 TOC",
"link": "一个很棒的摇滚链接" "link": "一个很棒的摇滚链接"
@ -451,7 +464,20 @@
"title": "版本", "title": "版本",
"backend_version": "后端版本", "backend_version": "后端版本",
"frontend_version": "前端版本" "frontend_version": "前端版本"
} },
"notification_setting_filters": "过滤器",
"domain_mutes": "域名",
"changed_email": "邮箱修改成功!",
"change_email_error": "修改你的电子邮箱时发生错误",
"change_email": "修改电子邮箱",
"allow_following_move": "正在关注的账号迁移时自动重新关注",
"notification_setting_privacy_option": "在通知推送中隐藏发送者和内容",
"notification_setting_privacy": "隐私",
"hide_follows_count_description": "不显示关注数",
"notification_visibility_emoji_reactions": "互动",
"notification_visibility_moves": "用户迁移",
"new_email": "新邮箱",
"emoji_reactions_on_timeline": "在时间线上显示表情符号互动"
}, },
"time": { "time": {
"day": "{0} 天", "day": "{0} 天",
@ -561,7 +587,10 @@
"quarantine": "从联合实例中禁止用户帖子", "quarantine": "从联合实例中禁止用户帖子",
"delete_user": "删除用户", "delete_user": "删除用户",
"delete_user_confirmation": "你确认吗?此操作无法撤销。" "delete_user_confirmation": "你确认吗?此操作无法撤销。"
} },
"hidden": "已隐藏",
"show_repeats": "显示转发",
"hide_repeats": "隐藏转发"
}, },
"user_profile": { "user_profile": {
"timeline_title": "用户时间线", "timeline_title": "用户时间线",
@ -586,9 +615,11 @@
"repeat": "转发", "repeat": "转发",
"reply": "回复", "reply": "回复",
"favorite": "收藏", "favorite": "收藏",
"user_settings": "用户设置" "user_settings": "用户设置",
"reject_follow_request": "拒绝关注请求",
"add_reaction": "添加互动"
}, },
"upload":{ "upload": {
"error": { "error": {
"base": "上传不成功。", "base": "上传不成功。",
"file_too_big": "文件太大了 [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", "file_too_big": "文件太大了 [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
@ -605,8 +636,8 @@
"search": { "search": {
"people": "人", "people": "人",
"hashtags": "Hashtags", "hashtags": "Hashtags",
"person_talking": "{count} 人论", "person_talking": "{count} 人正在讨论",
"people_talking": "{count} 人论", "people_talking": "{count} 人正在讨论",
"no_results": "没有搜索结果" "no_results": "没有搜索结果"
}, },
"password_reset": { "password_reset": {
@ -619,5 +650,49 @@
"not_found": "我们无法找到匹配的邮箱地址或者用户名。", "not_found": "我们无法找到匹配的邮箱地址或者用户名。",
"too_many_requests": "你触发了尝试的限制,请稍后再试。", "too_many_requests": "你触发了尝试的限制,请稍后再试。",
"password_reset_disabled": "密码重置已经被禁用。请联系你的实例管理员。" "password_reset_disabled": "密码重置已经被禁用。请联系你的实例管理员。"
},
"remote_user_resolver": {
"error": "未找到。",
"searching_for": "搜索",
"remote_user_resolver": "远程用户解析器"
},
"emoji": {
"keep_open": "选择器保持打开",
"stickers": "贴图",
"unicode": "Unicode 表情符号",
"custom": "自定义表情符号",
"add_emoji": "插入表情符号",
"search_emoji": "搜索表情符号",
"emoji": "表情符号"
},
"about": {
"mrf": {
"simple": {
"quarantine_desc": "本实例只会把公开状态发送非下列实例:",
"quarantine": "隔离",
"reject_desc": "本实例不会接收来自下列实例的消息:",
"reject": "拒绝",
"accept_desc": "本实例只接收来自下列实例的消息:",
"simple_policies": "站规",
"accept": "接受",
"media_removal": "移除媒体"
},
"mrf_policies_desc": "MRF 策略会影响本实例的互通行为。以下策略已启用:",
"mrf_policies": "已启动 MRF 策略",
"keyword": {
"ftl_removal": "从“全部已知网络”时间线上移除",
"keyword_policies": "关键词策略",
"is_replaced_by": "→",
"replace": "替换",
"reject": "拒绝"
},
"federation": "联邦"
}
},
"domain_mute_card": {
"unmute_progress": "正在取消隐藏……",
"unmute": "取消隐藏",
"mute_progress": "隐藏中……",
"mute": "隐藏"
} }
} }

View file

@ -31,7 +31,6 @@ import VueChatScroll from 'vue-chat-scroll'
import VueClickOutside from 'v-click-outside' import VueClickOutside from 'v-click-outside'
import PortalVue from 'portal-vue' import PortalVue from 'portal-vue'
import VBodyScrollLock from './directives/body_scroll_lock' import VBodyScrollLock from './directives/body_scroll_lock'
import VTooltip from 'v-tooltip'
import afterStoreSetup from './boot/after_store.js' import afterStoreSetup from './boot/after_store.js'
@ -44,13 +43,6 @@ Vue.use(VueChatScroll)
Vue.use(VueClickOutside) Vue.use(VueClickOutside)
Vue.use(PortalVue) Vue.use(PortalVue)
Vue.use(VBodyScrollLock) Vue.use(VBodyScrollLock)
Vue.use(VTooltip, {
popover: {
defaultTrigger: 'hover click',
defaultContainer: false,
defaultOffset: 5
}
})
const i18n = new VueI18n({ const i18n = new VueI18n({
// By default, use the browser locale, we will update it if neccessary // By default, use the browser locale, we will update it if neccessary

View file

@ -34,7 +34,8 @@ export const defaultState = {
likes: true, likes: true,
repeats: true, repeats: true,
moves: true, moves: true,
emojiReactions: false emojiReactions: false,
followRequest: true
}, },
webPushNotifications: false, webPushNotifications: false,
muteWords: [], muteWords: [],
@ -102,6 +103,7 @@ const config = {
setPreset(value) setPreset(value)
break break
case 'customTheme': case 'customTheme':
case 'customThemeSource':
applyTheme(value) applyTheme(value)
} }
} }

View file

@ -1,54 +1,58 @@
import { set } from 'vue' import { set } from 'vue'
import { getPreset, applyTheme } from '../services/style_setter/style_setter.js' import { getPreset, applyTheme } from '../services/style_setter/style_setter.js'
import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js'
import { instanceDefaultProperties } from './config.js' import { instanceDefaultProperties } from './config.js'
const defaultState = { const defaultState = {
// Stuff from static/config.json and apiConfig // Stuff from apiConfig
name: 'Pleroma FE', name: 'Pleroma FE',
registrationOpen: true, registrationOpen: true,
safeDM: true,
textlimit: 5000,
server: 'http://localhost:4040/', server: 'http://localhost:4040/',
theme: 'pleroma-dark', textlimit: 5000,
themeData: undefined, themeData: undefined,
background: '/static/aurora_borealis.jpg',
logo: '/static/logo.png',
logoMask: true,
logoMargin: '.2em',
redirectRootNoLogin: '/main/all',
redirectRootLogin: '/main/friends',
showInstanceSpecificPanel: false,
alwaysShowSubjectInput: true,
hideMutedPosts: false,
collapseMessageWithSubject: false,
hidePostStats: false,
hideUserStats: false,
hideFilteredStatuses: false,
disableChat: false,
scopeCopy: true,
subjectLineBehavior: 'email',
postContentType: 'text/plain',
hideSitename: false,
nsfwCensorImage: undefined,
vapidPublicKey: undefined, vapidPublicKey: undefined,
noAttachmentLinks: false,
showFeaturesPanel: true, // Stuff from static/config.json
minimalScopesMode: false, alwaysShowSubjectInput: true,
background: '/static/aurora_borealis.jpg',
collapseMessageWithSubject: false,
disableChat: false,
greentext: false, greentext: false,
hideFilteredStatuses: false,
hideMutedPosts: false,
hidePostStats: false,
hideSitename: false,
hideUserStats: false,
loginMethod: 'password',
logo: '/static/logo.png',
logoMargin: '.2em',
logoMask: true,
minimalScopesMode: false,
nsfwCensorImage: undefined,
postContentType: 'text/plain',
redirectRootLogin: '/main/friends',
redirectRootNoLogin: '/main/all',
scopeCopy: true,
showFeaturesPanel: true,
showInstanceSpecificPanel: false,
sidebarRight: false,
subjectLineBehavior: 'email',
theme: 'pleroma-dark',
// Nasty stuff // Nasty stuff
pleromaBackend: true,
emoji: [],
emojiFetched: false,
customEmoji: [], customEmoji: [],
customEmojiFetched: false, customEmojiFetched: false,
restrictedNicknames: [], emoji: [],
emojiFetched: false,
pleromaBackend: true,
postFormats: [], postFormats: [],
restrictedNicknames: [],
safeDM: true,
// Feature-set, apparently, not everything here is reported... // Feature-set, apparently, not everything here is reported...
mediaProxyAvailable: false,
chatAvailable: false, chatAvailable: false,
gopherAvailable: false, gopherAvailable: false,
mediaProxyAvailable: false,
suggestionsEnabled: false, suggestionsEnabled: false,
suggestionsWeb: '', suggestionsWeb: '',
@ -159,7 +163,14 @@ const instance = {
// No need to apply theme if there's user theme already // No need to apply theme if there's user theme already
const { customTheme } = rootState.config const { customTheme } = rootState.config
if (customTheme) return if (customTheme) return
// New theme presets don't have 'theme' property, they use 'source'
const themeSource = themeData.source
if (!themeData.theme || (themeSource && themeSource.themeEngineVersion === CURRENT_VERSION)) {
applyTheme(themeSource)
} else {
applyTheme(themeData.theme) applyTheme(themeData.theme)
}
}) })
}, },
fetchEmoji ({ dispatch, state }) { fetchEmoji ({ dispatch, state }) {

View file

@ -13,6 +13,7 @@ import {
omitBy omitBy
} from 'lodash' } from 'lodash'
import { set } from 'vue' import { set } from 'vue'
import { isStatusNotification } from '../services/notification_utils/notification_utils.js'
import apiService from '../services/api/api.service.js' import apiService from '../services/api/api.service.js'
// import parse from '../services/status_parser/status_parser.js' // import parse from '../services/status_parser/status_parser.js'
@ -321,7 +322,7 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us
const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters }) => { const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters }) => {
each(notifications, (notification) => { each(notifications, (notification) => {
if (notification.type !== 'follow' && notification.type !== 'move') { if (isStatusNotification(notification.type)) {
notification.action = addStatusToGlobalStorage(state, notification.action).item notification.action = addStatusToGlobalStorage(state, notification.action).item
notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item
} }
@ -361,13 +362,16 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot
case 'pleroma:move': case 'pleroma:move':
i18nString = 'migrated_to' i18nString = 'migrated_to'
break break
case 'follow_request':
i18nString = 'follow_request'
break
} }
if (notification.type === 'pleroma:emoji_reaction') { if (notification.type === 'pleroma:emoji_reaction') {
notifObj.body = rootGetters.i18n.t('notifications.reacted_with', [notification.emoji]) notifObj.body = rootGetters.i18n.t('notifications.reacted_with', [notification.emoji])
} else if (i18nString) { } else if (i18nString) {
notifObj.body = rootGetters.i18n.t('notifications.' + i18nString) notifObj.body = rootGetters.i18n.t('notifications.' + i18nString)
} else { } else if (isStatusNotification(notification.type)) {
notifObj.body = notification.status.text notifObj.body = notification.status.text
} }
@ -521,6 +525,17 @@ export const mutations = {
notification.seen = true notification.seen = true
}) })
}, },
markSingleNotificationAsSeen (state, { id }) {
const notification = find(state.notifications.data, n => n.id === id)
if (notification) notification.seen = true
},
dismissNotification (state, { id }) {
state.notifications.data = state.notifications.data.filter(n => n.id !== id)
},
updateNotification (state, { id, updater }) {
const notification = find(state.notifications.data, n => n.id === id)
notification && updater(notification)
},
queueFlush (state, { timeline, id }) { queueFlush (state, { timeline, id }) {
state.timelines[timeline].flushMarker = id state.timelines[timeline].flushMarker = id
}, },
@ -616,7 +631,7 @@ const statuses = {
commit('setNotificationsSilence', { value }) commit('setNotificationsSilence', { value })
}, },
fetchStatus ({ rootState, dispatch }, id) { fetchStatus ({ rootState, dispatch }, id) {
rootState.api.backendInteractor.fetchStatus({ id }) return rootState.api.backendInteractor.fetchStatus({ id })
.then((status) => dispatch('addNewStatuses', { statuses: [status] })) .then((status) => dispatch('addNewStatuses', { statuses: [status] }))
}, },
deleteStatus ({ rootState, commit }, status) { deleteStatus ({ rootState, commit }, status) {
@ -680,6 +695,24 @@ const statuses = {
credentials: rootState.users.currentUser.credentials credentials: rootState.users.currentUser.credentials
}) })
}, },
markSingleNotificationAsSeen ({ rootState, commit }, { id }) {
commit('markSingleNotificationAsSeen', { id })
apiService.markNotificationsAsSeen({
single: true,
id,
credentials: rootState.users.currentUser.credentials
})
},
dismissNotificationLocal ({ rootState, commit }, { id }) {
commit('dismissNotification', { id })
},
dismissNotification ({ rootState, commit }, { id }) {
commit('dismissNotification', { id })
rootState.api.backendInteractor.dismissNotification({ id })
},
updateNotification ({ rootState, commit }, { id, updater }) {
commit('updateNotification', { id, updater })
},
fetchFavsAndRepeats ({ rootState, commit }, id) { fetchFavsAndRepeats ({ rootState, commit }, id) {
Promise.all([ Promise.all([
rootState.api.backendInteractor.fetchFavoritedByUsers({ id }), rootState.api.backendInteractor.fetchFavoritedByUsers({ id }),

View file

@ -48,6 +48,11 @@ const unblockUser = (store, id) => {
} }
const muteUser = (store, id) => { const muteUser = (store, id) => {
const predictedRelationship = store.state.relationships[id] || { id }
predictedRelationship.muting = true
store.commit('updateUserRelationship', [predictedRelationship])
store.commit('addMuteId', id)
return store.rootState.api.backendInteractor.muteUser({ id }) return store.rootState.api.backendInteractor.muteUser({ id })
.then((relationship) => { .then((relationship) => {
store.commit('updateUserRelationship', [relationship]) store.commit('updateUserRelationship', [relationship])
@ -56,6 +61,10 @@ const muteUser = (store, id) => {
} }
const unmuteUser = (store, id) => { const unmuteUser = (store, id) => {
const predictedRelationship = store.state.relationships[id] || { id }
predictedRelationship.muting = false
store.commit('updateUserRelationship', [predictedRelationship])
return store.rootState.api.backendInteractor.unmuteUser({ id }) return store.rootState.api.backendInteractor.unmuteUser({ id })
.then((relationship) => store.commit('updateUserRelationship', [relationship])) .then((relationship) => store.commit('updateUserRelationship', [relationship]))
} }
@ -83,10 +92,6 @@ const unmuteDomain = (store, domain) => {
} }
export const mutations = { export const mutations = {
setMuted (state, { user: { id }, muted }) {
const user = state.usersObject[id]
set(user, 'muted', muted)
},
tagUser (state, { user: { id }, tag }) { tagUser (state, { user: { id }, tag }) {
const user = state.usersObject[id] const user = state.usersObject[id]
const tags = user.tags || [] const tags = user.tags || []
@ -146,26 +151,18 @@ export const mutations = {
} }
}, },
addNewUsers (state, users) { addNewUsers (state, users) {
each(users, (user) => mergeOrAdd(state.users, state.usersObject, user)) each(users, (user) => {
if (user.relationship) {
set(state.relationships, user.relationship.id, user.relationship)
}
mergeOrAdd(state.users, state.usersObject, user)
})
}, },
updateUserRelationship (state, relationships) { updateUserRelationship (state, relationships) {
relationships.forEach((relationship) => { relationships.forEach((relationship) => {
const user = state.usersObject[relationship.id] set(state.relationships, relationship.id, relationship)
if (user) {
user.follows_you = relationship.followed_by
user.following = relationship.following
user.muted = relationship.muting
user.statusnet_blocking = relationship.blocking
user.subscribed = relationship.subscribing
user.showing_reblogs = relationship.showing_reblogs
}
}) })
}, },
updateBlocks (state, blockedUsers) {
// Reset statusnet_blocking of all fetched users
each(state.users, (user) => { user.statusnet_blocking = false })
each(blockedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user))
},
saveBlockIds (state, blockIds) { saveBlockIds (state, blockIds) {
state.currentUser.blockIds = blockIds state.currentUser.blockIds = blockIds
}, },
@ -174,11 +171,6 @@ export const mutations = {
state.currentUser.blockIds.push(blockId) state.currentUser.blockIds.push(blockId)
} }
}, },
updateMutes (state, mutedUsers) {
// Reset muted of all fetched users
each(state.users, (user) => { user.muted = false })
each(mutedUsers, (user) => mergeOrAdd(state.users, state.usersObject, user))
},
saveMuteIds (state, muteIds) { saveMuteIds (state, muteIds) {
state.currentUser.muteIds = muteIds state.currentUser.muteIds = muteIds
}, },
@ -244,6 +236,10 @@ export const getters = {
return state.usersObject[query.toLowerCase()] return state.usersObject[query.toLowerCase()]
} }
return result return result
},
relationship: state => id => {
const rel = id && state.relationships[id]
return rel || { id, loading: true }
} }
} }
@ -254,7 +250,8 @@ export const defaultState = {
users: [], users: [],
usersObject: {}, usersObject: {},
signUpPending: false, signUpPending: false,
signUpErrors: [] signUpErrors: [],
relationships: {}
} }
const users = { const users = {
@ -279,7 +276,7 @@ const users = {
return store.rootState.api.backendInteractor.fetchBlocks() return store.rootState.api.backendInteractor.fetchBlocks()
.then((blocks) => { .then((blocks) => {
store.commit('saveBlockIds', map(blocks, 'id')) store.commit('saveBlockIds', map(blocks, 'id'))
store.commit('updateBlocks', blocks) store.commit('addNewUsers', blocks)
return blocks return blocks
}) })
}, },
@ -298,8 +295,8 @@ const users = {
fetchMutes (store) { fetchMutes (store) {
return store.rootState.api.backendInteractor.fetchMutes() return store.rootState.api.backendInteractor.fetchMutes()
.then((mutes) => { .then((mutes) => {
store.commit('updateMutes', mutes)
store.commit('saveMuteIds', map(mutes, 'id')) store.commit('saveMuteIds', map(mutes, 'id'))
store.commit('addNewUsers', mutes)
return mutes return mutes
}) })
}, },
@ -416,7 +413,7 @@ const users = {
}, },
addNewNotifications (store, { notifications }) { addNewNotifications (store, { notifications }) {
const users = map(notifications, 'from_profile') const users = map(notifications, 'from_profile')
const targetUsers = map(notifications, 'target') const targetUsers = map(notifications, 'target').filter(_ => _)
const notificationIds = notifications.map(_ => _.id) const notificationIds = notifications.map(_ => _.id)
store.commit('addNewUsers', users) store.commit('addNewUsers', users)
store.commit('addNewUsers', targetUsers) store.commit('addNewUsers', targetUsers)
@ -431,7 +428,7 @@ const users = {
store.commit('setUserForNotification', notification) store.commit('setUserForNotification', notification)
}) })
}, },
searchUsers (store, query) { searchUsers (store, { query }) {
return store.rootState.api.backendInteractor.searchUsers({ query }) return store.rootState.api.backendInteractor.searchUsers({ query })
.then((users) => { .then((users) => {
store.commit('addNewUsers', users) store.commit('addNewUsers', users)

View file

@ -4,7 +4,6 @@ import 'whatwg-fetch'
import { RegistrationError, StatusCodeError } from '../errors/errors' import { RegistrationError, StatusCodeError } from '../errors/errors'
/* eslint-env browser */ /* eslint-env browser */
const QVITTER_USER_NOTIFICATIONS_READ_URL = '/api/qvitter/statuses/notifications/read.json'
const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import' const BLOCKS_IMPORT_URL = '/api/pleroma/blocks_import'
const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import' const FOLLOW_IMPORT_URL = '/api/pleroma/follow_import'
const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account' const DELETE_ACCOUNT_URL = '/api/pleroma/delete_account'
@ -17,6 +16,7 @@ const DEACTIVATE_USER_URL = '/api/pleroma/admin/users/deactivate'
const ADMIN_USERS_URL = '/api/pleroma/admin/users' const ADMIN_USERS_URL = '/api/pleroma/admin/users'
const SUGGESTIONS_URL = '/api/v1/suggestions' const SUGGESTIONS_URL = '/api/v1/suggestions'
const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings' const NOTIFICATION_SETTINGS_URL = '/api/pleroma/notification_settings'
const NOTIFICATION_READ_URL = '/api/v1/pleroma/notifications/read'
const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa' const MFA_SETTINGS_URL = '/api/pleroma/accounts/mfa'
const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes' const MFA_BACKUP_CODES_URL = '/api/pleroma/accounts/mfa/backup_codes'
@ -29,6 +29,7 @@ const MASTODON_LOGIN_URL = '/api/v1/accounts/verify_credentials'
const MASTODON_REGISTRATION_URL = '/api/v1/accounts' const MASTODON_REGISTRATION_URL = '/api/v1/accounts'
const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites' const MASTODON_USER_FAVORITES_TIMELINE_URL = '/api/v1/favourites'
const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications' const MASTODON_USER_NOTIFICATIONS_URL = '/api/v1/notifications'
const MASTODON_DISMISS_NOTIFICATION_URL = id => `/api/v1/notifications/${id}/dismiss`
const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite` const MASTODON_FAVORITE_URL = id => `/api/v1/statuses/${id}/favourite`
const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite` const MASTODON_UNFAVORITE_URL = id => `/api/v1/statuses/${id}/unfavourite`
const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog` const MASTODON_RETWEET_URL = id => `/api/v1/statuses/${id}/reblog`
@ -323,7 +324,8 @@ const fetchFriends = ({ id, maxId, sinceId, limit = 20, credentials }) => {
const args = [ const args = [
maxId && `max_id=${maxId}`, maxId && `max_id=${maxId}`,
sinceId && `since_id=${sinceId}`, sinceId && `since_id=${sinceId}`,
limit && `limit=${limit}` limit && `limit=${limit}`,
`with_relationships=true`
].filter(_ => _).join('&') ].filter(_ => _).join('&')
url = url + (args ? '?' + args : '') url = url + (args ? '?' + args : '')
@ -357,7 +359,8 @@ const fetchFollowers = ({ id, maxId, sinceId, limit = 20, credentials }) => {
const args = [ const args = [
maxId && `max_id=${maxId}`, maxId && `max_id=${maxId}`,
sinceId && `since_id=${sinceId}`, sinceId && `since_id=${sinceId}`,
limit && `limit=${limit}` limit && `limit=${limit}`,
`with_relationships=true`
].filter(_ => _).join('&') ].filter(_ => _).join('&')
url += args ? '?' + args : '' url += args ? '?' + args : ''
@ -495,8 +498,7 @@ const fetchTimeline = ({
until = false, until = false,
userId = false, userId = false,
tag = false, tag = false,
withMuted = false, withMuted = false
withMove = false
}) => { }) => {
const timelineUrls = { const timelineUrls = {
public: MASTODON_PUBLIC_TIMELINE, public: MASTODON_PUBLIC_TIMELINE,
@ -536,12 +538,11 @@ const fetchTimeline = ({
if (timeline === 'public' || timeline === 'publicAndExternal') { if (timeline === 'public' || timeline === 'publicAndExternal') {
params.push(['only_media', false]) params.push(['only_media', false])
} }
if (timeline === 'notifications') { if (timeline !== 'favorites') {
params.push(['with_move', withMove]) params.push(['with_muted', withMuted])
} }
params.push(['count', 20]) params.push(['limit', 20])
params.push(['with_muted', withMuted])
const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') const queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
url += `?${queryString}` url += `?${queryString}`
@ -844,12 +845,16 @@ const suggestions = ({ credentials }) => {
}).then((data) => data.json()) }).then((data) => data.json())
} }
const markNotificationsAsSeen = ({ id, credentials }) => { const markNotificationsAsSeen = ({ id, credentials, single = false }) => {
const body = new FormData() const body = new FormData()
body.append('latest_id', id) if (single) {
body.append('id', id)
} else {
body.append('max_id', id)
}
return fetch(QVITTER_USER_NOTIFICATIONS_READ_URL, { return fetch(NOTIFICATION_READ_URL, {
body, body,
headers: authHeaders(credentials), headers: authHeaders(credentials),
method: 'POST' method: 'POST'
@ -880,12 +885,20 @@ const fetchPoll = ({ pollId, credentials }) => {
) )
} }
const fetchFavoritedByUsers = ({ id }) => { const fetchFavoritedByUsers = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_STATUS_FAVORITEDBY_URL(id) }).then((users) => users.map(parseUser)) return promisedRequest({
url: MASTODON_STATUS_FAVORITEDBY_URL(id),
method: 'GET',
credentials
}).then((users) => users.map(parseUser))
} }
const fetchRebloggedByUsers = ({ id }) => { const fetchRebloggedByUsers = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_STATUS_REBLOGGEDBY_URL(id) }).then((users) => users.map(parseUser)) return promisedRequest({
url: MASTODON_STATUS_REBLOGGEDBY_URL(id),
method: 'GET',
credentials
}).then((users) => users.map(parseUser))
} }
const fetchEmojiReactions = ({ id, credentials }) => { const fetchEmojiReactions = ({ id, credentials }) => {
@ -962,6 +975,8 @@ const search2 = ({ credentials, q, resolve, limit, offset, following }) => {
params.push(['following', true]) params.push(['following', true])
} }
params.push(['with_relationships', true])
let queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&') let queryString = map(params, (param) => `${param[0]}=${param[1]}`).join('&')
url += `?${queryString}` url += `?${queryString}`
@ -1002,6 +1017,15 @@ const unmuteDomain = ({ domain, credentials }) => {
}) })
} }
const dismissNotification = ({ credentials, id }) => {
return promisedRequest({
url: MASTODON_DISMISS_NOTIFICATION_URL(id),
method: 'POST',
payload: { id },
credentials
})
}
export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => { export const getMastodonSocketURI = ({ credentials, stream, args = {} }) => {
return Object.entries({ return Object.entries({
...(credentials ...(credentials
@ -1157,6 +1181,7 @@ const apiService = {
denyUser, denyUser,
suggestions, suggestions,
markNotificationsAsSeen, markNotificationsAsSeen,
dismissNotification,
vote, vote,
fetchPoll, fetchPoll,
fetchFavoritedByUsers, fetchFavoritedByUsers,

View file

@ -1,4 +1,5 @@
import escape from 'escape-html' import escape from 'escape-html'
import { isStatusNotification } from '../notification_utils/notification_utils.js'
const qvitterStatusType = (status) => { const qvitterStatusType = (status) => {
if (status.is_post_verb) { if (status.is_post_verb) {
@ -74,13 +75,7 @@ export const parseUser = (data) => {
output.token = data.pleroma.chat_token output.token = data.pleroma.chat_token
if (relationship) { if (relationship) {
output.follows_you = relationship.followed_by output.relationship = relationship
output.requested = relationship.requested
output.following = relationship.following
output.statusnet_blocking = relationship.blocking
output.muted = relationship.muting
output.showing_reblogs = relationship.showing_reblogs
output.subscribed = relationship.subscribing
} }
output.allow_following_move = data.pleroma.allow_following_move output.allow_following_move = data.pleroma.allow_following_move
@ -137,16 +132,10 @@ export const parseUser = (data) => {
output.statusnet_profile_url = data.statusnet_profile_url output.statusnet_profile_url = data.statusnet_profile_url
output.statusnet_blocking = data.statusnet_blocking
output.is_local = data.is_local output.is_local = data.is_local
output.role = data.role output.role = data.role
output.show_role = data.show_role output.show_role = data.show_role
output.follows_you = data.follows_you
output.muted = data.muted
if (data.rights) { if (data.rights) {
output.rights = { output.rights = {
moderator: data.rights.delete_others_notice, moderator: data.rights.delete_others_notice,
@ -160,10 +149,16 @@ export const parseUser = (data) => {
output.hide_follows_count = data.hide_follows_count output.hide_follows_count = data.hide_follows_count
output.hide_followers_count = data.hide_followers_count output.hide_followers_count = data.hide_followers_count
output.background_image = data.background_image output.background_image = data.background_image
// on mastoapi this info is contained in a "relationship"
output.following = data.following
// Websocket token // Websocket token
output.token = data.token output.token = data.token
// Convert relationsip data to expected format
output.relationship = {
muting: data.muted,
blocking: data.statusnet_blocking,
followed_by: data.follows_you,
following: data.following
}
} }
output.created_at = new Date(data.created_at) output.created_at = new Date(data.created_at)
@ -215,7 +210,7 @@ export const addEmojis = (string, emojis) => {
const regexSafeShortCode = emoji.shortcode.replace(matchOperatorsRegex, '\\$&') const regexSafeShortCode = emoji.shortcode.replace(matchOperatorsRegex, '\\$&')
return acc.replace( return acc.replace(
new RegExp(`:${regexSafeShortCode}:`, 'g'), new RegExp(`:${regexSafeShortCode}:`, 'g'),
`<img src='${emoji.url}' alt='${emoji.shortcode}' title='${emoji.shortcode}' class='emoji' />` `<img src='${emoji.url}' alt=':${emoji.shortcode}:' title=':${emoji.shortcode}:' class='emoji' />`
) )
}, string) }, string)
} }
@ -346,9 +341,7 @@ export const parseNotification = (data) => {
if (masto) { if (masto) {
output.type = mastoDict[data.type] || data.type output.type = mastoDict[data.type] || data.type
output.seen = data.pleroma.is_seen output.seen = data.pleroma.is_seen
output.status = output.type === 'follow' || output.type === 'pleroma:move' output.status = isStatusNotification(output.type) ? parseStatus(data.status) : null
? null
: parseStatus(data.status)
output.action = output.status // TODO: Refactor, this is unneeded output.action = output.status // TODO: Refactor, this is unneeded
output.target = output.type !== 'pleroma:move' output.target = output.type !== 'pleroma:move'
? null ? null

View file

@ -1,24 +1,27 @@
const fetchUser = (attempt, user, store) => new Promise((resolve, reject) => { const fetchRelationship = (attempt, userId, store) => new Promise((resolve, reject) => {
setTimeout(() => { setTimeout(() => {
store.state.api.backendInteractor.fetchUser({ id: user.id }) store.state.api.backendInteractor.fetchUserRelationship({ id: userId })
.then((user) => store.commit('addNewUsers', [user])) .then((relationship) => {
.then(() => resolve([user.following, user.requested, user.locked, attempt])) store.commit('updateUserRelationship', [relationship])
return relationship
})
.then((relationship) => resolve([relationship.following, relationship.requested, relationship.locked, attempt]))
.catch((e) => reject(e)) .catch((e) => reject(e))
}, 500) }, 500)
}).then(([following, sent, locked, attempt]) => { }).then(([following, sent, locked, attempt]) => {
if (!following && !(locked && sent) && attempt <= 3) { if (!following && !(locked && sent) && attempt <= 3) {
// If we BE reports that we still not following that user - retry, // If we BE reports that we still not following that user - retry,
// increment attempts by one // increment attempts by one
fetchUser(++attempt, user, store) fetchRelationship(++attempt, userId, store)
} }
}) })
export const requestFollow = (user, store) => new Promise((resolve, reject) => { export const requestFollow = (userId, store) => new Promise((resolve, reject) => {
store.state.api.backendInteractor.followUser({ id: user.id }) store.state.api.backendInteractor.followUser({ id: userId })
.then((updated) => { .then((updated) => {
store.commit('updateUserRelationship', [updated]) store.commit('updateUserRelationship', [updated])
if (updated.following || (user.locked && user.requested)) { if (updated.following || (updated.locked && updated.requested)) {
// If we get result immediately or the account is locked, just stop. // If we get result immediately or the account is locked, just stop.
resolve() resolve()
return return
@ -31,15 +34,15 @@ export const requestFollow = (user, store) => new Promise((resolve, reject) => {
// don't know that yet. // don't know that yet.
// Recursive Promise, it will call itself up to 3 times. // Recursive Promise, it will call itself up to 3 times.
return fetchUser(1, user, store) return fetchRelationship(1, updated, store)
.then(() => { .then(() => {
resolve() resolve()
}) })
}) })
}) })
export const requestUnfollow = (user, store) => new Promise((resolve, reject) => { export const requestUnfollow = (userId, store) => new Promise((resolve, reject) => {
store.state.api.backendInteractor.unfollowUser({ id: user.id }) store.state.api.backendInteractor.unfollowUser({ id: userId })
.then((updated) => { .then((updated) => {
store.commit('updateUserRelationship', [updated]) store.commit('updateUserRelationship', [updated])
resolve({ resolve({

View file

@ -1,4 +1,4 @@
import { filter, sortBy } from 'lodash' import { filter, sortBy, includes } from 'lodash'
export const notificationsFromStore = store => store.state.statuses.notifications.data export const notificationsFromStore = store => store.state.statuses.notifications.data
@ -7,10 +7,15 @@ export const visibleTypes = store => ([
store.state.config.notificationVisibility.mentions && 'mention', store.state.config.notificationVisibility.mentions && 'mention',
store.state.config.notificationVisibility.repeats && 'repeat', store.state.config.notificationVisibility.repeats && 'repeat',
store.state.config.notificationVisibility.follows && 'follow', store.state.config.notificationVisibility.follows && 'follow',
store.state.config.notificationVisibility.followRequest && 'follow_request',
store.state.config.notificationVisibility.moves && 'pleroma:move', store.state.config.notificationVisibility.moves && 'pleroma:move',
store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction' store.state.config.notificationVisibility.emojiReactions && 'pleroma:emoji_reaction'
].filter(_ => _)) ].filter(_ => _))
const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reaction']
export const isStatusNotification = (type) => includes(statusNotifications, type)
const sortById = (a, b) => { const sortById = (a, b) => {
const seqA = Number(a.id) const seqA = Number(a.id)
const seqB = Number(b.id) const seqB = Number(b.id)

View file

@ -11,12 +11,9 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => {
const rootState = store.rootState || store.state const rootState = store.rootState || store.state
const timelineData = rootState.statuses.notifications const timelineData = rootState.statuses.notifications
const hideMutedPosts = getters.mergedConfig.hideMutedPosts const hideMutedPosts = getters.mergedConfig.hideMutedPosts
const allowFollowingMove = rootState.users.currentUser.allow_following_move
args['withMuted'] = !hideMutedPosts args['withMuted'] = !hideMutedPosts
args['withMove'] = !allowFollowingMove
args['timeline'] = 'notifications' args['timeline'] = 'notifications'
if (older) { if (older) {
if (timelineData.minId !== Number.POSITIVE_INFINITY) { if (timelineData.minId !== Number.POSITIVE_INFINITY) {

View file

@ -8,6 +8,7 @@ export const LAYERS = {
undelay: null, // root undelay: null, // root
topBar: null, // no transparency support topBar: null, // no transparency support
badge: null, // no transparency support badge: null, // no transparency support
profileTint: null, // doesn't matter
fg: null, fg: null,
bg: 'underlay', bg: 'underlay',
highlight: 'bg', highlight: 'bg',
@ -29,6 +30,7 @@ export const LAYERS = {
* this allows redefining it to something else * this allows redefining it to something else
*/ */
export const DEFAULT_OPACITY = { export const DEFAULT_OPACITY = {
profileTint: 0.5,
alert: 0.5, alert: 0.5,
input: 0.5, input: 0.5,
faint: 0.5, faint: 0.5,
@ -119,6 +121,20 @@ export const SLOT_INHERITANCE = {
cGreen: '#00FF00', cGreen: '#00FF00',
cOrange: '#E3FF00', cOrange: '#E3FF00',
profileBg: {
depends: ['bg'],
color: (mod, bg) => ({
r: Math.floor(bg.r * 0.53),
g: Math.floor(bg.g * 0.56),
b: Math.floor(bg.b * 0.59)
})
},
profileTint: {
depends: ['bg'],
layer: 'profileTint',
opacity: 'profileTint'
},
highlight: { highlight: {
depends: ['bg'], depends: ['bg'],
color: (mod, bg) => brightness(5 * mod, bg).rgb color: (mod, bg) => brightness(5 * mod, bg).rgb

View file

@ -350,15 +350,47 @@ export const getColors = (sourceColors, sourceOpacity) => SLOT_ORDERED.reduce(({
if (!outputColor) { if (!outputColor) {
throw new Error('Couldn\'t generate color for ' + key) throw new Error('Couldn\'t generate color for ' + key)
} }
const opacitySlot = getOpacitySlot(key)
if (opacitySlot && outputColor.a === undefined) { const opacitySlot = value.opacity || getOpacitySlot(key)
const dependencySlot = deps[0] const ownOpacitySlot = value.opacity
if (dependencySlot && colors[dependencySlot] === 'transparent') {
if (ownOpacitySlot === null) {
outputColor.a = 1
} else if (sourceColor === 'transparent') {
outputColor.a = 0 outputColor.a = 0
} else { } else {
outputColor.a = Number(sourceOpacity[opacitySlot]) || OPACITIES[opacitySlot].defaultValue || 1 const opacityOverriden = ownOpacitySlot && sourceOpacity[opacitySlot] !== undefined
const dependencySlot = deps[0]
const dependencyColor = dependencySlot && colors[dependencySlot]
if (!ownOpacitySlot && dependencyColor && !value.textColor && ownOpacitySlot !== null) {
// Inheriting color from dependency (weird, i know)
// except if it's a text color or opacity slot is set to 'null'
outputColor.a = dependencyColor.a
} else if (!dependencyColor && !opacitySlot) {
// Remove any alpha channel if no dependency and no opacitySlot found
delete outputColor.a
} else {
// Otherwise try to assign opacity
if (dependencyColor && dependencyColor.a === 0) {
// transparent dependency shall make dependents transparent too
outputColor.a = 0
} else {
// Otherwise check if opacity is overriden and use that or default value instead
outputColor.a = Number(
opacityOverriden
? sourceOpacity[opacitySlot]
: (OPACITIES[opacitySlot] || {}).defaultValue
)
} }
} }
}
if (Number.isNaN(outputColor.a) || outputColor.a === undefined) {
outputColor.a = 1
}
if (opacitySlot) { if (opacitySlot) {
return { return {
colors: { ...colors, [key]: outputColor }, colors: { ...colors, [key]: outputColor },

View file

@ -1,23 +1,28 @@
{ {
"theme": "pleroma-dark",
"background": "/static/aurora_borealis.jpg",
"logo": "/static/logo.png",
"logoMask": true,
"logoMargin": ".1em",
"redirectRootNoLogin": "/main/all",
"redirectRootLogin": "/main/friends",
"showInstanceSpecificPanel": false,
"collapseMessageWithSubject": false,
"scopeCopy": true,
"subjectLineBehavior": "email",
"postContentType": "text/plain",
"alwaysShowSubjectInput": true, "alwaysShowSubjectInput": true,
"background": "/static/aurora_borealis.jpg",
"collapseMessageWithSubject": false,
"disableChat": false,
"greentext": false,
"hideFilteredStatuses": false,
"hideMutedPosts": false,
"hidePostStats": false, "hidePostStats": false,
"hideSitename": false,
"hideUserStats": false, "hideUserStats": false,
"loginMethod": "password", "loginMethod": "password",
"webPushNotifications": false, "logo": "/static/logo.png",
"noAttachmentLinks": false, "logoMargin": ".1em",
"logoMask": true,
"minimalScopesMode": false,
"nsfwCensorImage": "", "nsfwCensorImage": "",
"postContentType": "text/plain",
"redirectRootLogin": "/main/friends",
"redirectRootNoLogin": "/main/all",
"scopeCopy": true,
"showFeaturesPanel": true, "showFeaturesPanel": true,
"minimalScopesMode": false "showInstanceSpecificPanel": false,
"sidebarRight": false,
"subjectLineBehavior": "email",
"theme": "pleroma-dark",
"webPushNotifications": false
} }

View file

@ -345,6 +345,24 @@
"css": "link", "css": "link",
"code": 59427, "code": 59427,
"src": "fontawesome" "src": "fontawesome"
},
{
"uid": "4aad6bb50b02c18508aae9cbe14e784e",
"css": "share",
"code": 61920,
"src": "fontawesome"
},
{
"uid": "8b80d36d4ef43889db10bc1f0dc9a862",
"css": "user",
"code": 59428,
"src": "fontawesome"
},
{
"uid": "12f4ece88e46abd864e40b35e05b11cd",
"css": "ok",
"code": 59431,
"src": "fontawesome"
} }
] ]
} }

View file

@ -1,6 +1,6 @@
{ {
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "pleroma-dark": "/static/themes/pleroma-dark.json",
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "pleroma-light": "/static/themes/pleroma-light.json",
"pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"], "pleroma-amoled": [ "Pleroma Dark AMOLED", "#000000", "#111111", "#b0b0b1", "#d8a070", "#aa0000", "#0fa00f", "#0095ff", "#d59500"],
"classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ], "classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"], "bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],

View file

@ -0,0 +1,191 @@
{
"_pleroma_theme_version": 2,
"name": "Pleroma Dark",
"source": {
"themeEngineVersion": 3,
"fonts": {},
"shadows": {
"buttonHover": [
{
"x": 0,
"y": 0,
"blur": "1",
"spread": "2",
"color": "#b9b9ba",
"alpha": "0.4",
"inset": true
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"buttonPressed": [
{
"x": 0,
"y": 0,
"blur": 4,
"spread": 0,
"color": "#000000",
"alpha": 1,
"inset": true
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": 0,
"blur": "2",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": 1
}
],
"panelHeader": [
{
"x": 0,
"y": "1",
"blur": "3",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": "0.4"
},
{
"x": "0",
"y": "1",
"blur": "0",
"spread": 0,
"inset": true,
"color": "#ffffff",
"alpha": "0.2"
}
],
"panel": [
{
"x": "0",
"y": "0",
"blur": "3",
"spread": 0,
"color": "#000000",
"alpha": "0.5"
},
{
"x": "0",
"y": "4",
"blur": "6",
"spread": "3",
"inset": false,
"color": "#000000",
"alpha": "0.3"
}
],
"button": [
{
"x": 0,
"y": 0,
"blur": 2,
"spread": 0,
"color": "#000000",
"alpha": 1
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"topBar": [
{
"x": 0,
"y": "1",
"blur": 4,
"spread": 0,
"color": "#000000",
"alpha": "0.4"
},
{
"x": 0,
"y": "2",
"blur": "7",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": "0.3"
}
]
},
"opacity": {
"underlay": "0.6"
},
"colors": {
"bg": "#0f161e",
"fg": "#151e2b",
"text": "#b9b9ba",
"underlay": "#090e14",
"accent": "#e2b188",
"cBlue": "#81beea",
"cRed": "#d31014",
"cGreen": "#5dc94a",
"cOrange": "#ffc459",
"border": "--fg,3",
"topBarText": "--text,-9.75",
"topBarLink": "--topBarText",
"btnToggled": "--accent,-24.2",
"alertErrorText": "--text,21.2",
"badgeNotification": "#e15932",
"badgeNotificationText": "#ffffff"
},
"radii": {
"btn": "3",
"input": "3",
"panel": "3",
"avatar": "3",
"attachment": "3"
}
}
}

View file

@ -0,0 +1,219 @@
{
"_pleroma_theme_version": 2,
"name": "Pleroma Light",
"source": {
"themeEngineVersion": 3,
"fonts": {},
"shadows": {
"button": [
{
"x": 0,
"y": 0,
"blur": 2,
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": "0.5",
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"buttonHover": [
{
"x": 0,
"y": 0,
"blur": "2",
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": 0,
"y": "0",
"blur": "1",
"spread": "2",
"color": "#ffc39f",
"alpha": "1",
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
}
],
"input": [
{
"x": 0,
"y": 1,
"blur": 0,
"spread": 0,
"color": "#000000",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
},
{
"x": 0,
"y": 0,
"blur": "2",
"inset": true,
"spread": 0,
"color": "#000000",
"alpha": "0.15"
}
],
"panel": [
{
"x": "0",
"y": 1,
"blur": "3",
"spread": 0,
"color": "#000000",
"alpha": "0.5"
},
{
"x": "0",
"y": "3",
"blur": "6",
"spread": "1",
"inset": false,
"color": "#000000",
"alpha": "0.2"
}
],
"panelHeader": [
{
"x": 0,
"y": "1",
"blur": 0,
"spread": 0,
"inset": true,
"color": "#ffffff",
"alpha": "0.5"
},
{
"x": 0,
"y": "1",
"blur": "3",
"spread": 0,
"inset": false,
"color": "#000000",
"alpha": "0.3"
}
],
"buttonPressed": [
{
"x": 0,
"y": 0,
"blur": 4,
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": 0,
"y": 1,
"blur": "1",
"spread": "2",
"color": "#000000",
"alpha": "0.3",
"inset": true
},
{
"x": 0,
"y": -1,
"blur": 0,
"spread": 0,
"color": "#FFFFFF",
"alpha": 0.2,
"inset": true
}
],
"popup": [
{
"x": "1",
"y": "2",
"blur": "2",
"spread": 0,
"color": "#000000",
"alpha": "0.2"
},
{
"x": "1",
"y": "3",
"blur": "7",
"spread": "0",
"inset": false,
"color": "#000000",
"alpha": "0.2"
}
],
"avatarStatus": [
{
"x": 0,
"y": "1",
"blur": "4",
"spread": "0",
"inset": false,
"color": "#000000",
"alpha": "0.2"
}
]
},
"opacity": {
"underlay": "0.4"
},
"colors": {
"bg": "#f2f6f9",
"fg": "#d6dfed",
"text": "#304055",
"underlay": "#5d6086",
"accent": "#f55b1b",
"cBlue": "#0095ff",
"cRed": "#d31014",
"cGreen": "#0fa00f",
"cOrange": "#ffa500",
"border": "#d8e6f9",
"topBarText": "#304055",
"topBarLink": "--topBarText",
"btnToggled": "--accent,-24.2",
"input": "#dee3ed",
"badgeNotification": "#e83802"
},
"radii": {
"btn": "3",
"input": "3",
"panel": "3",
"avatar": "3",
"attachment": "3"
}
}
}

View file

@ -19,6 +19,7 @@ const actions = {
const testGetters = { const testGetters = {
findUser: state => getters.findUser(state.users), findUser: state => getters.findUser(state.users),
relationship: state => getters.relationship(state.users),
mergedConfig: state => ({ mergedConfig: state => ({
colors: '', colors: '',
highlight: {}, highlight: {},
@ -96,7 +97,8 @@ const externalProfileStore = new Vuex.Store({
credentials: '' credentials: ''
}, },
usersObject: { 100: extUser }, usersObject: { 100: extUser },
users: [extUser] users: [extUser],
relationships: {}
} }
} }
}) })
@ -164,7 +166,8 @@ const localProfileStore = new Vuex.Store({
credentials: '' credentials: ''
}, },
usersObject: { 100: localUser, 'testuser': localUser }, usersObject: { 100: localUser, 'testuser': localUser },
users: [localUser] users: [localUser],
relationships: {}
} }
} }
}) })

View file

@ -18,20 +18,6 @@ describe('The users module', () => {
expect(state.users).to.eql([user]) expect(state.users).to.eql([user])
expect(state.users[0].name).to.eql('Dude') expect(state.users[0].name).to.eql('Dude')
}) })
it('sets a mute bit on users', () => {
const state = cloneDeep(defaultState)
const user = { id: '1', name: 'Guy' }
mutations.addNewUsers(state, [user])
mutations.setMuted(state, { user, muted: true })
expect(user.muted).to.eql(true)
mutations.setMuted(state, { user, muted: false })
expect(user.muted).to.eql(false)
})
}) })
describe('findUser', () => { describe('findUser', () => {

View file

@ -338,9 +338,9 @@ describe('API Entities normalizer', () => {
describe('MastoAPI emoji adder', () => { describe('MastoAPI emoji adder', () => {
const emojis = makeMockEmojiMasto() const emojis = makeMockEmojiMasto()
const imageHtml = '<img src="https://example.com/image.png" alt="image" title="image" class="emoji" />' const imageHtml = '<img src="https://example.com/image.png" alt=":image:" title=":image:" class="emoji" />'
.replace(/"/g, '\'') .replace(/"/g, '\'')
const thinkHtml = '<img src="https://example.com/think.png" alt="thinking" title="thinking" class="emoji" />' const thinkHtml = '<img src="https://example.com/think.png" alt=":thinking:" title=":thinking:" class="emoji" />'
.replace(/"/g, '\'') .replace(/"/g, '\'')
it('correctly replaces shortcodes in supplied string', () => { it('correctly replaces shortcodes in supplied string', () => {
@ -366,8 +366,8 @@ describe('API Entities normalizer', () => {
shortcode: '[a-z] {|}*' shortcode: '[a-z] {|}*'
}]) }])
const result = addEmojis('This post has :c++: emoji and :[a-z] {|}*: emoji', emojis) const result = addEmojis('This post has :c++: emoji and :[a-z] {|}*: emoji', emojis)
expect(result).to.include('title=\'c++\'') expect(result).to.include('title=\':c++:\'')
expect(result).to.include('title=\'[a-z] {|}*\'') expect(result).to.include('title=\':[a-z] {|}*:\'')
}) })
}) })
}) })

View file

@ -5941,11 +5941,6 @@ pngjs@^3.3.0:
version "3.3.3" version "3.3.3"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b"
popper.js@^1.15.0:
version "1.15.0"
resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.15.0.tgz#5560b99bbad7647e9faa475c6b8056621f5a4ff2"
integrity sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==
portal-vue@^2.1.4: portal-vue@^2.1.4:
version "2.1.4" version "2.1.4"
resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.4.tgz#1fc679d77e294dc8d026f1eb84aa467de11b392e" resolved "https://registry.yarnpkg.com/portal-vue/-/portal-vue-2.1.4.tgz#1fc679d77e294dc8d026f1eb84aa467de11b392e"
@ -7823,15 +7818,6 @@ v-click-outside@^2.1.1:
version "2.1.3" version "2.1.3"
resolved "https://registry.yarnpkg.com/v-click-outside/-/v-click-outside-2.1.3.tgz#b7297abe833a439dc0895e6418a494381e64b5e7" resolved "https://registry.yarnpkg.com/v-click-outside/-/v-click-outside-2.1.3.tgz#b7297abe833a439dc0895e6418a494381e64b5e7"
v-tooltip@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/v-tooltip/-/v-tooltip-2.0.2.tgz#8610d9eece2cc44fd66c12ef2f12eec6435cab9b"
integrity sha512-xQ+qzOFfywkLdjHknRPgMMupQNS8yJtf9Utd5Dxiu/0n4HtrxqsgDtN2MLZ0LKbburtSAQgyypuE/snM8bBZhw==
dependencies:
lodash "^4.17.11"
popper.js "^1.15.0"
vue-resize "^0.4.5"
validate-npm-package-license@^3.0.1: validate-npm-package-license@^3.0.1:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"
@ -7906,11 +7892,6 @@ vue-loader@^14.0.0:
vue-style-loader "^4.0.1" vue-style-loader "^4.0.1"
vue-template-es2015-compiler "^1.6.0" vue-template-es2015-compiler "^1.6.0"
vue-resize@^0.4.5:
version "0.4.5"
resolved "https://registry.yarnpkg.com/vue-resize/-/vue-resize-0.4.5.tgz#4777a23042e3c05620d9cbda01c0b3cc5e32dcea"
integrity sha512-bhP7MlgJQ8TIkZJXAfDf78uJO+mEI3CaLABLjv0WNzr4CcGRGPIAItyWYnP6LsPA4Oq0WE+suidNs6dgpO4RHg==
vue-router@^3.0.1: vue-router@^3.0.1:
version "3.0.2" version "3.0.2"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.2.tgz#dedc67afe6c4e2bc25682c8b1c2a8c0d7c7e56be"