logo

pleroma-fe

My custom branche(s) on git.pleroma.social/pleroma/pleroma-fe git clone https://hacktivis.me/git/pleroma-fe.git
commit: cc170aa3ecffa75004760fc6d02bd87373132f7d
parent c3fcbbd918ddef4e3f574a464fd10f4899bb2dce
Author: Shpuld Shpuldson <shp@cock.li>
Date:   Sun,  8 Aug 2021 16:14:22 +0300

Update master with 2.4.0

Diffstat:

MCHANGELOG.md19+++++++++++++++++++
MCONTRIBUTORS.md1+
Mbuild/dev-server.js1+
Mbuild/webpack.base.conf.js14++++++++++++++
Mconfig/index.js5+++++
Mpackage.json5+++--
Msrc/App.js11+++++++----
Msrc/App.scss80++++++++++++++++++++++++++-----------------------------------------------------
Msrc/App.vue6+++---
Msrc/boot/after_store.js2+-
Msrc/boot/routes.js4++--
Msrc/components/account_actions/account_actions.vue25++++++++++---------------
Msrc/components/attachment/attachment.js2++
Msrc/components/attachment/attachment.vue6++++++
Msrc/components/chat_list/chat_list.vue5+----
Msrc/components/chat_message/chat_message.vue32+++++++++++++++++---------------
Dsrc/components/chat_panel/chat_panel.js41-----------------------------------------
Dsrc/components/chat_panel/chat_panel.vue143-------------------------------------------------------------------------------
Msrc/components/domain_mute_card/domain_mute_card.vue4++--
Msrc/components/emoji_input/emoji_input.js60++++++++++++++++++++++++++++++------------------------------
Msrc/components/emoji_input/emoji_input.vue1+
Dsrc/components/export_import/export_import.vue102-------------------------------------------------------------------------------
Msrc/components/extra_buttons/extra_buttons.vue24++++++++++--------------
Msrc/components/features_panel/features_panel.js2+-
Msrc/components/features_panel/features_panel.vue4++--
Asrc/components/flash/flash.js52++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/flash/flash.vue88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/font_control/font_control.js12++++--------
Msrc/components/font_control/font_control.vue35+++++++++++++----------------------
Msrc/components/global_notice_list/global_notice_list.vue8++++++++
Msrc/components/interface_language_switcher/interface_language_switcher.vue41++++++++++++++---------------------------
Msrc/components/moderation_tools/moderation_tools.js5+++++
Msrc/components/moderation_tools/moderation_tools.vue63++++++++++++++++++++++++++-------------------------------------
Msrc/components/nav_panel/nav_panel.js36++++++++++++++++++++++--------------
Msrc/components/nav_panel/nav_panel.vue70+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Asrc/components/notifications/notification_filters.vue122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/notifications/notifications.js13+++++--------
Msrc/components/notifications/notifications.scss7+++++--
Msrc/components/notifications/notifications.vue11++++++-----
Msrc/components/password_reset/password_reset.vue2+-
Msrc/components/poll/poll_form.js6++++--
Msrc/components/poll/poll_form.vue70+++++++++++++++++++++++++++-------------------------------------------
Msrc/components/popover/popover.js16+++++++++++++---
Msrc/components/popover/popover.vue35++++++++++++++++++++++++++++++-----
Msrc/components/post_status_form/post_status_form.js8++++----
Msrc/components/post_status_form/post_status_form.vue35+++++++++++++----------------------
Msrc/components/react_button/react_button.vue28+++++++++++++---------------
Msrc/components/registration/registration.vue2+-
Asrc/components/select/select.js21+++++++++++++++++++++
Asrc/components/select/select.vue62++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/selectable_list/selectable_list.vue7++-----
Asrc/components/settings_modal/helpers/boolean_setting.js38++++++++++++++++++++++++++++++++++++++
Msrc/components/settings_modal/helpers/boolean_setting.vue38+-------------------------------------
Asrc/components/settings_modal/helpers/choice_setting.js39+++++++++++++++++++++++++++++++++++++++
Asrc/components/settings_modal/helpers/choice_setting.vue29+++++++++++++++++++++++++++++
Msrc/components/settings_modal/helpers/modified_indicator.vue16++++++++--------
Msrc/components/settings_modal/settings_modal.js124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/settings_modal/settings_modal.vue68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/components/settings_modal/settings_modal_content.scss13++++++++++++-
Msrc/components/settings_modal/tabs/filtering_tab.js19+++++++++----------
Msrc/components/settings_modal/tabs/filtering_tab.vue28++++++----------------------
Msrc/components/settings_modal/tabs/general_tab.js17+++++++++++++++--
Msrc/components/settings_modal/tabs/general_tab.vue81+++++++++++++++++++++++++++----------------------------------------------------
Msrc/components/settings_modal/tabs/mutes_and_blocks_tab.vue73+++++++++++++++++++++++++++++--------------------------------------------
Msrc/components/settings_modal/tabs/notifications_tab.vue2+-
Msrc/components/settings_modal/tabs/profile_tab.vue6+++---
Msrc/components/settings_modal/tabs/security_tab/security_tab.vue6+++---
Msrc/components/settings_modal/tabs/theme_tab/theme_tab.js65+++++++++++++++++++++++++++++++++++++++++++----------------------
Msrc/components/settings_modal/tabs/theme_tab/theme_tab.vue108+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/components/shadow_control/shadow_control.js4+++-
Msrc/components/shadow_control/shadow_control.vue46++++++++++++++++++----------------------------
Asrc/components/shout_panel/shout_panel.js53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/shout_panel/shout_panel.vue148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/side_drawer/side_drawer.js1-
Msrc/components/side_drawer/side_drawer.vue4+---
Msrc/components/status_popover/status_popover.vue8+++-----
Msrc/components/timeline/timeline.js9++++++---
Asrc/components/timeline/timeline.scss31+++++++++++++++++++++++++++++++
Msrc/components/timeline/timeline.vue36++++++------------------------------
Asrc/components/timeline/timeline_quick_settings.js61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/components/timeline/timeline_quick_settings.vue102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/timeline_menu/timeline_menu.js31+++++--------------------------
Msrc/components/timeline_menu/timeline_menu.vue86++++++++++++++++++-------------------------------------------------------------
Asrc/components/timeline_menu/timeline_menu_content.js29+++++++++++++++++++++++++++++
Asrc/components/timeline_menu/timeline_menu_content.vue66++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/components/user_card/user_card.js15++++++++++-----
Msrc/components/user_card/user_card.vue69+++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/components/user_list_popover/user_list_popover.vue59+++++++++++++++++++++++++++++------------------------------
Msrc/components/user_profile/user_profile.vue10++--------
Msrc/components/user_reporting_modal/user_reporting_modal.vue5+----
Msrc/i18n/de.json355+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/i18n/en.json111+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msrc/i18n/eo.json21++++++++++++++++++---
Msrc/i18n/es.json127+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/i18n/eu.json46+++++++++++++++++++++++++++-------------------
Msrc/i18n/fr.json243+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/i18n/it.json207+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msrc/i18n/ja_pedantic.json58++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/i18n/ko.json24++++++++++++++++--------
Msrc/i18n/nb.json26++++++++++++++++++++++----
Msrc/i18n/nl.json352+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msrc/i18n/ru.json269+++++++++++++++++++++++++++++++++++++++++++++++++------------------------------
Msrc/i18n/uk.json55++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/i18n/zh.json55+++++++++++++++++++++++++++++++++++++++++++------------
Msrc/i18n/zh_Hant.json65+++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msrc/main.js6++----
Msrc/modules/api.js106++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Dsrc/modules/chat.js33---------------------------------
Msrc/modules/config.js24+++++++++++++++++++++---
Msrc/modules/instance.js6+++---
Asrc/modules/shout.js33+++++++++++++++++++++++++++++++++
Msrc/modules/statuses.js29++++++++++++++++++++++-------
Msrc/modules/users.js7++++---
Msrc/services/api/api.service.js6+++++-
Msrc/services/backend_interactor_service/backend_interactor_service.js12++++++++++--
Msrc/services/entity_normalizer/entity_normalizer.service.js2+-
Asrc/services/export_import/export_import.js55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/services/file_type/file_type.service.js4++++
Msrc/services/notification_utils/notification_utils.js7+++++++
Msrc/services/notifications_fetcher/notifications_fetcher.service.js6++++--
Asrc/services/ruffle_service/ruffle_service.js40++++++++++++++++++++++++++++++++++++++++
Msrc/services/style_setter/style_setter.js2+-
Msrc/services/theme_data/pleromafe.js28++++++++++++++++++++++++++++
Msrc/services/timeline_fetcher/timeline_fetcher.service.js9+++++++--
Mstatic/config.json1-
Myarn.lock855++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
126 files changed, 4283 insertions(+), 2099 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -3,6 +3,18 @@ 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/). +## [2.4.0] - 2021-08-08 +### Added +- Added a quick settings to timeline header for easier access +- Added option to mark posts as sensitive by default +- Added quick filters for notifications +- Implemented user option to change sidebar position to the right side +- Implemented user option to hide floating shout panel +- Implemented "edit profile" button if viewing own profile which opens profile settings + +### Fixed +- Fixed follow request count showing in the wrong location in mobile view + ## [2.3.0] - 2021-03-01 ### Fixed @@ -12,9 +24,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Fixed some UI jumpiness when opening images particularly in chat view - Fixed chat unread badge looking weird - Fixed punycode names not working properly +- Fixed notifications crashing on an invalid notification ### Changed - Display 'people voted' instead of 'votes' for multi-choice polls +- Changed the "Timelines" link in side panel to toggle show all timeline options inside the panel +- Renamed "Timeline" to "Home Timeline" to be more clear - Optimized chat to not get horrible performance after keeping the same chat open for a long time - When opening emoji picker or react picker, it automatically focuses the search field - Language picker now uses native language names @@ -31,6 +46,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Fixed - Follows/Followers tabs on user profiles now display the content properly. - Handle punycode in screen names +- Fixed local dev mode having non-functional websockets in some cases +- Show notices for websocket events (errors, abnormal closures, reconnections) +- Fix not being able to re-enable websocket until page refresh +- Fix annoying issue where timeline might have few posts when streaming is enabled ### Changed - Don't filter own posts when they hit your wordfilter diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md @@ -3,6 +3,7 @@ Contributors of this project. - Constance Variable (lambadalambda@social.heldscal.la): Code - Coco Snuss (cocosnuss@social.heldscal.la): Code - wakarimasen (wakarimasen@shitposter.club): NSFW hiding image +- eris (eris@disqordia.space): Code - dtluna (dtluna@social.heldscal.la): Code - sonyam (sonyam@social.heldscal.la): Background images - hakui (hakui@freezepeach.xyz): CSS and styling diff --git a/build/dev-server.js b/build/dev-server.js @@ -21,6 +21,7 @@ var compiler = webpack(webpackConfig) var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath, + writeToDisk: true, stats: { colors: true, chunks: false diff --git a/build/webpack.base.conf.js b/build/webpack.base.conf.js @@ -3,6 +3,7 @@ var config = require('../config') var utils = require('./utils') var projectRoot = path.resolve(__dirname, '../') var ServiceWorkerWebpackPlugin = require('serviceworker-webpack-plugin') +var CopyPlugin = require('copy-webpack-plugin'); var env = process.env.NODE_ENV // check env & config/index.js to decide weither to enable CSS Sourcemaps for the @@ -93,6 +94,19 @@ module.exports = { new ServiceWorkerWebpackPlugin({ entry: path.join(__dirname, '..', 'src/sw.js'), filename: 'sw-pleroma.js' + }), + // This copies Ruffle's WASM to a directory so that JS side can access it + new CopyPlugin({ + patterns: [ + { + from: "node_modules/ruffle-mirror/*", + to: "static/ruffle", + flatten: true + }, + ], + options: { + concurrency: 100, + }, }) ] } diff --git a/config/index.js b/config/index.js @@ -3,6 +3,11 @@ const path = require('path') let settings = {} try { settings = require('./local.json') + if (settings.target && settings.target.endsWith('/')) { + // replacing trailing slash since it can conflict with some apis + // and that's how actual BE reports its url + settings.target = settings.target.replace(/\/$/, '') + } console.log('Using local dev server settings (/config/local.json):') console.log(JSON.stringify(settings, null, 2)) } catch (e) { diff --git a/package.json b/package.json @@ -32,9 +32,9 @@ "phoenix": "^1.3.0", "portal-vue": "^2.1.4", "punycode.js": "^2.1.0", + "ruffle-mirror": "^2021.4.10", "v-click-outside": "^2.1.1", "vue": "^2.6.11", - "vue-chat-scroll": "^1.2.1", "vue-i18n": "^7.3.2", "vue-router": "^3.0.1", "vue-template-compiler": "^2.6.11", @@ -58,6 +58,7 @@ "chalk": "^1.1.3", "chromedriver": "^87.0.1", "connect-history-api-fallback": "^1.1.0", + "copy-webpack-plugin": "^6.4.1", "cross-spawn": "^4.0.2", "css-loader": "^0.28.0", "custom-event-polyfill": "^1.0.7", @@ -112,7 +113,7 @@ "url-loader": "^1.1.2", "vue-loader": "^14.0.0", "vue-style-loader": "^4.0.0", - "webpack": "^4.0.0", + "webpack": "^4.44.0", "webpack-dev-middleware": "^3.6.0", "webpack-hot-middleware": "^2.12.2", "webpack-merge": "^0.14.1" diff --git a/src/App.js b/src/App.js @@ -4,7 +4,7 @@ import Notifications from './components/notifications/notifications.vue' import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue' import FeaturesPanel from './components/features_panel/features_panel.vue' import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue' -import ChatPanel from './components/chat_panel/chat_panel.vue' +import ShoutPanel from './components/shout_panel/shout_panel.vue' import SettingsModal from './components/settings_modal/settings_modal.vue' import MediaModal from './components/media_modal/media_modal.vue' import SideDrawer from './components/side_drawer/side_drawer.vue' @@ -26,7 +26,7 @@ export default { InstanceSpecificPanel, FeaturesPanel, WhoToFollowPanel, - ChatPanel, + ShoutPanel, MediaModal, SideDrawer, MobilePostStatusButton, @@ -65,7 +65,7 @@ export default { } } }, - chat () { return this.$store.state.chat.channel.state === 'joined' }, + shout () { return this.$store.state.shout.channel.state === 'joined' }, suggestionsEnabled () { return this.$store.state.instance.suggestionsEnabled }, showInstanceSpecificPanel () { return this.$store.state.instance.showInstanceSpecificPanel && @@ -73,11 +73,14 @@ export default { this.$store.state.instance.instanceSpecificPanelContent }, showFeaturesPanel () { return this.$store.state.instance.showFeaturesPanel }, + hideShoutbox () { + return this.$store.getters.mergedConfig.hideShoutbox + }, isMobileLayout () { return this.$store.state.interface.mobileLayout }, privateMode () { return this.$store.state.instance.private }, sidebarAlign () { return { - 'order': this.$store.state.instance.sidebarRight ? 99 : 0 + 'order': this.$store.getters.mergedConfig.sidebarRight ? 99 : 0 } }, ...mapGetters(['mergedConfig']) diff --git a/src/App.scss b/src/App.scss @@ -187,7 +187,7 @@ a { } } -input, textarea, .select, .input { +input, textarea, .input { &.unstyled { border-radius: 0; @@ -217,47 +217,11 @@ input, textarea, .select, .input { hyphens: none; padding: 8px .5em; - &.select { - padding: 0; - } - - &:disabled, &[disabled=disabled] { + &:disabled, &[disabled=disabled], &.disabled { cursor: not-allowed; opacity: 0.5; } - .select-down-icon { - position: absolute; - top: 0; - bottom: 0; - right: 5px; - height: 100%; - color: $fallback--text; - color: var(--inputText, $fallback--text); - line-height: 28px; - z-index: 0; - pointer-events: none; - } - - select { - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; - background: transparent; - border: none; - color: $fallback--text; - color: var(--inputText, --text, $fallback--text); - margin: 0; - padding: 0 2em 0 .2em; - font-family: sans-serif; - font-family: var(--inputFont, sans-serif); - font-size: 14px; - width: 100%; - z-index: 1; - height: 28px; - line-height: 16px; - } - &[type=range] { background: none; border: none; @@ -547,9 +511,21 @@ main-router { border-radius: var(--panelRadius, $fallback--panelRadius); } -.panel-footer { +/* TODO Should remove timeline-footer from here when we refactor panels into + * separate component and utilize slots + */ +.panel-footer, .timeline-footer { + display: flex; border-radius: 0 0 $fallback--panelRadius $fallback--panelRadius; border-radius: 0 0 var(--panelRadius, $fallback--panelRadius) var(--panelRadius, $fallback--panelRadius); + flex: none; + padding: 0.6em 0.6em; + text-align: left; + line-height: 28px; + align-items: baseline; + border-width: 1px 0 0 0; + border-style: solid; + border-color: var(--border, $fallback--border); .faint { color: $fallback--faint; @@ -706,6 +682,15 @@ nav { color: var(--alertWarningPanelText, $fallback--text); } } + + &.success { + background-color: var(--alertSuccess, $fallback--alertWarning); + color: var(--alertSuccessText, $fallback--text); + + .panel-heading & { + color: var(--alertSuccessPanelText, $fallback--text); + } + } } .faint { @@ -809,13 +794,6 @@ nav { } } -.select-multiple { - display: flex; - .option-list { - margin: 0; - padding-left: .5em; - } -} .setting-list, .option-list{ list-style-type: none; @@ -862,16 +840,10 @@ nav { } .new-status-notification { - position:relative; - margin-top: -1px; + position: relative; font-size: 1.1em; - border-width: 1px 0 0 0; - border-style: solid; - border-color: var(--border, $fallback--border); - padding: 10px; z-index: 1; - background-color: $fallback--fg; - background-color: var(--panel, $fallback--fg); + flex: 1; } .chat-layout { diff --git a/src/App.vue b/src/App.vue @@ -49,10 +49,10 @@ </div> <media-modal /> </div> - <chat-panel - v-if="currentUser && chat" + <shout-panel + v-if="currentUser && shout && !hideShoutbox" :floating="true" - class="floating-chat mobile-hidden" + class="floating-shout mobile-hidden" /> <MobilePostStatusButton /> <UserReportingModal /> diff --git a/src/boot/after_store.js b/src/boot/after_store.js @@ -240,7 +240,7 @@ const getNodeInfo = async ({ store }) => { store.dispatch('setInstanceOption', { name: 'registrationOpen', value: data.openRegistrations }) store.dispatch('setInstanceOption', { name: 'mediaProxyAvailable', value: features.includes('media_proxy') }) store.dispatch('setInstanceOption', { name: 'safeDM', value: features.includes('safe_dm_mentions') }) - store.dispatch('setInstanceOption', { name: 'chatAvailable', value: features.includes('chat') }) + store.dispatch('setInstanceOption', { name: 'shoutAvailable', value: features.includes('chat') }) store.dispatch('setInstanceOption', { name: 'pleromaChatMessagesAvailable', value: features.includes('pleroma_chat_messages') }) store.dispatch('setInstanceOption', { name: 'gopherAvailable', value: features.includes('gopher') }) store.dispatch('setInstanceOption', { name: 'pollsAvailable', value: features.includes('polls') }) diff --git a/src/boot/routes.js b/src/boot/routes.js @@ -16,7 +16,7 @@ import FollowRequests from 'components/follow_requests/follow_requests.vue' import OAuthCallback from 'components/oauth_callback/oauth_callback.vue' import Notifications from 'components/notifications/notifications.vue' import AuthForm from 'components/auth_form/auth_form.js' -import ChatPanel from 'components/chat_panel/chat_panel.vue' +import ShoutPanel from 'components/shout_panel/shout_panel.vue' import WhoToFollow from 'components/who_to_follow/who_to_follow.vue' import About from 'components/about/about.vue' import RemoteUserResolver from 'components/remote_user_resolver/remote_user_resolver.vue' @@ -64,7 +64,7 @@ export default (store) => { { name: 'friend-requests', path: '/friend-requests', component: FollowRequests, beforeEnter: validateAuthenticatedRoute }, { name: 'notifications', path: '/:username/notifications', component: Notifications, beforeEnter: validateAuthenticatedRoute }, { name: 'login', path: '/login', component: AuthForm }, - { name: 'chat-panel', path: '/chat-panel', component: ChatPanel, props: () => ({ floating: false }) }, + { name: 'shout-panel', path: '/shout-panel', component: ShoutPanel, props: () => ({ floating: false }) }, { name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) }, { name: 'search', path: '/search', component: Search, props: (route) => ({ query: route.query.query }) }, { name: 'who-to-follow', path: '/who-to-follow', component: WhoToFollow, beforeEnter: validateAuthenticatedRoute }, diff --git a/src/components/account_actions/account_actions.vue b/src/components/account_actions/account_actions.vue @@ -6,10 +6,7 @@ :bound-to="{ x: 'container' }" remove-padding > - <div - slot="content" - class="account-tools-popover" - > + <template v-slot:content> <div class="dropdown-menu"> <template v-if="relationship.following"> <button @@ -59,16 +56,15 @@ {{ $t('user_card.message') }} </button> </div> - </div> - <div - slot="trigger" - class="ellipsis-button" - > - <FAIcon - class="icon" - icon="ellipsis-v" - /> - </div> + </template> + <template v-slot:trigger> + <button class="button-unstyled ellipsis-button"> + <FAIcon + class="icon" + icon="ellipsis-v" + /> + </button> + </template> </Popover> </div> </template> @@ -83,7 +79,6 @@ } .ellipsis-button { - cursor: pointer; width: 2.5em; margin: -0.5em 0; padding: 0.5em 0; diff --git a/src/components/attachment/attachment.js b/src/components/attachment/attachment.js @@ -1,4 +1,5 @@ import StillImage from '../still-image/still-image.vue' +import Flash from '../flash/flash.vue' import VideoAttachment from '../video_attachment/video_attachment.vue' import nsfwImage from '../../assets/nsfw.png' import fileTypeService from '../../services/file_type/file_type.service.js' @@ -43,6 +44,7 @@ const Attachment = { } }, components: { + Flash, StillImage, VideoAttachment }, diff --git a/src/components/attachment/attachment.vue b/src/components/attachment/attachment.vue @@ -117,6 +117,11 @@ <!-- eslint-enable vue/no-v-html --> </div> </div> + + <Flash + v-if="type === 'flash'" + :src="attachment.large_thumb_url || attachment.url" + /> </div> </template> @@ -172,6 +177,7 @@ } .non-gallery.attachment { + &.flash, &.video { flex: 1 0 40%; } diff --git a/src/components/chat_list/chat_list.vue b/src/components/chat_list/chat_list.vue @@ -23,10 +23,7 @@ class="timeline" > <List :items="sortedChatList"> - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <ChatListItem :key="item.id" :compact="false" diff --git a/src/components/chat_message/chat_message.vue b/src/components/chat_message/chat_message.vue @@ -50,7 +50,7 @@ @show="menuOpened = true" @close="menuOpened = false" > - <div slot="content"> + <template v-slot:content> <div class="dropdown-menu"> <button class="button-default dropdown-item dropdown-item-icon" @@ -59,26 +59,28 @@ <FAIcon icon="times" /> {{ $t("chats.delete") }} </button> </div> - </div> - <button - slot="trigger" - class="button-default menu-icon" - :title="$t('chats.more')" - > - <FAIcon icon="ellipsis-h" /> - </button> + </template> + <template v-slot:trigger> + <button + class="button-default menu-icon" + :title="$t('chats.more')" + > + <FAIcon icon="ellipsis-h" /> + </button> + </template> </Popover> </div> <StatusContent :status="messageForStatusContent" :full-content="true" > - <span - slot="footer" - class="created-at" - > - {{ createdAt }} - </span> + <template v-slot:footer> + <span + class="created-at" + > + {{ createdAt }} + </span> + </template> </StatusContent> </div> </div> diff --git a/src/components/chat_panel/chat_panel.js b/src/components/chat_panel/chat_panel.js @@ -1,41 +0,0 @@ -import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faBullhorn, - faTimes -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faBullhorn, - faTimes -) - -const chatPanel = { - props: [ 'floating' ], - data () { - return { - currentMessage: '', - channel: null, - collapsed: true - } - }, - computed: { - messages () { - return this.$store.state.chat.messages - } - }, - methods: { - submit (message) { - this.$store.state.chat.channel.push('new_msg', { text: message }, 10000) - this.currentMessage = '' - }, - togglePanel () { - this.collapsed = !this.collapsed - }, - userProfileLink (user) { - return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames) - } - } -} - -export default chatPanel diff --git a/src/components/chat_panel/chat_panel.vue b/src/components/chat_panel/chat_panel.vue @@ -1,143 +0,0 @@ -<template> - <div - v-if="!collapsed || !floating" - class="chat-panel" - > - <div class="panel panel-default"> - <div - class="panel-heading timeline-heading" - :class="{ 'chat-heading': floating }" - @click.stop.prevent="togglePanel" - > - <div class="title"> - <span>{{ $t('shoutbox.title') }}</span> - <FAIcon - v-if="floating" - icon="times" - /> - </div> - </div> - <div - v-chat-scroll - class="chat-window" - > - <div - v-for="message in messages" - :key="message.id" - class="chat-message" - > - <span class="chat-avatar"> - <img :src="message.author.avatar"> - </span> - <div class="chat-content"> - <router-link - class="chat-name" - :to="userProfileLink(message.author)" - > - {{ message.author.username }} - </router-link> - <br> - <span class="chat-text"> - {{ message.text }} - </span> - </div> - </div> - </div> - <div class="chat-input"> - <textarea - v-model="currentMessage" - class="chat-input-textarea" - rows="1" - @keyup.enter="submit(currentMessage)" - /> - </div> - </div> - </div> - <div - v-else - class="chat-panel" - > - <div class="panel panel-default"> - <div - class="panel-heading stub timeline-heading chat-heading" - @click.stop.prevent="togglePanel" - > - <div class="title"> - <FAIcon - class="icon" - icon="bullhorn" - /> - {{ $t('shoutbox.title') }} - </div> - </div> - </div> - </div> -</template> - -<script src="./chat_panel.js"></script> - -<style lang="scss"> -@import '../../_variables.scss'; - -.floating-chat { - position: fixed; - right: 0px; - bottom: 0px; - z-index: 1000; - max-width: 25em; -} - -.chat-panel { - .chat-heading { - cursor: pointer; - - .icon { - color: $fallback--text; - color: var(--text, $fallback--text); - } - } - - .chat-window { - overflow-y: auto; - overflow-x: hidden; - max-height: 20em; - } - - .chat-window-container { - height: 100%; - } - - .chat-message { - display: flex; - padding: 0.2em 0.5em - } - - .chat-avatar { - img { - height: 24px; - width: 24px; - border-radius: $fallback--avatarRadius; - border-radius: var(--avatarRadius, $fallback--avatarRadius); - margin-right: 0.5em; - margin-top: 0.25em; - } - } - - .chat-input { - display: flex; - textarea { - flex: 1; - margin: 0.6em; - min-height: 3.5em; - resize: none; - } - } - - .chat-panel { - .title { - display: flex; - justify-content: space-between; - } - } -} -</style> diff --git a/src/components/domain_mute_card/domain_mute_card.vue b/src/components/domain_mute_card/domain_mute_card.vue @@ -9,7 +9,7 @@ class="btn button-default" > {{ $t('domain_mute_card.unmute') }} - <template slot="progress"> + <template v-slot:progress> {{ $t('domain_mute_card.unmute_progress') }} </template> </ProgressButton> @@ -19,7 +19,7 @@ class="btn button-default" > {{ $t('domain_mute_card.mute') }} - <template slot="progress"> + <template v-slot:progress> {{ $t('domain_mute_card.mute_progress') }} </template> </ProgressButton> diff --git a/src/components/emoji_input/emoji_input.js b/src/components/emoji_input/emoji_input.js @@ -57,6 +57,7 @@ const EmojiInput = { required: true, type: Function }, + // TODO VUE3: change to modelValue, change 'input' event to 'input' value: { /** * Used for v-model @@ -143,32 +144,31 @@ const EmojiInput = { } }, mounted () { - const slots = this.$slots.default - if (!slots || slots.length === 0) return - const input = slots.find(slot => ['input', 'textarea'].includes(slot.tag)) + const { root } = this.$refs + const input = root.querySelector('.emoji-input > input') || root.querySelector('.emoji-input > textarea') if (!input) return this.input = input this.resize() - input.elm.addEventListener('blur', this.onBlur) - input.elm.addEventListener('focus', this.onFocus) - input.elm.addEventListener('paste', this.onPaste) - input.elm.addEventListener('keyup', this.onKeyUp) - input.elm.addEventListener('keydown', this.onKeyDown) - input.elm.addEventListener('click', this.onClickInput) - input.elm.addEventListener('transitionend', this.onTransition) - input.elm.addEventListener('input', this.onInput) + input.addEventListener('blur', this.onBlur) + input.addEventListener('focus', this.onFocus) + input.addEventListener('paste', this.onPaste) + input.addEventListener('keyup', this.onKeyUp) + input.addEventListener('keydown', this.onKeyDown) + input.addEventListener('click', this.onClickInput) + input.addEventListener('transitionend', this.onTransition) + input.addEventListener('input', this.onInput) }, unmounted () { const { input } = this if (input) { - input.elm.removeEventListener('blur', this.onBlur) - input.elm.removeEventListener('focus', this.onFocus) - input.elm.removeEventListener('paste', this.onPaste) - input.elm.removeEventListener('keyup', this.onKeyUp) - input.elm.removeEventListener('keydown', this.onKeyDown) - input.elm.removeEventListener('click', this.onClickInput) - input.elm.removeEventListener('transitionend', this.onTransition) - input.elm.removeEventListener('input', this.onInput) + input.removeEventListener('blur', this.onBlur) + input.removeEventListener('focus', this.onFocus) + input.removeEventListener('paste', this.onPaste) + input.removeEventListener('keyup', this.onKeyUp) + input.removeEventListener('keydown', this.onKeyDown) + input.removeEventListener('click', this.onClickInput) + input.removeEventListener('transitionend', this.onTransition) + input.removeEventListener('input', this.onInput) } }, watch: { @@ -216,7 +216,7 @@ const EmojiInput = { }, 0) }, togglePicker () { - this.input.elm.focus() + this.input.focus() this.showPicker = !this.showPicker if (this.showPicker) { this.scrollIntoView() @@ -262,13 +262,13 @@ const EmojiInput = { this.$emit('input', newValue) const position = this.caret + (insertion + spaceAfter + spaceBefore).length if (!keepOpen) { - this.input.elm.focus() + this.input.focus() } this.$nextTick(function () { // Re-focus inputbox after clicking suggestion // Set selection right after the replacement instead of the very end - this.input.elm.setSelectionRange(position, position) + this.input.setSelectionRange(position, position) this.caret = position }) }, @@ -285,9 +285,9 @@ const EmojiInput = { this.$nextTick(function () { // Re-focus inputbox after clicking suggestion - this.input.elm.focus() + this.input.focus() // Set selection right after the replacement instead of the very end - this.input.elm.setSelectionRange(position, position) + this.input.setSelectionRange(position, position) this.caret = position }) e.preventDefault() @@ -349,7 +349,7 @@ const EmojiInput = { } this.$nextTick(() => { - const { offsetHeight } = this.input.elm + const { offsetHeight } = this.input const { picker } = this.$refs const pickerBottom = picker.$el.getBoundingClientRect().bottom if (pickerBottom > window.innerHeight) { @@ -414,8 +414,8 @@ const EmojiInput = { // Scroll the input element to the position of the cursor this.$nextTick(() => { - this.input.elm.blur() - this.input.elm.focus() + this.input.blur() + this.input.focus() }) } // Disable suggestions hotkeys if suggestions are hidden @@ -444,7 +444,7 @@ const EmojiInput = { // de-focuses the element (i.e. default browser behavior) if (key === 'Escape') { if (!this.temporarilyHideSuggestions) { - this.input.elm.focus() + this.input.focus() } } @@ -480,7 +480,7 @@ const EmojiInput = { if (!panel) return const picker = this.$refs.picker.$el const panelBody = this.$refs['panel-body'] - const { offsetHeight, offsetTop } = this.input.elm + const { offsetHeight, offsetTop } = this.input const offsetBottom = offsetTop + offsetHeight this.setPlacement(panelBody, panel, offsetBottom) @@ -494,7 +494,7 @@ const EmojiInput = { if (this.placement === 'top' || (this.placement === 'auto' && this.overflowsBottom(container))) { target.style.top = 'auto' - target.style.bottom = this.input.elm.offsetHeight + 'px' + target.style.bottom = this.input.offsetHeight + 'px' } }, overflowsBottom (el) { diff --git a/src/components/emoji_input/emoji_input.vue b/src/components/emoji_input/emoji_input.vue @@ -1,5 +1,6 @@ <template> <div + ref="root" v-click-outside="onClickOutside" class="emoji-input" :class="{ 'with-picker': !hideEmojiButton }" diff --git a/src/components/export_import/export_import.vue b/src/components/export_import/export_import.vue @@ -1,102 +0,0 @@ -<template> - <div class="import-export-container"> - <slot name="before" /> - <button - class="btn button-default" - @click="exportData" - > - {{ exportLabel }} - </button> - <button - class="btn button-default" - @click="importData" - > - {{ importLabel }} - </button> - <slot name="afterButtons" /> - <p - v-if="importFailed" - class="alert error" - > - {{ importFailedText }} - </p> - <slot name="afterError" /> - </div> -</template> - -<script> -export default { - props: [ - 'exportObject', - 'importLabel', - 'exportLabel', - 'importFailedText', - 'validator', - 'onImport', - 'onImportFailure' - ], - data () { - return { - importFailed: false - } - }, - methods: { - exportData () { - const stringified = JSON.stringify(this.exportObject, null, 2) // Pretty-print and indent with 2 spaces - - // Create an invisible link with a data url and simulate a click - const e = document.createElement('a') - e.setAttribute('download', 'pleroma_theme.json') - e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified)) - e.style.display = 'none' - - document.body.appendChild(e) - e.click() - document.body.removeChild(e) - }, - importData () { - this.importFailed = false - const filePicker = document.createElement('input') - filePicker.setAttribute('type', 'file') - filePicker.setAttribute('accept', '.json') - - filePicker.addEventListener('change', event => { - if (event.target.files[0]) { - // eslint-disable-next-line no-undef - const reader = new FileReader() - reader.onload = ({ target }) => { - try { - const parsed = JSON.parse(target.result) - const valid = this.validator(parsed) - if (valid) { - this.onImport(parsed) - } else { - this.importFailed = true - // this.onImportFailure(valid) - } - } catch (e) { - // This will happen both if there is a JSON syntax error or the theme is missing components - this.importFailed = true - // this.onImportFailure(e) - } - } - reader.readAsText(event.target.files[0]) - } - }) - - document.body.appendChild(filePicker) - filePicker.click() - document.body.removeChild(filePicker) - } - } -} -</script> - -<style lang="scss"> -.import-export-container { - display: flex; - flex-wrap: wrap; - align-items: baseline; - justify-content: center; -} -</style> diff --git a/src/components/extra_buttons/extra_buttons.vue b/src/components/extra_buttons/extra_buttons.vue @@ -7,10 +7,7 @@ :bound-to="{ x: 'container' }" remove-padding > - <div - slot="content" - slot-scope="{close}" - > + <template v-slot:content="{close}"> <div class="dropdown-menu"> <button v-if="canMute && !status.thread_muted" @@ -120,16 +117,15 @@ /><span>{{ $t("user_card.report") }}</span> </button> </div> - </div> - <span - slot="trigger" - class="popover-trigger" - > - <FAIcon - class="fa-scale-110 fa-old-padding" - icon="ellipsis-h" - /> - </span> + </template> + <template v-slot:trigger> + <button class="button-unstyled popover-trigger"> + <FAIcon + class="fa-scale-110 fa-old-padding" + icon="ellipsis-h" + /> + </button> + </template> </Popover> </template> diff --git a/src/components/features_panel/features_panel.js b/src/components/features_panel/features_panel.js @@ -2,7 +2,7 @@ import fileSizeFormatService from '../../services/file_size_format/file_size_for const FeaturesPanel = { computed: { - chat: function () { return this.$store.state.instance.chatAvailable }, + shout: function () { return this.$store.state.instance.shoutAvailable }, pleromaChatMessages: function () { return this.$store.state.instance.pleromaChatMessagesAvailable }, gopher: function () { return this.$store.state.instance.gopherAvailable }, whoToFollow: function () { return this.$store.state.instance.suggestionsEnabled }, diff --git a/src/components/features_panel/features_panel.vue b/src/components/features_panel/features_panel.vue @@ -8,8 +8,8 @@ </div> <div class="panel-body features-panel"> <ul> - <li v-if="chat"> - {{ $t('features_panel.chat') }} + <li v-if="shout"> + {{ $t('features_panel.shout') }} </li> <li v-if="pleromaChatMessages"> {{ $t('features_panel.pleroma_chat_messages') }} diff --git a/src/components/flash/flash.js b/src/components/flash/flash.js @@ -0,0 +1,52 @@ +import RuffleService from '../../services/ruffle_service/ruffle_service.js' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faStop, + faExclamationTriangle +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faStop, + faExclamationTriangle +) + +const Flash = { + props: [ 'src' ], + data () { + return { + player: false, // can be true, "hidden", false. hidden = element exists + loaded: false, + ruffleInstance: null + } + }, + methods: { + openPlayer () { + if (this.player) return // prevent double-loading, or re-loading on failure + this.player = 'hidden' + RuffleService.getRuffle().then((ruffle) => { + const player = ruffle.newest().createPlayer() + player.config = { + letterbox: 'on' + } + const container = this.$refs.container + container.appendChild(player) + player.style.width = '100%' + player.style.height = '100%' + player.load(this.src).then(() => { + this.player = true + }).catch((e) => { + console.error('Error loading ruffle', e) + this.player = 'error' + }) + this.ruffleInstance = player + }) + }, + closePlayer () { + console.log(this.ruffleInstance) + this.ruffleInstance.remove() + this.player = false + } + } +} + +export default Flash diff --git a/src/components/flash/flash.vue b/src/components/flash/flash.vue @@ -0,0 +1,88 @@ +<template> + <div class="Flash"> + <div + v-if="player === true || player === 'hidden'" + ref="container" + class="player" + :class="{ hidden: player === 'hidden' }" + /> + <button + v-if="player !== true" + class="button-unstyled placeholder" + @click="openPlayer" + > + <span + v-if="player === 'hidden'" + class="label" + > + {{ $t('general.loading') }} + </span> + <span + v-if="player === 'error'" + class="label" + > + {{ $t('general.flash_fail') }} + </span> + <span + v-else + class="label" + > + <p> + {{ $t('general.flash_content') }} + </p> + <p> + <FAIcon icon="exclamation-triangle" /> + {{ $t('general.flash_security') }} + </p> + </span> + </button> + <button + v-if="player" + class="button-unstyled hider" + @click="closePlayer" + > + <FAIcon icon="stop" /> + </button> + </div> +</template> + +<script src="./flash.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; +.Flash { + width: 100%; + height: 260px; + position: relative; + + .player { + height: 100%; + width: 100%; + } + + .hider { + top: 0; + } + + .label { + text-align: center; + flex: 1 1 0; + line-height: 1.2; + white-space: normal; + word-wrap: normal; + } + + .hidden { + display: none; + visibility: 'hidden'; + } + + .placeholder { + height: 100%; + flex: 1; + display: flex; + align-items: center; + justify-content: center; + } +} +</style> diff --git a/src/components/font_control/font_control.js b/src/components/font_control/font_control.js @@ -1,14 +1,10 @@ import { set } from 'vue' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faChevronDown -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faChevronDown -) +import Select from '../select/select.vue' export default { + components: { + Select + }, props: [ 'name', 'label', 'value', 'fallback', 'options', 'no-inherit' ], diff --git a/src/components/font_control/font_control.vue b/src/components/font_control/font_control.vue @@ -22,30 +22,20 @@ class="opt-l" :for="name + '-o'" /> - <label - :for="name + '-font-switcher'" - class="select" + <Select + :id="name + '-font-switcher'" + v-model="preset" :disabled="!present" + class="font-switcher" > - <select - :id="name + '-font-switcher'" - v-model="preset" - :disabled="!present" - class="font-switcher" + <option + v-for="option in availableOptions" + :key="option" + :value="option" > - <option - v-for="option in availableOptions" - :key="option" - :value="option" - > - {{ option === 'custom' ? $t('settings.style.fonts.custom') : option }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> + {{ option === 'custom' ? $t('settings.style.fonts.custom') : option }} + </option> + </Select> <input v-if="isCustom" :id="name" @@ -65,7 +55,8 @@ min-width: 10em; } &.custom { - .select { + /* TODO Should make proper joiners... */ + .font-switcher { border-top-right-radius: 0; border-bottom-right-radius: 0; } diff --git a/src/components/global_notice_list/global_notice_list.vue b/src/components/global_notice_list/global_notice_list.vue @@ -71,6 +71,14 @@ } } + .global-success { + background-color: var(--alertPopupSuccess, $fallback--cGreen); + color: var(--alertPopupSuccessText, $fallback--text); + .svg-inline--fa { + color: var(--alertPopupSuccessText, $fallback--text); + } + } + .global-info { background-color: var(--alertPopupNeutral, $fallback--fg); color: var(--alertPopupNeutralText, $fallback--text); diff --git a/src/components/interface_language_switcher/interface_language_switcher.vue b/src/components/interface_language_switcher/interface_language_switcher.vue @@ -3,27 +3,18 @@ <label for="interface-language-switcher"> {{ $t('settings.interfaceLanguage') }} </label> - <label - for="interface-language-switcher" - class="select" + <Select + id="interface-language-switcher" + v-model="language" > - <select - id="interface-language-switcher" - v-model="language" + <option + v-for="lang in languages" + :key="lang.code" + :value="lang.code" > - <option - v-for="lang in languages" - :key="lang.code" - :value="lang.code" - > - {{ lang.name }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> + {{ lang.name }} + </option> + </Select> </div> </template> @@ -32,16 +23,12 @@ import languagesObject from '../../i18n/messages' import localeService from '../../services/locale/locale.service.js' import ISO6391 from 'iso-639-1' import _ from 'lodash' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faChevronDown -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faChevronDown -) +import Select from '../select/select.vue' export default { + components: { + Select + }, computed: { languages () { return _.map(languagesObject.languages, (code) => ({ code: code, name: this.getLanguageName(code) })).sort((a, b) => a.name.localeCompare(b.name)) diff --git a/src/components/moderation_tools/moderation_tools.js b/src/components/moderation_tools/moderation_tools.js @@ -1,6 +1,11 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +import { faChevronDown } from '@fortawesome/free-solid-svg-icons' + import DialogModal from '../dialog_modal/dialog_modal.vue' import Popover from '../popover/popover.vue' +library.add(faChevronDown) + const FORCE_NSFW = 'mrf_tag:media-force-nsfw' const STRIP_MEDIA = 'mrf_tag:media-strip' const FORCE_UNLISTED = 'mrf_tag:force-unlisted' diff --git a/src/components/moderation_tools/moderation_tools.vue b/src/components/moderation_tools/moderation_tools.vue @@ -8,7 +8,7 @@ @show="setToggled(true)" @close="setToggled(false)" > - <div slot="content"> + <template v-slot:content> <div class="dropdown-menu"> <span v-if="user.is_local"> <button @@ -50,96 +50,98 @@ class="button-default dropdown-item" @click="toggleTag(tags.FORCE_NSFW)" > - {{ $t('user_card.admin_menu.force_nsfw') }} <span class="menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_NSFW) }" /> + {{ $t('user_card.admin_menu.force_nsfw') }} </button> <button class="button-default dropdown-item" @click="toggleTag(tags.STRIP_MEDIA)" > - {{ $t('user_card.admin_menu.strip_media') }} <span class="menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.STRIP_MEDIA) }" /> + {{ $t('user_card.admin_menu.strip_media') }} </button> <button class="button-default dropdown-item" @click="toggleTag(tags.FORCE_UNLISTED)" > - {{ $t('user_card.admin_menu.force_unlisted') }} <span class="menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.FORCE_UNLISTED) }" /> + {{ $t('user_card.admin_menu.force_unlisted') }} </button> <button class="button-default dropdown-item" @click="toggleTag(tags.SANDBOX)" > - {{ $t('user_card.admin_menu.sandbox') }} <span class="menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.SANDBOX) }" /> + {{ $t('user_card.admin_menu.sandbox') }} </button> <button v-if="user.is_local" class="button-default dropdown-item" @click="toggleTag(tags.DISABLE_REMOTE_SUBSCRIPTION)" > - {{ $t('user_card.admin_menu.disable_remote_subscription') }} <span class="menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_REMOTE_SUBSCRIPTION) }" /> + {{ $t('user_card.admin_menu.disable_remote_subscription') }} </button> <button v-if="user.is_local" class="button-default dropdown-item" @click="toggleTag(tags.DISABLE_ANY_SUBSCRIPTION)" > - {{ $t('user_card.admin_menu.disable_any_subscription') }} <span class="menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.DISABLE_ANY_SUBSCRIPTION) }" /> + {{ $t('user_card.admin_menu.disable_any_subscription') }} </button> <button v-if="user.is_local" class="button-default dropdown-item" @click="toggleTag(tags.QUARANTINE)" > - {{ $t('user_card.admin_menu.quarantine') }} <span class="menu-checkbox" :class="{ 'menu-checkbox-checked': hasTag(tags.QUARANTINE) }" /> + {{ $t('user_card.admin_menu.quarantine') }} </button> </span> </div> - </div> - <button - slot="trigger" - class="btn button-default btn-block" - :class="{ toggled }" - > - {{ $t('user_card.admin_menu.moderation') }} - </button> + </template> + <template v-slot:trigger> + <button + class="btn button-default btn-block moderation-tools-button" + :class="{ toggled }" + > + {{ $t('user_card.admin_menu.moderation') }} + <FAIcon icon="chevron-down" /> + </button> + </template> </Popover> <portal to="modal"> <DialogModal v-if="showDeleteUserDialog" :on-cancel="deleteUserDialog.bind(this, false)" > - <template slot="header"> + <template v-slot:header> {{ $t('user_card.admin_menu.delete_user') }} </template> <p>{{ $t('user_card.admin_menu.delete_user_confirmation') }}</p> - <template slot="footer"> + <template v-slot:footer> <button class="btn button-default" @click="deleteUserDialog(false)" @@ -163,25 +165,6 @@ <style lang="scss"> @import '../../_variables.scss'; -.menu-checkbox { - float: right; - min-width: 22px; - max-width: 22px; - min-height: 22px; - max-height: 22px; - line-height: 22px; - text-align: center; - border-radius: 0px; - background-color: $fallback--fg; - background-color: var(--input, $fallback--fg); - box-shadow: 0px 0px 2px black inset; - box-shadow: var(--inputShadow); - - &.menu-checkbox-checked::after { - content: '✓'; - } -} - .moderation-tools-popover { height: 100%; .trigger { @@ -189,4 +172,10 @@ height: 100%; } } + +.moderation-tools-button { + svg,i { + font-size: 0.8em; + } +} </style> diff --git a/src/components/nav_panel/nav_panel.js b/src/components/nav_panel/nav_panel.js @@ -1,4 +1,4 @@ -import { timelineNames } from '../timeline_menu/timeline_menu.js' +import TimelineMenuContent from '../timeline_menu/timeline_menu_content.vue' import { mapState, mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' @@ -7,10 +7,12 @@ import { faGlobe, faBookmark, faEnvelope, - faHome, + faChevronDown, + faChevronUp, faComments, faBell, - faInfoCircle + faInfoCircle, + faStream } from '@fortawesome/free-solid-svg-icons' library.add( @@ -18,10 +20,12 @@ library.add( faGlobe, faBookmark, faEnvelope, - faHome, + faChevronDown, + faChevronUp, faComments, faBell, - faInfoCircle + faInfoCircle, + faStream ) const NavPanel = { @@ -30,16 +34,20 @@ const NavPanel = { this.$store.dispatch('startFetchingFollowRequests') } }, + components: { + TimelineMenuContent + }, + data () { + return { + showTimelines: false + } + }, + methods: { + toggleTimelines () { + this.showTimelines = !this.showTimelines + } + }, computed: { - onTimelineRoute () { - return !!timelineNames()[this.$route.name] - }, - timelinesRoute () { - if (this.$store.state.interface.lastTimeline) { - return this.$store.state.interface.lastTimeline - } - return this.currentUser ? 'friends' : 'public-timeline' - }, ...mapState({ currentUser: state => state.users.currentUser, followRequestCount: state => state.api.followRequests.length, diff --git a/src/components/nav_panel/nav_panel.vue b/src/components/nav_panel/nav_panel.vue @@ -3,19 +3,33 @@ <div class="panel panel-default"> <ul> <li v-if="currentUser || !privateMode"> - <router-link - :to="{ name: timelinesRoute }" - :class="onTimelineRoute && 'router-link-active'" + <button + class="button-unstyled menu-item" + @click="toggleTimelines" > <FAIcon fixed-width class="fa-scale-110" - icon="home" + icon="stream" />{{ $t("nav.timelines") }} - </router-link> + <FAIcon + class="timelines-chevron" + fixed-width + :icon="showTimelines ? 'chevron-up' : 'chevron-down'" + /> + </button> + <div + v-show="showTimelines" + class="timelines-background" + > + <TimelineMenuContent class="timelines" /> + </div> </li> <li v-if="currentUser"> - <router-link :to="{ name: 'interactions', params: { username: currentUser.screen_name } }"> + <router-link + class="menu-item" + :to="{ name: 'interactions', params: { username: currentUser.screen_name } }" + > <FAIcon fixed-width class="fa-scale-110" @@ -24,7 +38,10 @@ </router-link> </li> <li v-if="currentUser && pleromaChatMessagesAvailable"> - <router-link :to="{ name: 'chats', params: { username: currentUser.screen_name } }"> + <router-link + class="menu-item" + :to="{ name: 'chats', params: { username: currentUser.screen_name } }" + > <div v-if="unreadChatCount" class="badge badge-notification" @@ -39,7 +56,10 @@ </router-link> </li> <li v-if="currentUser && currentUser.locked"> - <router-link :to="{ name: 'friend-requests' }"> + <router-link + class="menu-item" + :to="{ name: 'friend-requests' }" + > <FAIcon fixed-width class="fa-scale-110" @@ -54,7 +74,10 @@ </router-link> </li> <li> - <router-link :to="{ name: 'about' }"> + <router-link + class="menu-item" + :to="{ name: 'about' }" + > <FAIcon fixed-width class="fa-scale-110" @@ -91,14 +114,14 @@ border-color: var(--border, $fallback--border); padding: 0; - &:first-child a { + &:first-child .menu-item { border-top-right-radius: $fallback--panelRadius; border-top-right-radius: var(--panelRadius, $fallback--panelRadius); border-top-left-radius: $fallback--panelRadius; border-top-left-radius: var(--panelRadius, $fallback--panelRadius); } - &:last-child a { + &:last-child .menu-item { border-bottom-right-radius: $fallback--panelRadius; border-bottom-right-radius: var(--panelRadius, $fallback--panelRadius); border-bottom-left-radius: $fallback--panelRadius; @@ -110,13 +133,15 @@ border: none; } - a { + .menu-item { display: block; box-sizing: border-box; - align-items: stretch; height: 3.5em; line-height: 3.5em; padding: 0 1em; + width: 100%; + color: $fallback--link; + color: var(--link, $fallback--link); &:hover { background-color: $fallback--lightBg; @@ -146,6 +171,25 @@ } } + .timelines-chevron { + margin-left: 0.8em; + font-size: 1.1em; + } + + .timelines-background { + padding: 0 0 0 0.6em; + background-color: $fallback--lightBg; + background-color: var(--selectedMenu, $fallback--lightBg); + border-top: 1px solid; + border-color: $fallback--border; + border-color: var(--border, $fallback--border); + } + + .timelines { + background-color: $fallback--bg; + background-color: var(--bg, $fallback--bg); + } + .fa-scale-110 { margin-right: 0.8em; } diff --git a/src/components/notifications/notification_filters.vue b/src/components/notifications/notification_filters.vue @@ -0,0 +1,122 @@ +<template> + <Popover + trigger="click" + class="NotificationFilters" + placement="bottom" + :bound-to="{ x: 'container' }" + > + <template v-slot:content> + <div class="dropdown-menu"> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('likes')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.likes }" + />{{ $t('settings.notification_visibility_likes') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('repeats')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.repeats }" + />{{ $t('settings.notification_visibility_repeats') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('follows')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.follows }" + />{{ $t('settings.notification_visibility_follows') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('mentions')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.mentions }" + />{{ $t('settings.notification_visibility_mentions') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('emojiReactions')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.emojiReactions }" + />{{ $t('settings.notification_visibility_emoji_reactions') }} + </button> + <button + class="button-default dropdown-item" + @click="toggleNotificationFilter('moves')" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': filters.moves }" + />{{ $t('settings.notification_visibility_moves') }} + </button> + </div> + </template> + <template v-slot:trigger> + <button class="button-unstyled"> + <FAIcon icon="filter" /> + </button> + </template> + </Popover> +</template> + +<script> +import Popover from '../popover/popover.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faFilter } from '@fortawesome/free-solid-svg-icons' + +library.add( + faFilter +) + +export default { + components: { Popover }, + computed: { + filters () { + return this.$store.getters.mergedConfig.notificationVisibility + } + }, + methods: { + toggleNotificationFilter (type) { + this.$store.dispatch('setOption', { + name: 'notificationVisibility', + value: { + ...this.filters, + [type]: !this.filters[type] + } + }) + } + } +} +</script> + +<style lang="scss"> + +.NotificationFilters { + align-self: stretch; + + > button { + font-size: 1.2em; + padding-left: 0.7em; + padding-right: 0.2em; + line-height: 100%; + height: 100%; + } + + .dropdown-item { + margin: 0; + } +} + +</style> diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js @@ -1,5 +1,6 @@ import { mapGetters } from 'vuex' import Notification from '../notification/notification.vue' +import NotificationFilters from './notification_filters.vue' import notificationsFetcher from '../../services/notifications_fetcher/notifications_fetcher.service.js' import { notificationsFromStore, @@ -17,6 +18,10 @@ library.add( const DEFAULT_SEEN_TO_DISPLAY_COUNT = 30 const Notifications = { + components: { + Notification, + NotificationFilters + }, props: { // Disables display of panel header noHeading: Boolean, @@ -35,11 +40,6 @@ const Notifications = { seenToDisplayCount: DEFAULT_SEEN_TO_DISPLAY_COUNT } }, - created () { - const store = this.$store - const credentials = store.state.users.currentUser.credentials - notificationsFetcher.fetchAndUpdate({ store, credentials }) - }, computed: { mainClass () { return this.minimalMode ? '' : 'panel panel-default' @@ -70,9 +70,6 @@ const Notifications = { }, ...mapGetters(['unreadChatCount']) }, - components: { - Notification - }, watch: { unseenCountTitle (count) { if (count > 0) { diff --git a/src/components/notifications/notifications.scss b/src/components/notifications/notifications.scss @@ -1,6 +1,6 @@ @import '../../_variables.scss'; -.notifications { +.Notifications { &:not(.minimal) { // a bit of a hack to allow scrolling below notifications padding-bottom: 15em; @@ -11,6 +11,10 @@ color: var(--text, $fallback--text); } + .notifications-footer { + border: none; + } + .notification { position: relative; @@ -82,7 +86,6 @@ } } - .follow-text, .move-text { padding: 0.5em 0; overflow-wrap: break-word; diff --git a/src/components/notifications/notifications.vue b/src/components/notifications/notifications.vue @@ -1,7 +1,7 @@ <template> <div :class="{ minimal: minimalMode }" - class="notifications" + class="Notifications" > <div :class="mainClass"> <div @@ -22,6 +22,7 @@ > {{ $t('notifications.read') }} </button> + <NotificationFilters /> </div> <div class="panel-body"> <div @@ -34,10 +35,10 @@ <notification :notification="notification" /> </div> </div> - <div class="panel-footer"> + <div class="panel-footer notifications-footer"> <div v-if="bottomedOut" - class="new-status-notification text-center panel-footer faint" + class="new-status-notification text-center faint" > {{ $t('notifications.no_more_notifications') }} </div> @@ -46,13 +47,13 @@ class="button-unstyled -link -fullwidth" @click.prevent="fetchOlderNotifications()" > - <div class="new-status-notification text-center panel-footer"> + <div class="new-status-notification text-center"> {{ minimalMode ? $t('interactions.load_older') : $t('notifications.load_older') }} </div> </button> <div v-else - class="new-status-notification text-center panel-footer" + class="new-status-notification text-center" > <FAIcon icon="circle-notch" diff --git a/src/components/password_reset/password_reset.vue b/src/components/password_reset/password_reset.vue @@ -53,7 +53,7 @@ type="submit" class="btn button-default btn-block" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/poll/poll_form.js b/src/components/poll/poll_form.js @@ -1,19 +1,21 @@ import * as DateUtils from 'src/services/date_utils/date_utils.js' import { uniq } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' +import Select from '../select/select.vue' import { faTimes, - faChevronDown, faPlus } from '@fortawesome/free-solid-svg-icons' library.add( faTimes, - faChevronDown, faPlus ) export default { + components: { + Select + }, name: 'PollForm', props: ['visible'], data: () => ({ diff --git a/src/components/poll/poll_form.vue b/src/components/poll/poll_form.vue @@ -46,23 +46,19 @@ class="poll-type" :title="$t('polls.type')" > - <label - for="poll-type-selector" - class="select" + <Select + v-model="pollType" + class="poll-type-select" + unstyled="true" + @change="updatePollToParent" > - <select - v-model="pollType" - class="select" - @change="updatePollToParent" - > - <option value="single">{{ $t('polls.single_choice') }}</option> - <option value="multiple">{{ $t('polls.multiple_choices') }}</option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> + <option value="single"> + {{ $t('polls.single_choice') }} + </option> + <option value="multiple"> + {{ $t('polls.multiple_choices') }} + </option> + </Select> </div> <div class="poll-expiry" @@ -76,24 +72,20 @@ :max="maxExpirationInCurrentUnit" @change="expiryAmountChange" > - <label class="expiry-unit select"> - <select - v-model="expiryUnit" - @change="expiryAmountChange" + <Select + v-model="expiryUnit" + unstyled="true" + class="expiry-unit" + @change="expiryAmountChange" + > + <option + v-for="unit in expiryUnits" + :key="unit" + :value="unit" > - <option - v-for="unit in expiryUnits" - :key="unit" - :value="unit" - > - {{ $t(`time.${unit}_short`, ['']) }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> + {{ $t(`time.${unit}_short`, ['']) }} + </option> + </Select> </div> </div> </div> @@ -147,10 +139,8 @@ .poll-type { margin-right: 0.75em; flex: 1 1 60%; - .select { - border: none; - box-shadow: none; - background-color: transparent; + + .poll-type-select { padding-right: 0.75em; } } @@ -162,12 +152,6 @@ width: 3em; text-align: right; } - - .expiry-unit { - border: none; - box-shadow: none; - background-color: transparent; - } } } </style> diff --git a/src/components/popover/popover.js b/src/components/popover/popover.js @@ -3,25 +3,32 @@ const 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 selector to use as a replacement for the parent container // for getting boundaries for x an y axis boundToSelector: String, + // 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, + // Replaces the classes you may want for the popover container. // Use 'popover-default' in addition to get the default popover // styles with your custom class. popoverClass: String, + // If true, subtract padding when calculating position for the popover, // use it when popover offset looks to be different on top vs bottom. removePadding: Boolean @@ -47,8 +54,11 @@ const Popover = { } // 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". + // its children are what are inside the slot. Expect only one v-slot:trigger. const anchorEl = (this.$refs.trigger && this.$refs.trigger.children[0]) || this.$el + // SVGs don't have offsetWidth/Height, use fallback + const anchorWidth = anchorEl.offsetWidth || anchorEl.clientWidth + const anchorHeight = anchorEl.offsetHeight || anchorEl.clientHeight const screenBox = anchorEl.getBoundingClientRect() // Screen position of the origin point for popover const origin = { x: screenBox.left + screenBox.width * 0.5, y: screenBox.top } @@ -107,11 +117,11 @@ const Popover = { const yOffset = (this.offset && this.offset.y) || 0 const translateY = usingTop - ? -anchorEl.offsetHeight + vPadding - yOffset - content.offsetHeight + ? -anchorHeight + vPadding - yOffset - content.offsetHeight : yOffset const xOffset = (this.offset && this.offset.x) || 0 - const translateX = (anchorEl.offsetWidth * 0.5) - content.offsetWidth * 0.5 + horizOffset + xOffset + const translateX = anchorWidth * 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. diff --git a/src/components/popover/popover.vue b/src/components/popover/popover.vue @@ -82,10 +82,9 @@ .dropdown-item { line-height: 21px; - margin-right: 5px; overflow: auto; display: block; - padding: .25rem 1.0rem .25rem 1.5rem; + padding: .5em 0.75em; clear: both; font-weight: 400; text-align: inherit; @@ -101,10 +100,9 @@ --btnText: var(--popoverText, $fallback--text); &-icon { - padding-left: 0.5rem; - svg { - margin-right: 0.25rem; + width: 22px; + margin-right: 0.75rem; color: var(--menuPopoverIcon, $fallback--icon) } } @@ -123,6 +121,33 @@ } } + .menu-checkbox { + display: inline-block; + vertical-align: middle; + min-width: 22px; + max-width: 22px; + min-height: 22px; + max-height: 22px; + line-height: 22px; + text-align: center; + border-radius: 0px; + background-color: $fallback--fg; + background-color: var(--input, $fallback--fg); + box-shadow: 0px 0px 2px black inset; + box-shadow: var(--inputShadow); + margin-right: 0.75em; + + &.menu-checkbox-checked::after { + font-size: 1.25em; + content: '✓'; + } + + &.menu-checkbox-radio::after { + font-size: 2em; + content: '•'; + } + } + } } </style> diff --git a/src/components/post_status_form/post_status_form.js b/src/components/post_status_form/post_status_form.js @@ -11,10 +11,10 @@ import { reject, map, uniqBy, debounce } from 'lodash' import suggestor from '../emoji_input/suggestor.js' import { mapGetters, mapState } from 'vuex' import Checkbox from '../checkbox/checkbox.vue' +import Select from '../select/select.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { - faChevronDown, faSmileBeam, faPollH, faUpload, @@ -24,7 +24,6 @@ import { } from '@fortawesome/free-solid-svg-icons' library.add( - faChevronDown, faSmileBeam, faPollH, faUpload, @@ -84,6 +83,7 @@ const PostStatusForm = { PollForm, ScopeSelector, Checkbox, + Select, Attachment, StatusContent }, @@ -115,7 +115,7 @@ const PostStatusForm = { ? this.copyMessageScope : this.$store.state.users.currentUser.default_scope - const { postContentType: contentType } = this.$store.getters.mergedConfig + const { postContentType: contentType, sensitiveByDefault } = this.$store.getters.mergedConfig return { dropFiles: [], @@ -126,7 +126,7 @@ const PostStatusForm = { newStatus: { spoilerText: this.subject || '', status: statusText, - nsfw: false, + nsfw: !!sensitiveByDefault, files: [], poll: {}, mediaDescriptions: {}, diff --git a/src/components/post_status_form/post_status_form.vue b/src/components/post_status_form/post_status_form.vue @@ -189,28 +189,19 @@ v-if="postFormats.length > 1" class="text-format" > - <label - for="post-content-type" - class="select" + <Select + id="post-content-type" + v-model="newStatus.contentType" + class="form-control" > - <select - id="post-content-type" - v-model="newStatus.contentType" - class="form-control" + <option + v-for="postFormat in postFormats" + :key="postFormat" + :value="postFormat" > - <option - v-for="postFormat in postFormats" - :key="postFormat" - :value="postFormat" - > - {{ $t(`post_status.content_type["${postFormat}"]`) }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> + {{ $t(`post_status.content_type["${postFormat}"]`) }} + </option> + </Select> </div> <div v-if="postFormats.length === 1 && postFormats[0] !== 'text/plain'" @@ -272,7 +263,7 @@ disabled class="btn button-default" > - {{ $t('general.submit') }} + {{ $t('post_status.post') }} </button> <!-- touchstart is used to keep the OSK at the same position after a message send --> <button @@ -282,7 +273,7 @@ @touchstart.stop.prevent="postStatus($event, newStatus)" @click.stop.prevent="postStatus($event, newStatus)" > - {{ $t('general.submit') }} + {{ $t('post_status.post') }} </button> </div> <div diff --git a/src/components/react_button/react_button.vue b/src/components/react_button/react_button.vue @@ -8,10 +8,7 @@ remove-padding @show="focusInput" > - <div - slot="content" - slot-scope="{close}" - > + <template v-slot:content="{close}"> <div class="reaction-picker-filter"> <input v-model="filterWord" @@ -41,17 +38,18 @@ </span> <div class="reaction-bottom-fader" /> </div> - </div> - <span - slot="trigger" - class="popover-trigger" - :title="$t('tool_tip.add_reaction')" - > - <FAIcon - class="fa-scale-110 fa-old-padding" - :icon="['far', 'smile-beam']" - /> - </span> + </template> + <template v-slot:trigger> + <button + class="button-unstyled popover-trigger" + :title="$t('tool_tip.add_reaction')" + > + <FAIcon + class="fa-scale-110 fa-old-padding" + :icon="['far', 'smile-beam']" + /> + </button> + </template> </Popover> </template> diff --git a/src/components/registration/registration.vue b/src/components/registration/registration.vue @@ -230,7 +230,7 @@ type="submit" class="btn button-default" > - {{ $t('general.submit') }} + {{ $t('registration.register') }} </button> </div> </div> diff --git a/src/components/select/select.js b/src/components/select/select.js @@ -0,0 +1,21 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faChevronDown +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faChevronDown +) + +export default { + model: { + prop: 'value', + event: 'change' + }, + props: [ + 'value', + 'disabled', + 'unstyled', + 'kind' + ] +} diff --git a/src/components/select/select.vue b/src/components/select/select.vue @@ -0,0 +1,62 @@ + +<template> + <label + class="Select input" + :class="{ disabled, unstyled }" + > + <select + :disabled="disabled" + :value="value" + @change="$emit('change', $event.target.value)" + > + <slot /> + </select> + <FAIcon + class="select-down-icon" + icon="chevron-down" + /> + </label> +</template> + +<script src="./select.js"> </script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.Select { + padding: 0; + + select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background: transparent; + border: none; + color: $fallback--text; + color: var(--inputText, --text, $fallback--text); + margin: 0; + padding: 0 2em 0 .2em; + font-family: sans-serif; + font-family: var(--inputFont, sans-serif); + font-size: 14px; + width: 100%; + z-index: 1; + height: 28px; + line-height: 16px; + } + + .select-down-icon { + position: absolute; + top: 0; + bottom: 0; + right: 5px; + height: 100%; + color: $fallback--text; + color: var(--inputText, $fallback--text); + line-height: 28px; + z-index: 0; + pointer-events: none; + } + +} +</style> diff --git a/src/components/selectable_list/selectable_list.vue b/src/components/selectable_list/selectable_list.vue @@ -24,10 +24,7 @@ :items="items" :get-key="getKey" > - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <div class="selectable-list-item-inner" :class="{ 'selectable-list-item-selected-inner': isSelected(item) }" @@ -44,7 +41,7 @@ /> </div> </template> - <template slot="empty"> + <template v-slot:empty> <slot name="empty" /> </template> </List> diff --git a/src/components/settings_modal/helpers/boolean_setting.js b/src/components/settings_modal/helpers/boolean_setting.js @@ -0,0 +1,38 @@ +import { get, set } from 'lodash' +import Checkbox from 'src/components/checkbox/checkbox.vue' +import ModifiedIndicator from './modified_indicator.vue' +export default { + components: { + Checkbox, + ModifiedIndicator + }, + props: [ + 'path', + 'disabled' + ], + computed: { + pathDefault () { + const [firstSegment, ...rest] = this.path.split('.') + return [firstSegment + 'DefaultValue', ...rest].join('.') + }, + state () { + const value = get(this.$parent, this.path) + if (value === undefined) { + return this.defaultState + } else { + return value + } + }, + defaultState () { + return get(this.$parent, this.pathDefault) + }, + isChanged () { + return this.state !== this.defaultState + } + }, + methods: { + update (e) { + set(this.$parent, this.path, e) + } + } +} diff --git a/src/components/settings_modal/helpers/boolean_setting.vue b/src/components/settings_modal/helpers/boolean_setting.vue @@ -18,40 +18,4 @@ </label> </template> -<script> -import { get, set } from 'lodash' -import Checkbox from 'src/components/checkbox/checkbox.vue' -import ModifiedIndicator from './modified_indicator.vue' -export default { - components: { - Checkbox, - ModifiedIndicator - }, - props: [ - 'path', - 'disabled' - ], - computed: { - pathDefault () { - const [firstSegment, ...rest] = this.path.split('.') - return [firstSegment + 'DefaultValue', ...rest].join('.') - }, - state () { - return get(this.$parent, this.path) - }, - isChanged () { - return get(this.$parent, this.path) !== get(this.$parent, this.pathDefault) - } - }, - methods: { - update (e) { - set(this.$parent, this.path, e) - } - } -} -</script> - -<style lang="scss"> -.BooleanSetting { -} -</style> +<script src="./boolean_setting.js"></script> diff --git a/src/components/settings_modal/helpers/choice_setting.js b/src/components/settings_modal/helpers/choice_setting.js @@ -0,0 +1,39 @@ +import { get, set } from 'lodash' +import Select from 'src/components/select/select.vue' +import ModifiedIndicator from './modified_indicator.vue' +export default { + components: { + Select, + ModifiedIndicator + }, + props: [ + 'path', + 'disabled', + 'options' + ], + computed: { + pathDefault () { + const [firstSegment, ...rest] = this.path.split('.') + return [firstSegment + 'DefaultValue', ...rest].join('.') + }, + state () { + const value = get(this.$parent, this.path) + if (value === undefined) { + return this.defaultState + } else { + return value + } + }, + defaultState () { + return get(this.$parent, this.pathDefault) + }, + isChanged () { + return this.state !== this.defaultState + } + }, + methods: { + update (e) { + set(this.$parent, this.path, e) + } + } +} diff --git a/src/components/settings_modal/helpers/choice_setting.vue b/src/components/settings_modal/helpers/choice_setting.vue @@ -0,0 +1,29 @@ +<template> + <label + class="ChoiceSetting" + > + <slot /> + <Select + :value="state" + :disabled="disabled" + @change="update" + > + <option + v-for="option in options" + :key="option.key" + :value="option.value" + > + {{ option.label }} + {{ option.value === defaultState ? $t('settings.instance_default_simple') : '' }} + </option> + </Select> + <ModifiedIndicator :changed="isChanged" /> + </label> +</template> + +<script src="./choice_setting.js"></script> + +<style lang="scss"> +.ChoiceSetting { +} +</style> diff --git a/src/components/settings_modal/helpers/modified_indicator.vue b/src/components/settings_modal/helpers/modified_indicator.vue @@ -6,18 +6,18 @@ <Popover trigger="hover" > - <span slot="trigger"> + <template v-slot:trigger> &nbsp; <FAIcon icon="wrench" + :aria-label="$t('settings.setting_changed')" /> - </span> - <div - slot="content" - class="modified-tooltip" - > - {{ $t('settings.setting_changed') }} - </div> + </template> + <template v-slot:content> + <div class="modified-tooltip"> + {{ $t('settings.setting_changed') }} + </div> + </template> </Popover> </span> </template> diff --git a/src/components/settings_modal/settings_modal.js b/src/components/settings_modal/settings_modal.js @@ -2,10 +2,55 @@ import Modal from 'src/components/modal/modal.vue' import PanelLoading from 'src/components/panel_loading/panel_loading.vue' import AsyncComponentError from 'src/components/async_component_error/async_component_error.vue' import getResettableAsyncComponent from 'src/services/resettable_async_component.js' +import Popover from '../popover/popover.vue' +import { library } from '@fortawesome/fontawesome-svg-core' +import { cloneDeep } from 'lodash' +import { + newImporter, + newExporter +} from 'src/services/export_import/export_import.js' +import { + faTimes, + faFileUpload, + faFileDownload, + faChevronDown +} from '@fortawesome/free-solid-svg-icons' +import { + faWindowMinimize +} from '@fortawesome/free-regular-svg-icons' + +const PLEROMAFE_SETTINGS_MAJOR_VERSION = 1 +const PLEROMAFE_SETTINGS_MINOR_VERSION = 0 + +library.add( + faTimes, + faWindowMinimize, + faFileUpload, + faFileDownload, + faChevronDown +) const SettingsModal = { + data () { + return { + dataImporter: newImporter({ + validator: this.importValidator, + onImport: this.onImport, + onImportFailure: this.onImportFailure + }), + dataThemeExporter: newExporter({ + filename: 'pleromafe_settings.full', + getExportedObject: () => this.generateExport(true) + }), + dataExporter: newExporter({ + filename: 'pleromafe_settings', + getExportedObject: () => this.generateExport() + }) + } + }, components: { Modal, + Popover, SettingsModalContent: getResettableAsyncComponent( () => import('./settings_modal_content.vue'), { @@ -21,6 +66,85 @@ const SettingsModal = { }, peekModal () { this.$store.dispatch('togglePeekSettingsModal') + }, + importValidator (data) { + if (!Array.isArray(data._pleroma_settings_version)) { + return { + messageKey: 'settings.file_import_export.invalid_file' + } + } + + const [major, minor] = data._pleroma_settings_version + + if (major > PLEROMAFE_SETTINGS_MAJOR_VERSION) { + return { + messageKey: 'settings.file_export_import.errors.file_too_new', + messageArgs: { + fileMajor: major, + feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION + } + } + } + + if (major < PLEROMAFE_SETTINGS_MAJOR_VERSION) { + return { + messageKey: 'settings.file_export_import.errors.file_too_old', + messageArgs: { + fileMajor: major, + feMajor: PLEROMAFE_SETTINGS_MAJOR_VERSION + } + } + } + + if (minor > PLEROMAFE_SETTINGS_MINOR_VERSION) { + this.$store.dispatch('pushGlobalNotice', { + level: 'warning', + messageKey: 'settings.file_export_import.errors.file_slightly_new' + }) + } + + return true + }, + onImportFailure (result) { + if (result.error) { + this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_settings_imported', level: 'error' }) + } else { + this.$store.dispatch('pushGlobalNotice', { ...result.validationResult, level: 'error' }) + } + }, + onImport (data) { + if (data) { this.$store.dispatch('loadSettings', data) } + }, + restore () { + this.dataImporter.importData() + }, + backup () { + this.dataExporter.exportData() + }, + backupWithTheme () { + this.dataThemeExporter.exportData() + }, + generateExport (theme = false) { + const { config } = this.$store.state + let sample = config + if (!theme) { + const ignoreList = new Set([ + 'customTheme', + 'customThemeSource', + 'colors' + ]) + sample = Object.fromEntries( + Object + .entries(sample) + .filter(([key]) => !ignoreList.has(key)) + ) + } + const clone = cloneDeep(sample) + clone._pleroma_settings_version = [ + PLEROMAFE_SETTINGS_MAJOR_VERSION, + PLEROMAFE_SETTINGS_MINOR_VERSION + ] + return clone } }, computed: { diff --git a/src/components/settings_modal/settings_modal.vue b/src/components/settings_modal/settings_modal.vue @@ -31,20 +31,84 @@ </transition> <button class="btn button-default" + :title="$t('general.peek')" @click="peekModal" > - {{ $t('general.peek') }} + <FAIcon + :icon="['far', 'window-minimize']" + fixed-width + /> </button> <button class="btn button-default" + :title="$t('general.close')" @click="closeModal" > - {{ $t('general.close') }} + <FAIcon + icon="times" + fixed-width + /> </button> </div> <div class="panel-body"> <SettingsModalContent v-if="modalOpenedOnce" /> </div> + <div class="panel-footer"> + <Popover + class="export" + trigger="click" + placement="top" + :offset="{ y: 5, x: 5 }" + :bound-to="{ x: 'container' }" + remove-padding + > + <template v-slot:trigger> + <button + class="btn button-default" + :title="$t('general.close')" + > + <span>{{ $t("settings.file_export_import.backup_restore") }}</span> + <FAIcon + icon="chevron-down" + /> + </button> + </template> + <template v-slot:content="{close}"> + <div class="dropdown-menu"> + <button + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="backup" + @click="close" + > + <FAIcon + icon="file-download" + fixed-width + /><span>{{ $t("settings.file_export_import.backup_settings") }}</span> + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="backupWithTheme" + @click="close" + > + <FAIcon + icon="file-download" + fixed-width + /><span>{{ $t("settings.file_export_import.backup_settings_theme") }}</span> + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click.prevent="restore" + @click="close" + > + <FAIcon + icon="file-upload" + fixed-width + /><span>{{ $t("settings.file_export_import.restore_settings") }}</span> + </button> + </div> + </template> + </Popover> + </div> </div> </Modal> </template> diff --git a/src/components/settings_modal/settings_modal_content.scss b/src/components/settings_modal/settings_modal_content.scss @@ -7,13 +7,24 @@ margin: 1em 1em 1.4em; padding-bottom: 1.4em; - > div { + > div, + > label { + display: block; margin-bottom: .5em; &:last-child { margin-bottom: 0; } } + .select-multiple { + display: flex; + + .option-list { + margin: 0; + padding-left: .5em; + } + } + &:last-child { border-bottom: none; padding-bottom: 0; diff --git a/src/components/settings_modal/tabs/filtering_tab.js b/src/components/settings_modal/tabs/filtering_tab.js @@ -1,24 +1,23 @@ import { filter, trim } from 'lodash' import BooleanSetting from '../helpers/boolean_setting.vue' +import ChoiceSetting from '../helpers/choice_setting.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faChevronDown -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faChevronDown -) const FilteringTab = { data () { return { - muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n') + muteWordsStringLocal: this.$store.getters.mergedConfig.muteWords.join('\n'), + replyVisibilityOptions: ['all', 'following', 'self'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.reply_visibility_${mode}`) + })) } }, components: { - BooleanSetting + BooleanSetting, + ChoiceSetting }, computed: { ...SharedComputedObject(), diff --git a/src/components/settings_modal/tabs/filtering_tab.vue b/src/components/settings_modal/tabs/filtering_tab.vue @@ -36,29 +36,13 @@ </li> </ul> </div> - <div> + <ChoiceSetting + id="replyVisibility" + path="replyVisibility" + :options="replyVisibilityOptions" + > {{ $t('settings.replies_in_timeline') }} - <label - for="replyVisibility" - class="select" - > - <select - id="replyVisibility" - v-model="replyVisibility" - > - <option - value="all" - selected - >{{ $t('settings.reply_visibility_all') }}</option> - <option value="following">{{ $t('settings.reply_visibility_following') }}</option> - <option value="self">{{ $t('settings.reply_visibility_self') }}</option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> - </div> + </ChoiceSetting> <div> <BooleanSetting path="hidePostStats"> {{ $t('settings.hide_post_stats') }} diff --git a/src/components/settings_modal/tabs/general_tab.js b/src/components/settings_modal/tabs/general_tab.js @@ -1,21 +1,25 @@ import BooleanSetting from '../helpers/boolean_setting.vue' +import ChoiceSetting from '../helpers/choice_setting.vue' import InterfaceLanguageSwitcher from 'src/components/interface_language_switcher/interface_language_switcher.vue' import SharedComputedObject from '../helpers/shared_computed_object.js' import { library } from '@fortawesome/fontawesome-svg-core' import { - faChevronDown, faGlobe } from '@fortawesome/free-solid-svg-icons' library.add( - faChevronDown, faGlobe ) const GeneralTab = { data () { return { + subjectLineOptions: ['email', 'noop', 'masto'].map(mode => ({ + key: mode, + value: mode, + label: this.$t(`settings.subject_line_${mode === 'masto' ? 'mastodon' : mode}`) + })), loopSilentAvailable: // Firefox Object.getOwnPropertyDescriptor(HTMLVideoElement.prototype, 'mozHasAudio') || @@ -27,17 +31,26 @@ const GeneralTab = { }, components: { BooleanSetting, + ChoiceSetting, InterfaceLanguageSwitcher }, computed: { postFormats () { return this.$store.state.instance.postFormats || [] }, + postContentOptions () { + return this.postFormats.map(format => ({ + key: format, + value: format, + label: this.$t(`post_status.content_type["${format}"]`) + })) + }, instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }, instanceWallpaperUsed () { return this.$store.state.instance.background && !this.$store.state.users.currentUser.background_image }, + instanceShoutboxPresent () { return this.$store.state.instance.shoutAvailable }, ...SharedComputedObject() } } diff --git a/src/components/settings_modal/tabs/general_tab.vue b/src/components/settings_modal/tabs/general_tab.vue @@ -11,11 +11,21 @@ {{ $t('settings.hide_isp') }} </BooleanSetting> </li> + <li> + <BooleanSetting path="sidebarRight"> + {{ $t('settings.right_sidebar') }} + </BooleanSetting> + </li> <li v-if="instanceWallpaperUsed"> <BooleanSetting path="hideInstanceWallpaper"> {{ $t('settings.hide_wallpaper') }} </BooleanSetting> </li> + <li v-if="instanceShoutboxPresent"> + <BooleanSetting path="hideShoutbox"> + {{ $t('settings.hide_shoutbox') }} + </BooleanSetting> + </li> </ul> </div> <div class="setting-item"> @@ -85,66 +95,31 @@ </BooleanSetting> </li> <li> - <div> + <ChoiceSetting + id="subjectLineBehavior" + path="subjectLineBehavior" + :options="subjectLineOptions" + > {{ $t('settings.subject_line_behavior') }} - <label - for="subjectLineBehavior" - class="select" - > - <select - id="subjectLineBehavior" - v-model="subjectLineBehavior" - > - <option value="email"> - {{ $t('settings.subject_line_email') }} - {{ subjectLineBehaviorDefaultValue == 'email' ? $t('settings.instance_default_simple') : '' }} - </option> - <option value="masto"> - {{ $t('settings.subject_line_mastodon') }} - {{ subjectLineBehaviorDefaultValue == 'mastodon' ? $t('settings.instance_default_simple') : '' }} - </option> - <option value="noop"> - {{ $t('settings.subject_line_noop') }} - {{ subjectLineBehaviorDefaultValue == 'noop' ? $t('settings.instance_default_simple') : '' }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> - </div> + </ChoiceSetting> </li> <li v-if="postFormats.length > 0"> - <div> + <ChoiceSetting + id="postContentType" + path="postContentType" + :options="postContentOptions" + > {{ $t('settings.post_status_content_type') }} - <label - for="postContentType" - class="select" - > - <select - id="postContentType" - v-model="postContentType" - > - <option - v-for="postFormat in postFormats" - :key="postFormat" - :value="postFormat" - > - {{ $t(`post_status.content_type["${postFormat}"]`) }} - {{ postContentTypeDefaultValue === postFormat ? $t('settings.instance_default_simple') : '' }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> - </div> + </ChoiceSetting> </li> <li> <BooleanSetting path="minimalScopesMode"> - {{ $t('settings.minimal_scopes_mode') }} {{ minimalScopesModeDefaultValue }} + {{ $t('settings.minimal_scopes_mode') }} + </BooleanSetting> + </li> + <li> + <BooleanSetting path="sensitiveByDefault"> + {{ $t('settings.sensitive_by_default') }} </BooleanSetting> </li> <li> diff --git a/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue b/src/components/settings_modal/tabs/mutes_and_blocks_tab.vue @@ -10,20 +10,18 @@ :query="queryUserIds" :placeholder="$t('settings.search_user_to_block')" > - <BlockCard - slot-scope="row" - :user-id="row.item" - /> + <template v-slot="row"> + <BlockCard + :user-id="row.item" + /> + </template> </Autosuggest> </div> <BlockList :refresh="true" :get-key="i => i" > - <template - slot="header" - slot-scope="{selected}" - > + <template v-slot:header="{selected}"> <div class="bulk-actions"> <ProgressButton v-if="selected.length > 0" @@ -31,7 +29,7 @@ :click="() => blockUsers(selected)" > {{ $t('user_card.block') }} - <template slot="progress"> + <template v-slot:progress> {{ $t('user_card.block_progress') }} </template> </ProgressButton> @@ -41,19 +39,16 @@ :click="() => unblockUsers(selected)" > {{ $t('user_card.unblock') }} - <template slot="progress"> + <template v-slot:progress> {{ $t('user_card.unblock_progress') }} </template> </ProgressButton> </div> </template> - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <BlockCard :user-id="item" /> </template> - <template slot="empty"> + <template v-slot:empty> {{ $t('settings.no_blocks') }} </template> </BlockList> @@ -68,20 +63,18 @@ :query="queryUserIds" :placeholder="$t('settings.search_user_to_mute')" > - <MuteCard - slot-scope="row" - :user-id="row.item" - /> + <template v-slot="row"> + <MuteCard + :user-id="row.item" + /> + </template> </Autosuggest> </div> <MuteList :refresh="true" :get-key="i => i" > - <template - slot="header" - slot-scope="{selected}" - > + <template v-slot:header="{selected}"> <div class="bulk-actions"> <ProgressButton v-if="selected.length > 0" @@ -89,7 +82,7 @@ :click="() => muteUsers(selected)" > {{ $t('user_card.mute') }} - <template slot="progress"> + <template v-slot:progress> {{ $t('user_card.mute_progress') }} </template> </ProgressButton> @@ -99,19 +92,16 @@ :click="() => unmuteUsers(selected)" > {{ $t('user_card.unmute') }} - <template slot="progress"> + <template v-slot:progress> {{ $t('user_card.unmute_progress') }} </template> </ProgressButton> </div> </template> - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <MuteCard :user-id="item" /> </template> - <template slot="empty"> + <template v-slot:empty> {{ $t('settings.no_mutes') }} </template> </MuteList> @@ -124,20 +114,18 @@ :query="queryKnownDomains" :placeholder="$t('settings.type_domains_to_mute')" > - <DomainMuteCard - slot-scope="row" - :domain="row.item" - /> + <template v-slot="row"> + <DomainMuteCard + :domain="row.item" + /> + </template> </Autosuggest> </div> <DomainMuteList :refresh="true" :get-key="i => i" > - <template - slot="header" - slot-scope="{selected}" - > + <template v-slot:header="{selected}"> <div class="bulk-actions"> <ProgressButton v-if="selected.length > 0" @@ -145,19 +133,16 @@ :click="() => unmuteDomains(selected)" > {{ $t('domain_mute_card.unmute') }} - <template slot="progress"> + <template v-slot:progress> {{ $t('domain_mute_card.unmute_progress') }} </template> </ProgressButton> </div> </template> - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <DomainMuteCard :domain="item" /> </template> - <template slot="empty"> + <template v-slot:empty> {{ $t('settings.no_mutes') }} </template> </DomainMuteList> diff --git a/src/components/settings_modal/tabs/notifications_tab.vue b/src/components/settings_modal/tabs/notifications_tab.vue @@ -24,7 +24,7 @@ class="btn button-default" @click="updateNotificationSettings" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/settings_modal/tabs/profile_tab.vue b/src/components/settings_modal/tabs/profile_tab.vue @@ -153,7 +153,7 @@ class="btn button-default" @click="updateProfile" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> <div class="setting-item"> @@ -227,7 +227,7 @@ class="btn button-default" @click="submitBanner(banner)" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> <div class="setting-item"> @@ -266,7 +266,7 @@ class="btn button-default" @click="submitBackground(background)" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/settings_modal/tabs/security_tab/security_tab.vue b/src/components/settings_modal/tabs/security_tab/security_tab.vue @@ -22,7 +22,7 @@ class="btn button-default" @click="changeEmail" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> <p v-if="changedEmail"> {{ $t('settings.changed_email') }} @@ -60,7 +60,7 @@ class="btn button-default" @click="changePassword" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> <p v-if="changedPassword"> {{ $t('settings.changed_password') }} @@ -133,7 +133,7 @@ class="btn button-default" @click="confirmDelete" > - {{ $t('general.submit') }} + {{ $t('settings.save') }} </button> </div> </div> diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.js b/src/components/settings_modal/tabs/theme_tab/theme_tab.js @@ -16,6 +16,10 @@ import { colors2to3 } from 'src/services/style_setter/style_setter.js' import { + newImporter, + newExporter +} from 'src/services/export_import/export_import.js' +import { SLOT_INHERITANCE } from 'src/services/theme_data/pleromafe.js' import { @@ -31,18 +35,10 @@ import ShadowControl from 'src/components/shadow_control/shadow_control.vue' import FontControl from 'src/components/font_control/font_control.vue' import ContrastRatio from 'src/components/contrast_ratio/contrast_ratio.vue' import TabSwitcher from 'src/components/tab_switcher/tab_switcher.js' -import ExportImport from 'src/components/export_import/export_import.vue' import Checkbox from 'src/components/checkbox/checkbox.vue' +import Select from 'src/components/select/select.vue' import Preview from './preview.vue' -import { library } from '@fortawesome/fontawesome-svg-core' -import { - faChevronDown -} from '@fortawesome/free-solid-svg-icons' - -library.add( - faChevronDown -) // List of color values used in v1 const v1OnlyNames = [ @@ -67,8 +63,18 @@ const colorConvert = (color) => { export default { data () { return { + themeImporter: newImporter({ + validator: this.importValidator, + onImport: this.onImport, + onImportFailure: this.onImportFailure + }), + themeExporter: newExporter({ + filename: 'pleroma_theme', + getExportedObject: () => this.exportedTheme + }), availableStyles: [], - selected: this.$store.getters.mergedConfig.theme, + selected: '', + selectedTheme: this.$store.getters.mergedConfig.theme, themeWarning: undefined, tempImportFile: undefined, engineVersion: 0, @@ -202,7 +208,7 @@ export default { } }, selectedVersion () { - return Array.isArray(this.selected) ? 1 : 2 + return Array.isArray(this.selectedTheme) ? 1 : 2 }, currentColors () { return Object.keys(SLOT_INHERITANCE) @@ -383,8 +389,8 @@ export default { FontControl, TabSwitcher, Preview, - ExportImport, - Checkbox + Checkbox, + Select }, methods: { loadTheme ( @@ -528,10 +534,15 @@ export default { this.previewColors.mod ) }, + importTheme () { this.themeImporter.importData() }, + exportTheme () { this.themeExporter.exportData() }, onImport (parsed, forceSource = false) { this.tempImportFile = parsed this.loadTheme(parsed, 'file', forceSource) }, + onImportFailure (result) { + this.$store.dispatch('pushGlobalNotice', { messageKey: 'settings.invalid_theme_imported', level: 'error' }) + }, importValidator (parsed) { const version = parsed._pleroma_theme_version return version >= 1 || version <= 2 @@ -735,6 +746,16 @@ export default { } }, selected () { + this.selectedTheme = Object.entries(this.availableStyles).find(([k, s]) => { + if (Array.isArray(s)) { + console.log(s[0] === this.selected, this.selected) + return s[0] === this.selected + } else { + return s.name === this.selected + } + })[1] + }, + selectedTheme () { this.dismissWarning() if (this.selectedVersion === 1) { if (!this.keepRoundness) { @@ -752,17 +773,17 @@ export default { if (!this.keepColor) { this.clearV1() - this.bgColorLocal = this.selected[1] - this.fgColorLocal = this.selected[2] - this.textColorLocal = this.selected[3] - this.linkColorLocal = this.selected[4] - this.cRedColorLocal = this.selected[5] - this.cGreenColorLocal = this.selected[6] - this.cBlueColorLocal = this.selected[7] - this.cOrangeColorLocal = this.selected[8] + this.bgColorLocal = this.selectedTheme[1] + this.fgColorLocal = this.selectedTheme[2] + this.textColorLocal = this.selectedTheme[3] + this.linkColorLocal = this.selectedTheme[4] + this.cRedColorLocal = this.selectedTheme[5] + this.cGreenColorLocal = this.selectedTheme[6] + this.cBlueColorLocal = this.selectedTheme[7] + this.cOrangeColorLocal = this.selectedTheme[8] } } else if (this.selectedVersion >= 2) { - this.normalizeLocalState(this.selected.theme, 2, this.selected.source) + this.normalizeLocalState(this.selectedTheme.theme, 2, this.selectedTheme.source) } } } diff --git a/src/components/settings_modal/tabs/theme_tab/theme_tab.vue b/src/components/settings_modal/tabs/theme_tab/theme_tab.vue @@ -48,46 +48,47 @@ </template> </div> </div> - <ExportImport - :export-object="exportedTheme" - :export-label="$t(&quot;settings.export_theme&quot;)" - :import-label="$t(&quot;settings.import_theme&quot;)" - :import-failed-text="$t(&quot;settings.invalid_theme_imported&quot;)" - :on-import="onImport" - :validator="importValidator" - > - <template slot="before"> - <div class="presets"> - {{ $t('settings.presets') }} - <label - for="preset-switcher" - class="select" + <div class="top"> + <div class="presets"> + {{ $t('settings.presets') }} + <label + for="preset-switcher" + class="select" + > + <Select + id="preset-switcher" + v-model="selected" + class="preset-switcher" > - <select - id="preset-switcher" - v-model="selected" - class="preset-switcher" + <option + v-for="style in availableStyles" + :key="style.name" + :value="style.name || style[0]" + :style="{ + backgroundColor: style[1] || (style.theme || style.source).colors.bg, + color: style[3] || (style.theme || style.source).colors.text + }" > - <option - v-for="style in availableStyles" - :key="style.name" - :value="style" - :style="{ - backgroundColor: style[1] || (style.theme || style.source).colors.bg, - color: style[3] || (style.theme || style.source).colors.text - }" - > - {{ style[0] || style.name }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> - </div> - </template> - </ExportImport> + {{ style[0] || style.name }} + </option> + </Select> + </label> + </div> + <div class="export-import"> + <button + class="btn button-default" + @click="importTheme" + > + {{ $t(&quot;settings.import_theme&quot;) }} + </button> + <button + class="btn button-default" + @click="exportTheme" + > + {{ $t(&quot;settings.export_theme&quot;) }} + </button> + </div> + </div> </div> <div class="save-load-options"> <span class="keep-option"> @@ -902,28 +903,19 @@ <div class="tab-header shadow-selector"> <div class="select-container"> {{ $t('settings.style.shadows.component') }} - <label - for="shadow-switcher" - class="select" + <Select + id="shadow-switcher" + v-model="shadowSelected" + class="shadow-switcher" > - <select - id="shadow-switcher" - v-model="shadowSelected" - class="shadow-switcher" + <option + v-for="shadow in shadowsAvailable" + :key="shadow" + :value="shadow" > - <option - v-for="shadow in shadowsAvailable" - :key="shadow" - :value="shadow" - > - {{ $t('settings.style.shadows.components.' + shadow) }} - </option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> + {{ $t('settings.style.shadows.components.' + shadow) }} + </option> + </Select> </div> <div class="override"> <label diff --git a/src/components/shadow_control/shadow_control.js b/src/components/shadow_control/shadow_control.js @@ -1,5 +1,6 @@ import ColorInput from '../color_input/color_input.vue' import OpacityInput from '../opacity_input/opacity_input.vue' +import Select from '../select/select.vue' import { getCssShadow } from '../../services/style_setter/style_setter.js' import { hex2rgb } from '../../services/color_convert/color_convert.js' import { library } from '@fortawesome/fontawesome-svg-core' @@ -45,7 +46,8 @@ export default { }, components: { ColorInput, - OpacityInput + OpacityInput, + Select }, methods: { add () { diff --git a/src/components/shadow_control/shadow_control.vue b/src/components/shadow_control/shadow_control.vue @@ -59,30 +59,20 @@ :disabled="usingFallback" class="id-control style-control" > - <label - for="shadow-switcher" - class="select" + <Select + id="shadow-switcher" + v-model="selectedId" + class="shadow-switcher" :disabled="!ready || usingFallback" > - <select - id="shadow-switcher" - v-model="selectedId" - class="shadow-switcher" - :disabled="!ready || usingFallback" + <option + v-for="(shadow, index) in cValue" + :key="index" + :value="index" > - <option - v-for="(shadow, index) in cValue" - :key="index" - :value="index" - > - {{ $t('settings.style.shadows.shadow_id', { value: index }) }} - </option> - </select> - <FAIcon - icon="chevron-down" - class="select-down-icon" - /> - </label> + {{ $t('settings.style.shadows.shadow_id', { value: index }) }} + </option> + </Select> <button class="btn button-default" :disabled="!ready || !present" @@ -316,20 +306,20 @@ .id-control { align-items: stretch; - .select, .btn { + + .shadow-switcher { + flex: 1; + } + + .shadow-switcher, .btn { min-width: 1px; margin-right: 5px; } + .btn { padding: 0 .4em; margin: 0 .1em; } - .select { - flex: 1; - select { - align-self: initial; - } - } } } } diff --git a/src/components/shout_panel/shout_panel.js b/src/components/shout_panel/shout_panel.js @@ -0,0 +1,53 @@ +import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faBullhorn, + faTimes +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faBullhorn, + faTimes +) + +const shoutPanel = { + props: [ 'floating' ], + data () { + return { + currentMessage: '', + channel: null, + collapsed: true + } + }, + computed: { + messages () { + return this.$store.state.shout.messages + } + }, + methods: { + submit (message) { + this.$store.state.shout.channel.push('new_msg', { text: message }, 10000) + this.currentMessage = '' + }, + togglePanel () { + this.collapsed = !this.collapsed + }, + userProfileLink (user) { + return generateProfileLink(user.id, user.username, this.$store.state.instance.restrictedNicknames) + } + }, + watch: { + messages (newVal) { + const scrollEl = this.$el.querySelector('.chat-window') + if (!scrollEl) return + if (scrollEl.scrollTop + scrollEl.offsetHeight + 20 > scrollEl.scrollHeight) { + this.$nextTick(() => { + if (!scrollEl) return + scrollEl.scrollTop = scrollEl.scrollHeight - scrollEl.offsetHeight + }) + } + } + } +} + +export default shoutPanel diff --git a/src/components/shout_panel/shout_panel.vue b/src/components/shout_panel/shout_panel.vue @@ -0,0 +1,148 @@ +<template> + <div + v-if="!collapsed || !floating" + class="shout-panel" + > + <div class="panel panel-default"> + <div + class="panel-heading timeline-heading" + :class="{ 'shout-heading': floating }" + @click.stop.prevent="togglePanel" + > + <div class="title"> + {{ $t('shoutbox.title') }} + <FAIcon + v-if="floating" + icon="times" + class="close-icon" + /> + </div> + </div> + <div class="shout-window"> + <div + v-for="message in messages" + :key="message.id" + class="shout-message" + > + <span class="shout-avatar"> + <img :src="message.author.avatar"> + </span> + <div class="shout-content"> + <router-link + class="shout-name" + :to="userProfileLink(message.author)" + > + {{ message.author.username }} + </router-link> + <br> + <span class="shout-text"> + {{ message.text }} + </span> + </div> + </div> + </div> + <div class="shout-input"> + <textarea + v-model="currentMessage" + class="shout-input-textarea" + rows="1" + @keyup.enter="submit(currentMessage)" + /> + </div> + </div> + </div> + <div + v-else + class="shout-panel" + > + <div class="panel panel-default"> + <div + class="panel-heading stub timeline-heading shout-heading" + @click.stop.prevent="togglePanel" + > + <div class="title"> + <FAIcon + class="icon" + icon="bullhorn" + /> + {{ $t('shoutbox.title') }} + </div> + </div> + </div> + </div> +</template> + +<script src="./shout_panel.js"></script> + +<style lang="scss"> +@import '../../_variables.scss'; + +.floating-shout { + position: fixed; + right: 0px; + bottom: 0px; + z-index: 1000; + max-width: 25em; +} + +.shout-panel { + .shout-heading { + cursor: pointer; + + .icon { + color: $fallback--text; + color: var(--text, $fallback--text); + margin-right: 0.5em; + } + + .title { + display: flex; + justify-content: space-between; + align-items: center; + } + } + + .shout-window { + overflow-y: auto; + overflow-x: hidden; + max-height: 20em; + } + + .shout-window-container { + height: 100%; + } + + .shout-message { + display: flex; + padding: 0.2em 0.5em + } + + .shout-avatar { + img { + height: 24px; + width: 24px; + border-radius: $fallback--avatarRadius; + border-radius: var(--avatarRadius, $fallback--avatarRadius); + margin-right: 0.5em; + margin-top: 0.25em; + } + } + + .shout-input { + display: flex; + textarea { + flex: 1; + margin: 0.6em; + min-height: 3.5em; + resize: none; + } + } + + .shout-panel { + .title { + display: flex; + justify-content: space-between; + } + } +} +</style> diff --git a/src/components/side_drawer/side_drawer.js b/src/components/side_drawer/side_drawer.js @@ -49,7 +49,6 @@ const SideDrawer = { currentUser () { return this.$store.state.users.currentUser }, - chat () { return this.$store.state.chat.channel.state === 'joined' }, unseenNotifications () { return unseenNotificationsFromStore(this.$store) }, diff --git a/src/components/side_drawer/side_drawer.vue b/src/components/side_drawer/side_drawer.vue @@ -273,9 +273,7 @@ --icon: var(--popoverIcon, $fallback--icon); .badge { - position: absolute; - right: 0.7rem; - top: 1em; + margin-left: 10px; } } diff --git a/src/components/status_popover/status_popover.vue b/src/components/status_popover/status_popover.vue @@ -5,12 +5,10 @@ :bound-to="{ x: 'container' }" @show="enter" > - <template slot="trigger"> + <template v-slot:trigger> <slot /> </template> - <div - slot="content" - > + <template v-slot:content> <Status v-if="status" :is-preview="true" @@ -33,7 +31,7 @@ size="2x" /> </div> - </div> + </template> </Popover> </template> diff --git a/src/components/timeline/timeline.js b/src/components/timeline/timeline.js @@ -2,12 +2,14 @@ import Status from '../status/status.vue' import timelineFetcher from '../../services/timeline_fetcher/timeline_fetcher.service.js' import Conversation from '../conversation/conversation.vue' import TimelineMenu from '../timeline_menu/timeline_menu.vue' +import TimelineQuickSettings from './timeline_quick_settings.vue' import { debounce, throttle, keyBy } from 'lodash' import { library } from '@fortawesome/fontawesome-svg-core' -import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' +import { faCircleNotch, faCog } from '@fortawesome/free-solid-svg-icons' library.add( - faCircleNotch + faCircleNotch, + faCog ) export const getExcludedStatusIdsByPinning = (statuses, pinnedStatusIds) => { @@ -47,7 +49,8 @@ const Timeline = { components: { Status, Conversation, - TimelineMenu + TimelineMenu, + TimelineQuickSettings }, computed: { newStatusCount () { diff --git a/src/components/timeline/timeline.scss b/src/components/timeline/timeline.scss @@ -0,0 +1,31 @@ +@import '../../_variables.scss'; + +.Timeline { + .loadmore-text { + opacity: 1; + } + + &.-blocked { + cursor: progress; + } + + .timeline-heading { + max-width: 100%; + flex-wrap: nowrap; + align-items: center; + position: relative; + + .loadmore-button { + flex-shrink: 0; + } + + .loadmore-text { + flex-shrink: 0; + line-height: 1em; + } + } + + .timeline-footer { + border: none; + } +} diff --git a/src/components/timeline/timeline.vue b/src/components/timeline/timeline.vue @@ -16,6 +16,7 @@ > {{ $t('timeline.up_to_date') }} </div> + <TimelineQuickSettings v-if="!embedded" /> </div> <div :class="classes.body"> <div @@ -51,13 +52,13 @@ <div :class="classes.footer"> <div v-if="count===0" - class="new-status-notification text-center panel-footer faint" + class="new-status-notification text-center faint" > {{ $t('timeline.no_statuses') }} </div> <div v-else-if="bottomedOut" - class="new-status-notification text-center panel-footer faint" + class="new-status-notification text-center faint" > {{ $t('timeline.no_more_statuses') }} </div> @@ -66,13 +67,13 @@ class="button-unstyled -link -fullwidth" @click.prevent="fetchOlderStatuses()" > - <div class="new-status-notification text-center panel-footer"> + <div class="new-status-notification text-center"> {{ $t('timeline.load_older') }} </div> </button> <div v-else - class="new-status-notification text-center panel-footer" + class="new-status-notification text-center" > <FAIcon icon="circle-notch" @@ -86,29 +87,4 @@ <script src="./timeline.js"></script> -<style lang="scss"> -@import '../../_variables.scss'; - -.Timeline { - .loadmore-text { - opacity: 1; - } - - &.-blocked { - cursor: progress; - } -} - -.timeline-heading { - max-width: 100%; - flex-wrap: nowrap; - align-items: center; - .loadmore-button { - flex-shrink: 0; - } - .loadmore-text { - flex-shrink: 0; - line-height: 1em; - } -} -</style> +<style src="./timeline.scss" lang="scss"> </style> diff --git a/src/components/timeline/timeline_quick_settings.js b/src/components/timeline/timeline_quick_settings.js @@ -0,0 +1,61 @@ +import Popover from '../popover/popover.vue' +import { mapGetters } from 'vuex' +import { library } from '@fortawesome/fontawesome-svg-core' +import { faFilter, faFont, faWrench } from '@fortawesome/free-solid-svg-icons' + +library.add( + faFilter, + faFont, + faWrench +) + +const TimelineQuickSettings = { + components: { + Popover + }, + methods: { + setReplyVisibility (visibility) { + this.$store.dispatch('setOption', { name: 'replyVisibility', value: visibility }) + this.$store.dispatch('queueFlushAll') + }, + openTab (tab) { + this.$store.dispatch('openSettingsModalTab', tab) + } + }, + computed: { + ...mapGetters(['mergedConfig']), + loggedIn () { + return !!this.$store.state.users.currentUser + }, + replyVisibilitySelf: { + get () { return this.mergedConfig.replyVisibility === 'self' }, + set () { this.setReplyVisibility('self') } + }, + replyVisibilityFollowing: { + get () { return this.mergedConfig.replyVisibility === 'following' }, + set () { this.setReplyVisibility('following') } + }, + replyVisibilityAll: { + get () { return this.mergedConfig.replyVisibility === 'all' }, + set () { this.setReplyVisibility('all') } + }, + hideMedia: { + get () { return this.mergedConfig.hideAttachments || this.mergedConfig.hideAttachmentsInConv }, + set () { + const value = !this.hideMedia + this.$store.dispatch('setOption', { name: 'hideAttachments', value }) + this.$store.dispatch('setOption', { name: 'hideAttachmentsInConv', value }) + } + }, + hideMutedPosts: { + get () { return this.mergedConfig.hideMutedPosts || this.mergedConfig.hideFilteredStatuses }, + set () { + const value = !this.hideMutedPosts + this.$store.dispatch('setOption', { name: 'hideMutedPosts', value }) + this.$store.dispatch('setOption', { name: 'hideFilteredStatuses', value }) + } + } + } +} + +export default TimelineQuickSettings diff --git a/src/components/timeline/timeline_quick_settings.vue b/src/components/timeline/timeline_quick_settings.vue @@ -0,0 +1,102 @@ +<template> + <Popover + trigger="click" + class="TimelineQuickSettings" + :bound-to="{ x: 'container' }" + > + <template v-slot:content> + <div class="dropdown-menu"> + <div v-if="loggedIn"> + <button + class="button-default dropdown-item" + @click="replyVisibilityAll = true" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-radio': replyVisibilityAll }" + />{{ $t('settings.reply_visibility_all') }} + </button> + <button + class="button-default dropdown-item" + @click="replyVisibilityFollowing = true" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-radio': replyVisibilityFollowing }" + />{{ $t('settings.reply_visibility_following_short') }} + </button> + <button + class="button-default dropdown-item" + @click="replyVisibilitySelf = true" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-radio': replyVisibilitySelf }" + />{{ $t('settings.reply_visibility_self_short') }} + </button> + <div + role="separator" + class="dropdown-divider" + /> + </div> + <button + class="button-default dropdown-item" + @click="hideMedia = !hideMedia" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': hideMedia }" + />{{ $t('settings.hide_media_previews') }} + </button> + <button + class="button-default dropdown-item" + @click="hideMutedPosts = !hideMutedPosts" + > + <span + class="menu-checkbox" + :class="{ 'menu-checkbox-checked': hideMutedPosts }" + />{{ $t('settings.hide_all_muted_posts') }} + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click="openTab('filtering')" + > + <FAIcon icon="font" />{{ $t('settings.word_filter') }} + </button> + <button + class="button-default dropdown-item dropdown-item-icon" + @click="openTab('general')" + > + <FAIcon icon="wrench" />{{ $t('settings.more_settings') }} + </button> + </div> + </template> + <template v-slot:trigger> + <button class="button-unstyled"> + <FAIcon icon="filter" /> + </button> + </template> + </Popover> +</template> + +<script src="./timeline_quick_settings.js"></script> + +<style lang="scss"> + +.TimelineQuickSettings { + align-self: stretch; + + > button { + font-size: 1.2em; + padding-left: 0.7em; + padding-right: 0.2em; + line-height: 100%; + height: 100%; + } + + .dropdown-item { + margin: 0; + } +} + +</style> diff --git a/src/components/timeline_menu/timeline_menu.js b/src/components/timeline_menu/timeline_menu.js @@ -1,29 +1,17 @@ import Popover from '../popover/popover.vue' -import { mapState } from 'vuex' +import TimelineMenuContent from './timeline_menu_content.vue' import { library } from '@fortawesome/fontawesome-svg-core' import { - faUsers, - faGlobe, - faBookmark, - faEnvelope, - faHome, faChevronDown } from '@fortawesome/free-solid-svg-icons' -library.add( - faUsers, - faGlobe, - faBookmark, - faEnvelope, - faHome, - faChevronDown -) +library.add(faChevronDown) // Route -> i18n key mapping, exported and not in the computed // because nav panel benefits from the same information. export const timelineNames = () => { return { - 'friends': 'nav.timeline', + 'friends': 'nav.home_timeline', 'bookmarks': 'nav.bookmarks', 'dms': 'nav.dms', 'public-timeline': 'nav.public_tl', @@ -33,7 +21,8 @@ export const timelineNames = () => { const TimelineMenu = { components: { - Popover + Popover, + TimelineMenuContent }, data () { return { @@ -41,9 +30,6 @@ const TimelineMenu = { } }, created () { - if (this.currentUser && this.currentUser.locked) { - this.$store.dispatch('startFetchingFollowRequests') - } if (timelineNames()[this.$route.name]) { this.$store.dispatch('setLastTimeline', this.$route.name) } @@ -75,13 +61,6 @@ const TimelineMenu = { const i18nkey = timelineNames()[this.$route.name] return i18nkey ? this.$t(i18nkey) : route } - }, - computed: { - ...mapState({ - currentUser: state => state.users.currentUser, - privateMode: state => state.instance.private, - federating: state => state.instance.federating - }) } } diff --git a/src/components/timeline_menu/timeline_menu.vue b/src/components/timeline_menu/timeline_menu.vue @@ -9,74 +9,26 @@ @show="openMenu" @close="() => isOpen = false" > - <div - slot="content" - class="timeline-menu-popover panel panel-default" - > - <ul> - <li v-if="currentUser"> - <router-link :to="{ name: 'friends' }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="home" - />{{ $t("nav.timeline") }} - </router-link> - </li> - <li v-if="currentUser"> - <router-link :to="{ name: 'bookmarks'}"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="bookmark" - />{{ $t("nav.bookmarks") }} - </router-link> - </li> - <li v-if="currentUser"> - <router-link :to="{ name: 'dms', params: { username: currentUser.screen_name } }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="envelope" - />{{ $t("nav.dms") }} - </router-link> - </li> - <li v-if="currentUser || !privateMode"> - <router-link :to="{ name: 'public-timeline' }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="users" - />{{ $t("nav.public_tl") }} - </router-link> - </li> - <li v-if="federating && (currentUser || !privateMode)"> - <router-link :to="{ name: 'public-external-timeline' }"> - <FAIcon - fixed-width - class="fa-scale-110 fa-old-padding " - icon="globe" - />{{ $t("nav.twkn") }} - </router-link> - </li> - </ul> - </div> - <div - slot="trigger" - class="title timeline-menu-title" - > - <span class="timeline-title">{{ timelineName() }}</span> - <span> - <FAIcon - size="sm" - icon="chevron-down" + <template v-slot:content> + <div class="timeline-menu-popover popover-default"> + <TimelineMenuContent /> + </div> + </template> + <template v-slot:trigger> + <button class="button-unstyled title timeline-menu-title"> + <span class="timeline-title">{{ timelineName() }}</span> + <span> + <FAIcon + size="sm" + icon="chevron-down" + /> + </span> + <span + class="click-blocker" + @click="blockOpen" /> - </span> - <span - class="click-blocker" - @click="blockOpen" - /> - </div> + </button> + </template> </Popover> </template> diff --git a/src/components/timeline_menu/timeline_menu_content.js b/src/components/timeline_menu/timeline_menu_content.js @@ -0,0 +1,29 @@ +import { mapState } from 'vuex' +import { library } from '@fortawesome/fontawesome-svg-core' +import { + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faHome +} from '@fortawesome/free-solid-svg-icons' + +library.add( + faUsers, + faGlobe, + faBookmark, + faEnvelope, + faHome +) + +const TimelineMenuContent = { + computed: { + ...mapState({ + currentUser: state => state.users.currentUser, + privateMode: state => state.instance.private, + federating: state => state.instance.federating + }) + } +} + +export default TimelineMenuContent diff --git a/src/components/timeline_menu/timeline_menu_content.vue b/src/components/timeline_menu/timeline_menu_content.vue @@ -0,0 +1,66 @@ +<template> + <ul> + <li v-if="currentUser"> + <router-link + class="menu-item" + :to="{ name: 'friends' }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="home" + />{{ $t("nav.home_timeline") }} + </router-link> + </li> + <li v-if="currentUser || !privateMode"> + <router-link + class="menu-item" + :to="{ name: 'public-timeline' }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="users" + />{{ $t("nav.public_tl") }} + </router-link> + </li> + <li v-if="federating && (currentUser || !privateMode)"> + <router-link + class="menu-item" + :to="{ name: 'public-external-timeline' }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="globe" + />{{ $t("nav.twkn") }} + </router-link> + </li> + <li v-if="currentUser"> + <router-link + class="menu-item" + :to="{ name: 'bookmarks'}" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="bookmark" + />{{ $t("nav.bookmarks") }} + </router-link> + </li> + <li v-if="currentUser"> + <router-link + class="menu-item" + :to="{ name: 'dms', params: { username: currentUser.screen_name } }" + > + <FAIcon + fixed-width + class="fa-scale-110 fa-old-padding " + icon="envelope" + />{{ $t("nav.dms") }} + </router-link> + </li> + </ul> +</template> + +<script src="./timeline_menu_content.js" ></script> diff --git a/src/components/user_card/user_card.js b/src/components/user_card/user_card.js @@ -4,23 +4,24 @@ import ProgressButton from '../progress_button/progress_button.vue' import FollowButton from '../follow_button/follow_button.vue' import ModerationTools from '../moderation_tools/moderation_tools.vue' import AccountActions from '../account_actions/account_actions.vue' +import Select from '../select/select.vue' import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator' import { mapGetters } from 'vuex' import { library } from '@fortawesome/fontawesome-svg-core' import { faBell, faRss, - faChevronDown, faSearchPlus, - faExternalLinkAlt + faExternalLinkAlt, + faEdit } from '@fortawesome/free-solid-svg-icons' library.add( faRss, faBell, - faChevronDown, faSearchPlus, - faExternalLinkAlt + faExternalLinkAlt, + faEdit ) export default { @@ -118,7 +119,8 @@ export default { ModerationTools, AccountActions, ProgressButton, - FollowButton + FollowButton, + Select }, methods: { muteUser () { @@ -153,6 +155,9 @@ export default { this.$store.state.instance.restrictedNicknames ) }, + openProfileTab () { + this.$store.dispatch('openSettingsModalTab', 'profile') + }, zoomAvatar () { const attachment = { url: this.user.profile_image_url_original, diff --git a/src/components/user_card/user_card.vue b/src/components/user_card/user_card.vue @@ -53,17 +53,29 @@ > {{ user.name }} </div> - <a + <button + v-if="!isOtherUser && user.is_local" + class="button-unstyled edit-profile-button" + @click.stop="openProfileTab" + > + <FAIcon + fixed-width + class="icon" + icon="edit" + :title="$t('user_card.edit_profile')" + /> + </button> + <button v-if="isOtherUser && !user.is_local" :href="user.statusnet_profile_url" target="_blank" - class="external-link-button" + class="button-unstyled external-link-button" > <FAIcon class="icon" icon="external-link-alt" /> - </a> + </button> <AccountActions v-if="isOtherUser && loggedIn" :user="user" @@ -132,25 +144,24 @@ class="userHighlightCl" type="color" > - <label - for="theme_tab" - class="userHighlightSel select" + <Select + :id="'userHighlightSel'+user.id" + v-model="userHighlightType" + class="userHighlightSel" > - <select - :id="'userHighlightSel'+user.id" - v-model="userHighlightType" - class="userHighlightSel" - > - <option value="disabled">No highlight</option> - <option value="solid">Solid bg</option> - <option value="striped">Striped bg</option> - <option value="side">Side stripe</option> - </select> - <FAIcon - class="select-down-icon" - icon="chevron-down" - /> - </label> + <option value="disabled"> + {{ $t('user_card.highlight.disabled') }} + </option> + <option value="solid"> + {{ $t('user_card.highlight.solid') }} + </option> + <option value="striped"> + {{ $t('user_card.highlight.striped') }} + </option> + <option value="side"> + {{ $t('user_card.highlight.side') }} + </option> + </Select> </div> </div> <div @@ -427,7 +438,7 @@ } } - .external-link-button { + .external-link-button, .edit-profile-button { cursor: pointer; width: 2.5em; text-align: center; @@ -541,15 +552,11 @@ flex: 1 0 auto; } - .userHighlightSel, - .userHighlightSel.select { + .userHighlightSel { padding-top: 0; padding-bottom: 0; flex: 1 0 auto; } - .userHighlightSel.select svg { - line-height: 22px; - } .userHighlightText { width: 70px; @@ -558,9 +565,7 @@ .userHighlightCl, .userHighlightText, - .userHighlightSel, - .userHighlightSel.select { - height: 22px; + .userHighlightSel { vertical-align: top; margin-right: .5em; margin-bottom: .25em; @@ -585,6 +590,10 @@ } } +.sidebar .edit-profile-button { + display: none; +} + .user-counts { display: flex; line-height:16px; diff --git a/src/components/user_list_popover/user_list_popover.vue b/src/components/user_list_popover/user_list_popover.vue @@ -4,40 +4,39 @@ placement="top" :offset="{ y: 5 }" > - <template slot="trigger"> + <template v-slot:trigger> <slot /> </template> - <div - slot="content" - class="user-list-popover" - > - <div v-if="users.length"> - <div - v-for="(user) in usersCapped" - :key="user.id" - class="user-list-row" - > - <UserAvatar - :user="user" - class="avatar-small" - :compact="true" - /> - <div class="user-list-names"> - <!-- eslint-disable vue/no-v-html --> - <span v-html="user.name_html" /> - <!-- eslint-enable vue/no-v-html --> - <span class="user-list-screen-name">{{ user.screen_name_ui }}</span> + <template v-slot:content> + <div class="user-list-popover"> + <template v-if="users.length"> + <div + v-for="(user) in usersCapped" + :key="user.id" + class="user-list-row" + > + <UserAvatar + :user="user" + class="avatar-small" + :compact="true" + /> + <div class="user-list-names"> + <!-- eslint-disable vue/no-v-html --> + <span v-html="user.name_html" /> + <!-- eslint-enable vue/no-v-html --> + <span class="user-list-screen-name">{{ user.screen_name_ui }}</span> + </div> </div> - </div> - </div> - <div v-else> - <FAIcon - icon="circle-notch" - spin - size="3x" - /> + </template> + <template v-else> + <FAIcon + icon="circle-notch" + spin + size="3x" + /> + </template> </div> - </div> + </template> </Popover> </template> diff --git a/src/components/user_profile/user_profile.vue b/src/components/user_profile/user_profile.vue @@ -60,10 +60,7 @@ :disabled="!user.friends_count" > <FriendList :user-id="userId"> - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <FollowCard :user="item" /> </template> </FriendList> @@ -75,10 +72,7 @@ :disabled="!user.followers_count" > <FollowerList :user-id="userId"> - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <FollowCard :user="item" :no-follows-you="isUs" diff --git a/src/components/user_reporting_modal/user_reporting_modal.vue b/src/components/user_reporting_modal/user_reporting_modal.vue @@ -45,10 +45,7 @@ </div> <div class="user-reporting-panel-right"> <List :items="statuses"> - <template - slot="item" - slot-scope="{item}" - > + <template v-slot:item="{item}"> <div class="status-fadein user-reporting-panel-sitem"> <Status :in-conversation="false" diff --git a/src/i18n/de.json b/src/i18n/de.json @@ -9,7 +9,9 @@ "scope_options": "Reichweitenoptionen", "text_limit": "Zeichenlimit", "title": "Funktionen", - "who_to_follow": "Wem folgen?" + "who_to_follow": "Wem folgen?", + "upload_limit": "Maximale Upload Größe", + "pleroma_chat_messages": "Pleroma Chat" }, "finder": { "error_fetching_user": "Fehler beim Suchen des Benutzers", @@ -28,7 +30,16 @@ "disable": "Deaktivieren", "enable": "Aktivieren", "confirm": "Bestätigen", - "verify": "Verifizieren" + "verify": "Verifizieren", + "role": { + "moderator": "Moderator", + "admin": "Admin" + }, + "peek": "Schau rein", + "close": "Schliessen", + "retry": "Versuche es erneut", + "error_retry": "Bitte versuche es erneut", + "loading": "Lade…" }, "login": { "login": "Anmelden", @@ -63,7 +74,11 @@ "search": "Suche", "preferences": "Voreinstellungen", "administration": "Administration", - "who_to_follow": "Wem folgen" + "who_to_follow": "Wem folgen", + "chats": "Chats", + "timelines": "Zeitlinie", + "bookmarks": "Lesezeichen", + "home_timeline": "Heim Zeitlinie" }, "notifications": { "broken_favorite": "Unbekannte Nachricht, suche danach…", @@ -76,7 +91,8 @@ "follow_request": "möchte dir folgen", "migrated_to": "migrierte zu", "reacted_with": "reagierte mit {0}", - "no_more_notifications": "Keine Benachrichtigungen mehr" + "no_more_notifications": "Keine Benachrichtigungen mehr", + "error": "Error beim laden von Neuigkeiten" }, "post_status": { "new_status": "Neuen Status veröffentlichen", @@ -105,7 +121,13 @@ "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" - } + }, + "media_description_error": "Medien konnten nicht neu geladen werden, versuche es erneut", + "empty_status_error": "Eine leere Nachricht ohne Anhänge kann nicht gesendet werden", + "preview_empty": "Leer", + "preview": "Vorschau", + "post": "Post", + "media_description": "Medienbeschreibung" }, "registration": { "bio": "Bio", @@ -124,9 +146,12 @@ "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.", + "bio_placeholder": "z.B.\nHallo, ich bin Lain.\nIch bin ein super süßes blushy-crushy Anime Girl aus dem vorstädtischen Japan. Du kennst mich vielleicht von Wired.", "fullname_placeholder": "z.B. Lain Iwakura", - "username_placeholder": "z.B. lain" + "username_placeholder": "z.B. lain", + "register": "Registrierung", + "reason_placeholder": "Diese Instanz bestätigt Registrierungen manuell. \nLass die Admins wissen warum du dich registrieren willst.", + "reason": "Grund zur Anmeldung" }, "settings": { "attachmentRadius": "Anhänge", @@ -136,7 +161,7 @@ "avatarRadius": "Avatare", "background": "Hintergrund", "bio": "Bio", - "btnRadius": "Buttons", + "btnRadius": "Knöpfe", "cBlue": "Blau (Antworten, folgt dir)", "cGreen": "Grün (Retweet)", "cOrange": "Orange (Favorisieren)", @@ -201,7 +226,7 @@ "name_bio": "Name & Bio", "new_password": "Neues Passwort", "notification_visibility": "Benachrichtigungstypen, die angezeigt werden sollen", - "notification_visibility_follows": "Follows", + "notification_visibility_follows": "Folgt", "notification_visibility_likes": "Favoriten", "notification_visibility_mentions": "Erwähnungen", "notification_visibility_repeats": "Wiederholungen", @@ -268,7 +293,24 @@ "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", "clear_all": "Alles leeren", - "clear_opacity": "Deckkraft leeren" + "clear_opacity": "Deckkraft leeren", + "help": { + "fe_downgraded": "PleromaFE Version wurde zurückgerollt.", + "older_version_imported": "Die Datei, die du importiert hast, wurde für eine ältere Version vom FE gemacht.", + "future_version_imported": "Die Datei, die du importiert hast, wurde für eine neuere Version vom FE gemacht.", + "v2_imported": "Die Datei, die du importiert hast, war für eine ältere Version des FEs. Wir versuchen, die Kompatibilität zu maximieren, aber es könnte trotzdem Inkonsistenz auftreten.", + "upgraded_from_v2": "PleromaFE wurde modernisiert, dein Theme könnte etwas anders aussehen als vorher.", + "snapshot_source_mismatch": "Versionskonflikt: vermutlich wurde das FE zurückgesetzt und dann ein Update durchgeführt. Falls das Theme mit einer alten FE-Version erstellt wurde, sollte vermutlich die alte Version verwendet werden, andernfalls die neue.", + "migration_napshot_gone": "Snapshot konnte nicht gefunden werden, die Anzeige könnte daher teilweise möglicherweise nicht den Erwartungen entsprechen.", + "migration_snapshot_ok": "Vorsichtshalber wurde ein Snapshot des Themes geladen. Alternativ kann versucht werden, die Daten des Themes selbst zu laden.", + "snapshot_present": "Snapshot des Themes wurde geladen, alle entsprechenden Einstellungen wurden überschrieben. Alternativ können die tatsächlichen Daten des Themes geladen werden.", + "fe_upgraded": "Mit dem Upgrade wurde auch eine neue Version von Pleromas Theme Engine installiert.", + "snapshot_missing": "Die Datei enthält keinen Theme-Snapshot, die Darstellung kann daher möglicherweise abweichend sein." + }, + "use_source": "Neue Version", + "use_snapshot": "Alte Version", + "keep_as_is": "Lass es so, wie es ist", + "load_theme": "Lade Theme" }, "common": { "color": "Farbe", @@ -303,7 +345,27 @@ "borders": "Rahmen", "buttons": "Schaltflächen", "inputs": "Eingabefelder", - "faint_text": "Verblasster Text" + "faint_text": "Verblasster Text", + "disabled": "aus", + "selectedMenu": "Ausgewähltes Menüelement", + "selectedPost": "Ausgewählter Post", + "pressed": "Gedrückt", + "highlight": "Hervorgehobene Elemente", + "icons": "Icons", + "poll": "Umfragegraph", + "post": "Posts/Benutzerinfo", + "alert_neutral": "Neutral", + "alert_warning": "Warnung", + "wallpaper": "Hintergrund", + "popover": "Kurzinfo, Menüs, Popover-Fenster", + "chat": { + "border": "Ränder", + "outgoing": "Ausgehend", + "incoming": "Eingehend" + }, + "toggled": "Umgeschaltet", + "underlay": "Halbtransparenter Hintergrund", + "tabs": "Reiter" }, "radii": { "_tab_label": "Abrundungen" @@ -325,7 +387,7 @@ "inset_classic": "Eingesetzte Schatten werden mit {0} verwendet" }, "components": { - "panel": "Panel", + "panel": "Bedienfeld", "panelHeader": "Panel-Kopf", "topBar": "Obere Leiste", "avatar": "Benutzer-Avatar (in der Profilansicht)", @@ -335,8 +397,9 @@ "buttonHover": "Schaltfläche (hover)", "buttonPressed": "Schaltfläche (gedrückt)", "buttonPressedHover": "Schaltfläche (gedrückt+hover)", - "input": "Input field" - } + "input": "Eingabefeld" + }, + "hintV3": "Um die Farbe der Schatten zu bestimmen, kann auch die Auszeichnung {0} verwendet werden, um einen anderen Fabbereich zu nutzen." }, "fonts": { "_tab_label": "Schriften", @@ -384,11 +447,14 @@ }, "verify": { "desc": "Um 2FA zu aktivieren, gib den Code von deiner 2FA-App ein:" - } + }, + "confirm_and_enable": "Bestätige und aktiviere OTP", + "setup_otp": "Richte OTP ein", + "wait_pre_setup_otp": "OTP voreinstellen" }, "enter_current_password_to_confirm": "Gib dein aktuelles Passwort ein, um deine Identität zu bestätigen", "security": "Sicherheit", - "allow_following_move": "Erlaube automatisches Folgen, sobald ein gefolgter Nutzer umzieht", + "allow_following_move": "Erlaube auto-follow, wenn von dir verfolgte Accounts umziehen", "blocks_imported": "Blocks importiert! Die Verarbeitung wird einen Moment brauchen.", "block_import_error": "Fehler beim Importieren der Blocks", "block_import": "Block Import", @@ -400,7 +466,79 @@ "change_email_error": "Es trat ein Problem auf beim Versuch, deine Email Adresse zu ändern.", "change_email": "Ändere Email", "import_blocks_from_a_csv_file": "Importiere Blocks von einer CSV Datei", - "accent": "Akzent" + "accent": "Akzent", + "no_blocks": "Keine Blocks", + "notification_visibility_emoji_reactions": "Reaktionen", + "new_email": "Neue Email", + "profile_fields": { + "value": "Inhalt", + "name": "Label", + "add_field": "Feld hinzufügen", + "label": "Profil Metadaten" + }, + "bot": "Dies ist ein Bot Account", + "blocks_tab": "Blocks", + "save": "Änderungen speichern", + "show_moderator_badge": "Zeige Moderator-Abzeichen auf meinem Profil", + "show_admin_badge": "Zeige Admin-Abzeichen auf meinem Profil", + "no_mutes": "Keine Stummschaltungen", + "reset_profile_background": "Profilhintergrund zurücksetzen", + "reset_avatar": "Avatar zurücksetzten", + "search_user_to_mute": "Suche, wen du stummschalten willst", + "search_user_to_block": "Suche, wen du blocken willst", + "reply_visibility_self_short": "Zeige antworten nur einem selbst", + "reply_visibility_following_short": "Zeige Antworten an meine Follower", + "notification_visibility_moves": "Nutzer zieht um", + "file_export_import": { + "errors": { + "file_too_new": "Inkompatible Major Version: {fileMajor}, dieses PleromaFE Version (settings ver {feMajor}) ist zu alt", + "invalid_file": "Die ausgewählte Datei kann nicht zur Wiederherstellung verwendet werden. Keine Änderungen wurden umgesetzt.", + "file_too_old": "Inkompatible Major Version: {fileMajor}, die Dateiversion ist zu alt und wird nicht mehr unterstützt (min. set. ver. {feMajor})", + "file_slightly_new": "Geringfügige Abweichung in der Dateiversion, einige Einstellungen konnten möglicherweise nicht geladen werden" + }, + "restore_settings": "Einstellungen von einer Datei wiederherstellen", + "backup_settings_theme": "Einstellungen und Theme in eine Datei speichern", + "backup_settings": "Einstellungen in Datei speichern", + "backup_restore": "Einstellungen backuppen" + }, + "hide_wallpaper": "Verstecke Instanzhintergrundbild", + "hide_all_muted_posts": "Verstecke stummgeschaltete Posts", + "hide_media_previews": "Verstecke Vorschau von Medien", + "word_filter": "Wort Filter", + "mutes_and_blocks": "Stummgeschaltete und Geblockte", + "chatMessageRadius": "Chat Nachricht", + "import_mutes_from_a_csv_file": "Importiere stummgeschaltete User von einer cvs Datei", + "mutes_imported": "Stummgeschaltete User wurden importiert! Verarbeitung dauert eine Weile.", + "mute_import_error": "Fehler beim Importieren von stummgeschalteten Usern", + "mute_import": "Stumm geschaltete User importieren", + "mute_export_button": "Stumm geschaltete User in eine cvs Datei exportieren", + "mute_export": "Stumm geschaltete User exportieren", + "setting_changed": "Einstellungen weichen von den Standardeinstellungen ab", + "notification_blocks": "Einen User zu blocken stoppt alle Benachrichtigungen von ihm und deabonniert ihn.", + "version": { + "frontend_version": "Frontend Version", + "backend_version": "Backend Version", + "title": "Version" + }, + "notification_mutes": "Um nicht mehr die Benachrichtigungen von einem bestimmten User zu bekommen, verwende eine Stummschaltung.", + "user_mutes": "User", + "notification_setting_privacy": "Privatsphäre", + "notification_setting_filters": "Filter", + "greentext": "Meme Pfeile", + "fun": "Spaß", + "upload_a_photo": "Lade ein Foto hoch", + "type_domains_to_mute": "Tippe die Domains ein, die du stummschalten willst", + "useStreamingApiWarning": "(Nicht empfohlen, experimentell, bekannt dafür, Posts zu überspringen)", + "useStreamingApi": "Empfange Posts und Benachrichtigungen in Echtzeit", + "more_settings": "Weitere Einstellungen", + "notification_setting_hide_notification_contents": "Absender und Inhalte von Push-Nachrichten verbergen", + "notification_setting_block_from_strangers": "Benachrichtigungen von Nutzern blockieren, denen Du nicht folgst", + "virtual_scrolling": "Rendering der Timeline optimieren", + "sensitive_by_default": "Alle Beiträge standardmäßig als heikel markieren", + "reset_background_confirm": "Hintergrund wirklich zurücksetzen?", + "reset_banner_confirm": "Banner wirklich zurücksetzen?", + "reset_avatar_confirm": "Avatar wirklich zurücksetzen?", + "reset_profile_banner": "Profilbanner zurücksetzen" }, "timeline": { "collapse": "Einklappen", @@ -410,7 +548,13 @@ "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" + "up_to_date": "Aktuell", + "no_statuses": "Keine Beiträge", + "no_more_statuses": "Keine weiteren Beiträge", + "reload": "Neu laden", + "error": "Fehler beim Lesen der Timeline: {0}", + "socket_broke": "Netzverbindung verloren: CloseEvent code {0}", + "socket_reconnected": "Netzverbindung hergestellt" }, "user_card": { "approve": "Genehmigen", @@ -433,11 +577,52 @@ "remote_follow": "Folgen", "statuses": "Beiträge", "admin_menu": { - "sandbox": "Erzwinge Beiträge nur für Follower sichtbar zu sein" + "sandbox": "Erzwinge Beiträge nur für Follower sichtbar zu sein", + "delete_user_confirmation": "Achtung! Diese Entscheidung kann nicht rückgängig gemacht werden! Trotzdem durchführen?", + "grant_admin": "Administratorprivilegien gewähren", + "delete_user": "Nutzer löschen", + "strip_media": "Medien von Beiträgen entfernen", + "force_nsfw": "Alle Beiträge als pervers markieren", + "activate_account": "Aktiviere Account", + "revoke_moderator": "Administratorstatuß wiederrufen", + "grant_moderator": "Moderatorstatuß gewähren", + "revoke_admin": "Administratorstatuß wiederrufen", + "moderation": "Moderation", + "delete_account": "Konto löschen", + "deactivate_account": "Konto deaktivieren", + "quarantine": "Beiträge des Nutzers können nur auf der eigenen Instanz gesehen werden", + "disable_any_subscription": "Alle Folgeanfragen für diesen Nutzer grundsätzlich ablehnen", + "disable_remote_subscription": "Nutzer anderer Instanzen vom Folgen dieses Nutzers ausschließen", + "force_unlisted": "Beiträge von der öffentlichen Zeitleiste ausschliessen" + }, + "block_progress": "Blocken…", + "unblock_progress": "Entblocken…", + "unblock": "Entblocken", + "report": "Melden", + "mention": "Erwähnungen", + "media": "Medien", + "hidden": "Versteckt", + "favorites": "Favoriten", + "bot": "Bot", + "show_repeats": "Geteilte Beiträge anzeigen", + "hide_repeats": "Geteilte Beiträge nicht anzeigen", + "mute_progress": "Stummschalten erfolgt…", + "unmute_progress": "Aufhebung erfolgt…", + "unmute": "Stummschalten aufheben", + "unsubscribe": "Entfolgen", + "subscribe": "Folgen", + "message": "Nachricht", + "highlight": { + "side": "Randmarkierung", + "striped": "gestreifter Hintergrund", + "solid": "kein Muster verwenden", + "disabled": "Nicht hervorheben" } }, "user_profile": { - "timeline_title": "Beiträge" + "timeline_title": "Beiträge", + "profile_loading_error": "Beim Laden dieses Profils ist ein Fehler aufgetreten.", + "profile_does_not_exist": "Profil nicht vorhanden." }, "who_to_follow": { "more": "Mehr", @@ -448,13 +633,18 @@ "repeat": "Wiederholen", "reply": "Antworten", "favorite": "Favorisieren", - "user_settings": "Benutzereinstellungen" + "user_settings": "Benutzereinstellungen", + "bookmark": "Lesezeichen", + "reject_follow_request": "Folgeanfrage ablehnen", + "accept_follow_request": "Folgeanfrage annehmen", + "add_reaction": "Emoji-Reaktion hinzufügen" }, "upload": { "error": { "base": "Hochladen fehlgeschlagen.", "file_too_big": "Datei ist zu groß [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", - "default": "Bitte versuche es später erneut" + "default": "Bitte versuche es später erneut", + "message": "Hochladen fehlgeschlagen" }, "file_size_units": { "B": "B", @@ -478,7 +668,7 @@ "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", - "too_many_requests": "Kurze Pause. Zu viele Versuche. Bitte, später nochmal probieren.", + "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." @@ -486,21 +676,21 @@ "about": { "mrf": { "federation": "Föderation", - "mrf_policies": "Aktivierte MRF Richtlinien", + "mrf_policies": "Aktive 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", + "ftl_removal": "Von der Zeitleiste \"Das 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:" + "ftl_removal_desc": "Dieser Instanz entfernt folgende Instanzen von der \"Das bekannte Netzwerk\" Zeitleiste:" }, "keyword": { "keyword_policies": "Keyword Richtlinien", @@ -509,7 +699,7 @@ "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:" + "mrf_policies_desc": "MRF Richtlinien beeinflussen das Föderationsverhalten dieser Instanz. Die folgenden Richtlinien sind aktiv:" }, "staff": "Mitarbeiter" }, @@ -550,7 +740,9 @@ "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}" + "expires_in": "Die Umfrage endet in {0}", + "votes_count": "{count} Stimme | {count} Stimmen", + "people_voted_count": "{count} Person hat gewählt | {count} Personen haben gewählt" }, "emoji": { "stickers": "Sticker", @@ -560,12 +752,12 @@ "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.", + "load_all_hint": "Erste {saneAmount} Emoji geladen, alle Emoji zu laden könnte Leistungsprobleme verursachen.", "unicode": "Unicode Emoji" }, "interactions": { "load_older": "Lade ältere Interaktionen", - "follows": "Neue Follows", + "follows": "Neue Follower", "favs_repeats": "Wiederholungen und Favoriten", "moves": "Benutzer migriert zu" }, @@ -573,7 +765,106 @@ "select_all": "Wähle alle" }, "remote_user_resolver": { - "searching_for": "Suche nach", - "error": "Nicht gefunden." + "searching_for": "Suche für", + "error": "Nicht gefunden.", + "remote_user_resolver": "Resolver für Nutzer auf anderen Instanzen" + }, + "errors": { + "storage_unavailable": "Pleroma konnte nicht auf den Browser Speicher zugreifen. Deine Anmeldung und deine Einstellungen werden nicht gespeichert. Es kann unvorhersehbare Probleme geben. Versuche ansonsten Cookies zu erlauben." + }, + "shoutbox": { + "title": "Shoutbox" + }, + "chats": { + "error_sending_message": "Beim Senden der Nachricht ist ein Fehler aufgetreten.", + "error_loading_chat": "Beim Laden des Chats ist ein Fehler aufgetreten.", + "delete_confirm": "Soll diese Nachricht wirklich gelöscht werden?", + "empty_message_error": "Die Nachricht darf nicht leer sein.", + "delete": "Löschen", + "message_user": "Nachricht an {nickname} senden", + "empty_chat_list_placeholder": "Es sind noch keine Chats vorhanden. Jetzt einen Chat starten!", + "more": "Mehr", + "you": "Du:", + "new": "Neuer Chat", + "chats": "Chats" + }, + "user_reporting": { + "generic_error": "Beim Verarbeiten der Anfrage ist ein Fehler aufgetreten.", + "submit": "Senden", + "forward_to": "Weiterleiten an {0}", + "forward_description": "Das fragliche Konto befindet sich auf einem anderen Server. Soll eine Kopie der Beschwerde an den dortigen Verantwortlichen gesendet werden?", + "additional_comments": "Weitere Anmerkungen", + "add_comment_description": "Die Beschwerde wird an die Moderatoren dieser Instanz gesendet. Die Gründe für die Beschwerde können hier angegeben werden:", + "title": "{0} melddn" + }, + "status": { + "copy_link": "Beitragslink kopieren", + "status_unavailable": "Beitrag nicht verfügbar", + "unmute_conversation": "Konversation nicht mehr stummstellen", + "mute_conversation": "Konversation stummstellen", + "replies_list": "Antworten:", + "reply_to": "Antworten auf", + "delete_confirm": "Möchtest du diese Beitrag wirklich löschen?", + "pinned": "Angeheftet", + "unpin": "Nicht mehr an Profil anheften", + "pin": "An Profil anheften", + "delete": "Lösche Beitrag", + "favorites": "Favoriten", + "expand": "Ausklappen", + "nsfw": "NSFW", + "status_deleted": "Dieser Beitrag wurde gelöscht", + "hide_content": "Inhalt verbergen", + "show_content": "Inhalt anzeigen", + "hide_full_subject": "Vollständiges Thema verbergen", + "show_full_subject": "Vollständiges Thema anzeigen", + "thread_muted": "Thread stummgeschaltet", + "external_source": "Externe Quelle", + "unbookmark": "Lesezeichen entfernen", + "bookmark": "Lesezeichen setzen", + "repeats": "Geteilte Beiträge", + "thread_muted_and_words": ", enthält folgende Wörter:" + }, + "time": { + "seconds_short": "{0}s", + "second_short": "{0}s", + "seconds": "{0} Sekunden", + "second": "{0} Sekunde", + "now_short": "jetzt", + "years_short": "{0}Jhr", + "year_short": "{0}Jhr", + "years": "{0} Jahren", + "year": "{0} Jahr", + "weeks_short": "{0}W", + "week_short": "{0}W", + "weeks": "{0} Wochen", + "week": "{0} Woche", + "now": "gerade eben", + "months_short": "{0}Mo", + "month_short": "{0}Mo", + "months": "{0} Monaten", + "month": "{0} Monat", + "minutes_short": "{0}Min", + "minute_short": "{0}Min", + "minutes": "{0} Minuten", + "minute": "{0} Minute", + "in_past": "vor {0}", + "in_future": "in {0}", + "hours_short": "{0}Std", + "hour_short": "{0}Std", + "hours": "{0} Stunden", + "hour": "{0} Stunde", + "days_short": "{0}T", + "day_short": "{0}T", + "days": "{0} Tage", + "day": "{0} Tag" + }, + "display_date": { + "today": "Heute" + }, + "file_type": { + "file": "Datei", + "image": "Bild", + "video": "Video", + "audio": "Audio" } } diff --git a/src/i18n/en.json b/src/i18n/en.json @@ -3,27 +3,27 @@ "mrf": { "federation": "Federation", "keyword": { - "keyword_policies": "Keyword Policies", + "keyword_policies": "Keyword policies", "ftl_removal": "Removal from \"The Whole Known Network\" Timeline", "reject": "Reject", "replace": "Replace", "is_replaced_by": "→" }, - "mrf_policies": "Enabled MRF Policies", + "mrf_policies": "Enabled MRF policies", "mrf_policies_desc": "MRF policies manipulate the federation behaviour of the instance. The following policies are enabled:", "simple": { - "simple_policies": "Instance-specific Policies", + "simple_policies": "Instance-specific policies", "accept": "Accept", "accept_desc": "This instance only accepts messages from the following instances:", "reject": "Reject", "reject_desc": "This instance will not accept messages from the following instances:", "quarantine": "Quarantine", "quarantine_desc": "This instance will send only public posts to the following instances:", - "ftl_removal": "Removal from \"The Whole Known Network\" Timeline", - "ftl_removal_desc": "This instance removes these instances from \"The Whole Known Network\" timeline:", + "ftl_removal": "Removal from \"Known Network\" Timeline", + "ftl_removal_desc": "This instance removes these instances from \"Known Network\" timeline:", "media_removal": "Media Removal", "media_removal_desc": "This instance removes media from posts on the following instances:", - "media_nsfw": "Media Force-set As Sensitive", + "media_nsfw": "Media force-set as sensitive", "media_nsfw_desc": "This instance forces media to be set sensitive in posts on the following instances:" } }, @@ -79,7 +79,10 @@ "role": { "admin": "Admin", "moderator": "Moderator" - } + }, + "flash_content": "Click to show Flash content using Ruffle (Experimental, may not work).", + "flash_security": "Note that this can be potentially dangerous since Flash content is still arbitrary code.", + "flash_fail": "Failed to load flash content, see console for details." }, "image_cropper": { "crop_picture": "Crop picture", @@ -118,12 +121,13 @@ "about": "About", "administration": "Administration", "back": "Back", - "friend_requests": "Follow Requests", + "friend_requests": "Follow requests", "mentions": "Mentions", "interactions": "Interactions", - "dms": "Direct Messages", - "public_tl": "Public Timeline", + "dms": "Direct messages", + "public_tl": "Public timeline", "timeline": "Timeline", + "home_timeline": "Home timeline", "twkn": "Known Network", "bookmarks": "Bookmarks", "user_search": "User Search", @@ -148,8 +152,8 @@ "reacted_with": "reacted with {0}" }, "polls": { - "add_poll": "Add Poll", - "add_option": "Add Option", + "add_poll": "Add poll", + "add_option": "Add option", "option": "Option", "votes": "votes", "people_voted_count": "{count} person voted | {count} people voted", @@ -178,7 +182,7 @@ "storage_unavailable": "Pleroma could not access browser storage. Your login or your local settings won't be saved and you might encounter unexpected issues. Try enabling cookies." }, "interactions": { - "favs_repeats": "Repeats and Favorites", + "favs_repeats": "Repeats and favorites", "follows": "New follows", "moves": "User migrates", "load_older": "Load older interactions" @@ -200,6 +204,7 @@ "direct_warning_to_all": "This post will be visible to all the mentioned users.", "direct_warning_to_first_only": "This post will only be visible to the mentioned users at the beginning of the message.", "posting": "Posting", + "post": "Post", "preview": "Preview", "preview_empty": "Empty", "empty_status_error": "Can't post an empty status with no files", @@ -210,10 +215,10 @@ "unlisted": "This post will not be visible in Public Timeline and The Whole Known Network" }, "scope": { - "direct": "Direct - Post to mentioned users only", - "private": "Followers-only - Post to followers only", - "public": "Public - Post to public timelines", - "unlisted": "Unlisted - Do not post to public timelines" + "direct": "Direct - post to mentioned users only", + "private": "Followers-only - post to followers only", + "public": "Public - post to public timelines", + "unlisted": "Unlisted - do not post to public timelines" } }, "registration": { @@ -230,6 +235,7 @@ "bio_placeholder": "e.g.\nHi, I'm Lain.\nI’m an anime girl living in suburban Japan. You may know me from the Wired.", "reason": "Reason to register", "reason_placeholder": "This instance approves registrations manually.\nLet the administration know why you want to register.", + "register": "Register", "validations": { "username_required": "cannot be left blank", "fullname_required": "cannot be left blank", @@ -249,6 +255,7 @@ }, "settings": { "app_name": "App name", + "save": "Save changes", "security": "Security", "setting_changed": "Setting is different from default", "enter_current_password_to_confirm": "Enter your current password to confirm your identity", @@ -277,7 +284,7 @@ "attachmentRadius": "Attachments", "attachments": "Attachments", "avatar": "Avatar", - "avatarAltRadius": "Avatars (Notifications)", + "avatarAltRadius": "Avatars (notifications)", "avatarRadius": "Avatars", "background": "Background", "bio": "Bio", @@ -299,10 +306,10 @@ "cGreen": "Green (Retweet)", "cOrange": "Orange (Favorite)", "cRed": "Red (Cancel)", - "change_email": "Change Email", + "change_email": "Change email", "change_email_error": "There was an issue changing your email.", "changed_email": "Email changed successfully!", - "change_password": "Change Password", + "change_password": "Change password", "change_password_error": "There was an issue changing your password.", "changed_password": "Password changed successfully!", "chatMessageRadius": "Chat message", @@ -311,9 +318,9 @@ "confirm_new_password": "Confirm new password", "current_password": "Current password", "mutes_and_blocks": "Mutes and Blocks", - "data_import_export_tab": "Data Import / Export", + "data_import_export_tab": "Data import / export", "default_vis": "Default visibility scope", - "delete_account": "Delete Account", + "delete_account": "Delete account", "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_instructions": "Type your password in the input below to confirm account deletion.", @@ -325,6 +332,7 @@ "export_theme": "Save preset", "filtering": "Filtering", "filtering_explanation": "All statuses containing these words will be muted, one per line", + "word_filter": "Word filter", "follow_export": "Follow export", "follow_export_button": "Export your follows to a csv file", "follow_import": "Follow import", @@ -335,9 +343,13 @@ "general": "General", "hide_attachments_in_convo": "Hide attachments in conversations", "hide_attachments_in_tl": "Hide attachments in timeline", + "hide_media_previews": "Hide media previews", "hide_muted_posts": "Hide posts of muted users", + "hide_all_muted_posts": "Hide muted posts", "max_thumbnails": "Maximum amount of thumbnails per post", "hide_isp": "Hide instance-specific panel", + "hide_shoutbox": "Hide instance shoutbox", + "right_sidebar": "Show sidebar on the right side", "hide_wallpaper": "Hide instance wallpaper", "preload_images": "Preload images", "use_one_click_nsfw": "Open NSFW attachments with just one click", @@ -361,20 +373,32 @@ "loop_video_silent_only": "Loop only videos without sound (i.e. Mastodon's \"gifs\")", "mutes_tab": "Mutes", "play_videos_in_modal": "Play videos in a popup frame", + "file_export_import": { + "backup_restore": "Settings backup", + "backup_settings": "Backup settings to file", + "backup_settings_theme": "Backup settings and theme to file", + "restore_settings": "Restore settings from file", + "errors": { + "invalid_file": "The selected file is not a supported Pleroma settings backup. No changes were made.", + "file_too_new": "Incompatile major version: {fileMajor}, this PleromaFE (settings ver {feMajor}) is too old to handle it", + "file_too_old": "Incompatile major version: {fileMajor}, file version is too old and not supported (min. set. ver. {feMajor})", + "file_slightly_new": "File minor version is different, some settings might not load" + } + }, "profile_fields": { "label": "Profile metadata", - "add_field": "Add Field", + "add_field": "Add field", "name": "Label", "value": "Content" }, "use_contain_fit": "Don't crop the attachment in thumbnails", "name": "Name", - "name_bio": "Name & Bio", - "new_email": "New Email", + "name_bio": "Name & bio", + "new_email": "New email", "new_password": "New password", "notification_visibility": "Types of notifications to show", "notification_visibility_follows": "Follows", - "notification_visibility_likes": "Likes", + "notification_visibility_likes": "Favorites", "notification_visibility_mentions": "Mentions", "notification_visibility_repeats": "Repeats", "notification_visibility_moves": "User Migrates", @@ -386,25 +410,27 @@ "hide_followers_description": "Don't show who's following me", "hide_follows_count_description": "Don't show follow count", "hide_followers_count_description": "Don't show follower count", - "show_admin_badge": "Show Admin badge in my profile", - "show_moderator_badge": "Show Moderator badge in my profile", + "show_admin_badge": "Show \"Admin\" badge in my profile", + "show_moderator_badge": "Show \"Moderator\" badge in my profile", "nsfw_clickthrough": "Enable clickthrough attachment and link preview image hiding for NSFW statuses", "oauth_tokens": "OAuth tokens", "token": "Token", - "refresh_token": "Refresh Token", - "valid_until": "Valid Until", + "refresh_token": "Refresh token", + "valid_until": "Valid until", "revoke_token": "Revoke", "panelRadius": "Panels", "pause_on_unfocused": "Pause streaming when tab is not focused", "presets": "Presets", - "profile_background": "Profile Background", - "profile_banner": "Profile Banner", + "profile_background": "Profile background", + "profile_banner": "Profile banner", "profile_tab": "Profile", "radii_help": "Set up interface edge rounding (in pixels)", "replies_in_timeline": "Replies in timeline", "reply_visibility_all": "Show all replies", "reply_visibility_following": "Only show replies directed at me or users I'm following", "reply_visibility_self": "Only show replies directed at me", + "reply_visibility_following_short": "Show replies to my follows", + "reply_visibility_self_short": "Show replies to self only", "autohide_floating_post_button": "Automatically hide New Post button (mobile)", "saving_err": "Error saving settings", "saving_ok": "Settings saved", @@ -429,6 +455,7 @@ "subject_line_mastodon": "Like mastodon: copy as is", "subject_line_noop": "Do not copy", "post_status_content_type": "Post status content type", + "sensitive_by_default": "Mark posts as sensitive by default", "stop_gifs": "Play-on-hover GIFs", "streaming": "Enable automatic streaming of new posts when scrolled to the top", "user_mutes": "Users", @@ -458,6 +485,7 @@ "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.", "enable_web_push_notifications": "Enable web push notifications", + "more_settings": "More settings", "style": { "switcher": { "keep_color": "Keep colors", @@ -606,8 +634,8 @@ }, "version": { "title": "Version", - "backend_version": "Backend Version", - "frontend_version": "Frontend Version" + "backend_version": "Backend version", + "frontend_version": "Frontend version" } }, "time": { @@ -655,7 +683,9 @@ "reload": "Reload", "up_to_date": "Up-to-date", "no_more_statuses": "No more statuses", - "no_statuses": "No statuses" + "no_statuses": "No statuses", + "socket_reconnected": "Realtime connection established", + "socket_broke": "Realtime connection lost: CloseEvent code {0}" }, "status": { "favorites": "Favorites", @@ -689,6 +719,7 @@ "block": "Block", "blocked": "Blocked!", "deny": "Deny", + "edit_profile": "Edit profile", "favorites": "Favorites", "follow": "Follow", "follow_sent": "Request sent!", @@ -739,10 +770,16 @@ "quarantine": "Disallow user posts from federating", "delete_user": "Delete user", "delete_user_confirmation": "Are you absolutely sure? This action cannot be undone." + }, + "highlight": { + "disabled": "No highlight", + "solid": "Solid bg", + "striped": "Striped bg", + "side": "Side stripe" } }, "user_profile": { - "timeline_title": "User Timeline", + "timeline_title": "User timeline", "profile_does_not_exist": "Sorry, this profile does not exist.", "profile_loading_error": "Sorry, there was an error loading this profile." }, @@ -760,7 +797,7 @@ "who_to_follow": "Who to follow" }, "tool_tip": { - "media_upload": "Upload Media", + "media_upload": "Upload media", "repeat": "Repeat", "reply": "Reply", "favorite": "Favorite", diff --git a/src/i18n/eo.json b/src/i18n/eo.json @@ -156,7 +156,9 @@ "password_required": "ne povas resti malplena", "password_confirmation_required": "ne povas resti malplena", "password_confirmation_match": "samu la pasvorton" - } + }, + "reason_placeholder": "Ĉi-node oni aprobas registriĝojn permane.\nSciigu la administrantojn kial vi volas registriĝi.", + "reason": "Kialo registriĝi" }, "settings": { "app_name": "Nomo de aplikaĵo", @@ -523,7 +525,14 @@ "mute_export_button": "Elportu viajn silentigojn al CSV-dosiero", "mute_export": "Elporto de silentigoj", "hide_wallpaper": "Kaŝi fonbildon de nodo", - "setting_changed": "Agordo malsamas de la implicita" + "setting_changed": "Agordo malsamas de la implicita", + "more_settings": "Pliaj agordoj", + "sensitive_by_default": "Implicite marki afiŝojn konsternaj", + "reply_visibility_following_short": "Montri respondojn por miaj abonatoj", + "hide_all_muted_posts": "Kaŝi silentigitajn afiŝojn", + "hide_media_previews": "Kaŝi antaŭrigardojn al vidaŭdaĵoj", + "word_filter": "Vortofiltro", + "reply_visibility_self_short": "Montri nur respondojn por mi" }, "timeline": { "collapse": "Maletendi", @@ -594,7 +603,13 @@ "hide_repeats": "Kaŝi ripetojn", "unsubscribe": "Ne ricevi sciigojn", "subscribe": "Ricevi sciigojn", - "bot": "Roboto" + "bot": "Roboto", + "highlight": { + "side": "Flanka strio", + "striped": "Stria fono", + "solid": "Unueca fono", + "disabled": "Senemfaze" + } }, "user_profile": { "timeline_title": "Historio de uzanto", diff --git a/src/i18n/es.json b/src/i18n/es.json @@ -34,12 +34,16 @@ "enable": "Habilitar", "confirm": "Confirmar", "verify": "Verificar", - "peek": "Ojear", + "peek": "Previsualizar", "close": "Cerrar", "dismiss": "Descartar", "retry": "Inténtalo de nuevo", "error_retry": "Por favor, inténtalo de nuevo", - "loading": "Cargando…" + "loading": "Cargando…", + "role": { + "admin": "Administrador/a", + "moderator": "Moderador/a" + } }, "image_cropper": { "crop_picture": "Recortar la foto", @@ -82,8 +86,8 @@ "friend_requests": "Solicitudes de seguimiento", "mentions": "Menciones", "interactions": "Interacciones", - "dms": "Mensajes Directos", - "public_tl": "Línea Temporal Pública", + "dms": "Mensajes directos", + "public_tl": "Línea temporal pública", "timeline": "Línea Temporal", "twkn": "Red Conocida", "user_search": "Búsqueda de Usuarios", @@ -92,7 +96,8 @@ "preferences": "Preferencias", "chats": "Chats", "timelines": "Líneas de Tiempo", - "bookmarks": "Marcadores" + "bookmarks": "Marcadores", + "home_timeline": "Línea temporal personal" }, "notifications": { "broken_favorite": "Estado desconocido, buscándolo…", @@ -120,7 +125,9 @@ "expiry": "Tiempo de vida de la encuesta", "expires_in": "La encuesta termina en {0}", "expired": "La encuesta terminó hace {0}", - "not_enough_options": "Muy pocas opciones únicas en la encuesta" + "not_enough_options": "Muy pocas opciones únicas en la encuesta", + "people_voted_count": "{count} persona votó | {count} personas votaron", + "votes_count": "{count} voto | {count} votos" }, "emoji": { "stickers": "Pegatinas", @@ -137,14 +144,14 @@ "add_sticker": "Añadir Pegatina" }, "interactions": { - "favs_repeats": "Favoritos y Repetidos", + "favs_repeats": "Favoritos y repetidos", "follows": "Nuevos seguidores", "load_older": "Cargar interacciones más antiguas", "moves": "Usuario Migrado" }, "post_status": { "new_status": "Publicar un nuevo estado", - "account_not_locked_warning": "Tu cuenta no está {0}. Cualquiera puede seguirte y leer las entradas para Solo-Seguidores.", + "account_not_locked_warning": "Tu cuenta no está {0}. Cualquiera puede seguirte y leer las publicaciones para Solo-Seguidores.", "account_not_locked_warning_link": "bloqueada", "attachments_sensitive": "Contenido sensible", "content_type": { @@ -164,16 +171,17 @@ "unlisted": "Esta publicación no será visible en la Línea Temporal Pública ni en Toda La Red Conocida" }, "scope": { - "direct": "Directo - Solo para los usuarios mencionados", - "private": "Solo-seguidores - Solo tus seguidores leerán la publicación", - "public": "Público - Entradas visibles en las Líneas Temporales Públicas", - "unlisted": "Sin listar - Entradas no visibles en las Líneas Temporales Públicas" + "direct": "Directo - solo para los usuarios mencionados", + "private": "Solo-seguidores - solo tus seguidores leerán la publicación", + "public": "Público - publicaciones visibles en las líneas temporales públicas", + "unlisted": "Sin listar -publicaciones no visibles en las líneas temporales públicas" }, "media_description_error": "Error al actualizar el archivo, inténtalo de nuevo", "empty_status_error": "No se puede publicar un estado vacío y sin archivos adjuntos", "preview_empty": "Vacío", "preview": "Vista previa", - "media_description": "Descripción multimedia" + "media_description": "Descripción multimedia", + "post": "Publicación" }, "registration": { "bio": "Biografía", @@ -194,7 +202,10 @@ "password_required": "no puede estar vacío", "password_confirmation_required": "no puede estar vacío", "password_confirmation_match": "la contraseña no coincide" - } + }, + "reason_placeholder": "Los registros de esta instancia son aprobados manualmente.\nComéntanos por qué quieres registrarte aquí.", + "reason": "Razón para registrarse", + "register": "Registrarse" }, "selectable_list": { "select_all": "Seleccionar todo" @@ -227,7 +238,7 @@ "attachmentRadius": "Adjuntos", "attachments": "Adjuntos", "avatar": "Avatar", - "avatarAltRadius": "Avatares (Notificaciones)", + "avatarAltRadius": "Avatares (notificaciones)", "avatarRadius": "Avatares", "background": "Fondo", "bio": "Biografía", @@ -245,19 +256,19 @@ "change_password": "Cambiar contraseña", "change_password_error": "Hubo un problema cambiando la contraseña.", "changed_password": "¡Contraseña cambiada correctamente!", - "collapse_subject": "Colapsar entradas con tema", + "collapse_subject": "Colapsar publicaciones con tema", "composing": "Redactando", "confirm_new_password": "Confirmar la nueva contraseña", "current_avatar": "Tu avatar actual", "current_password": "Contraseña actual", "current_profile_banner": "Tu cabecera actual", - "data_import_export_tab": "Importar / Exportar Datos", + "data_import_export_tab": "Importar / Exportar datos", "default_vis": "Alcance de visibilidad por defecto", "delete_account": "Eliminar la cuenta", "discoverable": "Permitir la aparición de esta cuenta en los resultados de búsqueda y otros servicios", "delete_account_description": "Eliminar para siempre los datos y desactivar la cuenta.", "pad_emoji": "Rellenar con espacios al agregar emojis desde el selector", - "delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el administrador de tu instancia.", + "delete_account_error": "Hubo un error al eliminar tu cuenta. Si el fallo persiste, ponte en contacto con el/la administrador/a de tu instancia.", "delete_account_instructions": "Escribe tu contraseña para confirmar la eliminación de tu cuenta.", "avatar_size_instruction": "El tamaño mínimo recomendado para el avatar es de 150X150 píxeles.", "export_theme": "Exportar tema", @@ -277,7 +288,7 @@ "hide_isp": "Ocultar el panel específico de la instancia", "preload_images": "Precargar las imágenes", "use_one_click_nsfw": "Abrir los adjuntos NSFW con un solo click", - "hide_post_stats": "Ocultar las estadísticas de las entradas (p.ej. el número de favoritos)", + "hide_post_stats": "Ocultar las estadísticas de las publicaciones (p.ej. el número de favoritos)", "hide_user_stats": "Ocultar las estadísticas del usuario (p.ej. el número de seguidores)", "hide_filtered_statuses": "Ocultar estados filtrados", "import_blocks_from_a_csv_file": "Importar lista de usuarios bloqueados dese un archivo csv", @@ -299,22 +310,22 @@ "play_videos_in_modal": "Reproducir los vídeos en un marco emergente", "use_contain_fit": "No recortar los adjuntos en miniaturas", "name": "Nombre", - "name_bio": "Nombre y Biografía", + "name_bio": "Nombre y biografía", "new_password": "Nueva contraseña", "notification_visibility": "Tipos de notificaciones a mostrar", "notification_visibility_follows": "Nuevos seguidores", - "notification_visibility_likes": "Me gustan (Likes)", + "notification_visibility_likes": "Favoritos", "notification_visibility_mentions": "Menciones", "notification_visibility_repeats": "Repeticiones (Repeats)", - "no_rich_text_description": "Eliminar el formato de texto enriquecido de todas las entradas", + "no_rich_text_description": "Eliminar el formato de texto enriquecido de todas las publicaciones", "no_blocks": "No hay usuarios bloqueados", "no_mutes": "No hay usuarios silenciados", "hide_follows_description": "No mostrar a quién sigo", "hide_followers_description": "No mostrar quién me sigue", "hide_follows_count_description": "No mostrar el número de cuentas que sigo", "hide_followers_count_description": "No mostrar el número de cuentas que me siguen", - "show_admin_badge": "Mostrar la insignia de Administrador en mi perfil", - "show_moderator_badge": "Mostrar la insignia de Moderador en mi perfil", + "show_admin_badge": "Mostrar la insignia de \"Administrador/a\" en mi perfil", + "show_moderator_badge": "Mostrar la insignia de \"Moderador/a\" en mi perfil", "nsfw_clickthrough": "Habilitar la ocultación de la imagen de vista previa del enlace y el adjunto para los estados NSFW por defecto", "oauth_tokens": "Tokens de OAuth", "token": "Token", @@ -324,8 +335,8 @@ "panelRadius": "Paneles", "pause_on_unfocused": "Parar la transmisión cuando no estés en foco", "presets": "Por defecto", - "profile_background": "Fondo del Perfil", - "profile_banner": "Cabecera del Perfil", + "profile_background": "Imagen de fondo del perfil", + "profile_banner": "Imagen de cabecera del perfil", "profile_tab": "Perfil", "radii_help": "Establezca el redondeo de las esquinas de la interfaz (en píxeles)", "replies_in_timeline": "Réplicas en la línea temporal", @@ -356,7 +367,7 @@ "theme": "Tema", "theme_help": "Use códigos de color hexadecimales (#rrggbb) para personalizar su tema de colores.", "theme_help_v2_1": "También puede invalidar los colores y la opacidad de ciertos componentes si activa la casilla de verificación. Use el botón \"Borrar todo\" para deshacer los cambios.", - "theme_help_v2_2": "Los iconos debajo de algunas entradas son indicadores de contraste de fondo/texto, desplace el ratón por encima para obtener información más detallada. Tenga en cuenta que cuando se utilizan indicadores de contraste de transparencia se muestra el peor caso posible.", + "theme_help_v2_2": "Los iconos debajo de algunas publicaciones son indicadores de contraste de fondo/texto, desplace el ratón por encima para obtener información más detallada. Tenga en cuenta que cuando se utilizan indicadores de contraste de transparencia se muestra el peor caso posible.", "tooltipRadius": "Información/alertas", "upload_a_photo": "Subir una foto", "user_settings": "Ajustes del Usuario", @@ -476,7 +487,7 @@ "panelHeader": "Cabecera del panel", "topBar": "Barra superior", "avatar": "Avatar del usuario (en la vista del perfil)", - "avatarStatus": "Avatar del usuario (en la vista de la entrada)", + "avatarStatus": "Avatar del usuario (en la vista de la publicación)", "popup": "Ventanas y textos emergentes (popups & tooltips)", "button": "Botones", "buttonHover": "Botón (encima)", @@ -517,8 +528,8 @@ }, "version": { "title": "Versión", - "backend_version": "Versión del Backend", - "frontend_version": "Versión del Frontend" + "backend_version": "Versión del backend", + "frontend_version": "Versión del frontend" }, "notification_visibility_moves": "Usuario Migrado", "greentext": "Texto verde (meme arrows)", @@ -529,7 +540,7 @@ "fun": "Divertido", "type_domains_to_mute": "Buscar dominios para silenciar", "useStreamingApiWarning": "(no recomendado, experimental, puede omitir publicaciones)", - "useStreamingApi": "Recibir entradas y notificaciones en tiempo real", + "useStreamingApi": "Recibir publicaciones y notificaciones en tiempo real", "user_mutes": "Usuarios", "reset_profile_background": "Restablecer el fondo de pantalla", "reset_background_confirm": "¿Estás seguro de restablecer el fondo de pantalla?", @@ -563,7 +574,24 @@ "mute_export_button": "Exportar los silenciados a un archivo csv", "mute_export": "Exportar silenciados", "hide_wallpaper": "Ocultar el fondo de pantalla de la instancia", - "setting_changed": "La configuración es diferente a la predeterminada" + "setting_changed": "La configuración es diferente a la predeterminada", + "hide_all_muted_posts": "Ocultar las publicaciones silenciadas", + "more_settings": "Más opciones", + "sensitive_by_default": "Identificar las publicaciones como sensibles de forma predeterminada", + "reply_visibility_self_short": "Mostrar respuestas solo a uno mismo", + "reply_visibility_following_short": "Mostrar las réplicas a mis seguidores", + "hide_media_previews": "Ocultar la vista previa multimedia", + "word_filter": "Filtro de palabras", + "save": "Guardar los cambios", + "file_export_import": { + "errors": { + "invalid_file": "El archivo seleccionado no es válido como copia de seguridad de Pleroma. No se han realizado cambios." + }, + "restore_settings": "Restaurar ajustes desde archivo", + "backup_settings_theme": "Copia de seguridad de la configuración y tema a archivo", + "backup_settings": "Copia de seguridad de la configuración a archivo", + "backup_restore": "Copia de seguridad de la configuración" + } }, "time": { "day": "{0} día", @@ -611,7 +639,9 @@ "no_more_statuses": "No hay más estados", "no_statuses": "Sin estados", "reload": "Recargar", - "error": "Error obteniendo la linea de tiempo:{0}" + "error": "Error obteniendo la linea de tiempo:{0}", + "socket_broke": "Conexión en timpo real perdida: código del motivo {0}", + "socket_reconnected": "Establecida la conexión en tiempo real" }, "status": { "favorites": "Favoritos", @@ -635,7 +665,7 @@ "status_unavailable": "Estado no disponible", "bookmark": "Marcar", "unbookmark": "Desmarcar", - "status_deleted": "Esta entrada ha sido eliminada", + "status_deleted": "Esta publicación ha sido eliminada", "nsfw": "NSFW (No apropiado para el trabajo)", "expand": "Expandir", "external_source": "Fuente externa" @@ -674,10 +704,10 @@ "mute_progress": "Silenciando…", "admin_menu": { "moderation": "Moderación", - "grant_admin": "Conceder permisos de Administrador", - "revoke_admin": "Revocar permisos de Administrador", - "grant_moderator": "Conceder permisos de Moderador", - "revoke_moderator": "Revocar permisos de Moderador", + "grant_admin": "Conceder permisos de Administrador/a", + "revoke_admin": "Revocar permisos de Administrador/a", + "grant_moderator": "Conceder permisos de Moderador/a", + "revoke_moderator": "Revocar permisos de Moderador/a", "activate_account": "Activar cuenta", "deactivate_account": "Desactivar cuenta", "delete_account": "Eliminar cuenta", @@ -698,16 +728,23 @@ "roles": { "moderator": "Moderador", "admin": "Administrador" - } + }, + "highlight": { + "striped": "Fondo rayado", + "side": "Raya lateral", + "solid": "Fondo sólido", + "disabled": "Sin resaltado" + }, + "bot": "Bot" }, "user_profile": { - "timeline_title": "Linea Temporal del Usuario", + "timeline_title": "Línea temporal del usuario", "profile_does_not_exist": "Lo sentimos, este perfil no existe.", "profile_loading_error": "Lo sentimos, hubo un error al cargar este perfil." }, "user_reporting": { "title": "Reportando a {0}", - "add_comment_description": "El informe será enviado a los moderadores de su instancia. Puedes proporcionar una explicación de por qué estás reportando esta cuenta a continuación:", + "add_comment_description": "El informe será enviado a los/las moderadores/as de su instancia. Puedes proporcionar una explicación de por qué estás reportando esta cuenta a continuación:", "additional_comments": "Comentarios adicionales", "forward_description": "La cuenta es de otro servidor. ¿Enviar una copia del informe allí también?", "forward_to": "Reenviar a {0}", @@ -719,7 +756,7 @@ "who_to_follow": "A quién seguir" }, "tool_tip": { - "media_upload": "Subir Medios", + "media_upload": "Subir multimedia", "repeat": "Repetir", "reply": "Contestar", "favorite": "Favorito", @@ -777,12 +814,12 @@ "simple": { "accept_desc": "Esta instancia solo acepta mensajes de las siguientes instancias:", "media_nsfw_desc": "Esta instancia obliga a que los archivos multimedia se establezcan como sensibles en las publicaciones de las siguientes instancias:", - "media_nsfw": "Forzar Multimedia Como Sensible", + "media_nsfw": "Forzar contenido multimedia como sensible", "media_removal_desc": "Esta instancia elimina los archivos multimedia de las publicaciones de las siguientes instancias:", "media_removal": "Eliminar Multimedia", "quarantine": "Cuarentena", - "ftl_removal_desc": "Esta instancia elimina las siguientes instancias de la línea de tiempo \"Toda la red conocida\":", - "ftl_removal": "Eliminar de la línea de tiempo \"Toda La Red Conocida\"", + "ftl_removal_desc": "Esta instancia elimina las siguientes instancias de la línea de tiempo \"Red Conocida\":", + "ftl_removal": "Eliminar de la línea de tiempo \"Red Conocida\"", "quarantine_desc": "Esta instancia enviará solo publicaciones públicas a las siguientes instancias:", "simple_policies": "Políticas específicas de la instancia", "reject_desc": "Esta instancia no aceptará mensajes de las siguientes instancias:", diff --git a/src/i18n/eu.json b/src/i18n/eu.json @@ -14,7 +14,8 @@ "text_limit": "Testu limitea", "title": "Ezaugarriak", "who_to_follow": "Nori jarraitu", - "pleroma_chat_messages": "Pleroma Txata" + "pleroma_chat_messages": "Pleroma Txata", + "upload_limit": "Kargatzeko muga" }, "finder": { "error_fetching_user": "Errorea erabiltzailea eskuratzen", @@ -38,7 +39,11 @@ "dismiss": "Baztertu", "retry": "Saiatu berriro", "error_retry": "Saiatu berriro mesedez", - "loading": "Kargatzen…" + "loading": "Kargatzen…", + "role": { + "moderator": "Moderatzailea", + "admin": "Administratzailea" + } }, "image_cropper": { "crop_picture": "Moztu argazkia", @@ -81,8 +86,8 @@ "friend_requests": "Jarraitzeko eskaerak", "mentions": "Aipamenak", "interactions": "Interakzioak", - "dms": "Zuzeneko Mezuak", - "public_tl": "Denbora-lerro Publikoa", + "dms": "Zuzeneko mezuak", + "public_tl": "Denbora-lerro publikoa", "timeline": "Denbora-lerroa", "twkn": "Ezagutzen den Sarea", "user_search": "Erabiltzailea Bilatu", @@ -104,7 +109,8 @@ "no_more_notifications": "Ez dago jakinarazpen gehiago", "reacted_with": "{0}kin erreakzionatu zuen", "migrated_to": "hona migratua:", - "follow_request": "jarraitu nahi zaitu" + "follow_request": "jarraitu nahi zaitu", + "error": "Errorea jakinarazpenak eskuratzean: {0}" }, "polls": { "add_poll": "Inkesta gehitu", @@ -118,7 +124,9 @@ "expiry": "Inkestaren iraupena", "expires_in": "Inkesta {0} bukatzen da", "expired": "Inkesta {0} bukatu zen", - "not_enough_options": "Aukera gutxiegi inkestan" + "not_enough_options": "Aukera gutxiegi inkestan", + "votes_count": "{count} boto| {count} boto", + "people_voted_count": "Pertsona batek bozkatu du | {count} pertsonak bozkatu dute" }, "emoji": { "stickers": "Pegatinak", @@ -160,9 +168,9 @@ "unlisted": "Mezu hau ez da argitaratuko Denbora-lerro Publikoan ezta Ezagutzen den Sarean" }, "scope": { - "direct": "Zuzena: Bidali aipatutako erabiltzaileei besterik ez", - "private": "Jarraitzaileentzako bakarrik: Bidali jarraitzaileentzat bakarrik", - "public": "Publikoa: Bistaratu denbora-lerro publikoetan", + "direct": "Zuzena: bidali aipatutako erabiltzaileei besterik ez", + "private": "Jarraitzaileentzako bakarrik: bidali jarraitzaileentzat bakarrik", + "public": "Publikoa: bistaratu denbora-lerro publikoetan", "unlisted": "Zerrendatu gabea: ez bidali denbora-lerro publikoetara" } }, @@ -218,7 +226,7 @@ "attachmentRadius": "Eranskinak", "attachments": "Eranskinak", "avatar": "Avatarra", - "avatarAltRadius": "Avatarra (Aipamenak)", + "avatarAltRadius": "Abatarra (aipamenak)", "avatarRadius": "Avatarrak", "background": "Atzeko planoa", "bio": "Biografia", @@ -242,7 +250,7 @@ "current_avatar": "Zure uneko avatarra", "current_password": "Indarrean dagoen pasahitza", "current_profile_banner": "Zure profilaren banner-a", - "data_import_export_tab": "Datuak Inportatu / Esportatu", + "data_import_export_tab": "Datuak inportatu / esportatu", "default_vis": "Lehenetsitako ikusgaitasunak", "delete_account": "Ezabatu kontua", "discoverable": "Baimendu zure kontua kanpo bilaketa-emaitzetan eta bestelako zerbitzuetan agertzea", @@ -304,19 +312,19 @@ "hide_followers_description": "Ez erakutsi nor ari den ni jarraitzen", "hide_follows_count_description": "Ez erakutsi jarraitzen ari naizen kontuen kopurua", "hide_followers_count_description": "Ez erakutsi nire jarraitzaileen kontuen kopurua", - "show_admin_badge": "Erakutsi Administratzaile etiketa nire profilan", - "show_moderator_badge": "Erakutsi Moderatzaile etiketa nire profilan", + "show_admin_badge": "Erakutsi \"Administratzaile\" etiketa nire profilan", + "show_moderator_badge": "Erakutsi \"Moderatzaile\" etiketa nire profilan", "nsfw_clickthrough": "Gaitu klika hunkigarri eranskinak ezkutatzeko", "oauth_tokens": "OAuth tokenak", "token": "Tokena", - "refresh_token": "Berrgin Tokena", - "valid_until": "Baliozkoa Arte", + "refresh_token": "Berrgin tokena", + "valid_until": "Baliozkoa arte", "revoke_token": "Ezeztatu", "panelRadius": "Panelak", "pause_on_unfocused": "Eguneraketa automatikoa gelditu fitxatik kanpo", "presets": "Aurrezarpenak", "profile_background": "Profilaren atzeko planoa", - "profile_banner": "Profilaren Banner-a", + "profile_banner": "Profilaren banner-a", "profile_tab": "Profila", "radii_help": "Konfiguratu interfazearen ertzen biribiltzea (pixeletan)", "replies_in_timeline": "Denbora-lerroko erantzunak", @@ -470,8 +478,8 @@ }, "version": { "title": "Bertsioa", - "backend_version": "Backend Bertsioa", - "frontend_version": "Frontend Bertsioa" + "backend_version": "Backend bertsioa", + "frontend_version": "Frontend bertsioa" } }, "time": { @@ -657,7 +665,7 @@ "federation": "Federazioa", "simple": { "media_nsfw_desc": "Instantzia honek hurrengo instantzien multimediak sentikorrak izatera behartzen ditu:", - "media_nsfw": "Behartu Multimedia Sentikor", + "media_nsfw": "Behartu multimedia sentikor moduan", "media_removal_desc": "Instantzia honek atxikitutako multimedia hurrengo instantzietatik ezabatzen ditu:", "media_removal": "Multimedia Ezabatu", "ftl_removal_desc": "Instantzia honek hurrengo instantziak ezabatzen ditu \"Ezagutzen den Sarea\" denbora-lerrotik:", diff --git a/src/i18n/fr.json b/src/i18n/fr.json @@ -9,16 +9,17 @@ "features_panel": { "chat": "Chat", "gopher": "Gopher", - "media_proxy": "Proxy média", + "media_proxy": "Proxy pièce-jointes", "scope_options": "Options de visibilité", - "text_limit": "Limite de texte", - "title": "Caractéristiques", - "who_to_follow": "Personnes à suivre", - "pleroma_chat_messages": "Chat Pleroma" + "text_limit": "Limite du texte", + "title": "Fonctionnalités", + "who_to_follow": "Suggestions de suivis", + "pleroma_chat_messages": "Chat Pleroma", + "upload_limit": "Limite de téléversement" }, "finder": { - "error_fetching_user": "Erreur lors de la recherche de l'utilisateur·ice", - "find_user": "Chercher un-e utilisateur·ice" + "error_fetching_user": "Erreur lors de la recherche du compte", + "find_user": "Rechercher un compte" }, "general": { "apply": "Appliquer", @@ -26,19 +27,23 @@ "more": "Plus", "generic_error": "Une erreur s'est produite", "optional": "optionnel", - "show_more": "Montrer plus", - "show_less": "Montrer moins", + "show_more": "Afficher plus", + "show_less": "Afficher moins", "cancel": "Annuler", "disable": "Désactiver", "enable": "Activer", "confirm": "Confirmer", "verify": "Vérifier", - "dismiss": "Rejeter", + "dismiss": "Ignorer", "peek": "Jeter un coup d'œil", "close": "Fermer", "retry": "Réessayez", "error_retry": "Veuillez réessayer", - "loading": "Chargement…" + "loading": "Chargement…", + "role": { + "moderator": "Modo'", + "admin": "Admin" + } }, "image_cropper": { "crop_picture": "Rogner l'image", @@ -47,7 +52,7 @@ "cancel": "Annuler" }, "importer": { - "submit": "Soumettre", + "submit": "Envoyer", "success": "Importé avec succès.", "error": "Une erreur est survenue pendant l'import de ce fichier." }, @@ -56,17 +61,17 @@ "description": "Connexion avec OAuth", "logout": "Déconnexion", "password": "Mot de passe", - "placeholder": "p.e. lain", + "placeholder": "ex. lain", "register": "S'inscrire", "username": "Identifiant", "hint": "Connectez-vous pour rejoindre la discussion", "authentication_code": "Code d'authentification", "enter_recovery_code": "Entrez un code de récupération", - "enter_two_factor_code": "Entrez un code à double authentification", + "enter_two_factor_code": "Entrez un code double-facteur", "recovery_code": "Code de récupération", "heading": { - "totp": "Authentification à double authentification", - "recovery": "Récuperation de la double authentification" + "totp": "Authentification à double-facteur", + "recovery": "Récupération de l'authentification à double-facteur" } }, "media_modal": { @@ -78,24 +83,26 @@ "back": "Retour", "chat": "Chat local", "friend_requests": "Demandes de suivi", - "mentions": "Notifications", + "mentions": "Mentions", "interactions": "Interactions", "dms": "Messages directs", - "public_tl": "Fil d'actualité public", - "timeline": "Fil d'actualité", + "public_tl": "Flux publique", + "timeline": "Flux personnel", "twkn": "Réseau connu", - "user_search": "Recherche d'utilisateur·ice", - "who_to_follow": "Qui suivre", + "user_search": "Recherche de comptes", + "who_to_follow": "Suggestion de suivit", "preferences": "Préférences", "search": "Recherche", "administration": "Administration", "chats": "Chats", - "bookmarks": "Marques-Pages" + "bookmarks": "Marques-Pages", + "timelines": "Flux", + "home_timeline": "Flux personnel" }, "notifications": { - "broken_favorite": "Message inconnu, chargement…", + "broken_favorite": "Message inconnu, recherche en cours…", "favorited_you": "a aimé votre statut", - "followed_you": "a commencé à vous suivre", + "followed_you": "vous suit", "load_older": "Charger les notifications précédentes", "notifications": "Notifications", "read": "Lu !", @@ -103,7 +110,8 @@ "no_more_notifications": "Aucune notification supplémentaire", "migrated_to": "a migré à", "reacted_with": "a réagi avec {0}", - "follow_request": "veut vous suivre" + "follow_request": "veut vous suivre", + "error": "Erreur de chargement des notifications : {0}" }, "interactions": { "favs_repeats": "Partages et favoris", @@ -115,7 +123,7 @@ "new_status": "Poster un nouveau statut", "account_not_locked_warning": "Votre compte n'est pas {0}. N'importe qui peut vous suivre pour voir vos billets en Abonné·e·s uniquement.", "account_not_locked_warning_link": "verrouillé", - "attachments_sensitive": "Marquer le média comme sensible", + "attachments_sensitive": "Marquer les pièce-jointes comme sensible", "content_type": { "text/plain": "Texte brut", "text/html": "HTML", @@ -130,32 +138,33 @@ "scope_notice": { "public": "Ce statut sera visible par tout le monde", "private": "Ce statut sera visible par seulement vos abonné⋅e⋅s", - "unlisted": "Ce statut ne sera pas visible dans le Fil d'actualité public et l'Ensemble du réseau connu" + "unlisted": "Ce statut ne sera pas visible dans le Flux Public et le Flux Fédéré" }, "scope": { "direct": "Direct - N'envoyer qu'aux personnes mentionnées", - "private": "Abonné·e·s uniquement - Seul·e·s vos abonné·e·s verront vos billets", - "public": "Publique - Afficher dans les fils publics", - "unlisted": "Non-Listé - Ne pas afficher dans les fils publics" + "private": "Abonné·e·s uniquement - Seul·e·s vos abonné·e·s verront vos status", + "public": "Publique - Afficher dans les flux publics", + "unlisted": "Non-Listé - Ne pas afficher dans les flux publics" }, "media_description_error": "Échec de téléversement du media, essayez encore", - "empty_status_error": "Impossible de poster un statut vide sans attachements", + "empty_status_error": "Impossible de poster un statut vide sans pièces-jointes", "preview_empty": "Vide", "preview": "Prévisualisation", - "media_description": "Description de l'attachement" + "media_description": "Description de la pièce-jointe", + "post": "Post" }, "registration": { "bio": "Biographie", - "email": "Adresse mail", + "email": "Courriel", "fullname": "Pseudonyme", "password_confirm": "Confirmation du mot de passe", "registration": "Inscription", "token": "Jeton d'invitation", "captcha": "CAPTCHA", "new_captcha": "Cliquez sur l'image pour avoir un nouveau captcha", - "username_placeholder": "p.e. lain", - "fullname_placeholder": "p.e. Lain Iwakura", - "bio_placeholder": "p.e.\nSalut, je suis Lain\nJe suis une héroïne d'animé qui vit dans une banlieue japonaise. Vous me connaissez peut-être du Wired.", + "username_placeholder": "ex. lain", + "fullname_placeholder": "ex. Lain Iwakura", + "bio_placeholder": "ex.\nSalut, je suis Lain\nJe suis une héroïne d'animation qui vit dans une banlieue japonaise. Vous me connaissez peut-être du Wired.", "validations": { "username_required": "ne peut pas être laissé vide", "fullname_required": "ne peut pas être laissé vide", @@ -163,7 +172,10 @@ "password_required": "ne peut pas être laissé vide", "password_confirmation_required": "ne peut pas être laissé vide", "password_confirmation_match": "doit être identique au mot de passe" - } + }, + "reason_placeholder": "Cette instance modère les inscriptions manuellement.\nExpliquer ce qui motive votre inscription à l'administration.", + "reason": "Motivation d'inscription", + "register": "Enregistrer" }, "selectable_list": { "select_all": "Tout selectionner" @@ -177,20 +189,20 @@ "setup_otp": "Configurer OTP", "wait_pre_setup_otp": "préconfiguration OTP", "confirm_and_enable": "Confirmer & activer OTP", - "title": "Double authentification", + "title": "Authentification double-facteur", "generate_new_recovery_codes": "Générer de nouveaux codes de récupération", - "warning_of_generate_new_codes": "Quand vous générez de nouveauc codes de récupération, vos anciens codes ne fonctionnerons plus.", + "warning_of_generate_new_codes": "Quand vous générez de nouveaux codes de récupération, vos anciens codes ne fonctionnerons plus.", "recovery_codes": "Codes de récupération.", "waiting_a_recovery_codes": "Réception des codes de récupération…", - "recovery_codes_warning": "Écrivez les codes ou sauvez les quelquepart sécurisé - sinon vous ne les verrez plus jamais. Si vous perdez l'accès à votre application de double authentification et codes de récupération vous serez vérouillé en dehors de votre compte.", - "authentication_methods": "Methodes d'authentification", + "recovery_codes_warning": "Écrivez ces codes ou sauvegardez les dans un endroit sécurisé - sinon vous ne les verrez plus jamais. Si vous perdez l'accès à votre application de double authentification et codes de récupération vous serez verrouillé en dehors de votre compte.", + "authentication_methods": "Méthodes d'authentification", "scan": { "title": "Scanner", - "desc": "En utilisant votre application de double authentification, scannez ce QR code ou entrez la clé textuelle :", + "desc": "En utilisant votre application d'authentification à double-facteur, scannez ce QR code ou entrez la clé textuelle :", "secret_code": "Clé" }, "verify": { - "desc": "Pour activer la double authentification, entrez le code depuis votre application :" + "desc": "Pour activer l'authentification à double-facteur, entrez le code donné par votre application :" } }, "attachmentRadius": "Pièces jointes", @@ -201,10 +213,10 @@ "background": "Arrière-plan", "bio": "Biographie", "block_export": "Export des comptes bloqués", - "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_error": "Erreur lors de l'import des comptes bloqués", - "blocks_imported": "Blocks importés ! Le traitement va prendre un moment.", + "blocks_imported": "Blocages importés ! Le traitement va prendre un moment.", "blocks_tab": "Bloqué·e·s", "btnRadius": "Boutons", "cBlue": "Bleu (répondre, suivre)", @@ -224,31 +236,31 @@ "default_vis": "Visibilité par défaut", "delete_account": "Supprimer le compte", "delete_account_description": "Supprimer définitivement vos données et désactiver votre compte.", - "delete_account_error": "Il y a eu un problème lors de la tentative de suppression de votre compte. Si le problème persiste, contactez l'administrateur⋅ice de cette instance.", + "delete_account_error": "Il y a eu un problème lors de la tentative de suppression de votre compte. Si le problème persiste, contactez l'administration de cette instance.", "delete_account_instructions": "Indiquez votre mot de passe ci-dessous pour confirmer la suppression de votre compte.", "avatar_size_instruction": "La taille minimale recommandée pour l'image de l'avatar est de 150x150 pixels.", "export_theme": "Enregistrer le thème", - "filtering": "Filtre", + "filtering": "Filtrage", "filtering_explanation": "Tous les statuts contenant ces mots seront masqués. Un mot par ligne", - "follow_export": "Exporter les abonnements", - "follow_export_button": "Exporter les abonnements en csv", - "follow_import": "Importer des abonnements", - "follow_import_error": "Erreur lors de l'importation des abonnements", - "follows_imported": "Abonnements importés ! Le traitement peut prendre un moment.", + "follow_export": "Exporter les suivis", + "follow_export_button": "Exporter les suivis dans un fichier CSV", + "follow_import": "Import des suivis", + "follow_import_error": "Erreur lors de l'importation des suivis", + "follows_imported": "Suivis importés ! Le traitement peut prendre un moment.", "foreground": "Premier plan", "general": "Général", "hide_attachments_in_convo": "Masquer les pièces jointes dans les conversations", - "hide_attachments_in_tl": "Masquer les pièces jointes dans le journal", - "hide_muted_posts": "Masquer les statuts des utilisateurs masqués", + "hide_attachments_in_tl": "Masquer les pièces jointes dans le flux", + "hide_muted_posts": "Masquer les statuts des comptes masqués", "max_thumbnails": "Nombre maximum de miniatures par statuts", - "hide_isp": "Masquer le panneau spécifique a l'instance", + "hide_isp": "Masquer le panneau de l'instance", "preload_images": "Précharger les images", - "use_one_click_nsfw": "Ouvrir les pièces-jointes NSFW avec un seul clic", - "hide_post_stats": "Masquer les statistiques de publication (le nombre de favoris)", - "hide_user_stats": "Masquer les statistiques de profil (le nombre d'amis)", + "use_one_click_nsfw": "Ouvrir les pièces-jointes sensibles avec un seul clic", + "hide_post_stats": "Masquer les statistiques des messages (ex. le nombre de favoris)", + "hide_user_stats": "Masquer les statistiques de compte (ex. le nombre de suivis)", "hide_filtered_statuses": "Masquer les statuts filtrés", - "import_blocks_from_a_csv_file": "Importer les blocages depuis un fichier csv", - "import_followers_from_a_csv_file": "Importer des abonnements depuis un fichier csv", + "import_blocks_from_a_csv_file": "Import de blocages depuis un fichier CSV", + "import_followers_from_a_csv_file": "Import de suivis depuis un fichier CSV", "import_theme": "Charger le thème", "inputRadius": "Champs de texte", "checkboxRadius": "Cases à cocher", @@ -269,7 +281,7 @@ "name_bio": "Nom & Bio", "new_password": "Nouveau mot de passe", "notification_visibility": "Types de notifications à afficher", - "notification_visibility_follows": "Abonnements", + "notification_visibility_follows": "Suivis", "notification_visibility_likes": "J'aime", "notification_visibility_mentions": "Mentionnés", "notification_visibility_repeats": "Partages", @@ -278,8 +290,8 @@ "no_mutes": "Aucun masqués", "hide_follows_description": "Ne pas afficher à qui je suis abonné", "hide_followers_description": "Ne pas afficher qui est abonné à moi", - "show_admin_badge": "Afficher le badge d'Administrateur⋅ice sur mon profil", - "show_moderator_badge": "Afficher le badge de Modérateur⋅ice sur mon profil", + "show_admin_badge": "Afficher le badge d'Admin sur mon profil", + "show_moderator_badge": "Afficher le badge de Modo' sur mon profil", "nsfw_clickthrough": "Activer le clic pour dévoiler les pièces jointes et cacher l'aperçu des liens pour les statuts marqués comme sensibles", "oauth_tokens": "Jetons OAuth", "token": "Jeton", @@ -289,11 +301,11 @@ "panelRadius": "Fenêtres", "pause_on_unfocused": "Suspendre le streaming lorsque l'onglet n'est pas actif", "presets": "Thèmes prédéfinis", - "profile_background": "Image de fond", + "profile_background": "Image de fond de profil", "profile_banner": "Bannière de profil", "profile_tab": "Profil", "radii_help": "Vous pouvez ici choisir le niveau d'arrondi des angles de l'interface (en pixels)", - "replies_in_timeline": "Réponses au journal", + "replies_in_timeline": "Réponses dans le flux", "reply_visibility_all": "Montrer toutes les réponses", "reply_visibility_following": "Afficher uniquement les réponses adressées à moi ou aux personnes que je suis", "reply_visibility_self": "Afficher uniquement les réponses adressées à moi", @@ -309,7 +321,7 @@ "set_new_profile_background": "Changer d'image de fond", "set_new_profile_banner": "Changer de bannière", "settings": "Paramètres", - "subject_input_always_show": "Toujours copier le champ de sujet", + "subject_input_always_show": "Toujours afficher le champ Sujet", "subject_line_behavior": "Copier le sujet en répondant", "subject_line_email": "Similaire au courriel : « re : sujet »", "subject_line_mastodon": "Comme mastodon : copier tel quel", @@ -348,7 +360,7 @@ "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.", + "v2_imported": "Le fichier que vous avez importé vient d'une version antérieure. Nous essayons de maximizer la compatibilité mais il peut 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.", "snapshot_source_mismatch": "Conflict de version : Probablement due à un retour arrière puis remise à jour de la version de PleromaFE, si vous avez charger le thème en utilisant une version antérieure vous voulez probablement utiliser la version antérieure, autrement utiliser la version postérieure.", @@ -432,7 +444,7 @@ "filter_hint": { "always_drop_shadow": "Attention, cette ombre utilise toujours {0} quand le navigateur le supporte.", "drop_shadow_syntax": "{0} ne supporte pas le paramètre {1} et mot-clé {2}.", - "avatar_inset": "Veuillez noter que combiner a la fois les ombres internes et non-internes sur les avatars peut fournir des résultats innatendus avec la transparence des avatars.", + "avatar_inset": "Veuillez noter que combiner à la fois les ombres internes et non-internes sur les avatars peut fournir des résultats inattendus avec la transparence des avatars.", "spread_zero": "Les ombres avec une dispersion > 0 apparaitrons comme si ils étaient à zéro", "inset_classic": "L'ombre interne utilisera toujours {0}" }, @@ -487,15 +499,15 @@ }, "change_email": "Changer de courriel", "domain_mutes": "Domaines", - "pad_emoji": "Rajouter un espace autour de l'émoji après l’avoir choisit", + "pad_emoji": "Entourer les émoji d'espaces après leur sélections", "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": "Chercher 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.", + "allow_following_move": "Activer le suivit automatique à la migration des comptes", + "change_email_error": "Il y a eu un problème pour changer 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", @@ -510,7 +522,7 @@ "accent": "Accent", "chatMessageRadius": "Message de chat", "bot": "Ce compte est un robot", - "import_mutes_from_a_csv_file": "Importer les masquages depuis un fichier CSV", + "import_mutes_from_a_csv_file": "Import de masquages depuis un fichier CSV", "mutes_imported": "Masquages importés ! Leur application peut prendre du temps.", "mute_import_error": "Erreur à l'import des masquages", "mute_import": "Import des masquages", @@ -518,24 +530,36 @@ "mute_export": "Export des masquages", "notification_setting_hide_notification_contents": "Cacher l'expéditeur et le contenu des notifications push", "notification_setting_block_from_strangers": "Bloquer les notifications des utilisateur⋅ice⋅s que vous ne suivez pas", - "virtual_scrolling": "Optimiser le rendu du fil d'actualité", + "virtual_scrolling": "Optimiser le rendu des flux", "reset_background_confirm": "Voulez-vraiment réinitialiser l'arrière-plan ?", "reset_banner_confirm": "Voulez-vraiment réinitialiser la bannière ?", "reset_avatar_confirm": "Voulez-vraiment réinitialiser l'avatar ?", "reset_profile_banner": "Réinitialiser la bannière du profil", - "reset_profile_background": "Réinitialiser l'arrière-plan du profil", + "reset_profile_background": "Réinitialiser le fond du profil", "reset_avatar": "Réinitialiser l'avatar", "profile_fields": { "value": "Contenu", - "name": "Étiquette", - "add_field": "Ajouter un champ" - } + "name": "Nom du champ", + "add_field": "Ajouter un champ", + "label": "Champs du profil" + }, + "hide_media_previews": "Cacher la prévisualisation des pièces jointes", + "mutes_and_blocks": "Masquage et Blocages", + "setting_changed": "Préférence modifiée", + "more_settings": "Plus de préférences", + "sensitive_by_default": "Marquer les messages comme sensible par défaut", + "reply_visibility_self_short": "Uniquement les réponses à moi", + "reply_visibility_following_short": "Montrer les réponses à mes suivis", + "hide_wallpaper": "Cacher le fond d'écran", + "hide_all_muted_posts": "Cacher les messages masqués", + "word_filter": "Filtrage par mots", + "save": "Enregistrer les changements" }, "timeline": { "collapse": "Fermer", "conversation": "Conversation", "error_fetching": "Erreur en cherchant les mises à jour", - "load_older": "Afficher plus", + "load_older": "Afficher des status plus ancien", "no_retweet_hint": "Le message est marqué en abonnés-seulement ou direct et ne peut pas être partagé", "repeated": "a partagé", "show_new": "Afficher plus", @@ -543,14 +567,16 @@ "no_more_statuses": "Pas plus de statuts", "no_statuses": "Aucun statuts", "reload": "Recharger", - "error": "Erreur lors de l'affichage du fil d'actualité : {0}" + "error": "Erreur lors de l'affichage du flux : {0}", + "socket_broke": "Connexion temps-réel perdue : CloseEvent code {0}", + "socket_reconnected": "Connexion temps-réel établie" }, "status": { "favorites": "Favoris", "repeats": "Partages", "delete": "Supprimer statuts", - "pin": "Agraffer sur le profil", - "unpin": "Dégraffer du profil", + "pin": "Agrafer sur le profil", + "unpin": "Dégrafer du profil", "pinned": "Agraffé", "delete_confirm": "Voulez-vous vraiment supprimer ce statuts ?", "reply_to": "Réponse à", @@ -630,10 +656,17 @@ "moderator": "Modérateur⋅ice", "admin": "Administrateur⋅ice" }, - "message": "Message" + "message": "Message", + "highlight": { + "disabled": "Sans mise-en-valeur", + "solid": "Fond uni", + "side": "Coté rayé", + "striped": "Fond rayé" + }, + "bot": "Robot" }, "user_profile": { - "timeline_title": "Journal de l'utilisateur⋅ice", + "timeline_title": "Flux du compte", "profile_does_not_exist": "Désolé, ce profil n'existe pas.", "profile_loading_error": "Désolé, il y a eu une erreur au chargement du profil." }, @@ -669,45 +702,45 @@ "message": "Envoi échoué : {0}" }, "file_size_units": { - "B": "O", - "KiB": "KiO", - "MiB": "MiO", - "GiB": "GiO", - "TiB": "TiO" + "B": "o", + "KiB": "Ko", + "MiB": "Mo", + "GiB": "Go", + "TiB": "To" } }, "about": { "mrf": { "keyword": { - "reject": "Rejeté", - "replace": "Remplacer", - "keyword_policies": "Politiques par mot-clés", + "reject": "Rejette", + "replace": "Remplace", + "keyword_policies": "Filtrage par mots-clés", "ftl_removal": "Suppression du flux fédéré", "is_replaced_by": "→" }, "simple": { "simple_policies": "Politiques par instances", - "accept": "Accepter", - "accept_desc": "Cette instance accepte des messages seulement depuis ces instances :", - "reject": "Rejeter", + "accept": "Acceptées", + "accept_desc": "Cette instance accepte les messages seulement depuis ces instances :", + "reject": "Rejetées", "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", + "quarantine_desc": "Cette instance enverra seulement des messages publics à ces instances :", + "ftl_removal_desc": "Cette instance supprime les instance suivantes du flux fédéré :", + "media_removal": "Suppression des pièce-jointes", "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 :" + "ftl_removal": "Supprimées du flux fédéré", + "media_nsfw_desc": "Cette instance force les pièce-jointes comme sensible pour les messages des instances suivantes :" }, "federation": "Fédération", - "mrf_policies": "Politiques MRF activées", + "mrf_policies": "Politiques MRF actives", "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": "Masqué", "mute_progress": "Masquage…", "unmute": "Démasquer", "unmute_progress": "Démasquage…" @@ -724,7 +757,9 @@ "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}" + "expired": "Sondage terminé il y a {0}", + "people_voted_count": "{count} voteur | {count} voteurs", + "votes_count": "{count} vote | {count} votes" }, "emoji": { "emoji": "Émoji", @@ -735,11 +770,11 @@ "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", - "keep_open": "Garder le sélecteur ouvert" + "keep_open": "Garder ouvert" }, "remote_user_resolver": { "error": "Non trouvé.", - "searching_for": "Rechercher", + "searching_for": "Recherche pour", "remote_user_resolver": "Résolution de compte distant" }, "time": { diff --git a/src/i18n/it.json b/src/i18n/it.json @@ -27,7 +27,7 @@ "mentions": "Menzioni", "public_tl": "Sequenza pubblica", "timeline": "Sequenza personale", - "twkn": "Sequenza globale", + "twkn": "Sequenza federale", "chat": "Chat della stanza", "friend_requests": "Vogliono seguirti", "about": "Informazioni", @@ -41,14 +41,15 @@ "preferences": "Preferenze", "bookmarks": "Segnalibri", "chats": "Conversazioni", - "timelines": "Sequenze" + "timelines": "Sequenze", + "home_timeline": "Sequenza personale" }, "notifications": { "followed_you": "ti segue", "notifications": "Notifiche", "read": "Letto!", "broken_favorite": "Stato sconosciuto, lo sto cercando…", - "favorited_you": "gradisce il tuo messaggio", + "favorited_you": "ha gradito", "load_older": "Carica notifiche precedenti", "repeated_you": "ha condiviso il tuo messaggio", "follow_request": "vuole seguirti", @@ -71,10 +72,10 @@ "name_bio": "Nome ed introduzione", "nsfw_clickthrough": "Fai click per visualizzare gli allegati offuscati", "profile_background": "Sfondo della tua pagina", - "profile_banner": "Stendardo del tuo profilo", + "profile_banner": "Gonfalone del tuo profilo", "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", + "set_new_profile_background": "Scegli un nuovo sfondo", + "set_new_profile_banner": "Scegli un nuovo gonfalone", "settings": "Impostazioni", "theme": "Tema", "user_settings": "Impostazioni Utente", @@ -83,9 +84,9 @@ "avatarRadius": "Icone utente", "background": "Sfondo", "btnRadius": "Pulsanti", - "cBlue": "Blu (risposte, seguire)", + "cBlue": "Blu (rispondi, segui)", "cGreen": "Verde (ripeti)", - "cOrange": "Arancione (gradire)", + "cOrange": "Arancione (gradisci)", "cRed": "Rosso (annulla)", "change_password": "Cambia password", "change_password_error": "C'è stato un problema durante il cambiamento della password.", @@ -98,7 +99,7 @@ "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.", + "delete_account_instructions": "Digita la tua password nel campo sottostante per eliminare il 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", @@ -109,7 +110,7 @@ "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)", + "hide_user_stats": "Nascondi statistiche dell'utente (es. il numero di 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", @@ -118,12 +119,12 @@ "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", + "lock_account_description": "Vaglia manualmente i nuovi seguaci", "loop_video": "Riproduci video in ciclo continuo", - "loop_video_silent_only": "Riproduci solo video senza audio in ciclo continuo (es. le \"gif\" di Mastodon)", + "loop_video_silent_only": "Riproduci solo video muti 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_follows": "Nuovi seguaci", "notification_visibility_likes": "Preferiti", "notification_visibility_mentions": "Menzioni", "notification_visibility_repeats": "Condivisioni", @@ -138,7 +139,7 @@ "presets": "Valori predefiniti", "profile_tab": "Profilo", "radii_help": "Imposta il raggio degli angoli (in pixel)", - "replies_in_timeline": "Risposte nella sequenza personale", + "replies_in_timeline": "Risposte nelle sequenze", "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", @@ -148,7 +149,7 @@ "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.", + "theme_help": "Usa colori esadecimali (#rrggbb) per personalizzare il tuo schema di colori.", "tooltipRadius": "Suggerimenti/avvisi", "values": { "false": "no", @@ -156,7 +157,7 @@ }, "avatar_size_instruction": "La taglia minima per l'icona personale è 150x150 pixel.", "domain_mutes": "Domini", - "discoverable": "Permetti la scoperta di questo profilo da servizi di ricerca ed altro", + "discoverable": "Permetti la scoperta di questo profilo a servizi di ricerca ed altro", "composing": "Composizione", "changed_email": "Email cambiata con successo!", "change_email_error": "C'è stato un problema nel cambiare la tua email.", @@ -167,18 +168,18 @@ "block_import": "Importa blocchi", "block_export_button": "Esporta i tuoi blocchi in un file CSV", "block_export": "Esporta blocchi", - "allow_following_move": "Consenti", + "allow_following_move": "Consenti l'iscrizione automatica ai profili traslocati", "mfa": { "verify": { "desc": "Per abilitare l'autenticazione bifattoriale, inserisci il codice fornito dalla tua applicazione:" }, "scan": { "secret_code": "Codice", - "desc": "Con la tua applicazione bifattoriale, acquisisci questo QR o inserisci il codice manualmente:", + "desc": "Con la tua applicazione bifattoriale, acquisisci il QR o inserisci il codice:", "title": "Acquisisci" }, "authentication_methods": "Metodi di accesso", - "recovery_codes_warning": "Appuntati i codici o salvali in un posto sicuro, altrimenti rischi di non rivederli mai più. Se perderai l'accesso sia alla tua applicazione bifattoriale che ai codici di recupero non potrai più accedere al tuo profilo.", + "recovery_codes_warning": "Metti i codici al sicuro, perché non potrai più visualizzarli. Se perderai l'accesso sia alla tua applicazione bifattoriale che ai codici di recupero non potrai più accedere al tuo profilo.", "waiting_a_recovery_codes": "Ricevo codici di recupero…", "recovery_codes": "Codici di recupero.", "warning_of_generate_new_codes": "Alla generazione di nuovi codici di recupero, quelli vecchi saranno disattivati.", @@ -197,14 +198,14 @@ "help": { "older_version_imported": "Il tema importato è stato creato per una versione precedente dell'interfaccia.", "future_version_imported": "Il tema importato è stato creato per una versione più recente dell'interfaccia.", - "v2_imported": "Il tema importato è stato creato per una vecchia interfaccia. Non tutto potrebbe essere come prima.", - "upgraded_from_v2": "L'interfaccia è stata aggiornata, il tema potrebbe essere diverso da come lo intendevi.", + "v2_imported": "Il tema importato è stato creato per una vecchia interfaccia. Non tutto potrebbe essere come inteso.", + "upgraded_from_v2": "L'interfaccia è stata aggiornata, il tema potrebbe essere diverso da come lo ricordi.", "migration_snapshot_ok": "Ho caricato l'anteprima del tema. Puoi provare a caricarne i contenuti.", "fe_downgraded": "L'interfaccia è stata portata ad una versione precedente.", "fe_upgraded": "Lo schema dei temi è stato aggiornato insieme all'interfaccia.", "snapshot_missing": "Il tema non è provvisto di anteprima, quindi potrebbe essere diverso da come appare.", "snapshot_present": "Tutti i valori sono sostituiti dall'anteprima del tema. Puoi invece caricare i suoi contenuti.", - "snapshot_source_mismatch": "Conflitto di versione: probabilmente l'interfaccia è stata portata ad una versione precedente e poi aggiornata di nuovo. Se hai modificato il tema con una versione precedente dell'interfaccia, usa la vecchia versione del tema, altrimenti puoi usare la nuova.", + "snapshot_source_mismatch": "Conflitto di versione: probabilmente l'interfaccia è stata portata indietro e poi aggiornata di nuovo. Se hai modificato il tema con una vecchia versione usa il tema precedente, altrimenti puoi usare il nuovo.", "migration_napshot_gone": "Anteprima del tema non trovata, non tutto potrebbe essere come ricordi." }, "use_source": "Nuova versione", @@ -227,7 +228,7 @@ "contrast": { "context": { "text": "per il testo", - "18pt": "per il testo grande (oltre 17pt)" + "18pt": "per il testo oltre 17pt" }, "level": { "bad": "non soddisfa le linee guida di alcun livello", @@ -250,7 +251,7 @@ "selectedMenu": "Voce menù selezionata", "selectedPost": "Messaggio selezionato", "pressed": "Premuto", - "highlight": "Elementi evidenziati", + "highlight": "Elementi in risalto", "icons": "Icone", "poll": "Grafico sondaggi", "underlay": "Sottostante", @@ -312,8 +313,8 @@ "fonts": { "_tab_label": "Font", "custom": "Personalizzato", - "weight": "Peso (grassettatura)", - "size": "Dimensione (in pixel)", + "weight": "Grassettatura", + "size": "Dimensione in pixel", "family": "Nome font", "components": { "postCode": "Font a spaziatura fissa incluso in un messaggio", @@ -340,15 +341,15 @@ }, "enable_web_push_notifications": "Abilita notifiche web push", "fun": "Divertimento", - "notification_mutes": "Per non ricevere notifiche da uno specifico utente, zittiscilo.", + "notification_mutes": "Per non ricevere notifiche da uno specifico utente, silenzialo.", "notification_setting_privacy_option": "Nascondi mittente e contenuti delle notifiche push", "notification_setting_privacy": "Privacy", "notification_setting_filters": "Filtri", "notifications": "Notifiche", "greentext": "Frecce da meme", "upload_a_photo": "Carica un'immagine", - "type_domains_to_mute": "Cerca domini da zittire", - "theme_help_v2_2": "Le icone dietro alcuni elementi sono indicatori del contrasto fra testo e sfondo, passaci sopra col puntatore per ulteriori informazioni. Se si usano delle trasparenze, questi indicatori mostrano il peggior caso possibile.", + "type_domains_to_mute": "Cerca domini da silenziare", + "theme_help_v2_2": "Le icone vicino alcuni elementi sono indicatori del contrasto fra testo e sfondo, passaci sopra col puntatore per ulteriori informazioni. Se usani trasparenze, questi indicatori mostrano il peggior caso possibile.", "theme_help_v2_1": "Puoi anche forzare colore ed opacità di alcuni elementi selezionando la casella. Usa il pulsante \"Azzera\" per azzerare tutte le forzature.", "useStreamingApiWarning": "(Sconsigliato, sperimentale, può saltare messaggi)", "useStreamingApi": "Ricevi messaggi e notifiche in tempo reale", @@ -361,7 +362,7 @@ "subject_input_always_show": "Mostra sempre il campo Oggetto", "minimal_scopes_mode": "Riduci opzioni di visibilità", "scope_copy": "Risposte ereditano la visibilità (messaggi privati lo fanno sempre)", - "search_user_to_mute": "Cerca utente da zittire", + "search_user_to_mute": "Cerca utente da silenziare", "search_user_to_block": "Cerca utente da bloccare", "autohide_floating_post_button": "Nascondi automaticamente il pulsante di composizione (mobile)", "show_moderator_badge": "Mostra l'insegna di moderatore sulla mia pagina", @@ -370,14 +371,14 @@ "hide_follows_count_description": "Non mostrare quanti utenti seguo", "hide_followers_description": "Non mostrare i miei seguaci", "hide_follows_description": "Non mostrare chi seguo", - "no_mutes": "Nessun utente zittito", + "no_mutes": "Nessun utente silenziato", "no_blocks": "Nessun utente bloccato", "notification_visibility_emoji_reactions": "Reazioni", "notification_visibility_moves": "Migrazioni utenti", "new_email": "Nuova email", "use_contain_fit": "Non ritagliare le anteprime degli allegati", "play_videos_in_modal": "Riproduci video in un riquadro a sbalzo", - "mutes_tab": "Zittiti", + "mutes_tab": "Silenziati", "interface": "Interfaccia", "instance_default_simple": "(predefinito)", "checkboxRadius": "Caselle di selezione", @@ -387,60 +388,82 @@ "preload_images": "Precarica immagini", "hide_isp": "Nascondi pannello della stanza", "max_thumbnails": "Numero massimo di anteprime per messaggio", - "hide_muted_posts": "Nascondi messaggi degli utenti zilenziati", + "hide_muted_posts": "Nascondi messaggi degli utenti silenziati", "accent": "Accento", - "emoji_reactions_on_timeline": "Mostra emoji di reazione sulle sequenze", + "emoji_reactions_on_timeline": "Mostra reazioni nelle sequenze", "pad_emoji": "Affianca spazi agli emoji inseriti tramite selettore", "notification_blocks": "Bloccando un utente non riceverai più le sue notifiche né lo seguirai più.", - "mutes_and_blocks": "Zittiti e bloccati", + "mutes_and_blocks": "Silenziati e bloccati", "profile_fields": { "value": "Contenuto", - "name": "Etichetta", + "name": "Descrizione", "add_field": "Aggiungi campo", "label": "Metadati profilo" }, - "bot": "Questo profilo è di un robot", + "bot": "Questo è un robot", "version": { "frontend_version": "Versione interfaccia", "backend_version": "Versione backend", "title": "Versione" }, "reset_avatar": "Azzera icona", - "reset_profile_background": "Azzera sfondo profilo", - "reset_profile_banner": "Azzera stendardo profilo", + "reset_profile_background": "Azzera sfondo", + "reset_profile_banner": "Azzera gonfalone", "reset_avatar_confirm": "Vuoi veramente azzerare l'icona?", - "reset_banner_confirm": "Vuoi veramente azzerare lo stendardo?", + "reset_banner_confirm": "Vuoi veramente azzerare il gonfalone?", "reset_background_confirm": "Vuoi veramente azzerare lo sfondo?", "chatMessageRadius": "Messaggi istantanei", "notification_setting_hide_notification_contents": "Nascondi mittente e contenuti delle notifiche push", "notification_setting_block_from_strangers": "Blocca notifiche da utenti che non segui", "virtual_scrolling": "Velocizza l'elaborazione delle sequenze", "import_mutes_from_a_csv_file": "Importa silenziati da un file CSV", - "mutes_imported": "Silenziati importati! Saranno elaborati a breve.", + "mutes_imported": "Silenziati importati! Elaborazione in corso.", "mute_import_error": "Errore nell'importazione", - "mute_import": "Importa silenziati", - "mute_export_button": "Esporta la tua lista di silenziati in un file CSV", + "mute_import": "Carica silenziati", + "mute_export_button": "Esporta i silenziati in un file CSV", "mute_export": "Esporta silenziati", "hide_wallpaper": "Nascondi sfondo della stanza", - "setting_changed": "Valore personalizzato" + "setting_changed": "Valore personalizzato", + "more_settings": "Altre impostazioni", + "sensitive_by_default": "Tutti i miei messaggi sono scabrosi", + "reply_visibility_self_short": "Vedi solo risposte a te", + "reply_visibility_following_short": "Vedi risposte a messaggi di altri", + "hide_all_muted_posts": "Nascondi messaggi silenziati", + "hide_media_previews": "Nascondi anteprime", + "word_filter": "Parole filtrate", + "save": "Salva modifiche", + "file_export_import": { + "errors": { + "file_slightly_new": "Versione minore diversa, qualcosa potrebbe non combaciare.", + "file_too_old": "Versione troppo vecchia: {fileMajor}. Questa versione dell'interfaccia ({feMajor}) non supporta il file.", + "file_too_new": "Versione troppo recente: {fileMajor}. Questa versione dell'interfaccia ({feMajor}) non supporta il file.", + "invalid_file": "Il file selezionato non è un archivio supportato. Nessuna modifica è stata apportata." + }, + "restore_settings": "Carica impostazioni sul server", + "backup_settings_theme": "Archivia impostazioni e tema localmente", + "backup_settings": "Archivia impostazioni localmente", + "backup_restore": "Archiviazione impostazioni" + } }, "timeline": { "error_fetching": "Errore nell'aggiornamento", - "load_older": "Carica messaggi più vecchi", + "load_older": "Carica messaggi precedenti", "show_new": "Mostra nuovi", "up_to_date": "Aggiornato", - "collapse": "Riduci", + "collapse": "Ripiega", "conversation": "Conversazione", "no_retweet_hint": "Il messaggio è diretto o solo per seguaci e non può essere condiviso", - "repeated": "condiviso", + "repeated": "ha condiviso", "no_statuses": "Nessun messaggio", "no_more_statuses": "Fine dei messaggi", "reload": "Ricarica", - "error": "Errore nel caricare la sequenza: {0}" + "error": "Errore nel caricare la sequenza: {0}", + "socket_broke": "Connessione tempo reale interrotta: codice {0}", + "socket_reconnected": "Connesso in tempo reale" }, "user_card": { "follow": "Segui", - "followees": "Chi stai seguendo", + "followees": "Segue", "followers": "Seguaci", "following": "Seguìto!", "follows_you": "Ti segue!", @@ -454,13 +477,13 @@ "deny": "Nega", "remote_follow": "Segui da remoto", "admin_menu": { - "delete_user_confirmation": "Ne sei completamente sicuro? Quest'azione non può essere annullata.", + "delete_user_confirmation": "Ne sei completamente sicuro? Non potrai tornare indietro.", "delete_user": "Elimina utente", "quarantine": "I messaggi non arriveranno alle altre stanze", "disable_any_subscription": "Rendi utente non seguibile", "disable_remote_subscription": "Blocca i tentativi di seguirlo da altre stanze", "sandbox": "Rendi tutti i messaggi solo per seguaci", - "force_unlisted": "Rendi tutti i messaggi invisibili", + "force_unlisted": "Nascondi tutti i messaggi", "strip_media": "Rimuovi ogni allegato ai messaggi", "force_nsfw": "Oscura tutti i messaggi", "delete_account": "Elimina profilo", @@ -474,7 +497,7 @@ }, "show_repeats": "Mostra condivisioni", "hide_repeats": "Nascondi condivisioni", - "mute_progress": "Zittisco…", + "mute_progress": "Silenzio…", "unmute_progress": "Riabilito…", "unmute": "Riabilita", "block_progress": "Blocco…", @@ -483,7 +506,7 @@ "unsubscribe": "Disdici", "subscribe": "Abbònati", "report": "Segnala", - "mention": "Menzioni", + "mention": "Menziona", "media": "Media", "its_you": "Sei tu!", "hidden": "Nascosto", @@ -493,7 +516,13 @@ "follow_sent": "Richiesta inviata!", "favorites": "Preferiti", "message": "Contatta", - "bot": "Bot" + "bot": "Bot", + "highlight": { + "side": "Nastro a lato", + "striped": "A righe", + "solid": "Un colore", + "disabled": "Nessun risalto" + } }, "chat": { "title": "Chat" @@ -549,21 +578,22 @@ "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" + "unlisted": "Nascosto - Non visibile sulla sequenza pubblica" }, "scope_notice": { "unlisted": "Questo messaggio non sarà visibile sulla sequenza locale né su quella pubblica", "private": "Questo messaggio sarà visibile solo ai tuoi seguaci", "public": "Questo messaggio sarà visibile a tutti" }, - "direct_warning_to_first_only": "Questo messaggio sarà visibile solo agli utenti menzionati all'inizio.", + "direct_warning_to_first_only": "Questo messaggio sarà visibile solo agli utenti menzionati in testa.", "direct_warning_to_all": "Questo messaggio sarà visibile a tutti i menzionati.", "new_status": "Nuovo messaggio", - "empty_status_error": "Non puoi pubblicare messaggi vuoti senza allegati", + "empty_status_error": "Aggiungi del testo o degli allegati", "preview_empty": "Vuoto", "preview": "Anteprima", "media_description_error": "Allegati non caricati, riprova", - "media_description": "Descrizione allegati" + "media_description": "Descrizione allegati", + "post": "Pubblica" }, "registration": { "bio": "Introduzione", @@ -583,13 +613,14 @@ "bio_placeholder": "es.\nCiao, sono Lupo Lucio.\nSono un lupo fantastico che vive nel Fantabosco. Forse mi hai visto alla Melevisione.", "fullname_placeholder": "es. Lupo Lucio", "username_placeholder": "es. mister_wolf", - "new_captcha": "Clicca l'immagine per avere un altro captcha", + "new_captcha": "Clicca il captcha per averne uno nuovo", "captcha": "CAPTCHA", "reason_placeholder": "L'amministratore esamina ciascuna richiesta.\nFornisci il motivo della tua iscrizione.", - "reason": "Motivo dell'iscrizione" + "reason": "Motivo dell'iscrizione", + "register": "Registrati" }, "user_profile": { - "timeline_title": "Sequenza dell'Utente", + "timeline_title": "Sequenza dell'utente", "profile_loading_error": "Spiacente, c'è stato un errore nel caricamento del profilo.", "profile_does_not_exist": "Spiacente, questo profilo non esiste." }, @@ -605,7 +636,7 @@ "replace": "Sostituisci", "is_replaced_by": "→", "keyword_policies": "Regole per parole chiave", - "ftl_removal": "Rimozione dalla sequenza globale" + "ftl_removal": "Rimozione dalla sequenza federale" }, "simple": { "reject": "Rifiuta", @@ -615,8 +646,8 @@ "reject_desc": "Questa stanza rifiuterà i messaggi provenienti dalle seguenti:", "quarantine": "Quarantena", "quarantine_desc": "Questa stanza inoltrerà solo messaggi pubblici alle seguenti:", - "ftl_removal": "Rimozione dalla sequenza globale", - "ftl_removal_desc": "Questa stanza rimuove le seguenti dalla sequenza globale:", + "ftl_removal": "Rimozione dalla sequenza federale", + "ftl_removal_desc": "Questa stanza rimuove le seguenti dalla sequenza federale:", "media_removal": "Rimozione multimedia", "media_removal_desc": "Questa istanza rimuove gli allegati dalle seguenti stanze:", "media_nsfw": "Allegati oscurati d'ufficio", @@ -628,8 +659,8 @@ "staff": "Responsabili" }, "domain_mute_card": { - "mute": "Zittisci", - "mute_progress": "Zittisco…", + "mute": "Silenzia", + "mute_progress": "Silenzio…", "unmute": "Ascolta", "unmute_progress": "Procedo…" }, @@ -705,8 +736,8 @@ "favorites": "Preferiti", "hide_content": "Nascondi contenuti", "show_content": "Mostra contenuti", - "hide_full_subject": "Nascondi intero oggetto", - "show_full_subject": "Mostra intero oggetto", + "hide_full_subject": "Nascondi oggetto intero", + "show_full_subject": "Mostra oggetto intero", "thread_muted_and_words": ", contiene:", "thread_muted": "Discussione silenziata", "copy_link": "Copia collegamento", @@ -714,46 +745,46 @@ "unmute_conversation": "Riabilita conversazione", "mute_conversation": "Silenzia conversazione", "replies_list": "Risposte:", - "reply_to": "Rispondi a", + "reply_to": "In risposta a", "delete_confirm": "Vuoi veramente eliminare questo messaggio?", "unbookmark": "Rimuovi segnalibro", "bookmark": "Aggiungi segnalibro", "status_deleted": "Questo messagio è stato cancellato", - "nsfw": "Pruriginoso", - "external_source": "Vai al sito", + "nsfw": "DISDICEVOLE", + "external_source": "Vai all'origine", "expand": "Espandi" }, "time": { - "years_short": "{0}a", - "year_short": "{0}a", + "years_short": "{0} a", + "year_short": "{0} a", "years": "{0} anni", "year": "{0} anno", - "weeks_short": "{0}set", - "week_short": "{0}set", - "seconds_short": "{0}sec", - "second_short": "{0}sec", + "weeks_short": "{0} stm", + "week_short": "{0} stm", + "seconds_short": "{0} sec", + "second_short": "{0} sec", "weeks": "{0} settimane", "week": "{0} settimana", "seconds": "{0} secondi", "second": "{0} secondo", - "now_short": "ora", + "now_short": "adesso", "now": "adesso", - "months_short": "{0}me", - "month_short": "{0}me", + "months_short": "{0} ms", + "month_short": "{0} ms", "months": "{0} mesi", "month": "{0} mese", - "minutes_short": "{0}min", - "minute_short": "{0}min", + "minutes_short": "{0} min", + "minute_short": "{0} min", "minutes": "{0} minuti", "minute": "{0} minuto", "in_past": "{0} fa", "in_future": "fra {0}", - "hours_short": "{0}h", - "days_short": "{0}g", - "hour_short": "{0}h", + "hours_short": "{0} h", + "days_short": "{0} g", + "hour_short": "{0} h", "hours": "{0} ore", "hour": "{0} ora", - "day_short": "{0}g", + "day_short": "{0} g", "days": "{0} giorni", "day": "{0} giorno" }, @@ -767,7 +798,7 @@ "add_comment_description": "La segnalazione sarà inviata ai moderatori della tua stanza. Puoi motivarla qui sotto:" }, "password_reset": { - "password_reset_required_but_mailer_is_disabled": "Devi reimpostare la tua password, ma non puoi farlo. Contatta il tuo amministratore.", + "password_reset_required_but_mailer_is_disabled": "Devi reimpostare la tua password, ma non puoi farlo. Contatta l'amministratore.", "password_reset_required": "Devi reimpostare la tua password per poter continuare.", "password_reset_disabled": "Non puoi azzerare la tua password. Contatta il tuo amministratore.", "too_many_requests": "Hai raggiunto il numero massimo di tentativi, riprova più tardi.", @@ -808,7 +839,7 @@ "add_reaction": "Reagisci", "favorite": "Gradisci", "reply": "Rispondi", - "repeat": "Ripeti", + "repeat": "Condividi", "media_upload": "Carica allegati" }, "display_date": { diff --git a/src/i18n/ja_pedantic.json b/src/i18n/ja_pedantic.json @@ -86,7 +86,7 @@ "mentions": "通知", "interactions": "インタラクション", "dms": "ダイレクトメッセージ", - "public_tl": "パブリックタイムライン", + "public_tl": "公開タイムライン", "timeline": "タイムライン", "twkn": "すべてのネットワーク", "user_search": "ユーザーを探す", @@ -96,7 +96,8 @@ "administration": "管理", "bookmarks": "ブックマーク", "timelines": "タイムライン", - "chats": "チャット" + "chats": "チャット", + "home_timeline": "ホームタイムライン" }, "notifications": { "broken_favorite": "ステータスが見つかりません。探しています…", @@ -173,14 +174,15 @@ "scope": { "direct": "ダイレクト: メンションされたユーザーのみに届きます", "private": "フォロワー限定: フォロワーのみに届きます", - "public": "パブリック: パブリックタイムラインに届きます", - "unlisted": "アンリステッド: パブリックタイムラインに届きません" + "public": "パブリック: 公開タイムラインに届きます", + "unlisted": "アンリステッド: 公開タイムラインに届きません" }, "media_description_error": "メディアのアップロードに失敗しました。もう一度お試しください", "empty_status_error": "投稿内容を入力してください", "preview_empty": "何もありません", "preview": "プレビュー", - "media_description": "メディアの説明" + "media_description": "メディアの説明", + "post": "投稿" }, "registration": { "bio": "プロフィール", @@ -203,7 +205,8 @@ "password_confirmation_match": "パスワードが違います" }, "reason_placeholder": "このインスタンスは、新規登録を手動で受け付けています。\n登録したい理由を、インスタンスの管理者に教えてください。", - "reason": "登録するための目的" + "reason": "登録するための目的", + "register": "登録" }, "selectable_list": { "select_all": "すべて選択" @@ -323,8 +326,8 @@ "hide_followers_description": "フォロワーを見せない", "hide_follows_count_description": "フォローしている人の数を見せない", "hide_followers_count_description": "フォロワーの数を見せない", - "show_admin_badge": "管理者のバッジを見せる", - "show_moderator_badge": "モデレーターのバッジを見せる", + "show_admin_badge": "\"管理者\"のバッジを見せる", + "show_moderator_badge": "\"モデレーター\"のバッジを見せる", "nsfw_clickthrough": "NSFWなファイルを隠す", "oauth_tokens": "OAuthトークン", "token": "トークン", @@ -334,8 +337,8 @@ "panelRadius": "パネル", "pause_on_unfocused": "タブにフォーカスがないときストリーミングを止める", "presets": "プリセット", - "profile_background": "プロフィールのバックグラウンド", - "profile_banner": "プロフィールバナー", + "profile_background": "プロフィールの背景", + "profile_banner": "プロフィールのバナー", "profile_tab": "プロフィール", "radii_help": "インターフェースの丸さを設定する", "replies_in_timeline": "タイムラインのリプライ", @@ -413,8 +416,8 @@ "contrast": { "hint": "コントラストは {ratio} です。{level}。({context})", "level": { - "aa": "AAレベルガイドライン (ミニマル) を満たします", - "aaa": "AAAレベルガイドライン (レコメンデッド) を満たします", + "aa": "AAレベルガイドライン (最低限) を満たします", + "aaa": "AAAレベルガイドライン (推奨) を満たします", "bad": "ガイドラインを満たしません" }, "context": { @@ -573,7 +576,24 @@ "mute_export": "ミュートのエクスポート", "allow_following_move": "フォロー中のアカウントが引っ越したとき、自動フォローを許可する", "setting_changed": "規定の設定と異なっています", - "greentext": "引用を緑色で表示" + "greentext": "引用を緑色で表示", + "sensitive_by_default": "はじめから投稿をセンシティブとして設定", + "more_settings": "その他の設定", + "reply_visibility_self_short": "自分宛のリプライを見る", + "reply_visibility_following_short": "フォローしている人に宛てられたリプライを見る", + "hide_all_muted_posts": "ミュートした投稿を隠す", + "hide_media_previews": "メディアのプレビューを隠す", + "word_filter": "単語フィルタ", + "file_export_import": { + "errors": { + "invalid_file": "これはPleromaの設定をバックアップしたファイルではありません。" + }, + "restore_settings": "設定をファイルから復元する", + "backup_settings_theme": "テーマを含む設定をファイルにバックアップする", + "backup_settings": "設定をファイルにバックアップする", + "backup_restore": "設定をバックアップ" + }, + "save": "変更を保存" }, "time": { "day": "{0}日", @@ -709,7 +729,13 @@ "hide_repeats": "リピートを隠す", "message": "メッセージ", "hidden": "隠す", - "bot": "bot" + "bot": "bot", + "highlight": { + "solid": "背景を単色にする", + "striped": "背景を縞模様にする", + "side": "端に線を付ける", + "disabled": "強調しない" + } }, "user_profile": { "timeline_title": "ユーザータイムライン", @@ -783,8 +809,8 @@ "media_nsfw": "メディアを閲覧注意に設定", "media_removal_desc": "このインスタンスでは、以下のインスタンスからの投稿に対して、メディアを除去します:", "media_removal": "メディア除去", - "ftl_removal": "「接続しているすべてのネットワーク」タイムラインから除外", - "ftl_removal_desc": "このインスタンスでは、以下のインスタンスを「接続しているすべてのネットワーク」タイムラインから除外します:", + "ftl_removal": "「既知のネットワーク」タイムラインから除外", + "ftl_removal_desc": "このインスタンスでは、以下のインスタンスを「既知のネットワーク」タイムラインから除外します:", "quarantine_desc": "このインスタンスでは、以下のインスタンスに対して公開投稿のみを送信します:", "quarantine": "検疫", "reject_desc": "このインスタンスでは、以下のインスタンスからのメッセージを受け付けません:", diff --git a/src/i18n/ko.json b/src/i18n/ko.json @@ -77,7 +77,8 @@ "search": "검색", "bookmarks": "북마크", "interactions": "대화", - "administration": "관리" + "administration": "관리", + "home_timeline": "홈 타임라인" }, "notifications": { "broken_favorite": "알 수 없는 게시물입니다, 검색합니다…", @@ -90,7 +91,8 @@ "no_more_notifications": "알림이 없습니다", "migrated_to": "이사했습니다", "reacted_with": "{0} 로 반응했습니다", - "error": "알림 불러오기 실패: {0}" + "error": "알림 불러오기 실패: {0}", + "follow_request": "당신에게 팔로우 신청" }, "post_status": { "new_status": "새 게시물 게시", @@ -402,10 +404,11 @@ }, "mutes_and_blocks": "침묵과 차단", "chatMessageRadius": "챗 메시지", - "change_email": "전자메일 주소 바꾸기", + "change_email": "메일주소 바꾸기", "changed_email": "메일주소가 갱신되었습니다!", "bot": "이 계정은 bot입니다", - "mutes_tab": "침묵" + "mutes_tab": "침묵", + "app_name": "앱 이름" }, "timeline": { "collapse": "접기", @@ -468,7 +471,8 @@ }, "interactions": { "follows": "새 팔로워", - "favs_repeats": "반복과 즐겨찾기" + "favs_repeats": "반복과 즐겨찾기", + "moves": "계정 통합" }, "emoji": { "load_all": "전체 {emojiAmount} 이모지 불러오기", @@ -523,8 +527,8 @@ "media_nsfw": "매체를 민감함으로 설정", "media_removal_desc": "이 인스턴스에서는 아래의 인스턴스로부터 보내온 투고에 붙혀 있는 매체는 제거됩니다:", "media_removal": "매체 제거", - "ftl_removal_desc": "이 인스턴스에서 아래의 인스턴스들은 \"알려진 모든 네트워크\" 타임라인에서 제외됩니다:", - "ftl_removal": "\"알려진 모든 네트워크\" 타임라인에서 제외", + "ftl_removal_desc": "이 인스턴스에서 아래의 인스턴스들은 \"알려진 네트워크\" 타임라인에서 제외됩니다:", + "ftl_removal": "\"알려진 네트워크\" 타임라인에서 제외", "quarantine_desc": "이 인스턴스는 아래의 인스턴스에게 공개투고만을 보냅니다:", "quarantine": "검역", "reject_desc": "이 인스턴스에서는 아래의 인스턴스로부터 보내온 투고를 받아들이지 않습니다:", @@ -581,6 +585,10 @@ "day": "{0} 일" }, "remote_user_resolver": { - "error": "찾을 수 없습니다." + "error": "찾을 수 없습니다.", + "searching_for": "검색중" + }, + "selectable_list": { + "select_all": "모두 선택" } } diff --git a/src/i18n/nb.json b/src/i18n/nb.json @@ -41,8 +41,8 @@ }, "importer": { "submit": "Send", - "success": "Importering fullført", - "error": "Det oppsto en feil under importering av denne filen" + "success": "Importering fullført.", + "error": "Det oppsto en feil under importering av denne filen." }, "login": { "login": "Logg inn", @@ -85,7 +85,7 @@ "bookmarks": "Bokmerker" }, "notifications": { - "broken_favorite": "Ukjent status, leter etter den...", + "broken_favorite": "Ukjent status, leter etter den…", "favorited_you": "likte din status", "followed_you": "fulgte deg", "load_older": "Last eldre varsler", @@ -447,7 +447,8 @@ "title": "Versjon", "backend_version": "Backend Versjon", "frontend_version": "Frontend Versjon" - } + }, + "hide_wallpaper": "Skjul instansens bakgrunnsbilde" }, "time": { "day": "{0} dag", @@ -602,5 +603,22 @@ "person_talking": "{count} person snakker om dette", "people_talking": "{count} personer snakker om dette", "no_results": "Ingen resultater" + }, + "about": { + "mrf": { + "simple": { + "quarantine": "Karantene", + "reject_desc": "Denne instansen vil ikke godta meldinger fra følgende instanser:", + "reject": "Avvis", + "accept_desc": "Denne instansen godtar kun meldinger fra følgende instanser:", + "accept": "Aksepter" + }, + "keyword": { + "is_replaced_by": "→", + "replace": "Erstatt", + "reject": "Avvis", + "ftl_removal": "Fjerning fra \"Det hele kjente nettverket\" Tidslinjen" + } + } } } diff --git a/src/i18n/nl.json b/src/i18n/nl.json @@ -5,11 +5,13 @@ "features_panel": { "chat": "Chat", "gopher": "Gopher", - "media_proxy": "Media proxy", + "media_proxy": "Mediaproxy", "scope_options": "Zichtbaarheidsopties", - "text_limit": "Tekst limiet", + "text_limit": "Tekstlimiet", "title": "Kenmerken", - "who_to_follow": "Wie te volgen" + "who_to_follow": "Wie te volgen", + "upload_limit": "Upload limiet", + "pleroma_chat_messages": "Pleroma Chat" }, "finder": { "error_fetching_user": "Fout tijdens ophalen gebruiker", @@ -17,11 +19,11 @@ }, "general": { "apply": "Toepassen", - "submit": "Verzend", + "submit": "Verzenden", "more": "Meer", "optional": "optioneel", - "show_more": "Bekijk meer", - "show_less": "Bekijk minder", + "show_more": "Meer tonen", + "show_less": "Minder tonen", "dismiss": "Opheffen", "cancel": "Annuleren", "disable": "Uitschakelen", @@ -29,28 +31,32 @@ "confirm": "Bevestigen", "verify": "Verifiëren", "generic_error": "Er is een fout opgetreden", - "peek": "Spiek", + "peek": "Spieken", "close": "Sluiten", "retry": "Opnieuw proberen", "error_retry": "Probeer het opnieuw", - "loading": "Laden…" + "loading": "Laden…", + "role": { + "moderator": "Moderator", + "admin": "Beheerder" + } }, "login": { - "login": "Log in", - "description": "Log in met OAuth", + "login": "Inloggen", + "description": "Inloggen met OAuth", "logout": "Uitloggen", "password": "Wachtwoord", - "placeholder": "bijv. lain", + "placeholder": "bijv. barbapapa", "register": "Registreren", "username": "Gebruikersnaam", "hint": "Log in om deel te nemen aan de discussie", - "authentication_code": "Authenticatie code", + "authentication_code": "Authenticatiecode", "enter_recovery_code": "Voer een herstelcode in", - "enter_two_factor_code": "Voer een twee-factor code in", + "enter_two_factor_code": "Voer een twee-factorcode in", "recovery_code": "Herstelcode", "heading": { - "totp": "Twee-factor authenticatie", - "recovery": "Twee-factor herstelling" + "totp": "Twee-factorauthenticatie", + "recovery": "Twee-factorherstelling" } }, "nav": { @@ -59,35 +65,40 @@ "chat": "Lokale Chat", "friend_requests": "Volgverzoeken", "mentions": "Vermeldingen", - "dms": "Directe Berichten", - "public_tl": "Publieke Tijdlijn", + "dms": "Privéberichten", + "public_tl": "Openbare tijdlijn", "timeline": "Tijdlijn", - "twkn": "Het Geheel Bekende Netwerk", + "twkn": "Bekende Netwerk", "user_search": "Gebruiker Zoeken", "who_to_follow": "Wie te volgen", "preferences": "Voorkeuren", - "administration": "Administratie", + "administration": "Beheer", "search": "Zoeken", - "interactions": "Interacties" + "interactions": "Interacties", + "chats": "Chats", + "home_timeline": "Thuis tijdlijn", + "timelines": "Tijdlijnen", + "bookmarks": "Bladwijzers" }, "notifications": { "broken_favorite": "Onbekende status, aan het zoeken…", "favorited_you": "vond je status leuk", "followed_you": "volgt jou", - "load_older": "Laad oudere meldingen", + "load_older": "Oudere meldingen laden", "notifications": "Meldingen", "read": "Gelezen!", - "repeated_you": "Herhaalde je status", + "repeated_you": "herhaalde je status", "no_more_notifications": "Geen meldingen meer", "migrated_to": "is gemigreerd naar", "follow_request": "wil je volgen", - "reacted_with": "reageerde met {0}" + "reacted_with": "reageerde met {0}", + "error": "Fout bij ophalen van meldingen: {0}" }, "post_status": { "new_status": "Nieuwe status plaatsen", - "account_not_locked_warning": "Je account is niet {0}. Iedereen kan je volgen om je alleen-volgers berichten te lezen.", + "account_not_locked_warning": "Je account is niet {0}. Iedereen kan je volgen om je alleen-volgers-berichten te lezen.", "account_not_locked_warning_link": "gesloten", - "attachments_sensitive": "Markeer bijlagen als gevoelig", + "attachments_sensitive": "Bijlagen als gevoelig markeren", "content_type": { "text/plain": "Platte tekst", "text/html": "HTML", @@ -99,26 +110,32 @@ "direct_warning": "Deze post zal enkel zichtbaar zijn voor de personen die genoemd zijn.", "posting": "Plaatsen", "scope": { - "direct": "Direct - Post enkel naar vermelde gebruikers", - "private": "Enkel volgers - Post enkel naar volgers", - "public": "Publiek - Post op publieke tijdlijnen", - "unlisted": "Niet Vermelden - Niet tonen op publieke tijdlijnen" + "direct": "Privé - bericht enkel naar vermelde gebruikers sturen", + "private": "Enkel volgers - bericht enkel naar volgers sturen", + "public": "Openbaar - bericht op openbare tijdlijnen plaatsen", + "unlisted": "Niet vermelden - niet tonen op openbare tijdlijnen" }, "direct_warning_to_all": "Dit bericht zal zichtbaar zijn voor alle vermelde gebruikers.", "direct_warning_to_first_only": "Dit bericht zal alleen zichtbaar zijn voor de vermelde gebruikers aan het begin van het bericht.", "scope_notice": { "public": "Dit bericht zal voor iedereen zichtbaar zijn", - "unlisted": "Dit bericht zal niet zichtbaar zijn in de Publieke Tijdlijn en Het Geheel Bekende Netwerk", + "unlisted": "Dit bericht zal niet zichtbaar zijn in de Openbare Tijdlijn en Het Geheel Bekende Netwerk", "private": "Dit bericht zal voor alleen je volgers zichtbaar zijn" - } + }, + "post": "Bericht", + "empty_status_error": "Kan geen lege status zonder bijlagen plaatsen", + "preview_empty": "Leeg", + "preview": "Voorbeeld", + "media_description": "Mediaomschrijving", + "media_description_error": "Kon media niet ophalen, probeer het opnieuw" }, "registration": { "bio": "Bio", - "email": "Email", - "fullname": "Weergave naam", + "email": "E-mail", + "fullname": "Weergavenaam", "password_confirm": "Wachtwoord bevestiging", "registration": "Registratie", - "token": "Uitnodigings-token", + "token": "Uitnodigingstoken", "captcha": "CAPTCHA", "new_captcha": "Klik op de afbeelding voor een nieuwe captcha", "validations": { @@ -131,13 +148,16 @@ }, "username_placeholder": "bijv. lain", "fullname_placeholder": "bijv. Lain Iwakura", - "bio_placeholder": "bijv.\nHallo, ik ben Lain.\nIk ben een anime meisje woonachtig in een buitenwijk in Japan. Je kent me misschien van the Wired." + "bio_placeholder": "bijv.\nHallo, ik ben Lain.\nIk ben een animemeisje woonachtig in een buitenwijk in Japan. Je kent me misschien van the Wired.", + "reason_placeholder": "Deze instantie keurt registraties handmatig goed.\nLaat de beheerder weten waarom je wilt registreren.", + "reason": "Reden voor registratie", + "register": "Registreren" }, "settings": { "attachmentRadius": "Bijlages", "attachments": "Bijlages", "avatar": "Avatar", - "avatarAltRadius": "Avatars (Meldingen)", + "avatarAltRadius": "Avatars (meldingen)", "avatarRadius": "Avatars", "background": "Achtergrond", "bio": "Bio", @@ -146,7 +166,7 @@ "cGreen": "Groen (Herhalen)", "cOrange": "Oranje (Favoriet)", "cRed": "Rood (Annuleren)", - "change_password": "Wachtwoord Wijzigen", + "change_password": "Wachtwoord wijzigen", "change_password_error": "Er is een fout opgetreden bij het wijzigen van je wachtwoord.", "changed_password": "Wachtwoord succesvol gewijzigd!", "collapse_subject": "Klap berichten met een onderwerp in", @@ -155,30 +175,30 @@ "current_avatar": "Je huidige avatar", "current_password": "Huidig wachtwoord", "current_profile_banner": "Je huidige profiel banner", - "data_import_export_tab": "Data Import / Export", + "data_import_export_tab": "Data-import / export", "default_vis": "Standaard zichtbaarheidsbereik", - "delete_account": "Account Verwijderen", + "delete_account": "Account verwijderen", "delete_account_description": "Permanent je gegevens verwijderen en account deactiveren.", "delete_account_error": "Er is een fout opgetreden bij het verwijderen van je account. Indien dit probleem zich voor blijft doen, neem dan contact op met de beheerder van deze instantie.", "delete_account_instructions": "Voer je wachtwoord in het onderstaande invoerveld in om het verwijderen van je account te bevestigen.", - "export_theme": "Preset opslaan", + "export_theme": "Voorinstelling opslaan", "filtering": "Filtering", - "filtering_explanation": "Alle statussen die deze woorden bevatten worden genegeerd, één filter per lijn", + "filtering_explanation": "Alle statussen die deze woorden bevatten worden genegeerd, één filter per regel", "follow_export": "Volgers exporteren", - "follow_export_button": "Exporteer je volgers naar een csv bestand", + "follow_export_button": "Exporteer je volgers naar een csv-bestand", "follow_export_processing": "Aan het verwerken, binnen enkele ogenblikken wordt je gevraagd je bestand te downloaden", "follow_import": "Volgers importeren", "follow_import_error": "Fout bij importeren volgers", "follows_imported": "Volgers geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", "foreground": "Voorgrond", "general": "Algemeen", - "hide_attachments_in_convo": "Verberg bijlages in conversaties", - "hide_attachments_in_tl": "Verberg bijlages in de tijdlijn", - "hide_isp": "Verberg instantie-specifiek paneel", + "hide_attachments_in_convo": "Bijlagen in conversaties verbergen", + "hide_attachments_in_tl": "Bijlagen in tijdlijn verbergen", + "hide_isp": "Instantie-specifiek paneel verbergen", "preload_images": "Afbeeldingen vooraf laden", - "hide_post_stats": "Verberg bericht statistieken (bijv. het aantal favorieten)", - "hide_user_stats": "Verberg bericht statistieken (bijv. het aantal volgers)", - "import_followers_from_a_csv_file": "Importeer volgers uit een csv bestand", + "hide_post_stats": "Bericht statistieken verbergen (bijv. het aantal favorieten)", + "hide_user_stats": "Gebruikers-statistieken verbergen (bijv. het aantal volgers)", + "import_followers_from_a_csv_file": "Gevolgden uit een csv bestand importeren", "import_theme": "Preset laden", "inputRadius": "Invoervelden", "checkboxRadius": "Checkboxen", @@ -186,35 +206,35 @@ "instance_default_simple": "(standaard)", "interface": "Interface", "interfaceLanguage": "Interface taal", - "invalid_theme_imported": "Het geselecteerde bestand is geen door Pleroma ondersteund thema. Er zijn geen aanpassingen gedaan.", + "invalid_theme_imported": "Het geselecteerde bestand is niet een door Pleroma ondersteund thema. Er zijn geen aanpassingen gedaan.", "limited_availability": "Niet beschikbaar in je browser", "links": "Links", - "lock_account_description": "Laat volgers enkel toe na expliciete toestemming", - "loop_video": "Herhaal video's", - "loop_video_silent_only": "Herhaal enkel video's zonder geluid (bijv. Mastodon's \"gifs\")", + "lock_account_description": "Volgers enkel na expliciete toestemming toelaten", + "loop_video": "Video's herhalen", + "loop_video_silent_only": "Enkel video's zonder geluid herhalen (bijv. Mastodon's \"gifs\")", "name": "Naam", - "name_bio": "Naam & Bio", + "name_bio": "Naam & bio", "new_password": "Nieuw wachtwoord", "notification_visibility": "Type meldingen die getoond worden", - "notification_visibility_follows": "Volgingen", - "notification_visibility_likes": "Vind-ik-leuks", + "notification_visibility_follows": "Gevolgden", + "notification_visibility_likes": "Favorieten", "notification_visibility_mentions": "Vermeldingen", "notification_visibility_repeats": "Herhalingen", "no_rich_text_description": "Verwijder rich text formattering van alle berichten", "hide_network_description": "Toon niet wie mij volgt en wie ik volg.", - "nsfw_clickthrough": "Doorklikbaar verbergen van gevoelige bijlages inschakelen", + "nsfw_clickthrough": "Doorklikbaar verbergen van gevoelige bijlages en link voorbeelden inschakelen", "oauth_tokens": "OAuth-tokens", "token": "Token", - "refresh_token": "Token Vernieuwen", + "refresh_token": "Token vernieuwen", "valid_until": "Geldig tot", "revoke_token": "Intrekken", "panelRadius": "Panelen", "pause_on_unfocused": "Streamen pauzeren wanneer de tab niet in focus is", "presets": "Presets", - "profile_background": "Profiel Achtergrond", - "profile_banner": "Profiel Banner", + "profile_background": "Profiel achtergrond", + "profile_banner": "Profiel banner", "profile_tab": "Profiel", - "radii_help": "Stel afronding van hoeken in de interface in (in pixels)", + "radii_help": "Afronding van hoeken in de interface instellen (in pixels)", "replies_in_timeline": "Antwoorden in tijdlijn", "reply_visibility_all": "Alle antwoorden tonen", "reply_visibility_following": "Enkel antwoorden tonen die aan mij of gevolgde gebruikers gericht zijn", @@ -222,13 +242,13 @@ "saving_err": "Fout tijdens opslaan van instellingen", "saving_ok": "Instellingen opgeslagen", "security_tab": "Beveiliging", - "scope_copy": "Neem bereik over bij beantwoorden (Directe Berichten blijven altijd Direct)", + "scope_copy": "Bereik overnemen bij beantwoorden (Privéberichten blijven altijd privé)", "set_new_avatar": "Nieuwe avatar instellen", "set_new_profile_background": "Nieuwe profiel achtergrond instellen", "set_new_profile_banner": "Nieuwe profiel banner instellen", "settings": "Instellingen", "subject_input_always_show": "Altijd onderwerpveld tonen", - "subject_line_behavior": "Onderwerp kopiëren bij antwoorden", + "subject_line_behavior": "Onderwerp kopiëren bij beantwoorden", "subject_line_email": "Zoals email: \"re: onderwerp\"", "subject_line_mastodon": "Zoals mastodon: kopieer zoals het is", "subject_line_noop": "Niet kopiëren", @@ -236,7 +256,7 @@ "streaming": "Automatisch streamen van nieuwe berichten inschakelen wanneer tot boven gescrold is", "text": "Tekst", "theme": "Thema", - "theme_help": "Gebruik hex color codes (#rrggbb) om je kleurschema te wijzigen.", + "theme_help": "Hex kleur codes (#rrggbb) gebruiken om je kleur thema te wijzigen.", "theme_help_v2_1": "Je kan ook de kleur en transparantie van bepaalde componenten overschrijven door de checkbox aan te vinken, gebruik de \"Alles wissen\" knop om alle overschrijvingen te annuleren.", "theme_help_v2_2": "Iconen onder sommige onderdelen zijn achtergrond/tekst contrast indicatoren, zweef er over voor gedetailleerde info. Hou er rekening mee dat bij doorzichtigheid de ergst mogelijke situatie wordt weer gegeven.", "tooltipRadius": "Tooltips/alarmen", @@ -323,7 +343,13 @@ "popover": "Tooltips, menu's, popovers", "post": "Berichten / Gebruiker bios", "alert_neutral": "Neutraal", - "alert_warning": "Waarschuwing" + "alert_warning": "Waarschuwing", + "chat": { + "border": "Rand", + "outgoing": "Uitgaand", + "incoming": "Binnenkomend" + }, + "wallpaper": "Achtergrond" }, "radii": { "_tab_label": "Rondheid" @@ -399,50 +425,50 @@ "setup_otp": "OTP instellen", "wait_pre_setup_otp": "OTP voorinstellen", "confirm_and_enable": "Bevestig en schakel OTP in", - "title": "Twee-factor Authenticatie", + "title": "Twee-factorauthenticatie", "generate_new_recovery_codes": "Genereer nieuwe herstelcodes", "recovery_codes": "Herstelcodes.", - "waiting_a_recovery_codes": "Backup codes ontvangen…", - "authentication_methods": "Authenticatie methodes", + "waiting_a_recovery_codes": "Back-upcodes ontvangen…", + "authentication_methods": "Authenticatiemethodes", "scan": { "title": "Scannen", - "desc": "Scan de QR code of voer een sleutel in met je twee-factor applicatie:", + "desc": "Scan de QR-code of voer een sleutel in met je twee-factorapplicatie:", "secret_code": "Sleutel" }, "verify": { - "desc": "Voer de code van je twee-factor applicatie in om twee-factor authenticatie in te schakelen:" + "desc": "Voer de code van je twee-factorapplicatie in om twee-factorauthenticatie in te schakelen:" }, - "warning_of_generate_new_codes": "Wanneer je nieuwe herstelcodes genereert, zullen je oude code niet langer werken.", - "recovery_codes_warning": "Schrijf de codes op of sla ze op een veilige locatie op - anders kun je ze niet meer inzien. Als je toegang tot je 2FA app en herstelcodes verliest, zal je buitengesloten zijn uit je account." + "warning_of_generate_new_codes": "Wanneer je nieuwe herstelcodes genereert, zullen je oude codes niet langer werken.", + "recovery_codes_warning": "Schrijf de codes op of sla ze op een veilige locatie op - anders kun je ze niet meer inzien. Als je toegang tot je 2FA-app en herstelcodes verliest, zal je buitengesloten zijn van je account." }, "allow_following_move": "Automatisch volgen toestaan wanneer een gevolgd account migreert", "block_export": "Blokkades exporteren", "block_import": "Blokkades importeren", "blocks_imported": "Blokkades geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", "blocks_tab": "Blokkades", - "change_email": "Email wijzigen", - "change_email_error": "Er is een fout opgetreden tijdens het wijzigen van je email.", - "changed_email": "Email succesvol gewijzigd!", + "change_email": "E-mail wijzigen", + "change_email_error": "Er is een fout opgetreden tijdens het wijzigen van je e-mailadres.", + "changed_email": "E-mailadres succesvol gewijzigd!", "domain_mutes": "Domeinen", - "avatar_size_instruction": "De aangeraden minimale afmeting voor avatar afbeeldingen is 150x150 pixels.", + "avatar_size_instruction": "De aangeraden minimale afmeting voor avatar-afbeeldingen is 150x150 pixels.", "pad_emoji": "Vul emoji aan met spaties wanneer deze met de picker ingevoegd worden", - "emoji_reactions_on_timeline": "Toon emoji reacties op de tijdlijn", + "emoji_reactions_on_timeline": "Toon emoji-reacties op de tijdlijn", "accent": "Accent", - "hide_muted_posts": "Verberg berichten van genegeerde gebruikers", + "hide_muted_posts": "Berichten van genegeerde gebruikers verbergen", "max_thumbnails": "Maximaal aantal miniaturen per bericht", - "use_one_click_nsfw": "Open gevoelige bijlagen met slechts één klik", + "use_one_click_nsfw": "Gevoelige bijlagen met slechts één klik openen", "hide_filtered_statuses": "Gefilterde statussen verbergen", - "import_blocks_from_a_csv_file": "Importeer blokkades van een csv bestand", - "mutes_tab": "Negeringen", - "play_videos_in_modal": "Speel video's af in een popup frame", - "new_email": "Nieuwe Email", + "import_blocks_from_a_csv_file": "Blokkades van een csv bestand importeren", + "mutes_tab": "Genegeerden", + "play_videos_in_modal": "Video's in een popup frame afspelen", + "new_email": "Nieuwe e-mail", "notification_visibility_emoji_reactions": "Reacties", "no_blocks": "Geen blokkades", - "no_mutes": "Geen negeringen", + "no_mutes": "Geen genegeerden", "hide_followers_description": "Niet tonen wie mij volgt", "hide_followers_count_description": "Niet mijn volgers aantal tonen", "hide_follows_count_description": "Niet mijn gevolgde aantal tonen", - "show_admin_badge": "Beheerders badge tonen in mijn profiel", + "show_admin_badge": "\"Beheerder\" badge in mijn profiel tonen", "autohide_floating_post_button": "Nieuw Bericht knop automatisch verbergen (mobiel)", "search_user_to_block": "Zoek wie je wilt blokkeren", "search_user_to_mute": "Zoek wie je wilt negeren", @@ -452,31 +478,69 @@ "useStreamingApi": "Berichten en meldingen in real-time ontvangen", "useStreamingApiWarning": "(Afgeraden, experimenteel, kan berichten overslaan)", "type_domains_to_mute": "Zoek domeinen om te negeren", - "upload_a_photo": "Upload een foto", + "upload_a_photo": "Foto uploaden", "fun": "Plezier", "greentext": "Meme pijlen", - "block_export_button": "Exporteer je geblokkeerde gebruikers naar een csv bestand", + "block_export_button": "Exporteer je geblokkeerde gebruikers naar een csv-bestand", "block_import_error": "Fout bij importeren blokkades", "discoverable": "Sta toe dat dit account ontdekt kan worden in zoekresultaten en andere diensten", - "use_contain_fit": "Snij bijlage in miniaturen niet bij", + "use_contain_fit": "Bijlage in miniaturen niet bijsnijden", "notification_visibility_moves": "Gebruiker Migraties", "hide_follows_description": "Niet tonen wie ik volg", - "show_moderator_badge": "Moderators badge tonen in mijn profiel", + "show_moderator_badge": "\"Moderator\" badge in mijn profiel tonen", "notification_setting_filters": "Filters", "notification_blocks": "Door een gebruiker te blokkeren, ontvang je geen meldingen meer van de gebruiker en wordt je abonnement op de gebruiker opgeheven.", "version": { - "frontend_version": "Frontend Versie", - "backend_version": "Backend Versie", + "frontend_version": "Frontend versie", + "backend_version": "Backend versie", "title": "Versie" }, "mutes_and_blocks": "Negeringen en Blokkades", "profile_fields": { "value": "Inhoud", "name": "Label", - "add_field": "Veld Toevoegen", + "add_field": "Veld toevoegen", "label": "Profiel metadata" }, - "bot": "Dit is een bot account" + "bot": "Dit is een bot-account", + "setting_changed": "Instelling verschilt van standaard waarde", + "save": "Wijzigingen opslaan", + "hide_media_previews": "Media voorbeelden verbergen", + "word_filter": "Woord filter", + "chatMessageRadius": "Chatbericht", + "mute_export": "Genegeerden export", + "mute_export_button": "Exporteer je genegeerden naar een csv-bestand", + "mute_import_error": "Fout tijdens het importeren van genegeerden", + "mute_import": "Genegeerden import", + "mutes_imported": "Genegeerden geïmporteerd! Het kan even duren voordat deze verwerkt zijn.", + "more_settings": "Meer instellingen", + "notification_setting_hide_notification_contents": "Afzender en inhoud van push meldingen verbergen", + "notification_setting_block_from_strangers": "Meldingen van gebruikers die je niet volgt blokkeren", + "virtual_scrolling": "Tijdlijn rendering optimaliseren", + "sensitive_by_default": "Berichten standaard als gevoelig markeren", + "reset_avatar_confirm": "Wil je echt de avatar herstellen?", + "reset_banner_confirm": "Wil je echt de banner herstellen?", + "reset_background_confirm": "Wil je echt de achtergrond herstellen?", + "reset_profile_banner": "Profiel banner herstellen", + "reset_profile_background": "Profiel achtergrond herstellen", + "reset_avatar": "Avatar herstellen", + "reply_visibility_self_short": "Alleen antwoorden aan mijzelf tonen", + "reply_visibility_following_short": "Antwoorden naar mijn gevolgden tonen", + "file_export_import": { + "errors": { + "file_slightly_new": "Bestand minor versie is verschillend, sommige instellingen kunnen mogelijk niet worden geladen", + "file_too_old": "Incompatibele hoofdversie: {fileMajor}, bestandsversie is te oud en wordt niet ondersteund (minimale versie {feMajor})", + "file_too_new": "Incompatibele hoofdversie: {fileMajor}, deze PleromaFE (instellingen versie {feMajor}) is te oud om deze te ondersteunen", + "invalid_file": "Het geselecteerde bestand is niet een door Pleroma ondersteunde instellingen back-up. Er zijn geen wijzigingen gemaakt." + }, + "restore_settings": "Instellingen uit bestand herstellen", + "backup_settings_theme": "Instellingen en thema naar bestand back-uppen", + "backup_settings": "Instellingen naar bestand back-uppen", + "backup_restore": "Instellingen backup" + }, + "hide_wallpaper": "Instantie achtergrond verbergen", + "hide_all_muted_posts": "Genegeerde berichten verbergen", + "import_mutes_from_a_csv_file": "Importeer genegeerden van een csv bestand" }, "timeline": { "collapse": "Inklappen", @@ -488,7 +552,11 @@ "show_new": "Nieuwe tonen", "up_to_date": "Up-to-date", "no_statuses": "Geen statussen", - "no_more_statuses": "Geen statussen meer" + "no_more_statuses": "Geen statussen meer", + "socket_broke": "Realtime verbinding verloren: CloseEvent code {0}", + "socket_reconnected": "Realtime verbinding opgezet", + "reload": "Verversen", + "error": "Fout tijdens het ophalen van tijdlijn: {0}" }, "user_card": { "approve": "Goedkeuren", @@ -543,10 +611,18 @@ "report": "Aangeven", "mention": "Vermelding", "media": "Media", - "hidden": "Verborgen" + "hidden": "Verborgen", + "highlight": { + "side": "Zijstreep", + "striped": "Gestreepte achtergrond", + "solid": "Effen achtergrond", + "disabled": "Geen highlight" + }, + "bot": "Bot", + "message": "Bericht" }, "user_profile": { - "timeline_title": "Gebruikers Tijdlijn", + "timeline_title": "Gebruikerstijdlijn", "profile_loading_error": "Sorry, er is een fout opgetreden bij het laden van dit profiel.", "profile_does_not_exist": "Sorry, dit profiel bestaat niet." }, @@ -555,20 +631,22 @@ "who_to_follow": "Wie te volgen" }, "tool_tip": { - "media_upload": "Media Uploaden", + "media_upload": "Media uploaden", "repeat": "Herhalen", "reply": "Beantwoorden", "favorite": "Favoriet maken", "user_settings": "Gebruikers Instellingen", "reject_follow_request": "Volg-verzoek afwijzen", "accept_follow_request": "Volg-aanvraag accepteren", - "add_reaction": "Reactie toevoegen" + "add_reaction": "Reactie toevoegen", + "bookmark": "Bladwijzer" }, "upload": { "error": { "base": "Upload mislukt.", "file_too_big": "Bestand is te groot [{filesize}{filesizeunit} / {allowedsize}{allowedsizeunit}]", - "default": "Probeer het later opnieuw" + "default": "Probeer het later opnieuw", + "message": "Upload is mislukt: {0}" }, "file_size_units": { "B": "B", @@ -585,25 +663,25 @@ "reject": "Afwijzen", "replace": "Vervangen", "is_replaced_by": "→", - "keyword_policies": "Zoekwoord Beleid", + "keyword_policies": "Zoekwoordbeleid", "ftl_removal": "Verwijdering van \"Het Geheel Bekende Netwerk\" Tijdlijn" }, - "mrf_policies_desc": "MRF regels beïnvloeden het federatie gedrag van de instantie. De volgende regels zijn ingeschakeld:", - "mrf_policies": "Ingeschakelde MRF Regels", + "mrf_policies_desc": "MRF-regels beïnvloeden het federatiegedrag van de instantie. De volgende regels zijn ingeschakeld:", + "mrf_policies": "Ingeschakelde MRF-regels", "simple": { - "simple_policies": "Instantie-specifieke Regels", + "simple_policies": "Instantiespecifieke regels", "accept": "Accepteren", "accept_desc": "Deze instantie accepteert alleen berichten van de volgende instanties:", "reject": "Afwijzen", "reject_desc": "Deze instantie zal geen berichten accepteren van de volgende instanties:", "quarantine": "Quarantaine", - "quarantine_desc": "Deze instantie zal alleen publieke berichten sturen naar de volgende instanties:", - "ftl_removal_desc": "Deze instantie verwijdert de volgende instanties van \"Het Geheel Bekende Netwerk\" tijdlijn:", + "quarantine_desc": "Deze instantie zal alleen openbare berichten sturen naar de volgende instanties:", + "ftl_removal_desc": "Deze instantie verwijdert de volgende instanties van \"Bekende Netwerk\" tijdlijn:", "media_removal_desc": "Deze instantie verwijdert media van berichten van de volgende instanties:", "media_nsfw_desc": "Deze instantie stelt media in als gevoelig in berichten van de volgende instanties:", - "ftl_removal": "Verwijderen van \"Het Geheel Bekende Netwerk\" Tijdlijn", - "media_removal": "Media Verwijdering", - "media_nsfw": "Forceer Media als Gevoelig" + "ftl_removal": "Verwijderen van \"Bekende Netwerk\" Tijdlijn", + "media_removal": "Mediaverwijdering", + "media_nsfw": "Forceer media als gevoelig" } }, "staff": "Personeel" @@ -634,8 +712,8 @@ "next": "Volgende" }, "polls": { - "add_poll": "Poll Toevoegen", - "add_option": "Optie Toevoegen", + "add_poll": "Poll toevoegen", + "add_option": "Optie toevoegen", "option": "Optie", "votes": "stemmen", "vote": "Stem", @@ -645,29 +723,31 @@ "expires_in": "Poll eindigt in {0}", "expired": "Poll is {0} geleden beëindigd", "not_enough_options": "Te weinig opties in poll", - "type": "Poll type" + "type": "Poll-type", + "votes_count": "{count} stem | {count} stemmen", + "people_voted_count": "{count} persoon heeft gestemd | {count} personen hebben gestemd" }, "emoji": { "emoji": "Emoji", "keep_open": "Picker openhouden", - "search_emoji": "Zoek voor een emoji", + "search_emoji": "Emoji zoeken", "add_emoji": "Emoji invoegen", - "unicode": "Unicode emoji", + "unicode": "Unicode-emoji", "load_all": "Alle {emojiAmount} emoji worden geladen", "stickers": "Stickers", "load_all_hint": "Eerste {saneAmount} emoji geladen, alle emoji tegelijk laden kan problemen veroorzaken met prestaties.", "custom": "Gepersonaliseerde emoji" }, "interactions": { - "favs_repeats": "Herhalingen en Favorieten", - "follows": "Nieuwe volgingen", - "moves": "Gebruiker migreert", + "favs_repeats": "Herhalingen en favorieten", + "follows": "Nieuwe gevolgden", + "moves": "Gebruikermigraties", "load_older": "Oudere interacties laden" }, "remote_user_resolver": { "searching_for": "Zoeken naar", "error": "Niet gevonden.", - "remote_user_resolver": "Externe gebruikers zoeker" + "remote_user_resolver": "Externe gebruikers-zoeker" }, "selectable_list": { "select_all": "Alles selecteren" @@ -715,7 +795,17 @@ "repeats": "Herhalingen", "favorites": "Favorieten", "thread_muted_and_words": ", heeft woorden:", - "thread_muted": "Thread genegeerd" + "thread_muted": "Thread genegeerd", + "expand": "Uitklappen", + "nsfw": "Gevoelig", + "status_deleted": "Dit bericht is verwijderd", + "hide_content": "Inhoud verbergen", + "show_content": "Inhoud tonen", + "hide_full_subject": "Volledig onderwerp verbergen", + "show_full_subject": "Volledig onderwerp tonen", + "external_source": "Externe bron", + "unbookmark": "Bladwijzer verwijderen", + "bookmark": "Bladwijzer toevoegen" }, "time": { "years_short": "{0}j", @@ -750,5 +840,33 @@ "day_short": "{0}d", "days": "{0} dagen", "day": "{0} dag" + }, + "shoutbox": { + "title": "Shoutbox" + }, + "errors": { + "storage_unavailable": "Pleroma kon browseropslag niet benaderen. Je login of lokale instellingen worden niet opgeslagen en je kunt onverwachte problemen ondervinden. Probeer cookies te accepteren." + }, + "display_date": { + "today": "Vandaag" + }, + "file_type": { + "file": "Bestand", + "image": "Afbeelding", + "video": "Video", + "audio": "Audio" + }, + "chats": { + "empty_chat_list_placeholder": "Je hebt nog geen chats. Start een nieuwe chat!", + "error_sending_message": "Er is iets fout gegaan tijdens het verzenden van het bericht.", + "error_loading_chat": "Er is iets fout gegaan tijdens het laden van de chat.", + "delete_confirm": "Wil je echt dit bericht verwijderen?", + "more": "Meer", + "empty_message_error": "Kan niet een leeg bericht plaatsen", + "new": "Nieuwe Chat", + "chats": "Chats", + "delete": "Verwijderen", + "message_user": "Spreek met {nickname}", + "you": "Jij:" } } diff --git a/src/i18n/ru.json b/src/i18n/ru.json @@ -13,7 +13,7 @@ "disable": "Оключить", "enable": "Включить", "confirm": "Подтвердить", - "verify": "Проверить", + "verify": "Подтверждение", "more": "Больше", "generic_error": "Произошла ошибка", "optional": "не обязательно", @@ -44,8 +44,8 @@ "heading": { "TotpForm": "Двухфакторная аутентификация", "RecoveryForm": "Two-factor recovery", - "totp": "Двухфакторная аутентификация", - "recovery": "Двухфакторное возвращение аккаунта" + "totp": "Двухэтапная аутентификация", + "recovery": "Восстановление двухэтапной аутентификации" }, "hint": "Войдите чтобы присоединиться к дискуссии", "description": "Войти с помощью OAuth" @@ -55,8 +55,8 @@ "chat": "Локальный чат", "mentions": "Упоминания", "interactions": "Взаимодействия", - "public_tl": "Публичная лента", - "timeline": "Лента", + "public_tl": "Локальная лента", + "timeline": "Главная", "twkn": "Федеративная лента", "search": "Поиск", "friend_requests": "Запросы на чтение", @@ -65,10 +65,11 @@ "timelines": "Ленты", "preferences": "Настройки", "who_to_follow": "Кого читать", - "dms": "Личные Сообщения", + "dms": "Личные сообщения", "administration": "Панель администратора", - "about": "О сервере", - "user_search": "Поиск пользователей" + "about": "Об узле", + "user_search": "Поиск пользователей", + "home_timeline": "Главная" }, "notifications": { "broken_favorite": "Неизвестный статус, ищем…", @@ -79,35 +80,35 @@ "read": "Прочесть", "repeated_you": "повторил(а) ваш статус", "follow_request": "хочет читать вас", - "reacted_with": "добавил реакцию: {0}", - "migrated_to": "мигрировал на", + "reacted_with": "добавил(а) реакцию: {0}", + "migrated_to": "перехал на", "no_more_notifications": "Нет дальнейших уведомлений", "error": "Ошибка при обновлении уведомлений: {0}" }, "interactions": { - "favs_repeats": "Повторы и фавориты", + "favs_repeats": "Повторы и отметки «Нравится»", "follows": "Новые читатели", "load_older": "Загрузить старые взаимодействия", - "moves": "Миграции пользователей" + "moves": "Переезды" }, "post_status": { - "account_not_locked_warning": "Ваш аккаунт не {0}. Кто угодно может начать читать вас чтобы видеть посты только для подписчиков.", - "account_not_locked_warning_link": "залочен", - "attachments_sensitive": "Вложения содержат чувствительный контент", + "account_not_locked_warning": "Ваша учетная запись не {0}. Кто угодно может начать читать вас чтобы видеть статусы только для читателей.", + "account_not_locked_warning_link": "закрыт", + "attachments_sensitive": "Вложения имеют щекотливый характер", "content_warning": "Тема (не обязательно)", "default": "Что нового?", "direct_warning": "Этот пост будет виден только упомянутым пользователям", "posting": "Отправляется", "scope_notice": { - "public": "Этот пост будет виден всем", - "private": "Этот пост будет виден только вашим подписчикам", - "unlisted": "Этот пост не будет виден в публичной и федеративной ленте" + "public": "Этот статус будет виден всем", + "private": "Этот статус будет виден только вашим читателям", + "unlisted": "Этот статус не будет виден в локальной и федеративной ленте" }, "scope": { - "direct": "Личное - этот пост видят только те кто в нём упомянут", - "private": "Для подписчиков - этот пост видят только подписчики", - "public": "Публичный - этот пост виден всем", - "unlisted": "Непубличный - этот пост не виден на публичных лентах" + "direct": "Личное сообщение - этот статус видят только те, кто в нём упомянут", + "private": "Для читателей - этот статус видят только ваши читатели", + "public": "Публичный - этот статус виден всем", + "unlisted": "Тихий - этот пост виден всем, но не отображается в публичных лентах" }, "preview_empty": "Пустой предпросмотр", "media_description_error": "Не удалось обновить вложение, попробуйте еще раз", @@ -122,11 +123,12 @@ "text/plain": "Простой текст" }, "media_description": "Описание вложения", - "new_status": "Написать новый статус" + "new_status": "Написать новый статус", + "post": "Опубликовать" }, "registration": { - "bio": "Описание", - "email": "Email", + "bio": "О себе", + "email": "Электронная почта", "fullname": "Отображаемое имя", "password_confirm": "Подтверждение пароля", "registration": "Регистрация", @@ -143,7 +145,10 @@ "fullname_placeholder": "например: Почтальон Печкин", "username_placeholder": "например: pechkin", "captcha": "Код подтверждения", - "new_captcha": "Нажмите на изображение чтобы получить новый код" + "new_captcha": "Нажмите на изображение чтобы получить новый код", + "reason_placeholder": "Данный узел обрабатывает запросы на регистрацию вручную.\nРасскажите администрации почему вы хотите зарегистрироваться.", + "reason": "Причина регистрации", + "register": "Зарегистрироваться" }, "settings": { "enter_current_password_to_confirm": "Введите свой текущий пароль", @@ -152,7 +157,7 @@ "setup_otp": "Настройка OTP", "wait_pre_setup_otp": "предварительная настройка OTP", "confirm_and_enable": "Подтвердить и включить OTP", - "title": "Двухфакторная аутентификация", + "title": "Двухэтапная аутентификация", "generate_new_recovery_codes": "Получить новые коды востановления", "warning_of_generate_new_codes": "После получения новых кодов восстановления, старые больше не будут работать.", "recovery_codes": "Коды восстановления.", @@ -161,11 +166,11 @@ "authentication_methods": "Методы аутентификации", "scan": { "title": "Сканирование", - "desc": "Используйте приложение для двухэтапной аутентификации для сканирования этого QR-код или введите текстовый ключ:", + "desc": "Отсканируйте QR-код приложением для двухэтапной аутентификации или введите текстовый ключ:", "secret_code": "Ключ" }, "verify": { - "desc": "Чтобы включить двухэтапную аутентификации, введите код из вашего приложение для двухэтапной аутентификации:" + "desc": "Чтобы включить двухэтапную аутентификацию, введите код из приложения-аутентификатора:" } }, "attachmentRadius": "Прикреплённые файлы", @@ -174,16 +179,16 @@ "avatarAltRadius": "Аватары в уведомлениях", "avatarRadius": "Аватары", "background": "Фон", - "bio": "Описание", + "bio": "О себе", "btnRadius": "Кнопки", - "bot": "Это аккаунт бота", + "bot": "Это учётная запись бота", "cBlue": "Ответить, читать", "cGreen": "Повторить", "cOrange": "Нравится", "cRed": "Отменить", - "change_email": "Сменить email", - "change_email_error": "Произошла ошибка при попытке изменить email.", - "changed_email": "Email изменён успешно!", + "change_email": "Сменить адрес электронной почты", + "change_email_error": "Произошла ошибка при попытке изменить электронную почту.", + "changed_email": "Электронная почта изменена успешно!", "change_password": "Сменить пароль", "change_password_error": "Произошла ошибка при попытке изменить пароль.", "changed_password": "Пароль изменён успешно!", @@ -193,9 +198,9 @@ "current_password": "Текущий пароль", "current_profile_banner": "Текущий баннер профиля", "data_import_export_tab": "Импорт / Экспорт данных", - "delete_account": "Удалить аккаунт", - "delete_account_description": "Удалить вашу учётную запись и все ваши сообщения.", - "delete_account_error": "Возникла ошибка в процессе удаления вашего аккаунта. Если это повторяется, свяжитесь с администратором вашего сервера.", + "delete_account": "Удалить учетную запись", + "delete_account_description": "Навсегда удалить вашу учётную запись и ваши статусы.", + "delete_account_error": "Возникла ошибка в процессе удаления вашей учетной записи. Если это повторяется, свяжитесь с администратором данного узла.", "delete_account_instructions": "Введите ваш пароль в поле ниже для подтверждения удаления.", "export_theme": "Сохранить Тему", "filtering": "Фильтрация", @@ -221,28 +226,28 @@ "interfaceLanguage": "Язык интерфейса", "limited_availability": "Не доступно в вашем браузере", "links": "Ссылки", - "lock_account_description": "Аккаунт доступен только подтверждённым подписчикам", + "lock_account_description": "Сделать учетную запись закрытой — подтверждать читателей вручную", "loop_video": "Зациливать видео", "loop_video_silent_only": "Зацикливать только беззвучные видео (т.е. \"гифки\" с Mastodon)", "name": "Имя", - "name_bio": "Имя и описание", - "new_email": "Новый email", + "name_bio": "Личные данные", + "new_email": "Новый адрес электронной почты", "new_password": "Новый пароль", "fun": "Потешное", "greentext": "Мемные стрелочки", "notification_visibility": "Показывать уведомления", - "notification_visibility_follows": "Подписки", + "notification_visibility_follows": "Новые читатели", "notification_visibility_likes": "Лайки", "notification_visibility_mentions": "Упоминания", "notification_visibility_repeats": "Повторы", - "no_rich_text_description": "Убрать форматирование из всех постов", + "no_rich_text_description": "Убрать форматирование из всех статусов", "hide_follows_description": "Не показывать кого я читаю", "hide_followers_description": "Не показывать кто читает меня", "hide_follows_count_description": "Не показывать число читаемых пользователей", - "hide_followers_count_description": "Не показывать число моих подписчиков", + "hide_followers_count_description": "Не показывать число моих читателей", "show_admin_badge": "Показывать значок администратора в моем профиле", "show_moderator_badge": "Показывать значок модератора в моем профиле", - "nsfw_clickthrough": "Включить скрытие вложений и предпросмотра ссылок для NSFW статусов", + "nsfw_clickthrough": "Включить скрытие вложений и предпросмотра ссылок для статусов щекотливого характера", "oauth_tokens": "OAuth токены", "token": "Токен", "refresh_token": "Рефреш токен", @@ -257,14 +262,14 @@ "radii_help": "Скругление углов элементов интерфейса (в пикселях)", "replies_in_timeline": "Ответы в ленте", "reply_visibility_all": "Показывать все ответы", - "reply_visibility_following": "Показывать только ответы мне или тех на кого я подписан", + "reply_visibility_following": "Показывать только ответы мне или тем кого я читаю", "reply_visibility_self": "Показывать только ответы мне", - "autohide_floating_post_button": "Автоматически скрывать кнопку постинга (в мобильной версии)", + "autohide_floating_post_button": "Автоматически скрывать кнопку \"Написать новый статус\" (в мобильной версии)", "saving_err": "Не удалось сохранить настройки", "saving_ok": "Сохранено", "security_tab": "Безопасность", - "scope_copy": "Копировать видимость поста при ответе (всегда включено для Личных Сообщений)", - "minimal_scopes_mode": "Минимизировать набор опций видимости поста", + "scope_copy": "Копировать видимость поста при ответе (всегда включено для личных сообщений)", + "minimal_scopes_mode": "Показывать только личное сообщение и публичный статус в опциях видимости", "set_new_avatar": "Загрузить новый аватар", "set_new_profile_background": "Загрузить новый фон профиля", "set_new_profile_banner": "Загрузить новый баннер профиля", @@ -273,7 +278,7 @@ "stop_gifs": "Проигрывать GIF анимации только при наведении", "streaming": "Включить автоматическую загрузку новых сообщений при прокрутке вверх", "useStreamingApi": "Получать сообщения и уведомления в реальном времени", - "useStreamingApiWarning": "(Не рекомендуется, экспериментально, сообщения могут пропадать)", + "useStreamingApiWarning": "(Не рекомендуется, экспериментально, статусы могут пропадать)", "text": "Текст", "theme": "Тема", "theme_help": "Используйте шестнадцатеричные коды цветов (#rrggbb) для настройки темы.", @@ -305,7 +310,8 @@ "older_version_imported": "Файл, который вы импортировали, был сделан в старой версии фронт-энда.", "future_version_imported": "Файл, который вы импортировали, был сделан в новой версии фронт-энда.", "v2_imported": "Файл, который вы импортировали, был сделан под старый фронт-энд. Мы стараемся улучшить совместимость, но все еще возможны несостыковки.", - "upgraded_from_v2": "Фронт-энд Pleroma был изменен. Выбранная тема может выглядеть слегка по-другому." + "upgraded_from_v2": "Фронт-энд Pleroma был изменен. Выбранная тема может выглядеть слегка по-другому.", + "fe_downgraded": "Версия фронт-энда Pleroma была откачена." } }, "common": { @@ -337,13 +343,29 @@ "badge": "Фон значков", "badge_notification": "Уведомления", "panel_header": "Заголовок панели", - "top_bar": "Верняя полоска", + "top_bar": "Верхняя полоска", "borders": "Границы", "buttons": "Кнопки", "inputs": "Поля ввода", "faint_text": "Маловажный текст", - "post": "Сообщения и описание пользователя", - "alert_neutral": "Нейтральный" + "post": "Статусы и раздел \"О себе\"", + "alert_neutral": "Нейтральный", + "alert_warning": "Предупреждение", + "selectedPost": "Выбранный статус", + "pressed": "Нажатие", + "highlight": "Выделенные элементы", + "icons": "Иконки", + "poll": "График результатов опроса", + "wallpaper": "Фон", + "chat": { + "border": "Границы", + "outgoing": "Исходящие", + "incoming": "Входящие" + }, + "tabs": "Вкладки", + "toggled": "Включено", + "disabled": "Отключено", + "selectedMenu": "Выбранный пункт меню" }, "radii": { "_tab_label": "Скругление" @@ -368,8 +390,8 @@ "panel": "Панель", "panelHeader": "Заголовок панели", "topBar": "Верхняя полоска", - "avatar": "Аватарка (профиль)", - "avatarStatus": "Аватарка (в ленте)", + "avatar": "Аватар (профиль)", + "avatarStatus": "Аватар (в ленте)", "popup": "Всплывающие подсказки", "button": "Кнопки", "buttonHover": "Кнопки (наведен курсор)", @@ -385,7 +407,7 @@ "interface": "Интерфейс", "input": "Поля ввода", "post": "Текст постов", - "postCode": "Моноширинный текст в посте (форматирование)" + "postCode": "Моноширинный текст в статусе (форматирование)" }, "family": "Шрифт", "size": "Размер (в пикселях)", @@ -407,12 +429,12 @@ "link": "ссылка" } }, - "allow_following_move": "Разрешить автоматически читать новый аккаунт при перемещении на другой сервер", + "allow_following_move": "Автоматически начать читать новый профиль при переезде", "hide_user_stats": "Не показывать статистику пользователей (например количество читателей)", - "discoverable": "Разрешить показ аккаунта в поисковиках и других сервисах", - "default_vis": "Видимость постов по умолчанию", + "discoverable": "Разрешить показывать учетную запись в поисковых системах и прочих сервисах", + "default_vis": "Видимость статусов по умолчанию", "mutes_and_blocks": "Блокировки и игнорируемые", - "composing": "Составление постов", + "composing": "Составление статусов", "chatMessageRadius": "Сообщения в беседе", "blocks_tab": "Блокировки", "import_mutes_from_a_csv_file": "Импортировать игнорируемых из CSV файла", @@ -432,12 +454,12 @@ "post_status_content_type": "Формат составляемых статусов по умолчанию", "subject_line_noop": "Не копировать", "subject_line_mastodon": "Как в Mastodon: скопировать как есть", - "subject_line_email": "Как в e-mail: \"re: тема\"", + "subject_line_email": "Как в электронной почте: \"re: тема\"", "subject_line_behavior": "Копировать тему в ответах", "no_mutes": "Нет игнорируемых", "no_blocks": "Нет блокировок", "notification_visibility_emoji_reactions": "Реакции", - "notification_visibility_moves": "Миграции пользователей", + "notification_visibility_moves": "Переезды", "use_contain_fit": "Не обрезать вложения в миниатюрах", "profile_fields": { "value": "Значение", @@ -452,7 +474,7 @@ "hide_filtered_statuses": "Не показывать отфильтрованные статусы", "hide_muted_posts": "Не показывать статусы игнорируемых пользователей", "hide_post_stats": "Не показывать статистику статусов (например количество отметок «Нравится»)", - "use_one_click_nsfw": "Открывать NSFW вложения одним кликом", + "use_one_click_nsfw": "Открывать вложения имеющие щекотливый характер одним кликом", "preload_images": "Предварительно загружать изображения", "max_thumbnails": "Максимальное число миниатюр показываемых в статусе", "emoji_reactions_on_timeline": "Показывать эмодзи реакции в ленте", @@ -464,26 +486,43 @@ "virtual_scrolling": "Оптимизировать рендеринг ленты", "hide_wallpaper": "Скрыть обои узла", "accent": "Акцент", - "upload_a_photo": "Загрузить фото", - "notification_mutes": "Чтобы не получать уведомления от определённого пользователя, заглушите его.", - "reset_avatar_confirm": "Вы действительно хотите сбросить личный образ?", - "reset_profile_banner": "Сбросить личный баннер", - "reset_profile_background": "Сбросить личные обои", - "reset_avatar": "Сбросить личный образ", - "search_user_to_mute": "Искать, кого вы хотите заглушить", - "search_user_to_block": "Искать, кого вы хотите заблокировать", - "pad_emoji": "Выделять эмодзи пробелами при добавлении из панели", - "avatar_size_instruction": "Желательный наименьший размер личного образа 150 на 150 пикселей.", + "upload_a_photo": "Загрузить изображение", + "notification_mutes": "Чтобы не получать уведомления от конкретного пользователя, заглушите его.", + "reset_avatar_confirm": "Вы точно хотите сбросить аватар?", + "reset_profile_banner": "Сбросить баннер профиля", + "reset_profile_background": "Сбросить фон профиля", + "reset_avatar": "Сбросить аватар", + "search_user_to_mute": "Поиск того, кого вы хотите заглушить", + "search_user_to_block": "Поиск того, кого вы хотите заблокировать", + "pad_emoji": "Разделять эмодзи пробелами, когда они добавляются из меню", + "avatar_size_instruction": "Рекомендуется использовать изображение больше чем 150 на 150 пикселей в качестве аватара.", "enable_web_push_notifications": "Включить web push-уведомления", "notification_blocks": "Блокировка пользователя выключает все уведомления от него, а также отписывает вас от него.", - "notification_setting_hide_notification_contents": "Скрыть отправителя и содержимое push-уведомлений" + "notification_setting_hide_notification_contents": "Скрыть отправителя и содержимое push-уведомлений", + "version": { + "title": "Версия", + "frontend_version": "Версия фронт-энда", + "backend_version": "Версия бэк-энда" + }, + "word_filter": "Фильтр слов", + "sensitive_by_default": "Помечать статусы как имеющие щекотливый характер по умолчанию", + "reply_visibility_self_short": "Показывать ответы только вам", + "reply_visibility_following_short": "Показывать ответы тем кого вы читаете", + "hide_all_muted_posts": "Не показывать игнорируемые статусы", + "hide_media_previews": "Не показывать вложения в ленте", + "setting_changed": "Отличается от значения по умолчанию", + "reset_background_confirm": "Вы точно хотите сбросить фон?", + "reset_banner_confirm": "Вы точно хотите сбросить баннер?", + "type_domains_to_mute": "Поиск узлов, которые вы хотите заглушить", + "more_settings": "Остальные настройки", + "save": "Сохранить изменения" }, "timeline": { "collapse": "Свернуть", "conversation": "Разговор", "error_fetching": "Ошибка при обновлении", "load_older": "Загрузить старые статусы", - "no_retweet_hint": "Пост помечен как \"только для подписчиков\" или \"личное\" и поэтому не может быть повторён", + "no_retweet_hint": "Статус помечен как \"только для читателей\" или \"личное сообщение\" и потому не может быть повторён", "repeated": "повторил(а)", "show_new": "Показать новые", "up_to_date": "Обновлено", @@ -492,7 +531,7 @@ "status": { "bookmark": "Добавить в закладки", "unbookmark": "Удалить из закладок", - "status_deleted": "Пост удален", + "status_deleted": "Статус удален", "reply_to": "Ответ", "repeats": "Повторы", "favorites": "Понравилось", @@ -528,16 +567,16 @@ "revoke_admin": "Забрать права администратора", "grant_moderator": "Сделать модератором", "revoke_moderator": "Забрать права модератора", - "activate_account": "Активировать аккаунт", - "deactivate_account": "Деактивировать аккаунт", - "delete_account": "Удалить аккаунт", - "force_nsfw": "Отмечать посты пользователя как NSFW", - "strip_media": "Убирать вложения из постов пользователя", - "force_unlisted": "Не добавлять посты в публичные ленты", + "activate_account": "Активировать учетную запись", + "deactivate_account": "Деактивировать учетную запись", + "delete_account": "Удалить учетную запись", + "force_nsfw": "Отмечать статусы пользователя как имеющие щекотливый характер", + "strip_media": "Убирать вложения из статусов пользователя", + "force_unlisted": "Не показывать статусы в публичных лентах", "sandbox": "Принудить видимость постов только читателям", - "disable_remote_subscription": "Запретить читать с удаленных серверов", + "disable_remote_subscription": "Запретить читать с других узлов", "disable_any_subscription": "Запретить читать пользователя", - "quarantine": "Не федерировать посты пользователя", + "quarantine": "Не федерировать статусы пользователя", "delete_user": "Удалить пользователя", "delete_user_confirmation": "Вы уверены? Это действие нельзя отменить." }, @@ -545,7 +584,14 @@ "mention": "Упомянуть", "show_repeats": "Показывать повторы", "hide_repeats": "Скрыть повторы", - "report": "Пожаловаться" + "report": "Пожаловаться", + "message": "Написать сообщение", + "highlight": { + "side": "Полоска сбоку", + "striped": "Фон в полоску", + "solid": "Сплошной фон", + "disabled": "Нет выделения" + } }, "user_profile": { "timeline_title": "Лента пользователя" @@ -560,30 +606,31 @@ "password_reset": { "forgot_password": "Забыли пароль?", "password_reset": "Сброс пароля", - "instruction": "Введите ваш email или имя пользователя, и мы отправим вам ссылку для сброса пароля.", - "placeholder": "Ваш email или имя пользователя", - "check_email": "Проверьте ваш email и перейдите по ссылке для сброса пароля.", + "instruction": "Введите ваш адрес электронной почты или имя пользователя: на вашу электронную почту будет отправлена ссылка для сброса пароля.", + "placeholder": "Ваш адрес электронной почты или имя пользователя", + "check_email": "Проверьте вашу электронную почту и перейдите по ссылке для сброса пароля.", "return_home": "Вернуться на главную страницу", "too_many_requests": "Вы исчерпали допустимое количество попыток, попробуйте позже.", - "password_reset_disabled": "Сброс пароля отключен. Cвяжитесь с администратором вашего сервера." + "password_reset_disabled": "Автоматический сброс пароля отключен. Свяжитесь с администратором данного узла для сброса пароля.", + "password_reset_required_but_mailer_is_disabled": "Вы должны сбросить свой пароль, однако автоматический сброс пароля отключен. Пожалуйста свяжитесь с администратором данного узла." }, "about": { "mrf": { "federation": "Федерация", "simple": { - "accept_desc": "Данный сервер принимает сообщения только со следующих серверов:", - "ftl_removal_desc": "Данный сервер скрывает следующие сервера с федеративной ленты:", - "media_nsfw_desc": "Данный сервер принужденно помечает вложения со следущих серверов как NSFW:", - "simple_policies": "Правила для определенных серверов", - "accept": "Принимаемые сообщения", - "reject": "Отклоняемые сообщения", - "reject_desc": "Данный сервер не принимает сообщения со следующих серверов:", + "accept_desc": "Данный узел принимает сообщения только со следующих узлов:", + "ftl_removal_desc": "Данный узел скрывает следующие узлы с федеративной ленты:", + "media_nsfw_desc": "Данный узел принужденно помечает вложения со следующих узлов как имеющие щекотливый характер:", + "simple_policies": "Правила для определенных узлов", + "accept": "Белый список", + "reject": "Черный список", + "reject_desc": "Данный узел не принимает сообщения со следующих узлов:", "quarantine": "Зона карантина", - "quarantine_desc": "Данный сервер отправляет только публичные посты следующим серверам:", + "quarantine_desc": "Данный узел отправляет только публичные статусы следующим узлам:", "ftl_removal": "Скрытие с федеративной ленты", "media_removal": "Удаление вложений", - "media_removal_desc": "Данный сервер удаляет вложения со следующих серверов:", - "media_nsfw": "Принужденно помеченно как NSFW" + "media_removal_desc": "Данный узел удаляет вложения со следующих узлов:", + "media_nsfw": "Принужденно помеченно как имеющее щекотливый характер" }, "keyword": { "ftl_removal": "Убрать из федеративной ленты", @@ -593,7 +640,7 @@ "is_replaced_by": "→" }, "mrf_policies": "Активные правила MRF (модуль переписывания сообщений)", - "mrf_policies_desc": "Правила MRF (модуль переписывания сообщений) влияют на федерацию данного сервера. Следующие правила активны:" + "mrf_policies_desc": "Правила MRF (модуль переписывания сообщений) влияют на федерацию данного узла. Следующие правила активны:" }, "staff": "Администрация" }, @@ -644,7 +691,9 @@ "votes": "голосов", "option": "Вариант", "add_option": "Добавить вариант", - "add_poll": "Прикрепить опрос" + "add_poll": "Прикрепить опрос", + "votes_count": "{count} голос | {count} голосов", + "people_voted_count": "{count} человек проголосовал | {count} человек проголосовали" }, "media_modal": { "next": "Следующая", @@ -702,10 +751,26 @@ "chats": "Беседы", "delete": "Удалить", "message_user": "Напишите {nickname}", - "you": "Вы:" + "you": "Вы:", + "error_sending_message": "Произошла ошибка при отправке сообщения." }, "remote_user_resolver": { "error": "Не найдено.", "searching_for": "Ищем" + }, + "upload": { + "error": { + "message": "Произошла ошибка при загрузке: {0}" + } + }, + "user_reporting": { + "add_comment_description": "Жалоба будет направлена модераторам вашего узла. Вы можете указать причину жалобы ниже:", + "forward_description": "Данный пользователь находится на другом узле. Отослать туда копию вашей жалобы?" + }, + "file_type": { + "file": "Файл", + "video": "Видеозапись", + "audio": "Аудиозапись", + "image": "Изображение" } } diff --git a/src/i18n/uk.json b/src/i18n/uk.json @@ -30,7 +30,7 @@ "features_panel": { "gopher": "Gopher", "pleroma_chat_messages": "Чати", - "chat": "Міні-чат", + "chat": "Оголошення", "who_to_follow": "Кого відстежувати", "title": "Особливості", "scope_options": "Параметри обсягу", @@ -49,7 +49,7 @@ "mute": "Ігнорувати" }, "shoutbox": { - "title": "Міні-чат" + "title": "Оголошення" }, "about": { "staff": "Адміністрація", @@ -122,7 +122,9 @@ "votes": "голосів", "option": "Відповідь", "add_poll": "Додати опитування", - "not_enough_options": "Замало унікальних варіантів в опитуванні" + "not_enough_options": "Замало унікальних варіантів в опитуванні", + "people_voted_count": "{count} особа проголосувала | {count} осіб проголосувало", + "votes_count": "{count} голос | {count} голосів" }, "notifications": { "reacted_with": "додав реакцію: {0}", @@ -155,7 +157,8 @@ "interactions": "Взаємодії", "mentions": "Згадування", "back": "Назад", - "administration": "Адміністрування" + "administration": "Адміністрування", + "home_timeline": "Домашня стрічка" }, "media_modal": { "next": "Наступна", @@ -246,7 +249,8 @@ }, "preview_empty": "Пустий", "media_description_error": "Не вдалось оновити медіа, спробуйте ще раз", - "media_description": "Опис медіа" + "media_description": "Опис медіа", + "post": "Опублікувати" }, "settings": { "blocks_imported": "Блокування імпортовані! Їх обробка триватиме певний час.", @@ -608,7 +612,28 @@ "backend_version": "Версія бекенду", "title": "Версія" }, - "hide_wallpaper": "Сховати шпалери екземпляру" + "hide_wallpaper": "Сховати шпалери екземпляру", + "more_settings": "Більше налаштувань", + "sensitive_by_default": "Визначати допис як дратівливий за замовчуванням", + "reply_visibility_self_short": "Показувати відповіді лише мені", + "reply_visibility_following_short": "Показувати відповіді тим, на кого я підписаний", + "hide_all_muted_posts": "Приховати приглушені повідомлення", + "hide_media_previews": "Приховати попередній перегляд медіа", + "word_filter": "Фільтр слів", + "setting_changed": "Конфігурація відрізняється від типової", + "save": "Зберегти зміни", + "file_export_import": { + "errors": { + "file_slightly_new": "Другорядна версія файлу відрізняється, деякі налаштування можуть бути не прийняті", + "file_too_old": "Несумісна основна версія: {fileMajor}, версія файлу занадто стара і не підтримується (мінімальна версія налаштувань {feMajor})", + "file_too_new": "Несумісна основна версія: {fileMajor}, ця версія PleromaFE ({feMajor}) занадто стара для його обробки", + "invalid_file": "Вибраний файл не є резервною копією налаштувань Pleroma. Ніяких змін не було зроблено." + }, + "restore_settings": "Відновити налаштування з файлу", + "backup_settings_theme": "Резервне копіювання налаштувань та теми у файл", + "backup_settings": "Резервне копіювання налаштувань у файл", + "backup_restore": "Резервне копіювання налаштувань" + } }, "selectable_list": { "select_all": "Вибрати все" @@ -637,7 +662,10 @@ "fullname": "Відображене ім'я", "email": "Ел. пошта", "bio": "Про себе", - "captcha": "CAPTCHA" + "captcha": "CAPTCHA", + "register": "Зареєструватися", + "reason_placeholder": "Цей інстанс обробляє запити на реєстрацію вручну.\nРозкажіть адміністрації чому ви хочете зареєструватися.", + "reason": "Причина реєстрації" }, "who_to_follow": { "who_to_follow": "На кого підписатися", @@ -764,7 +792,14 @@ "unblock": "Розблокувати", "remote_follow": "Підписатись", "muted": "Заглушений", - "mute": "Заглушити" + "mute": "Заглушити", + "highlight": { + "side": "Смужка ліворуч", + "striped": "Смугастий фон", + "solid": "Суцільний фон", + "disabled": "Не виділяти" + }, + "bot": "Бот" }, "status": { "copy_link": "Скопіювати посилання на допис", @@ -804,7 +839,9 @@ "conversation": "Розмова", "no_statuses": "Ніяких статусів", "repeated": "поширив(-ла)", - "no_retweet_hint": "Запис, позначено як \"тільки для підписників\" або \"особисте\" і тому не може бути поширений" + "no_retweet_hint": "Запис, позначено як \"тільки для підписників\" або \"особисте\" і тому не може бути поширений", + "socket_broke": "Втрачено з'єднання у реальному часі: код {0}", + "socket_reconnected": "Встановлено з'єднання у реальному часі" }, "user_reporting": { "submit": "Відправити", diff --git a/src/i18n/zh.json b/src/i18n/zh.json @@ -96,7 +96,8 @@ "administration": "管理员", "chats": "聊天", "timelines": "时间线", - "bookmarks": "书签" + "bookmarks": "书签", + "home_timeline": "主页时间线" }, "notifications": { "broken_favorite": "未知的状态,正在搜索中…", @@ -168,7 +169,8 @@ "preview": "预览", "media_description": "媒体描述", "media_description_error": "更新媒体失败,请重试", - "empty_status_error": "不能发布没有内容、没有附件的发文" + "empty_status_error": "不能发布没有内容、没有附件的发文", + "post": "发送" }, "registration": { "bio": "简介", @@ -191,7 +193,8 @@ "password_confirmation_match": "密码不一致" }, "reason_placeholder": "此实例的注册需要手动批准。\n请让管理员知道您为什么想要注册。", - "reason": "注册理由" + "reason": "注册理由", + "register": "注册" }, "selectable_list": { "select_all": "选择全部" @@ -298,7 +301,7 @@ "new_password": "新密码", "notification_visibility": "要显示的通知类型", "notification_visibility_follows": "关注", - "notification_visibility_likes": "点赞", + "notification_visibility_likes": "喜欢", "notification_visibility_mentions": "提及", "notification_visibility_repeats": "转发", "no_rich_text_description": "不显示富文本格式", @@ -306,8 +309,8 @@ "no_mutes": "没有隐藏", "hide_follows_description": "不要显示我所关注的人", "hide_followers_description": "不要显示关注我的人", - "show_admin_badge": "在我的个人资料中显示管理员徽章", - "show_moderator_badge": "在我的个人资料中显示监察员徽章", + "show_admin_badge": "在我的个人资料中显示“管理员”徽章", + "show_moderator_badge": "在我的个人资料中显示“监察员”徽章", "nsfw_clickthrough": "将不和谐附件和链接预览隐藏,点击才会显示", "oauth_tokens": "OAuth令牌", "token": "令牌", @@ -561,7 +564,27 @@ "mute_export_button": "导出你的隐藏名单到一个 csv 文件", "mute_export": "隐藏名单导出", "hide_wallpaper": "隐藏实例壁纸", - "setting_changed": "与默认设置不同" + "setting_changed": "与默认设置不同", + "more_settings": "更多设置", + "sensitive_by_default": "默认标记发文为敏感内容", + "reply_visibility_self_short": "只显示对我本人的回复", + "reply_visibility_following_short": "显示对我关注的人的回复", + "hide_all_muted_posts": "不显示已隐藏的发文", + "hide_media_previews": "隐藏媒体预览", + "word_filter": "词语过滤", + "save": "保存更改", + "file_export_import": { + "errors": { + "file_slightly_new": "文件的小版本不同,有些设置可能无法加载", + "file_too_old": "不兼容的主版本:{fileMajor},文件版本过旧,不受支持(最小设置版本 {feMajor})", + "file_too_new": "不兼容的主版本:{fileMajor},此 PleromaFE(设置版本 {feMajor})过旧,无法处理", + "invalid_file": "所选文件不是受支持的 Pleroma 设置备份。没有进行任何更改。" + }, + "restore_settings": "从文件恢复设置", + "backup_settings_theme": "备份设置和主题到文件", + "backup_settings": "备份设置到文件", + "backup_restore": "设置备份" + } }, "time": { "day": "{0} 天", @@ -609,7 +632,9 @@ "no_more_statuses": "没有更多的状态", "no_statuses": "没有状态更新", "reload": "重新载入", - "error": "取得时间轴时发生错误:{0}" + "error": "取得时间轴时发生错误:{0}", + "socket_broke": "丢失实时连接:CloseEvent code {0}", + "socket_reconnected": "已建立实时连接" }, "status": { "favorites": "喜欢", @@ -693,7 +718,13 @@ "hide_repeats": "隐藏转发", "message": "消息", "mention": "提及", - "bot": "机器人" + "bot": "机器人", + "highlight": { + "side": "侧边条纹", + "striped": "条纹背景", + "solid": "单一颜色背景", + "disabled": "不突出显示" + } }, "user_profile": { "timeline_title": "用户时间线", @@ -788,8 +819,8 @@ "media_nsfw_desc": "本实例将来自以下实例的媒体内容强制设置为敏感内容:", "media_nsfw": "强制设置媒体为敏感内容", "media_removal_desc": "本实例移除来自以下实例的媒体内容:", - "ftl_removal_desc": "该实例在从“全部已知网络”时间线上移除了下列实例:", - "ftl_removal": "从“全部已知网络”时间线上移除" + "ftl_removal_desc": "该实例在从“已知网络”时间线上移除了下列实例:", + "ftl_removal": "从“已知网络”时间线上移除" }, "mrf_policies_desc": "MRF 策略会影响本实例的互通行为。以下策略已启用:", "mrf_policies": "已启用的 MRF 策略", @@ -811,7 +842,7 @@ "mute": "隐藏" }, "errors": { - "storage_unavailable": "Pleroma 无法访问浏览器储存。您的登陆名以及本地设置将不会被保存,您可能遇到意外问题。请尝试启用 cookies。" + "storage_unavailable": "Pleroma 无法访问浏览器储存。您的登陆以及本地设置将不会被保存,您也可能遇到未知问题。请尝试启用 cookies。" }, "shoutbox": { "title": "留言板" diff --git a/src/i18n/zh_Hant.json b/src/i18n/zh_Hant.json @@ -22,7 +22,9 @@ "votes": "票", "option": "選項", "add_option": "增加選項", - "add_poll": "增加投票" + "add_poll": "增加投票", + "votes_count": "{count} 票 | {count} 票", + "people_voted_count": "{count} 人已投票 | {count} 人已投票" }, "notifications": { "reacted_with": "作出了 {0} 的反應", @@ -55,7 +57,8 @@ "friend_requests": "關注請求", "back": "後退", "administration": "管理員", - "about": "關於" + "about": "關於", + "home_timeline": "家時間線" }, "media_modal": { "next": "往後", @@ -108,7 +111,11 @@ "loading": "載入中…", "more": "更多", "submit": "提交", - "apply": "應用" + "apply": "應用", + "role": { + "moderator": "主持人", + "admin": "管理員" + } }, "finder": { "find_user": "尋找用戶", @@ -322,7 +329,7 @@ "notification_visibility_moves": "用戶遷移", "notification_visibility_repeats": "轉發", "notification_visibility_mentions": "提及", - "notification_visibility_likes": "點贊", + "notification_visibility_likes": "喜歡", "interfaceLanguage": "界面語言", "instance_default": "(默認:{value})", "inputRadius": "輸入框", @@ -424,7 +431,7 @@ "notification_blocks": "封鎖一個用戶會停掉所有他的通知,等同於取消關注。", "enable_web_push_notifications": "啟用 web 推送通知", "presets": "預置", - "profile_background": "個人背景圖", + "profile_background": "配置文件背景圖", "profile_banner": "橫幅圖片", "profile_tab": "個人資料", "radii_help": "設置界面邊緣的圓角 (單位:像素)", @@ -512,7 +519,7 @@ "show_moderator_badge": "顯示主持人徽章", "oauth_tokens": "OAuth代幣", "token": "代幣", - "refresh_token": "刷新代幣", + "refresh_token": "刷新token", "useStreamingApiWarning": "(不推薦使用,實驗性的,已知跳過文章)", "fun": "有趣", "notification_setting_hide_notification_contents": "隱藏推送通知中的發送者與內容信息", @@ -528,7 +535,28 @@ "mute_import_error": "導入靜音時出錯", "mute_export_button": "將靜音導出到csv文件", "mute_export": "靜音導出", - "hide_wallpaper": "隱藏實例桌布" + "hide_wallpaper": "隱藏實例桌布", + "reply_visibility_self_short": "只顯示對我本人的回复", + "reply_visibility_following_short": "顯示對我關注的人的回复", + "hide_all_muted_posts": "不顯示已隱藏的帖子", + "hide_media_previews": "隱藏媒體預覽", + "word_filter": "詞過濾", + "setting_changed": "與默認設置不同", + "more_settings": "更多設置", + "save": "保存更改", + "file_export_import": { + "errors": { + "invalid_file": "所選文件不是受支持的Pleroma設置備份。 沒有進行任何更改。", + "file_too_new": "不兼容的主版本:{fileMajor},此 PleromaFE(設置版本 {feMajor})過舊,無法處理", + "file_too_old": "不兼容的主版本:{fileMajor},文件版本過舊,不受支持(最小設置版本 {feMajor})", + "file_slightly_new": "檔案的小版本不同,有些設置可能無法載入" + }, + "restore_settings": "從文件還原設置", + "backup_settings_theme": "備份設置和主題到文件", + "backup_settings": "備份設置到文件", + "backup_restore": "設定備份" + }, + "sensitive_by_default": "默認標記發文為敏感內容" }, "chats": { "more": "更多", @@ -644,7 +672,8 @@ "attachments_sensitive": "標記附件為敏感內容", "account_not_locked_warning_link": "上鎖", "default": "剛剛抵達洛杉磯。", - "empty_status_error": "無法發佈沒有附件的空發文" + "empty_status_error": "不能發布沒有內容,沒有附件的發文", + "post": "發送" }, "errors": { "storage_unavailable": "Pleroma無法訪問瀏覽器存儲。您的登錄名或本地設置將不會保存,您可能會遇到意外問題。嘗試啟用Cookie。" @@ -661,13 +690,15 @@ "up_to_date": "已是最新", "no_more_statuses": "没有更多發文", "no_statuses": "没有發文", - "error": "取得時間線時發生錯誤:{0}" + "error": "取得時間線時發生錯誤:{0}", + "socket_reconnected": "已建立實時連接", + "socket_broke": "丟失實時連接:CloseEvent代碼{0}" }, "interactions": { "load_older": "載入更早的互動", "moves": "用戶遷移", "follows": "新的關注者", - "favs_repeats": "轉發和收藏" + "favs_repeats": "轉發和喜歡" }, "selectable_list": { "select_all": "選擇全部" @@ -696,7 +727,10 @@ "registration": "註冊", "password_confirm": "確認密碼", "email": "電子郵箱", - "bio": "簡介" + "bio": "簡介", + "reason_placeholder": "此實例的註冊需要手動批准。\n請讓管理知道您為什麼想要註冊。", + "reason": "註冊理由", + "register": "註冊" }, "user_card": { "its_you": "就是你!!", @@ -756,7 +790,14 @@ "roles": { "moderator": "主持人", "admin": "管理員" - } + }, + "highlight": { + "disabled": "無突出顯示", + "solid": "單色背景", + "striped": "條紋背景", + "side": "彩條" + }, + "bot": "機器人" }, "user_profile": { "timeline_title": "用戶時間線", diff --git a/src/main.js b/src/main.js @@ -11,7 +11,7 @@ import statusesModule from './modules/statuses.js' import usersModule from './modules/users.js' import apiModule from './modules/api.js' import configModule from './modules/config.js' -import chatModule from './modules/chat.js' +import shoutModule from './modules/shout.js' import oauthModule from './modules/oauth.js' import authFlowModule from './modules/auth_flow.js' import mediaViewerModule from './modules/media_viewer.js' @@ -28,7 +28,6 @@ import pushNotifications from './lib/push_notifications_plugin.js' import messages from './i18n/messages.js' -import VueChatScroll from 'vue-chat-scroll' import VueClickOutside from 'v-click-outside' import PortalVue from 'portal-vue' import VBodyScrollLock from './directives/body_scroll_lock' @@ -42,7 +41,6 @@ const currentLocale = (window.navigator.language || 'en').split('-')[0] Vue.use(Vuex) Vue.use(VueRouter) Vue.use(VueI18n) -Vue.use(VueChatScroll) Vue.use(VueClickOutside) Vue.use(PortalVue) Vue.use(VBodyScrollLock) @@ -90,7 +88,7 @@ const persistedStateOptions = { users: usersModule, api: apiModule, config: configModule, - chat: chatModule, + shout: shoutModule, oauth: oauthModule, authFlow: authFlowModule, mediaViewer: mediaViewerModule, diff --git a/src/modules/api.js b/src/modules/api.js @@ -3,8 +3,11 @@ import { WSConnectionStatus } from '../services/api/api.service.js' import { maybeShowChatNotification } from '../services/chat_utils/chat_utils.js' import { Socket } from 'phoenix' +const retryTimeout = (multiplier) => 1000 * multiplier + const api = { state: { + retryMultiplier: 1, backendInteractor: backendInteractorService(), fetchers: {}, socket: null, @@ -34,18 +37,43 @@ const api = { }, setMastoUserSocketStatus (state, value) { state.mastoUserSocketStatus = value + }, + incrementRetryMultiplier (state) { + state.retryMultiplier = Math.max(++state.retryMultiplier, 3) + }, + resetRetryMultiplier (state) { + state.retryMultiplier = 1 } }, actions: { - // Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets - enableMastoSockets (store) { - const { state, dispatch } = store - if (state.mastoUserSocket) return + /** + * Global MastoAPI socket control, in future should disable ALL sockets/(re)start relevant sockets + * + * @param {Boolean} [initial] - whether this enabling happened at boot time or not + */ + enableMastoSockets (store, initial) { + const { state, dispatch, commit } = store + // Do not initialize unless nonexistent or closed + if ( + state.mastoUserSocket && + ![ + WebSocket.CLOSED, + WebSocket.CLOSING + ].includes(state.mastoUserSocket.getState()) + ) { + return + } + if (initial) { + commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING_INITIAL) + } else { + commit('setMastoUserSocketStatus', WSConnectionStatus.STARTING) + } return dispatch('startMastoUserSocket') }, disableMastoSockets (store) { - const { state, dispatch } = store + const { state, dispatch, commit } = store if (!state.mastoUserSocket) return + commit('setMastoUserSocketStatus', WSConnectionStatus.DISABLED) return dispatch('stopMastoUserSocket') }, @@ -91,11 +119,29 @@ const api = { } ) state.mastoUserSocket.addEventListener('open', () => { + // Do not show notification when we just opened up the page + if (state.mastoUserSocketStatus !== WSConnectionStatus.STARTING_INITIAL) { + dispatch('pushGlobalNotice', { + level: 'success', + messageKey: 'timeline.socket_reconnected', + timeout: 5000 + }) + } + // Stop polling if we were errored or disabled + if (new Set([ + WSConnectionStatus.ERROR, + WSConnectionStatus.DISABLED + ]).has(state.mastoUserSocketStatus)) { + dispatch('stopFetchingTimeline', { timeline: 'friends' }) + dispatch('stopFetchingNotifications') + dispatch('stopFetchingChats') + } + commit('resetRetryMultiplier') commit('setMastoUserSocketStatus', WSConnectionStatus.JOINED) }) state.mastoUserSocket.addEventListener('error', ({ detail: error }) => { console.error('Error in MastoAPI websocket:', error) - commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR) + // TODO is this needed? dispatch('clearOpenedChats') }) state.mastoUserSocket.addEventListener('close', ({ detail: closeEvent }) => { @@ -106,14 +152,26 @@ const api = { const { code } = closeEvent if (ignoreCodes.has(code)) { console.debug(`Not restarting socket becasue of closure code ${code} is in ignore list`) + commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED) } else { console.warn(`MastoAPI websocket disconnected, restarting. CloseEvent code: ${code}`) - dispatch('startFetchingTimeline', { timeline: 'friends' }) - dispatch('startFetchingNotifications') - dispatch('startFetchingChats') - dispatch('restartMastoUserSocket') + setTimeout(() => { + dispatch('startMastoUserSocket') + }, retryTimeout(state.retryMultiplier)) + commit('incrementRetryMultiplier') + if (state.mastoUserSocketStatus !== WSConnectionStatus.ERROR) { + dispatch('startFetchingTimeline', { timeline: 'friends' }) + dispatch('startFetchingNotifications') + dispatch('startFetchingChats') + dispatch('pushGlobalNotice', { + level: 'error', + messageKey: 'timeline.socket_broke', + messageArgs: [code], + timeout: 5000 + }) + } + commit('setMastoUserSocketStatus', WSConnectionStatus.ERROR) } - commit('setMastoUserSocketStatus', WSConnectionStatus.CLOSED) dispatch('clearOpenedChats') }) resolve() @@ -122,15 +180,6 @@ const api = { } }) }, - restartMastoUserSocket ({ dispatch }) { - // This basically starts MastoAPI user socket and stops conventional - // fetchers when connection reestablished - return dispatch('startMastoUserSocket').then(() => { - dispatch('stopFetchingTimeline', { timeline: 'friends' }) - dispatch('stopFetchingNotifications') - dispatch('stopFetchingChats') - }) - }, stopMastoUserSocket ({ state, dispatch }) { dispatch('startFetchingTimeline', { timeline: 'friends' }) dispatch('startFetchingNotifications') @@ -156,6 +205,13 @@ const api = { if (!fetcher) return store.commit('removeFetcher', { fetcherName: timeline, fetcher }) }, + fetchTimeline (store, timeline, { ...rest }) { + store.state.backendInteractor.fetchTimeline({ + store, + timeline, + ...rest + }) + }, // Notifications startFetchingNotifications (store) { @@ -168,6 +224,12 @@ const api = { if (!fetcher) return store.commit('removeFetcher', { fetcherName: 'notifications', fetcher }) }, + fetchNotifications (store, { ...rest }) { + store.state.backendInteractor.fetchNotifications({ + store, + ...rest + }) + }, // Follow requests startFetchingFollowRequests (store) { @@ -193,12 +255,12 @@ const api = { initializeSocket ({ dispatch, commit, state, rootState }) { // Set up websocket connection const token = state.wsToken - if (rootState.instance.chatAvailable && typeof token !== 'undefined' && state.socket === null) { + if (rootState.instance.shoutAvailable && typeof token !== 'undefined' && state.socket === null) { const socket = new Socket('/socket', { params: { token } }) socket.connect() commit('setSocket', socket) - dispatch('initializeChat', socket) + dispatch('initializeShout', socket) } }, disconnectFromSocket ({ commit, state }) { diff --git a/src/modules/chat.js b/src/modules/chat.js @@ -1,33 +0,0 @@ -const chat = { - state: { - messages: [], - channel: { state: '' } - }, - mutations: { - setChannel (state, channel) { - state.channel = channel - }, - addMessage (state, message) { - state.messages.push(message) - state.messages = state.messages.slice(-19, 20) - }, - setMessages (state, messages) { - state.messages = messages.slice(-19, 20) - } - }, - actions: { - initializeChat (store, socket) { - const channel = socket.channel('chat:public') - channel.on('new_msg', (msg) => { - store.commit('addMessage', msg) - }) - channel.on('messages', ({ messages }) => { - store.commit('setMessages', messages) - }) - channel.join() - store.commit('setChannel', channel) - } - } -} - -export default chat diff --git a/src/modules/config.js b/src/modules/config.js @@ -21,6 +21,7 @@ export const defaultState = { customThemeSource: undefined, hideISP: false, hideInstanceWallpaper: false, + hideShoutbox: false, // bad name: actually hides posts of muted USERS hideMutedPosts: undefined, // instance default collapseMessageWithSubject: undefined, // instance default @@ -44,7 +45,7 @@ export const defaultState = { likes: true, repeats: true, moves: true, - emojiReactions: false, + emojiReactions: true, followRequest: true, chatMention: true }, @@ -54,6 +55,7 @@ export const defaultState = { interfaceLanguage: browserLocale, hideScopeNotice: false, useStreamingApi: false, + sidebarRight: undefined, // instance default scopeCopy: undefined, // instance default subjectLineBehavior: undefined, // instance default alwaysShowSubjectInput: undefined, // instance default @@ -67,7 +69,8 @@ export const defaultState = { greentext: undefined, // instance default hidePostStats: undefined, // instance default hideUserStats: undefined, // instance default - virtualScrolling: undefined // instance default + virtualScrolling: undefined, // instance default + sensitiveByDefault: undefined // instance default } // caching the instance default properties @@ -91,7 +94,8 @@ const config = { const { defaultConfig } = rootGetters return { ...defaultConfig, - ...state + // Do not override with undefined + ...Object.fromEntries(Object.entries(state).filter(([k, v]) => v !== undefined)) } } }, @@ -109,6 +113,20 @@ const config = { } }, actions: { + loadSettings ({ dispatch }, data) { + const knownKeys = new Set(Object.keys(defaultState)) + const presentKeys = new Set(Object.keys(data)) + const intersection = new Set() + for (let elem of presentKeys) { + if (knownKeys.has(elem)) { + intersection.add(elem) + } + } + + intersection.forEach( + name => dispatch('setOption', { name, value: data[name] }) + ) + }, setHighlight ({ commit, dispatch }, { user, color, type }) { commit('setHighlight', { user, color, type }) }, diff --git a/src/modules/instance.js b/src/modules/instance.js @@ -19,7 +19,6 @@ const defaultState = { defaultBanner: '/images/banner.png', background: '/static/aurora_borealis.jpg', collapseMessageWithSubject: false, - disableChat: false, greentext: false, hideFilteredStatuses: false, hideMutedPosts: false, @@ -43,6 +42,7 @@ const defaultState = { subjectLineBehavior: 'email', theme: 'pleroma-dark', virtualScrolling: true, + sensitiveByDefault: false, // Nasty stuff customEmoji: [], @@ -56,7 +56,7 @@ const defaultState = { knownDomains: [], // Feature-set, apparently, not everything here is reported... - chatAvailable: false, + shoutAvailable: false, pleromaChatMessagesAvailable: false, gopherAvailable: false, mediaProxyAvailable: false, @@ -106,7 +106,7 @@ const instance = { case 'name': dispatch('setPageTitle') break - case 'chatAvailable': + case 'shoutAvailable': if (value) { dispatch('initializeSocket') } diff --git a/src/modules/shout.js b/src/modules/shout.js @@ -0,0 +1,33 @@ +const shout = { + state: { + messages: [], + channel: { state: '' } + }, + mutations: { + setChannel (state, channel) { + state.channel = channel + }, + addMessage (state, message) { + state.messages.push(message) + state.messages = state.messages.slice(-19, 20) + }, + setMessages (state, messages) { + state.messages = messages.slice(-19, 20) + } + }, + actions: { + initializeShout (store, socket) { + const channel = socket.channel('chat:public') + channel.on('new_msg', (msg) => { + store.commit('addMessage', msg) + }) + channel.on('messages', ({ messages }) => { + store.commit('setMessages', messages) + }) + channel.join() + store.commit('setChannel', channel) + } + } +} + +export default shout diff --git a/src/modules/statuses.js b/src/modules/statuses.js @@ -13,7 +13,11 @@ import { omitBy } from 'lodash' import { set } from 'vue' -import { isStatusNotification, maybeShowNotification } from '../services/notification_utils/notification_utils.js' +import { + isStatusNotification, + isValidNotification, + maybeShowNotification +} from '../services/notification_utils/notification_utils.js' import apiService from '../services/api/api.service.js' const emptyTl = (userId = 0) => ({ @@ -310,8 +314,24 @@ const addNewStatuses = (state, { statuses, showImmediately = false, timeline, us } } +const updateNotificationsMinMaxId = (state, notification) => { + state.notifications.maxId = notification.id > state.notifications.maxId + ? notification.id + : state.notifications.maxId + state.notifications.minId = notification.id < state.notifications.minId + ? notification.id + : state.notifications.minId +} + const addNewNotifications = (state, { dispatch, notifications, older, visibleNotificationTypes, rootGetters, newNotificationSideEffects }) => { each(notifications, (notification) => { + // If invalid notification, update ids but don't add it to store + if (!isValidNotification(notification)) { + console.error('Invalid notification:', notification) + updateNotificationsMinMaxId(state, notification) + return + } + if (isStatusNotification(notification.type)) { notification.action = addStatusToGlobalStorage(state, notification.action).item notification.status = notification.status && addStatusToGlobalStorage(state, notification.status).item @@ -323,12 +343,7 @@ const addNewNotifications = (state, { dispatch, notifications, older, visibleNot // Only add a new notification if we don't have one for the same action if (!state.notifications.idStore.hasOwnProperty(notification.id)) { - state.notifications.maxId = notification.id > state.notifications.maxId - ? notification.id - : state.notifications.maxId - state.notifications.minId = notification.id < state.notifications.minId - ? notification.id - : state.notifications.minId + updateNotificationsMinMaxId(state, notification) state.notifications.data.push(notification) state.notifications.idStore[notification.id] = notification diff --git a/src/modules/users.js b/src/modules/users.js @@ -531,7 +531,7 @@ const users = { if (user.token) { store.dispatch('setWsToken', user.token) - // Initialize the chat socket. + // Initialize the shout socket. store.dispatch('initializeSocket') } @@ -547,9 +547,10 @@ const users = { } if (store.getters.mergedConfig.useStreamingApi) { - store.dispatch('enableMastoSockets').catch((error) => { + store.dispatch('fetchTimeline', 'friends', { since: null }) + store.dispatch('fetchNotifications', { since: null }) + store.dispatch('enableMastoSockets', true).catch((error) => { console.error('Failed initializing MastoAPI Streaming socket', error) - startPolling() }).then(() => { store.dispatch('fetchChats', { latest: true }) setTimeout(() => store.dispatch('setNotificationsSilence', false), 10000) diff --git a/src/services/api/api.service.js b/src/services/api/api.service.js @@ -1152,6 +1152,7 @@ export const ProcessedWS = ({ // 1000 = Normal Closure eventTarget.close = () => { socket.close(1000, 'Shutting down socket') } + eventTarget.getState = () => socket.readyState return eventTarget } @@ -1183,7 +1184,10 @@ export const handleMastoWS = (wsEvent) => { export const WSConnectionStatus = Object.freeze({ 'JOINED': 1, 'CLOSED': 2, - 'ERROR': 3 + 'ERROR': 3, + 'DISABLED': 4, + 'STARTING': 5, + 'STARTING_INITIAL': 6 }) const chats = ({ credentials }) => { diff --git a/src/services/backend_interactor_service/backend_interactor_service.js b/src/services/backend_interactor_service/backend_interactor_service.js @@ -1,17 +1,25 @@ import apiService, { getMastodonSocketURI, ProcessedWS } from '../api/api.service.js' -import timelineFetcherService from '../timeline_fetcher/timeline_fetcher.service.js' +import timelineFetcher from '../timeline_fetcher/timeline_fetcher.service.js' import notificationsFetcher from '../notifications_fetcher/notifications_fetcher.service.js' import followRequestFetcher from '../../services/follow_request_fetcher/follow_request_fetcher.service' const backendInteractorService = credentials => ({ startFetchingTimeline ({ timeline, store, userId = false, tag }) { - return timelineFetcherService.startFetching({ timeline, store, credentials, userId, tag }) + return timelineFetcher.startFetching({ timeline, store, credentials, userId, tag }) + }, + + fetchTimeline (args) { + return timelineFetcher.fetchAndUpdate({ ...args, credentials }) }, startFetchingNotifications ({ store }) { return notificationsFetcher.startFetching({ store, credentials }) }, + fetchNotifications (args) { + return notificationsFetcher.fetchAndUpdate({ ...args, credentials }) + }, + startFetchingFollowRequests ({ store }) { return followRequestFetcher.startFetching({ store, credentials }) }, diff --git a/src/services/entity_normalizer/entity_normalizer.service.js b/src/services/entity_normalizer/entity_normalizer.service.js @@ -205,7 +205,7 @@ export const parseUser = (data) => { // Convert punycode to unicode for UI output.screen_name_ui = output.screen_name - if (output.screen_name.includes('@')) { + if (output.screen_name && output.screen_name.includes('@')) { const parts = output.screen_name.split('@') let unicodeDomain = punycode.toUnicode(parts[1]) if (unicodeDomain !== parts[1]) { diff --git a/src/services/export_import/export_import.js b/src/services/export_import/export_import.js @@ -0,0 +1,55 @@ +export const newExporter = ({ + filename = 'data', + getExportedObject +}) => ({ + exportData () { + const stringified = JSON.stringify(getExportedObject(), null, 2) // Pretty-print and indent with 2 spaces + + // Create an invisible link with a data url and simulate a click + const e = document.createElement('a') + e.setAttribute('download', `${filename}.json`) + e.setAttribute('href', 'data:application/json;base64,' + window.btoa(stringified)) + e.style.display = 'none' + + document.body.appendChild(e) + e.click() + document.body.removeChild(e) + } +}) + +export const newImporter = ({ + onImport, + onImportFailure, + validator = () => true +}) => ({ + importData () { + const filePicker = document.createElement('input') + filePicker.setAttribute('type', 'file') + filePicker.setAttribute('accept', '.json') + + filePicker.addEventListener('change', event => { + if (event.target.files[0]) { + // eslint-disable-next-line no-undef + const reader = new FileReader() + reader.onload = ({ target }) => { + try { + const parsed = JSON.parse(target.result) + const validationResult = validator(parsed) + if (validationResult === true) { + onImport(parsed) + } else { + onImportFailure({ validationResult }) + } + } catch (error) { + onImportFailure({ error }) + } + } + reader.readAsText(event.target.files[0]) + } + }) + + document.body.appendChild(filePicker) + filePicker.click() + document.body.removeChild(filePicker) + } +}) diff --git a/src/services/file_type/file_type.service.js b/src/services/file_type/file_type.service.js @@ -2,6 +2,10 @@ // or the entire service could be just mimetype service that only operates // on mimetypes and not files. Currently the naming is confusing. const fileType = mimetype => { + if (mimetype.match(/flash/)) { + return 'flash' + } + if (mimetype.match(/text\/html/)) { return 'html' } diff --git a/src/services/notification_utils/notification_utils.js b/src/services/notification_utils/notification_utils.js @@ -22,6 +22,13 @@ const statusNotifications = ['like', 'mention', 'repeat', 'pleroma:emoji_reactio export const isStatusNotification = (type) => includes(statusNotifications, type) +export const isValidNotification = (notification) => { + if (isStatusNotification(notification.type) && !notification.status) { + return false + } + return true +} + const sortById = (a, b) => { const seqA = Number(a.id) const seqB = Number(b.id) diff --git a/src/services/notifications_fetcher/notifications_fetcher.service.js b/src/services/notifications_fetcher/notifications_fetcher.service.js @@ -5,7 +5,7 @@ const update = ({ store, notifications, older }) => { store.dispatch('addNewNotifications', { notifications, older }) } -const fetchAndUpdate = ({ store, credentials, older = false }) => { +const fetchAndUpdate = ({ store, credentials, older = false, since }) => { const args = { credentials } const { getters } = store const rootState = store.rootState || store.state @@ -25,8 +25,10 @@ const fetchAndUpdate = ({ store, credentials, older = false }) => { return fetchNotifications({ store, args, older }) } else { // fetch new notifications - if (timelineData.maxId !== Number.POSITIVE_INFINITY) { + if (since === undefined && timelineData.maxId !== Number.POSITIVE_INFINITY) { args['since'] = timelineData.maxId + } else if (since !== null) { + args['since'] = since } const result = fetchNotifications({ store, args, older }) diff --git a/src/services/ruffle_service/ruffle_service.js b/src/services/ruffle_service/ruffle_service.js @@ -0,0 +1,40 @@ +const createRuffleService = () => { + let ruffleInstance = null + + const getRuffle = () => new Promise((resolve, reject) => { + if (ruffleInstance) { + resolve(ruffleInstance) + return + } + // Ruffle needs these to be set before it's loaded + // https://github.com/ruffle-rs/ruffle/issues/3952 + window.RufflePlayer = {} + window.RufflePlayer.config = { + polyfills: false, + publicPath: '/static/ruffle' + } + + // Currently it's seems like a better way of loading ruffle + // because it needs the wasm publically accessible, but it needs path to it + // and filename of wasm seems to be pseudo-randomly generated (is it a hash?) + const script = document.createElement('script') + // see webpack config, using CopyPlugin to copy it from node_modules + // provided via ruffle-mirror + script.src = '/static/ruffle/ruffle.js' + script.type = 'text/javascript' + script.onerror = (e) => { reject(e) } + script.onabort = (e) => { reject(e) } + script.oncancel = (e) => { reject(e) } + script.onload = () => { + ruffleInstance = window.RufflePlayer + resolve(ruffleInstance) + } + document.body.appendChild(script) + }) + + return { getRuffle } +} + +const RuffleService = createRuffleService() + +export default RuffleService diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js @@ -380,7 +380,7 @@ export const colors2to3 = (colors) => { */ export const shadows2to3 = (shadows, opacity) => { return Object.entries(shadows).reduce((shadowsAcc, [slotName, shadowDefs]) => { - const isDynamic = ({ color }) => color.startsWith('--') + const isDynamic = ({ color = '#000000' }) => color.startsWith('--') const getOpacity = ({ color }) => opacity[getOpacitySlot(color.substring(2).split(',')[0])] const newShadow = shadowDefs.reduce((shadowAcc, def) => [ ...shadowAcc, diff --git a/src/services/theme_data/pleromafe.js b/src/services/theme_data/pleromafe.js @@ -616,6 +616,23 @@ export const SLOT_INHERITANCE = { textColor: true }, + alertSuccess: { + depends: ['cGreen'], + opacity: 'alert' + }, + alertSuccessText: { + depends: ['text'], + layer: 'alert', + variant: 'alertSuccess', + textColor: true + }, + alertSuccessPanelText: { + depends: ['panelText'], + layer: 'alertPanel', + variant: 'alertSuccess', + textColor: true + }, + alertNeutral: { depends: ['text'], opacity: 'alert' @@ -656,6 +673,17 @@ export const SLOT_INHERITANCE = { textColor: true }, + alertPopupSuccess: { + depends: ['alertSuccess'], + opacity: 'alertPopup' + }, + alertPopupSuccessText: { + depends: ['alertSuccessText'], + layer: 'popover', + variant: 'alertPopupSuccess', + textColor: true + }, + alertPopupNeutral: { depends: ['alertNeutral'], opacity: 'alertPopup' diff --git a/src/services/timeline_fetcher/timeline_fetcher.service.js b/src/services/timeline_fetcher/timeline_fetcher.service.js @@ -23,7 +23,8 @@ const fetchAndUpdate = ({ showImmediately = false, userId = false, tag = false, - until + until, + since }) => { const args = { timeline, credentials } const rootState = store.rootState || store.state @@ -35,7 +36,11 @@ const fetchAndUpdate = ({ if (older) { args['until'] = until || timelineData.minId } else { - args['since'] = timelineData.maxId + if (since === undefined) { + args['since'] = timelineData.maxId + } else if (since !== null) { + args['since'] = since + } } args['userId'] = userId diff --git a/static/config.json b/static/config.json @@ -2,7 +2,6 @@ "alwaysShowSubjectInput": true, "background": "/static/aurora_borealis.jpg", "collapseMessageWithSubject": false, - "disableChat": false, "greentext": false, "hideFilteredStatuses": false, "hideMutedPosts": false, diff --git a/yarn.lock b/yarn.lock @@ -936,6 +936,14 @@ "@nodelib/fs.scandir" "2.1.3" fastq "^1.6.0" +"@npmcli/move-file@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" + integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" + "@stylelint/postcss-css-in-js@^0.37.1": version "0.37.2" resolved "https://registry.yarnpkg.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.37.2.tgz#7e5a84ad181f4234a2480803422a47b8749af3d2" @@ -961,6 +969,11 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== +"@types/json-schema@^7.0.6": + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== + "@types/minimist@^1.2.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.0.tgz#69a23a3ad29caf0097f06eda59b361ee2f0639f6" @@ -1022,132 +1035,149 @@ dom-event-types "^1.0.0" lodash "^4.17.4" -"@webassemblyjs/ast@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" - dependencies: - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" - -"@webassemblyjs/floating-point-hex-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" - -"@webassemblyjs/helper-api-error@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" - -"@webassemblyjs/helper-buffer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" - -"@webassemblyjs/helper-code-frame@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" - dependencies: - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/helper-fsm@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" - -"@webassemblyjs/helper-module-context@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" - dependencies: - "@webassemblyjs/ast" "1.8.5" - mamacro "^0.0.3" - -"@webassemblyjs/helper-wasm-bytecode@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" - -"@webassemblyjs/helper-wasm-section@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - -"@webassemblyjs/ieee754@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" +"@webassemblyjs/ast@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" + integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== + dependencies: + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" + +"@webassemblyjs/floating-point-hex-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" + integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== + +"@webassemblyjs/helper-api-error@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" + integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== + +"@webassemblyjs/helper-buffer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" + integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== + +"@webassemblyjs/helper-code-frame@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" + integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== + dependencies: + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/helper-fsm@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" + integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== + +"@webassemblyjs/helper-module-context@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" + integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== + dependencies: + "@webassemblyjs/ast" "1.9.0" + +"@webassemblyjs/helper-wasm-bytecode@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" + integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== + +"@webassemblyjs/helper-wasm-section@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" + integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + +"@webassemblyjs/ieee754@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" + integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== dependencies: "@xtuc/ieee754" "^1.2.0" -"@webassemblyjs/leb128@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" +"@webassemblyjs/leb128@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" + integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== dependencies: "@xtuc/long" "4.2.2" -"@webassemblyjs/utf8@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" - -"@webassemblyjs/wasm-edit@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/helper-wasm-section" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-opt" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - "@webassemblyjs/wast-printer" "1.8.5" - -"@webassemblyjs/wasm-gen@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wasm-opt@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-buffer" "1.8.5" - "@webassemblyjs/wasm-gen" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - -"@webassemblyjs/wasm-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-wasm-bytecode" "1.8.5" - "@webassemblyjs/ieee754" "1.8.5" - "@webassemblyjs/leb128" "1.8.5" - "@webassemblyjs/utf8" "1.8.5" - -"@webassemblyjs/wast-parser@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" - dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/floating-point-hex-parser" "1.8.5" - "@webassemblyjs/helper-api-error" "1.8.5" - "@webassemblyjs/helper-code-frame" "1.8.5" - "@webassemblyjs/helper-fsm" "1.8.5" +"@webassemblyjs/utf8@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" + integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== + +"@webassemblyjs/wasm-edit@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" + integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/helper-wasm-section" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-opt" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + "@webassemblyjs/wast-printer" "1.9.0" + +"@webassemblyjs/wasm-gen@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" + integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wasm-opt@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" + integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-buffer" "1.9.0" + "@webassemblyjs/wasm-gen" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + +"@webassemblyjs/wasm-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" + integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-wasm-bytecode" "1.9.0" + "@webassemblyjs/ieee754" "1.9.0" + "@webassemblyjs/leb128" "1.9.0" + "@webassemblyjs/utf8" "1.9.0" + +"@webassemblyjs/wast-parser@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" + integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/floating-point-hex-parser" "1.9.0" + "@webassemblyjs/helper-api-error" "1.9.0" + "@webassemblyjs/helper-code-frame" "1.9.0" + "@webassemblyjs/helper-fsm" "1.9.0" "@xtuc/long" "4.2.2" -"@webassemblyjs/wast-printer@1.8.5": - version "1.8.5" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" +"@webassemblyjs/wast-printer@1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" + integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/wast-parser" "1.8.5" + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/wast-parser" "1.9.0" "@xtuc/long" "4.2.2" "@xtuc/ieee754@^1.2.0": @@ -1176,18 +1206,19 @@ accepts@~1.3.5: mime-types "~2.1.18" negotiator "0.6.1" -acorn-dynamic-import@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" - acorn-jsx@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" -acorn@^6.0.2, acorn@^6.0.5, acorn@^6.0.7: +acorn@^6.0.2, acorn@^6.0.7: version "6.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" +acorn@^6.4.1: + version "6.4.2" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" + integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== + after@0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" @@ -1222,6 +1253,11 @@ ajv-keywords@^3.1.0: version "3.4.0" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" +ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + ajv@^6.1.0, ajv@^6.9.1: version "6.10.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" @@ -1241,6 +1277,16 @@ ajv@^6.10.2: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -1307,6 +1353,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" + integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -1691,6 +1745,11 @@ binary-extensions@^1.0.0: version "1.12.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14" +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + blob@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" @@ -1699,9 +1758,10 @@ bluebird@^3.1.1, bluebird@^3.3.0: version "3.5.3" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" -bluebird@^3.5.3: - version "3.5.4" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714" +bluebird@^3.5.5: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: version "4.11.8" @@ -1767,7 +1827,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1909,25 +1969,50 @@ bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" -cacache@^11.3.2: - version "11.3.2" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" +cacache@^12.0.2: + version "12.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" + integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== dependencies: - bluebird "^3.5.3" + bluebird "^3.5.5" chownr "^1.1.1" figgy-pudding "^3.5.1" - glob "^7.1.3" + glob "^7.1.4" graceful-fs "^4.1.15" + infer-owner "^1.0.3" lru-cache "^5.1.1" mississippi "^3.0.0" mkdirp "^0.5.1" move-concurrently "^1.0.1" promise-inflight "^1.0.1" - rimraf "^2.6.2" + rimraf "^2.6.3" ssri "^6.0.1" unique-filename "^1.1.1" y18n "^4.0.0" +cacache@^15.0.5: + version "15.0.6" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.6.tgz#65a8c580fda15b59150fb76bf3f3a8e45d583099" + integrity sha512-g1WYDMct/jzW+JdWEyjaX2zoBkZ6ZT9VpOyp2I/VMtDsNLffNat3kqPFfi1eDRSK9/SuKGyORDHcQMcPF8sQ/w== + dependencies: + "@npmcli/move-file" "^1.0.1" + chownr "^2.0.0" + fs-minipass "^2.0.0" + glob "^7.1.4" + infer-owner "^1.0.4" + lru-cache "^6.0.0" + minipass "^3.1.1" + minipass-collect "^1.0.2" + minipass-flush "^1.0.5" + minipass-pipeline "^1.2.2" + mkdirp "^1.0.3" + p-map "^4.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^8.0.1" + tar "^6.0.2" + unique-filename "^1.1.1" + cache-base@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -2117,7 +2202,7 @@ chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" -chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3: +chokidar@^2.0.0, chokidar@^2.0.3: version "2.1.6" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.6.tgz#b6cad653a929e244ce8a834244164d241fa954c5" dependencies: @@ -2135,19 +2220,57 @@ chokidar@^2.0.0, chokidar@^2.0.2, chokidar@^2.0.3: optionalDependencies: fsevents "^1.2.7" +chokidar@^2.1.8: + version "2.1.8" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" + integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chokidar@^3.4.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.5.0" + optionalDependencies: + fsevents "~2.3.1" + chownr@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + chromatism@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/chromatism/-/chromatism-3.0.0.tgz#a7249d353c1e4f3577e444ac41171c4e2e624b12" -chrome-trace-event@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" - dependencies: - tslib "^1.9.0" +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== chromedriver@^87.0.1: version "87.0.4" @@ -2350,9 +2473,10 @@ commander@2.9.0: dependencies: graceful-readlink ">= 1.0.0" -commander@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== commondir@^1.0.1: version "1.0.1" @@ -2464,6 +2588,23 @@ copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" +copy-webpack-plugin@^6.4.1: + version "6.4.1" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.4.1.tgz#138cd9b436dbca0a6d071720d5414848992ec47e" + integrity sha512-MXyPCjdPVx5iiWyl40Va3JGh27bKzOTNY3NjUTrosD2q7dR/cLD0013uqJ3BpFbUjyONINjb6qI7nDIJujrMbA== + dependencies: + cacache "^15.0.5" + fast-glob "^3.2.4" + find-cache-dir "^3.3.1" + glob-parent "^5.1.1" + globby "^11.0.1" + loader-utils "^2.0.0" + normalize-path "^3.0.0" + p-limit "^3.0.2" + schema-utils "^3.0.0" + serialize-javascript "^5.0.1" + webpack-sources "^1.4.3" + core-js-compat@^3.4.7: version "3.4.8" resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.4.8.tgz#f72e6a4ed76437ea710928f44615f926a81607d5" @@ -3053,6 +3194,11 @@ emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" +emojis-list@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" + integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== + encodeurl@~1.0.1, encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -3100,12 +3246,13 @@ engine.io@~3.2.0: engine.io-parser "~2.1.0" ws "~3.3.1" -enhanced-resolve@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" +enhanced-resolve@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" + integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== dependencies: graceful-fs "^4.1.2" - memory-fs "^0.4.0" + memory-fs "^0.5.0" tapable "^1.0.0" ent@~2.2.0: @@ -3572,6 +3719,18 @@ fast-glob@^3.1.1: micromatch "^4.0.2" picomatch "^2.2.1" +fast-glob@^3.2.4: + version "3.2.5" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" + integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3687,7 +3846,7 @@ find-cache-dir@^0.1.1: mkdirp "^0.5.1" pkg-dir "^1.0.0" -find-cache-dir@^2.0.0: +find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" dependencies: @@ -3695,6 +3854,15 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" +find-cache-dir@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" + integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== + dependencies: + commondir "^1.0.1" + make-dir "^3.0.2" + pkg-dir "^4.1.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -3714,7 +3882,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -3799,6 +3967,13 @@ fs-minipass@^1.2.5: dependencies: minipass "^2.2.1" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -3819,6 +3994,11 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + ftp@~0.3.10: version "0.3.10" resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" @@ -3920,6 +4100,13 @@ glob-parent@^5.1.0: dependencies: is-glob "^4.0.1" +glob-parent@^5.1.1, glob-parent@~5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + glob@7.0.5: version "7.0.5" resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.5.tgz#b4202a69099bbb4d292a7c1b95b6682b67ebdc95" @@ -3974,6 +4161,18 @@ glob@^7.1.2: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.4: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-modules@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-2.0.0.tgz#997605ad2345f27f51539bea26574421215c7780" @@ -4404,6 +4603,11 @@ indexof@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" +infer-owner@^1.0.3, infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -4526,6 +4730,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -4647,7 +4858,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" dependencies: @@ -5130,9 +5341,10 @@ loader-fs-cache@^1.0.0: find-cache-dir "^0.1.1" mkdirp "0.5.1" -loader-runner@^2.3.0: +loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== loader-utils@^0.2.16, loader-utils@^0.2.3: version "0.2.17" @@ -5151,6 +5363,24 @@ loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.1.0: emojis-list "^2.0.0" json5 "^1.0.1" +loader-utils@^1.2.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" + integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^1.0.1" + +loader-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" + integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== + dependencies: + big.js "^5.2.2" + emojis-list "^3.0.0" + json5 "^2.1.2" + localforage@^1.5.0: version "1.7.3" resolved "https://registry.yarnpkg.com/localforage/-/localforage-1.7.3.tgz#0082b3ca9734679e1bd534995bdd3b24cf10f204" @@ -5504,6 +5734,13 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + lru-cache@~2.6.5: version "2.6.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" @@ -5515,9 +5752,12 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -mamacro@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" +make-dir@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" map-age-cleaner@^0.1.1: version "0.1.3" @@ -5596,13 +5836,21 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^2.0.0" -memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: +memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" dependencies: errno "^0.1.3" readable-stream "^2.0.1" +memory-fs@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" + integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -5668,7 +5916,7 @@ micromatch@^2.3.11: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" dependencies: @@ -5788,6 +6036,27 @@ minimist@^1.2.5: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== +minipass-collect@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" + integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== + dependencies: + minipass "^3.0.0" + +minipass-flush@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" + integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== + dependencies: + minipass "^3.0.0" + +minipass-pipeline@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" + integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== + dependencies: + minipass "^3.0.0" + minipass@^2.2.1, minipass@^2.3.4: version "2.3.5" resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" @@ -5795,12 +6064,27 @@ minipass@^2.2.1, minipass@^2.3.4: safe-buffer "^5.1.2" yallist "^3.0.0" +minipass@^3.0.0, minipass@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" + integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== + dependencies: + yallist "^4.0.0" + minizlib@^1.1.1: version "1.2.1" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" dependencies: minipass "^2.2.1" +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mississippi@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" @@ -5823,13 +6107,20 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: minimist "0.0.8" -mkdirp@^1.0.4: +mkdirp@^0.5.3: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -5951,6 +6242,11 @@ neo-async@^2.5.0: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" +neo-async@^2.6.1: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + netmask@~1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" @@ -5980,9 +6276,10 @@ no-case@^2.2.0: dependencies: lower-case "^1.1.1" -node-libs-browser@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.0.tgz#c72f60d9d46de08a940dedbb25f3ffa2f9bbaa77" +node-libs-browser@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" + integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== dependencies: assert "^1.1.1" browserify-zlib "^0.2.0" @@ -5994,7 +6291,7 @@ node-libs-browser@^2.0.0: events "^3.0.0" https-browserify "^1.0.0" os-browserify "^0.3.0" - path-browserify "0.0.0" + path-browserify "0.0.1" process "^0.11.10" punycode "^1.2.4" querystring-es3 "^0.2.0" @@ -6006,7 +6303,7 @@ node-libs-browser@^2.0.0: tty-browserify "0.0.0" url "^0.11.0" util "^0.11.0" - vm-browserify "0.0.4" + vm-browserify "^1.0.1" node-modules-regexp@^1.0.0: version "1.0.0" @@ -6085,7 +6382,7 @@ normalize-path@^2.0.1, normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -6321,6 +6618,13 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -6486,9 +6790,10 @@ pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" +path-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" + integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== path-dirname@^1.0.0: version "1.0.2" @@ -6572,6 +6877,11 @@ phoenix@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/phoenix/-/phoenix-1.4.0.tgz#9cec8dbd8cbc59ecd2147bc09ca8ceb56b860d75" +picomatch@^2.0.4: + version "2.2.3" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.3.tgz#465547f359ccc206d3c48e46a1bcb89bf7ee619d" + integrity sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg== + picomatch@^2.0.5, picomatch@^2.2.1: version "2.2.2" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" @@ -6620,6 +6930,13 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +pkg-dir@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + pngjs@^3.3.0: version "3.3.3" resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-3.3.3.tgz#85173703bde3edac8998757b96e5821d0966a21b" @@ -7196,9 +7513,10 @@ randomatic@^3.0.0: kind-of "^6.0.0" math-random "^1.0.1" -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" @@ -7335,6 +7653,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" + integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== + dependencies: + picomatch "^2.2.1" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -7630,12 +7955,19 @@ rfdc@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" -rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2: +rimraf@2.6.3, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" dependencies: glob "^7.1.3" +rimraf@^2.6.3: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== + dependencies: + glob "^7.1.3" + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -7650,6 +7982,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +ruffle-mirror@^2021.4.10: + version "2021.4.11" + resolved "https://registry.yarnpkg.com/ruffle-mirror/-/ruffle-mirror-2021.4.11.tgz#039940e0a68e6849259dbef6b54fb877ac4373e7" + integrity sha512-a3N2OkPCJauiHBloHoZgCn/mSUlybyb9Ps4ikPGgHUy8iXPy6qMqh62imvNDU07tBJc5Y0c5mRHBFJRgpMgEpA== + run-async@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" @@ -7725,6 +8062,15 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +schema-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" + integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== + dependencies: + "@types/json-schema" "^7.0.6" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + selenium-server@2.53.1: version "2.53.1" resolved "https://registry.yarnpkg.com/selenium-server/-/selenium-server-2.53.1.tgz#d681528812f3c2e0531a6b7e613e23bb02cce8a6" @@ -7742,7 +8088,7 @@ semver@^5.5.1: version "5.7.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" -semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -7769,9 +8115,19 @@ send@0.16.2: range-parser "~1.2.0" statuses "~1.4.0" -serialize-javascript@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.7.0.tgz#d6e0dfb2a3832a8c94468e6eb1db97e55a192a65" +serialize-javascript@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" + integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" + integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== + dependencies: + randombytes "^2.1.0" serve-static@1.13.2: version "1.13.2" @@ -8011,9 +8367,10 @@ source-map-support@^0.5.16: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@~0.5.10: - version "0.5.12" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" +source-map-support@~0.5.12: + version "0.5.19" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" + integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -8079,6 +8436,13 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +ssri@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" + integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== + dependencies: + minipass "^3.1.1" + state-toggle@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.3.tgz#e123b16a88e143139b09c6852221bc9815917dfe" @@ -8423,7 +8787,7 @@ table@^5.4.6: slice-ansi "^2.1.0" string-width "^3.0.0" -tapable@^1.0.0, tapable@^1.1.0: +tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -8439,6 +8803,18 @@ tar@^4: safe-buffer "^5.1.2" yallist "^3.0.2" +tar@^6.0.2: + version "6.1.0" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" + integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^3.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + tcp-port-used@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/tcp-port-used/-/tcp-port-used-1.0.1.tgz#46061078e2d38c73979a2c2c12b5a674e6689d70" @@ -8446,27 +8822,29 @@ tcp-port-used@^1.0.1: debug "4.1.0" is2 "2.0.1" -terser-webpack-plugin@^1.1.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.4.tgz#56f87540c28dd5265753431009388f473b5abba3" +terser-webpack-plugin@^1.4.3: + version "1.4.5" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" + integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== dependencies: - cacache "^11.3.2" - find-cache-dir "^2.0.0" + cacache "^12.0.2" + find-cache-dir "^2.1.0" is-wsl "^1.1.0" schema-utils "^1.0.0" - serialize-javascript "^1.7.0" + serialize-javascript "^4.0.0" source-map "^0.6.1" - terser "^3.17.0" - webpack-sources "^1.3.0" + terser "^4.1.2" + webpack-sources "^1.4.0" worker-farm "^1.7.0" -terser@^3.17.0: - version "3.17.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" +terser@^4.1.2: + version "4.8.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" + integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== dependencies: - commander "^2.19.0" + commander "^2.20.0" source-map "~0.6.1" - source-map-support "~0.5.10" + source-map-support "~0.5.12" text-encoding@0.6.4: version "0.6.4" @@ -8913,20 +9291,15 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - dependencies: - indexof "0.0.1" +vm-browserify@^1.0.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" + integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== void-elements@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" -vue-chat-scroll@^1.2.1: - version "1.3.5" - resolved "https://registry.yarnpkg.com/vue-chat-scroll/-/vue-chat-scroll-1.3.5.tgz#a5ee5bae5058f614818a96eac5ee3be4394a2f68" - vue-eslint-parser@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-5.0.0.tgz#00f4e4da94ec974b821a26ff0ed0f7a78402b8a1" @@ -9000,13 +9373,23 @@ vuex@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.0.1.tgz#e761352ebe0af537d4bb755a9b9dc4be3df7efd2" -watchpack@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" +watchpack-chokidar2@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" + integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== + dependencies: + chokidar "^2.1.8" + +watchpack@^1.7.4: + version "1.7.5" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" + integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== dependencies: - chokidar "^2.0.2" graceful-fs "^4.1.2" neo-async "^2.5.0" + optionalDependencies: + chokidar "^3.4.1" + watchpack-chokidar2 "^2.0.1" webpack-dev-middleware@^3.2.0, webpack-dev-middleware@^3.6.0: version "3.7.0" @@ -9042,41 +9425,49 @@ webpack-merge@^0.14.1: lodash.isplainobject "^3.2.0" lodash.merge "^3.3.2" -webpack-sources@^1.1.0, webpack-sources@^1.3.0: +webpack-sources@^1.1.0: version "1.3.0" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" dependencies: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@^4.0.0: - version "4.32.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.32.1.tgz#afe0cc7dd2b196e5a58f8d1d385311cfbb5d68c0" +webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: + version "1.4.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" + integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== dependencies: - "@webassemblyjs/ast" "1.8.5" - "@webassemblyjs/helper-module-context" "1.8.5" - "@webassemblyjs/wasm-edit" "1.8.5" - "@webassemblyjs/wasm-parser" "1.8.5" - acorn "^6.0.5" - acorn-dynamic-import "^4.0.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - chrome-trace-event "^1.0.0" - enhanced-resolve "^4.1.0" - eslint-scope "^4.0.0" + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.44.0: + version "4.46.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" + integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== + dependencies: + "@webassemblyjs/ast" "1.9.0" + "@webassemblyjs/helper-module-context" "1.9.0" + "@webassemblyjs/wasm-edit" "1.9.0" + "@webassemblyjs/wasm-parser" "1.9.0" + acorn "^6.4.1" + ajv "^6.10.2" + ajv-keywords "^3.4.1" + chrome-trace-event "^1.0.2" + enhanced-resolve "^4.5.0" + eslint-scope "^4.0.3" json-parse-better-errors "^1.0.2" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - micromatch "^3.1.8" - mkdirp "~0.5.0" - neo-async "^2.5.0" - node-libs-browser "^2.0.0" + loader-runner "^2.4.0" + loader-utils "^1.2.3" + memory-fs "^0.4.1" + micromatch "^3.1.10" + mkdirp "^0.5.3" + neo-async "^2.6.1" + node-libs-browser "^2.2.1" schema-utils "^1.0.0" - tapable "^1.1.0" - terser-webpack-plugin "^1.1.0" - watchpack "^1.5.0" - webpack-sources "^1.3.0" + tapable "^1.1.3" + terser-webpack-plugin "^1.4.3" + watchpack "^1.7.4" + webpack-sources "^1.4.1" whet.extend@~0.9.9: version "0.9.9" @@ -9180,6 +9571,11 @@ yallist@^3.0.0, yallist@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yaml@^1.7.2: version "1.10.0" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e" @@ -9228,3 +9624,8 @@ yauzl@^2.10.0: yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==