fix conflicts
This commit is contained in:
commit
32b1ae15d2
79 changed files with 6406 additions and 3830 deletions
39
CHANGELOG.md
39
CHANGELOG.md
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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"
|
|
||||||
:offset="5"
|
|
||||||
>
|
>
|
||||||
<div slot="popover">
|
<div
|
||||||
|
slot="content"
|
||||||
|
class="account-tools-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);
|
||||||
|
|
|
@ -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"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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 })
|
||||||
this.$store.dispatch('removeFollowRequest', this.user)
|
.then(() => {
|
||||||
|
this.$store.dispatch('dismissNotificationLocal', { id: notifId })
|
||||||
|
this.$store.dispatch('removeFollowRequest', this.user)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'"
|
||||||
|
|
|
@ -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);
|
||||||
|
|
156
src/components/popover/popover.js
Normal file
156
src/components/popover/popover.js
Normal 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
|
118
src/components/popover/popover.vue
Normal file
118
src/components/popover/popover.vue
Normal 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>
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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 () {
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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
|
<i
|
||||||
v-if="loggedIn"
|
slot="trigger"
|
||||||
@click.prevent="openReactionSelect"
|
class="icon-smile button-icon add-reaction-button"
|
||||||
>
|
:title="$t('tool_tip.add_reaction')"
|
||||||
<i
|
/>
|
||||||
class="icon-smile button-icon add-reaction-button"
|
</Popover>
|
||||||
:title="$t('tool_tip.add_reaction')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-popover>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script src="./react_button.js" ></script>
|
<script src="./react_button.js" ></script>
|
||||||
|
|
|
@ -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,15 +14,17 @@ const registration = {
|
||||||
},
|
},
|
||||||
captcha: {}
|
captcha: {}
|
||||||
}),
|
}),
|
||||||
validations: {
|
validations () {
|
||||||
user: {
|
return {
|
||||||
email: { required },
|
user: {
|
||||||
username: { required },
|
email: { required: requiredIf(() => this.accountActivationRequired) },
|
||||||
fullname: { required },
|
username: { required },
|
||||||
password: { required },
|
fullname: { required },
|
||||||
confirm: {
|
password: { required },
|
||||||
required,
|
confirm: {
|
||||||
sameAsPassword: sameAs('password')
|
required,
|
||||||
|
sameAsPassword: sameAs('password')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -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: {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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('>')) {
|
|
||||||
// This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works
|
|
||||||
return processHtml(html, (string) => {
|
|
||||||
if (string.includes('>') &&
|
|
||||||
string
|
|
||||||
.replace(/<[^>]+?>/gi, '') // remove all tags
|
|
||||||
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
|
|
||||||
.trim()
|
|
||||||
.startsWith('>')) {
|
|
||||||
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: {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
210
src/components/status_content/status_content.js
Normal file
210
src/components/status_content/status_content.js
Normal 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('>')) {
|
||||||
|
// This checks if post has '>' at the beginning, excluding mentions so that @mention >impying works
|
||||||
|
return processHtml(html, (string) => {
|
||||||
|
if (string.includes('>') &&
|
||||||
|
string
|
||||||
|
.replace(/<[^>]+?>/gi, '') // remove all tags
|
||||||
|
.replace(/@\w+/gi, '') // remove mentions (even failed ones)
|
||||||
|
.trim()
|
||||||
|
.startsWith('>')) {
|
||||||
|
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
|
240
src/components/status_content/status_content.vue
Normal file
240
src/components/status_content/status_content.vue
Normal 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>
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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="popover">
|
<template slot="trigger">
|
||||||
|
<slot />
|
||||||
|
</template>
|
||||||
|
<div
|
||||||
|
slot="content"
|
||||||
|
>
|
||||||
<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,50 +38,25 @@
|
||||||
<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;
|
border-width: 1px;
|
||||||
border-width: 1px;
|
border-radius: $fallback--tooltipRadius;
|
||||||
border-radius: $fallback--tooltipRadius;
|
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;
|
||||||
|
|
||||||
|
|
|
@ -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,21 +35,11 @@ export default {
|
||||||
}]
|
}]
|
||||||
},
|
},
|
||||||
style () {
|
style () {
|
||||||
const color = this.$store.getters.mergedConfig.customTheme.colors
|
return {
|
||||||
? this.$store.getters.mergedConfig.customTheme.colors.bg // v2
|
backgroundImage: [
|
||||||
: this.$store.getters.mergedConfig.colors.bg // v1
|
`linear-gradient(to bottom, var(--profileTint), var(--profileTint))`,
|
||||||
|
`url(${this.user.cover_photo})`
|
||||||
if (color) {
|
].join(', ')
|
||||||
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 {
|
|
||||||
backgroundColor: `rgb(${Math.floor(rgb.r * 0.53)}, ${Math.floor(rgb.g * 0.56)}, ${Math.floor(rgb.b * 0.59)})`,
|
|
||||||
backgroundImage: [
|
|
||||||
`linear-gradient(to bottom, ${tintColor}, ${tintColor})`,
|
|
||||||
`url(${this.user.cover_photo})`
|
|
||||||
].join(', ')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isOtherUser () {
|
isOtherUser () {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
979
src/i18n/de.json
979
src/i18n/de.json
|
@ -1,413 +1,584 @@
|
||||||
{
|
{
|
||||||
"chat": {
|
"chat": {
|
||||||
"title": "Chat"
|
"title": "Chat"
|
||||||
},
|
|
||||||
"features_panel": {
|
|
||||||
"chat": "Chat",
|
|
||||||
"gopher": "Gopher",
|
|
||||||
"media_proxy": "Medienproxy",
|
|
||||||
"scope_options": "Reichweitenoptionen",
|
|
||||||
"text_limit": "Textlimit",
|
|
||||||
"title": "Features",
|
|
||||||
"who_to_follow": "Wem folgen?"
|
|
||||||
},
|
|
||||||
"finder": {
|
|
||||||
"error_fetching_user": "Fehler beim Suchen des Benutzers",
|
|
||||||
"find_user": "Finde Benutzer"
|
|
||||||
},
|
|
||||||
"general": {
|
|
||||||
"apply": "Anwenden",
|
|
||||||
"submit": "Absenden"
|
|
||||||
},
|
|
||||||
"login": {
|
|
||||||
"login": "Anmelden",
|
|
||||||
"description": "Mit OAuth anmelden",
|
|
||||||
"logout": "Abmelden",
|
|
||||||
"password": "Passwort",
|
|
||||||
"placeholder": "z.B. lain",
|
|
||||||
"register": "Registrieren",
|
|
||||||
"username": "Benutzername"
|
|
||||||
},
|
|
||||||
"nav": {
|
|
||||||
"about": "Über",
|
|
||||||
"back": "Zurück",
|
|
||||||
"chat": "Lokaler Chat",
|
|
||||||
"friend_requests": "Followanfragen",
|
|
||||||
"mentions": "Erwähnungen",
|
|
||||||
"interactions": "Interaktionen",
|
|
||||||
"dms": "Direktnachrichten",
|
|
||||||
"public_tl": "Öffentliche Zeitleiste",
|
|
||||||
"timeline": "Zeitleiste",
|
|
||||||
"twkn": "Das gesamte bekannte Netzwerk",
|
|
||||||
"user_search": "Benutzersuche",
|
|
||||||
"search": "Suche",
|
|
||||||
"preferences": "Voreinstellungen"
|
|
||||||
},
|
|
||||||
"notifications": {
|
|
||||||
"broken_favorite": "Unbekannte Nachricht, suche danach...",
|
|
||||||
"favorited_you": "favorisierte deine Nachricht",
|
|
||||||
"followed_you": "folgt dir",
|
|
||||||
"load_older": "Ältere Benachrichtigungen laden",
|
|
||||||
"notifications": "Benachrichtigungen",
|
|
||||||
"read": "Gelesen!",
|
|
||||||
"repeated_you": "wiederholte deine Nachricht"
|
|
||||||
},
|
|
||||||
"post_status": {
|
|
||||||
"new_status": "Neuen Status veröffentlichen",
|
|
||||||
"account_not_locked_warning": "Dein Profil ist nicht {0}. Wer dir folgen will, kann das jederzeit tun und dann auch deine privaten Beiträge sehen.",
|
|
||||||
"account_not_locked_warning_link": "gesperrt",
|
|
||||||
"attachments_sensitive": "Anhänge als heikel markieren",
|
|
||||||
"content_type": {
|
|
||||||
"text/plain": "Nur Text"
|
|
||||||
},
|
},
|
||||||
"content_warning": "Betreff (optional)",
|
"features_panel": {
|
||||||
"default": "Sitze gerade im Hofbräuhaus.",
|
"chat": "Chat",
|
||||||
"direct_warning": "Dieser Beitrag wird nur für die erwähnten Nutzer sichtbar sein.",
|
"gopher": "Gopher",
|
||||||
"posting": "Veröffentlichen",
|
"media_proxy": "Medienproxy",
|
||||||
"scope": {
|
"scope_options": "Reichweitenoptionen",
|
||||||
"direct": "Direkt - Beitrag nur an erwähnte Profile",
|
"text_limit": "Zeichenlimit",
|
||||||
"private": "Nur Follower - Beitrag nur für Follower sichtbar",
|
"title": "Funktionen",
|
||||||
"public": "Öffentlich - Beitrag an öffentliche Zeitleisten",
|
"who_to_follow": "Wem folgen?"
|
||||||
"unlisted": "Nicht gelistet - Nicht in öffentlichen Zeitleisten anzeigen"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"registration": {
|
|
||||||
"bio": "Bio",
|
|
||||||
"email": "Email",
|
|
||||||
"fullname": "Angezeigter Name",
|
|
||||||
"password_confirm": "Passwort bestätigen",
|
|
||||||
"registration": "Registrierung",
|
|
||||||
"token": "Einladungsschlüssel",
|
|
||||||
"captcha": "CAPTCHA",
|
|
||||||
"new_captcha": "Zum Erstellen eines neuen Captcha auf das Bild klicken.",
|
|
||||||
"validations": {
|
|
||||||
"username_required": "darf nicht leer sein",
|
|
||||||
"fullname_required": "darf nicht leer sein",
|
|
||||||
"email_required": "darf nicht leer sein",
|
|
||||||
"password_required": "darf nicht leer sein",
|
|
||||||
"password_confirmation_required": "darf nicht leer sein",
|
|
||||||
"password_confirmation_match": "sollte mit dem Passwort identisch sein."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"attachmentRadius": "Anhänge",
|
|
||||||
"attachments": "Anhänge",
|
|
||||||
"autoload": "Aktiviere automatisches Laden von älteren Beiträgen beim scrollen",
|
|
||||||
"avatar": "Avatar",
|
|
||||||
"avatarAltRadius": "Avatare (Benachrichtigungen)",
|
|
||||||
"avatarRadius": "Avatare",
|
|
||||||
"background": "Hintergrund",
|
|
||||||
"bio": "Bio",
|
|
||||||
"btnRadius": "Buttons",
|
|
||||||
"cBlue": "Blau (Antworten, Folgt dir)",
|
|
||||||
"cGreen": "Grün (Retweet)",
|
|
||||||
"cOrange": "Orange (Favorisieren)",
|
|
||||||
"cRed": "Rot (Abbrechen)",
|
|
||||||
"change_password": "Passwort ändern",
|
|
||||||
"change_password_error": "Es gab ein Problem bei der Änderung des Passworts.",
|
|
||||||
"changed_password": "Passwort erfolgreich geändert!",
|
|
||||||
"collapse_subject": "Beiträge mit Betreff einklappen",
|
|
||||||
"composing": "Verfassen",
|
|
||||||
"confirm_new_password": "Neues Passwort bestätigen",
|
|
||||||
"current_avatar": "Dein derzeitiger Avatar",
|
|
||||||
"current_password": "Aktuelles Passwort",
|
|
||||||
"current_profile_banner": "Der derzeitige Banner deines Profils",
|
|
||||||
"data_import_export_tab": "Datenimport/-export",
|
|
||||||
"default_vis": "Standard-Sichtbarkeitsumfang",
|
|
||||||
"delete_account": "Account löschen",
|
|
||||||
"delete_account_description": "Lösche deinen Account und alle deine Nachrichten 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_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",
|
|
||||||
"avatar_size_instruction": "Die empfohlene minimale Größe für Avatare ist 150x150 Pixel.",
|
|
||||||
"pad_emoji": "Emojis mit Leerzeichen umrahmen",
|
|
||||||
"export_theme": "Farbschema speichern",
|
|
||||||
"filtering": "Filtern",
|
|
||||||
"filtering_explanation": "Alle Beiträge die diese Wörter enthalten werden ausgeblendet. Ein Wort pro Zeile.",
|
|
||||||
"follow_export": "Follower exportieren",
|
|
||||||
"follow_export_button": "Exportiere deine Follows in eine csv-Datei",
|
|
||||||
"follow_export_processing": "In Bearbeitung. Die Liste steht gleich zum herunterladen bereit.",
|
|
||||||
"follow_import": "Followers importieren",
|
|
||||||
"follow_import_error": "Fehler beim importieren der Follower",
|
|
||||||
"follows_imported": "Followers importiert! Die Bearbeitung kann eine Zeit lang dauern.",
|
|
||||||
"foreground": "Vordergrund",
|
|
||||||
"general": "Allgemein",
|
|
||||||
"hide_attachments_in_convo": "Anhänge in Unterhaltungen ausblenden",
|
|
||||||
"hide_attachments_in_tl": "Anhänge in der Zeitleiste ausblenden",
|
|
||||||
"hide_muted_posts": "Verberge Beiträge stummgeschalteter Nutzer",
|
|
||||||
"max_thumbnails": "Maximale Anzahl von Vorschaubildern pro Beitrag",
|
|
||||||
"hide_isp": "Instanz-spezifisches Panel ausblenden",
|
|
||||||
"preload_images": "Bilder vorausladen",
|
|
||||||
"use_one_click_nsfw": "Heikle Anhänge mit nur einem Klick öffnen",
|
|
||||||
"hide_post_stats": "Beitragsstatistiken verbergen (z.B. die Anzahl der Favoriten)",
|
|
||||||
"hide_user_stats": "Benutzerstatistiken verbergen (z.B. die Anzahl der Follower)",
|
|
||||||
"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_theme": "Farbschema laden",
|
|
||||||
"inputRadius": "Eingabefelder",
|
|
||||||
"checkboxRadius": "Auswahlfelder",
|
|
||||||
"instance_default": "(Standard: {value})",
|
|
||||||
"instance_default_simple": "(Standard)",
|
|
||||||
"interface": "Oberfläche",
|
|
||||||
"interfaceLanguage": "Sprache der Oberfläche",
|
|
||||||
"invalid_theme_imported": "Die ausgewählte Datei ist kein unterstütztes Pleroma-Theme. Keine Änderungen wurden vorgenommen.",
|
|
||||||
"limited_availability": "In deinem Browser nicht verfügbar",
|
|
||||||
"links": "Links",
|
|
||||||
"lock_account_description": "Sperre deinen Account, um neue Follower zu genehmigen oder abzulehnen",
|
|
||||||
"loop_video": "Videos wiederholen",
|
|
||||||
"loop_video_silent_only": "Nur Videos ohne Ton wiederholen (z.B. Mastodons \"gifs\")",
|
|
||||||
"mutes_tab": "Mutes",
|
|
||||||
"play_videos_in_modal": "Videos in größerem Medienfenster abspielen",
|
|
||||||
"use_contain_fit": "Vorschaubilder nicht zuschneiden",
|
|
||||||
"name": "Name",
|
|
||||||
"name_bio": "Name & Bio",
|
|
||||||
"new_password": "Neues Passwort",
|
|
||||||
"notification_visibility": "Benachrichtigungstypen, die angezeigt werden sollen",
|
|
||||||
"notification_visibility_follows": "Follows",
|
|
||||||
"notification_visibility_likes": "Favoriten",
|
|
||||||
"notification_visibility_mentions": "Erwähnungen",
|
|
||||||
"notification_visibility_repeats": "Wiederholungen",
|
|
||||||
"no_rich_text_description": "Rich-Text Formatierungen von allen Beiträgen entfernen",
|
|
||||||
"hide_follows_description": "Zeige nicht, wem ich folge",
|
|
||||||
"hide_followers_description": "Zeige nicht, wer mir folgt",
|
|
||||||
"hide_follows_count_description": "Verberge die Anzahl deiner Gefolgten",
|
|
||||||
"hide_followers_count_description": "Verberge die Anzahl deiner Folgenden",
|
|
||||||
"nsfw_clickthrough": "Aktiviere ausblendbares Overlay für Anhänge, die als NSFW markiert sind",
|
|
||||||
"oauth_tokens": "OAuth-Token",
|
|
||||||
"token": "Zeichen",
|
|
||||||
"refresh_token": "Token aktualisieren",
|
|
||||||
"valid_until": "Gültig bis",
|
|
||||||
"revoke_token": "Widerrufen",
|
|
||||||
"panelRadius": "Panel",
|
|
||||||
"pause_on_unfocused": "Streaming pausieren, wenn das Tab nicht fokussiert ist",
|
|
||||||
"presets": "Voreinstellungen",
|
|
||||||
"profile_background": "Profilhintergrund",
|
|
||||||
"profile_banner": "Profilbanner",
|
|
||||||
"profile_tab": "Profil",
|
|
||||||
"radii_help": "Kantenrundung (in Pixel) der Oberfläche anpassen",
|
|
||||||
"replies_in_timeline": "Antworten in der Zeitleiste",
|
|
||||||
"reply_link_preview": "Antwortlink-Vorschau beim Überfahren mit der Maus aktivieren",
|
|
||||||
"reply_visibility_all": "Alle Antworten zeigen",
|
|
||||||
"reply_visibility_following": "Zeige nur Antworten an mich oder an Benutzer, denen ich folge",
|
|
||||||
"reply_visibility_self": "Nur Antworten an mich anzeigen",
|
|
||||||
"autohide_floating_post_button": "Automatisches Verbergen des Knopfs für neue Beiträge (mobil)",
|
|
||||||
"saving_err": "Fehler beim Speichern der Einstellungen",
|
|
||||||
"saving_ok": "Einstellungen gespeichert",
|
|
||||||
"security_tab": "Sicherheit",
|
|
||||||
"scope_copy": "Reichweite beim Antworten übernehmen (Direktnachrichten werden immer kopiert)",
|
|
||||||
"minimal_scopes_mode": "Minimiere Reichweitenoptionen",
|
|
||||||
"set_new_avatar": "Setze einen neuen Avatar",
|
|
||||||
"set_new_profile_background": "Setze einen neuen Hintergrund für dein Profil",
|
|
||||||
"set_new_profile_banner": "Setze einen neuen Banner für dein Profil",
|
|
||||||
"settings": "Einstellungen",
|
|
||||||
"subject_input_always_show": "Betreff-Feld immer anzeigen",
|
|
||||||
"subject_line_behavior": "Betreff beim Antworten kopieren",
|
|
||||||
"subject_line_email": "Wie Email: \"re: Betreff\"",
|
|
||||||
"subject_line_mastodon": "Wie Mastodon: unverändert kopieren",
|
|
||||||
"subject_line_noop": "Nicht kopieren",
|
|
||||||
"post_status_content_type": "Beitragsart",
|
|
||||||
"stop_gifs": "Animationen nur beim Darüberfahren abspielen",
|
|
||||||
"streaming": "Aktiviere automatisches Laden (Streaming) von neuen Beiträgen",
|
|
||||||
"text": "Text",
|
|
||||||
"theme": "Farbschema",
|
|
||||||
"theme_help": "Benutze HTML-Farbcodes (#rrggbb) um dein Farbschema anzupassen",
|
|
||||||
"theme_help_v2_1": "Du kannst auch die Farben und die Deckkraft bestimmter Komponenten überschreiben, indem du das Kontrollkästchen umschaltest. Verwende die Schaltfläche \"Alle löschen\", um alle Überschreibungen zurückzusetzen.",
|
|
||||||
"theme_help_v2_2": "Unter einigen Einträgen befinden sich Symbole für Hintergrund-/Textkontrastindikatoren, für detaillierte Informationen fahre mit der Maus darüber. Bitte beachte, dass bei der Verwendung von Transparenz Kontrastindikatoren den schlechtest möglichen Fall darstellen.",
|
|
||||||
"tooltipRadius": "Tooltips/Warnungen",
|
|
||||||
"user_settings": "Benutzereinstellungen",
|
|
||||||
"values": {
|
|
||||||
"false": "nein",
|
|
||||||
"true": "Ja"
|
|
||||||
},
|
},
|
||||||
"notifications": "Benachrichtigungen",
|
"finder": {
|
||||||
"enable_web_push_notifications": "Web-Pushbenachrichtigungen aktivieren",
|
"error_fetching_user": "Fehler beim Suchen des Benutzers",
|
||||||
|
"find_user": "Finde Benutzer"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"apply": "Anwenden",
|
||||||
|
"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": "Anmelden",
|
||||||
|
"description": "Mit OAuth anmelden",
|
||||||
|
"logout": "Abmelden",
|
||||||
|
"password": "Passwort",
|
||||||
|
"placeholder": "z.B. lain",
|
||||||
|
"register": "Registrieren",
|
||||||
|
"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": {
|
||||||
|
"about": "Über",
|
||||||
|
"back": "Zurück",
|
||||||
|
"chat": "Lokaler Chat",
|
||||||
|
"friend_requests": "Followanfragen",
|
||||||
|
"mentions": "Erwähnungen",
|
||||||
|
"interactions": "Interaktionen",
|
||||||
|
"dms": "Direktnachrichten",
|
||||||
|
"public_tl": "Öffentliche Zeitleiste",
|
||||||
|
"timeline": "Zeitleiste",
|
||||||
|
"twkn": "Das gesamte bekannte Netzwerk",
|
||||||
|
"user_search": "Benutzersuche",
|
||||||
|
"search": "Suche",
|
||||||
|
"preferences": "Voreinstellungen",
|
||||||
|
"administration": "Administration",
|
||||||
|
"who_to_follow": "Wem folgen"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"broken_favorite": "Unbekannte Nachricht, suche danach...",
|
||||||
|
"favorited_you": "favorisierte deine Nachricht",
|
||||||
|
"followed_you": "folgt dir",
|
||||||
|
"load_older": "Ältere Benachrichtigungen laden",
|
||||||
|
"notifications": "Benachrichtigungen",
|
||||||
|
"read": "Gelesen!",
|
||||||
|
"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": {
|
||||||
|
"new_status": "Neuen Status veröffentlichen",
|
||||||
|
"account_not_locked_warning": "Dein Profil ist nicht {0}. Wer dir folgen will, kann das jederzeit tun und dann auch deine privaten Beiträge sehen.",
|
||||||
|
"account_not_locked_warning_link": "gesperrt",
|
||||||
|
"attachments_sensitive": "Anhänge als heikel markieren",
|
||||||
|
"content_type": {
|
||||||
|
"text/plain": "Nur Text",
|
||||||
|
"text/bbcode": "BBCode",
|
||||||
|
"text/markdown": "Markdown",
|
||||||
|
"text/html": "HTML"
|
||||||
|
},
|
||||||
|
"content_warning": "Betreff (optional)",
|
||||||
|
"default": "Sitze gerade im Hofbräuhaus.",
|
||||||
|
"direct_warning": "Dieser Beitrag wird nur für die erwähnten Nutzer sichtbar sein.",
|
||||||
|
"posting": "Veröffentlichen",
|
||||||
|
"scope": {
|
||||||
|
"direct": "Direkt - Beitrag nur an erwähnte Profile",
|
||||||
|
"private": "Nur Follower - Beitrag nur für Follower sichtbar",
|
||||||
|
"public": "Öffentlich - Beitrag an öffentliche Zeitleisten",
|
||||||
|
"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": {
|
||||||
|
"bio": "Bio",
|
||||||
|
"email": "Email",
|
||||||
|
"fullname": "Angezeigter Name",
|
||||||
|
"password_confirm": "Passwort bestätigen",
|
||||||
|
"registration": "Registrierung",
|
||||||
|
"token": "Einladungsschlüssel",
|
||||||
|
"captcha": "CAPTCHA",
|
||||||
|
"new_captcha": "Zum Erstellen eines neuen Captcha auf das Bild klicken.",
|
||||||
|
"validations": {
|
||||||
|
"username_required": "darf nicht leer sein",
|
||||||
|
"fullname_required": "darf nicht leer sein",
|
||||||
|
"email_required": "darf nicht leer sein",
|
||||||
|
"password_required": "darf nicht leer sein",
|
||||||
|
"password_confirmation_required": "darf nicht leer 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": {
|
||||||
|
"attachmentRadius": "Anhänge",
|
||||||
|
"attachments": "Anhänge",
|
||||||
|
"autoload": "Aktiviere automatisches Laden von älteren Beiträgen beim scrollen",
|
||||||
|
"avatar": "Avatar",
|
||||||
|
"avatarAltRadius": "Avatare (Benachrichtigungen)",
|
||||||
|
"avatarRadius": "Avatare",
|
||||||
|
"background": "Hintergrund",
|
||||||
|
"bio": "Bio",
|
||||||
|
"btnRadius": "Buttons",
|
||||||
|
"cBlue": "Blau (Antworten, folgt dir)",
|
||||||
|
"cGreen": "Grün (Retweet)",
|
||||||
|
"cOrange": "Orange (Favorisieren)",
|
||||||
|
"cRed": "Rot (Abbrechen)",
|
||||||
|
"change_password": "Passwort ändern",
|
||||||
|
"change_password_error": "Es gab ein Problem bei der Änderung des Passworts.",
|
||||||
|
"changed_password": "Passwort erfolgreich geändert!",
|
||||||
|
"collapse_subject": "Beiträge mit Betreff einklappen",
|
||||||
|
"composing": "Verfassen",
|
||||||
|
"confirm_new_password": "Neues Passwort bestätigen",
|
||||||
|
"current_avatar": "Dein derzeitiger Avatar",
|
||||||
|
"current_password": "Aktuelles Passwort",
|
||||||
|
"current_profile_banner": "Der derzeitige Banner deines Profils",
|
||||||
|
"data_import_export_tab": "Datenimport/-export",
|
||||||
|
"default_vis": "Standard-Sichtbarkeitsumfang",
|
||||||
|
"delete_account": "Account löschen",
|
||||||
|
"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_instructions": "Tippe dein Passwort unten in das Feld ein, um die Löschung deines Accounts zu bestätigen.",
|
||||||
|
"discoverable": "Erlaube, dass dieser Account in Suchergebnissen auftaucht",
|
||||||
|
"avatar_size_instruction": "Die empfohlene minimale Größe für Avatare ist 150x150 Pixel.",
|
||||||
|
"pad_emoji": "Emojis mit Leerzeichen umrahmen",
|
||||||
|
"export_theme": "Farbschema speichern",
|
||||||
|
"filtering": "Filtern",
|
||||||
|
"filtering_explanation": "Alle Beiträge, welche diese Wörter enthalten, werden ausgeblendet. Ein Wort pro Zeile.",
|
||||||
|
"follow_export": "Follower exportieren",
|
||||||
|
"follow_export_button": "Exportiere deine Follows in eine csv-Datei",
|
||||||
|
"follow_export_processing": "In Bearbeitung. Die Liste steht gleich zum herunterladen bereit.",
|
||||||
|
"follow_import": "Follower importieren",
|
||||||
|
"follow_import_error": "Fehler beim Importieren der Follower",
|
||||||
|
"follows_imported": "Follower importiert! Die Bearbeitung kann einen Moment dauern.",
|
||||||
|
"foreground": "Vordergrund",
|
||||||
|
"general": "Allgemein",
|
||||||
|
"hide_attachments_in_convo": "Anhänge in Unterhaltungen ausblenden",
|
||||||
|
"hide_attachments_in_tl": "Anhänge in der Zeitleiste ausblenden",
|
||||||
|
"hide_muted_posts": "Verberge Beiträge stummgeschalteter Nutzer",
|
||||||
|
"max_thumbnails": "Maximale Anzahl von Vorschaubildern pro Beitrag",
|
||||||
|
"hide_isp": "Instanz-spezifisches Panel ausblenden",
|
||||||
|
"preload_images": "Bilder vorausladen",
|
||||||
|
"use_one_click_nsfw": "Heikle Anhänge mit nur einem Klick öffnen",
|
||||||
|
"hide_post_stats": "Beitragsstatistiken verbergen (z.B. die Anzahl der Favoriten)",
|
||||||
|
"hide_user_stats": "Benutzerstatistiken verbergen (z.B. die Anzahl der Follower)",
|
||||||
|
"hide_filtered_statuses": "Gefilterte Beiträge verbergen",
|
||||||
|
"import_followers_from_a_csv_file": "Importiere Follower aus einer CSV-Datei",
|
||||||
|
"import_theme": "Farbschema laden",
|
||||||
|
"inputRadius": "Eingabefelder",
|
||||||
|
"checkboxRadius": "Auswahlfelder",
|
||||||
|
"instance_default": "(Standard: {value})",
|
||||||
|
"instance_default_simple": "(Standard)",
|
||||||
|
"interface": "Oberfläche",
|
||||||
|
"interfaceLanguage": "Sprache der Oberfläche",
|
||||||
|
"invalid_theme_imported": "Die ausgewählte Datei ist kein unterstütztes Pleroma-Theme. Keine Änderungen wurden vorgenommen.",
|
||||||
|
"limited_availability": "In deinem Browser nicht verfügbar",
|
||||||
|
"links": "Links",
|
||||||
|
"lock_account_description": "Sperre deinen Account, um neue Follower zu genehmigen oder abzulehnen",
|
||||||
|
"loop_video": "Videos wiederholen",
|
||||||
|
"loop_video_silent_only": "Nur Videos ohne Ton wiederholen (z.B. Mastodons \"gifs\")",
|
||||||
|
"mutes_tab": "Stummschaltungen",
|
||||||
|
"play_videos_in_modal": "Videos in größerem Medienfenster abspielen",
|
||||||
|
"use_contain_fit": "Vorschaubilder nicht zuschneiden",
|
||||||
|
"name": "Name",
|
||||||
|
"name_bio": "Name & Bio",
|
||||||
|
"new_password": "Neues Passwort",
|
||||||
|
"notification_visibility": "Benachrichtigungstypen, die angezeigt werden sollen",
|
||||||
|
"notification_visibility_follows": "Follows",
|
||||||
|
"notification_visibility_likes": "Favoriten",
|
||||||
|
"notification_visibility_mentions": "Erwähnungen",
|
||||||
|
"notification_visibility_repeats": "Wiederholungen",
|
||||||
|
"no_rich_text_description": "Rich-Text Formatierungen von allen Beiträgen entfernen",
|
||||||
|
"hide_follows_description": "Zeige nicht, wem ich folge",
|
||||||
|
"hide_followers_description": "Zeige nicht, wer mir folgt",
|
||||||
|
"hide_follows_count_description": "Verberge die Anzahl deiner Gefolgten",
|
||||||
|
"hide_followers_count_description": "Verberge die Anzahl deiner Folgenden",
|
||||||
|
"nsfw_clickthrough": "Aktiviere ausblendbares Overlay für Anhänge, die als NSFW markiert sind",
|
||||||
|
"oauth_tokens": "OAuth-Token",
|
||||||
|
"token": "Zeichen",
|
||||||
|
"refresh_token": "Token aktualisieren",
|
||||||
|
"valid_until": "Gültig bis",
|
||||||
|
"revoke_token": "Widerrufen",
|
||||||
|
"panelRadius": "Panel",
|
||||||
|
"pause_on_unfocused": "Streaming pausieren, wenn das Tab nicht fokussiert ist",
|
||||||
|
"presets": "Voreinstellungen",
|
||||||
|
"profile_background": "Profilhintergrund",
|
||||||
|
"profile_banner": "Profilbanner",
|
||||||
|
"profile_tab": "Profil",
|
||||||
|
"radii_help": "Kantenrundung (in Pixel) der Oberfläche anpassen",
|
||||||
|
"replies_in_timeline": "Antworten in der Zeitleiste",
|
||||||
|
"reply_link_preview": "Antwortlink-Vorschau beim Überfahren mit der Maus aktivieren",
|
||||||
|
"reply_visibility_all": "Alle Antworten zeigen",
|
||||||
|
"reply_visibility_following": "Zeige nur Antworten an mich oder an Benutzer, denen ich folge",
|
||||||
|
"reply_visibility_self": "Nur Antworten an mich anzeigen",
|
||||||
|
"autohide_floating_post_button": "Automatisches Verbergen des Knopfs für neue Beiträge (mobil)",
|
||||||
|
"saving_err": "Fehler beim Speichern der Einstellungen",
|
||||||
|
"saving_ok": "Einstellungen gespeichert",
|
||||||
|
"security_tab": "Sicherheit",
|
||||||
|
"scope_copy": "Reichweite beim Antworten übernehmen (Direktnachrichten werden immer kopiert)",
|
||||||
|
"minimal_scopes_mode": "Minimiere Reichweitenoptionen",
|
||||||
|
"set_new_avatar": "Setze einen neuen Avatar",
|
||||||
|
"set_new_profile_background": "Setze einen neuen Hintergrund für dein Profil",
|
||||||
|
"set_new_profile_banner": "Setze einen neuen Banner für dein Profil",
|
||||||
|
"settings": "Einstellungen",
|
||||||
|
"subject_input_always_show": "Betreff-Feld immer anzeigen",
|
||||||
|
"subject_line_behavior": "Betreff beim Antworten kopieren",
|
||||||
|
"subject_line_email": "Wie Email: \"re: Betreff\"",
|
||||||
|
"subject_line_mastodon": "Wie Mastodon: unverändert kopieren",
|
||||||
|
"subject_line_noop": "Nicht kopieren",
|
||||||
|
"post_status_content_type": "Beitragsart",
|
||||||
|
"stop_gifs": "Animationen nur beim Darüberfahren abspielen",
|
||||||
|
"streaming": "Aktiviere automatisches Laden (Streaming) von neuen Beiträgen",
|
||||||
|
"text": "Text",
|
||||||
|
"theme": "Farbschema",
|
||||||
|
"theme_help": "Benutze HTML-Farbcodes (#rrggbb) um dein Farbschema anzupassen",
|
||||||
|
"theme_help_v2_1": "Du kannst auch die Farben und die Deckkraft bestimmter Komponenten überschreiben, indem du das Kontrollkästchen umschaltest. Verwende die Schaltfläche \"Alle löschen\", um alle Überschreibungen zurückzusetzen.",
|
||||||
|
"theme_help_v2_2": "Unter einigen Einträgen befinden sich Symbole für Hintergrund-/Textkontrastindikatoren, für detaillierte Informationen fahre mit der Maus darüber. Bitte beachte, dass bei der Verwendung von Transparenz Kontrastindikatoren den schlechtest möglichen Fall darstellen.",
|
||||||
|
"tooltipRadius": "Tooltips/Warnungen",
|
||||||
|
"user_settings": "Benutzereinstellungen",
|
||||||
|
"values": {
|
||||||
|
"false": "nein",
|
||||||
|
"true": "Ja"
|
||||||
|
},
|
||||||
|
"notifications": "Benachrichtigungen",
|
||||||
|
"enable_web_push_notifications": "Web-Pushbenachrichtigungen aktivieren",
|
||||||
"style": {
|
"style": {
|
||||||
"switcher": {
|
"switcher": {
|
||||||
"keep_color": "Farben beibehalten",
|
"keep_color": "Farben beibehalten",
|
||||||
"keep_shadows": "Schatten beibehalten",
|
"keep_shadows": "Schatten beibehalten",
|
||||||
"keep_opacity": "Deckkraft beibehalten",
|
"keep_opacity": "Deckkraft beibehalten",
|
||||||
"keep_roundness": "Abrundungen beibehalten",
|
"keep_roundness": "Abrundungen beibehalten",
|
||||||
"keep_fonts": "Schriften beibehalten",
|
"keep_fonts": "Schriften beibehalten",
|
||||||
"save_load_hint": "Die \"Beibehalten\"-Optionen behalten die aktuell eingestellten Optionen beim Auswählen oder Laden von Designs bei, sie speichern diese Optionen auch beim Exportieren eines Designs. Wenn alle Kontrollkästchen deaktiviert sind, wird beim Exportieren des Designs alles gespeichert.",
|
"save_load_hint": "Die \"Beibehalten\"-Optionen behalten die aktuell eingestellten Optionen beim Auswählen oder Laden von Designs bei, sie speichern diese Optionen auch beim Exportieren eines Designs. Wenn alle Kontrollkästchen deaktiviert sind, wird beim Exportieren des Designs alles gespeichert.",
|
||||||
"reset": "Zurücksetzen",
|
"reset": "Zurücksetzen",
|
||||||
"clear_all": "Alles leeren",
|
"clear_all": "Alles leeren",
|
||||||
"clear_opacity": "Deckkraft leeren"
|
"clear_opacity": "Deckkraft leeren"
|
||||||
},
|
},
|
||||||
"common": {
|
"common": {
|
||||||
"color": "Farbe",
|
"color": "Farbe",
|
||||||
"opacity": "Deckkraft",
|
"opacity": "Deckkraft",
|
||||||
"contrast": {
|
"contrast": {
|
||||||
"hint": "Das Kontrastverhältnis ist {ratio}, es {level} {context}",
|
"hint": "Das Kontrastverhältnis ist {ratio}, es {level} {context}",
|
||||||
"level": {
|
"level": {
|
||||||
"aa": "entspricht Level AA Richtlinie (minimum)",
|
"aa": "entspricht Level AA Richtlinie (minimum)",
|
||||||
"aaa": "entspricht Level AAA Richtlinie (empfohlen)",
|
"aaa": "entspricht Level AAA Richtlinie (empfohlen)",
|
||||||
"bad": "entspricht keiner Richtlinien zur Barrierefreiheit"
|
"bad": "entspricht keiner Richtlinien zur Barrierefreiheit"
|
||||||
},
|
},
|
||||||
"context": {
|
"context": {
|
||||||
"18pt": "für großen (18pt+) Text",
|
"18pt": "für großen (18pt+) Text",
|
||||||
"text": "für Text"
|
"text": "für Text"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common_colors": {
|
"common_colors": {
|
||||||
"_tab_label": "Allgemein",
|
"_tab_label": "Allgemein",
|
||||||
"main": "Allgemeine Farben",
|
"main": "Allgemeine Farben",
|
||||||
"foreground_hint": "Siehe Reiter \"Erweitert\" für eine detailliertere Einstellungen",
|
"foreground_hint": "Siehe Reiter \"Erweitert\" für eine detailliertere Einstellungen",
|
||||||
"rgbo": "Symbole, Betonungen, Kennzeichnungen"
|
"rgbo": "Symbole, Betonungen, Kennzeichnungen"
|
||||||
},
|
},
|
||||||
"advanced_colors": {
|
"advanced_colors": {
|
||||||
"_tab_label": "Erweitert",
|
"_tab_label": "Erweitert",
|
||||||
"alert": "Warnhinweis-Hintergrund",
|
"alert": "Warnhinweis-Hintergrund",
|
||||||
"alert_error": "Fehler",
|
"alert_error": "Fehler",
|
||||||
"badge": "Kennzeichnungs-Hintergrund",
|
"badge": "Kennzeichnungs-Hintergrund",
|
||||||
"badge_notification": "Benachrichtigung",
|
"badge_notification": "Benachrichtigung",
|
||||||
"panel_header": "Panel-Kopf",
|
"panel_header": "Panel-Kopf",
|
||||||
"top_bar": "Obere Leiste",
|
"top_bar": "Obere Leiste",
|
||||||
"borders": "Rahmen",
|
"borders": "Rahmen",
|
||||||
"buttons": "Schaltflächen",
|
"buttons": "Schaltflächen",
|
||||||
"inputs": "Eingabefelder",
|
"inputs": "Eingabefelder",
|
||||||
"faint_text": "Verblasster Text"
|
"faint_text": "Verblasster Text"
|
||||||
},
|
},
|
||||||
"radii": {
|
"radii": {
|
||||||
"_tab_label": "Abrundungen"
|
"_tab_label": "Abrundungen"
|
||||||
},
|
},
|
||||||
"shadows": {
|
"shadows": {
|
||||||
"_tab_label": "Schatten und Beleuchtung",
|
"_tab_label": "Schatten und Beleuchtung",
|
||||||
"component": "Komponente",
|
"component": "Komponente",
|
||||||
"override": "Überschreiben",
|
"override": "Überschreiben",
|
||||||
"shadow_id": "Schatten #{value}",
|
"shadow_id": "Schatten #{value}",
|
||||||
"blur": "Unschärfe",
|
"blur": "Unschärfe",
|
||||||
"spread": "Streuung",
|
"spread": "Streuung",
|
||||||
"inset": "Einsatz",
|
"inset": "Einsatz",
|
||||||
"hint": "Für Schatten kannst du auch --variable als Farbwert verwenden, um CSS3-Variablen zu verwenden. Bitte beachte, dass die Einstellung der Deckkraft in diesem Fall nicht funktioniert.",
|
"hint": "Für Schatten kannst du auch --variable als Farbwert verwenden, um CSS3-Variablen zu verwenden. Bitte beachte, dass die Einstellung der Deckkraft in diesem Fall nicht funktioniert.",
|
||||||
"filter_hint": {
|
"filter_hint": {
|
||||||
"always_drop_shadow": "Achtung, dieser Schatten verwendet immer {0}, wenn der Browser dies unterstützt.",
|
"always_drop_shadow": "Achtung, dieser Schatten verwendet immer {0}, wenn der Browser dies unterstützt.",
|
||||||
"drop_shadow_syntax": "{0} unterstützt Parameter {1} und Schlüsselwort {2} nicht.",
|
"drop_shadow_syntax": "{0} unterstützt Parameter {1} und Schlüsselwort {2} nicht.",
|
||||||
"avatar_inset": "Bitte beachte, dass die Kombination von eingesetzten und nicht eingesetzten Schatten auf Avataren zu unerwarteten Ergebnissen bei transparenten Avataren führen kann.",
|
"avatar_inset": "Bitte beachte, dass die Kombination von eingesetzten und nicht eingesetzten Schatten auf Avataren zu unerwarteten Ergebnissen bei transparenten Avataren führen kann.",
|
||||||
"spread_zero": "Schatten mit einer Streuung > 0 erscheinen so, als ob sie auf Null gesetzt wären.",
|
"spread_zero": "Schatten mit einer Streuung > 0 erscheinen so, als ob sie auf Null gesetzt wären.",
|
||||||
"inset_classic": "Eingesetzte Schatten werden mit {0} verwendet"
|
"inset_classic": "Eingesetzte Schatten werden mit {0} verwendet"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"panel": "Panel",
|
||||||
|
"panelHeader": "Panel-Kopf",
|
||||||
|
"topBar": "Obere Leiste",
|
||||||
|
"avatar": "Benutzer-Avatar (in der Profilansicht)",
|
||||||
|
"avatarStatus": "Benutzer-Avatar (in der Beitragsanzeige)",
|
||||||
|
"popup": "Dialogfenster und Hinweistexte",
|
||||||
|
"button": "Schaltfläche",
|
||||||
|
"buttonHover": "Schaltfläche (hover)",
|
||||||
|
"buttonPressed": "Schaltfläche (gedrückt)",
|
||||||
|
"buttonPressedHover": "Schaltfläche (gedrückt+hover)",
|
||||||
|
"input": "Input field"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fonts": {
|
||||||
|
"_tab_label": "Schriften",
|
||||||
|
"help": "Wähl die Schriftart, die für Elemente der Benutzeroberfläche verwendet werden soll. Für \" Benutzerdefiniert\" musst du den genauen Schriftnamen eingeben, wie er im System angezeigt wird.",
|
||||||
|
"components": {
|
||||||
|
"interface": "Oberfläche",
|
||||||
|
"input": "Eingabefelder",
|
||||||
|
"post": "Beitragstext",
|
||||||
|
"postCode": "Dicktengleicher Text in einem Beitrag (Rich-Text)"
|
||||||
|
},
|
||||||
|
"family": "Schriftname",
|
||||||
|
"size": "Größe (in px)",
|
||||||
|
"weight": "Gewicht (Dicke)",
|
||||||
|
"custom": "Benutzerdefiniert"
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"header": "Vorschau",
|
||||||
|
"content": "Inhalt",
|
||||||
|
"error": "Beispielfehler",
|
||||||
|
"button": "Schaltfläche",
|
||||||
|
"text": "Ein Haufen mehr von {0} und {1}",
|
||||||
|
"mono": "Inhalt",
|
||||||
|
"input": "Sitze gerade im Hofbräuhaus.",
|
||||||
|
"faint_link": "Hilfreiche Anleitung",
|
||||||
|
"fine_print": "Lies unser {0}, um nichts Nützliches zu lernen!",
|
||||||
|
"header_faint": "Das ist in Ordnung",
|
||||||
|
"checkbox": "Ich habe die Allgemeinen Geschäftsbedingungen überflogen",
|
||||||
|
"link": "ein netter kleiner Link"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"components": {
|
"app_name": "Anwendungsname",
|
||||||
"panel": "Panel",
|
"mfa": {
|
||||||
"panelHeader": "Panel-Kopf",
|
"otp": "OTP",
|
||||||
"topBar": "Obere Leiste",
|
"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.",
|
||||||
"avatar": "Benutzer-Avatar (in der Profilansicht)",
|
"recovery_codes": "Wiederherstellungs-Codes.",
|
||||||
"avatarStatus": "Benutzer-Avatar (in der Beitragsanzeige)",
|
"warning_of_generate_new_codes": "Wenn du neue Wiederherstellungs-Codes generierst, werden die alten Codes nicht mehr funktionieren.",
|
||||||
"popup": "Dialogfenster und Hinweistexte",
|
"generate_new_recovery_codes": "Generiere neue Wiederherstellungs-Codes",
|
||||||
"button": "Schaltfläche",
|
"title": "Zwei-Faktor Authentifizierung",
|
||||||
"buttonHover": "Schaltfläche (hover)",
|
"waiting_a_recovery_codes": "Erhalte Wiederherstellungscodes...",
|
||||||
"buttonPressed": "Schaltfläche (gedrückt)",
|
"authentication_methods": "Authentifizierungsmethoden",
|
||||||
"buttonPressedHover": "Schaltfläche (gedrückt+hover)",
|
"scan": {
|
||||||
"input": "Input field"
|
"title": "Scan",
|
||||||
}
|
"secret_code": "Schlüssel",
|
||||||
},
|
"desc": "Wenn du deine 2FA App verwendest, scanne diesen QR Code oder gebe den Schlüssel ein:"
|
||||||
"fonts": {
|
},
|
||||||
"_tab_label": "Schriften",
|
"verify": {
|
||||||
"help": "Wähl die Schriftart, die für Elemente der Benutzeroberfläche verwendet werden soll. Für \" Benutzerdefiniert\" musst du den genauen Schriftnamen eingeben, wie er im System angezeigt wird.",
|
"desc": "Um 2FA zu aktivieren, gib den Code von deiner 2FA-App ein:"
|
||||||
"components": {
|
}
|
||||||
"interface": "Oberfläche",
|
|
||||||
"input": "Eingabefelder",
|
|
||||||
"post": "Beitragstext",
|
|
||||||
"postCode": "Dicktengleicher Text in einem Beitrag (Rich-Text)"
|
|
||||||
},
|
},
|
||||||
"family": "Schriftname",
|
"enter_current_password_to_confirm": "Gib dein aktuelles Passwort ein, um deine Identität zu bestätigen",
|
||||||
"size": "Größe (in px)",
|
"security": "Sicherheit",
|
||||||
"weight": "Gewicht (Dicke)",
|
"allow_following_move": "Erlaube automatisches Folgen, sobald ein gefolgter Nutzer umzieht",
|
||||||
"custom": "Benutzerdefiniert"
|
"blocks_imported": "Blocks importiert! Die Verarbeitung wird einen Moment brauchen.",
|
||||||
},
|
"block_import_error": "Fehler beim Importieren der Blocks",
|
||||||
"preview": {
|
"block_import": "Block Import",
|
||||||
"header": "Vorschau",
|
"block_export_button": "Exportiere deine Blocks in eine csv Datei",
|
||||||
"content": "Inhalt",
|
"block_export": "Block Export",
|
||||||
"error": "Beispielfehler",
|
"emoji_reactions_on_timeline": "Zeige Emoji-Reaktionen auf der Zeitleiste",
|
||||||
"button": "Schaltfläche",
|
"domain_mutes": "Domains",
|
||||||
"text": "Ein Haufen mehr von {0} und {1}",
|
"changed_email": "Email Adresse erfolgreich geändert!",
|
||||||
"mono": "Inhalt",
|
"change_email_error": "Es trat ein Problem auf beim Versuch, deine Email Adresse zu ändern.",
|
||||||
"input": "Sitze gerade im Hofbräuhaus.",
|
"change_email": "Ändere Email",
|
||||||
"faint_link": "Hilfreiche Anleitung",
|
"notification_setting_non_followers": "Nutzer, die dir nicht folgen",
|
||||||
"fine_print": "Lies unser {0}, um nichts Nützliches zu lernen!",
|
"notification_setting_followers": "Nutzer, die dir folgen",
|
||||||
"header_faint": "Das ist in Ordnung",
|
"import_blocks_from_a_csv_file": "Importiere Blocks von einer CSV Datei",
|
||||||
"checkbox": "Ich habe die Allgemeinen Geschäftsbedingungen überflogen",
|
"accent": "Akzent"
|
||||||
"link": "ein netter kleiner Link"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"timeline": {
|
|
||||||
"collapse": "Einklappen",
|
|
||||||
"conversation": "Unterhaltung",
|
|
||||||
"error_fetching": "Fehler beim Laden",
|
|
||||||
"load_older": "Lade ältere Beiträge",
|
|
||||||
"no_retweet_hint": "Der Beitrag ist als nur-für-Follower oder als Direktnachricht markiert und kann nicht wiederholt werden.",
|
|
||||||
"repeated": "wiederholte",
|
|
||||||
"show_new": "Zeige Neuere",
|
|
||||||
"up_to_date": "Aktuell"
|
|
||||||
},
|
|
||||||
"user_card": {
|
|
||||||
"approve": "Genehmigen",
|
|
||||||
"block": "Blockieren",
|
|
||||||
"blocked": "Blockiert!",
|
|
||||||
"deny": "Ablehnen",
|
|
||||||
"follow": "Folgen",
|
|
||||||
"follow_sent": "Anfrage gesendet!",
|
|
||||||
"follow_progress": "Anfragen…",
|
|
||||||
"follow_again": "Anfrage erneut senden?",
|
|
||||||
"follow_unfollow": "Folgen beenden",
|
|
||||||
"followees": "Folgt",
|
|
||||||
"followers": "Followers",
|
|
||||||
"following": "Folgst du!",
|
|
||||||
"follows_you": "Folgt dir!",
|
|
||||||
"its_you": "Das bist du!",
|
|
||||||
"mute": "Stummschalten",
|
|
||||||
"muted": "Stummgeschaltet",
|
|
||||||
"per_day": "pro Tag",
|
|
||||||
"remote_follow": "Folgen",
|
|
||||||
"statuses": "Beiträge"
|
|
||||||
},
|
|
||||||
"user_profile": {
|
|
||||||
"timeline_title": "Beiträge"
|
|
||||||
},
|
|
||||||
"who_to_follow": {
|
|
||||||
"more": "Mehr",
|
|
||||||
"who_to_follow": "Wem soll ich folgen"
|
|
||||||
},
|
|
||||||
"tool_tip": {
|
|
||||||
"media_upload": "Medien hochladen",
|
|
||||||
"repeat": "Wiederholen",
|
|
||||||
"reply": "Antworten",
|
|
||||||
"favorite": "Favorisieren",
|
|
||||||
"user_settings": "Benutzereinstellungen"
|
|
||||||
},
|
|
||||||
"upload":{
|
|
||||||
"error": {
|
|
||||||
"base": "Hochladen fehlgeschlagen.",
|
|
||||||
"file_too_big": "Datei ist zu groß [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
|
|
||||||
"default": "Bitte versuche es später erneut"
|
|
||||||
},
|
},
|
||||||
"file_size_units": {
|
"timeline": {
|
||||||
"B": "B",
|
"collapse": "Einklappen",
|
||||||
"KiB": "KiB",
|
"conversation": "Unterhaltung",
|
||||||
"MiB": "MiB",
|
"error_fetching": "Fehler beim Laden",
|
||||||
"GiB": "GiB",
|
"load_older": "Lade ältere Beiträge",
|
||||||
"TiB": "TiB"
|
"no_retweet_hint": "Der Beitrag ist als nur-für-Follower oder als Direktnachricht markiert und kann nicht wiederholt werden.",
|
||||||
|
"repeated": "wiederholte",
|
||||||
|
"show_new": "Zeige Neuere",
|
||||||
|
"up_to_date": "Aktuell"
|
||||||
|
},
|
||||||
|
"user_card": {
|
||||||
|
"approve": "Genehmigen",
|
||||||
|
"block": "Blockieren",
|
||||||
|
"blocked": "Blockiert!",
|
||||||
|
"deny": "Ablehnen",
|
||||||
|
"follow": "Folgen",
|
||||||
|
"follow_sent": "Anfrage gesendet!",
|
||||||
|
"follow_progress": "Anfragen…",
|
||||||
|
"follow_again": "Anfrage erneut senden?",
|
||||||
|
"follow_unfollow": "Folgen beenden",
|
||||||
|
"followees": "Folgt",
|
||||||
|
"followers": "Folgende",
|
||||||
|
"following": "Folgst du!",
|
||||||
|
"follows_you": "Folgt dir!",
|
||||||
|
"its_you": "Das bist du!",
|
||||||
|
"mute": "Stummschalten",
|
||||||
|
"muted": "Stummgeschaltet",
|
||||||
|
"per_day": "pro Tag",
|
||||||
|
"remote_follow": "Folgen",
|
||||||
|
"statuses": "Beiträge",
|
||||||
|
"admin_menu": {
|
||||||
|
"sandbox": "Erzwinge Beiträge nur für Follower sichtbar zu sein"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user_profile": {
|
||||||
|
"timeline_title": "Beiträge"
|
||||||
|
},
|
||||||
|
"who_to_follow": {
|
||||||
|
"more": "Mehr",
|
||||||
|
"who_to_follow": "Wem soll ich folgen"
|
||||||
|
},
|
||||||
|
"tool_tip": {
|
||||||
|
"media_upload": "Medien hochladen",
|
||||||
|
"repeat": "Wiederholen",
|
||||||
|
"reply": "Antworten",
|
||||||
|
"favorite": "Favorisieren",
|
||||||
|
"user_settings": "Benutzereinstellungen"
|
||||||
|
},
|
||||||
|
"upload": {
|
||||||
|
"error": {
|
||||||
|
"base": "Hochladen fehlgeschlagen.",
|
||||||
|
"file_too_big": "Datei ist zu groß [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]",
|
||||||
|
"default": "Bitte versuche es später erneut"
|
||||||
|
},
|
||||||
|
"file_size_units": {
|
||||||
|
"B": "B",
|
||||||
|
"KiB": "KiB",
|
||||||
|
"MiB": "MiB",
|
||||||
|
"GiB": "GiB",
|
||||||
|
"TiB": "TiB"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"people": "Leute",
|
||||||
|
"hashtags": "Hashtags",
|
||||||
|
"person_talking": "{count} Person spricht darüber",
|
||||||
|
"people_talking": "{count} Leute sprechen darüber",
|
||||||
|
"no_results": "Keine Ergebnisse"
|
||||||
|
},
|
||||||
|
"password_reset": {
|
||||||
|
"forgot_password": "Passwort vergessen?",
|
||||||
|
"password_reset": "Password zurücksetzen",
|
||||||
|
"instruction": "Wenn du hier deinen Benutznamen oder die zugehörige E-Mail-Adresse eingibst, kann dir der Server einen Link zum Passwortzurücksetzen zuschicken.",
|
||||||
|
"placeholder": "Dein Benutzername oder die zugehörige E-Mail-Adresse",
|
||||||
|
"check_email": "Im E-Mail-Posteingang des angebenen Kontos müsste sich jetzt (oder zumindest in Kürze) die E-Mail mit dem Link zum Passwortzurücksetzen befinden.",
|
||||||
|
"return_home": "Zurück zur Heimseite",
|
||||||
|
"not_found": "Benutzername/E-Mail-Adresse nicht gefunden. Vertippt?",
|
||||||
|
"too_many_requests": "Kurze Pause. Zu viele Versuche. Bitte, später nochmal probieren.",
|
||||||
|
"password_reset_disabled": "Passwortzurücksetzen deaktiviert. Bitte Administrator kontaktieren.",
|
||||||
|
"password_reset_required": "Passwortzurücksetzen erforderlich",
|
||||||
|
"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."
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"search": {
|
|
||||||
"people": "Leute",
|
|
||||||
"hashtags": "Hashtags",
|
|
||||||
"person_talking": "{count} Person spricht darüber",
|
|
||||||
"people_talking": "{count} Leute sprechen darüber",
|
|
||||||
"no_results": "Keine Ergebnisse"
|
|
||||||
},
|
|
||||||
"password_reset": {
|
|
||||||
"forgot_password": "Passwort vergessen?",
|
|
||||||
"password_reset": "Password zurücksetzen",
|
|
||||||
"instruction": "Wenn du hier deinen Benutznamen oder die zugehörige E-Mail-Adresse eingibst, kann dir der Server einen Link zum Passwortzurücksetzen zuschicken.",
|
|
||||||
"placeholder": "Dein Benutzername oder die zugehörige E-Mail-Adresse",
|
|
||||||
"check_email": "Im E-Mail-Posteingang des angebenen Kontos müsste sich jetzt (oder zumindest in Kürze) die E-Mail mit dem Link zum Passwortzurücksetzen befinden.",
|
|
||||||
"return_home": "Zurück zur Heimseite",
|
|
||||||
"not_found": "Benutzername/E-Mail-Adresse nicht gefunden. Vertippt?",
|
|
||||||
"too_many_requests": "Kurze Pause. Zu viele Versuche. Bitte, später nochmal probieren.",
|
|
||||||
"password_reset_disabled": "Passwortzurücksetzen deaktiviert. Bitte Administrator kontaktieren.",
|
|
||||||
"password_reset_required": "Passwortzurücksetzen erforderlich",
|
|
||||||
"password_reset_required_but_mailer_is_disabled": "Passwortzurücksetzen wäre erforderlich, ist aber deaktiviert. Bitte Administrator kontaktieren."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
1080
src/i18n/fi.json
1080
src/i18n/fi.json
File diff suppressed because it is too large
Load diff
215
src/i18n/fr.json
215
src/i18n/fr.json
|
@ -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 l’avoir 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 l’administration 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."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
521
src/i18n/it.json
521
src/i18n/it.json
|
@ -1,206 +1,323 @@
|
||||||
{
|
{
|
||||||
"general": {
|
"general": {
|
||||||
"submit": "Invia",
|
"submit": "Invia",
|
||||||
"apply": "Applica"
|
"apply": "Applica",
|
||||||
},
|
"more": "Altro",
|
||||||
"nav": {
|
"generic_error": "Errore",
|
||||||
"mentions": "Menzioni",
|
"optional": "facoltativo",
|
||||||
"public_tl": "Sequenza temporale pubblica",
|
"show_more": "Mostra tutto",
|
||||||
"timeline": "Sequenza temporale",
|
"show_less": "Ripiega",
|
||||||
"twkn": "L'intera rete conosciuta",
|
"dismiss": "Chiudi",
|
||||||
"chat": "Chat Locale",
|
"cancel": "Annulla",
|
||||||
"friend_requests": "Richieste di Seguirti"
|
"disable": "Disabilita",
|
||||||
},
|
"enable": "Abilita",
|
||||||
"notifications": {
|
"confirm": "Conferma",
|
||||||
"followed_you": "ti segue",
|
"verify": "Verifica"
|
||||||
"notifications": "Notifiche",
|
|
||||||
"read": "Leggi!",
|
|
||||||
"broken_favorite": "Stato sconosciuto, lo sto cercando...",
|
|
||||||
"favorited_you": "ha messo mi piace al tuo stato",
|
|
||||||
"load_older": "Carica notifiche più vecchie",
|
|
||||||
"repeated_you": "ha condiviso il tuo stato"
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"attachments": "Allegati",
|
|
||||||
"autoload": "Abilita caricamento automatico quando si raggiunge fondo pagina",
|
|
||||||
"avatar": "Avatar",
|
|
||||||
"bio": "Introduzione",
|
|
||||||
"current_avatar": "Il tuo avatar attuale",
|
|
||||||
"current_profile_banner": "Il tuo banner attuale",
|
|
||||||
"filtering": "Filtri",
|
|
||||||
"filtering_explanation": "Tutti i post contenenti queste parole saranno silenziati, uno per linea",
|
|
||||||
"hide_attachments_in_convo": "Nascondi gli allegati presenti nelle conversazioni",
|
|
||||||
"hide_attachments_in_tl": "Nascondi gli allegati presenti nella sequenza temporale",
|
|
||||||
"name": "Nome",
|
|
||||||
"name_bio": "Nome & Introduzione",
|
|
||||||
"nsfw_clickthrough": "Abilita il click per visualizzare gli allegati segnati come NSFW",
|
|
||||||
"profile_background": "Sfondo della tua pagina",
|
|
||||||
"profile_banner": "Banner del tuo profilo",
|
|
||||||
"reply_link_preview": "Abilita il link per la risposta al passaggio del mouse",
|
|
||||||
"set_new_avatar": "Scegli un nuovo avatar",
|
|
||||||
"set_new_profile_background": "Scegli un nuovo sfondo per la tua pagina",
|
|
||||||
"set_new_profile_banner": "Scegli un nuovo banner per il tuo profilo",
|
|
||||||
"settings": "Impostazioni",
|
|
||||||
"theme": "Tema",
|
|
||||||
"user_settings": "Impostazioni Utente",
|
|
||||||
"attachmentRadius": "Allegati",
|
|
||||||
"avatarAltRadius": "Avatar (Notifiche)",
|
|
||||||
"avatarRadius": "Avatar",
|
|
||||||
"background": "Sfondo",
|
|
||||||
"btnRadius": "Pulsanti",
|
|
||||||
"cBlue": "Blu (Rispondere, seguire)",
|
|
||||||
"cGreen": "Verde (Condividi)",
|
|
||||||
"cOrange": "Arancio (Mi piace)",
|
|
||||||
"cRed": "Rosso (Annulla)",
|
|
||||||
"change_password": "Cambia Password",
|
|
||||||
"change_password_error": "C'è stato un problema durante il cambiamento della password.",
|
|
||||||
"changed_password": "Password cambiata correttamente!",
|
|
||||||
"collapse_subject": "Riduci post che hanno un oggetto",
|
|
||||||
"confirm_new_password": "Conferma la nuova password",
|
|
||||||
"current_password": "Password attuale",
|
|
||||||
"data_import_export_tab": "Importa / Esporta Dati",
|
|
||||||
"default_vis": "Visibilità predefinita dei post",
|
|
||||||
"delete_account": "Elimina Account",
|
|
||||||
"delete_account_description": "Elimina definitivamente il tuo account e tutti i tuoi messaggi.",
|
|
||||||
"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_instructions": "Digita la tua password nel campo sottostante per confermare l'eliminazione dell'account.",
|
|
||||||
"export_theme": "Salva settaggi",
|
|
||||||
"follow_export": "Esporta la lista di chi segui",
|
|
||||||
"follow_export_button": "Esporta la lista di chi segui in un file csv",
|
|
||||||
"follow_export_processing": "Sto elaborando, presto ti sarà chiesto di scaricare il tuo file",
|
|
||||||
"follow_import": "Importa la lista di chi segui",
|
|
||||||
"follow_import_error": "Errore nell'importazione della lista di chi segui",
|
|
||||||
"follows_imported": "Importazione riuscita! L'elaborazione richiederà un po' di tempo.",
|
|
||||||
"foreground": "In primo piano",
|
|
||||||
"general": "Generale",
|
|
||||||
"hide_post_stats": "Nascondi statistiche dei post (es. il numero di mi piace)",
|
|
||||||
"hide_user_stats": "Nascondi statistiche dell'utente (es. il numero di chi ti segue)",
|
|
||||||
"import_followers_from_a_csv_file": "Importa una lista di chi segui da un file csv",
|
|
||||||
"import_theme": "Carica settaggi",
|
|
||||||
"inputRadius": "Campi di testo",
|
|
||||||
"instance_default": "(predefinito: {value})",
|
|
||||||
"interfaceLanguage": "Linguaggio dell'interfaccia",
|
|
||||||
"invalid_theme_imported": "Il file selezionato non è un file di tema per Pleroma supportato. Il tuo tema non è stato modificato.",
|
|
||||||
"limited_availability": "Non disponibile nel tuo browser",
|
|
||||||
"links": "Collegamenti",
|
|
||||||
"lock_account_description": "Limita il tuo account solo per contatti approvati",
|
|
||||||
"loop_video": "Riproduci video in ciclo continuo",
|
|
||||||
"loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le gif di Mastodon)",
|
|
||||||
"new_password": "Nuova password",
|
|
||||||
"notification_visibility": "Tipi di notifiche da mostrare",
|
|
||||||
"notification_visibility_follows": "Nuove persone ti seguono",
|
|
||||||
"notification_visibility_likes": "Mi piace",
|
|
||||||
"notification_visibility_mentions": "Menzioni",
|
|
||||||
"notification_visibility_repeats": "Condivisioni",
|
|
||||||
"no_rich_text_description": "Togli la formattazione del testo da tutti i post",
|
|
||||||
"oauth_tokens": "Token OAuth",
|
|
||||||
"token": "Token",
|
|
||||||
"refresh_token": "Aggiorna token",
|
|
||||||
"valid_until": "Valido fino a",
|
|
||||||
"revoke_token": "Revocare",
|
|
||||||
"panelRadius": "Pannelli",
|
|
||||||
"pause_on_unfocused": "Metti in pausa l'aggiornamento continuo quando la scheda non è in primo piano",
|
|
||||||
"presets": "Valori predefiniti",
|
|
||||||
"profile_tab": "Profilo",
|
|
||||||
"radii_help": "Imposta l'arrotondamento dei bordi (in pixel)",
|
|
||||||
"replies_in_timeline": "Risposte nella sequenza temporale",
|
|
||||||
"reply_visibility_all": "Mostra tutte le risposte",
|
|
||||||
"reply_visibility_following": "Mostra solo le risposte dirette a me o agli utenti che seguo",
|
|
||||||
"reply_visibility_self": "Mostra solo risposte dirette a me",
|
|
||||||
"saving_err": "Errore nel salvataggio delle impostazioni",
|
|
||||||
"saving_ok": "Impostazioni salvate",
|
|
||||||
"security_tab": "Sicurezza",
|
|
||||||
"stop_gifs": "Riproduci GIF al passaggio del cursore del mouse",
|
|
||||||
"streaming": "Abilita aggiornamento automatico dei nuovi post quando si è in alto alla pagina",
|
|
||||||
"text": "Testo",
|
|
||||||
"theme_help": "Usa codici colore esadecimali (#rrggbb) per personalizzare il tuo schema di colori.",
|
|
||||||
"tooltipRadius": "Descrizioni/avvisi",
|
|
||||||
"values": {
|
|
||||||
"false": "no",
|
|
||||||
"true": "si"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"timeline": {
|
|
||||||
"error_fetching": "Errore nel prelievo aggiornamenti",
|
|
||||||
"load_older": "Carica messaggi più vecchi",
|
|
||||||
"show_new": "Mostra nuovi",
|
|
||||||
"up_to_date": "Aggiornato",
|
|
||||||
"collapse": "Riduci",
|
|
||||||
"conversation": "Conversazione",
|
|
||||||
"no_retweet_hint": "La visibilità del post è impostata solo per chi ti segue o messaggio diretto e non può essere condiviso",
|
|
||||||
"repeated": "condiviso"
|
|
||||||
},
|
|
||||||
"user_card": {
|
|
||||||
"follow": "Segui",
|
|
||||||
"followees": "Chi stai seguendo",
|
|
||||||
"followers": "Chi ti segue",
|
|
||||||
"following": "Lo stai seguendo!",
|
|
||||||
"follows_you": "Ti segue!",
|
|
||||||
"mute": "Silenzia",
|
|
||||||
"muted": "Silenziato",
|
|
||||||
"per_day": "al giorno",
|
|
||||||
"statuses": "Messaggi",
|
|
||||||
"approve": "Approva",
|
|
||||||
"block": "Blocca",
|
|
||||||
"blocked": "Bloccato!",
|
|
||||||
"deny": "Nega",
|
|
||||||
"remote_follow": "Segui da remoto"
|
|
||||||
},
|
|
||||||
"chat": {
|
|
||||||
"title": "Chat"
|
|
||||||
},
|
|
||||||
"features_panel": {
|
|
||||||
"chat": "Chat",
|
|
||||||
"gopher": "Gopher",
|
|
||||||
"media_proxy": "Media proxy",
|
|
||||||
"scope_options": "Opzioni di visibilità",
|
|
||||||
"text_limit": "Lunghezza limite",
|
|
||||||
"title": "Caratteristiche",
|
|
||||||
"who_to_follow": "Chi seguire"
|
|
||||||
},
|
|
||||||
"finder": {
|
|
||||||
"error_fetching_user": "Errore nel recupero dell'utente",
|
|
||||||
"find_user": "Trova utente"
|
|
||||||
},
|
|
||||||
"login": {
|
|
||||||
"login": "Accedi",
|
|
||||||
"logout": "Disconnettiti",
|
|
||||||
"password": "Password",
|
|
||||||
"placeholder": "es. lain",
|
|
||||||
"register": "Registrati",
|
|
||||||
"username": "Nome utente"
|
|
||||||
},
|
|
||||||
"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_link": "bloccato",
|
|
||||||
"attachments_sensitive": "Segna allegati come sensibili",
|
|
||||||
"content_type": {
|
|
||||||
"text/plain": "Testo normale"
|
|
||||||
},
|
},
|
||||||
"content_warning": "Oggetto (facoltativo)",
|
"nav": {
|
||||||
"default": "Appena atterrato in L.A.",
|
"mentions": "Menzioni",
|
||||||
"direct_warning": "Questo post sarà visibile solo dagli utenti menzionati.",
|
"public_tl": "Sequenza pubblica",
|
||||||
"posting": "Pubblica",
|
"timeline": "Sequenza personale",
|
||||||
"scope": {
|
"twkn": "Sequenza globale",
|
||||||
"direct": "Diretto - Pubblicato solo per gli utenti menzionati",
|
"chat": "Chat della stanza",
|
||||||
"private": "Solo per chi ti segue - Visibile solo da chi ti segue",
|
"friend_requests": "Vogliono seguirti",
|
||||||
"public": "Pubblico - Visibile sulla sequenza temporale pubblica",
|
"about": "Informazioni",
|
||||||
"unlisted": "Non elencato - Non visibile sulla sequenza temporale pubblica"
|
"administration": "Amministrazione",
|
||||||
|
"back": "Indietro",
|
||||||
|
"interactions": "Interazioni",
|
||||||
|
"dms": "Messaggi diretti",
|
||||||
|
"user_search": "Ricerca utenti",
|
||||||
|
"search": "Ricerca",
|
||||||
|
"who_to_follow": "Chi seguire",
|
||||||
|
"preferences": "Preferenze"
|
||||||
|
},
|
||||||
|
"notifications": {
|
||||||
|
"followed_you": "ti segue",
|
||||||
|
"notifications": "Notifiche",
|
||||||
|
"read": "Letto!",
|
||||||
|
"broken_favorite": "Stato sconosciuto, lo sto cercando...",
|
||||||
|
"favorited_you": "ha gradito il tuo messaggio",
|
||||||
|
"load_older": "Carica notifiche precedenti",
|
||||||
|
"repeated_you": "ha condiviso il tuo messaggio",
|
||||||
|
"follow_request": "vuole seguirti",
|
||||||
|
"no_more_notifications": "Fine delle notifiche",
|
||||||
|
"migrated_to": "è migrato verso",
|
||||||
|
"reacted_with": "ha reagito con"
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"attachments": "Allegati",
|
||||||
|
"autoload": "Abilita caricamento automatico quando raggiungi il fondo pagina",
|
||||||
|
"avatar": "Icona utente",
|
||||||
|
"bio": "Introduzione",
|
||||||
|
"current_avatar": "La tua icona attuale",
|
||||||
|
"current_profile_banner": "Il tuo stendardo attuale",
|
||||||
|
"filtering": "Filtri",
|
||||||
|
"filtering_explanation": "Tutti i post contenenti queste parole saranno silenziati, una per riga",
|
||||||
|
"hide_attachments_in_convo": "Nascondi gli allegati presenti nelle conversazioni",
|
||||||
|
"hide_attachments_in_tl": "Nascondi gli allegati presenti nelle sequenze",
|
||||||
|
"name": "Nome",
|
||||||
|
"name_bio": "Nome ed introduzione",
|
||||||
|
"nsfw_clickthrough": "Fai click per visualizzare gli allegati nascosti",
|
||||||
|
"profile_background": "Sfondo della tua pagina",
|
||||||
|
"profile_banner": "Stendardo del tuo profilo",
|
||||||
|
"reply_link_preview": "Visualizza le risposte al passaggio del cursore",
|
||||||
|
"set_new_avatar": "Scegli una nuova icona",
|
||||||
|
"set_new_profile_background": "Scegli un nuovo sfondo per la tua pagina",
|
||||||
|
"set_new_profile_banner": "Scegli un nuovo stendardo per il tuo profilo",
|
||||||
|
"settings": "Impostazioni",
|
||||||
|
"theme": "Tema",
|
||||||
|
"user_settings": "Impostazioni Utente",
|
||||||
|
"attachmentRadius": "Allegati",
|
||||||
|
"avatarAltRadius": "Icone utente (Notifiche)",
|
||||||
|
"avatarRadius": "Icone utente",
|
||||||
|
"background": "Sfondo",
|
||||||
|
"btnRadius": "Pulsanti",
|
||||||
|
"cBlue": "Blu (risposte, seguire)",
|
||||||
|
"cGreen": "Verde (ripeti)",
|
||||||
|
"cOrange": "Arancione (gradire)",
|
||||||
|
"cRed": "Rosso (annulla)",
|
||||||
|
"change_password": "Cambia password",
|
||||||
|
"change_password_error": "C'è stato un problema durante il cambiamento della password.",
|
||||||
|
"changed_password": "Password cambiata correttamente!",
|
||||||
|
"collapse_subject": "Ripiega messaggi con Oggetto",
|
||||||
|
"confirm_new_password": "Conferma la nuova password",
|
||||||
|
"current_password": "La tua password attuale",
|
||||||
|
"data_import_export_tab": "Importa o esporta dati",
|
||||||
|
"default_vis": "Visibilità predefinita dei messaggi",
|
||||||
|
"delete_account": "Elimina profilo",
|
||||||
|
"delete_account_description": "Elimina definitivamente i tuoi dati e disattiva il tuo profilo.",
|
||||||
|
"delete_account_error": "C'è stato un problema durante l'eliminazione del tuo profilo. Se il problema persiste contatta l'amministratore della tua stanza.",
|
||||||
|
"delete_account_instructions": "Digita la tua password nel campo sottostante per confermare l'eliminazione del tuo profilo.",
|
||||||
|
"export_theme": "Salva impostazioni",
|
||||||
|
"follow_export": "Esporta la lista di chi segui",
|
||||||
|
"follow_export_button": "Esporta la lista di chi segui in un file CSV",
|
||||||
|
"follow_export_processing": "Sto elaborando, presto ti sarà chiesto di scaricare il tuo file",
|
||||||
|
"follow_import": "Importa la lista di chi segui",
|
||||||
|
"follow_import_error": "Errore nell'importazione della lista di chi segui",
|
||||||
|
"follows_imported": "Importazione riuscita! L'elaborazione richiederà un po' di tempo.",
|
||||||
|
"foreground": "Primo piano",
|
||||||
|
"general": "Generale",
|
||||||
|
"hide_post_stats": "Nascondi statistiche dei messaggi (es. il numero di preferenze)",
|
||||||
|
"hide_user_stats": "Nascondi statistiche dell'utente (es. il numero dei tuoi seguaci)",
|
||||||
|
"import_followers_from_a_csv_file": "Importa una lista di chi segui da un file CSV",
|
||||||
|
"import_theme": "Carica impostazioni",
|
||||||
|
"inputRadius": "Campi di testo",
|
||||||
|
"instance_default": "(predefinito: {value})",
|
||||||
|
"interfaceLanguage": "Lingua dell'interfaccia",
|
||||||
|
"invalid_theme_imported": "Il file selezionato non è un tema supportato da Pleroma. Il tuo tema non è stato modificato.",
|
||||||
|
"limited_availability": "Non disponibile nel tuo browser",
|
||||||
|
"links": "Collegamenti",
|
||||||
|
"lock_account_description": "Limita il tuo account solo a seguaci approvati",
|
||||||
|
"loop_video": "Riproduci video in ciclo continuo",
|
||||||
|
"loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le \"gif\" di Mastodon)",
|
||||||
|
"new_password": "Nuova password",
|
||||||
|
"notification_visibility": "Tipi di notifiche da mostrare",
|
||||||
|
"notification_visibility_follows": "Nuove persone ti seguono",
|
||||||
|
"notification_visibility_likes": "Preferiti",
|
||||||
|
"notification_visibility_mentions": "Menzioni",
|
||||||
|
"notification_visibility_repeats": "Condivisioni",
|
||||||
|
"no_rich_text_description": "Togli la formattazione del testo da tutti i messaggi",
|
||||||
|
"oauth_tokens": "Token OAuth",
|
||||||
|
"token": "Token",
|
||||||
|
"refresh_token": "Aggiorna token",
|
||||||
|
"valid_until": "Valido fino a",
|
||||||
|
"revoke_token": "Revoca",
|
||||||
|
"panelRadius": "Pannelli",
|
||||||
|
"pause_on_unfocused": "Interrompi l'aggiornamento continuo mentre la scheda è in secondo piano",
|
||||||
|
"presets": "Valori predefiniti",
|
||||||
|
"profile_tab": "Profilo",
|
||||||
|
"radii_help": "Imposta il raggio degli angoli (in pixel)",
|
||||||
|
"replies_in_timeline": "Risposte nella sequenza personale",
|
||||||
|
"reply_visibility_all": "Mostra tutte le risposte",
|
||||||
|
"reply_visibility_following": "Mostra solo le risposte rivolte a me o agli utenti che seguo",
|
||||||
|
"reply_visibility_self": "Mostra solo risposte rivolte a me",
|
||||||
|
"saving_err": "Errore nel salvataggio delle impostazioni",
|
||||||
|
"saving_ok": "Impostazioni salvate",
|
||||||
|
"security_tab": "Sicurezza",
|
||||||
|
"stop_gifs": "Riproduci GIF al passaggio del cursore",
|
||||||
|
"streaming": "Mostra automaticamente i nuovi messaggi quando sei in cima alla pagina",
|
||||||
|
"text": "Testo",
|
||||||
|
"theme_help": "Usa codici colore esadecimali (#rrggbb) per personalizzare il tuo schema di colori.",
|
||||||
|
"tooltipRadius": "Descrizioni/avvisi",
|
||||||
|
"values": {
|
||||||
|
"false": "no",
|
||||||
|
"true": "sì"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"error_fetching": "Errore nell'aggiornamento",
|
||||||
|
"load_older": "Carica messaggi più vecchi",
|
||||||
|
"show_new": "Mostra nuovi",
|
||||||
|
"up_to_date": "Aggiornato",
|
||||||
|
"collapse": "Riduci",
|
||||||
|
"conversation": "Conversazione",
|
||||||
|
"no_retweet_hint": "Il messaggio è diretto o solo per seguaci e non può essere condiviso",
|
||||||
|
"repeated": "condiviso"
|
||||||
|
},
|
||||||
|
"user_card": {
|
||||||
|
"follow": "Segui",
|
||||||
|
"followees": "Chi stai seguendo",
|
||||||
|
"followers": "Seguaci",
|
||||||
|
"following": "Seguìto!",
|
||||||
|
"follows_you": "Ti segue!",
|
||||||
|
"mute": "Silenzia",
|
||||||
|
"muted": "Silenziato",
|
||||||
|
"per_day": "al giorno",
|
||||||
|
"statuses": "Messaggi",
|
||||||
|
"approve": "Approva",
|
||||||
|
"block": "Blocca",
|
||||||
|
"blocked": "Bloccato!",
|
||||||
|
"deny": "Nega",
|
||||||
|
"remote_follow": "Segui da remoto"
|
||||||
|
},
|
||||||
|
"chat": {
|
||||||
|
"title": "Chat"
|
||||||
|
},
|
||||||
|
"features_panel": {
|
||||||
|
"chat": "Chat",
|
||||||
|
"gopher": "Gopher",
|
||||||
|
"media_proxy": "Proxy multimedia",
|
||||||
|
"scope_options": "Opzioni visibilità",
|
||||||
|
"text_limit": "Lunghezza massima",
|
||||||
|
"title": "Caratteristiche",
|
||||||
|
"who_to_follow": "Chi seguire"
|
||||||
|
},
|
||||||
|
"finder": {
|
||||||
|
"error_fetching_user": "Errore nel recupero dell'utente",
|
||||||
|
"find_user": "Trova utente"
|
||||||
|
},
|
||||||
|
"login": {
|
||||||
|
"login": "Accedi",
|
||||||
|
"logout": "Disconnettiti",
|
||||||
|
"password": "Password",
|
||||||
|
"placeholder": "es. Lupo Lucio",
|
||||||
|
"register": "Registrati",
|
||||||
|
"username": "Nome utente",
|
||||||
|
"description": "Accedi con OAuth",
|
||||||
|
"hint": "Accedi per partecipare alla discussione",
|
||||||
|
"authentication_code": "Codice di autenticazione",
|
||||||
|
"enter_recovery_code": "Inserisci un codice di recupero",
|
||||||
|
"enter_two_factor_code": "Inserisci un codice two-factor",
|
||||||
|
"recovery_code": "Codice di recupero",
|
||||||
|
"heading": {
|
||||||
|
"totp": "Autenticazione two-factor",
|
||||||
|
"recovery": "Recupero two-factor"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"post_status": {
|
||||||
|
"account_not_locked_warning": "Il tuo profilo non è {0}. Chiunque può seguirti e vedere i tuoi messaggi riservati ai tuoi seguaci.",
|
||||||
|
"account_not_locked_warning_link": "protetto",
|
||||||
|
"attachments_sensitive": "Nascondi gli allegati",
|
||||||
|
"content_type": {
|
||||||
|
"text/plain": "Testo normale"
|
||||||
|
},
|
||||||
|
"content_warning": "Oggetto (facoltativo)",
|
||||||
|
"default": "Sono appena atterrato a Fiumicino.",
|
||||||
|
"direct_warning": "Questo post sarà visibile solo dagli utenti menzionati.",
|
||||||
|
"posting": "Sto pubblicando",
|
||||||
|
"scope": {
|
||||||
|
"direct": "Diretto - Visibile solo agli utenti menzionati",
|
||||||
|
"private": "Solo per seguaci - Visibile solo dai tuoi seguaci",
|
||||||
|
"public": "Pubblico - Visibile sulla sequenza pubblica",
|
||||||
|
"unlisted": "Non elencato - Non visibile sulla sequenza pubblica"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"registration": {
|
||||||
|
"bio": "Introduzione",
|
||||||
|
"email": "Email",
|
||||||
|
"fullname": "Nome visualizzato",
|
||||||
|
"password_confirm": "Conferma password",
|
||||||
|
"registration": "Registrazione",
|
||||||
|
"token": "Codice d'invito"
|
||||||
|
},
|
||||||
|
"user_profile": {
|
||||||
|
"timeline_title": "Sequenza dell'Utente"
|
||||||
|
},
|
||||||
|
"who_to_follow": {
|
||||||
|
"more": "Altro",
|
||||||
|
"who_to_follow": "Chi seguire"
|
||||||
|
},
|
||||||
|
"about": {
|
||||||
|
"mrf": {
|
||||||
|
"federation": "Federazione",
|
||||||
|
"keyword": {
|
||||||
|
"reject": "Rifiuta",
|
||||||
|
"replace": "Sostituisci",
|
||||||
|
"is_replaced_by": "→",
|
||||||
|
"keyword_policies": "Regole per parole chiave",
|
||||||
|
"ftl_removal": "Rimozione dalla sequenza globale"
|
||||||
|
},
|
||||||
|
"simple": {
|
||||||
|
"reject": "Rifiuta",
|
||||||
|
"accept": "Accetta",
|
||||||
|
"simple_policies": "Regole specifiche alla stanza",
|
||||||
|
"accept_desc": "Questa stanza accetta messaggi solo dalle seguenti stanze:",
|
||||||
|
"reject_desc": "Questa stanza non accetterà messaggi dalle stanze seguenti:",
|
||||||
|
"quarantine": "Quarantena",
|
||||||
|
"quarantine_desc": "Questa stanza inoltrerà solo messaggi pubblici alle seguenti stanze:",
|
||||||
|
"ftl_removal": "Rimozione dalla sequenza globale",
|
||||||
|
"ftl_removal_desc": "Questa stanza rimuove le seguenti stanze dalla sequenza globale:",
|
||||||
|
"media_removal": "Rimozione multimedia",
|
||||||
|
"media_removal_desc": "Questa istanza rimuove gli allegati dalle seguenti stanze:",
|
||||||
|
"media_nsfw": "Allegati oscurati forzatamente",
|
||||||
|
"media_nsfw_desc": "Questa stanza oscura gli allegati dei messaggi provenienti da queste stanze:"
|
||||||
|
},
|
||||||
|
"mrf_policies": "Regole RM abilitate",
|
||||||
|
"mrf_policies_desc": "Le regole RM cambiano il comportamento federativo della stanza. Vigono le seguenti regole:"
|
||||||
|
},
|
||||||
|
"staff": "Equipaggio"
|
||||||
|
},
|
||||||
|
"domain_mute_card": {
|
||||||
|
"mute": "Zittisci",
|
||||||
|
"mute_progress": "Zittisco...",
|
||||||
|
"unmute": "Ascolta",
|
||||||
|
"unmute_progress": "Procedo..."
|
||||||
|
},
|
||||||
|
"exporter": {
|
||||||
|
"export": "Esporta",
|
||||||
|
"processing": "In elaborazione, il tuo file sarà scaricabile a breve"
|
||||||
|
},
|
||||||
|
"image_cropper": {
|
||||||
|
"crop_picture": "Ritaglia immagine",
|
||||||
|
"save": "Salva",
|
||||||
|
"save_without_cropping": "Salva senza ritagliare",
|
||||||
|
"cancel": "Annulla"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"registration": {
|
|
||||||
"bio": "Introduzione",
|
|
||||||
"email": "Email",
|
|
||||||
"fullname": "Nome visualizzato",
|
|
||||||
"password_confirm": "Conferma password",
|
|
||||||
"registration": "Registrazione",
|
|
||||||
"token": "Codice d'invito"
|
|
||||||
},
|
|
||||||
"user_profile": {
|
|
||||||
"timeline_title": "Sequenza Temporale dell'Utente"
|
|
||||||
},
|
|
||||||
"who_to_follow": {
|
|
||||||
"more": "Più",
|
|
||||||
"who_to_follow": "Chi seguire"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
1110
src/i18n/nl.json
1110
src/i18n/nl.json
File diff suppressed because it is too large
Load diff
1162
src/i18n/pl.json
1162
src/i18n/pl.json
File diff suppressed because it is too large
Load diff
872
src/i18n/ru.json
872
src/i18n/ru.json
|
@ -1,414 +1,478 @@
|
||||||
{
|
{
|
||||||
"chat": {
|
"chat": {
|
||||||
"title": "Чат"
|
"title": "Чат"
|
||||||
},
|
|
||||||
"finder": {
|
|
||||||
"error_fetching_user": "Пользователь не найден",
|
|
||||||
"find_user": "Найти пользователя"
|
|
||||||
},
|
|
||||||
"general": {
|
|
||||||
"apply": "Применить",
|
|
||||||
"submit": "Отправить",
|
|
||||||
"cancel": "Отмена",
|
|
||||||
"disable": "Оключить",
|
|
||||||
"enable": "Включить",
|
|
||||||
"confirm": "Подтвердить",
|
|
||||||
"verify": "Проверить"
|
|
||||||
},
|
|
||||||
"login": {
|
|
||||||
"login": "Войти",
|
|
||||||
"logout": "Выйти",
|
|
||||||
"password": "Пароль",
|
|
||||||
"placeholder": "e.c. lain",
|
|
||||||
"register": "Зарегистрироваться",
|
|
||||||
"username": "Имя пользователя",
|
|
||||||
"authentication_code": "Код аутентификации",
|
|
||||||
"enter_recovery_code": "Ввести код восстановления",
|
|
||||||
"enter_two_factor_code": "Ввести код аутентификации",
|
|
||||||
"recovery_code": "Код восстановления",
|
|
||||||
"heading" : {
|
|
||||||
"TotpForm" : "Двухфакторная аутентификация",
|
|
||||||
"RecoveryForm" : "Two-factor recovery"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nav": {
|
|
||||||
"back": "Назад",
|
|
||||||
"chat": "Локальный чат",
|
|
||||||
"mentions": "Упоминания",
|
|
||||||
"interactions": "Взаимодействия",
|
|
||||||
"public_tl": "Публичная лента",
|
|
||||||
"timeline": "Лента",
|
|
||||||
"twkn": "Федеративная лента",
|
|
||||||
"search": "Поиск"
|
|
||||||
},
|
|
||||||
"notifications": {
|
|
||||||
"broken_favorite": "Неизвестный статус, ищем...",
|
|
||||||
"favorited_you": "нравится ваш статус",
|
|
||||||
"followed_you": "начал(а) читать вас",
|
|
||||||
"load_older": "Загрузить старые уведомления",
|
|
||||||
"notifications": "Уведомления",
|
|
||||||
"read": "Прочесть",
|
|
||||||
"repeated_you": "повторил(а) ваш статус"
|
|
||||||
},
|
|
||||||
"interactions": {
|
|
||||||
"favs_repeats": "Повторы и фавориты",
|
|
||||||
"follows": "Новые подписки",
|
|
||||||
"load_older": "Загрузить старые взаимодействия"
|
|
||||||
},
|
|
||||||
"post_status": {
|
|
||||||
"account_not_locked_warning": "Ваш аккаунт не {0}. Кто угодно может зафоловить вас чтобы прочитать посты только для подписчиков",
|
|
||||||
"account_not_locked_warning_link": "залочен",
|
|
||||||
"attachments_sensitive": "Вложения содержат чувствительный контент",
|
|
||||||
"content_warning": "Тема (не обязательно)",
|
|
||||||
"default": "Что нового?",
|
|
||||||
"direct_warning": "Этот пост будет виден только упомянутым пользователям",
|
|
||||||
"posting": "Отправляется",
|
|
||||||
"scope_notice": {
|
|
||||||
"public": "Этот пост будет виден всем",
|
|
||||||
"private": "Этот пост будет виден только вашим подписчикам",
|
|
||||||
"unlisted": "Этот пост не будет виден в публичной и федеративной ленте"
|
|
||||||
},
|
},
|
||||||
"scope": {
|
"finder": {
|
||||||
"direct": "Личное - этот пост видят только те кто в нём упомянут",
|
"error_fetching_user": "Пользователь не найден",
|
||||||
"private": "Для подписчиков - этот пост видят только подписчики",
|
"find_user": "Найти пользователя"
|
||||||
"public": "Публичный - этот пост виден всем",
|
|
||||||
"unlisted": "Непубличный - этот пост не виден на публичных лентах"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"registration": {
|
|
||||||
"bio": "Описание",
|
|
||||||
"email": "Email",
|
|
||||||
"fullname": "Отображаемое имя",
|
|
||||||
"password_confirm": "Подтверждение пароля",
|
|
||||||
"registration": "Регистрация",
|
|
||||||
"token": "Код приглашения",
|
|
||||||
"validations": {
|
|
||||||
"username_required": "не должно быть пустым",
|
|
||||||
"fullname_required": "не должно быть пустым",
|
|
||||||
"email_required": "не должен быть пустым",
|
|
||||||
"password_required": "не должен быть пустым",
|
|
||||||
"password_confirmation_required": "не должно быть пустым",
|
|
||||||
"password_confirmation_match": "должно совпадать с паролем"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"settings": {
|
|
||||||
"enter_current_password_to_confirm": "Введите свой текущий пароль",
|
|
||||||
"mfa": {
|
|
||||||
"otp" : "OTP",
|
|
||||||
"setup_otp" : "Настройка OTP",
|
|
||||||
"wait_pre_setup_otp" : "предварительная настройка OTP",
|
|
||||||
"confirm_and_enable" : "Подтвердить и включить OTP",
|
|
||||||
"title": "Двухфакторная аутентификация",
|
|
||||||
"generate_new_recovery_codes" : "Получить новые коды востановления",
|
|
||||||
"warning_of_generate_new_codes" : "После получения новых кодов восстановления, старые больше не будут работать.",
|
|
||||||
"recovery_codes" : "Коды восстановления.",
|
|
||||||
"waiting_a_recovery_codes": "Получение кодов восстановления ...",
|
|
||||||
"recovery_codes_warning" : "Запишите эти коды и держите в безопасном месте - иначе вы их больше не увидите. Если вы потеряете доступ к OTP приложению - без резервных кодов вы больше не сможете залогиниться.",
|
|
||||||
"authentication_methods" : "Методы аутентификации",
|
|
||||||
"scan": {
|
|
||||||
"title": "Сканирование",
|
|
||||||
"desc": "Используйте приложение для двухэтапной аутентификации для сканирования этого QR-код или введите текстовый ключ:",
|
|
||||||
"secret_code": "Ключ"
|
|
||||||
},
|
|
||||||
"verify": {
|
|
||||||
"desc": "Чтобы включить двухэтапную аутентификации, введите код из вашего приложение для двухэтапной аутентификации:"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"attachmentRadius": "Прикреплённые файлы",
|
"general": {
|
||||||
"attachments": "Вложения",
|
"apply": "Применить",
|
||||||
"autoload": "Включить автоматическую загрузку при прокрутке вниз",
|
"submit": "Отправить",
|
||||||
"avatar": "Аватар",
|
"cancel": "Отмена",
|
||||||
"avatarAltRadius": "Аватары в уведомлениях",
|
"disable": "Оключить",
|
||||||
"avatarRadius": "Аватары",
|
"enable": "Включить",
|
||||||
"background": "Фон",
|
"confirm": "Подтвердить",
|
||||||
"bio": "Описание",
|
"verify": "Проверить",
|
||||||
"btnRadius": "Кнопки",
|
"more": "Больше",
|
||||||
"cBlue": "Ответить, читать",
|
"generic_error": "Произошла ошибка",
|
||||||
"cGreen": "Повторить",
|
"optional": "не обязательно",
|
||||||
"cOrange": "Нравится",
|
"show_less": "Показать меньше",
|
||||||
"cRed": "Отменить",
|
"show_more": "Показать больше"
|
||||||
"change_email": "Сменить email",
|
|
||||||
"change_email_error": "Произошла ошибка при попытке изменить email.",
|
|
||||||
"changed_email": "Email изменён успешно.",
|
|
||||||
"change_password": "Сменить пароль",
|
|
||||||
"change_password_error": "Произошла ошибка при попытке изменить пароль.",
|
|
||||||
"changed_password": "Пароль изменён успешно.",
|
|
||||||
"collapse_subject": "Сворачивать посты с темой",
|
|
||||||
"confirm_new_password": "Подтверждение нового пароля",
|
|
||||||
"current_avatar": "Текущий аватар",
|
|
||||||
"current_password": "Текущий пароль",
|
|
||||||
"current_profile_banner": "Текущий баннер профиля",
|
|
||||||
"data_import_export_tab": "Импорт / Экспорт данных",
|
|
||||||
"delete_account": "Удалить аккаунт",
|
|
||||||
"delete_account_description": "Удалить ваш аккаунт и все ваши сообщения.",
|
|
||||||
"delete_account_error": "Возникла ошибка в процессе удаления вашего аккаунта. Если это повторяется, свяжитесь с администратором вашего сервера.",
|
|
||||||
"delete_account_instructions": "Введите ваш пароль в поле ниже для подтверждения удаления.",
|
|
||||||
"export_theme": "Сохранить Тему",
|
|
||||||
"filtering": "Фильтрация",
|
|
||||||
"filtering_explanation": "Все статусы, содержащие данные слова, будут игнорироваться, по одному в строке",
|
|
||||||
"follow_export": "Экспортировать читаемых",
|
|
||||||
"follow_export_button": "Экспортировать читаемых в файл .csv",
|
|
||||||
"follow_export_processing": "Ведётся обработка, скоро вам будет предложено загрузить файл",
|
|
||||||
"follow_import": "Импортировать читаемых",
|
|
||||||
"follow_import_error": "Ошибка при импортировании читаемых.",
|
|
||||||
"follows_imported": "Список читаемых импортирован. Обработка займёт некоторое время..",
|
|
||||||
"foreground": "Передний план",
|
|
||||||
"general": "Общие",
|
|
||||||
"hide_attachments_in_convo": "Прятать вложения в разговорах",
|
|
||||||
"hide_attachments_in_tl": "Прятать вложения в ленте",
|
|
||||||
"hide_isp": "Скрыть серверную панель",
|
|
||||||
"import_followers_from_a_csv_file": "Импортировать читаемых из файла .csv",
|
|
||||||
"import_theme": "Загрузить Тему",
|
|
||||||
"inputRadius": "Поля ввода",
|
|
||||||
"checkboxRadius": "Чекбоксы",
|
|
||||||
"instance_default": "(по умолчанию: {value})",
|
|
||||||
"instance_default_simple": "(по умолчанию)",
|
|
||||||
"interface": "Интерфейс",
|
|
||||||
"interfaceLanguage": "Язык интерфейса",
|
|
||||||
"limited_availability": "Не доступно в вашем браузере",
|
|
||||||
"links": "Ссылки",
|
|
||||||
"lock_account_description": "Аккаунт доступен только подтверждённым подписчикам",
|
|
||||||
"loop_video": "Зациливать видео",
|
|
||||||
"loop_video_silent_only": "Зацикливать только беззвучные видео (т.е. \"гифки\" с Mastodon)",
|
|
||||||
"name": "Имя",
|
|
||||||
"name_bio": "Имя и описание",
|
|
||||||
"new_email": "Новый email",
|
|
||||||
"new_password": "Новый пароль",
|
|
||||||
"fun": "Потешное",
|
|
||||||
"greentext": "Мемные стрелочки",
|
|
||||||
"notification_visibility": "Показывать уведомления",
|
|
||||||
"notification_visibility_follows": "Подписки",
|
|
||||||
"notification_visibility_likes": "Лайки",
|
|
||||||
"notification_visibility_mentions": "Упоминания",
|
|
||||||
"notification_visibility_repeats": "Повторы",
|
|
||||||
"no_rich_text_description": "Убрать форматирование из всех постов",
|
|
||||||
"hide_follows_description": "Не показывать кого я читаю",
|
|
||||||
"hide_followers_description": "Не показывать кто читает меня",
|
|
||||||
"hide_follows_count_description": "Не показывать число читаемых пользователей",
|
|
||||||
"hide_followers_count_description": "Не показывать число моих подписчиков",
|
|
||||||
"show_admin_badge": "Показывать значок администратора в моем профиле",
|
|
||||||
"show_moderator_badge": "Показывать значок модератора в моем профиле",
|
|
||||||
"nsfw_clickthrough": "Включить скрытие NSFW вложений",
|
|
||||||
"oauth_tokens": "OAuth токены",
|
|
||||||
"token": "Токен",
|
|
||||||
"refresh_token": "Рефреш токен",
|
|
||||||
"valid_until": "Годен до",
|
|
||||||
"revoke_token": "Удалить",
|
|
||||||
"panelRadius": "Панели",
|
|
||||||
"pause_on_unfocused": "Приостановить загрузку когда вкладка не в фокусе",
|
|
||||||
"presets": "Пресеты",
|
|
||||||
"profile_background": "Фон профиля",
|
|
||||||
"profile_banner": "Баннер профиля",
|
|
||||||
"profile_tab": "Профиль",
|
|
||||||
"radii_help": "Скругление углов элементов интерфейса (в пикселях)",
|
|
||||||
"replies_in_timeline": "Ответы в ленте",
|
|
||||||
"reply_link_preview": "Включить предварительный просмотр ответа при наведении мыши",
|
|
||||||
"reply_visibility_all": "Показывать все ответы",
|
|
||||||
"reply_visibility_following": "Показывать только ответы мне и тех на кого я подписан",
|
|
||||||
"reply_visibility_self": "Показывать только ответы мне",
|
|
||||||
"autohide_floating_post_button": "Автоматически скрывать кнопку постинга (в мобильной версии)",
|
|
||||||
"saving_err": "Не удалось сохранить настройки",
|
|
||||||
"saving_ok": "Сохранено",
|
|
||||||
"security_tab": "Безопасность",
|
|
||||||
"scope_copy": "Копировать видимость поста при ответе (всегда включено для Личных Сообщений)",
|
|
||||||
"minimal_scopes_mode": "Минимизировать набор опций видимости поста",
|
|
||||||
"set_new_avatar": "Загрузить новый аватар",
|
|
||||||
"set_new_profile_background": "Загрузить новый фон профиля",
|
|
||||||
"set_new_profile_banner": "Загрузить новый баннер профиля",
|
|
||||||
"settings": "Настройки",
|
|
||||||
"subject_input_always_show": "Всегда показывать поле ввода темы",
|
|
||||||
"stop_gifs": "Проигрывать GIF анимации только при наведении",
|
|
||||||
"streaming": "Включить автоматическую загрузку новых сообщений при прокрутке вверх",
|
|
||||||
"useStreamingApi": "Получать сообщения и уведомления в реальном времени",
|
|
||||||
"useStreamingApiWarning": "(Не рекомендуется, экспериментально, сообщения могут пропадать)",
|
|
||||||
"text": "Текст",
|
|
||||||
"theme": "Тема",
|
|
||||||
"theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.",
|
|
||||||
"theme_help_v2_1": "Вы так же можете перепоределить цвета определенных компонентов нажав соотв. галочку. Используйте кнопку \"Очистить всё\" чтобы снять все переопределения",
|
|
||||||
"theme_help_v2_2": "Под некоторыми полями ввода это идикаторы контрастности, наведите на них мышью чтобы узнать больше. Приспользовании прозрачности контраст расчитывается для наихудшего варианта.",
|
|
||||||
"tooltipRadius": "Всплывающие подсказки/уведомления",
|
|
||||||
"user_settings": "Настройки пользователя",
|
|
||||||
"values": {
|
|
||||||
"false": "нет",
|
|
||||||
"true": "да"
|
|
||||||
},
|
},
|
||||||
"style": {
|
"login": {
|
||||||
"switcher": {
|
"login": "Войти",
|
||||||
"keep_color": "Оставить цвета",
|
"logout": "Выйти",
|
||||||
"keep_shadows": "Оставить тени",
|
"password": "Пароль",
|
||||||
"keep_opacity": "Оставить прозрачность",
|
"placeholder": "e.c. lain",
|
||||||
"keep_roundness": "Оставить скругление",
|
"register": "Зарегистрироваться",
|
||||||
"keep_fonts": "Оставить шрифты",
|
"username": "Имя пользователя",
|
||||||
"save_load_hint": "Опции \"оставить...\" позволяют сохранить текущие настройки при выборе другой темы или импорта её из файла. Так же они влияют на то какие компоненты будут сохранены при экспорте темы. Когда все галочки сняты все компоненты будут экспортированы.",
|
"authentication_code": "Код аутентификации",
|
||||||
"reset": "Сбросить",
|
"enter_recovery_code": "Ввести код восстановления",
|
||||||
"clear_all": "Очистить всё",
|
"enter_two_factor_code": "Ввести код аутентификации",
|
||||||
"clear_opacity": "Очистить прозрачность"
|
"recovery_code": "Код восстановления",
|
||||||
},
|
"heading": {
|
||||||
"common": {
|
"TotpForm": "Двухфакторная аутентификация",
|
||||||
"color": "Цвет",
|
"RecoveryForm": "Two-factor recovery"
|
||||||
"opacity": "Прозрачность",
|
|
||||||
"contrast": {
|
|
||||||
"hint": "Уровень контраста: {ratio}, что {level} {context}",
|
|
||||||
"level": {
|
|
||||||
"aa": "соответствует гайдлайну Level AA (минимальный)",
|
|
||||||
"aaa": "соответствует гайдлайну Level AAA (рекомендуемый)",
|
|
||||||
"bad": "не соответствует каким либо гайдлайнам"
|
|
||||||
},
|
|
||||||
"context": {
|
|
||||||
"18pt": "для крупного (18pt+) текста",
|
|
||||||
"text": "для текста"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"common_colors": {
|
"nav": {
|
||||||
"_tab_label": "Общие",
|
"back": "Назад",
|
||||||
"main": "Общие цвета",
|
"chat": "Локальный чат",
|
||||||
"foreground_hint": "См. вкладку \"Дополнительно\" для более детального контроля",
|
"mentions": "Упоминания",
|
||||||
"rgbo": "Иконки, акценты, ярылки"
|
"interactions": "Взаимодействия",
|
||||||
},
|
"public_tl": "Публичная лента",
|
||||||
"advanced_colors": {
|
"timeline": "Лента",
|
||||||
"_tab_label": "Дополнительно",
|
"twkn": "Федеративная лента",
|
||||||
"alert": "Фон уведомлений",
|
"search": "Поиск",
|
||||||
"alert_error": "Ошибки",
|
"friend_requests": "Запросы на чтение"
|
||||||
"badge": "Фон значков",
|
},
|
||||||
"badge_notification": "Уведомления",
|
"notifications": {
|
||||||
"panel_header": "Заголовок панели",
|
"broken_favorite": "Неизвестный статус, ищем...",
|
||||||
"top_bar": "Верняя полоска",
|
"favorited_you": "нравится ваш статус",
|
||||||
"borders": "Границы",
|
"followed_you": "начал(а) читать вас",
|
||||||
"buttons": "Кнопки",
|
"load_older": "Загрузить старые уведомления",
|
||||||
"inputs": "Поля ввода",
|
"notifications": "Уведомления",
|
||||||
"faint_text": "Маловажный текст"
|
"read": "Прочесть",
|
||||||
},
|
"repeated_you": "повторил(а) ваш статус",
|
||||||
"radii": {
|
"follow_request": "хочет читать вас"
|
||||||
"_tab_label": "Скругление"
|
},
|
||||||
},
|
"interactions": {
|
||||||
"shadows": {
|
"favs_repeats": "Повторы и фавориты",
|
||||||
"_tab_label": "Светотень",
|
"follows": "Новые подписки",
|
||||||
"component": "Компонент",
|
"load_older": "Загрузить старые взаимодействия"
|
||||||
"override": "Переопределить",
|
},
|
||||||
"shadow_id": "Тень №{value}",
|
"post_status": {
|
||||||
"blur": "Размытие",
|
"account_not_locked_warning": "Ваш аккаунт не {0}. Кто угодно может начать читать вас чтобы видеть посты только для подписчиков.",
|
||||||
"spread": "Разброс",
|
"account_not_locked_warning_link": "залочен",
|
||||||
"inset": "Внутренняя",
|
"attachments_sensitive": "Вложения содержат чувствительный контент",
|
||||||
"hint": "Для теней вы так же можете использовать --variable в качестве цвета чтобы использовать CSS3-переменные. В таком случае прозрачность работать не будет.",
|
"content_warning": "Тема (не обязательно)",
|
||||||
"filter_hint": {
|
"default": "Что нового?",
|
||||||
"always_drop_shadow": "Внимание, эта тень всегда использует {0} когда браузер поддерживает это",
|
"direct_warning": "Этот пост будет виден только упомянутым пользователям",
|
||||||
"drop_shadow_syntax": "{0} не поддерживает параметр {1} и ключевое слово {2}",
|
"posting": "Отправляется",
|
||||||
"avatar_inset": "Одновременное использование внутренних и внешних теней на (прозрачных) аватарках может дать не те результаты что вы ожидаете",
|
"scope_notice": {
|
||||||
"spread_zero": "Тени с разбросом > 0 будут выглядеть как если бы разброс установлен в 0",
|
"public": "Этот пост будет виден всем",
|
||||||
"inset_classic": "Внутренние тени будут использовать {0}"
|
"private": "Этот пост будет виден только вашим подписчикам",
|
||||||
|
"unlisted": "Этот пост не будет виден в публичной и федеративной ленте"
|
||||||
},
|
},
|
||||||
"components": {
|
"scope": {
|
||||||
"panel": "Панель",
|
"direct": "Личное - этот пост видят только те кто в нём упомянут",
|
||||||
"panelHeader": "Заголовок панели",
|
"private": "Для подписчиков - этот пост видят только подписчики",
|
||||||
"topBar": "Верхняя полоска",
|
"public": "Публичный - этот пост виден всем",
|
||||||
"avatar": "Аватарка (профиль)",
|
"unlisted": "Непубличный - этот пост не виден на публичных лентах"
|
||||||
"avatarStatus": "Аватарка (в ленте)",
|
|
||||||
"popup": "Всплывающие подсказки",
|
|
||||||
"button": "Кнопки",
|
|
||||||
"buttonHover": "Кнопки (наведен курсор)",
|
|
||||||
"buttonPressed": "Кнопки (нажата)",
|
|
||||||
"buttonPressedHover": "Кнопки (нажата+наведен курсор)",
|
|
||||||
"input": "Поля ввода"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fonts": {
|
"registration": {
|
||||||
"_tab_label": "Шрифты",
|
"bio": "Описание",
|
||||||
"help": "Выберите тип шрифта для использования в интерфейсе. При выборе варианта \"другой\" надо ввести название шрифта в точности как он называется в системе.",
|
"email": "Email",
|
||||||
"components": {
|
"fullname": "Отображаемое имя",
|
||||||
"interface": "Интерфейс",
|
"password_confirm": "Подтверждение пароля",
|
||||||
"input": "Поля ввода",
|
"registration": "Регистрация",
|
||||||
"post": "Текст постов",
|
"token": "Код приглашения",
|
||||||
"postCode": "Моноширинный текст в посте (форматирование)"
|
"validations": {
|
||||||
|
"username_required": "не должно быть пустым",
|
||||||
|
"fullname_required": "не должно быть пустым",
|
||||||
|
"email_required": "не должен быть пустым",
|
||||||
|
"password_required": "не должен быть пустым",
|
||||||
|
"password_confirmation_required": "не должно быть пустым",
|
||||||
|
"password_confirmation_match": "должно совпадать с паролем"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"settings": {
|
||||||
|
"enter_current_password_to_confirm": "Введите свой текущий пароль",
|
||||||
|
"mfa": {
|
||||||
|
"otp": "OTP",
|
||||||
|
"setup_otp": "Настройка OTP",
|
||||||
|
"wait_pre_setup_otp": "предварительная настройка OTP",
|
||||||
|
"confirm_and_enable": "Подтвердить и включить OTP",
|
||||||
|
"title": "Двухфакторная аутентификация",
|
||||||
|
"generate_new_recovery_codes": "Получить новые коды востановления",
|
||||||
|
"warning_of_generate_new_codes": "После получения новых кодов восстановления, старые больше не будут работать.",
|
||||||
|
"recovery_codes": "Коды восстановления.",
|
||||||
|
"waiting_a_recovery_codes": "Получение кодов восстановления ...",
|
||||||
|
"recovery_codes_warning": "Запишите эти коды и держите в безопасном месте - иначе вы их больше не увидите. Если вы потеряете доступ к OTP приложению - без резервных кодов вы больше не сможете залогиниться.",
|
||||||
|
"authentication_methods": "Методы аутентификации",
|
||||||
|
"scan": {
|
||||||
|
"title": "Сканирование",
|
||||||
|
"desc": "Используйте приложение для двухэтапной аутентификации для сканирования этого QR-код или введите текстовый ключ:",
|
||||||
|
"secret_code": "Ключ"
|
||||||
|
},
|
||||||
|
"verify": {
|
||||||
|
"desc": "Чтобы включить двухэтапную аутентификации, введите код из вашего приложение для двухэтапной аутентификации:"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"family": "Шрифт",
|
"attachmentRadius": "Прикреплённые файлы",
|
||||||
"size": "Размер (в пикселях)",
|
"attachments": "Вложения",
|
||||||
"weight": "Ширина",
|
"autoload": "Включить автоматическую загрузку при прокрутке вниз",
|
||||||
"custom": "Другой"
|
"avatar": "Аватар",
|
||||||
},
|
"avatarAltRadius": "Аватары в уведомлениях",
|
||||||
"preview": {
|
"avatarRadius": "Аватары",
|
||||||
"header": "Пример",
|
"background": "Фон",
|
||||||
"content": "Контент",
|
"bio": "Описание",
|
||||||
"error": "Ошибка стоп 000",
|
"btnRadius": "Кнопки",
|
||||||
"button": "Кнопка",
|
"cBlue": "Ответить, читать",
|
||||||
"text": "Еще немного {0} и масенькая {1}",
|
"cGreen": "Повторить",
|
||||||
"mono": "контента",
|
"cOrange": "Нравится",
|
||||||
"input": "Что нового?",
|
"cRed": "Отменить",
|
||||||
"faint_link": "Его придется убрать",
|
"change_email": "Сменить email",
|
||||||
"fine_print": "Если проблемы остались — ваш гуртовщик мыши плохо стоит. {0}.",
|
"change_email_error": "Произошла ошибка при попытке изменить email.",
|
||||||
"header_faint": "Все идет по плану",
|
"changed_email": "Email изменён успешно!",
|
||||||
"checkbox": "Я подтверждаю что не было ни единого разрыва",
|
"change_password": "Сменить пароль",
|
||||||
"link": "ссылка"
|
"change_password_error": "Произошла ошибка при попытке изменить пароль.",
|
||||||
}
|
"changed_password": "Пароль изменён успешно!",
|
||||||
|
"collapse_subject": "Сворачивать посты с темой",
|
||||||
|
"confirm_new_password": "Подтверждение нового пароля",
|
||||||
|
"current_avatar": "Текущий аватар",
|
||||||
|
"current_password": "Текущий пароль",
|
||||||
|
"current_profile_banner": "Текущий баннер профиля",
|
||||||
|
"data_import_export_tab": "Импорт / Экспорт данных",
|
||||||
|
"delete_account": "Удалить аккаунт",
|
||||||
|
"delete_account_description": "Удалить ваш аккаунт и все ваши сообщения.",
|
||||||
|
"delete_account_error": "Возникла ошибка в процессе удаления вашего аккаунта. Если это повторяется, свяжитесь с администратором вашего сервера.",
|
||||||
|
"delete_account_instructions": "Введите ваш пароль в поле ниже для подтверждения удаления.",
|
||||||
|
"export_theme": "Сохранить Тему",
|
||||||
|
"filtering": "Фильтрация",
|
||||||
|
"filtering_explanation": "Все статусы, содержащие данные слова, будут игнорироваться, по одному в строке",
|
||||||
|
"follow_export": "Экспортировать читаемых",
|
||||||
|
"follow_export_button": "Экспортировать читаемых в файл .csv",
|
||||||
|
"follow_export_processing": "Ведётся обработка, скоро вам будет предложено загрузить файл",
|
||||||
|
"follow_import": "Импортировать читаемых",
|
||||||
|
"follow_import_error": "Ошибка при импортировании читаемых",
|
||||||
|
"follows_imported": "Список читаемых импортирован. Обработка займёт некоторое время..",
|
||||||
|
"foreground": "Передний план",
|
||||||
|
"general": "Общие",
|
||||||
|
"hide_attachments_in_convo": "Прятать вложения в разговорах",
|
||||||
|
"hide_attachments_in_tl": "Прятать вложения в ленте",
|
||||||
|
"hide_isp": "Скрыть серверную панель",
|
||||||
|
"import_followers_from_a_csv_file": "Импортировать читаемых из файла .csv",
|
||||||
|
"import_theme": "Загрузить Тему",
|
||||||
|
"inputRadius": "Поля ввода",
|
||||||
|
"checkboxRadius": "Чекбоксы",
|
||||||
|
"instance_default": "(по умолчанию: {value})",
|
||||||
|
"instance_default_simple": "(по умолчанию)",
|
||||||
|
"interface": "Интерфейс",
|
||||||
|
"interfaceLanguage": "Язык интерфейса",
|
||||||
|
"limited_availability": "Не доступно в вашем браузере",
|
||||||
|
"links": "Ссылки",
|
||||||
|
"lock_account_description": "Аккаунт доступен только подтверждённым подписчикам",
|
||||||
|
"loop_video": "Зациливать видео",
|
||||||
|
"loop_video_silent_only": "Зацикливать только беззвучные видео (т.е. \"гифки\" с Mastodon)",
|
||||||
|
"name": "Имя",
|
||||||
|
"name_bio": "Имя и описание",
|
||||||
|
"new_email": "Новый email",
|
||||||
|
"new_password": "Новый пароль",
|
||||||
|
"fun": "Потешное",
|
||||||
|
"greentext": "Мемные стрелочки",
|
||||||
|
"notification_visibility": "Показывать уведомления",
|
||||||
|
"notification_visibility_follows": "Подписки",
|
||||||
|
"notification_visibility_likes": "Лайки",
|
||||||
|
"notification_visibility_mentions": "Упоминания",
|
||||||
|
"notification_visibility_repeats": "Повторы",
|
||||||
|
"no_rich_text_description": "Убрать форматирование из всех постов",
|
||||||
|
"hide_follows_description": "Не показывать кого я читаю",
|
||||||
|
"hide_followers_description": "Не показывать кто читает меня",
|
||||||
|
"hide_follows_count_description": "Не показывать число читаемых пользователей",
|
||||||
|
"hide_followers_count_description": "Не показывать число моих подписчиков",
|
||||||
|
"show_admin_badge": "Показывать значок администратора в моем профиле",
|
||||||
|
"show_moderator_badge": "Показывать значок модератора в моем профиле",
|
||||||
|
"nsfw_clickthrough": "Включить скрытие NSFW вложений",
|
||||||
|
"oauth_tokens": "OAuth токены",
|
||||||
|
"token": "Токен",
|
||||||
|
"refresh_token": "Рефреш токен",
|
||||||
|
"valid_until": "Годен до",
|
||||||
|
"revoke_token": "Удалить",
|
||||||
|
"panelRadius": "Панели",
|
||||||
|
"pause_on_unfocused": "Приостановить загрузку когда вкладка не в фокусе",
|
||||||
|
"presets": "Пресеты",
|
||||||
|
"profile_background": "Фон профиля",
|
||||||
|
"profile_banner": "Баннер профиля",
|
||||||
|
"profile_tab": "Профиль",
|
||||||
|
"radii_help": "Скругление углов элементов интерфейса (в пикселях)",
|
||||||
|
"replies_in_timeline": "Ответы в ленте",
|
||||||
|
"reply_link_preview": "Включить предварительный просмотр ответа при наведении мыши",
|
||||||
|
"reply_visibility_all": "Показывать все ответы",
|
||||||
|
"reply_visibility_following": "Показывать только ответы мне или тех на кого я подписан",
|
||||||
|
"reply_visibility_self": "Показывать только ответы мне",
|
||||||
|
"autohide_floating_post_button": "Автоматически скрывать кнопку постинга (в мобильной версии)",
|
||||||
|
"saving_err": "Не удалось сохранить настройки",
|
||||||
|
"saving_ok": "Сохранено",
|
||||||
|
"security_tab": "Безопасность",
|
||||||
|
"scope_copy": "Копировать видимость поста при ответе (всегда включено для Личных Сообщений)",
|
||||||
|
"minimal_scopes_mode": "Минимизировать набор опций видимости поста",
|
||||||
|
"set_new_avatar": "Загрузить новый аватар",
|
||||||
|
"set_new_profile_background": "Загрузить новый фон профиля",
|
||||||
|
"set_new_profile_banner": "Загрузить новый баннер профиля",
|
||||||
|
"settings": "Настройки",
|
||||||
|
"subject_input_always_show": "Всегда показывать поле ввода темы",
|
||||||
|
"stop_gifs": "Проигрывать GIF анимации только при наведении",
|
||||||
|
"streaming": "Включить автоматическую загрузку новых сообщений при прокрутке вверх",
|
||||||
|
"useStreamingApi": "Получать сообщения и уведомления в реальном времени",
|
||||||
|
"useStreamingApiWarning": "(Не рекомендуется, экспериментально, сообщения могут пропадать)",
|
||||||
|
"text": "Текст",
|
||||||
|
"theme": "Тема",
|
||||||
|
"theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.",
|
||||||
|
"theme_help_v2_1": "Вы так же можете перепоределить цвета определенных компонентов нажав соотв. галочку. Используйте кнопку \"Очистить всё\" чтобы снять все переопределения.",
|
||||||
|
"theme_help_v2_2": "Под некоторыми полями ввода это идикаторы контрастности, наведите на них мышью чтобы узнать больше. Приспользовании прозрачности контраст расчитывается для наихудшего варианта.",
|
||||||
|
"tooltipRadius": "Всплывающие подсказки/уведомления",
|
||||||
|
"user_settings": "Настройки пользователя",
|
||||||
|
"values": {
|
||||||
|
"false": "нет",
|
||||||
|
"true": "да"
|
||||||
|
},
|
||||||
|
"style": {
|
||||||
|
"switcher": {
|
||||||
|
"keep_color": "Оставить цвета",
|
||||||
|
"keep_shadows": "Оставить тени",
|
||||||
|
"keep_opacity": "Оставить прозрачность",
|
||||||
|
"keep_roundness": "Оставить скругление",
|
||||||
|
"keep_fonts": "Оставить шрифты",
|
||||||
|
"save_load_hint": "Опции \"оставить...\" позволяют сохранить текущие настройки при выборе другой темы или импорта её из файла. Так же они влияют на то какие компоненты будут сохранены при экспорте темы. Когда все галочки сняты все компоненты будут экспортированы.",
|
||||||
|
"reset": "Сбросить",
|
||||||
|
"clear_all": "Очистить всё",
|
||||||
|
"clear_opacity": "Очистить прозрачность"
|
||||||
|
},
|
||||||
|
"common": {
|
||||||
|
"color": "Цвет",
|
||||||
|
"opacity": "Прозрачность",
|
||||||
|
"contrast": {
|
||||||
|
"hint": "Уровень контраста: {ratio}, что {level} {context}",
|
||||||
|
"level": {
|
||||||
|
"aa": "соответствует гайдлайну Level AA (минимальный)",
|
||||||
|
"aaa": "соответствует гайдлайну Level AAA (рекомендуемый)",
|
||||||
|
"bad": "не соответствует каким либо гайдлайнам"
|
||||||
|
},
|
||||||
|
"context": {
|
||||||
|
"18pt": "для крупного (18pt+) текста",
|
||||||
|
"text": "для текста"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"common_colors": {
|
||||||
|
"_tab_label": "Общие",
|
||||||
|
"main": "Общие цвета",
|
||||||
|
"foreground_hint": "См. вкладку \"Дополнительно\" для более детального контроля",
|
||||||
|
"rgbo": "Иконки, акценты, ярылки"
|
||||||
|
},
|
||||||
|
"advanced_colors": {
|
||||||
|
"_tab_label": "Дополнительно",
|
||||||
|
"alert": "Фон уведомлений",
|
||||||
|
"alert_error": "Ошибки",
|
||||||
|
"badge": "Фон значков",
|
||||||
|
"badge_notification": "Уведомления",
|
||||||
|
"panel_header": "Заголовок панели",
|
||||||
|
"top_bar": "Верняя полоска",
|
||||||
|
"borders": "Границы",
|
||||||
|
"buttons": "Кнопки",
|
||||||
|
"inputs": "Поля ввода",
|
||||||
|
"faint_text": "Маловажный текст"
|
||||||
|
},
|
||||||
|
"radii": {
|
||||||
|
"_tab_label": "Скругление"
|
||||||
|
},
|
||||||
|
"shadows": {
|
||||||
|
"_tab_label": "Светотень",
|
||||||
|
"component": "Компонент",
|
||||||
|
"override": "Переопределить",
|
||||||
|
"shadow_id": "Тень №{value}",
|
||||||
|
"blur": "Размытие",
|
||||||
|
"spread": "Разброс",
|
||||||
|
"inset": "Внутренняя",
|
||||||
|
"hint": "Для теней вы так же можете использовать --variable в качестве цвета чтобы использовать CSS3-переменные. В таком случае прозрачность работать не будет.",
|
||||||
|
"filter_hint": {
|
||||||
|
"always_drop_shadow": "Внимание, эта тень всегда использует {0} когда браузер поддерживает это.",
|
||||||
|
"drop_shadow_syntax": "{0} не поддерживает параметр {1} и ключевое слово {2}.",
|
||||||
|
"avatar_inset": "Одновременное использование внутренних и внешних теней на (прозрачных) аватарках может дать не те результаты что вы ожидаете.",
|
||||||
|
"spread_zero": "Тени с разбросом > 0 будут выглядеть как если бы разброс установлен в 0",
|
||||||
|
"inset_classic": "Внутренние тени будут использовать {0}"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"panel": "Панель",
|
||||||
|
"panelHeader": "Заголовок панели",
|
||||||
|
"topBar": "Верхняя полоска",
|
||||||
|
"avatar": "Аватарка (профиль)",
|
||||||
|
"avatarStatus": "Аватарка (в ленте)",
|
||||||
|
"popup": "Всплывающие подсказки",
|
||||||
|
"button": "Кнопки",
|
||||||
|
"buttonHover": "Кнопки (наведен курсор)",
|
||||||
|
"buttonPressed": "Кнопки (нажата)",
|
||||||
|
"buttonPressedHover": "Кнопки (нажата+наведен курсор)",
|
||||||
|
"input": "Поля ввода"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fonts": {
|
||||||
|
"_tab_label": "Шрифты",
|
||||||
|
"help": "Выберите тип шрифта для использования в интерфейсе. При выборе варианта \"другой\" надо ввести название шрифта в точности как он называется в системе.",
|
||||||
|
"components": {
|
||||||
|
"interface": "Интерфейс",
|
||||||
|
"input": "Поля ввода",
|
||||||
|
"post": "Текст постов",
|
||||||
|
"postCode": "Моноширинный текст в посте (форматирование)"
|
||||||
|
},
|
||||||
|
"family": "Шрифт",
|
||||||
|
"size": "Размер (в пикселях)",
|
||||||
|
"weight": "Ширина",
|
||||||
|
"custom": "Другой"
|
||||||
|
},
|
||||||
|
"preview": {
|
||||||
|
"header": "Пример",
|
||||||
|
"content": "Контент",
|
||||||
|
"error": "Ошибка стоп 000",
|
||||||
|
"button": "Кнопка",
|
||||||
|
"text": "Еще немного {0} и масенькая {1}",
|
||||||
|
"mono": "контента",
|
||||||
|
"input": "Что нового?",
|
||||||
|
"faint_link": "Его придется убрать",
|
||||||
|
"fine_print": "Если проблемы остались — ваш гуртовщик мыши плохо стоит. {0}.",
|
||||||
|
"header_faint": "Все идет по плану",
|
||||||
|
"checkbox": "Я подтверждаю что не было ни единого разрыва",
|
||||||
|
"link": "ссылка"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification_setting_non_followers": "Не читающие вас",
|
||||||
|
"allow_following_move": "Разрешить автоматически читать новый аккаунт при перемещении на другой сервер",
|
||||||
|
"hide_user_stats": "Не показывать статистику пользователей (например количество читателей)",
|
||||||
|
"notification_setting_followers": "Читающие вас",
|
||||||
|
"notification_setting_follows": "Читаемые вами",
|
||||||
|
"notification_setting_non_follows": "Не читаемые вами"
|
||||||
|
},
|
||||||
|
"timeline": {
|
||||||
|
"collapse": "Свернуть",
|
||||||
|
"conversation": "Разговор",
|
||||||
|
"error_fetching": "Ошибка при обновлении",
|
||||||
|
"load_older": "Загрузить старые статусы",
|
||||||
|
"no_retweet_hint": "Пост помечен как \"только для подписчиков\" или \"личное\" и поэтому не может быть повторён",
|
||||||
|
"repeated": "повторил(а)",
|
||||||
|
"show_new": "Показать новые",
|
||||||
|
"up_to_date": "Обновлено"
|
||||||
|
},
|
||||||
|
"user_card": {
|
||||||
|
"block": "Заблокировать",
|
||||||
|
"blocked": "Заблокирован",
|
||||||
|
"favorites": "Понравившиеся",
|
||||||
|
"follow": "Читать",
|
||||||
|
"follow_sent": "Запрос отправлен!",
|
||||||
|
"follow_progress": "Запрашиваем…",
|
||||||
|
"follow_again": "Запросить еще раз?",
|
||||||
|
"follow_unfollow": "Перестать читать",
|
||||||
|
"followees": "Читаемые",
|
||||||
|
"followers": "Читатели",
|
||||||
|
"following": "Читаю!",
|
||||||
|
"follows_you": "Читает вас!",
|
||||||
|
"mute": "Игнорировать",
|
||||||
|
"muted": "Игнорирую",
|
||||||
|
"per_day": "в день",
|
||||||
|
"remote_follow": "Читать удалённо",
|
||||||
|
"statuses": "Статусы",
|
||||||
|
"admin_menu": {
|
||||||
|
"moderation": "Опции модератора",
|
||||||
|
"grant_admin": "Сделать администратором",
|
||||||
|
"revoke_admin": "Забрать права администратора",
|
||||||
|
"grant_moderator": "Сделать модератором",
|
||||||
|
"revoke_moderator": "Забрать права модератора",
|
||||||
|
"activate_account": "Активировать аккаунт",
|
||||||
|
"deactivate_account": "Деактивировать аккаунт",
|
||||||
|
"delete_account": "Удалить аккаунт",
|
||||||
|
"force_nsfw": "Отмечать посты пользователя как NSFW",
|
||||||
|
"strip_media": "Убирать вложения из постов пользователя",
|
||||||
|
"force_unlisted": "Не добавлять посты в публичные ленты",
|
||||||
|
"sandbox": "Принудить видимость постов только читателям",
|
||||||
|
"disable_remote_subscription": "Запретить читать с удаленных серверов",
|
||||||
|
"disable_any_subscription": "Запретить читать пользователя",
|
||||||
|
"quarantine": "Не федерировать посты пользователя",
|
||||||
|
"delete_user": "Удалить пользователя",
|
||||||
|
"delete_user_confirmation": "Вы уверены? Это действие нельзя отменить."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user_profile": {
|
||||||
|
"timeline_title": "Лента пользователя"
|
||||||
|
},
|
||||||
|
"search": {
|
||||||
|
"people": "Люди",
|
||||||
|
"hashtags": "Хэштэги",
|
||||||
|
"person_talking": "Популярно у {count} человека",
|
||||||
|
"people_talking": "Популярно у {count} человек",
|
||||||
|
"no_results": "Ничего не найдено"
|
||||||
|
},
|
||||||
|
"password_reset": {
|
||||||
|
"forgot_password": "Забыли пароль?",
|
||||||
|
"password_reset": "Сброс пароля",
|
||||||
|
"instruction": "Введите ваш email или имя пользователя, и мы отправим вам ссылку для сброса пароля.",
|
||||||
|
"placeholder": "Ваш email или имя пользователя",
|
||||||
|
"check_email": "Проверьте ваш email и перейдите по ссылке для сброса пароля.",
|
||||||
|
"return_home": "Вернуться на главную страницу",
|
||||||
|
"not_found": "Мы не смогли найти аккаунт с таким email-ом или именем пользователя.",
|
||||||
|
"too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.",
|
||||||
|
"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": "Отклонить запрос на чтение"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"timeline": {
|
|
||||||
"collapse": "Свернуть",
|
|
||||||
"conversation": "Разговор",
|
|
||||||
"error_fetching": "Ошибка при обновлении",
|
|
||||||
"load_older": "Загрузить старые статусы",
|
|
||||||
"no_retweet_hint": "Пост помечен как \"только для подписчиков\" или \"личное\" и поэтому не может быть повторён",
|
|
||||||
"repeated": "повторил(а)",
|
|
||||||
"show_new": "Показать новые",
|
|
||||||
"up_to_date": "Обновлено"
|
|
||||||
},
|
|
||||||
"user_card": {
|
|
||||||
"block": "Заблокировать",
|
|
||||||
"blocked": "Заблокирован",
|
|
||||||
"favorites": "Понравившиеся",
|
|
||||||
"follow": "Читать",
|
|
||||||
"follow_sent": "Запрос отправлен!",
|
|
||||||
"follow_progress": "Запрашиваем…",
|
|
||||||
"follow_again": "Запросить еще заново?",
|
|
||||||
"follow_unfollow": "Перестать читать",
|
|
||||||
"followees": "Читаемые",
|
|
||||||
"followers": "Читатели",
|
|
||||||
"following": "Читаю",
|
|
||||||
"follows_you": "Читает вас",
|
|
||||||
"mute": "Игнорировать",
|
|
||||||
"muted": "Игнорирую",
|
|
||||||
"per_day": "в день",
|
|
||||||
"remote_follow": "Читать удалённо",
|
|
||||||
"statuses": "Статусы",
|
|
||||||
"admin_menu": {
|
|
||||||
"moderation": "Опции модератора",
|
|
||||||
"grant_admin": "Сделать администратором",
|
|
||||||
"revoke_admin": "Забрать права администратора",
|
|
||||||
"grant_moderator": "Сделать модератором",
|
|
||||||
"revoke_moderator": "Забрать права модератора",
|
|
||||||
"activate_account": "Активировать аккаунт",
|
|
||||||
"deactivate_account": "Деактивировать аккаунт",
|
|
||||||
"delete_account": "Удалить аккаунт",
|
|
||||||
"force_nsfw": "Отмечать посты пользователя как NSFW",
|
|
||||||
"strip_media": "Убирать вложения из постов пользователя",
|
|
||||||
"force_unlisted": "Не добавлять посты в публичные ленты",
|
|
||||||
"sandbox": "Посты доступны только для подписчиков",
|
|
||||||
"disable_remote_subscription": "Запретить подписываться с удаленных серверов",
|
|
||||||
"disable_any_subscription": "Запретить подписываться на пользователя",
|
|
||||||
"quarantine": "Не федерировать посты пользователя",
|
|
||||||
"delete_user": "Удалить пользователя",
|
|
||||||
"delete_user_confirmation": "Вы уверены? Это действие нельзя отменить."
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"user_profile": {
|
|
||||||
"timeline_title": "Лента пользователя"
|
|
||||||
},
|
|
||||||
"search": {
|
|
||||||
"people": "Люди",
|
|
||||||
"hashtags": "Хэштэги",
|
|
||||||
"person_talking": "Популярно у {count} человека",
|
|
||||||
"people_talking": "Популярно у {count} человек",
|
|
||||||
"no_results": "Ничего не найдено"
|
|
||||||
},
|
|
||||||
"password_reset": {
|
|
||||||
"forgot_password": "Забыли пароль?",
|
|
||||||
"password_reset": "Сброс пароля",
|
|
||||||
"instruction": "Введите ваш email или имя пользователя, и мы отправим вам ссылку для сброса пароля.",
|
|
||||||
"placeholder": "Ваш email или имя пользователя",
|
|
||||||
"check_email": "Проверьте ваш email и перейдите по ссылке для сброса пароля.",
|
|
||||||
"return_home": "Вернуться на главную страницу",
|
|
||||||
"not_found": "Мы не смогли найти аккаунт с таким email-ом или именем пользователя.",
|
|
||||||
"too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.",
|
|
||||||
"password_reset_disabled": "Сброс пароля отключен. Cвяжитесь с администратором вашего сервера."
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
1295
src/i18n/zh.json
1295
src/i18n/zh.json
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
applyTheme(themeData.theme)
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
fetchEmoji ({ dispatch, state }) {
|
fetchEmoji ({ dispatch, state }) {
|
||||||
|
|
|
@ -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 }),
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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({
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ownOpacitySlot = value.opacity
|
||||||
|
|
||||||
|
if (ownOpacitySlot === null) {
|
||||||
|
outputColor.a = 1
|
||||||
|
} else if (sourceColor === 'transparent') {
|
||||||
|
outputColor.a = 0
|
||||||
|
} else {
|
||||||
|
const opacityOverriden = ownOpacitySlot && sourceOpacity[opacitySlot] !== undefined
|
||||||
|
|
||||||
const dependencySlot = deps[0]
|
const dependencySlot = deps[0]
|
||||||
if (dependencySlot && colors[dependencySlot] === 'transparent') {
|
const dependencyColor = dependencySlot && colors[dependencySlot]
|
||||||
outputColor.a = 0
|
|
||||||
|
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 {
|
} else {
|
||||||
outputColor.a = Number(sourceOpacity[opacitySlot]) || OPACITIES[opacitySlot].defaultValue || 1
|
// 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 },
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
|
@ -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"],
|
||||||
|
|
191
static/themes/pleroma-dark.json
Normal file
191
static/themes/pleroma-dark.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
219
static/themes/pleroma-light.json
Normal file
219
static/themes/pleroma-light.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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: {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -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', () => {
|
||||||
|
|
|
@ -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] {|}*:\'')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
19
yarn.lock
19
yarn.lock
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue