mirror of
https://git.kescher.at/CatCatNya/catstodon.git
synced 2024-11-22 10:38:07 +01:00
Merge commit '3554c527954441fd924586a49c7d99a89101ac7e' into glitch-soc/merge-upstream
Conflicts: - `app/controllers/authorize_interactions_controller.rb`: Small conflict due to our theming system. - `streaming/index.js`: Upstream refactored part of the streaming server. We had some extra logic for handling local-only posts. Applied the refactor.
This commit is contained in:
commit
92fa9d34b0
131 changed files with 932 additions and 1197 deletions
|
@ -307,7 +307,7 @@ GEM
|
|||
activesupport (>= 5.1)
|
||||
haml (>= 4.0.6)
|
||||
railties (>= 5.1)
|
||||
haml_lint (0.49.1)
|
||||
haml_lint (0.49.2)
|
||||
haml (>= 4.0, < 6.2)
|
||||
parallel (~> 1.10)
|
||||
rainbow
|
||||
|
@ -583,7 +583,7 @@ GEM
|
|||
responders (3.1.0)
|
||||
actionpack (>= 5.2)
|
||||
railties (>= 5.2)
|
||||
rexml (3.2.5)
|
||||
rexml (3.2.6)
|
||||
rotp (6.2.2)
|
||||
rouge (4.1.2)
|
||||
rpam2 (4.0.2)
|
||||
|
|
12
app/chewy/instances_index.rb
Normal file
12
app/chewy/instances_index.rb
Normal file
|
@ -0,0 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class InstancesIndex < Chewy::Index
|
||||
settings index: { refresh_interval: '30s' }
|
||||
|
||||
index_scope ::Instance.searchable
|
||||
|
||||
root date_detection: false do
|
||||
field :domain, type: 'text', index_prefixes: { min_chars: 1 }
|
||||
field :accounts_count, type: 'long'
|
||||
end
|
||||
end
|
|
@ -15,7 +15,7 @@ class Api::V1::Instances::PeersController < Api::BaseController
|
|||
|
||||
def index
|
||||
cache_even_if_authenticated!
|
||||
render_with_cache(expires_in: 1.day) { Instance.where.not(domain: DomainBlock.select(:domain)).pluck(:domain) }
|
||||
render_with_cache(expires_in: 1.day) { Instance.searchable.pluck(:domain) }
|
||||
end
|
||||
|
||||
private
|
||||
|
|
45
app/controllers/api/v1/peers/search_controller.rb
Normal file
45
app/controllers/api/v1/peers/search_controller.rb
Normal file
|
@ -0,0 +1,45 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::Peers::SearchController < Api::BaseController
|
||||
before_action :require_enabled_api!
|
||||
before_action :set_domains
|
||||
|
||||
skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
|
||||
skip_around_action :set_locale
|
||||
|
||||
vary_by ''
|
||||
|
||||
def index
|
||||
cache_even_if_authenticated!
|
||||
render json: @domains
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_enabled_api!
|
||||
head 404 unless Setting.peers_api_enabled && !whitelist_mode?
|
||||
end
|
||||
|
||||
def set_domains
|
||||
return if params[:q].blank?
|
||||
|
||||
if Chewy.enabled?
|
||||
@domains = InstancesIndex.query(function_score: {
|
||||
query: {
|
||||
prefix: {
|
||||
domain: params[:q],
|
||||
},
|
||||
},
|
||||
|
||||
field_value_factor: {
|
||||
field: 'accounts_count',
|
||||
modifier: 'log2p',
|
||||
},
|
||||
}).limit(10).pluck(:domain)
|
||||
else
|
||||
domain = params[:q].strip
|
||||
domain = TagManager.instance.normalize_domain(domain)
|
||||
@domains = Instance.searchable.where(Instance.arel_table[:domain].matches("#{Instance.sanitize_sql_like(domain)}%", false, true)).limit(10).pluck(:domain)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,33 +3,19 @@
|
|||
class AuthorizeInteractionsController < ApplicationController
|
||||
include Authorization
|
||||
|
||||
layout 'modal'
|
||||
|
||||
before_action :authenticate_user!
|
||||
before_action :set_body_classes
|
||||
before_action :set_resource
|
||||
before_action :set_pack
|
||||
|
||||
def show
|
||||
if @resource.is_a?(Account)
|
||||
render :show
|
||||
redirect_to web_url("@#{@resource.pretty_acct}")
|
||||
elsif @resource.is_a?(Status)
|
||||
redirect_to web_url("@#{@resource.account.pretty_acct}/#{@resource.id}")
|
||||
else
|
||||
render :error
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
if @resource.is_a?(Account) && FollowService.new.call(current_account, @resource, with_rate_limit: true)
|
||||
render :success
|
||||
else
|
||||
render :error
|
||||
end
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
render :error
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_resource
|
||||
|
@ -62,12 +48,4 @@ class AuthorizeInteractionsController < ApplicationController
|
|||
def uri_param
|
||||
params[:uri] || params.fetch(:acct, '').delete_prefix('acct:')
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'modal-layout'
|
||||
end
|
||||
|
||||
def set_pack
|
||||
use_pack 'modal'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ class BackupsController < ApplicationController
|
|||
|
||||
def download
|
||||
case Paperclip::Attachment.default_options[:storage]
|
||||
when :s3
|
||||
when :s3, :azure
|
||||
redirect_to @backup.dump.expiring_url(10), allow_other_host: true
|
||||
when :fog
|
||||
if Paperclip::Attachment.default_options.dig(:fog_credentials, :openstack_temp_url_key).present?
|
||||
|
|
43
app/controllers/remote_interaction_helper_controller.rb
Normal file
43
app/controllers/remote_interaction_helper_controller.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoteInteractionHelperController < ApplicationController
|
||||
vary_by ''
|
||||
|
||||
skip_before_action :require_functional!
|
||||
skip_around_action :set_locale
|
||||
skip_before_action :update_user_sign_in
|
||||
|
||||
content_security_policy do |p|
|
||||
# We inherit the normal `script-src`
|
||||
|
||||
# Set every directive that does not have a fallback
|
||||
p.default_src :none
|
||||
p.form_action :none
|
||||
p.base_uri :none
|
||||
|
||||
# Disable every directive with a fallback to cut on response size
|
||||
p.base_uri false
|
||||
p.font_src false
|
||||
p.img_src false
|
||||
p.style_src false
|
||||
p.media_src false
|
||||
p.frame_src false
|
||||
p.manifest_src false
|
||||
p.connect_src false
|
||||
p.child_src false
|
||||
p.worker_src false
|
||||
|
||||
# Widen the directives that we do need
|
||||
p.frame_ancestors :self
|
||||
p.connect_src :https
|
||||
end
|
||||
|
||||
def index
|
||||
expires_in(5.minutes, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day)
|
||||
|
||||
response.headers['X-Frame-Options'] = 'SAMEORIGIN'
|
||||
response.headers['Referrer-Policy'] = 'no-referrer'
|
||||
|
||||
render layout: 'helper_frame'
|
||||
end
|
||||
end
|
|
@ -19,6 +19,7 @@ module WellKnown
|
|||
|
||||
def set_account
|
||||
username = username_from_resource
|
||||
|
||||
@account = begin
|
||||
if username == Rails.configuration.x.local_domain
|
||||
Account.representative
|
||||
|
|
|
@ -278,7 +278,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({
|
|||
modalProps: {
|
||||
type,
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('url'),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
|
|
@ -370,16 +370,33 @@ class Header extends ImmutablePureComponent {
|
|||
const acct = isLocal && domain ? `${account.get('acct')}@${domain}` : account.get('acct');
|
||||
const isIndexable = !account.get('noindex');
|
||||
|
||||
let badge;
|
||||
const badges = [];
|
||||
|
||||
if (account.get('bot')) {
|
||||
badge = (<div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Automated' /></div>);
|
||||
badges.push(
|
||||
<div key='bot-badge' className='account-role bot'>
|
||||
<Icon id='cogs' /> { ' ' }
|
||||
<FormattedMessage id='account.badges.bot' defaultMessage='Automated' />
|
||||
</div>
|
||||
);
|
||||
} else if (account.get('group')) {
|
||||
badge = (<div className='account-role group'><FormattedMessage id='account.badges.group' defaultMessage='Group' /></div>);
|
||||
} else {
|
||||
badge = null;
|
||||
badges.push(
|
||||
<div key='group-badge' className='account-role group'>
|
||||
<Icon id='users' /> { ' ' }
|
||||
<FormattedMessage id='account.badges.group' defaultMessage='Group' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
account.get('roles', []).forEach((role) => {
|
||||
badges.push(
|
||||
<div key={`role-badge-${role.get('id')}`} className={`account-role user-role-${account.getIn(['roles', 0, 'id'])}`}>
|
||||
<Icon id='circle' /> { ' ' }
|
||||
<span>{role.get('name')} ({domain})</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
{!(suspended || hidden || account.get('moved')) && account.getIn(['relationship', 'requested_by']) && <FollowRequestNoteContainer account={account} />}
|
||||
|
@ -414,13 +431,19 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
<div className='account__header__tabs__name'>
|
||||
<h1>
|
||||
<span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
|
||||
<span dangerouslySetInnerHTML={displayNameHtml} />
|
||||
<small>
|
||||
<span>@{acct}</span> {lockedIcon}
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{badges.length > 0 && (
|
||||
<div className='account__header__badges'>
|
||||
{badges}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!(suspended || hidden) && (
|
||||
<div className='account__header__extra'>
|
||||
<div className='account__header__bio' ref={this.setRef}>
|
||||
|
|
|
@ -83,7 +83,7 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
|
|||
modalProps: {
|
||||
type: 'follow',
|
||||
accountId: account.get('id'),
|
||||
url: account.get('url'),
|
||||
url: account.get('uri'),
|
||||
},
|
||||
}));
|
||||
},
|
||||
|
|
|
@ -281,13 +281,12 @@ class ComposeForm extends ImmutablePureComponent {
|
|||
autoFocus={autoFocus}
|
||||
lang={this.props.lang}
|
||||
>
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
|
||||
<div className='compose-form__modifiers'>
|
||||
<UploadFormContainer />
|
||||
<PollFormContainer />
|
||||
</div>
|
||||
</AutosuggestTextarea>
|
||||
<EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} />
|
||||
|
||||
<div className='compose-form__buttons-wrapper'>
|
||||
<div className='compose-form__buttons'>
|
||||
|
|
|
@ -139,10 +139,6 @@ class Search extends PureComponent {
|
|||
this.setState({ expanded: false, selectedOption: -1 });
|
||||
};
|
||||
|
||||
findTarget = () => {
|
||||
return this.searchForm;
|
||||
};
|
||||
|
||||
handleHashtagClick = () => {
|
||||
const { router } = this.context;
|
||||
const { value, onClickSearchResult } = this.props;
|
||||
|
|
|
@ -22,7 +22,7 @@ export const ExplorePrompt = () => (
|
|||
<p>
|
||||
<FormattedMessage
|
||||
id='home.explore_prompt.body'
|
||||
defaultMessage="Your home feed will have a mix of posts from the hashtags you've chosen to follow, the people you've chosen to follow, and the posts they boost. It's looking pretty quiet right now, so how about:"
|
||||
defaultMessage="Your home feed will have a mix of posts from the hashtags you've chosen to follow, the people you've chosen to follow, and the posts they boost. If that feels too quiet, you may want to:"
|
||||
/>
|
||||
</p>
|
||||
|
||||
|
|
|
@ -1,95 +1,296 @@
|
|||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { throttle, escapeRegExp } from 'lodash';
|
||||
|
||||
import { openModal, closeModal } from 'mastodon/actions/modal';
|
||||
import api from 'mastodon/api';
|
||||
import Button from 'mastodon/components/button';
|
||||
import { Icon } from 'mastodon/components/icon';
|
||||
import { registrationsOpen } from 'mastodon/initial_state';
|
||||
|
||||
const messages = defineMessages({
|
||||
loginPrompt: { id: 'interaction_modal.login.prompt', defaultMessage: 'Domain of your home server, e.g. mastodon.social' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, { accountId }) => ({
|
||||
displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']),
|
||||
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
onSignupClick() {
|
||||
dispatch(closeModal({
|
||||
modalType: undefined,
|
||||
ignoreFocus: false,
|
||||
}));
|
||||
dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
|
||||
dispatch(closeModal());
|
||||
dispatch(openModal('CLOSED_REGISTRATIONS'));
|
||||
},
|
||||
});
|
||||
|
||||
class Copypaste extends PureComponent {
|
||||
const PERSISTENCE_KEY = 'mastodon_home';
|
||||
|
||||
const isValidDomain = value => {
|
||||
const url = new URL('https:///path');
|
||||
url.hostname = value;
|
||||
return url.hostname === value;
|
||||
};
|
||||
|
||||
const valueToDomain = value => {
|
||||
// If the user starts typing an URL
|
||||
if (/^https?:\/\//.test(value)) {
|
||||
try {
|
||||
const url = new URL(value);
|
||||
|
||||
// Consider that if there is a path, the URL is more meaningful than a bare domain
|
||||
if (url.pathname.length > 1) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return url.host;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
// If the user writes their full handle including username
|
||||
} else if (value.includes('@')) {
|
||||
if (value.replace(/^@/, '').split('@').length > 2) {
|
||||
return undefined;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
return value;
|
||||
};
|
||||
|
||||
const addInputToOptions = (value, options) => {
|
||||
value = value.trim();
|
||||
|
||||
if (value.includes('.') && isValidDomain(value)) {
|
||||
return [value].concat(options.filter((x) => x !== value));
|
||||
}
|
||||
|
||||
return options;
|
||||
};
|
||||
|
||||
class LoginForm extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
value: PropTypes.string,
|
||||
resourceUrl: PropTypes.string,
|
||||
intl: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
copied: false,
|
||||
value: localStorage ? (localStorage.getItem(PERSISTENCE_KEY) || '') : '',
|
||||
expanded: false,
|
||||
selectedOption: -1,
|
||||
isLoading: false,
|
||||
isSubmitting: false,
|
||||
error: false,
|
||||
options: [],
|
||||
networkOptions: [],
|
||||
};
|
||||
|
||||
setRef = c => {
|
||||
this.input = c;
|
||||
};
|
||||
|
||||
handleInputClick = () => {
|
||||
this.setState({ copied: false });
|
||||
this.input.focus();
|
||||
this.input.select();
|
||||
this.input.setSelectionRange(0, this.input.value.length);
|
||||
handleChange = ({ target }) => {
|
||||
this.setState(state => ({ value: target.value, isLoading: true, error: false, options: addInputToOptions(target.value, state.networkOptions) }), () => this._loadOptions());
|
||||
};
|
||||
|
||||
handleButtonClick = () => {
|
||||
const { value } = this.props;
|
||||
navigator.clipboard.writeText(value);
|
||||
this.input.blur();
|
||||
this.setState({ copied: true });
|
||||
this.timeout = setTimeout(() => this.setState({ copied: false }), 700);
|
||||
handleMessage = (event) => {
|
||||
const { resourceUrl } = this.props;
|
||||
|
||||
if (event.origin !== window.origin || event.source !== this.iframeRef.contentWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data?.type === 'fetchInteractionURL-failure') {
|
||||
this.setState({ isSubmitting: false, error: true });
|
||||
} else if (event.data?.type === 'fetchInteractionURL-success') {
|
||||
if (/^https?:\/\//.test(event.data.template)) {
|
||||
if (localStorage) {
|
||||
localStorage.setItem(PERSISTENCE_KEY, event.data.uri_or_domain);
|
||||
}
|
||||
|
||||
window.location.href = event.data.template.replace('{uri}', encodeURIComponent(resourceUrl));
|
||||
} else {
|
||||
this.setState({ isSubmitting: false, error: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
componentDidMount () {
|
||||
window.addEventListener('message', this.handleMessage);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('message', this.handleMessage);
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
const { value } = this.state;
|
||||
|
||||
this.setState({ isSubmitting: true });
|
||||
|
||||
this.iframeRef.contentWindow.postMessage({
|
||||
type: 'fetchInteractionURL',
|
||||
uri_or_domain: value.trim(),
|
||||
}, window.origin);
|
||||
};
|
||||
|
||||
setIFrameRef = (iframe) => {
|
||||
this.iframeRef = iframe;
|
||||
}
|
||||
|
||||
handleFocus = () => {
|
||||
this.setState({ expanded: true });
|
||||
};
|
||||
|
||||
handleBlur = () => {
|
||||
this.setState({ expanded: false });
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
const { options, selectedOption } = this.state;
|
||||
|
||||
switch(e.key) {
|
||||
case 'ArrowDown':
|
||||
e.preventDefault();
|
||||
|
||||
if (options.length > 0) {
|
||||
this.setState({ selectedOption: Math.min(selectedOption + 1, options.length - 1) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
e.preventDefault();
|
||||
|
||||
if (options.length > 0) {
|
||||
this.setState({ selectedOption: Math.max(selectedOption - 1, -1) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Enter':
|
||||
e.preventDefault();
|
||||
|
||||
if (selectedOption === -1) {
|
||||
this.handleSubmit();
|
||||
} else if (options.length > 0) {
|
||||
this.setState({ value: options[selectedOption], error: false }, () => this.handleSubmit());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
handleOptionClick = e => {
|
||||
const index = Number(e.currentTarget.getAttribute('data-index'));
|
||||
const option = this.state.options[index];
|
||||
|
||||
e.preventDefault();
|
||||
this.setState({ selectedOption: index, value: option, error: false }, () => this.handleSubmit());
|
||||
};
|
||||
|
||||
_loadOptions = throttle(() => {
|
||||
const { value } = this.state;
|
||||
|
||||
const domain = valueToDomain(value.trim());
|
||||
|
||||
if (typeof domain === 'undefined') {
|
||||
this.setState({ options: [], networkOptions: [], isLoading: false, error: true });
|
||||
return;
|
||||
}
|
||||
|
||||
if (domain.length === 0) {
|
||||
this.setState({ options: [], networkOptions: [], isLoading: false });
|
||||
return;
|
||||
}
|
||||
|
||||
api().get('/api/v1/peers/search', { params: { q: domain } }).then(({ data }) => {
|
||||
if (!data) {
|
||||
data = [];
|
||||
}
|
||||
|
||||
this.setState((state) => ({ networkOptions: data, options: addInputToOptions(state.value, data), isLoading: false }));
|
||||
}).catch(() => {
|
||||
this.setState({ isLoading: false });
|
||||
});
|
||||
}, 200, { leading: true, trailing: true });
|
||||
|
||||
render () {
|
||||
const { value } = this.props;
|
||||
const { copied } = this.state;
|
||||
const { intl } = this.props;
|
||||
const { value, expanded, options, selectedOption, error, isSubmitting } = this.state;
|
||||
const domain = (valueToDomain(value) || '').trim();
|
||||
const domainRegExp = new RegExp(`(${escapeRegExp(domain)})`, 'gi');
|
||||
const hasPopOut = domain.length > 0 && options.length > 0;
|
||||
|
||||
return (
|
||||
<div className={classNames('copypaste', { copied })}>
|
||||
<input
|
||||
type='text'
|
||||
ref={this.setRef}
|
||||
value={value}
|
||||
readOnly
|
||||
onClick={this.handleInputClick}
|
||||
<div className={classNames('interaction-modal__login', { focused: expanded, expanded: hasPopOut, invalid: error })}>
|
||||
|
||||
<iframe
|
||||
ref={this.setIFrameRef}
|
||||
style={{display: 'none'}}
|
||||
src='/remote_interaction_helper'
|
||||
sandbox='allow-scripts allow-same-origin'
|
||||
title='remote interaction helper'
|
||||
/>
|
||||
|
||||
<button className='button' onClick={this.handleButtonClick}>
|
||||
{copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : <FormattedMessage id='copypaste.copy' defaultMessage='Copy' />}
|
||||
</button>
|
||||
<div className='interaction-modal__login__input'>
|
||||
<input
|
||||
ref={this.setRef}
|
||||
type='text'
|
||||
value={value}
|
||||
placeholder={intl.formatMessage(messages.loginPrompt)}
|
||||
aria-label={intl.formatMessage(messages.loginPrompt)}
|
||||
autoFocus
|
||||
onChange={this.handleChange}
|
||||
onFocus={this.handleFocus}
|
||||
onBlur={this.handleBlur}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
/>
|
||||
|
||||
<Button onClick={this.handleSubmit} disabled={isSubmitting}><FormattedMessage id='interaction_modal.login.action' defaultMessage='Take me home' /></Button>
|
||||
</div>
|
||||
|
||||
{hasPopOut && (
|
||||
<div className='search__popout'>
|
||||
<div className='search__popout__menu'>
|
||||
{options.map((option, i) => (
|
||||
<button key={option} onMouseDown={this.handleOptionClick} data-index={i} className={classNames('search__popout__menu__item', { selected: selectedOption === i })}>
|
||||
{option.split(domainRegExp).map((part, i) => (
|
||||
part.toLowerCase() === domain.toLowerCase() ? (
|
||||
<mark key={i}>
|
||||
{part}
|
||||
</mark>
|
||||
) : (
|
||||
<span key={i}>
|
||||
{part}
|
||||
</span>
|
||||
)
|
||||
))}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class InteractionModal extends PureComponent {
|
||||
const IntlLoginForm = injectIntl(LoginForm);
|
||||
|
||||
class InteractionModal extends React.PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
displayNameHtml: PropTypes.string,
|
||||
url: PropTypes.string,
|
||||
type: PropTypes.oneOf(['reply', 'reblog', 'favourite', 'follow']),
|
||||
onSignupClick: PropTypes.func.isRequired,
|
||||
signupUrl: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
handleSignupClick = () => {
|
||||
|
@ -97,7 +298,7 @@ class InteractionModal extends PureComponent {
|
|||
};
|
||||
|
||||
render () {
|
||||
const { url, type, displayNameHtml, signupUrl } = this.props;
|
||||
const { url, type, displayNameHtml } = this.props;
|
||||
|
||||
const name = <bdi dangerouslySetInnerHTML={{ __html: displayNameHtml }} />;
|
||||
|
||||
|
@ -130,13 +331,13 @@ class InteractionModal extends PureComponent {
|
|||
|
||||
if (registrationsOpen) {
|
||||
signupButton = (
|
||||
<a href={signupUrl} className='button button--block button-tertiary'>
|
||||
<a href='/auth/sign_up' className='link-button'>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</a>
|
||||
);
|
||||
} else {
|
||||
signupButton = (
|
||||
<button className='button button--block button-tertiary' onClick={this.handleSignupClick}>
|
||||
<button className='link-button' onClick={this.handleSignupClick}>
|
||||
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
|
||||
</button>
|
||||
);
|
||||
|
@ -146,22 +347,13 @@ class InteractionModal extends PureComponent {
|
|||
<div className='modal-root__modal interaction-modal'>
|
||||
<div className='interaction-modal__lead'>
|
||||
<h3><span className='interaction-modal__icon'>{icon}</span> {title}</h3>
|
||||
<p>{actionDescription} <FormattedMessage id='interaction_modal.preamble' defaultMessage="Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one." /></p>
|
||||
<p>{actionDescription} <strong><FormattedMessage id='interaction_modal.sign_in' defaultMessage='You are not logged in to this server. Where is your account hosted?' /></strong></p>
|
||||
</div>
|
||||
|
||||
<div className='interaction-modal__choices'>
|
||||
<div className='interaction-modal__choices__choice'>
|
||||
<h3><FormattedMessage id='interaction_modal.on_this_server' defaultMessage='On this server' /></h3>
|
||||
<a href='/auth/sign_in' className='button button--block'><FormattedMessage id='sign_in_banner.sign_in' defaultMessage='Login' /></a>
|
||||
{signupButton}
|
||||
</div>
|
||||
<IntlLoginForm resourceUrl={url} />
|
||||
|
||||
<div className='interaction-modal__choices__choice'>
|
||||
<h3><FormattedMessage id='interaction_modal.on_another_server' defaultMessage='On a different server' /></h3>
|
||||
<p><FormattedMessage id='interaction_modal.other_server_instructions' defaultMessage='Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.' /></p>
|
||||
<Copypaste value={url} />
|
||||
</div>
|
||||
</div>
|
||||
<p className='hint'><FormattedMessage id='interaction_modal.sign_in_hint' defaultMessage="Tip: That's the website where you signed up. If you don't remember, look for the welcome e-mail in your inbox. You can also enter your full username! (e.g. @Mastodon@mastodon.social)" /></p>
|
||||
<p><FormattedMessage id='interaction_modal.no_account_yet' defaultMessage='Not on Mastodon?' /> {signupButton}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@ class Footer extends ImmutablePureComponent {
|
|||
modalProps: {
|
||||
type: 'reply',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('url'),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class Footer extends ImmutablePureComponent {
|
|||
modalProps: {
|
||||
type: 'favourite',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('url'),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ class Footer extends ImmutablePureComponent {
|
|||
modalProps: {
|
||||
type: 'reblog',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('url'),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -252,7 +252,7 @@ class Status extends ImmutablePureComponent {
|
|||
modalProps: {
|
||||
type: 'favourite',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('url'),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
@ -289,7 +289,7 @@ class Status extends ImmutablePureComponent {
|
|||
modalProps: {
|
||||
type: 'reply',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('url'),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
@ -319,7 +319,7 @@ class Status extends ImmutablePureComponent {
|
|||
modalProps: {
|
||||
type: 'reblog',
|
||||
accountId: status.getIn(['account', 'id']),
|
||||
url: status.get('url'),
|
||||
url: status.get('uri'),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -191,7 +191,6 @@
|
|||
"conversation.open": "View conversation",
|
||||
"conversation.with": "With {names}",
|
||||
"copypaste.copied": "Copied",
|
||||
"copypaste.copy": "Copy",
|
||||
"copypaste.copy_to_clipboard": "Copy to clipboard",
|
||||
"directory.federated": "From known fediverse",
|
||||
"directory.local": "From {domain} only",
|
||||
|
@ -303,7 +302,7 @@
|
|||
"home.column_settings.basic": "Basic",
|
||||
"home.column_settings.show_reblogs": "Show boosts",
|
||||
"home.column_settings.show_replies": "Show replies",
|
||||
"home.explore_prompt.body": "Your home feed will have a mix of posts from the hashtags you've chosen to follow, the people you've chosen to follow, and the posts they boost. It's looking pretty quiet right now, so how about:",
|
||||
"home.explore_prompt.body": "Your home feed will have a mix of posts from the hashtags you've chosen to follow, the people you've chosen to follow, and the posts they boost. If that feels too quiet, you may want to:",
|
||||
"home.explore_prompt.title": "This is your home base within Mastodon.",
|
||||
"home.hide_announcements": "Hide announcements",
|
||||
"home.show_announcements": "Show announcements",
|
||||
|
@ -311,10 +310,13 @@
|
|||
"interaction_modal.description.follow": "With an account on Mastodon, you can follow {name} to receive their posts in your home feed.",
|
||||
"interaction_modal.description.reblog": "With an account on Mastodon, you can boost this post to share it with your own followers.",
|
||||
"interaction_modal.description.reply": "With an account on Mastodon, you can respond to this post.",
|
||||
"interaction_modal.login.action": "Take me home",
|
||||
"interaction_modal.login.prompt": "Domain of your home server, e.g. mastodon.social",
|
||||
"interaction_modal.no_account_yet": "Not on Mastodon?",
|
||||
"interaction_modal.on_another_server": "On a different server",
|
||||
"interaction_modal.on_this_server": "On this server",
|
||||
"interaction_modal.other_server_instructions": "Copy and paste this URL into the search field of your favorite Mastodon app or the web interface of your Mastodon server.",
|
||||
"interaction_modal.preamble": "Since Mastodon is decentralized, you can use your existing account hosted by another Mastodon server or compatible platform if you don't have an account on this one.",
|
||||
"interaction_modal.sign_in": "You are not logged in to this server. Where is your account hosted?",
|
||||
"interaction_modal.sign_in_hint": "Tip: That's the website where you signed up. If you don't remember, look for the welcome e-mail in your inbox. You can also enter your full username! (e.g. @Mastodon@mastodon.social)",
|
||||
"interaction_modal.title.favourite": "Favorite {name}'s post",
|
||||
"interaction_modal.title.follow": "Follow {name}",
|
||||
"interaction_modal.title.reblog": "Boost {name}'s post",
|
||||
|
|
172
app/javascript/packs/remote_interaction_helper.ts
Normal file
172
app/javascript/packs/remote_interaction_helper.ts
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
|
||||
This script is meant to to be used in an `iframe` with the sole purpose of doing webfinger queries
|
||||
client-side without being restricted by a strict `connect-src` Content-Security-Policy directive.
|
||||
|
||||
It communicates with the parent window through message events that are authenticated by origin,
|
||||
and performs no other task.
|
||||
|
||||
*/
|
||||
|
||||
import './public-path';
|
||||
|
||||
import axios from 'axios';
|
||||
|
||||
interface JRDLink {
|
||||
rel: string;
|
||||
template?: string;
|
||||
href?: string;
|
||||
}
|
||||
|
||||
const isJRDLink = (link: unknown): link is JRDLink =>
|
||||
typeof link === 'object' &&
|
||||
link !== null &&
|
||||
'rel' in link &&
|
||||
typeof link.rel === 'string' &&
|
||||
(!('template' in link) || typeof link.template === 'string') &&
|
||||
(!('href' in link) || typeof link.href === 'string');
|
||||
|
||||
const findLink = (rel: string, data: unknown): JRDLink | undefined => {
|
||||
if (
|
||||
typeof data === 'object' &&
|
||||
data !== null &&
|
||||
'links' in data &&
|
||||
data.links instanceof Array
|
||||
) {
|
||||
return data.links.find(
|
||||
(link): link is JRDLink => isJRDLink(link) && link.rel === rel,
|
||||
);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const findTemplateLink = (data: unknown) =>
|
||||
findLink('http://ostatus.org/schema/1.0/subscribe', data)?.template;
|
||||
|
||||
const fetchInteractionURLSuccess = (
|
||||
uri_or_domain: string,
|
||||
template: string,
|
||||
) => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: 'fetchInteractionURL-success',
|
||||
uri_or_domain,
|
||||
template,
|
||||
},
|
||||
window.origin,
|
||||
);
|
||||
};
|
||||
|
||||
const fetchInteractionURLFailure = () => {
|
||||
window.parent.postMessage(
|
||||
{
|
||||
type: 'fetchInteractionURL-failure',
|
||||
},
|
||||
window.origin,
|
||||
);
|
||||
};
|
||||
|
||||
const isValidDomain = (value: string) => {
|
||||
const url = new URL('https:///path');
|
||||
url.hostname = value;
|
||||
return url.hostname === value;
|
||||
};
|
||||
|
||||
// Attempt to find a remote interaction URL from a domain
|
||||
const fromDomain = (domain: string) => {
|
||||
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||
|
||||
axios
|
||||
.get(`https://${domain}/.well-known/webfinger`, {
|
||||
params: { resource: `https://${domain}` },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const template = findTemplateLink(data);
|
||||
fetchInteractionURLSuccess(domain, template ?? fallbackTemplate);
|
||||
return;
|
||||
})
|
||||
.catch(() => {
|
||||
fetchInteractionURLSuccess(domain, fallbackTemplate);
|
||||
});
|
||||
};
|
||||
|
||||
// Attempt to find a remote interaction URL from an arbitrary URL
|
||||
const fromURL = (url: string) => {
|
||||
const domain = new URL(url).host;
|
||||
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||
|
||||
axios
|
||||
.get(`https://${domain}/.well-known/webfinger`, {
|
||||
params: { resource: url },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const template = findTemplateLink(data);
|
||||
fetchInteractionURLSuccess(url, template ?? fallbackTemplate);
|
||||
return;
|
||||
})
|
||||
.catch(() => {
|
||||
fromDomain(domain);
|
||||
});
|
||||
};
|
||||
|
||||
// Attempt to find a remote interaction URL from a `user@domain` string
|
||||
const fromAcct = (acct: string) => {
|
||||
acct = acct.replace(/^@/, '');
|
||||
|
||||
const segments = acct.split('@');
|
||||
|
||||
if (segments.length !== 2 || !segments[0] || !isValidDomain(segments[1])) {
|
||||
fetchInteractionURLFailure();
|
||||
return;
|
||||
}
|
||||
|
||||
const domain = segments[1];
|
||||
const fallbackTemplate = `https://${domain}/authorize_interaction?uri={uri}`;
|
||||
|
||||
axios
|
||||
.get(`https://${domain}/.well-known/webfinger`, {
|
||||
params: { resource: `acct:${acct}` },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
const template = findTemplateLink(data);
|
||||
fetchInteractionURLSuccess(acct, template ?? fallbackTemplate);
|
||||
return;
|
||||
})
|
||||
.catch(() => {
|
||||
// TODO: handle host-meta?
|
||||
fromDomain(domain);
|
||||
});
|
||||
};
|
||||
|
||||
const fetchInteractionURL = (uri_or_domain: string) => {
|
||||
if (/^https?:\/\//.test(uri_or_domain)) {
|
||||
fromURL(uri_or_domain);
|
||||
} else if (uri_or_domain.includes('@')) {
|
||||
fromAcct(uri_or_domain);
|
||||
} else {
|
||||
fromDomain(uri_or_domain);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', (event: MessageEvent<unknown>) => {
|
||||
// Check message origin
|
||||
if (
|
||||
!window.origin ||
|
||||
window.parent !== event.source ||
|
||||
event.origin !== window.origin
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
event.data &&
|
||||
typeof event.data === 'object' &&
|
||||
'type' in event.data &&
|
||||
event.data.type === 'fetchInteractionURL' &&
|
||||
'uri_or_domain' in event.data &&
|
||||
typeof event.data.uri_or_domain === 'string'
|
||||
) {
|
||||
fetchInteractionURL(event.data.uri_or_domain);
|
||||
}
|
||||
});
|
|
@ -188,30 +188,43 @@
|
|||
}
|
||||
|
||||
.account-role,
|
||||
.information-badge,
|
||||
.simple_form .recommended,
|
||||
.simple_form .not_recommended {
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
cursor: default;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--user-role-accent, $ui-secondary-color);
|
||||
background-color: var(--user-role-background, rgba($ui-secondary-color, 0.1));
|
||||
border: 1px solid var(--user-role-border, rgba($ui-secondary-color, 0.5));
|
||||
color: $ui-secondary-color;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.moderator {
|
||||
.information-badge,
|
||||
.simple_form .recommended,
|
||||
.simple_form .not_recommended {
|
||||
background-color: rgba($ui-secondary-color, 0.1);
|
||||
border: 1px solid rgba($ui-secondary-color, 0.5);
|
||||
}
|
||||
|
||||
.account-role {
|
||||
border: 1px solid $highlight-text-color;
|
||||
|
||||
.fa {
|
||||
color: var(--user-role-accent, $highlight-text-color);
|
||||
}
|
||||
}
|
||||
|
||||
.information-badge {
|
||||
&.superapp {
|
||||
color: $success-green;
|
||||
background-color: rgba($success-green, 0.1);
|
||||
border-color: rgba($success-green, 0.5);
|
||||
}
|
||||
|
||||
&.admin {
|
||||
color: lighten($error-red, 12%);
|
||||
background-color: rgba(lighten($error-red, 12%), 0.1);
|
||||
border-color: rgba(lighten($error-red, 12%), 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
.simple_form .not_recommended {
|
||||
|
|
|
@ -2891,6 +2891,7 @@ $ui-header-height: 55px;
|
|||
border-radius: 4px;
|
||||
transition: box-shadow 300ms linear;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
|
||||
&.active {
|
||||
transition: none;
|
||||
|
@ -7331,6 +7332,16 @@ noscript {
|
|||
}
|
||||
}
|
||||
|
||||
&__badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.account-role {
|
||||
line-height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
|
@ -7369,10 +7380,6 @@ noscript {
|
|||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.account-role {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.emojione {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
@ -8350,13 +8357,13 @@ noscript {
|
|||
.interaction-modal {
|
||||
max-width: 90vw;
|
||||
width: 600px;
|
||||
background: $ui-base-color;
|
||||
background: var(--modal-background-color);
|
||||
border: 1px solid var(--modal-border-color);
|
||||
border-radius: 8px;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 20px;
|
||||
padding: 40px;
|
||||
|
||||
h3 {
|
||||
font-size: 22px;
|
||||
|
@ -8365,63 +8372,100 @@ noscript {
|
|||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
color: $darker-text-color;
|
||||
|
||||
strong {
|
||||
color: $primary-text-color;
|
||||
font-weight: 700;
|
||||
}
|
||||
}
|
||||
|
||||
p.hint {
|
||||
margin-bottom: 14px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
&__icon {
|
||||
color: $highlight-text-color;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
&__lead {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
color: $darker-text-color;
|
||||
}
|
||||
}
|
||||
|
||||
&__choices {
|
||||
display: flex;
|
||||
&__login {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
|
||||
&__choice {
|
||||
flex: 0 0 auto;
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
&__input {
|
||||
@include search-input;
|
||||
|
||||
h3 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
border: 1px solid lighten($ui-base-color, 8%);
|
||||
padding: 4px 6px;
|
||||
color: $primary-text-color;
|
||||
font-size: 16px;
|
||||
line-height: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
p {
|
||||
color: $darker-text-color;
|
||||
margin-bottom: 20px;
|
||||
input {
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
font: inherit;
|
||||
border: 0;
|
||||
padding: 15px - 4px 15px - 6px;
|
||||
flex: 1 1 auto;
|
||||
|
||||
&::placeholder {
|
||||
color: lighten($darker-text-color, 4%);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
margin-bottom: 10px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
.search__popout {
|
||||
margin-top: -1px;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
border: 1px solid lighten($ui-base-color, 8%);
|
||||
}
|
||||
|
||||
&.focused &__input {
|
||||
border-color: $highlight-text-color;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
}
|
||||
|
||||
&.invalid &__input {
|
||||
border-color: $error-red;
|
||||
}
|
||||
|
||||
&.expanded .search__popout {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&.expanded &__input {
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: $no-gap-breakpoint - 1px) {
|
||||
&__choices {
|
||||
display: block;
|
||||
|
||||
&__choice {
|
||||
width: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
.link-button {
|
||||
font-size: inherit;
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -96,4 +96,6 @@ $font-monospace: 'mastodon-font-monospace' !default;
|
|||
--dropdown-background-color: #{lighten($ui-base-color, 4%)};
|
||||
--dropdown-shadow: 0 20px 25px -5px #{rgba($base-shadow-color, 0.25)},
|
||||
0 8px 10px -6px #{rgba($base-shadow-color, 0.25)};
|
||||
--modal-background-color: #{darken($ui-base-color, 4%)};
|
||||
--modal-border-color: #{lighten($ui-base-color, 4%)};
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ interface AccountApiResponseValues {
|
|||
note: string;
|
||||
statuses_count: number;
|
||||
url: string;
|
||||
uri: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
|
|||
|
||||
target_accounts.each do |target_account|
|
||||
target_statuses = target_statuses_by_account[target_account.id]
|
||||
replied_to_accounts = Account.local.where(id: target_statuses.filter_map(&:in_reply_to_account_id))
|
||||
replied_to_accounts = target_statuses.nil? ? [] : Account.local.where(id: target_statuses.filter_map(&:in_reply_to_account_id))
|
||||
|
||||
next if target_account.suspended? || (!target_account.local? && replied_to_accounts.none?)
|
||||
|
||||
|
|
|
@ -76,6 +76,9 @@ class AttachmentBatch
|
|||
when :fog
|
||||
logger.debug { "Deleting #{attachment.path(style)}" }
|
||||
attachment.directory.files.new(key: attachment.path(style)).destroy
|
||||
when :azure
|
||||
logger.debug { "Deleting #{attachment.path(style)}" }
|
||||
attachment.destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
26
app/lib/importer/instances_index_importer.rb
Normal file
26
app/lib/importer/instances_index_importer.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Importer::InstancesIndexImporter < Importer::BaseImporter
|
||||
def import!
|
||||
index.adapter.default_scope.find_in_batches(batch_size: @batch_size) do |tmp|
|
||||
in_work_unit(tmp) do |instances|
|
||||
bulk = Chewy::Index::Import::BulkBuilder.new(index, to_index: instances).bulk_body
|
||||
|
||||
indexed = bulk.count { |entry| entry[:index] }
|
||||
deleted = bulk.count { |entry| entry[:delete] }
|
||||
|
||||
Chewy::Index::Import::BulkRequest.new(index).perform(bulk)
|
||||
|
||||
[indexed, deleted]
|
||||
end
|
||||
end
|
||||
|
||||
wait!
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def index
|
||||
InstancesIndex
|
||||
end
|
||||
end
|
|
@ -11,6 +11,8 @@ class WebfingerResource
|
|||
|
||||
def username
|
||||
case resource
|
||||
when %r{\A(https?://)?#{instance_actor_regexp}/?\Z}
|
||||
Rails.configuration.x.local_domain
|
||||
when /\Ahttps?/i
|
||||
username_from_url
|
||||
when /@/
|
||||
|
@ -22,6 +24,13 @@ class WebfingerResource
|
|||
|
||||
private
|
||||
|
||||
def instance_actor_regexp
|
||||
hosts = [Rails.configuration.x.local_domain, Rails.configuration.x.web_domain]
|
||||
hosts.concat(Rails.configuration.x.alternate_domains) if Rails.configuration.x.alternate_domains.present?
|
||||
|
||||
Regexp.union(hosts)
|
||||
end
|
||||
|
||||
def username_from_url
|
||||
if account_show_page?
|
||||
path_params[:username]
|
||||
|
|
|
@ -21,6 +21,7 @@ class Instance < ApplicationRecord
|
|||
belongs_to :unavailable_domain # skipcq: RB-RL1031
|
||||
end
|
||||
|
||||
scope :searchable, -> { where.not(domain: DomainBlock.select(:domain)) }
|
||||
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
|
||||
scope :by_domain_and_subdomains, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") }
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||
include FormattingHelper
|
||||
|
||||
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
|
||||
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
||||
:note, :url, :uri, :avatar, :avatar_static, :header, :header_static,
|
||||
:followers_count, :following_count, :statuses_count, :last_status_at
|
||||
|
||||
has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
|
||||
|
@ -66,6 +66,10 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
|||
ActivityPub::TagManager.instance.url_for(object)
|
||||
end
|
||||
|
||||
def uri
|
||||
ActivityPub::TagManager.instance.uri_for(object)
|
||||
end
|
||||
|
||||
def avatar
|
||||
full_asset_url(object.suspended? ? object.avatar.default_url : object.avatar_original_url)
|
||||
end
|
||||
|
|
|
@ -22,6 +22,7 @@ class WebfingerSerializer < ActiveModel::Serializer
|
|||
[
|
||||
{ rel: 'http://webfinger.net/rel/profile-page', type: 'text/html', href: about_more_url(instance_actor: true) },
|
||||
{ rel: 'self', type: 'application/activity+json', href: instance_actor_url },
|
||||
{ rel: 'http://ostatus.org/schema/1.0/subscribe', template: "#{authorize_interaction_url}?uri={uri}" },
|
||||
]
|
||||
else
|
||||
[
|
||||
|
|
|
@ -85,7 +85,7 @@ class SuspendAccountService < BaseService
|
|||
rescue Aws::S3::Errors::NotImplemented => e
|
||||
Rails.logger.error "Error trying to change ACL on #{attachment.s3_object(style).key}: #{e.message}"
|
||||
end
|
||||
when :fog
|
||||
when :fog, :azure
|
||||
# Not supported
|
||||
when :filesystem
|
||||
begin
|
||||
|
|
|
@ -81,7 +81,7 @@ class UnsuspendAccountService < BaseService
|
|||
rescue Aws::S3::Errors::NotImplemented => e
|
||||
Rails.logger.error "Error trying to change ACL on #{attachment.s3_object(style).key}: #{e.message}"
|
||||
end
|
||||
when :fog
|
||||
when :fog, :azure
|
||||
# Not supported
|
||||
when :filesystem
|
||||
begin
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
%samp= ":#{custom_emoji.shortcode}:"
|
||||
|
||||
- if custom_emoji.local?
|
||||
%span.account-role.bot= custom_emoji.category&.name || t('admin.custom_emojis.uncategorized')
|
||||
%span.information-badge= custom_emoji.category&.name || t('admin.custom_emojis.uncategorized')
|
||||
|
||||
.batch-table__row__content__extra
|
||||
- if custom_emoji.local?
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
.post-follow-actions
|
||||
%div= link_to t('authorize_follow.post_follow.web'), web_url("@#{@resource.pretty_acct}"), class: 'button button--block'
|
||||
%div= link_to t('authorize_follow.post_follow.return'), ActivityPub::TagManager.instance.url_for(@resource), class: 'button button--block'
|
||||
%div= t('authorize_follow.post_follow.close')
|
|
@ -1,3 +0,0 @@
|
|||
.form-container
|
||||
.flash-message#error_explanation
|
||||
= t('authorize_follow.error')
|
|
@ -1,24 +0,0 @@
|
|||
- content_for :page_title do
|
||||
= t('authorize_follow.title', acct: @resource.pretty_acct)
|
||||
|
||||
.form-container
|
||||
.follow-prompt
|
||||
= render 'application/card', account: @resource
|
||||
|
||||
- if current_account.following?(@resource)
|
||||
.flash-message
|
||||
%strong
|
||||
= t('authorize_follow.already_following')
|
||||
|
||||
= render 'post_follow_actions'
|
||||
- elsif current_account.requested?(@resource)
|
||||
.flash-message
|
||||
%strong
|
||||
= t('authorize_follow.already_requested')
|
||||
|
||||
= render 'post_follow_actions'
|
||||
- else
|
||||
= form_tag authorize_interaction_path, method: :post, class: 'simple_form' do
|
||||
= hidden_field_tag :action, :follow
|
||||
= hidden_field_tag :acct, @resource.acct
|
||||
= button_tag t('authorize_follow.follow'), type: :submit
|
|
@ -1,13 +0,0 @@
|
|||
- content_for :page_title do
|
||||
= t('authorize_follow.title', acct: @resource.pretty_acct)
|
||||
|
||||
.form-container
|
||||
.follow-prompt
|
||||
- if @resource.locked?
|
||||
%h2= t('authorize_follow.follow_request')
|
||||
- else
|
||||
%h2= t('authorize_follow.following')
|
||||
|
||||
= render 'application/card', account: @resource
|
||||
|
||||
= render 'post_follow_actions'
|
|
@ -5,8 +5,6 @@
|
|||
<%- UserRole.where(highlighted: true).select { |role| role.color.present? }.each do |role| %>
|
||||
.user-role-<%= role.id %> {
|
||||
--user-role-accent: <%= role.color %>;
|
||||
--user-role-background: <%= role.color + '19' %>;
|
||||
--user-role-border: <%= role.color + '80' %>;
|
||||
}
|
||||
|
||||
<%- end %>
|
||||
|
|
8
app/views/layouts/helper_frame.html.haml
Normal file
8
app/views/layouts/helper_frame.html.haml
Normal file
|
@ -0,0 +1,8 @@
|
|||
!!! 5
|
||||
%html
|
||||
%head
|
||||
%meta{ charset: 'utf-8' }/
|
||||
|
||||
= javascript_pack_tag 'common', crossorigin: 'anonymous'
|
||||
|
||||
= yield :header_tags
|
|
@ -14,7 +14,7 @@
|
|||
%strong.announcements-list__item__title
|
||||
= application.name
|
||||
- if application.superapp?
|
||||
%span.account-role.moderator= t('doorkeeper.authorized_applications.index.superapp')
|
||||
%span.information-badge.superapp= t('doorkeeper.authorized_applications.index.superapp')
|
||||
|
||||
.announcements-list__item__action-bar
|
||||
.announcements-list__item__meta
|
||||
|
|
4
app/views/remote_interaction_helper/index.html.haml
Normal file
4
app/views/remote_interaction_helper/index.html.haml
Normal file
|
@ -0,0 +1,4 @@
|
|||
- content_for :header_tags do
|
||||
%meta{ name: 'robots', content: 'noindex' }/
|
||||
|
||||
= javascript_pack_tag 'remote_interaction_helper', crossorigin: 'anonymous'
|
|
@ -9,7 +9,7 @@ class PushConversationWorker
|
|||
message = InlineRenderer.render(conversation, conversation.account, :conversation)
|
||||
timeline_id = "timeline:direct:#{conversation.account_id}"
|
||||
|
||||
redis.publish(timeline_id, Oj.dump(event: :conversation, payload: message, queued_at: (Time.now.to_f * 1000.0).to_i))
|
||||
redis.publish(timeline_id, Oj.dump(event: :conversation, payload: message))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
|
|
@ -9,7 +9,7 @@ class PushEncryptedMessageWorker
|
|||
message = InlineRenderer.render(encrypted_message, nil, :encrypted_message)
|
||||
timeline_id = "timeline:#{encrypted_message.device.account_id}:#{encrypted_message.device.device_id}"
|
||||
|
||||
redis.publish(timeline_id, Oj.dump(event: :encrypted_message, payload: message, queued_at: (Time.now.to_f * 1000.0).to_i))
|
||||
redis.publish(timeline_id, Oj.dump(event: :encrypted_message, payload: message))
|
||||
rescue ActiveRecord::RecordNotFound
|
||||
true
|
||||
end
|
||||
|
|
|
@ -25,8 +25,7 @@ class PushUpdateWorker
|
|||
def message
|
||||
Oj.dump(
|
||||
event: update? ? :'status.update' : :update,
|
||||
payload: @payload,
|
||||
queued_at: (Time.now.to_f * 1000.0).to_i
|
||||
payload: @payload
|
||||
)
|
||||
end
|
||||
|
||||
|
|
|
@ -7,5 +7,6 @@ class Scheduler::InstanceRefreshScheduler
|
|||
|
||||
def perform
|
||||
Instance.refresh
|
||||
InstancesIndex.import if Chewy.enabled?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -982,18 +982,6 @@ an:
|
|||
view_strikes: Veyer amonestacions pasadas contra la tuya cuenta
|
||||
too_fast: Formulario ninviau masiau rapido, lo intente de nuevo.
|
||||
use_security_key: Usar la clau de seguranza
|
||||
authorize_follow:
|
||||
already_following: Ya yes seguindo a esta cuenta
|
||||
already_requested: Ya has ninviau una solicitut de seguimiento a ixa cuenta
|
||||
error: Desafortunadament, ha ocurriu una error buscando la cuenta remota
|
||||
follow: Seguir
|
||||
follow_request: 'Tiens una solicitut de seguimiento de:'
|
||||
following: 'Exito! Agora yes seguindo a:'
|
||||
post_follow:
|
||||
close: U, puetz simplament zarrar esta finestra.
|
||||
return: Tornar ta lo perfil de l'usuario
|
||||
web: Ir ta lo puesto web
|
||||
title: Seguir a %{acct}
|
||||
challenge:
|
||||
confirm: Continar
|
||||
hint_html: "<strong>Tip:</strong> No tornaremos a preguntar-te per la clau entre la siguient hora."
|
||||
|
|
|
@ -1065,18 +1065,6 @@ ar:
|
|||
view_strikes: عرض السجلات السابقة ضد حسابك
|
||||
too_fast: تم إرسال النموذج بسرعة كبيرة، حاول مرة أخرى.
|
||||
use_security_key: استخدام مفتاح الأمان
|
||||
authorize_follow:
|
||||
already_following: أنت تتابع بالفعل هذا الحساب
|
||||
already_requested: لقد قُمتَ بإرسال طلب متابَعة إلى هذا الحساب مِن قَبل
|
||||
error: يا للأسف، وقع هناك خطأ إثر عملية البحث عن الحساب عن بعد
|
||||
follow: اتبع
|
||||
follow_request: 'لقد قمت بإرسال طلب متابعة إلى:'
|
||||
following: 'مرحى! أنت الآن تتبع:'
|
||||
post_follow:
|
||||
close: أو يمكنك إغلاق هذه النافذة.
|
||||
return: اظهر الملف التعريفي للمستخدم
|
||||
web: واصل إلى الويب
|
||||
title: إتباع %{acct}
|
||||
challenge:
|
||||
confirm: واصل
|
||||
hint_html: "<strong>توصية:</strong> لن نطلب منك ثانية كلمتك السرية في غضون الساعة اللاحقة."
|
||||
|
|
|
@ -481,15 +481,6 @@ ast:
|
|||
functional: La cuenta ta completamente operativa.
|
||||
pending: La to solicitú ta pendiente de que la revise'l nuesu personal ya ye posible que tarde tiempu. Vas recibir un mensaxe si s'aprueba.
|
||||
too_fast: El formulariu xubióse mui rápido, volvi tentalo.
|
||||
authorize_follow:
|
||||
already_following: Xá tas siguiendo a esta cuenta
|
||||
already_requested: Yá unviesti una solicitú de siguimientu a esa cuenta
|
||||
error: Desafortunadamente, hebo un error al buscar la cuenta remota
|
||||
follow_request: 'Unviesti una solicitú de siguimientu a:'
|
||||
post_follow:
|
||||
close: O pues zarrar esta ventana.
|
||||
return: Amosar el perfil de la cuenta
|
||||
web: Dir a la web
|
||||
challenge:
|
||||
confirm: Siguir
|
||||
hint_html: "<strong>Conseyu:</strong> nun vamos volver pidite la contraseña hasta dientro d'una hora."
|
||||
|
|
|
@ -1095,18 +1095,6 @@ be:
|
|||
view_strikes: Праглядзець мінулыя папярэджанні для вашага ўліковага запісу
|
||||
too_fast: Форма адпраўлена занадта хутка, паспрабуйце яшчэ раз.
|
||||
use_security_key: Выкарыстаеце ключ бяспекі
|
||||
authorize_follow:
|
||||
already_following: Вы ўжо падпісаныя на гэты ўліковы запіс
|
||||
already_requested: Вы ўжо адправілі запыт на гэты ўліковы запіс
|
||||
error: На жаль, падчас пошуку аддаленага ўліковага запісу здарылася памылка
|
||||
follow: Падпісацца
|
||||
follow_request: 'Вы адправілі запыт на падпіску:'
|
||||
following: 'Поспех! Цяпер вы падпісаны на:'
|
||||
post_follow:
|
||||
close: Або, вы можаце проста закрыць гэтае акно.
|
||||
return: Паказаць профіль карыстальніка
|
||||
web: Перайсці ў вэб-версію
|
||||
title: Падпісацца на %{acct}
|
||||
challenge:
|
||||
confirm: Працягнуць
|
||||
hint_html: "<strong>Парада:</strong> Мы не будзем запытваць ваш пароль зноўку на працягу наступнай гадзіны."
|
||||
|
|
|
@ -1059,18 +1059,6 @@ bg:
|
|||
view_strikes: Преглед на предишните предупреждения против акаунта ви
|
||||
too_fast: Образецът подаден пребързо, опитайте пак.
|
||||
use_security_key: Употреба на ключ за сигурност
|
||||
authorize_follow:
|
||||
already_following: Вече следвате този акаунт
|
||||
already_requested: Вече сте изпратили заявка за последване до този акаунт
|
||||
error: Възникна грешка, търсейки отдалечения акаунт
|
||||
follow: Последвай
|
||||
follow_request: 'Изпратихте следната заявка до:'
|
||||
following: 'Успешно! Сега сте последвали:'
|
||||
post_follow:
|
||||
close: Или просто затворете този прозорец.
|
||||
return: Показване на профила на потребителя
|
||||
web: Към мрежата
|
||||
title: Последвай %{acct}
|
||||
challenge:
|
||||
confirm: Продължаване
|
||||
hint_html: "<strong>Съвет</strong>: няма да ви питаме пак за паролата през следващия час."
|
||||
|
|
|
@ -299,11 +299,6 @@ br:
|
|||
security: Diogelroez
|
||||
status:
|
||||
account_status: Statud ar gont
|
||||
authorize_follow:
|
||||
follow: Heuliañ
|
||||
post_follow:
|
||||
web: Distreiñ d'an etrefas web
|
||||
title: Heuliañ %{acct}
|
||||
challenge:
|
||||
confirm: Kenderc' hel
|
||||
invalid_password: Ger-tremen diwiriek
|
||||
|
|
|
@ -1060,18 +1060,6 @@ ca:
|
|||
view_strikes: Veure accions del passat contra el teu compte
|
||||
too_fast: Formulari enviat massa ràpid, torna a provar-ho.
|
||||
use_security_key: Usa clau de seguretat
|
||||
authorize_follow:
|
||||
already_following: Ja estàs seguint aquest compte
|
||||
already_requested: Ja has enviat una sol·licitud de seguiment a aquest usuari
|
||||
error: Malauradament, ha ocorregut un error cercant el compte remot
|
||||
follow: Segueix
|
||||
follow_request: 'Has enviat una sol·licitud de seguiment a:'
|
||||
following: 'Perfecte! Ara segueixes:'
|
||||
post_follow:
|
||||
close: O bé, pots tancar aquesta finestra.
|
||||
return: Mostra el perfil de l'usuari
|
||||
web: Vés a la web
|
||||
title: Segueix %{acct}
|
||||
challenge:
|
||||
confirm: Continua
|
||||
hint_html: "<strong>Pista:</strong> No et preguntarem un altre cop la teva contrasenya en la pròxima hora."
|
||||
|
|
|
@ -620,18 +620,6 @@ ckb:
|
|||
view_strikes: بینینی لێدانەکانی ڕابردوو لە دژی ئەکاونتەکەت
|
||||
too_fast: فۆڕم زۆر خێرا پێشکەش کراوە، دووبارە هەوڵبدەرەوە.
|
||||
use_security_key: کلیلی ئاسایش بەکاربهێنە
|
||||
authorize_follow:
|
||||
already_following: ئێوە ئێستا شوێن کەوتووی ئەم هەژمارەیەی
|
||||
already_requested: تۆ پێشتر داواکاری بەدواداچوت ناردوە بۆ ئەو هەژمارە
|
||||
error: بەداخەوە هەڵەیەک هەبوو لە کاتی گەڕان بەدوای ئەو هەژمارەیە
|
||||
follow: شوێن کەوە
|
||||
follow_request: 'تۆ داواکاری شوێنکەوتنت ناردووە بۆ:'
|
||||
following: 'ئەنجام بوو! تۆ ئێستا بەدوای ئەم بەکارهێنەرە دەکەویت:'
|
||||
post_follow:
|
||||
close: یان، دەتوانیت ئەم پەنجەرەیە دابخەیت.
|
||||
return: پرۆفایلی بەکارهێنەر نیشان بدە
|
||||
web: بڕۆ بۆ وێب
|
||||
title: دوای %{acct} بکەوە
|
||||
challenge:
|
||||
confirm: بەردەوام بە
|
||||
hint_html: "<strong>خاڵ:</strong> ئیمە لە کاتژمێری داهاتوو تێپەروشەت لێداوا ناکەین."
|
||||
|
|
|
@ -583,18 +583,6 @@ co:
|
|||
redirecting_to: U vostru contu hè inattivu perchè riindirizza versu %{acct}.
|
||||
too_fast: Furmulariu mandatu troppu prestu, ripruvate.
|
||||
use_security_key: Utilizà a chjave di sicurità
|
||||
authorize_follow:
|
||||
already_following: Site digià abbunatu·a à stu contu
|
||||
already_requested: Avete digià mandatu una dumanda d'abbunamentu à stu contu
|
||||
error: Peccatu, c’hè statu un prublemu ricercandu u contu
|
||||
follow: Siguità
|
||||
follow_request: 'Avete dumandatu di siguità:'
|
||||
following: 'Eccu! Avà seguitate:'
|
||||
post_follow:
|
||||
close: O pudete ancu chjude sta finestra.
|
||||
return: Vede u prufile di l’utilizatore
|
||||
web: Andà à l’interfaccia web
|
||||
title: Siguità %{acct}
|
||||
challenge:
|
||||
confirm: Cuntinuvà
|
||||
hint_html: "<strong>Astuzia:</strong> Ùn avemu micca da dumandavvi stu codice per l'ore chì vene."
|
||||
|
|
|
@ -1075,18 +1075,6 @@ cs:
|
|||
view_strikes: Zobrazit minulé prohřešky vašeho účtu
|
||||
too_fast: Formulář byl odeslán příliš rychle, zkuste to znovu.
|
||||
use_security_key: Použít bezpečnostní klíč
|
||||
authorize_follow:
|
||||
already_following: Tento účet již sledujete
|
||||
already_requested: Tomuto účtu už jste žádost o sledování zaslali
|
||||
error: Při hledání vzdáleného účtu bohužel nastala chyba
|
||||
follow: Sledovat
|
||||
follow_request: 'Poslali jste žádost o sledování uživateli:'
|
||||
following: 'Podařilo se! Nyní sledujete uživatele:'
|
||||
post_follow:
|
||||
close: Nebo můžete toto okno klidně zavřít.
|
||||
return: Zobrazit profil uživatele
|
||||
web: Přejít na web
|
||||
title: Sledovat %{acct}
|
||||
challenge:
|
||||
confirm: Pokračovat
|
||||
hint_html: "<strong>Tip:</strong> Po dobu jedné hodiny vás o heslo nebudeme znovu žádat."
|
||||
|
|
|
@ -1132,18 +1132,6 @@ cy:
|
|||
view_strikes: Gweld rybuddion y gorffennol yn erbyn eich cyfrif
|
||||
too_fast: Cafodd y ffurflen ei chyflwyno'n rhy gyflym, ceisiwch eto.
|
||||
use_security_key: Defnyddiwch allwedd diogelwch
|
||||
authorize_follow:
|
||||
already_following: Rydych yn dilyn y cyfrif hwn yn barod
|
||||
already_requested: Rydych chi eisoes wedi anfon cais i ddilyn y cyfrif hwnnw
|
||||
error: Yn anffodus, roedd gwall tra'n edrych am y cyfrif pell
|
||||
follow: Dilyn
|
||||
follow_request: 'Rydych wedi anfon cais dilyn at:'
|
||||
following: 'Llwyddiant! Rydych nawr yn dilyn:'
|
||||
post_follow:
|
||||
close: Neu, gallwch gau'r ffenest hon.
|
||||
return: Dangos proffil y defnyddiwr
|
||||
web: Ewch i'r we
|
||||
title: Dilyn %{acct}
|
||||
challenge:
|
||||
confirm: Parhau
|
||||
hint_html: "<strong>Awgrym:</strong> Fyddwn ni ddim yn gofyn i chi am eich cyfrinair eto am yr awr nesaf."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ da:
|
|||
view_strikes: Se tidligere anmeldelser af din konto
|
||||
too_fast: Formularen indsendt for hurtigt, forsøg igen.
|
||||
use_security_key: Brug sikkerhedsnøgle
|
||||
authorize_follow:
|
||||
already_following: Du følger allerede denne konto
|
||||
already_requested: Du har allerede sendt en følgeanmodning til den konto
|
||||
error: Desværre opstod en fejl under søgning af fjernkontoen
|
||||
follow: Følg
|
||||
follow_request: 'Du har sendt en følgeanmodning til:'
|
||||
following: 'Accepteret! Du følger nu:'
|
||||
post_follow:
|
||||
close: Du kan også bare lukke dette vindue.
|
||||
return: Vis brugerens profil
|
||||
web: Gå til web
|
||||
title: Følg %{acct}
|
||||
challenge:
|
||||
confirm: Fortsæt
|
||||
hint_html: "<strong>Tip:</strong> Du bliver ikke anmodet om din adgangskode igen den næste time."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ de:
|
|||
view_strikes: Vorherige Verstöße deines Kontos ansehen
|
||||
too_fast: Formular zu schnell übermittelt. Bitte versuche es erneut.
|
||||
use_security_key: Sicherheitsschlüssel verwenden
|
||||
authorize_follow:
|
||||
already_following: Du folgst diesem Konto bereits
|
||||
already_requested: Du hast bereits eine Anfrage zum Folgen an dieses Konto gestellt
|
||||
error: Bedauerlicherweise konnte das externe Konto nicht geladen werden
|
||||
follow: Folgen
|
||||
follow_request: 'Du hast eine Anfrage zum Folgen gestellt an:'
|
||||
following: 'Erfolg! Du folgst nun:'
|
||||
post_follow:
|
||||
close: Oder du schließt einfach dieses Fenster.
|
||||
return: Profil anzeigen
|
||||
web: Im Webinterface öffnen
|
||||
title: "%{acct} folgen"
|
||||
challenge:
|
||||
confirm: Fortfahren
|
||||
hint_html: "<strong>Hinweis:</strong> Wir werden dich für die nächste Stunde nicht erneut nach deinem Passwort fragen."
|
||||
|
|
|
@ -1039,18 +1039,6 @@ el:
|
|||
view_strikes: Προβολή προηγούμενων ποινών εναντίον του λογαριασμού σας
|
||||
too_fast: Η φόρμα υποβλήθηκε πολύ γρήγορα, προσπαθήστε ξανά.
|
||||
use_security_key: Χρήση κλειδιού ασφαλείας
|
||||
authorize_follow:
|
||||
already_following: Ήδη ακολουθείς αυτό το λογαριασμό
|
||||
already_requested: Έχετε ήδη στείλει ένα αίτημα ακολούθησης σε αυτόν τον λογαριασμό
|
||||
error: Δυστυχώς παρουσιάστηκε ένα σφάλμα κατά την αναζήτηση του απομακρυσμένου λογαριασμού
|
||||
follow: Ακολούθησε
|
||||
follow_request: 'Έστειλες αίτημα παρακολούθησης προς:'
|
||||
following: 'Επιτυχία! Πλέον ακολουθείς τον/την:'
|
||||
post_follow:
|
||||
close: Ή, μπορείς απλά να κλείσεις αυτό το παράθυρο.
|
||||
return: Δείξε το προφίλ του χρήστη
|
||||
web: Πήγαινε στο δίκτυο
|
||||
title: Ακολούθησε %{acct}
|
||||
challenge:
|
||||
confirm: Συνέχεια
|
||||
hint_html: "<strong>Συμβουλή:</strong> Δεν θα σου ζητήσουμε τον κωδικό ασφαλείας σου ξανά για την επόμενη ώρα."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ en-GB:
|
|||
view_strikes: View past strikes against your account
|
||||
too_fast: Form submitted too fast, try again.
|
||||
use_security_key: Use security key
|
||||
authorize_follow:
|
||||
already_following: You are already following this account
|
||||
already_requested: You have already sent a follow request to that account
|
||||
error: Unfortunately, there was an error looking up the remote account
|
||||
follow: Follow
|
||||
follow_request: 'You have sent a follow request to:'
|
||||
following: 'Success! You are now following:'
|
||||
post_follow:
|
||||
close: Or, you can just close this window.
|
||||
return: Show the user's profile
|
||||
web: Go to web
|
||||
title: Follow %{acct}
|
||||
challenge:
|
||||
confirm: Continue
|
||||
hint_html: "<strong>Tip:</strong> We won't ask you for your password again for the next hour."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ en:
|
|||
view_strikes: View past strikes against your account
|
||||
too_fast: Form submitted too fast, try again.
|
||||
use_security_key: Use security key
|
||||
authorize_follow:
|
||||
already_following: You are already following this account
|
||||
already_requested: You have already sent a follow request to that account
|
||||
error: Unfortunately, there was an error looking up the remote account
|
||||
follow: Follow
|
||||
follow_request: 'You have sent a follow request to:'
|
||||
following: 'Success! You are now following:'
|
||||
post_follow:
|
||||
close: Or, you can just close this window.
|
||||
return: Show the user's profile
|
||||
web: Go to web
|
||||
title: Follow %{acct}
|
||||
challenge:
|
||||
confirm: Continue
|
||||
hint_html: "<strong>Tip:</strong> We won't ask you for your password again for the next hour."
|
||||
|
|
|
@ -1056,18 +1056,6 @@ eo:
|
|||
view_strikes: Vidi antauaj admonoj kontra via konto
|
||||
too_fast: Formularo sendita tro rapide, klopodu denove.
|
||||
use_security_key: Uzi sekurecan ŝlosilon
|
||||
authorize_follow:
|
||||
already_following: Vi jam sekvas tiun konton
|
||||
already_requested: Vi jam sendis peton de sekvado al ĉi tiu konto
|
||||
error: Bedaŭrinde, estis eraro en la serĉado de la fora konto
|
||||
follow: Sekvi
|
||||
follow_request: 'Vi sendis peton de sekvado al:'
|
||||
following: 'Sukceson! Vi nun sekvas:'
|
||||
post_follow:
|
||||
close: Aŭ, vi povas simple fermi ĉi tiun fenestron.
|
||||
return: Montri la profilon de la uzanto
|
||||
web: Iri al reto
|
||||
title: Sekvi %{acct}
|
||||
challenge:
|
||||
confirm: Daŭrigi
|
||||
hint_html: "<strong>Konsileto:</strong> Ni ne demandos pri via pasvorto ĝis 1 horo."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ es-AR:
|
|||
view_strikes: Ver incumplimientos pasados contra tu cuenta
|
||||
too_fast: Formulario enviado demasiado rápido, probá de nuevo.
|
||||
use_security_key: Usar la llave de seguridad
|
||||
authorize_follow:
|
||||
already_following: Ya estás siguiendo a esta cuenta
|
||||
already_requested: Ya enviaste una solicitud de seguimiento a esa cuenta
|
||||
error: Lamentablemente, ocurrió un error buscando la cuenta remota
|
||||
follow: Seguir
|
||||
follow_request: 'Enviaste una solicitud de seguimiento a:'
|
||||
following: "¡Listo! Ahora estás siguiendo a:"
|
||||
post_follow:
|
||||
close: O simplemente podés cerrar esta ventana.
|
||||
return: Mostrar el perfil del usuario
|
||||
web: Ir a la web
|
||||
title: Seguir a %{acct}
|
||||
challenge:
|
||||
confirm: Continuar
|
||||
hint_html: "<strong>Dato:</strong> No volveremos a preguntarte por la contraseña durante la siguiente hora."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ es-MX:
|
|||
view_strikes: Ver amonestaciones pasadas contra tu cuenta
|
||||
too_fast: Formulario enviado demasiado rápido, inténtelo de nuevo.
|
||||
use_security_key: Usar la clave de seguridad
|
||||
authorize_follow:
|
||||
already_following: Ya estás siguiendo a esta cuenta
|
||||
already_requested: Ya has enviado una solicitud de seguimiento a esa cuenta
|
||||
error: Desafortunadamente, ha ocurrido un error buscando la cuenta remota
|
||||
follow: Seguir
|
||||
follow_request: 'Tienes una solicitud de seguimiento de:'
|
||||
following: "¡Éxito! Ahora estás siguiendo a:"
|
||||
post_follow:
|
||||
close: O, puedes simplemente cerrar esta ventana.
|
||||
return: Regresar al perfil del usuario
|
||||
web: Ir al sitio web
|
||||
title: Seguir a %{acct}
|
||||
challenge:
|
||||
confirm: Continuar
|
||||
hint_html: "<strong>Tip:</strong> No volveremos a preguntarte por la contraseña durante la siguiente hora."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ es:
|
|||
view_strikes: Ver amonestaciones pasadas contra tu cuenta
|
||||
too_fast: Formulario enviado demasiado rápido, inténtelo de nuevo.
|
||||
use_security_key: Usar la clave de seguridad
|
||||
authorize_follow:
|
||||
already_following: Ya estás siguiendo a esta cuenta
|
||||
already_requested: Ya has enviado una solicitud de seguimiento a esa cuenta
|
||||
error: Desafortunadamente, ha ocurrido un error buscando la cuenta remota
|
||||
follow: Seguir
|
||||
follow_request: 'Tienes una solicitud de seguimiento de:'
|
||||
following: "¡Éxito! Ahora estás siguiendo a:"
|
||||
post_follow:
|
||||
close: O, puedes simplemente cerrar esta ventana.
|
||||
return: Regresar al perfil del usuario
|
||||
web: Ir al sitio web
|
||||
title: Seguir a %{acct}
|
||||
challenge:
|
||||
confirm: Continuar
|
||||
hint_html: "<strong>Tip:</strong> No volveremos a preguntarte por la contraseña durante la siguiente hora."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ et:
|
|||
view_strikes: Vaata enda eelnevaid juhtumeid
|
||||
too_fast: Vorm esitatud liiga kiirelt, proovi uuesti.
|
||||
use_security_key: Kasuta turvavõtit
|
||||
authorize_follow:
|
||||
already_following: Juba jälgid seda kontot
|
||||
already_requested: Saatsid juba sellele kontole jälgimistaotluse
|
||||
error: Kahjuks ilmus viga kasutaja kaugserverist otsimisel
|
||||
follow: Jälgi
|
||||
follow_request: 'Oled saatnud jälgimistaotluse kasutajale:'
|
||||
following: 'Õnnestus! Jälgid nüüd kasutajat:'
|
||||
post_follow:
|
||||
close: Või sulge lihtsalt see aken.
|
||||
return: Näita kasutaja profiili
|
||||
web: Mine veebi
|
||||
title: Jälgi %{acct}
|
||||
challenge:
|
||||
confirm: Jätka
|
||||
hint_html: "<strong>Nõuanne:</strong> Me ei küsi salasõna uuesti järgmise tunni jooksul."
|
||||
|
|
|
@ -1051,18 +1051,6 @@ eu:
|
|||
view_strikes: Ikusi zure kontuaren aurkako neurriak
|
||||
too_fast: Formularioa azkarregi bidali duzu, saiatu berriro.
|
||||
use_security_key: Erabili segurtasun gakoa
|
||||
authorize_follow:
|
||||
already_following: Kontu hau aurretik jarraitzen duzu
|
||||
already_requested: Bidali duzu dagoeneko kontu hori jarraitzeko eskaera bat
|
||||
error: Zoritxarrez, urruneko kontua bilatzean errore bat gertatu da
|
||||
follow: Jarraitu
|
||||
follow_request: 'Jarraitzeko eskari bat bidali duzu hona:'
|
||||
following: 'Ongi! Orain jarraitzen duzu:'
|
||||
post_follow:
|
||||
close: Edo, leiho hau besterik gabe itxi dezakezu.
|
||||
return: Erakutsi erabiltzailearen profila
|
||||
web: Joan webera
|
||||
title: Jarraitu %{acct}
|
||||
challenge:
|
||||
confirm: Jarraitu
|
||||
hint_html: "<strong>Oharra:</strong> Ez dizugu pasahitza berriro eskatuko ordu batez."
|
||||
|
|
|
@ -889,18 +889,6 @@ fa:
|
|||
view_strikes: دیدن شکایتهای گذشته از حسابتان
|
||||
too_fast: فرم با سرعت بسیار زیادی فرستاده شد، دوباره تلاش کنید.
|
||||
use_security_key: استفاده از کلید امنیتی
|
||||
authorize_follow:
|
||||
already_following: شما همین الان هم این حساب را پیمیگیرید
|
||||
already_requested: درخواست پیگیریای برای آن حساب فرستاده بودید
|
||||
error: متأسفانه حین یافتن آن حساب خطایی رخ داد
|
||||
follow: پی بگیرید
|
||||
follow_request: 'شما درخواست پیگیری فرستادهاید به:'
|
||||
following: 'انجام شد! شما هماینک پیگیر این کاربر هستید:'
|
||||
post_follow:
|
||||
close: یا این پنجره را ببندید.
|
||||
return: نمایهٔ این کاربر را نشان بده
|
||||
web: رفتن به وب
|
||||
title: پیگیری %{acct}
|
||||
challenge:
|
||||
confirm: ادامه
|
||||
hint_html: "<strong>نکته:</strong> ما در یک ساعت آینده گذرواژهتان را از شما نخواهیم پرسید."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ fi:
|
|||
view_strikes: Näytä tiliäsi koskevia aiempia varoituksia
|
||||
too_fast: Lomake lähetettiin liian nopeasti, yritä uudelleen.
|
||||
use_security_key: Käytä suojausavainta
|
||||
authorize_follow:
|
||||
already_following: Sinä seuraat jo tätä tiliä
|
||||
already_requested: Olet jo lähettänyt seurantapyynnön tälle tilille
|
||||
error: Valitettavasti etätilin haussa tapahtui virhe
|
||||
follow: Seuraa
|
||||
follow_request: 'Olet lähettänyt seuraamispyynnön käyttäjälle:'
|
||||
following: 'Onnistui! Seuraat käyttäjää:'
|
||||
post_follow:
|
||||
close: Tai voit sulkea tämän ikkunan.
|
||||
return: Palaa käyttäjän profiiliin
|
||||
web: Siirry verkkosivulle
|
||||
title: Seuraa käyttäjää %{acct}
|
||||
challenge:
|
||||
confirm: Jatka
|
||||
hint_html: "<strong>Vihje:</strong> Emme pyydä sinulta salasanaa uudelleen seuraavan tunnin aikana."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ fo:
|
|||
view_strikes: Vís eldri atsóknir móti tíni kontu
|
||||
too_fast: Oyðublaðið innsent ov skjótt, royn aftur.
|
||||
use_security_key: Brúka trygdarlykil
|
||||
authorize_follow:
|
||||
already_following: Tú fylgir longu hesi kontuni
|
||||
already_requested: Tú hevur longu sent eina fylgiumbøn til hasa kontuna
|
||||
error: Tíverri kom ein feilur, tá vit royndu at finna fjarkontuna
|
||||
follow: Fylg
|
||||
follow_request: 'Tú hevur sent eina fylgjaraumbøn til:'
|
||||
following: 'Góðkent! Tú fylgir nú:'
|
||||
post_follow:
|
||||
close: Ella kanst tú bara lata hetta vindeygað aftur.
|
||||
return: Vís vangan hjá brúkaranum
|
||||
web: Far á vevið
|
||||
title: Fylg %{acct}
|
||||
challenge:
|
||||
confirm: Hald á
|
||||
hint_html: "<strong>Góð ráð:</strong> vit spyrja teg ikki aftur um loyniorðið næsta tíman."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ fr-QC:
|
|||
view_strikes: Voir les sanctions précédemment appliquées à votre compte
|
||||
too_fast: Formulaire envoyé trop rapidement, veuillez réessayer.
|
||||
use_security_key: Utiliser la clé de sécurité
|
||||
authorize_follow:
|
||||
already_following: Vous suivez déjà ce compte
|
||||
already_requested: Vous avez déjà envoyé une demande d’abonnement à ce compte
|
||||
error: Malheureusement, une erreur s'est produite lors de la recherche du compte distant
|
||||
follow: Suivre
|
||||
follow_request: 'Vous avez demandé à suivre :'
|
||||
following: 'Youpi ! Vous suivez maintenant :'
|
||||
post_follow:
|
||||
close: Ou bien, vous pouvez fermer cette fenêtre.
|
||||
return: Afficher le profil de l’utilisateur·ice
|
||||
web: Retour à l’interface web
|
||||
title: Suivre %{acct}
|
||||
challenge:
|
||||
confirm: Continuer
|
||||
hint_html: "<strong>Astuce :</strong> Nous ne vous demanderons plus votre mot de passe pour la prochaine heure."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ fr:
|
|||
view_strikes: Voir les sanctions précédemment appliquées à votre compte
|
||||
too_fast: Formulaire envoyé trop rapidement, veuillez réessayer.
|
||||
use_security_key: Utiliser la clé de sécurité
|
||||
authorize_follow:
|
||||
already_following: Vous suivez déjà ce compte
|
||||
already_requested: Vous avez déjà envoyé une demande d’abonnement à ce compte
|
||||
error: Malheureusement, une erreur s'est produite lors de la recherche du compte distant
|
||||
follow: Suivre
|
||||
follow_request: 'Vous avez demandé à suivre :'
|
||||
following: 'Youpi ! Vous suivez maintenant :'
|
||||
post_follow:
|
||||
close: Ou bien, vous pouvez fermer cette fenêtre.
|
||||
return: Afficher le profil de l’utilisateur·ice
|
||||
web: Retour à l’interface web
|
||||
title: Suivre %{acct}
|
||||
challenge:
|
||||
confirm: Continuer
|
||||
hint_html: "<strong>Astuce :</strong> Nous ne vous demanderons plus votre mot de passe pour la prochaine heure."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ fy:
|
|||
view_strikes: Besjoch de earder troch moderatoaren fêststelde skeiningen dy’t jo makke hawwe
|
||||
too_fast: Formulier is te fluch yntsjinne. Probearje it nochris.
|
||||
use_security_key: Befeiligingskaai brûke
|
||||
authorize_follow:
|
||||
already_following: Jo folgje dizze account al
|
||||
already_requested: Jo hawwe al in folchfersyk nei dat account ferstjoerd
|
||||
error: Spitiger, der is in flater bard by it opsykjen fan de eksterne account
|
||||
follow: Folgje
|
||||
follow_request: 'Jo hawwe in folchfersyk yntsjinne by:'
|
||||
following: 'Slagge! Jo folgje no:'
|
||||
post_follow:
|
||||
close: Of jo kinne dit finster gewoan slute.
|
||||
return: Profyl fan dizze brûker toane
|
||||
web: Gean nei de webapp
|
||||
title: "%{acct} folgje"
|
||||
challenge:
|
||||
confirm: Trochgean
|
||||
hint_html: "<strong>Tip:</strong> Wy freegje jo it kommende oere net mear nei jo wachtwurd."
|
||||
|
|
|
@ -331,11 +331,6 @@ ga:
|
|||
status:
|
||||
account_status: Stádas cuntais
|
||||
too_fast: Cuireadh an fhoirm isteach róthapa, triail arís.
|
||||
authorize_follow:
|
||||
follow: Lean
|
||||
post_follow:
|
||||
return: Taispeáin próifíl an úsáideora
|
||||
title: Lean %{acct}
|
||||
challenge:
|
||||
confirm: Lean ar aghaidh
|
||||
datetime:
|
||||
|
|
|
@ -1095,18 +1095,6 @@ gd:
|
|||
view_strikes: Seall na rabhaidhean a fhuair an cunntas agad roimhe
|
||||
too_fast: Chaidh am foirm a chur a-null ro luath, feuch ris a-rithist.
|
||||
use_security_key: Cleachd iuchair tèarainteachd
|
||||
authorize_follow:
|
||||
already_following: Tha thu a’ leantainn a’ chunntais seo mu thràth
|
||||
already_requested: Chuir thu iarrtas leantainn dhan chunntas seo mu thràth
|
||||
error: Gu mì-fhortanach, thachair mearachd le lorg a’ chunntais chèin
|
||||
follow: Lean
|
||||
follow_request: 'Chuir thu iarrtas leantainn gu:'
|
||||
following: 'Taghta! Chaidh leat a’ leantainn:'
|
||||
post_follow:
|
||||
close: Air neo dùin an uinneag seo.
|
||||
return: Seall pròifil a’ chleachdaiche
|
||||
web: Tadhail air an duilleag-lìn
|
||||
title: Lean %{acct}
|
||||
challenge:
|
||||
confirm: Lean air adhart
|
||||
hint_html: "<strong>Gliocas:</strong> Chan iarr sinn am facal-faire agad ort a-rithist fad uair a thìde."
|
||||
|
|
|
@ -1060,18 +1060,6 @@ gl:
|
|||
view_strikes: Ver avisos anteriores respecto da túa conta
|
||||
too_fast: Formulario enviado demasiado rápido, inténtao outra vez.
|
||||
use_security_key: Usa chave de seguridade
|
||||
authorize_follow:
|
||||
already_following: Xa está a seguir esta conta
|
||||
already_requested: Xa tes enviada unha solicitude de seguimento a esa conta
|
||||
error: Desgraciadamente, algo fallou ao buscar a conta remota
|
||||
follow: Seguir
|
||||
follow_request: 'Enviaches unha petición de seguimento a:'
|
||||
following: 'Parabéns! Agora segues a:'
|
||||
post_follow:
|
||||
close: Ou, podes pechar esta ventá.
|
||||
return: Mostrar o perfil da usuaria
|
||||
web: Ir á web
|
||||
title: Seguir %{acct}
|
||||
challenge:
|
||||
confirm: Continuar
|
||||
hint_html: "<strong>Nota:</strong> Non che pediremos o contrasinal na seguinte hora."
|
||||
|
|
|
@ -1096,18 +1096,6 @@ he:
|
|||
view_strikes: צפיה בעברות קודמות שנרשמו נגד חשבונך
|
||||
too_fast: הטופס הוגש מהר מדי, נסה/י שוב.
|
||||
use_security_key: שימוש במפתח אבטחה
|
||||
authorize_follow:
|
||||
already_following: את/ה כבר עוקב/ת אחרי חשבון זה
|
||||
already_requested: כבר נשלחה בקשת מעקב לחשבון זה
|
||||
error: למרבה הצער, היתה שגיאה בחיפוש החשבון המרוחק
|
||||
follow: לעקוב
|
||||
follow_request: 'שלחת בקשת מעקב ל:'
|
||||
following: 'הצלחה! הינך עוקב עכשיו אחרי:'
|
||||
post_follow:
|
||||
close: או, פשוט לסגור חלון זה.
|
||||
return: הצג את פרופיל המשתמש
|
||||
web: מעבר לווב
|
||||
title: לעקוב אחרי %{acct}
|
||||
challenge:
|
||||
confirm: המשך
|
||||
hint_html: "<strong>טיפ:</strong> לא נבקש את סיסמתך שוב בשעה הקרובה."
|
||||
|
|
|
@ -64,10 +64,6 @@ hr:
|
|||
reset_password: Ponovno postavi lozinku
|
||||
security: Sigurnost
|
||||
set_new_password: Postavi novu lozinku
|
||||
authorize_follow:
|
||||
error: Nažalost, došlo je do greške tijekom traženja udaljenog računa
|
||||
follow: Prati
|
||||
title: Prati %{acct}
|
||||
datetime:
|
||||
distance_in_words:
|
||||
about_x_months: "%{count}mj"
|
||||
|
|
|
@ -1060,18 +1060,6 @@ hu:
|
|||
view_strikes: Fiókod ellen felrótt korábbi vétségek megtekintése
|
||||
too_fast: Túl gyorsan küldted el az űrlapot, próbáld később.
|
||||
use_security_key: Biztonsági kulcs használata
|
||||
authorize_follow:
|
||||
already_following: Már követed ezt a felhasználót
|
||||
already_requested: Már küldtél követési kérelmet ennek a fióknak
|
||||
error: Hiba történt a távoli felhasználó keresésekor
|
||||
follow: Követés
|
||||
follow_request: 'Engedélyt kértél az alábbi felhasználó követésére:'
|
||||
following: 'Siker! Mostantól követed az alábbi felhasználót:'
|
||||
post_follow:
|
||||
close: Akár be is zárhatod ezt az ablakot.
|
||||
return: A felhasználó profiljának mutatása
|
||||
web: Megtekintés a weben
|
||||
title: "%{acct} követése"
|
||||
challenge:
|
||||
confirm: Folytatás
|
||||
hint_html: "<strong>Hasznos:</strong> Nem fogjuk megint a jelszavadat kérdezni a következő órában."
|
||||
|
|
|
@ -481,17 +481,6 @@ hy:
|
|||
account_status: Հաշուի կարգավիճակ
|
||||
pending: Դիմումը պէտք է քննուի մեր անձնակազմի կողմից, ինչը կարող է մի փոքր ժամանակ խլել։ Դիմումի հաստատուելու դէպքում, կտեղեկացնենք նամակով։
|
||||
use_security_key: Օգտագործել անվտանգութեան բանալի
|
||||
authorize_follow:
|
||||
already_following: Դու արդէն հետեւում ես այս հաշուին
|
||||
already_requested: Դու արդէն ուղարկել ես հետեւմանն յայտ այս հաշուին
|
||||
follow: Հետևել
|
||||
follow_request: Դու ուղարկել ես հետեւելու հայց՝
|
||||
following: Յաջողութի՜ւն։ Դու այժմ հետեւում ես․
|
||||
post_follow:
|
||||
close: Կամ, կարող ես պարզապէս փակել այս պատուհանը։
|
||||
return: Ցուցադրել օգտատիրոջ էջը
|
||||
web: Անցնել վէբին
|
||||
title: Հետեւել %{acct}
|
||||
challenge:
|
||||
confirm: Շարունակել
|
||||
invalid_password: Անվաւեր ծածկագիր
|
||||
|
|
|
@ -959,18 +959,6 @@ id:
|
|||
view_strikes: Lihat hukuman lalu yang pernah terjadi kepada akun Anda
|
||||
too_fast: Formulir dikirim terlalu cepat, coba lagi.
|
||||
use_security_key: Gunakan kunci keamanan
|
||||
authorize_follow:
|
||||
already_following: Anda sudah mengikuti akun ini
|
||||
already_requested: Anda sudah mengirimkan permintaan untuk mengikuti akun tersebut
|
||||
error: Sayangnya, ada error saat melihat akun remote
|
||||
follow: Ikuti
|
||||
follow_request: 'Anda telah mengirim permintaan untuk mengikuti ke:'
|
||||
following: 'Berhasil! Anda sekarang mengikuti:'
|
||||
post_follow:
|
||||
close: Atau Anda dapat menutup jendela ini.
|
||||
return: Tampilkan profil pengguna
|
||||
web: Ke web
|
||||
title: Mengikuti %{acct}
|
||||
challenge:
|
||||
confirm: Lanjut
|
||||
hint_html: "<strong>Tip:</strong> Kami tidak akan meminta kata sandi Anda lagi untuk beberapa jam ke depan."
|
||||
|
|
|
@ -935,18 +935,6 @@ io:
|
|||
view_strikes: Videz antea streki kontre vua konto
|
||||
too_fast: Formulario sendesis tro rapide, probez itere.
|
||||
use_security_key: Uzes sekuresklefo
|
||||
authorize_follow:
|
||||
already_following: Vu ja sequis ca konto
|
||||
already_requested: Vu ja sendis sequodemando a ta konto
|
||||
error: Regretinde, eventis eraro probante konsultar la fora konto
|
||||
follow: Sequar
|
||||
follow_request: 'Vu sendis sequodemando a:'
|
||||
following: 'Suceso! Vu nun sequas:'
|
||||
post_follow:
|
||||
close: O, vu volas jus klozar ca panelo.
|
||||
return: Montrez priflo de uzanti
|
||||
web: Irez a interreto
|
||||
title: Sequar %{acct}
|
||||
challenge:
|
||||
confirm: Durez
|
||||
hint_html: "<strong>Guidilo:</strong> Ni ne demandos vua pasvorto itere til 1 horo."
|
||||
|
|
|
@ -1064,18 +1064,6 @@ is:
|
|||
view_strikes: Skoða fyrri bönn notandaaðgangsins þíns
|
||||
too_fast: Innfyllingarform sent inn of hratt, prófaðu aftur.
|
||||
use_security_key: Nota öryggislykil
|
||||
authorize_follow:
|
||||
already_following: Þú ert að þegar fylgjast með þessum aðgangi
|
||||
already_requested: Þú ert þegar búin/n að senda fylgjendabeiðni á þennan notanda
|
||||
error: Því miður, það kom upp villa við að fletta upp fjartengda notandaaðgangnum
|
||||
follow: Fylgjast með
|
||||
follow_request: 'Þú sendir beiðni um að fylgjast með til:'
|
||||
following: 'Tókst! Þú ert núna að fylgjast með:'
|
||||
post_follow:
|
||||
close: Eða að þú getur lokað þessum glugga.
|
||||
return: Birta notandasnið notandans
|
||||
web: Fara á vefinn
|
||||
title: Fylgjast með %{acct}
|
||||
challenge:
|
||||
confirm: Halda áfram
|
||||
hint_html: "<strong>Ábending:</strong> Við munum ekki spyrja þig um lykilorðið aftur næstu klukkustundina."
|
||||
|
|
|
@ -1062,18 +1062,6 @@ it:
|
|||
view_strikes: Visualizza le sanzioni precedenti prese nei confronti del tuo account
|
||||
too_fast: Modulo inviato troppo velocemente, riprova.
|
||||
use_security_key: Usa la chiave di sicurezza
|
||||
authorize_follow:
|
||||
already_following: Stai già seguendo questo account
|
||||
already_requested: Hai già mandato una richiesta di seguire questo account
|
||||
error: Sfortunatamente c'è stato un errore nel consultare l'account remoto
|
||||
follow: Segui
|
||||
follow_request: 'Hai mandato una richiesta di seguire:'
|
||||
following: 'Accettato! Ora stai seguendo:'
|
||||
post_follow:
|
||||
close: Oppure puoi chiudere questa finestra.
|
||||
return: Mostra il profilo dell'utente
|
||||
web: Vai al web
|
||||
title: Segui %{acct}
|
||||
challenge:
|
||||
confirm: Continua
|
||||
hint_html: "<strong>Suggerimento:</strong> Non ti chiederemo di nuovo la tua password per la prossima ora."
|
||||
|
|
|
@ -1042,18 +1042,6 @@ ja:
|
|||
view_strikes: 過去のストライクを表示
|
||||
too_fast: フォームの送信が速すぎます。もう一度やり直してください。
|
||||
use_security_key: セキュリティキーを使用
|
||||
authorize_follow:
|
||||
already_following: あなたは既にこのアカウントをフォローしています
|
||||
already_requested: 既にこのアカウントへフォローリクエストを送信しています
|
||||
error: 残念ながら、リモートアカウント情報の取得中にエラーが発生しました
|
||||
follow: フォロー
|
||||
follow_request: 'あなたは以下のアカウントにフォローリクエストを送信しました:'
|
||||
following: '成功! あなたは現在以下のアカウントをフォローしています:'
|
||||
post_follow:
|
||||
close: またはこのウィンドウを閉じます。
|
||||
return: ユーザーのプロフィールを見る
|
||||
web: Webを開く
|
||||
title: "%{acct}さんをフォロー"
|
||||
challenge:
|
||||
confirm: 続ける
|
||||
hint_html: 以後1時間はパスワードの再入力を求めません
|
||||
|
|
|
@ -232,17 +232,6 @@ ka:
|
|||
reset_password: პაროლის გადატვირთვა
|
||||
security: უსაფრთხოება
|
||||
set_new_password: ახალი პაროლის დაყენება
|
||||
authorize_follow:
|
||||
already_following: უკვე მიჰყვებით ამ ანგარიშს
|
||||
error: სამწუხაროთ, დისტანციური სერვერის წაკითხვამ გამოიწვია შეცდომა
|
||||
follow: გაყევი
|
||||
follow_request: 'დადევნების მოთხონვა გაეგზავნა:'
|
||||
following: 'წარმატება! ახლა მიჰყვებით:'
|
||||
post_follow:
|
||||
close: ან შეგიძლიათ დახუროთ ეს ფანჯარა.
|
||||
return: მომხმარებლის პროფილის ჩვენება
|
||||
web: ვებზე გადასვლა
|
||||
title: გაყევი %{acct}-ს
|
||||
datetime:
|
||||
distance_in_words:
|
||||
about_x_hours: "%{count}სთ"
|
||||
|
|
|
@ -461,14 +461,6 @@ kab:
|
|||
status:
|
||||
account_status: Addad n umiḍan
|
||||
use_security_key: Seqdec tasarut n teɣlist
|
||||
authorize_follow:
|
||||
already_following: Teṭafareḍ ya kan amiḍan-a
|
||||
follow: Ḍfeṛ
|
||||
following: 'Igerrez! Aqlik teṭafareḍ tura:'
|
||||
post_follow:
|
||||
return: Ssken-d amaɣnu n useqdac
|
||||
web: Ddu γer Web
|
||||
title: Ḍfeṛ %{acct}
|
||||
challenge:
|
||||
confirm: Kemmel
|
||||
invalid_password: Yir awal uffir
|
||||
|
|
|
@ -360,17 +360,6 @@ kk:
|
|||
confirming: Электрондық поштаны растау аяқталуын күтуде.
|
||||
pending: Сіздің өтінішіңіз біздің қызметкерлеріміздің қарауында. Бұл біраз уақыт алуы мүмкін. Өтінішіңіз мақұлданса, сізге электрондық пошта хабарламасы келеді.
|
||||
redirecting_to: Сіздің есептік жазбаңыз белсенді емес, себебі ол %{acct} жүйесіне қайта бағытталуда.
|
||||
authorize_follow:
|
||||
already_following: Бұл аккаунтқа жазылғансыз
|
||||
error: Өкінішке орай, қашықтағы тіркелгіні іздеуде қате пайда болды
|
||||
follow: Жазылу
|
||||
follow_request: 'Сіз жазылуға өтініш жібердіңіз:'
|
||||
following: 'Керемет! Сіз енді жазылдыңыз:'
|
||||
post_follow:
|
||||
close: Немесе терезені жаба салыңыз.
|
||||
return: Қолданушы профилін көрсет
|
||||
web: Вебте ашу
|
||||
title: Жазылу %{acct}
|
||||
challenge:
|
||||
confirm: Жалғастыру
|
||||
hint_html: "<strong> Кеңес: </strong> біз келесі сағат ішінде сізден құпия сөзді қайта сұрамаймыз."
|
||||
|
|
|
@ -1044,18 +1044,6 @@ ko:
|
|||
view_strikes: 내 계정에 대한 과거 중재 기록 보기
|
||||
too_fast: 너무 빠르게 양식이 제출되었습니다, 다시 시도하세요.
|
||||
use_security_key: 보안 키 사용
|
||||
authorize_follow:
|
||||
already_following: 이미 이 계정을 팔로우 하고 있습니다
|
||||
already_requested: 이미 이 계정에게 팔로우 요청을 보냈습니다
|
||||
error: 리모트 계정을 확인하는 도중 오류가 발생했습니다
|
||||
follow: 팔로우
|
||||
follow_request: '팔로우 요청을 보냄:'
|
||||
following: '성공! 당신은 다음 계정을 팔로우 하고 있습니다:'
|
||||
post_follow:
|
||||
close: 혹은, 그저 이 창을 닫을 수도 있습니다.
|
||||
return: 사용자 프로필 보기
|
||||
web: 웹으로 가기
|
||||
title: "%{acct} 를 팔로우"
|
||||
challenge:
|
||||
confirm: 계속
|
||||
hint_html: "<strong>팁:</strong> 한 시간 동안 다시 암호를 묻지 않을 것입니다."
|
||||
|
|
|
@ -979,18 +979,6 @@ ku:
|
|||
view_strikes: Binpêkirinên berê yên dijî ajimêrê xwe bibîne
|
||||
too_fast: Form pir zû hat şandin, dîsa biceribîne.
|
||||
use_security_key: Kilîteke ewlehiyê bi kar bîne
|
||||
authorize_follow:
|
||||
already_following: Jixwe tu vê ajimêrê dişopînî
|
||||
already_requested: Jixwe te ji vê ajimêrê re daxwazîya şopandinê şandi bû
|
||||
error: Mixabin, dema ajimêr hat gerandin çewtiyek çêbû
|
||||
follow: Bişopîne
|
||||
follow_request: 'Te ji vê kesê re daxwazîya şopandinê şand:'
|
||||
following: 'Serkeftin! Tu êdî dikarî bişopînî:'
|
||||
post_follow:
|
||||
close: An jî, tu dikarî tenê ev çarçoveyê bigirî.
|
||||
return: Profîla vê bikarhênerê nîşan bike
|
||||
web: Biçe tevneyê
|
||||
title: "%{acct} bişopîne"
|
||||
challenge:
|
||||
confirm: Bidomîne
|
||||
hint_html: "<strong>Nîşe:</strong>Ji bo demjimêreke din em ê borînpeyva te careke din ji te nexwazin."
|
||||
|
|
|
@ -267,17 +267,6 @@ lt:
|
|||
reset_password: Atstatyti slaptažodį
|
||||
security: Apsauga
|
||||
set_new_password: Nustatyti naują slaptažodį
|
||||
authorize_follow:
|
||||
already_following: Jūs jau sekate šią paskyrą
|
||||
error: Dėja, aptikta klaida ieškant tolimosios paskyros
|
||||
follow: Sekti
|
||||
follow_request: 'Jūs išsiuntėte sekimo prašymą:'
|
||||
following: 'Puiku! Jūs pradėjote sekti:'
|
||||
post_follow:
|
||||
close: Arba, Jūs galite uždaryti šį langą.
|
||||
return: Rodyti vartotojo paskyrą
|
||||
web: Eiti į
|
||||
title: Sekti %{acct}
|
||||
datetime:
|
||||
distance_in_words:
|
||||
about_x_hours: "%{count} val"
|
||||
|
|
|
@ -1057,18 +1057,6 @@ lv:
|
|||
view_strikes: Skati iepriekšējos brīdinājumus par savu kontu
|
||||
too_fast: Veidlapa ir iesniegta pārāk ātri, mēģini vēlreiz.
|
||||
use_security_key: Lietot drošības atslēgu
|
||||
authorize_follow:
|
||||
already_following: Tu jau seko šim kontam
|
||||
already_requested: Tu jau esi nosūtījis sekošanas pieteikumu šim kontam
|
||||
error: Diemžēl, meklējot attālināto kontu, radās kļūda
|
||||
follow: Sekot
|
||||
follow_request: 'Tu esi nosūtījis sekošanas pieteikumu:'
|
||||
following: 'Veiksmīgi! Tu tagad seko:'
|
||||
post_follow:
|
||||
close: Vai vienkārši aizver šo logu.
|
||||
return: Parādīt lietotāja profilu
|
||||
web: Doties uz tīmekli
|
||||
title: Sekot %{acct}
|
||||
challenge:
|
||||
confirm: Turpināt
|
||||
hint_html: "<strong>Padoms:</strong> Nākamās stundas laikā mēs tev vairs neprasīsim paroli."
|
||||
|
|
|
@ -76,16 +76,6 @@ ml:
|
|||
invites:
|
||||
filter:
|
||||
all: എല്ലാം
|
||||
authorize_follow:
|
||||
following: 'വിജയകരം! നിങ്ങൾ ഇപ്പോൾ പിന്തുടരുന്നു:'
|
||||
errors:
|
||||
'400': The request you submitted was invalid or malformed.
|
||||
'403': You don't have permission to view this page.
|
||||
'404': The page you are looking for isn't here.
|
||||
'406': This page is not available in the requested format.
|
||||
'410': The page you were looking for doesn't exist here anymore.
|
||||
'429': Too many requests
|
||||
'503': The page could not be served due to a temporary server failure.
|
||||
filters:
|
||||
contexts:
|
||||
notifications: അറിയിപ്പുകൾ
|
||||
|
|
|
@ -767,14 +767,6 @@ ms:
|
|||
account_status: Status akaun
|
||||
view_strikes: Lihat pelanggaran yang lepas terhadap akaun anda
|
||||
use_security_key: Gunakan kunci keselamatan
|
||||
authorize_follow:
|
||||
follow: Ikut
|
||||
follow_request: 'Anda telah menghantar permintaan mengikut kepada:'
|
||||
post_follow:
|
||||
close: Atau anda boleh tutup tetingkap ini.
|
||||
return: Tunjukkan profil pengguna
|
||||
web: Pergi ke web
|
||||
title: Ikuti %{acct}
|
||||
challenge:
|
||||
confirm: Teruskan
|
||||
invalid_password: Kata laluan tidak sah
|
||||
|
|
|
@ -1042,18 +1042,6 @@ my:
|
|||
view_strikes: သင့်အကောင့်ကို ဆန့်ကျင်သည့် ယခင်ကလုပ်ဆောင်ချက်များကို ကြည့်ပါ
|
||||
too_fast: ဖောင်တင်သည်မှာ မြန်နေပါသည်။ ထပ်စမ်းကြည့်ပါ။
|
||||
use_security_key: လုံခြုံရေးကီးကို သုံးပါ
|
||||
authorize_follow:
|
||||
already_following: သင်သည် ဤအကောင့်ကို စောင့်ကြည့်နေပြီဖြစ်ပါသည်
|
||||
already_requested: သင်သည် ထိုအကောင့်စောင့်ကြည့်ရန် တောင်းဆိုမှုတစ်ခု ပေးပို့ခဲ့ပြီးပါပြီ
|
||||
error: ကံမကောင်းစွာဖြင့် အဝေးမှထိန်းချုပ်သောအကောင့်ရှာဖွေရာတွင် အမှားအယွင်းတစ်ခုရှိခဲ့သည်
|
||||
follow: စောင့်ကြည့်မယ်
|
||||
follow_request: သင်သည် စောင့်ကြည့်မည် တောင်းဆိုချက်တစ်ခု ပေးပို့ထားသည်-
|
||||
following: သင် ယခု အောက်ပါအတိုင်း လုပ်ဆောင်နေပါသည် -
|
||||
post_follow:
|
||||
close: သို့မဟုတ် သင်သည် ဤဝင်းဒိုးကို ပိတ်နိုင်သည်
|
||||
return: အသုံးပြုသူ၏ ပရိုဖိုင်ကိုပြရန်
|
||||
web: ဝဘ်သို့ သွားပါ
|
||||
title: "%{acct} ကို စောင့်ကြည့်မယ်"
|
||||
challenge:
|
||||
confirm: ဆက်လုပ်မည်
|
||||
hint_html: "<strong>အကြံပြုချက် -</strong> နောက်နာရီများတွင် သင့်စကားဝှက်ကို ထပ်မံတောင်းဆိုမည်မဟုတ်ပါ။"
|
||||
|
|
|
@ -1060,18 +1060,6 @@ nl:
|
|||
view_strikes: Bekijk de eerder door moderatoren vastgestelde overtredingen die je hebt gemaakt
|
||||
too_fast: Formulier is te snel ingediend. Probeer het nogmaals.
|
||||
use_security_key: Beveiligingssleutel gebruiken
|
||||
authorize_follow:
|
||||
already_following: Je volgt dit account al
|
||||
already_requested: Je hebt al een volgverzoek naar dat account verstuurd
|
||||
error: Helaas, er is een fout opgetreden bij het opzoeken van de externe account
|
||||
follow: Volgen
|
||||
follow_request: 'Jij hebt een volgverzoek ingediend bij:'
|
||||
following: 'Succes! Jij volgt nu:'
|
||||
post_follow:
|
||||
close: Of je kunt dit venster gewoon sluiten.
|
||||
return: Profiel van deze gebruiker tonen
|
||||
web: Ga naar de webapp
|
||||
title: Volg %{acct}
|
||||
challenge:
|
||||
confirm: Doorgaan
|
||||
hint_html: "<strong>Tip:</strong> We vragen jou het komende uur niet meer naar jouw wachtwoord."
|
||||
|
|
|
@ -1049,18 +1049,6 @@ nn:
|
|||
view_strikes: Vis tidligere advarsler mot kontoen din
|
||||
too_fast: Skjemaet ble sendt inn for raskt, prøv på nytt.
|
||||
use_security_key: Bruk sikkerhetsnøkkel
|
||||
authorize_follow:
|
||||
already_following: Du fylgjer allereie denne kontoen
|
||||
already_requested: Du har allereie sendt ein fylgjespurnad til den kontoen
|
||||
error: Uheldigvis skjedde det en feil da vi prøvde å få tak i en bruker fra en annen instans
|
||||
follow: Fylg
|
||||
follow_request: 'Du har sendt ein fylgjeførespurnad til:'
|
||||
following: 'Suksess! No fylgjer du:'
|
||||
post_follow:
|
||||
close: Eller så kan du berre lukka att dette vindauget.
|
||||
return: Vis brukarprofilen
|
||||
web: Gå til nettet
|
||||
title: Fylg %{acct}
|
||||
challenge:
|
||||
confirm: Hald fram
|
||||
hint_html: "<strong>Tips:</strong> Vi skal ikkje spørja deg om passordet ditt igjen i laupet av den neste timen."
|
||||
|
|
|
@ -988,18 +988,6 @@
|
|||
view_strikes: Vis tidligere advarsler mot kontoen din
|
||||
too_fast: Skjemaet ble sendt inn for raskt, prøv på nytt.
|
||||
use_security_key: Bruk sikkerhetsnøkkel
|
||||
authorize_follow:
|
||||
already_following: Du følger allerede denne kontoen
|
||||
already_requested: Du har allerede sendt en følgeforespørsel til denne kontoen
|
||||
error: Dessverre oppstod det en feil da vi prøvde å få tak i brukeren fra tjeneren
|
||||
follow: Følg
|
||||
follow_request: 'Du har sendt en følgeforespørsel til:'
|
||||
following: 'Suksess! Nå følger du:'
|
||||
post_follow:
|
||||
close: Eller så kan du lukke dette vinduet.
|
||||
return: Gå tilbake til brukerens profil
|
||||
web: Gå til nettsiden
|
||||
title: Følg %{acct}
|
||||
challenge:
|
||||
confirm: Fortsett
|
||||
hint_html: "<strong>Tips:</strong> Vi ber deg ikke om passordet ditt igjen i løpet av neste time."
|
||||
|
|
|
@ -502,17 +502,6 @@ oc:
|
|||
account_status: Estat del compte
|
||||
functional: Vòstre compte es complètament foncional.
|
||||
use_security_key: Utilizar clau de seguretat
|
||||
authorize_follow:
|
||||
already_following: Seguètz ja aqueste compte
|
||||
error: O planhèm, i a agut una error al moment de cercar lo compte
|
||||
follow: Sègre
|
||||
follow_request: 'Avètz demandat de sègre :'
|
||||
following: 'Felicitacion ! Seguètz ara :'
|
||||
post_follow:
|
||||
close: O podètz tampar aquesta fenèstra.
|
||||
return: Veire lo perfil a la persona
|
||||
web: Tornar a l’interfàcia Web
|
||||
title: Sègre %{acct}
|
||||
challenge:
|
||||
confirm: Contunhar
|
||||
hint_html: "<strong>Asutúcia :</strong> vos demandarem pas vòstre senhal de nòu d’aquí unas oras."
|
||||
|
|
|
@ -1096,18 +1096,6 @@ pl:
|
|||
view_strikes: Zobacz dawne ostrzeżenia nałożone na twoje konto
|
||||
too_fast: Zbyt szybko przesłano formularz, spróbuj ponownie.
|
||||
use_security_key: Użyj klucza bezpieczeństwa
|
||||
authorize_follow:
|
||||
already_following: Już obserwujesz to konto
|
||||
already_requested: Już wysłałeś(-aś) prośbę o możliwość obserwowania tego konta
|
||||
error: Niestety, podczas sprawdzania zdalnego konta wystąpił błąd
|
||||
follow: Obserwuj
|
||||
follow_request: 'Wysłano prośbę o możliwość obserwowania:'
|
||||
following: 'Pomyślnie! Od teraz obserwujesz:'
|
||||
post_follow:
|
||||
close: Ewentualnie, możesz po prostu zamknąć tę stronę.
|
||||
return: Pokaż stronę użytkownika
|
||||
web: Przejdź do sieci
|
||||
title: Obserwuj %{acct}
|
||||
challenge:
|
||||
confirm: Kontynuuj
|
||||
hint_html: "<strong>Informacja:</strong> Nie będziemy prosić Cię o ponowne podanie hasła przez następną godzinę."
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue