diff options
Diffstat (limited to 'app')
78 files changed, 917 insertions, 241 deletions
diff --git a/app/controllers/api/v1/push/subscriptions_controller.rb b/app/controllers/api/v1/push/subscriptions_controller.rb new file mode 100644 index 000000000..5038cc03c --- /dev/null +++ b/app/controllers/api/v1/push/subscriptions_controller.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class Api::V1::Push::SubscriptionsController < Api::BaseController + before_action -> { doorkeeper_authorize! :push } + before_action :require_user! + before_action :set_web_push_subscription + + def create + @web_subscription&.destroy! + + @web_subscription = ::Web::PushSubscription.create!( + endpoint: subscription_params[:endpoint], + key_p256dh: subscription_params[:keys][:p256dh], + key_auth: subscription_params[:keys][:auth], + data: data_params, + user_id: current_user.id, + access_token_id: doorkeeper_token.id + ) + + render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer + end + + def update + raise ActiveRecord::RecordNotFound if @web_subscription.nil? + + @web_subscription.update!(data: data_params) + + render json: @web_subscription, serializer: REST::WebPushSubscriptionSerializer + end + + def destroy + @web_subscription&.destroy! + render_empty + end + + private + + def set_web_push_subscription + @web_subscription = ::Web::PushSubscription.find_by(access_token_id: doorkeeper_token.id) + end + + def subscription_params + params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) + end + + def data_params + return {} if params[:data].blank? + params.require(:data).permit(alerts: [:follow, :favourite, :reblog, :mention]) + end +end diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index 249e7c186..fe8e42580 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -31,22 +31,23 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController endpoint: subscription_params[:endpoint], key_p256dh: subscription_params[:keys][:p256dh], key_auth: subscription_params[:keys][:auth], - data: data + data: data, + user_id: active_session.user_id, + access_token_id: active_session.access_token_id ) active_session.update!(web_push_subscription: web_subscription) - render json: web_subscription.as_payload + render json: web_subscription, serializer: REST::WebPushSubscriptionSerializer end def update params.require([:id]) web_subscription = ::Web::PushSubscription.find(params[:id]) - web_subscription.update!(data: data_params) - render json: web_subscription.as_payload + render json: web_subscription, serializer: REST::WebPushSubscriptionSerializer end private @@ -56,6 +57,6 @@ class Api::Web::PushSubscriptionsController < Api::Web::BaseController end def data_params - @data_params ||= params.require(:data).permit(:alerts) + @data_params ||= params.require(:data).permit(alerts: [:follow, :favourite, :reblog, :mention]) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 158c0c10e..27ebc3333 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -9,6 +9,7 @@ class ApplicationController < ActionController::Base include Localized include UserTrackingConcern + include SessionTrackingConcern helper_method :current_account helper_method :current_session diff --git a/app/controllers/concerns/session_tracking_concern.rb b/app/controllers/concerns/session_tracking_concern.rb new file mode 100644 index 000000000..45361b019 --- /dev/null +++ b/app/controllers/concerns/session_tracking_concern.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module SessionTrackingConcern + extend ActiveSupport::Concern + + UPDATE_SIGN_IN_HOURS = 24 + + included do + before_action :set_session_activity + end + + private + + def set_session_activity + return unless session_needs_update? + current_session.touch + end + + def session_needs_update? + !current_session.nil? && current_session.updated_at < UPDATE_SIGN_IN_HOURS.hours.ago + end +end diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb index 189e4072e..2e9f73bb8 100644 --- a/app/controllers/invites_controller.rb +++ b/app/controllers/invites_controller.rb @@ -9,9 +9,9 @@ class InvitesController < ApplicationController before_action :set_pack def index - authorize :invite, :create? + authorize :invite, :index? - @invites = Invite.where(user: current_user) + @invites = invites @invite = Invite.new(expires_in: 1.day.to_i) end @@ -24,13 +24,13 @@ class InvitesController < ApplicationController if @invite.save redirect_to invites_path else - @invites = Invite.where(user: current_user) + @invites = invites render :index end end def destroy - @invite = Invite.where(user: current_user).find(params[:id]) + @invite = invites.find(params[:id]) authorize @invite, :destroy? @invite.expire! redirect_to invites_path @@ -42,6 +42,10 @@ class InvitesController < ApplicationController use_pack 'settings' end + def invites + Invite.where(user: current_user) + end + def resource_params params.require(:invite).permit(:max_uses, :expires_in) end diff --git a/app/controllers/shares_controller.rb b/app/controllers/shares_controller.rb index 3cbaccb35..4624c29a6 100644 --- a/app/controllers/shares_controller.rb +++ b/app/controllers/shares_controller.rb @@ -16,6 +16,7 @@ class SharesController < ApplicationController def initial_state_params text = [params[:title], params[:text], params[:url]].compact.join(' ') + { settings: Web::Setting.find_by(user: current_user)&.data || {}, push_subscription: current_account.user.web_push_subscription(current_session), diff --git a/app/javascript/flavours/glitch/actions/notifications.js b/app/javascript/flavours/glitch/actions/notifications.js index cf27eff90..0d52100b9 100644 --- a/app/javascript/flavours/glitch/actions/notifications.js +++ b/app/javascript/flavours/glitch/actions/notifications.js @@ -3,6 +3,7 @@ import { List as ImmutableList } from 'immutable'; import IntlMessageFormat from 'intl-messageformat'; import { fetchRelationships } from './accounts'; import { defineMessages } from 'react-intl'; +import { unescapeHTML } from 'flavours/glitch/util/html'; export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; @@ -40,13 +41,6 @@ const fetchRelatedRelationships = (dispatch, notifications) => { } }; -const unescapeHTML = (html) => { - const wrapper = document.createElement('div'); - html = html.replace(/<br \/>|<br>|\n/g, ' '); - wrapper.innerHTML = html; - return wrapper.textContent; -}; - export function updateNotifications(notification, intlMessages, intlLocale) { return (dispatch, getState) => { const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); diff --git a/app/javascript/flavours/glitch/components/modal_root.js b/app/javascript/flavours/glitch/components/modal_root.js index 789e117c7..89f81f58e 100644 --- a/app/javascript/flavours/glitch/components/modal_root.js +++ b/app/javascript/flavours/glitch/components/modal_root.js @@ -6,6 +6,7 @@ export default class ModalRoot extends React.PureComponent { static propTypes = { children: PropTypes.node, onClose: PropTypes.func.isRequired, + noEsc: PropTypes.bool, }; state = { @@ -16,7 +17,7 @@ export default class ModalRoot extends React.PureComponent { handleKeyUp = (e) => { if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27) - && !!this.props.children && !this.props.props.noEsc) { + && !!this.props.children && !this.props.noEsc) { this.props.onClose(); } } diff --git a/app/javascript/flavours/glitch/features/account/components/header.js b/app/javascript/flavours/glitch/features/account/components/header.js index 15bd6b365..464c73c9a 100644 --- a/app/javascript/flavours/glitch/features/account/components/header.js +++ b/app/javascript/flavours/glitch/features/account/components/header.js @@ -111,7 +111,7 @@ export default class Header extends ImmutablePureComponent { {fields.map((pair, i) => ( <dl key={i}> <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} /> - <dd dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} title={pair.get('value')} /> + <dd dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} title={pair.get('value_plain')} /> </dl> ))} </div> diff --git a/app/javascript/flavours/glitch/features/ui/components/modal_root.js b/app/javascript/flavours/glitch/features/ui/components/modal_root.js index 320c039a4..cdb6dc9d0 100644 --- a/app/javascript/flavours/glitch/features/ui/components/modal_root.js +++ b/app/javascript/flavours/glitch/features/ui/components/modal_root.js @@ -59,7 +59,7 @@ export default class ModalRoot extends React.PureComponent { const visible = !!type; return ( - <Base onClose={onClose}> + <Base onClose={onClose} noEsc={props.noEsc}> {visible && ( <BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}> {(SpecificComponent) => <SpecificComponent {...props} onClose={onClose} />} diff --git a/app/javascript/flavours/glitch/reducers/accounts.js b/app/javascript/flavours/glitch/reducers/accounts.js index 23fbd999c..e8127a871 100644 --- a/app/javascript/flavours/glitch/reducers/accounts.js +++ b/app/javascript/flavours/glitch/reducers/accounts.js @@ -57,6 +57,7 @@ import { STORE_HYDRATE } from 'flavours/glitch/actions/store'; import emojify from 'flavours/glitch/util/emoji'; import { Map as ImmutableMap, fromJS } from 'immutable'; import escapeTextContentForBrowser from 'escape-html'; +import { unescapeHTML } from 'flavours/glitch/util/html'; const normalizeAccount = (state, account) => { account = { ...account }; @@ -74,6 +75,7 @@ const normalizeAccount = (state, account) => { ...pair, name_emojified: emojify(escapeTextContentForBrowser(pair.name)), value_emojified: emojify(pair.value), + value_plain: unescapeHTML(pair.value), })); } diff --git a/app/javascript/flavours/glitch/styles/about.scss b/app/javascript/flavours/glitch/styles/about.scss index 55f31266f..c9c0e3081 100644 --- a/app/javascript/flavours/glitch/styles/about.scss +++ b/app/javascript/flavours/glitch/styles/about.scss @@ -322,6 +322,11 @@ $small-breakpoint: 960px; border: 0; border-bottom: 1px solid rgba($ui-base-lighter-color, .6); margin: 20px 0; + + &.spacer { + height: 1px; + border: 0; + } } .container-alt { @@ -681,6 +686,54 @@ $small-breakpoint: 960px; margin-bottom: 0; } + .account { + border-bottom: 0; + padding: 0; + + &__display-name { + align-items: center; + display: flex; + margin-right: 5px; + } + + div.account__display-name { + &:hover { + .display-name strong { + text-decoration: none; + } + } + + .account__avatar { + cursor: default; + } + } + + &__avatar-wrapper { + margin-left: 0; + flex: 0 0 auto; + } + + &__avatar { + width: 44px; + height: 44px; + background-size: 44px 44px; + } + + .display-name { + font-size: 15px; + + &__account { + font-size: 14px; + } + } + } + + @media screen and (max-width: $small-breakpoint) { + .contact { + margin-top: 30px; + } + } + @media screen and (max-width: $column-breakpoint) { padding: 25px 20px; } @@ -816,6 +869,8 @@ $small-breakpoint: 960px; font-size: 16px; line-height: inherit; font-weight: inherit; + margin: 0; + padding: 0; } .column { @@ -852,8 +907,13 @@ $small-breakpoint: 960px; } &__features { + & > p { + padding-right: 60px; + } + .features-list { - margin: 40px 0 !important; + margin: 40px 0; + margin-top: 30px; } &__action { @@ -862,17 +922,11 @@ $small-breakpoint: 960px; } .features-list { - margin-top: 20px; - .features-list__row { display: flex; padding: 10px 0; justify-content: space-between; - &:first-child { - padding-top: 0; - } - .visual { flex: 0 0 auto; display: flex; @@ -898,6 +952,14 @@ $small-breakpoint: 960px; } } } + + @media screen and (min-width: $small-breakpoint) { + display: grid; + grid-gap: 30px; + grid-template-columns: 1fr 1fr; + grid-auto-columns: 50%; + grid-auto-rows: max-content; + } } .extended-description { diff --git a/app/javascript/flavours/glitch/styles/admin.scss b/app/javascript/flavours/glitch/styles/admin.scss index b077df145..1948a2a23 100644 --- a/app/javascript/flavours/glitch/styles/admin.scss +++ b/app/javascript/flavours/glitch/styles/admin.scss @@ -141,14 +141,15 @@ } hr { - margin: 20px 0; + width: 100%; + height: 0; border: 0; - background: transparent; - border-bottom: 1px solid $ui-base-color; + border-bottom: 1px solid rgba($ui-base-lighter-color, .6); + margin: 20px 0; - &.section-break { - margin: 30px 0; - border-bottom: 2px solid $ui-base-lighter-color; + &.spacer { + height: 1px; + border: 0; } } @@ -273,22 +274,6 @@ } } -.flavour-screen { - display: block; - margin: 10px auto; - max-width: 100%; -} - -.flavour-description { - display: block; - font-size: 16px; - margin: 10px 0; - - & > p { - margin: 10px 0; - } -} - .report-accounts { display: flex; flex-wrap: wrap; @@ -351,34 +336,9 @@ } } -.report-note__comment { - margin-bottom: 20px; -} - -.report-note__form { - margin-bottom: 20px; - - .report-note__textarea { - box-sizing: border-box; - border: 0; - padding: 7px 4px; - margin-bottom: 10px; - font-size: 16px; - color: $inverted-text-color; - display: block; - width: 100%; - outline: 0; - font-family: inherit; - resize: vertical; - } - - .report-note__buttons { - text-align: right; - } - - .report-note__button { - margin: 0 0 5px 5px; - } +.simple_form.new_report_note, +.simple_form.new_account_moderation_note { + max-width: 100%; } .batch-form-box { @@ -406,13 +366,6 @@ } } -.batch-checkbox, -.batch-checkbox-all { - display: flex; - align-items: center; - margin-right: 5px; -} - .back-link { margin-bottom: 10px; font-size: 14px; @@ -432,7 +385,7 @@ } .log-entry { - margin-bottom: 8px; + margin-bottom: 20px; line-height: 20px; &__header { @@ -530,9 +483,12 @@ } } +a.name-tag, .name-tag { display: flex; align-items: center; + text-decoration: none; + color: $secondary-text-color; .avatar { display: block; @@ -544,4 +500,52 @@ .username { font-weight: 500; } + + &.suspended { + .username { + text-decoration: line-through; + color: lighten($error-red, 12%); + } + + .avatar { + filter: grayscale(100%); + opacity: 0.8; + } + } +} + +.speech-bubble { + margin-bottom: 20px; + border-left: 4px solid $ui-highlight-color; + + &.positive { + border-left-color: $success-green; + } + + &.negative { + border-left-color: lighten($error-red, 12%); + } + + &__bubble { + padding: 16px; + padding-left: 14px; + font-size: 15px; + line-height: 20px; + border-radius: 4px 4px 4px 0; + position: relative; + font-weight: 500; + + a { + color: $darker-text-color; + } + } + + &__owner { + padding: 8px; + padding-left: 12px; + } + + time { + color: $dark-text-color; + } } diff --git a/app/javascript/flavours/glitch/styles/components/accounts.scss b/app/javascript/flavours/glitch/styles/components/accounts.scss index 5167a507e..dadfa6d57 100644 --- a/app/javascript/flavours/glitch/styles/components/accounts.scss +++ b/app/javascript/flavours/glitch/styles/components/accounts.scss @@ -32,7 +32,8 @@ .account__avatar-wrapper { float: left; - margin: 6px 16px 6px 6px; + margin-left: 12px; + margin-right: 12px; } .account__avatar { diff --git a/app/javascript/flavours/glitch/styles/containers.scss b/app/javascript/flavours/glitch/styles/containers.scss index 9d5ab66a4..c40b38a5a 100644 --- a/app/javascript/flavours/glitch/styles/containers.scss +++ b/app/javascript/flavours/glitch/styles/containers.scss @@ -60,6 +60,7 @@ } } +.card-standalone__body, .media-gallery-standalone__body { overflow: hidden; } diff --git a/app/javascript/flavours/glitch/styles/forms.scss b/app/javascript/flavours/glitch/styles/forms.scss index 0b12742a9..f97890187 100644 --- a/app/javascript/flavours/glitch/styles/forms.scss +++ b/app/javascript/flavours/glitch/styles/forms.scss @@ -280,6 +280,11 @@ code { .actions { margin-top: 30px; display: flex; + + &.actions--top { + margin-top: 0; + margin-bottom: 30px; + } } button, @@ -563,9 +568,27 @@ code { .post-follow-actions { text-align: center; - color: $ui-primary-color; + color: $darker-text-color; div { margin-bottom: 4px; } } + +.alternative-login { + margin-top: 20px; + margin-bottom: 20px; + + h4 { + font-size: 16px; + color: $primary-text-color; + text-align: center; + margin-bottom: 20px; + border: 0; + padding: 0; + } + + .button { + display: block; + } +} diff --git a/app/javascript/flavours/glitch/styles/rtl.scss b/app/javascript/flavours/glitch/styles/rtl.scss index 77420c84b..e9099a9e9 100644 --- a/app/javascript/flavours/glitch/styles/rtl.scss +++ b/app/javascript/flavours/glitch/styles/rtl.scss @@ -1,6 +1,22 @@ body.rtl { direction: rtl; + .column-header > button { + text-align: right; + padding-left: 0; + padding-right: 15px; + } + + .landing-page__logo { + margin-right: 0; + margin-left: 20px; + } + + .landing-page .features-list .features-list__row .visual { + margin-left: 0; + margin-right: 15px; + } + .column-link__icon, .column-header__icon { margin-right: 0; diff --git a/app/javascript/flavours/glitch/styles/stream_entries.scss b/app/javascript/flavours/glitch/styles/stream_entries.scss index b505c1580..40963ae84 100644 --- a/app/javascript/flavours/glitch/styles/stream_entries.scss +++ b/app/javascript/flavours/glitch/styles/stream_entries.scss @@ -6,7 +6,8 @@ background: $simple-background-color; .detailed-status.light, - .status.light { + .status.light, + .more.light { border-bottom: 1px solid $ui-secondary-color; animation: none; } @@ -65,6 +66,10 @@ } } + .media-gallery__gifv__label { + bottom: 9px; + } + .status.light { padding: 14px 14px 14px (48px + 14px * 2); position: relative; @@ -145,10 +150,10 @@ a.status__content__spoiler-link { color: $primary-text-color; - background: $ui-primary-color; + background: $ui-base-color; &:hover { - background: lighten($ui-primary-color, 8%); + background: lighten($ui-base-color, 8%); } } } @@ -215,10 +220,10 @@ a.status__content__spoiler-link { color: $primary-text-color; - background: $ui-primary-color; + background: $ui-base-color; &:hover { - background: lighten($ui-primary-color, 8%); + background: lighten($ui-base-color, 8%); } } } @@ -261,16 +266,8 @@ } .media-spoiler { - background: $ui-primary-color; - color: $inverted-text-color; - transition: all 100ms linear; - - &:hover, - &:active, - &:focus { - background: darken($ui-primary-color, 5%); - color: unset; - } + background: $ui-base-color; + color: $darker-text-color; } .pre-header { @@ -299,6 +296,17 @@ text-decoration: underline; } } + + .more { + color: $darker-text-color; + display: block; + padding: 14px; + text-align: center; + + &:not(:hover) { + text-decoration: none; + } + } } .embed { diff --git a/app/javascript/flavours/glitch/styles/tables.scss b/app/javascript/flavours/glitch/styles/tables.scss index c12d84f1c..fa876e603 100644 --- a/app/javascript/flavours/glitch/styles/tables.scss +++ b/app/javascript/flavours/glitch/styles/tables.scss @@ -11,6 +11,7 @@ vertical-align: top; border-top: 1px solid $ui-base-color; text-align: left; + background: darken($ui-base-color, 4%); } & > thead > tr > th { @@ -48,9 +49,38 @@ } } - &.inline-table > tbody > tr:nth-child(odd) > td, - &.inline-table > tbody > tr:nth-child(odd) > th { - background: transparent; + &.inline-table { + & > tbody > tr:nth-child(odd) { + & > td, + & > th { + background: transparent; + } + } + + & > tbody > tr:first-child { + & > td, + & > th { + border-top: 0; + } + } + } + + &.batch-table { + & > thead > tr > th { + background: $ui-base-color; + border-top: 1px solid darken($ui-base-color, 8%); + border-bottom: 1px solid darken($ui-base-color, 8%); + + &:first-child { + border-radius: 4px 0 0; + border-left: 1px solid darken($ui-base-color, 8%); + } + + &:last-child { + border-radius: 0 4px 0 0; + border-right: 1px solid darken($ui-base-color, 8%); + } + } } } @@ -63,6 +93,13 @@ samp { font-family: 'mastodon-font-monospace', monospace; } +button.table-action-link { + background: transparent; + border: 0; + font: inherit; +} + +button.table-action-link, a.table-action-link { text-decoration: none; display: inline-block; @@ -79,4 +116,77 @@ a.table-action-link { font-weight: 400; margin-right: 5px; } + + &:first-child { + padding-left: 0; + } +} + +.batch-table { + &__toolbar, + &__row { + display: flex; + + &__select { + box-sizing: border-box; + padding: 8px 16px; + cursor: pointer; + min-height: 100%; + + input { + margin-top: 8px; + } + } + + &__actions, + &__content { + padding: 8px 0; + padding-right: 16px; + flex: 1 1 auto; + } + } + + &__toolbar { + border: 1px solid darken($ui-base-color, 8%); + background: $ui-base-color; + border-radius: 4px 0 0; + height: 47px; + align-items: center; + + &__actions { + text-align: right; + padding-right: 16px - 5px; + } + } + + &__row { + border: 1px solid darken($ui-base-color, 8%); + border-top: 0; + background: darken($ui-base-color, 4%); + + &:hover { + background: darken($ui-base-color, 2%); + } + + &:nth-child(even) { + background: $ui-base-color; + + &:hover { + background: lighten($ui-base-color, 2%); + } + } + + &__content { + padding-top: 12px; + padding-bottom: 16px; + } + } + + .status__content { + padding-top: 0; + + strong { + font-weight: 700; + } + } } diff --git a/app/javascript/flavours/glitch/util/html.js b/app/javascript/flavours/glitch/util/html.js new file mode 100644 index 000000000..0b646ce58 --- /dev/null +++ b/app/javascript/flavours/glitch/util/html.js @@ -0,0 +1,6 @@ +export const unescapeHTML = (html) => { + const wrapper = document.createElement('div'); + html = html.replace(/<br \/>|<br>|\n/g, ' '); + wrapper.innerHTML = html; + return wrapper.textContent; +}; diff --git a/app/javascript/mastodon/features/compose/components/upload.js b/app/javascript/mastodon/features/compose/components/upload.js index 61b2d19e0..bfa2b4727 100644 --- a/app/javascript/mastodon/features/compose/components/upload.js +++ b/app/javascript/mastodon/features/compose/components/upload.js @@ -77,7 +77,7 @@ export default class Upload extends ImmutablePureComponent { {({ scale }) => ( <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}> <div className={classNames('compose-form__upload__actions', { active })}> - <button className='icon-button' onClick={this.handleUndoClick}><i className='fa fa-times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Undo' /></button> + <button className='icon-button' onClick={this.handleUndoClick}><i className='fa fa-times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button> {media.get('type') === 'image' && <button className='icon-button' onClick={this.handleFocalPointClick}><i className='fa fa-crosshairs' /> <FormattedMessage id='upload_form.focus' defaultMessage='Crop' /></button>} </div> diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index d531d8fd5..a4f27cd31 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "حظر @{name}", "account.block_domain": "إخفاء كل شيئ قادم من إسم النطاق {domain}", "account.blocked": "محظور", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 971475114..dd747ab3b 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Блокирай", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index f2e3699d5..22c5453ca 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Bloca @{name}", "account.block_domain": "Amaga-ho tot de {domain}", "account.blocked": "Bloquejat", diff --git a/app/javascript/mastodon/locales/co.json b/app/javascript/mastodon/locales/co.json index 2d7427c55..9d8d950a8 100644 --- a/app/javascript/mastodon/locales/co.json +++ b/app/javascript/mastodon/locales/co.json @@ -1,9 +1,10 @@ { + "account.badges.bot": "Bot", "account.block": "Bluccà @{name}", "account.block_domain": "Piattà tuttu da {domain}", "account.blocked": "Bluccatu", "account.direct": "Missaghju direttu @{name}", - "account.disclaimer_full": "Information below may reflect the user's profile incompletely.", + "account.disclaimer_full": "Ghjè pussibule chì l’infurmazione quì sottu ùn rifletta micca u prufile sanu di l’utilizatore.", "account.domain_blocked": "Duminiu piattatu", "account.edit_profile": "Mudificà u prufile", "account.follow": "Siguità", @@ -28,7 +29,7 @@ "account.unfollow": "Ùn siguità più", "account.unmute": "Ùn piattà più @{name}", "account.unmute_notifications": "Ùn piattà più nutificazione da @{name}", - "account.view_full_profile": "View full profile", + "account.view_full_profile": "Vede tuttu u prufile", "alert.unexpected.message": "Un prublemu inaspettatu hè accadutu.", "alert.unexpected.title": "Uups!", "boost_modal.combo": "Pudete appughjà nant'à {combo} per saltà quessa a prussima volta", @@ -78,7 +79,7 @@ "confirmations.delete.message": "Site sicuru·a che vulete supprime stu statutu?", "confirmations.delete_list.confirm": "Toglie", "confirmations.delete_list.message": "Site sicuru·a che vulete supprime sta lista?", - "confirmations.domain_block.confirm": "Piattà tuttu u duminiu?", + "confirmations.domain_block.confirm": "Piattà tuttu u duminiu", "confirmations.domain_block.message": "Site sicuru·a che vulete piattà tuttu à {domain}? Saria forse abbastanza di bluccà ò piattà alcuni conti da quallà.", "confirmations.mute.confirm": "Piattà", "confirmations.mute.message": "Site sicuru·a che vulete piattà @{name}?", @@ -107,13 +108,13 @@ "empty_column.home.public_timeline": "a linea pubblica", "empty_column.list": "Ùn c'hè ancu nunda quì. Quandu membri di sta lista manderanu novi statuti, i vidarete quì.", "empty_column.notifications": "Ùn avete ancu nisuna nutificazione. Interact with others to start the conversation.", - "empty_column.public": "Ùn c'hè nunda quì! Scrivete qualcosa in pubblicu o seguitate utilizatori d'altre istanze per empie a linea pubblica.", + "empty_column.public": "Ùn c'hè nunda quì! Scrivete qualcosa in pubblicu o seguitate utilizatori d'altre istanze per empie a linea pubblica", "follow_request.authorize": "Auturizà", "follow_request.reject": "Righjittà", "getting_started.appsshort": "Applicazione", "getting_started.faq": "FAQ", "getting_started.heading": "Per principià", - "getting_started.open_source_notice": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un bug, nant'à GitHub: {github}", + "getting_started.open_source_notice": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un bug, nant'à GitHub: {github}.", "getting_started.userguide": "Guida d'utilizazione", "home.column_settings.advanced": "Avanzati", "home.column_settings.basic": "Bàsichi", @@ -200,7 +201,7 @@ "onboarding.page_six.apps_available": "Ci sò {apps} dispunibule per iOS, Android è altre piattaforme.", "onboarding.page_six.github": "Mastodon ghjè un lugiziale liberu. Pudete cuntribuisce à u codice o a traduzione, o palisà un prublemu, nant'à {github}.", "onboarding.page_six.guidelines": "regule di a cumunità", - "onboarding.page_six.read_guidelines": "Ùn vi scurdate di leghje e {guidelines} di {domain}", + "onboarding.page_six.read_guidelines": "Ùn vi scurdate di leghje e {guidelines} di {domain}!", "onboarding.page_six.various_app": "applicazione pè u telefuninu", "onboarding.page_three.profile": "Pudete mudificà u prufile per cambia u ritrattu, a descrizzione è u nome affissatu. Ci sò ancu alcun'altre preferenze.", "onboarding.page_three.search": "Fate usu di l'area di ricerca per truvà altre persone è vede hashtag cum'è {illustration} o {introductions}. Per vede qualcunu ch'ùn hè micca nant'à st'istanza, cercate u so identificatore complettu (pare un'email).", @@ -231,13 +232,13 @@ "report.target": "Signalamentu", "search.placeholder": "Circà", "search_popout.search_format": "Ricerca avanzata", - "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.full_text": "I testi simplici rimandanu i statuti ch'avete scritti, aghjunti à i vostri favuriti, spartuti o induve quelli site mintuvatu·a, è ancu i cugnomi, nomi pubblichi è hashtag chì currispondenu.", "search_popout.tips.hashtag": "hashtag", "search_popout.tips.status": "statutu", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.text": "Un testu simplice rimanda i nomi pubblichi, cugnomi è hashtag", "search_popout.tips.user": "utilizatore", "search_results.accounts": "Ghjente", - "search_results.hashtags": "Hashtags", + "search_results.hashtags": "Hashtag", "search_results.statuses": "Statuti", "search_results.total": "{count, number} {count, plural, one {risultatu} other {risultati}}", "standalone.public_title": "Una vista di...", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index f442e0675..fe5bc4443 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "@{name} blocken", "account.block_domain": "Alles von {domain} verstecken", "account.blocked": "Blockiert", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index b400349d2..74bf97361 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -552,6 +552,10 @@ { "defaultMessage": "Domain hidden", "id": "account.domain_blocked" + }, + { + "defaultMessage": "Bot", + "id": "account.badges.bot" } ], "path": "app/javascript/mastodon/features/account/components/header.json" @@ -815,7 +819,7 @@ "id": "upload_form.description" }, { - "defaultMessage": "Undo", + "defaultMessage": "Delete", "id": "upload_form.undo" }, { @@ -1866,4 +1870,4 @@ ], "path": "app/javascript/mastodon/features/video/index.json" } -] \ No newline at end of file +] diff --git a/app/javascript/mastodon/locales/el.json b/app/javascript/mastodon/locales/el.json index 5b36348eb..d1421be76 100644 --- a/app/javascript/mastodon/locales/el.json +++ b/app/javascript/mastodon/locales/el.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Απόκλεισε τον/την @{name}", "account.block_domain": "Απόκρυψε τα πάντα από τον/την", "account.blocked": "Αποκλεισμένος/η", @@ -111,7 +112,7 @@ "follow_request.authorize": "Ενέκρινε", "follow_request.reject": "Απέρριψε", "getting_started.appsshort": "Εφαρμογές", - "getting_started.faq": "FAQ", + "getting_started.faq": "Συχνές Ερωτήσεις", "getting_started.heading": "Ξεκινώντας", "getting_started.open_source_notice": "Το Mastodon είναι ελεύθερο λογισμικό. Μπορείς να συνεισφέρεις ή να αναφέρεις ζητήματα στο GitHub στο {github}.", "getting_started.userguide": "Οδηγός Χρηστών", @@ -174,7 +175,7 @@ "notification.follow": "{name} followed you", "notification.mention": "{name} mentioned you", "notification.reblog": "{name} boosted your status", - "notifications.clear": "Clear notifications", + "notifications.clear": "Καθαρισμός ειδοποιήσεων", "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", "notifications.column_settings.alert": "Desktop notifications", "notifications.column_settings.favourite": "Favourites:", @@ -194,7 +195,7 @@ "onboarding.page_one.full_handle": "Your full handle", "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.", "onboarding.page_one.welcome": "Welcome to Mastodon!", - "onboarding.page_six.admin": "Your instance's admin is {admin}.", + "onboarding.page_six.admin": "Ο διαχειριστής του κόμβου σου είναι ο/η {admin}.", "onboarding.page_six.almost_done": "Almost done...", "onboarding.page_six.appetoot": "Bon Appetoot!", "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.", @@ -248,13 +249,13 @@ "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Favourite", - "status.load_more": "Load more", + "status.load_more": "Φόρτωσε περισσότερα", "status.media_hidden": "Media hidden", "status.mention": "Mention @{name}", "status.more": "More", "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", - "status.open": "Expand this status", + "status.open": "Διεύρυνε αυτή την κατάσταση", "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Boost", diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d8e69fd3c..35c5a86e9 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", @@ -286,7 +287,7 @@ "upload_button.label": "Add media", "upload_form.description": "Describe for the visually impaired", "upload_form.focus": "Crop", - "upload_form.undo": "Undo", + "upload_form.undo": "Delete", "upload_progress.label": "Uploading...", "video.close": "Close video", "video.exit_fullscreen": "Exit full screen", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 1ab6d2aa0..4aa3d80a3 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Bloki @{name}", "account.block_domain": "Kaŝi ĉion de {domain}", "account.blocked": "Blokita", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 41d7db9da..8aac2d77d 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Bloquear", "account.block_domain": "Ocultar todo de {domain}", "account.blocked": "Bloqueado", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 49cdf5630..e927547e3 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokeatu @{name}", "account.block_domain": "{domain}(e)ko guztia ezkutatu", "account.blocked": "Blokeatuta", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 99aba00c3..e19b1cd85 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "مسدودسازی @{name}", "account.block_domain": "پنهانسازی همه چیز از سرور {domain}", "account.blocked": "مسدودشده", @@ -125,17 +126,17 @@ "keyboard_shortcuts.boost": "برای بازبوقیدن", "keyboard_shortcuts.column": "برای برجستهکردن یک نوشته در یکی از ستونها", "keyboard_shortcuts.compose": "برای فعالکردن کادر نوشتهٔ تازه", - "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.description": "توضیح", "keyboard_shortcuts.down": "برای پایینرفتن در فهرست", - "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.enter": "برای گشودن نوشته", "keyboard_shortcuts.favourite": "برای پسندیدن", - "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.heading": "میانبرهای صفحهکلید", "keyboard_shortcuts.hotkey": "میانبر", "keyboard_shortcuts.legend": "برای نمایش این راهنما", "keyboard_shortcuts.mention": "برای نامبردن از نویسنده", "keyboard_shortcuts.reply": "برای پاسخدادن", "keyboard_shortcuts.search": "برای فعالکردن جستجو", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "برای نمایش/نهفتن نوشتهٔ پشت هشدار محتوا", "keyboard_shortcuts.toot": "برای آغاز یک بوق تازه", "keyboard_shortcuts.unfocus": "برای برداشتن توجه از نوشتن/جستجو", "keyboard_shortcuts.up": "برای بالا رفتن در فهرست", @@ -211,7 +212,7 @@ "privacy.direct.short": "مستقیم", "privacy.private.long": "تنها به پیگیران نشان بده", "privacy.private.short": "خصوصی", - "privacy.public.long": "در فهرست عمومی نشان بده", + "privacy.public.long": "نمایش در فهرست عمومی", "privacy.public.short": "عمومی", "privacy.unlisted.long": "عمومی، ولی فهرست نکن", "privacy.unlisted.short": "فهرستنشده", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 07d4d9aa5..cfe9c4f33 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Estä @{name}", "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}", "account.blocked": "Estetty", @@ -29,8 +30,8 @@ "account.unmute": "Poista käyttäjän @{name} mykistys", "account.unmute_notifications": "Poista mykistys käyttäjän @{name} ilmoituksilta", "account.view_full_profile": "Näytä koko profiili", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "Tapahtui odottamaton virhe.", + "alert.unexpected.title": "Hups!", "boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}", "bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.", "bundle_column_error.retry": "Yritä uudestaan", @@ -41,7 +42,7 @@ "column.blocks": "Estetyt käyttäjät", "column.community": "Paikallinen aikajana", "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "Piilotetut verkkotunnukset", "column.favourites": "Suosikit", "column.follow_requests": "Seuraamispyynnöt", "column.home": "Koti", @@ -59,7 +60,7 @@ "column_header.unpin": "Poista kiinnitys", "column_subheading.navigation": "Navigaatio", "column_subheading.settings": "Asetukset", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Tämä tuuttaus näkyy vain mainituille käyttäjille.", "compose_form.hashtag_warning": "Tämä tuuttaus ei näy hashtag-hauissa, koska se on listaamaton. Hashtagien avulla voi hakea vain julkisia tuuttauksia.", "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.", "compose_form.lock_disclaimer.lock": "lukittu", @@ -135,7 +136,7 @@ "keyboard_shortcuts.mention": "mainitse julkaisija", "keyboard_shortcuts.reply": "vastaa", "keyboard_shortcuts.search": "siirry hakukenttään", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "näytä/piilota sisältövaroituksella merkitty teksti", "keyboard_shortcuts.toot": "ala kirjoittaa uutta tuuttausta", "keyboard_shortcuts.unfocus": "siirry pois tekstikentästä tai hakukentästä", "keyboard_shortcuts.up": "siirry listassa ylöspäin", @@ -158,7 +159,7 @@ "navigation_bar.blocks": "Estetyt käyttäjät", "navigation_bar.community_timeline": "Paikallinen aikajana", "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "Piilotetut verkkotunnukset", "navigation_bar.edit_profile": "Muokkaa profiilia", "navigation_bar.favourites": "Suosikit", "navigation_bar.follow_requests": "Seuraamispyynnöt", @@ -242,10 +243,10 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "Kurkistus sisälle...", "status.block": "Estä @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "Peru buustaus", "status.cannot_reblog": "Tätä julkaisua ei voi buustata", "status.delete": "Poista", - "status.direct": "Direct message @{name}", + "status.direct": "Viesti käyttäjälle @{name}", "status.embed": "Upota", "status.favourite": "Tykkää", "status.load_more": "Lataa lisää", @@ -258,7 +259,7 @@ "status.pin": "Kiinnitä profiiliin", "status.pinned": "Kiinnitetty tuuttaus", "status.reblog": "Buustaa", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Buustaa alkuperäiselle yleisölle", "status.reblogged_by": "{name} buustasi", "status.reply": "Vastaa", "status.replyAll": "Vastaa ketjuun", @@ -276,7 +277,7 @@ "tabs_bar.home": "Koti", "tabs_bar.local_timeline": "Paikallinen", "tabs_bar.notifications": "Ilmoitukset", - "tabs_bar.search": "Search", + "tabs_bar.search": "Hae", "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.", "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän", "upload_button.label": "Lisää mediaa", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 16bf4033c..b0bda275c 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Bloquer @{name}", "account.block_domain": "Tout masquer venant de {domain}", "account.blocked": "Bloqué", @@ -187,7 +188,7 @@ "notifications.column_settings.sound": "Émettre un son", "onboarding.done": "Effectué", "onboarding.next": "Suivant", - "onboarding.page_five.public_timelines": "Le fil public global affiche les messages de toutes les personnes suivies par les membres de {domain}. Le fil public local est identique mais se limite aux membres de {domain}.", + "onboarding.page_five.public_timelines": "Le fil public global affiche les messages de toutes les personnes suivies par les membres de {domain}. Le fil public local est identique, mais se limite aux membres de {domain}.", "onboarding.page_four.home": "L’Accueil affiche les messages des personnes que vous suivez.", "onboarding.page_four.notifications": "La colonne de notification vous avertit lors d'une interaction avec vous.", "onboarding.page_one.federation": "Mastodon est un réseau de serveurs indépendants qui se joignent pour former un réseau social plus vaste. Nous appelons ces serveurs des instances.", @@ -225,7 +226,7 @@ "reply_indicator.cancel": "Annuler", "report.forward": "Transférer à {target}", "report.forward_hint": "Le compte provient d'un autre serveur. Envoyez également une copie anonyme du rapport ?", - "report.hint": "Le rapport sera envoyé aux modérateurs de votre instance. Vous pouvez expliquer pourquoi vous signalez ce compte ci-dessous :", + "report.hint": "Le rapport sera envoyé aux modérateurs de votre instance. Vous pouvez expliquer pourquoi vous signalez le compte ci-dessous :", "report.placeholder": "Commentaires additionnels", "report.submit": "Envoyer", "report.target": "Signalement", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 652ca31d1..29885b896 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Bloquear @{name}", "account.block_domain": "Ocultar calquer contido de {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 0ffbb14f3..a4bcba8ff 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "חסימת @{name}", "account.block_domain": "להסתיר הכל מהקהילה {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index c41cc3ea1..bd1dfac4c 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokiraj @{name}", "account.block_domain": "Sakrij sve sa {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index a0c186184..5059a45d4 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "@{name} letiltása", "account.block_domain": "Minden elrejtése innen: {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index a0442bad4..7fe723892 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Արգելափակել @{name}֊ին", "account.block_domain": "Թաքցնել ամենը հետեւյալ տիրույթից՝ {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 2fd922544..07b26a0a5 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokir @{name}", "account.block_domain": "Sembunyikan segalanya dari {domain}", "account.blocked": "Terblokir", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index ed45ee11e..a8ab72339 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokusar @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index a7ca62015..9d3486152 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blocca @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Bloccato", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index dbb4562de..1bdf34a81 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "@{name}さんをブロック", "account.block_domain": "{domain}全体を非表示", "account.blocked": "ブロック済み", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 2a2734673..e72a7dff9 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "@{name}을 차단", "account.block_domain": "{domain} 전체를 숨김", "account.blocked": "차단 됨", @@ -29,9 +30,9 @@ "account.unmute": "뮤트 해제", "account.unmute_notifications": "@{name}의 알림 뮤트 해제", "account.view_full_profile": "전체 프로필 보기", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", - "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.", + "alert.unexpected.message": "예측하지 못한 에러가 발생했습니다.", + "alert.unexpected.title": "앗!", + "boost_modal.combo": "{combo}를 누르면 다음부터 이 과정을 건너뛸 수 있습니다", "bundle_column_error.body": "컴포넌트를 불러오는 과정에서 문제가 발생했습니다.", "bundle_column_error.retry": "다시 시도", "bundle_column_error.title": "네트워크 에러", @@ -40,8 +41,8 @@ "bundle_modal_error.retry": "다시 시도", "column.blocks": "차단 중인 사용자", "column.community": "로컬 타임라인", - "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.direct": "다이렉트 메시지", + "column.domain_blocks": "숨겨진 도메인", "column.favourites": "즐겨찾기", "column.follow_requests": "팔로우 요청", "column.home": "홈", @@ -59,17 +60,17 @@ "column_header.unpin": "고정 해제", "column_subheading.navigation": "내비게이션", "column_subheading.settings": "설정", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "이 툿은 멘션 된 유저들에게만 보여집니다.", "compose_form.hashtag_warning": "이 툿은 어떤 해시태그로도 검색 되지 않습니다. 전체공개로 게시 된 툿만이 해시태그로 검색 될 수 있습니다.", "compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.", "compose_form.lock_disclaimer.lock": "비공개", "compose_form.placeholder": "지금 무엇을 하고 있나요?", "compose_form.publish": "툿", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.marked": "Media is marked as sensitive", - "compose_form.sensitive.unmarked": "Media is not marked as sensitive", - "compose_form.spoiler.marked": "Text is hidden behind warning", - "compose_form.spoiler.unmarked": "Text is not hidden", + "compose_form.sensitive.marked": "미디어가 열람주의로 설정되어 있습니다", + "compose_form.sensitive.unmarked": "미디어가 열람주의로 설정 되어 있지 않습니다", + "compose_form.spoiler.marked": "열람주의가 설정되어 있습니다", + "compose_form.spoiler.unmarked": "열람주의가 설정 되어 있지 않습니다", "compose_form.spoiler_placeholder": "경고", "confirmation_modal.cancel": "취소", "confirmations.block.confirm": "차단", @@ -101,13 +102,13 @@ "emoji_button.symbols": "기호", "emoji_button.travel": "여행과 장소", "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "아직 다이렉트 메시지가 없습니다. 다이렉트 메시지를 보내거나 받은 경우, 여기에 표시 됩니다.", "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.", "empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.", "empty_column.home.public_timeline": "연합 타임라인", "empty_column.list": "리스트에 아직 아무 것도 없습니다.", - "empty_column.notifications": "아직 알림이 없습니다. 다른 사람과 대화를 시작해 보세요!", - "empty_column.public": "여기엔 아직 아무 것도 없습니다! 공개적으로 무언가 포스팅하거나, 다른 인스턴스 유저를 팔로우 해서 가득 채워보세요!", + "empty_column.notifications": "아직 알림이 없습니다. 다른 사람과 대화를 시작해 보세요.", + "empty_column.public": "여기엔 아직 아무 것도 없습니다! 공개적으로 무언가 포스팅하거나, 다른 인스턴스의 유저를 팔로우 해서 채워보세요", "follow_request.authorize": "허가", "follow_request.reject": "거부", "getting_started.appsshort": "애플리케이션", @@ -135,7 +136,7 @@ "keyboard_shortcuts.mention": "멘션", "keyboard_shortcuts.reply": "답장", "keyboard_shortcuts.search": "검색창에 포커스", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "CW로 가려진 텍스트를 표시/비표시", "keyboard_shortcuts.toot": "새 툿 작성", "keyboard_shortcuts.unfocus": "작성창에서 포커스 해제", "keyboard_shortcuts.up": "리스트에서 위로 이동", @@ -157,8 +158,8 @@ "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?", "navigation_bar.blocks": "차단한 사용자", "navigation_bar.community_timeline": "로컬 타임라인", - "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.direct": "다이렉트 메시지", + "navigation_bar.domain_blocks": "숨겨진 도메인", "navigation_bar.edit_profile": "프로필 편집", "navigation_bar.favourites": "즐겨찾기", "navigation_bar.follow_requests": "팔로우 요청", @@ -177,12 +178,12 @@ "notifications.clear": "알림 지우기", "notifications.clear_confirmation": "정말로 알림을 삭제하시겠습니까?", "notifications.column_settings.alert": "데스크탑 알림", - "notifications.column_settings.favourite": "즐겨찾기", - "notifications.column_settings.follow": "새 팔로워", - "notifications.column_settings.mention": "답글", + "notifications.column_settings.favourite": "즐겨찾기:", + "notifications.column_settings.follow": "새 팔로워:", + "notifications.column_settings.mention": "답글:", "notifications.column_settings.push": "푸시 알림", "notifications.column_settings.push_meta": "이 장치", - "notifications.column_settings.reblog": "부스트", + "notifications.column_settings.reblog": "부스트:", "notifications.column_settings.show": "컬럼에 표시", "notifications.column_settings.sound": "효과음 재생", "onboarding.done": "완료", @@ -200,7 +201,7 @@ "onboarding.page_six.apps_available": "iOS、Android 또는 다른 플랫폼에서 사용할 수 있는 {apps}이 있습니다.", "onboarding.page_six.github": "Mastodon는 오픈 소스 소프트웨어입니다. 버그 보고나 기능 추가 요청, 기여는 {github}에서 할 수 있습니다.", "onboarding.page_six.guidelines": "커뮤니티 가이드라인", - "onboarding.page_six.read_guidelines": "{guidelines}을 확인하는 것을 잊지 마세요.", + "onboarding.page_six.read_guidelines": "{domain}의 {guidelines}을 확인하는 것을 잊지 마세요!", "onboarding.page_six.various_app": "다양한 모바일 애플리케이션", "onboarding.page_three.profile": "[프로필 편집] 에서 자기 소개나 이름을 변경할 수 있습니다. 또한 다른 설정도 변경할 수 있습니다.", "onboarding.page_three.search": "검색 바에서 {illustration} 나 {introductions} 와 같이 특정 해시태그가 달린 포스트를 보거나, 사용자를 찾을 수 있습니다.", @@ -242,10 +243,10 @@ "search_results.total": "{count, number}건의 결과", "standalone.public_title": "지금 이런 이야기를 하고 있습니다…", "status.block": "@{name} 차단", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "부스트 취소", "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", "status.delete": "삭제", - "status.direct": "Direct message @{name}", + "status.direct": "@{name}에게 다이렉트 메시지", "status.embed": "공유하기", "status.favourite": "즐겨찾기", "status.load_more": "더 보기", @@ -258,7 +259,7 @@ "status.pin": "고정", "status.pinned": "고정 된 툿", "status.reblog": "부스트", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "원래의 수신자들에게 부스트", "status.reblogged_by": "{name}님이 부스트 했습니다", "status.reply": "답장", "status.replyAll": "전원에게 답장", @@ -267,16 +268,16 @@ "status.sensitive_warning": "민감한 미디어", "status.share": "공유", "status.show_less": "숨기기", - "status.show_less_all": "Show less for all", + "status.show_less_all": "모두 접기", "status.show_more": "더 보기", - "status.show_more_all": "Show more for all", + "status.show_more_all": "모두 펼치기", "status.unmute_conversation": "이 대화의 뮤트 해제하기", "status.unpin": "고정 해제", "tabs_bar.federated_timeline": "연합", "tabs_bar.home": "홈", "tabs_bar.local_timeline": "로컬", "tabs_bar.notifications": "알림", - "tabs_bar.search": "Search", + "tabs_bar.search": "검색", "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.", "upload_area.title": "드래그 & 드롭으로 업로드", "upload_button.label": "미디어 추가", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index adc1d19a7..a6bfe36f9 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokkeer @{name}", "account.block_domain": "Negeer alles van {domain}", "account.blocked": "Geblokkeerd", @@ -50,7 +51,7 @@ "column.notifications": "Meldingen", "column.pins": "Vastgezette toots", "column.public": "Globale tijdlijn", - "column_back_button.label": "terug", + "column_back_button.label": "Terug", "column_header.hide_settings": "Instellingen verbergen", "column_header.moveLeft_settings": "Kolom naar links verplaatsen", "column_header.moveRight_settings": "Kolom naar rechts verplaatsen", @@ -61,7 +62,7 @@ "column_subheading.settings": "Instellingen", "compose_form.direct_message_warning": "Deze toot zal alleen zichtbaar zijn voor alle vermelde gebruikers.", "compose_form.hashtag_warning": "Deze toot valt niet onder een hashtag te bekijken, omdat deze niet op openbare tijdlijnen wordt getoond. Alleen openbare toots kunnen via hashtags gevonden worden.", - "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en toots zien die je alleen aan volgers hebt gericht.", + "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en kan de toots zien die je alleen aan jouw volgers hebt gericht.", "compose_form.lock_disclaimer.lock": "besloten", "compose_form.placeholder": "Wat wil je kwijt?", "compose_form.publish": "Toot", @@ -76,10 +77,10 @@ "confirmations.block.message": "Weet je het zeker dat je {name} wilt blokkeren?", "confirmations.delete.confirm": "Verwijderen", "confirmations.delete.message": "Weet je het zeker dat je deze toot wilt verwijderen?", - "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.confirm": "Verwijderen", "confirmations.delete_list.message": "Weet je zeker dat je deze lijst definitief wilt verwijderen?", "confirmations.domain_block.confirm": "Negeer alles van deze server", - "confirmations.domain_block.message": "Weet je het echt, echt zeker dat je alles van {domain} wil negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en gewenst.", + "confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wil negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en gepaster.", "confirmations.mute.confirm": "Negeren", "confirmations.mute.message": "Weet je het zeker dat je {name} wilt negeren?", "confirmations.unfollow.confirm": "Ontvolgen", @@ -106,13 +107,13 @@ "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.", "empty_column.home.public_timeline": "de globale tijdlijn", "empty_column.list": "Er is nog niks in deze lijst. Wanneer lijstleden nieuwe toots publiceren, zijn deze hier te zien.", - "empty_column.notifications": "Je hebt nog geen meldingen. Heb interactie met andere mensen om het gesprek aan te gaan.", + "empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.", "empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere servers om het te vullen", "follow_request.authorize": "Goedkeuren", "follow_request.reject": "Afkeuren", "getting_started.appsshort": "Apps", "getting_started.faq": "FAQ", - "getting_started.heading": "Beginnen", + "getting_started.heading": "Aan de slag", "getting_started.open_source_notice": "Mastodon is vrije software. Je kunt bijdragen of problemen melden op GitHub via {github}.", "getting_started.userguide": "Gebruikersgids", "home.column_settings.advanced": "Geavanceerd", @@ -131,7 +132,7 @@ "keyboard_shortcuts.favourite": "om als favoriet te markeren", "keyboard_shortcuts.heading": "Sneltoetsen", "keyboard_shortcuts.hotkey": "Sneltoets", - "keyboard_shortcuts.legend": "om deze legenda weer te geven", + "keyboard_shortcuts.legend": "om deze legenda te tonen", "keyboard_shortcuts.mention": "om de auteur te vermelden", "keyboard_shortcuts.reply": "om te reageren", "keyboard_shortcuts.search": "om het zoekvak te focussen", @@ -187,7 +188,7 @@ "notifications.column_settings.sound": "Geluid afspelen", "onboarding.done": "Klaar", "onboarding.next": "Volgende", - "onboarding.page_five.public_timelines": "De lokale tijdlijn toont openbare toots van iedereen op {domain}. De globale tijdlijn toont openbare toots van iedereen die door gebruikers van {domain} worden gevolgd, dus ook mensen van andere Mastodonservers. Dit zijn de openbare tijdlijnen en vormen een uitstekende manier om nieuwe mensen te ontdekken.", + "onboarding.page_five.public_timelines": "De lokale tijdlijn toont openbare toots van iedereen op {domain}. De globale tijdlijn toont openbare toots van iedereen die door gebruikers van {domain} worden gevolgd, dus ook mensen van andere Mastodonservers. Dit zijn de openbare tijdlijnen en vormen een uitstekende manier om nieuwe mensen te leren kennen.", "onboarding.page_four.home": "Deze tijdlijn laat toots zien van mensen die jij volgt.", "onboarding.page_four.notifications": "De kolom met meldingen toont alle interacties die je met andere Mastodongebruikers hebt.", "onboarding.page_one.federation": "Mastodon is een netwerk van onafhankelijke servers die samen een groot sociaal netwerk vormen.", @@ -231,7 +232,7 @@ "report.target": "Rapporteer {target}", "search.placeholder": "Zoeken", "search_popout.search_format": "Geavanceerd zoeken", - "search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken naar jouw toots, gebooste toots, favorieten en naar toots waarin jij bent vermeldt, en naar gebruikersnamen, weergavenamen en hashtags.", + "search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken in jouw toots, gebooste toots, favorieten en in toots waarin jij bent vermeldt, en tevens naar gebruikersnamen, weergavenamen en hashtags.", "search_popout.tips.hashtag": "hashtag", "search_popout.tips.status": "toot", "search_popout.tips.text": "Gebruik gewone tekst om te zoeken op weergavenamen, gebruikersnamen en hashtags", @@ -253,7 +254,7 @@ "status.mention": "Vermeld @{name}", "status.more": "Meer", "status.mute": "Negeer @{name}", - "status.mute_conversation": "Negeer conversatie", + "status.mute_conversation": "Negeer gesprek", "status.open": "Toot volledig tonen", "status.pin": "Aan profielpagina vastmaken", "status.pinned": "Vastgemaakte toot", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 0ee6d0722..05dae0630 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokkér @{name}", "account.block_domain": "Skjul alt fra {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index c4cb996cf..ead57d63f 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blocar @{name}", "account.block_domain": "Tot amagar del domeni {domain}", "account.blocked": "Blocat", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 6d6db7c82..120edcb1c 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokuj @{name}", "account.block_domain": "Blokuj wszystko z {domain}", "account.blocked": "Zablokowany", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 7f8690f91..2360c41e7 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Bloquear @{name}", "account.block_domain": "Esconder tudo de {domain}", "account.blocked": "Bloqueado", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index ce816dc41..3ac92dd57 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Bloquear @{name}", "account.block_domain": "Esconder tudo do domínio {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 8eeebaf73..599873439 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Блокировать", "account.block_domain": "Блокировать все с {domain}", "account.blocked": "Заблокирован(а)", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index d69648ccf..1cf7dee80 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokovať @{name}", "account.block_domain": "Ukryť všetko z {domain}", "account.blocked": "Blokovaný/á", diff --git a/app/javascript/mastodon/locales/sl.json b/app/javascript/mastodon/locales/sl.json new file mode 100644 index 000000000..202dc4bd0 --- /dev/null +++ b/app/javascript/mastodon/locales/sl.json @@ -0,0 +1,297 @@ +{ + "account.badges.bot": "Bot", + "account.block": "Blokiraj @{name}", + "account.block_domain": "Skrij vse iz {domain}", + "account.blocked": "Blokirano", + "account.direct": "Neposredno sporočilo @{name}", + "account.disclaimer_full": "Information below may reflect the user's profile incompletely.", + "account.domain_blocked": "Domain hidden", + "account.edit_profile": "Edit profile", + "account.follow": "Follow", + "account.followers": "Followers", + "account.follows": "Follows", + "account.follows_you": "Follows you", + "account.hide_reblogs": "Hide boosts from @{name}", + "account.media": "Media", + "account.mention": "Mention @{name}", + "account.moved_to": "{name} has moved to:", + "account.mute": "Mute @{name}", + "account.mute_notifications": "Mute notifications from @{name}", + "account.muted": "Muted", + "account.posts": "Toots", + "account.posts_with_replies": "Toots and replies", + "account.report": "Report @{name}", + "account.requested": "Awaiting approval. Click to cancel follow request", + "account.share": "Share @{name}'s profile", + "account.show_reblogs": "Show boosts from @{name}", + "account.unblock": "Unblock @{name}", + "account.unblock_domain": "Unhide {domain}", + "account.unfollow": "Unfollow", + "account.unmute": "Unmute @{name}", + "account.unmute_notifications": "Unmute notifications from @{name}", + "account.view_full_profile": "View full profile", + "alert.unexpected.message": "An unexpected error occurred.", + "alert.unexpected.title": "Oops!", + "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", + "column.blocks": "Blocked users", + "column.community": "Local timeline", + "column.direct": "Direct messages", + "column.domain_blocks": "Hidden domains", + "column.favourites": "Favourites", + "column.follow_requests": "Follow requests", + "column.home": "Home", + "column.lists": "Lists", + "column.mutes": "Muted users", + "column.notifications": "Notifications", + "column.pins": "Pinned toot", + "column.public": "Federated timeline", + "column_back_button.label": "Back", + "column_header.hide_settings": "Hide settings", + "column_header.moveLeft_settings": "Move column to the left", + "column_header.moveRight_settings": "Move column to the right", + "column_header.pin": "Pin", + "column_header.show_settings": "Show settings", + "column_header.unpin": "Unpin", + "column_subheading.navigation": "Navigation", + "column_subheading.settings": "Settings", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", + "compose_form.lock_disclaimer.lock": "locked", + "compose_form.placeholder": "What is on your mind?", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.marked": "Media is marked as sensitive", + "compose_form.sensitive.unmarked": "Media is not marked as sensitive", + "compose_form.spoiler.marked": "Text is hidden behind warning", + "compose_form.spoiler.unmarked": "Text is not hidden", + "compose_form.spoiler_placeholder": "Write your warning here", + "confirmation_modal.cancel": "Cancel", + "confirmations.block.confirm": "Block", + "confirmations.block.message": "Are you sure you want to block {name}?", + "confirmations.delete.confirm": "Delete", + "confirmations.delete.message": "Are you sure you want to delete this status?", + "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.domain_block.confirm": "Hide entire domain", + "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", + "confirmations.mute.confirm": "Mute", + "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "embed.instructions": "Embed this status on your website by copying the code below.", + "embed.preview": "Here is what it will look like:", + "emoji_button.activity": "Activity", + "emoji_button.custom": "Custom", + "emoji_button.flags": "Flags", + "emoji_button.food": "Food & Drink", + "emoji_button.label": "Insert emoji", + "emoji_button.nature": "Nature", + "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻", + "emoji_button.objects": "Objects", + "emoji_button.people": "People", + "emoji_button.recent": "Frequently used", + "emoji_button.search": "Search...", + "emoji_button.search_results": "Search results", + "emoji_button.symbols": "Symbols", + "emoji_button.travel": "Travel & Places", + "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.hashtag": "There is nothing in this hashtag yet.", + "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", + "empty_column.home.public_timeline": "the public timeline", + "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", + "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", + "follow_request.authorize": "Authorize", + "follow_request.reject": "Reject", + "getting_started.appsshort": "Apps", + "getting_started.faq": "FAQ", + "getting_started.heading": "Getting started", + "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.", + "getting_started.userguide": "User Guide", + "home.column_settings.advanced": "Advanced", + "home.column_settings.basic": "Basic", + "home.column_settings.filter_regex": "Filter out by regular expressions", + "home.column_settings.show_reblogs": "Show boosts", + "home.column_settings.show_replies": "Show replies", + "home.settings": "Column settings", + "keyboard_shortcuts.back": "to navigate back", + "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.column": "to focus a status in one of the columns", + "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.mention": "to mention author", + "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toot": "to start a brand new toot", + "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.up": "to move up in the list", + "lightbox.close": "Close", + "lightbox.next": "Next", + "lightbox.previous": "Previous", + "lists.account.add": "Add to list", + "lists.account.remove": "Remove from list", + "lists.delete": "Delete list", + "lists.edit": "Edit list", + "lists.new.create": "Add list", + "lists.new.title_placeholder": "New list title", + "lists.search": "Search among people you follow", + "lists.subheading": "Your lists", + "loading_indicator.label": "Loading...", + "media_gallery.toggle_visible": "Toggle visibility", + "missing_indicator.label": "Not found", + "missing_indicator.sublabel": "This resource could not be found", + "mute_modal.hide_notifications": "Hide notifications from this user?", + "navigation_bar.blocks": "Blocked users", + "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.direct": "Direct messages", + "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.favourites": "Favourites", + "navigation_bar.follow_requests": "Follow requests", + "navigation_bar.info": "Extended information", + "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", + "navigation_bar.lists": "Lists", + "navigation_bar.logout": "Logout", + "navigation_bar.mutes": "Muted users", + "navigation_bar.pins": "Pinned toots", + "navigation_bar.preferences": "Preferences", + "navigation_bar.public_timeline": "Federated timeline", + "notification.favourite": "{name} favourited your status", + "notification.follow": "{name} followed you", + "notification.mention": "{name} mentioned you", + "notification.reblog": "{name} boosted your status", + "notifications.clear": "Clear notifications", + "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", + "notifications.column_settings.alert": "Desktop notifications", + "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.follow": "New followers:", + "notifications.column_settings.mention": "Mentions:", + "notifications.column_settings.push": "Push notifications", + "notifications.column_settings.push_meta": "This device", + "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.show": "Show in column", + "notifications.column_settings.sound": "Play sound", + "onboarding.done": "Done", + "onboarding.next": "Next", + "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.", + "onboarding.page_four.home": "The home timeline shows posts from people you follow.", + "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.", + "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", + "onboarding.page_one.full_handle": "Your full handle", + "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.", + "onboarding.page_one.welcome": "Welcome to Mastodon!", + "onboarding.page_six.admin": "Your instance's admin is {admin}.", + "onboarding.page_six.almost_done": "Almost done...", + "onboarding.page_six.appetoot": "Bon Appetoot!", + "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.", + "onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.", + "onboarding.page_six.guidelines": "community guidelines", + "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!", + "onboarding.page_six.various_app": "mobile apps", + "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.", + "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.", + "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.", + "onboarding.skip": "Skip", + "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Post to mentioned users only", + "privacy.direct.short": "Direct", + "privacy.private.long": "Post to followers only", + "privacy.private.short": "Followers-only", + "privacy.public.long": "Post to public timelines", + "privacy.public.short": "Public", + "privacy.unlisted.long": "Do not show in public timelines", + "privacy.unlisted.short": "Unlisted", + "regeneration_indicator.label": "Loading…", + "regeneration_indicator.sublabel": "Your home feed is being prepared!", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "now", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Cancel", + "report.forward": "Forward to {target}", + "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", + "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:", + "report.placeholder": "Additional comments", + "report.submit": "Submit", + "report.target": "Report {target}", + "search.placeholder": "Search", + "search_popout.search_format": "Advanced search format", + "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.user": "user", + "search_results.accounts": "People", + "search_results.hashtags": "Hashtags", + "search_results.statuses": "Toots", + "search_results.total": "{count, number} {count, plural, one {result} other {results}}", + "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", + "status.cannot_reblog": "This post cannot be boosted", + "status.delete": "Delete", + "status.direct": "Direct message @{name}", + "status.embed": "Embed", + "status.favourite": "Favourite", + "status.load_more": "Load more", + "status.media_hidden": "Media hidden", + "status.mention": "Mention @{name}", + "status.more": "More", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Mute conversation", + "status.open": "Expand this status", + "status.pin": "Pin on profile", + "status.pinned": "Pinned toot", + "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", + "status.reblogged_by": "{name} boosted", + "status.reply": "Reply", + "status.replyAll": "Reply to thread", + "status.report": "Report @{name}", + "status.sensitive_toggle": "Click to view", + "status.sensitive_warning": "Sensitive content", + "status.share": "Share", + "status.show_less": "Show less", + "status.show_less_all": "Show less for all", + "status.show_more": "Show more", + "status.show_more_all": "Show more for all", + "status.unmute_conversation": "Unmute conversation", + "status.unpin": "Unpin from profile", + "tabs_bar.federated_timeline": "Federated", + "tabs_bar.home": "Home", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", + "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "upload_area.title": "Drag & drop to upload", + "upload_button.label": "Add media", + "upload_form.description": "Describe for the visually impaired", + "upload_form.focus": "Crop", + "upload_form.undo": "Undo", + "upload_progress.label": "Uploading...", + "video.close": "Close video", + "video.exit_fullscreen": "Exit full screen", + "video.expand": "Expand video", + "video.fullscreen": "Full screen", + "video.hide": "Hide video", + "video.mute": "Mute sound", + "video.pause": "Pause", + "video.play": "Play", + "video.unmute": "Unmute sound" +} diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index b1ea0d179..490b3a51a 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blokiraj korisnika @{name}", "account.block_domain": "Sakrij sve sa domena {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index aa978675f..b331f9f48 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Блокирај корисника @{name}", "account.block_domain": "Сакриј све са домена {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 4efe88a7e..36b200764 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Blockera @{name}", "account.block_domain": "Dölj allt från {domain}", "account.blocked": "Blockerad", diff --git a/app/javascript/mastodon/locales/te.json b/app/javascript/mastodon/locales/te.json index a56720fee..038ac6abb 100644 --- a/app/javascript/mastodon/locales/te.json +++ b/app/javascript/mastodon/locales/te.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 82b44fe30..8d24395af 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 056fbfe8f..a377e7b74 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Engelle @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 1a7b58789..613d1a00d 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "Заблокувати", "account.block_domain": "Заглушити {domain}", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/whitelist_sl.json b/app/javascript/mastodon/locales/whitelist_sl.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_sl.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index a3a4de0af..073dbe6cb 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "屏蔽 @{name}", "account.block_domain": "隐藏来自 {domain} 的内容", "account.blocked": "Blocked", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 7719e08a6..0c012aa7d 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的一切文章", "account.blocked": "封鎖", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 84ff25e03..0f4d04947 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -1,4 +1,5 @@ { + "account.badges.bot": "Bot", "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的一切貼文", "account.blocked": "已被封鎖的", diff --git a/app/lib/request.rb b/app/lib/request.rb index fc7d398e0..731bf7687 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -51,16 +51,17 @@ class Request end def headers - (@account ? @headers.merge('Signature' => signature) : @headers).reverse_merge('Accept-Encoding' => 'gzip').without(REQUEST_TARGET) + (@account ? @headers.merge('Signature' => signature) : @headers).without(REQUEST_TARGET) end private def set_common_headers! - @headers[REQUEST_TARGET] = "#{@verb} #{@url.path}" - @headers['User-Agent'] = user_agent - @headers['Host'] = @url.host - @headers['Date'] = Time.now.utc.httpdate + @headers[REQUEST_TARGET] = "#{@verb} #{@url.path}" + @headers['User-Agent'] = user_agent + @headers['Host'] = @url.host + @headers['Date'] = Time.now.utc.httpdate + @headers['Accept-Encoding'] = 'gzip' if @verb != :head end def set_digest! diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index 7f1ef5191..20ddbca53 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -41,6 +41,9 @@ module Remotable rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" nil + rescue Paperclip::Error => e + Rails.logger.debug "Error processing remote #{attachment_name}: #{e}" + nil end end diff --git a/app/models/user.rb b/app/models/user.rb index 82d165079..d5ca9be36 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -245,7 +245,7 @@ class User < ApplicationRecord end def web_push_subscription(session) - session.web_push_subscription.nil? ? nil : session.web_push_subscription.as_payload + session.web_push_subscription.nil? ? nil : session.web_push_subscription end def invite_code=(code) diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb index 1736106f7..df549c6d3 100644 --- a/app/models/web/push_subscription.rb +++ b/app/models/web/push_subscription.rb @@ -3,38 +3,51 @@ # # Table name: web_push_subscriptions # -# id :bigint(8) not null, primary key -# endpoint :string not null -# key_p256dh :string not null -# key_auth :string not null -# data :json -# created_at :datetime not null -# updated_at :datetime not null +# id :bigint(8) not null, primary key +# endpoint :string not null +# key_p256dh :string not null +# key_auth :string not null +# data :json +# created_at :datetime not null +# updated_at :datetime not null +# access_token_id :bigint(8) +# user_id :bigint(8) # -require 'webpush' - class Web::PushSubscription < ApplicationRecord + belongs_to :user, optional: true + belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', optional: true + has_one :session_activation def push(notification) - I18n.with_locale(session_activation.user.locale || I18n.default_locale) do + I18n.with_locale(associated_user.locale || I18n.default_locale) do push_payload(message_from(notification), 48.hours.seconds) end end def pushable?(notification) - data&.key?('alerts') && data['alerts'][notification.type.to_s] + data&.key?('alerts') && ActiveModel::Type::Boolean.new.cast(data['alerts'][notification.type.to_s]) end - def as_payload - payload = { id: id, endpoint: endpoint } - payload[:alerts] = data['alerts'] if data&.key?('alerts') - payload + def associated_user + return @associated_user if defined?(@associated_user) + + @associated_user = if user_id.nil? + session_activation.user + else + user + end end - def access_token - find_or_create_access_token.token + def associated_access_token + return @associated_access_token if defined?(@associated_access_token) + + @associated_access_token = if access_token_id.nil? + find_or_create_access_token.token + else + access_token + end end private diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 1d17e2b0a..eefd853ff 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -2,19 +2,15 @@ class InitialStateSerializer < ActiveModel::Serializer attributes :meta, :compose, :accounts, - :media_attachments, :settings, :push_subscription, + :media_attachments, :settings :max_toot_chars - has_many :custom_emojis, serializer: REST::CustomEmojiSerializer + has_one :push_subscription, serializer: REST::WebPushSubscriptionSerializer def max_toot_chars StatusLengthValidator::MAX_CHARS end - def custom_emojis - CustomEmoji.local.where(disabled: false) - end - def meta store = { streaming_api_base_url: Rails.configuration.x.streaming_api_base_url, diff --git a/app/serializers/rest/web_push_subscription_serializer.rb b/app/serializers/rest/web_push_subscription_serializer.rb new file mode 100644 index 000000000..7fd952a56 --- /dev/null +++ b/app/serializers/rest/web_push_subscription_serializer.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class REST::WebPushSubscriptionSerializer < ActiveModel::Serializer + attributes :id, :endpoint, :alerts, :server_key + + def alerts + object.data&.dig('alerts') || {} + end + + def server_key + Rails.configuration.x.vapid_public_key + end +end diff --git a/app/serializers/web/notification_serializer.rb b/app/serializers/web/notification_serializer.rb index e5524fe7a..31c703832 100644 --- a/app/serializers/web/notification_serializer.rb +++ b/app/serializers/web/notification_serializer.rb @@ -54,7 +54,7 @@ class Web::NotificationSerializer < ActiveModel::Serializer def access_token return if actions.empty? - current_push_subscription.access_token + current_push_subscription.associated_access_token end def message diff --git a/app/services/notify_service.rb b/app/services/notify_service.rb index ba086449c..6490d2735 100644 --- a/app/services/notify_service.rb +++ b/app/services/notify_service.rb @@ -9,6 +9,7 @@ class NotifyService < BaseService return if recipient.user.nil? || blocked? create_notification + push_notification if @notification.browserable? send_email if email_enabled? rescue ActiveRecord::RecordInvalid return @@ -101,25 +102,27 @@ class NotifyService < BaseService def create_notification @notification.save! - return unless @notification.browserable? + end + + def push_notification + return if @notification.activity.nil? + Redis.current.publish("timeline:#{@recipient.id}", Oj.dump(event: :notification, payload: InlineRenderer.render(@notification, @recipient, :notification))) send_push_notifications end def send_push_notifications - # HACK: Can be caused by quickly unfavouriting a status, since creating - # a favourite and creating a notification are not wrapped in a transaction. - return if @notification.activity.nil? - - sessions_with_subscriptions = @recipient.user.session_activations.where.not(web_push_subscription: nil) - sessions_with_subscriptions_ids = sessions_with_subscriptions.select { |session| session.web_push_subscription.pushable? @notification }.map(&:id) + subscriptions_ids = ::Web::PushSubscription.where(user_id: @recipient.user.id) + .select { |subscription| subscription.pushable?(@notification) } + .map(&:id) - WebPushNotificationWorker.push_bulk(sessions_with_subscriptions_ids) do |session_activation_id| - [session_activation_id, @notification.id] + ::Web::PushNotificationWorker.push_bulk(subscriptions_ids) do |subscription_id| + [subscription_id, @notification.id] end end def send_email + return if @notification.activity.nil? NotificationMailer.public_send(@notification.type, @recipient, @notification).deliver_later end diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index bbf2139a5..cfdd3a945 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -8,6 +8,7 @@ %meta{ name: 'robots', content: 'noindex' }/ %link{ rel: 'salmon', href: api_salmon_url(@account.id) }/ + %link{ rel: 'alternate', type: 'application/rss+xml', href: account_url(@account, format: 'rss') }/ %link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/ %link{ rel: 'alternate', type: 'application/activity+json', href: ActivityPub::TagManager.instance.uri_for(@account) }/ diff --git a/app/views/tags/_og.html.haml b/app/views/tags/_og.html.haml index 853a499ae..a7c289bcb 100644 --- a/app/views/tags/_og.html.haml +++ b/app/views/tags/_og.html.haml @@ -2,5 +2,5 @@ = opengraph 'og:url', tag_url(@tag) = opengraph 'og:type', 'website' = opengraph 'og:title', "##{@tag.name}" -= opengraph 'og:description', t('about.about_hashtag_html', hashtag: @tag.name) += opengraph 'og:description', strip_tags(t('about.about_hashtag_html', hashtag: @tag.name)) = opengraph 'twitter:card', 'summary' diff --git a/app/views/tags/show.html.haml b/app/views/tags/show.html.haml index c2202a9eb..2b46e58c7 100644 --- a/app/views/tags/show.html.haml +++ b/app/views/tags/show.html.haml @@ -2,6 +2,8 @@ = "##{@tag.name}" - content_for :header_tags do + %link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/ + %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) = render 'og' diff --git a/app/workers/web/push_notification_worker.rb b/app/workers/web/push_notification_worker.rb new file mode 100644 index 000000000..4a40e5c8b --- /dev/null +++ b/app/workers/web/push_notification_worker.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class Web::PushNotificationWorker + include Sidekiq::Worker + + sidekiq_options backtrace: true + + def perform(subscription_id, notification_id) + subscription = ::Web::PushSubscription.find(subscription_id) + notification = Notification.find(notification_id) + + subscription.push(notification) unless notification.activity.nil? + rescue Webpush::InvalidSubscription, Webpush::ExpiredSubscription + subscription.destroy! + rescue ActiveRecord::RecordNotFound + true + end +end diff --git a/app/workers/web_push_notification_worker.rb b/app/workers/web_push_notification_worker.rb deleted file mode 100644 index eacea04c3..000000000 --- a/app/workers/web_push_notification_worker.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -class WebPushNotificationWorker - include Sidekiq::Worker - - sidekiq_options backtrace: true - - def perform(session_activation_id, notification_id) - session_activation = SessionActivation.find(session_activation_id) - notification = Notification.find(notification_id) - - return if session_activation.web_push_subscription.nil? || notification.activity.nil? - - session_activation.web_push_subscription.push(notification) - rescue Webpush::InvalidSubscription, Webpush::ExpiredSubscription - # Subscription expiration is not currently implemented in any browser - - session_activation.web_push_subscription.destroy! - session_activation.update!(web_push_subscription: nil) - - true - rescue ActiveRecord::RecordNotFound - true - end -end |