Merge branch 'master' into glitch-soc/merge-upstream

Conflicts:
- app/controllers/statuses_controller.rb
  minor conflict because of glitch-soc's theming system
- app/controllers/stream_entries_controller.rb
  minor conflict because of glitch-soc's theming system
signup-info-prompt
Thibaut Girka 2019-05-10 17:09:12 +02:00
commit 68629f2773
40 changed files with 669 additions and 130 deletions

View File

@ -10,6 +10,7 @@ DB_NAME=postgres
DB_PASS= DB_PASS=
DB_PORT=5432 DB_PORT=5432
# Optional ElasticSearch configuration # Optional ElasticSearch configuration
# You may also set ES_PREFIX to share the same cluster between multiple Mastodon servers (falls back to REDIS_NAMESPACE if not set)
# ES_ENABLED=true # ES_ENABLED=true
# ES_HOST=es # ES_HOST=es
# ES_PORT=9200 # ES_PORT=9200

View File

@ -3,6 +3,18 @@ Changelog
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [2.8.2] - 2019-05-05
### Added
- Add `SOURCE_TAG` environment variable ([ushitora-anqou](https://github.com/tootsuite/mastodon/pull/10698))
### Fixed
- Fix cropped hero image on frontpage ([BaptisteGelez](https://github.com/tootsuite/mastodon/pull/10702))
- Fix blurhash gem not compiling on some operating systems ([Gargron](https://github.com/tootsuite/mastodon/pull/10700))
- Fix unexpected CSS animations in some browsers ([ThibG](https://github.com/tootsuite/mastodon/pull/10699))
- Fix closing video modal scrolling timelines to top ([ThibG](https://github.com/tootsuite/mastodon/pull/10695))
## [2.8.1] - 2019-05-04 ## [2.8.1] - 2019-05-04
### Added ### Added

View File

@ -117,7 +117,7 @@ group :test do
gem 'rspec-sidekiq', '~> 3.0' gem 'rspec-sidekiq', '~> 3.0'
gem 'simplecov', '~> 0.16', require: false gem 'simplecov', '~> 0.16', require: false
gem 'webmock', '~> 3.5' gem 'webmock', '~> 3.5'
gem 'parallel_tests', '~> 2.28' gem 'parallel_tests', '~> 2.29'
end end
group :development do group :development do

View File

@ -395,7 +395,7 @@ GEM
av (~> 0.9.0) av (~> 0.9.0)
paperclip (>= 2.5.2) paperclip (>= 2.5.2)
parallel (1.17.0) parallel (1.17.0)
parallel_tests (2.28.0) parallel_tests (2.29.0)
parallel parallel
parser (2.6.3.0) parser (2.6.3.0)
ast (~> 2.4.0) ast (~> 2.4.0)
@ -480,7 +480,7 @@ GEM
link_header (~> 0.0, >= 0.0.8) link_header (~> 0.0, >= 0.0.8)
rdf-normalize (0.3.3) rdf-normalize (0.3.3)
rdf (>= 2.2, < 4.0) rdf (>= 2.2, < 4.0)
redis (4.1.0) redis (4.1.1)
redis-actionpack (5.0.2) redis-actionpack (5.0.2)
actionpack (>= 4.0, < 6) actionpack (>= 4.0, < 6)
redis-rack (>= 1, < 3) redis-rack (>= 1, < 3)
@ -727,7 +727,7 @@ DEPENDENCIES
ox (~> 2.10) ox (~> 2.10)
paperclip (~> 6.0) paperclip (~> 6.0)
paperclip-av-transcoder (~> 0.6) paperclip-av-transcoder (~> 0.6)
parallel_tests (~> 2.28) parallel_tests (~> 2.29)
pg (~> 1.1) pg (~> 1.1)
pghero (~> 2.2) pghero (~> 2.2)
pkg-config (~> 1.3) pkg-config (~> 1.3)

View File

@ -28,7 +28,11 @@ class StatusesController < ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
use_pack 'public' use_pack 'public'
mark_cacheable! unless user_signed_in?
unless user_signed_in?
skip_session!
expires_in 10.seconds, public: true
end
@body_classes = 'with-modals' @body_classes = 'with-modals'

View File

@ -16,6 +16,12 @@ class StreamEntriesController < ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
use_pack 'public' use_pack 'public'
unless user_signed_in?
skip_session!
expires_in 5.minutes, public: true
end
redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) if @type == 'status' redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) if @type == 'status'
end end

View File

@ -383,7 +383,7 @@ export function readyComposeSuggestionsAccounts(token, accounts) {
}; };
}; };
export function selectComposeSuggestion(position, token, suggestion) { export function selectComposeSuggestion(position, token, suggestion, path) {
return (dispatch, getState) => { return (dispatch, getState) => {
let completion, startPosition; let completion, startPosition;
@ -405,6 +405,7 @@ export function selectComposeSuggestion(position, token, suggestion) {
position: startPosition, position: startPosition,
token, token,
completion, completion,
path,
}); });
}; };
}; };

View File

@ -0,0 +1,229 @@
import React from 'react';
import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container';
import AutosuggestEmoji from './autosuggest_emoji';
import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types';
import { isRtl } from '../rtl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import classNames from 'classnames';
import { List as ImmutableList } from 'immutable';
const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => {
let word;
let left = str.slice(0, caretPosition).search(/\S+$/);
let right = str.slice(caretPosition).search(/\s/);
if (right < 0) {
word = str.slice(left);
} else {
word = str.slice(left, right + caretPosition);
}
if (!word || word.trim().length < 3 || searchTokens.indexOf(word[0]) === -1) {
return [null, null];
}
word = word.trim().toLowerCase();
if (word.length > 0) {
return [left + 1, word];
} else {
return [null, null];
}
};
export default class AutosuggestInput extends ImmutablePureComponent {
static propTypes = {
value: PropTypes.string,
suggestions: ImmutablePropTypes.list,
disabled: PropTypes.bool,
placeholder: PropTypes.string,
onSuggestionSelected: PropTypes.func.isRequired,
onSuggestionsClearRequested: PropTypes.func.isRequired,
onSuggestionsFetchRequested: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onKeyUp: PropTypes.func,
onKeyDown: PropTypes.func,
autoFocus: PropTypes.bool,
className: PropTypes.string,
id: PropTypes.string,
searchTokens: PropTypes.list,
maxLength: PropTypes.number,
};
static defaultProps = {
autoFocus: true,
searchTokens: ImmutableList(['@', ':', '#']),
};
state = {
suggestionsHidden: true,
focused: false,
selectedSuggestion: 0,
lastToken: null,
tokenStart: 0,
};
onChange = (e) => {
const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart, this.props.searchTokens);
if (token !== null && this.state.lastToken !== token) {
this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart });
this.props.onSuggestionsFetchRequested(token);
} else if (token === null) {
this.setState({ lastToken: null });
this.props.onSuggestionsClearRequested();
}
this.props.onChange(e);
}
onKeyDown = (e) => {
const { suggestions, disabled } = this.props;
const { selectedSuggestion, suggestionsHidden } = this.state;
if (disabled) {
e.preventDefault();
return;
}
if (e.which === 229 || e.isComposing) {
// Ignore key events during text composition
// e.key may be a name of the physical key even in this case (e.x. Safari / Chrome on Mac)
return;
}
switch(e.key) {
case 'Escape':
if (suggestions.size === 0 || suggestionsHidden) {
document.querySelector('.ui').parentElement.focus();
} else {
e.preventDefault();
this.setState({ suggestionsHidden: true });
}
break;
case 'ArrowDown':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
}
break;
case 'ArrowUp':
if (suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
}
break;
case 'Enter':
case 'Tab':
// Select suggestion
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
e.preventDefault();
e.stopPropagation();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
}
break;
}
if (e.defaultPrevented || !this.props.onKeyDown) {
return;
}
this.props.onKeyDown(e);
}
onBlur = () => {
this.setState({ suggestionsHidden: true, focused: false });
}
onFocus = () => {
this.setState({ focused: true });
}
onSuggestionClick = (e) => {
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
e.preventDefault();
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
this.input.focus();
}
componentWillReceiveProps (nextProps) {
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
this.setState({ suggestionsHidden: false });
}
}
setInput = (c) => {
this.input = c;
}
renderSuggestion = (suggestion, i) => {
const { selectedSuggestion } = this.state;
let inner, key;
if (typeof suggestion === 'object') {
inner = <AutosuggestEmoji emoji={suggestion} />;
key = suggestion.id;
} else if (suggestion[0] === '#') {
inner = suggestion;
key = suggestion;
} else {
inner = <AutosuggestAccountContainer id={suggestion} />;
key = suggestion;
}
return (
<div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
{inner}
</div>
);
}
render () {
const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength } = this.props;
const { suggestionsHidden } = this.state;
const style = { direction: 'ltr' };
if (isRtl(value)) {
style.direction = 'rtl';
}
return (
<div className='autosuggest-input'>
<label>
<span style={{ display: 'none' }}>{placeholder}</span>
<input
type='text'
ref={this.setInput}
disabled={disabled}
placeholder={placeholder}
autoFocus={autoFocus}
value={value}
onChange={this.onChange}
onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur}
style={style}
aria-autocomplete='list'
id={id}
className={className}
maxLength={maxLength}
/>
</label>
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
{suggestions.map(this.renderSuggestion)}
</div>
</div>
);
}
}

View File

@ -55,7 +55,8 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
}; };
state = { state = {
suggestionsHidden: false, suggestionsHidden: true,
focused: false,
selectedSuggestion: 0, selectedSuggestion: 0,
lastToken: null, lastToken: null,
tokenStart: 0, tokenStart: 0,
@ -134,7 +135,11 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
} }
onBlur = () => { onBlur = () => {
this.setState({ suggestionsHidden: true }); this.setState({ suggestionsHidden: true, focused: false });
}
onFocus = () => {
this.setState({ focused: true });
} }
onSuggestionClick = (e) => { onSuggestionClick = (e) => {
@ -145,7 +150,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
} }
componentWillReceiveProps (nextProps) { componentWillReceiveProps (nextProps) {
if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) { if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) {
this.setState({ suggestionsHidden: false }); this.setState({ suggestionsHidden: false });
} }
} }
@ -207,6 +212,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
onKeyUp={onKeyUp} onKeyUp={onKeyUp}
onFocus={this.onFocus}
onBlur={this.onBlur} onBlur={this.onBlur}
onPaste={this.onPaste} onPaste={this.onPaste}
style={style} style={style}

View File

@ -69,19 +69,19 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
}, },
onModalReblog (status) { onModalReblog (status) {
dispatch(reblog(status));
},
onReblog (status, e) {
if (status.get('reblogged')) { if (status.get('reblogged')) {
dispatch(unreblog(status)); dispatch(unreblog(status));
} else { } else {
dispatch(reblog(status));
}
},
onReblog (status, e) {
if (e.shiftKey || !boostModal) { if (e.shiftKey || !boostModal) {
this.onModalReblog(status); this.onModalReblog(status);
} else { } else {
dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog })); dispatch(openModal('BOOST', { status, onReblog: this.onModalReblog }));
} }
}
}, },
onFavourite (status) { onFavourite (status) {

View File

@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import Icon from 'mastodon/components/icon';
import { autoPlayGif, displayMedia } from 'mastodon/initial_state'; import { autoPlayGif, displayMedia } from 'mastodon/initial_state';
import classNames from 'classnames'; import classNames from 'classnames';
import { decode } from 'blurhash'; import { decode } from 'blurhash';
@ -88,8 +89,10 @@ export default class MediaItem extends ImmutablePureComponent {
const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`; const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
const height = width; const height = width;
const status = attachment.get('status'); const status = attachment.get('status');
const title = status.get('spoiler_text') || attachment.get('description');
let thumbnail = ''; let thumbnail = '';
let icon;
if (attachment.get('type') === 'unknown') { if (attachment.get('type') === 'unknown') {
// Skip // Skip
@ -131,11 +134,20 @@ export default class MediaItem extends ImmutablePureComponent {
); );
} }
if (!visible) {
icon = (
<span className='account-gallery__item__icons'>
<Icon id='eye-slash' />
</span>
);
}
return ( return (
<div className='account-gallery__item' style={{ width, height }}> <div className='account-gallery__item' style={{ width, height }}>
<a className='media-gallery__item-thumbnail' href={status.get('url')} target='_blank' onClick={this.handleClick}> <a className='media-gallery__item-thumbnail' href={status.get('url')} target='_blank' onClick={this.handleClick} title={title}>
<canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })} /> <canvas width={32} height={32} ref={this.setCanvasRef} className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })} />
{visible && thumbnail} {visible && thumbnail}
{!visible && icon}
</a> </a>
</div> </div>
); );

View File

@ -5,6 +5,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import ReplyIndicatorContainer from '../containers/reply_indicator_container'; import ReplyIndicatorContainer from '../containers/reply_indicator_container';
import AutosuggestTextarea from '../../../components/autosuggest_textarea'; import AutosuggestTextarea from '../../../components/autosuggest_textarea';
import AutosuggestInput from '../../../components/autosuggest_input';
import PollButtonContainer from '../containers/poll_button_container'; import PollButtonContainer from '../containers/poll_button_container';
import UploadButtonContainer from '../containers/upload_button_container'; import UploadButtonContainer from '../containers/upload_button_container';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
@ -103,7 +104,11 @@ class ComposeForm extends ImmutablePureComponent {
} }
onSuggestionSelected = (tokenStart, token, value) => { onSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value); this.props.onSuggestionSelected(tokenStart, token, value, ['text']);
}
onSpoilerSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['spoiler_text']);
} }
handleChangeSpoilerText = (e) => { handleChangeSpoilerText = (e) => {
@ -136,7 +141,7 @@ class ComposeForm extends ImmutablePureComponent {
this.autosuggestTextarea.textarea.focus(); this.autosuggestTextarea.textarea.focus();
} else if (this.props.spoiler !== prevProps.spoiler) { } else if (this.props.spoiler !== prevProps.spoiler) {
if (this.props.spoiler) { if (this.props.spoiler) {
this.spoilerText.focus(); this.spoilerText.input.focus();
} else { } else {
this.autosuggestTextarea.textarea.focus(); this.autosuggestTextarea.textarea.focus();
} }
@ -179,10 +184,21 @@ class ComposeForm extends ImmutablePureComponent {
<ReplyIndicatorContainer /> <ReplyIndicatorContainer />
<div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`}> <div className={`spoiler-input ${this.props.spoiler ? 'spoiler-input--visible' : ''}`}>
<label> <AutosuggestInput
<span style={{ display: 'none' }}>{intl.formatMessage(messages.spoiler_placeholder)}</span> placeholder={intl.formatMessage(messages.spoiler_placeholder)}
<input placeholder={intl.formatMessage(messages.spoiler_placeholder)} value={this.props.spoilerText} onChange={this.handleChangeSpoilerText} onKeyDown={this.handleKeyDown} tabIndex={this.props.spoiler ? 0 : -1} type='text' className='spoiler-input__input' id='cw-spoiler-input' ref={this.setSpoilerText} /> value={this.props.spoilerText}
</label> onChange={this.handleChangeSpoilerText}
onKeyDown={this.handleKeyDown}
disabled={!this.props.spoiler}
ref={this.setSpoilerText}
suggestions={this.props.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSpoilerSuggestionSelected}
searchTokens={[':']}
id='cw-spoiler-input'
className='spoiler-input__input'
/>
</div> </div>
<div className='compose-form__autosuggest-wrapper'> <div className='compose-form__autosuggest-wrapper'>

View File

@ -5,6 +5,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import IconButton from 'mastodon/components/icon_button'; import IconButton from 'mastodon/components/icon_button';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
import AutosuggestInput from 'mastodon/components/autosuggest_input';
import classNames from 'classnames'; import classNames from 'classnames';
const messages = defineMessages({ const messages = defineMessages({
@ -27,6 +28,10 @@ class Option extends React.PureComponent {
onChange: PropTypes.func.isRequired, onChange: PropTypes.func.isRequired,
onRemove: PropTypes.func.isRequired, onRemove: PropTypes.func.isRequired,
onToggleMultiple: PropTypes.func.isRequired, onToggleMultiple: PropTypes.func.isRequired,
suggestions: ImmutablePropTypes.list,
onClearSuggestions: PropTypes.func.isRequired,
onFetchSuggestions: PropTypes.func.isRequired,
onSuggestionSelected: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
@ -38,12 +43,25 @@ class Option extends React.PureComponent {
this.props.onRemove(this.props.index); this.props.onRemove(this.props.index);
}; };
handleToggleMultiple = e => { handleToggleMultiple = e => {
this.props.onToggleMultiple(); this.props.onToggleMultiple();
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
}; };
onSuggestionsClearRequested = () => {
this.props.onClearSuggestions();
}
onSuggestionsFetchRequested = (token) => {
this.props.onFetchSuggestions(token);
}
onSuggestionSelected = (tokenStart, token, value) => {
this.props.onSuggestionSelected(tokenStart, token, value, ['poll', 'options', this.props.index]);
}
render () { render () {
const { isPollMultiple, title, index, intl } = this.props; const { isPollMultiple, title, index, intl } = this.props;
@ -57,12 +75,16 @@ class Option extends React.PureComponent {
tabIndex='0' tabIndex='0'
/> />
<input <AutosuggestInput
type='text'
placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })} placeholder={intl.formatMessage(messages.option_placeholder, { number: index + 1 })}
maxLength={25} maxLength={25}
value={title} value={title}
onChange={this.handleOptionTitleChange} onChange={this.handleOptionTitleChange}
suggestions={this.props.suggestions}
onSuggestionsFetchRequested={this.onSuggestionsFetchRequested}
onSuggestionsClearRequested={this.onSuggestionsClearRequested}
onSuggestionSelected={this.onSuggestionSelected}
searchTokens={[':']}
/> />
</label> </label>
@ -87,6 +109,10 @@ class PollForm extends ImmutablePureComponent {
onAddOption: PropTypes.func.isRequired, onAddOption: PropTypes.func.isRequired,
onRemoveOption: PropTypes.func.isRequired, onRemoveOption: PropTypes.func.isRequired,
onChangeSettings: PropTypes.func.isRequired, onChangeSettings: PropTypes.func.isRequired,
suggestions: ImmutablePropTypes.list,
onClearSuggestions: PropTypes.func.isRequired,
onFetchSuggestions: PropTypes.func.isRequired,
onSuggestionSelected: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
}; };
@ -103,7 +129,7 @@ class PollForm extends ImmutablePureComponent {
}; };
render () { render () {
const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl } = this.props; const { options, expiresIn, isMultiple, onChangeOption, onRemoveOption, intl, ...other } = this.props;
if (!options) { if (!options) {
return null; return null;
@ -112,7 +138,7 @@ class PollForm extends ImmutablePureComponent {
return ( return (
<div className='compose-form__poll-wrapper'> <div className='compose-form__poll-wrapper'>
<ul> <ul>
{options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} />)} {options.map((title, i) => <Option title={title} key={i} index={i} onChange={onChangeOption} onRemove={onRemoveOption} isPollMultiple={isMultiple} onToggleMultiple={this.handleToggleMultiple} {...other} />)}
</ul> </ul>
<div className='poll__footer'> <div className='poll__footer'>

View File

@ -45,8 +45,8 @@ const mapDispatchToProps = (dispatch) => ({
dispatch(fetchComposeSuggestions(token)); dispatch(fetchComposeSuggestions(token));
}, },
onSuggestionSelected (position, token, suggestion) { onSuggestionSelected (position, token, suggestion, path) {
dispatch(selectComposeSuggestion(position, token, suggestion)); dispatch(selectComposeSuggestion(position, token, suggestion, path));
}, },
onChangeSpoilerText (checked) { onChangeSpoilerText (checked) {

View File

@ -1,8 +1,14 @@
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import PollForm from '../components/poll_form'; import PollForm from '../components/poll_form';
import { addPollOption, removePollOption, changePollOption, changePollSettings } from '../../../actions/compose'; import { addPollOption, removePollOption, changePollOption, changePollSettings } from '../../../actions/compose';
import {
clearComposeSuggestions,
fetchComposeSuggestions,
selectComposeSuggestion,
} from '../../../actions/compose';
const mapStateToProps = state => ({ const mapStateToProps = state => ({
suggestions: state.getIn(['compose', 'suggestions']),
options: state.getIn(['compose', 'poll', 'options']), options: state.getIn(['compose', 'poll', 'options']),
expiresIn: state.getIn(['compose', 'poll', 'expires_in']), expiresIn: state.getIn(['compose', 'poll', 'expires_in']),
isMultiple: state.getIn(['compose', 'poll', 'multiple']), isMultiple: state.getIn(['compose', 'poll', 'multiple']),
@ -24,6 +30,19 @@ const mapDispatchToProps = dispatch => ({
onChangeSettings(expiresIn, isMultiple) { onChangeSettings(expiresIn, isMultiple) {
dispatch(changePollSettings(expiresIn, isMultiple)); dispatch(changePollSettings(expiresIn, isMultiple));
}, },
onClearSuggestions () {
dispatch(clearComposeSuggestions());
},
onFetchSuggestions (token) {
dispatch(fetchComposeSuggestions(token));
},
onSuggestionSelected (position, token, accountId, path) {
dispatch(selectComposeSuggestion(position, token, accountId, path));
},
}); });
export default connect(mapStateToProps, mapDispatchToProps)(PollForm); export default connect(mapStateToProps, mapDispatchToProps)(PollForm);

View File

@ -11,6 +11,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import Icon from 'mastodon/components/icon'; import Icon from 'mastodon/components/icon';
const messages = defineMessages({ const messages = defineMessages({
cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
}); });
@ -51,6 +52,7 @@ class BoostModal extends ImmutablePureComponent {
render () { render () {
const { status, intl } = this.props; const { status, intl } = this.props;
const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog;
return ( return (
<div className='modal-root__modal boost-modal'> <div className='modal-root__modal boost-modal'>
@ -76,7 +78,7 @@ class BoostModal extends ImmutablePureComponent {
<div className='boost-modal__action-bar'> <div className='boost-modal__action-bar'>
<div><FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} /></div> <div><FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} /></div>
<Button text={intl.formatMessage(messages.reblog)} onClick={this.handleReblog} ref={this.setRef} /> <Button text={intl.formatMessage(buttonText)} onClick={this.handleReblog} ref={this.setRef} />
</div> </div>
</div> </div>
); );

View File

@ -17,7 +17,7 @@
"account.hide_reblogs": "Amaga els impulsos de @{name}", "account.hide_reblogs": "Amaga els impulsos de @{name}",
"account.link_verified_on": "La propietat d'aquest enllaç es va verificar el dia {date}", "account.link_verified_on": "La propietat d'aquest enllaç es va verificar el dia {date}",
"account.locked_info": "Aquest estat de privadesa del compte està definit com a bloquejat. El propietari revisa manualment qui pot seguir-lo.", "account.locked_info": "Aquest estat de privadesa del compte està definit com a bloquejat. El propietari revisa manualment qui pot seguir-lo.",
"account.media": "Media", "account.media": "Mèdia",
"account.mention": "Esmentar @{name}", "account.mention": "Esmentar @{name}",
"account.moved_to": "{name} s'ha mogut a:", "account.moved_to": "{name} s'ha mogut a:",
"account.mute": "Silencia @{name}", "account.mute": "Silencia @{name}",
@ -77,6 +77,7 @@
"compose_form.poll.remove_option": "Elimina aquesta opció", "compose_form.poll.remove_option": "Elimina aquesta opció",
"compose_form.publish": "Toot", "compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.hide": "Marcar mèdia com a sensible",
"compose_form.sensitive.marked": "Mèdia marcat com a sensible", "compose_form.sensitive.marked": "Mèdia marcat com a sensible",
"compose_form.sensitive.unmarked": "Mèdia no està marcat com a sensible", "compose_form.sensitive.unmarked": "Mèdia no està marcat com a sensible",
"compose_form.spoiler.marked": "Text es ocult sota l'avís", "compose_form.spoiler.marked": "Text es ocult sota l'avís",
@ -85,7 +86,7 @@
"confirmation_modal.cancel": "Cancel·la", "confirmation_modal.cancel": "Cancel·la",
"confirmations.block.block_and_report": "Block & Report", "confirmations.block.block_and_report": "Block & Report",
"confirmations.block.confirm": "Bloca", "confirmations.block.confirm": "Bloca",
"confirmations.block.message": "Estàs segur que vols blocar {name}?", "confirmations.block.message": "Estàs segur que vols bloquejar a {name}?",
"confirmations.delete.confirm": "Suprimeix", "confirmations.delete.confirm": "Suprimeix",
"confirmations.delete.message": "Estàs segur que vols suprimir aquest estat?", "confirmations.delete.message": "Estàs segur que vols suprimir aquest estat?",
"confirmations.delete_list.confirm": "Suprimeix", "confirmations.delete_list.confirm": "Suprimeix",
@ -125,7 +126,7 @@
"empty_column.favourited_statuses": "Encara no tens cap toot favorit. Quan en tinguis, apareixerà aquí.", "empty_column.favourited_statuses": "Encara no tens cap toot favorit. Quan en tinguis, apareixerà aquí.",
"empty_column.favourites": "Encara ningú ha marcat aquest toot com a favorit. Quan algú ho faci, apareixera aquí.", "empty_column.favourites": "Encara ningú ha marcat aquest toot com a favorit. Quan algú ho faci, apareixera aquí.",
"empty_column.follow_requests": "Encara no teniu cap petició de seguiment. Quan rebeu una, apareixerà aquí.", "empty_column.follow_requests": "Encara no teniu cap petició de seguiment. Quan rebeu una, apareixerà aquí.",
"empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.", "empty_column.hashtag": "Encara no hi ha res en aquesta etiqueta.",
"empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.", "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.",
"empty_column.home.public_timeline": "la línia de temps pública", "empty_column.home.public_timeline": "la línia de temps pública",
"empty_column.list": "Encara no hi ha res en aquesta llista. Quan els membres d'aquesta llista publiquin nous estats, apareixeran aquí.", "empty_column.list": "Encara no hi ha res en aquesta llista. Quan els membres d'aquesta llista publiquin nous estats, apareixeran aquí.",
@ -209,6 +210,7 @@
"lightbox.close": "Tancar", "lightbox.close": "Tancar",
"lightbox.next": "Següent", "lightbox.next": "Següent",
"lightbox.previous": "Anterior", "lightbox.previous": "Anterior",
"lightbox.view_context": "Veure el context",
"lists.account.add": "Afegir a la llista", "lists.account.add": "Afegir a la llista",
"lists.account.remove": "Treure de la llista", "lists.account.remove": "Treure de la llista",
"lists.delete": "Delete list", "lists.delete": "Delete list",
@ -340,7 +342,6 @@
"status.reply": "Respondre", "status.reply": "Respondre",
"status.replyAll": "Respondre al tema", "status.replyAll": "Respondre al tema",
"status.report": "Informar sobre @{name}", "status.report": "Informar sobre @{name}",
"status.sensitive_toggle": "Clic per veure",
"status.sensitive_warning": "Contingut sensible", "status.sensitive_warning": "Contingut sensible",
"status.share": "Compartir", "status.share": "Compartir",
"status.show_less": "Mostra menys", "status.show_less": "Mostra menys",

View File

@ -180,10 +180,6 @@
{ {
"defaultMessage": "Media hidden", "defaultMessage": "Media hidden",
"id": "status.media_hidden" "id": "status.media_hidden"
},
{
"defaultMessage": "Click to view",
"id": "status.sensitive_toggle"
} }
], ],
"path": "app/javascript/mastodon/components/media_gallery.json" "path": "app/javascript/mastodon/components/media_gallery.json"
@ -1096,6 +1092,10 @@
{ {
"defaultMessage": "Media is not marked as sensitive", "defaultMessage": "Media is not marked as sensitive",
"id": "compose_form.sensitive.unmarked" "id": "compose_form.sensitive.unmarked"
},
{
"defaultMessage": "Mark media as sensitive",
"id": "compose_form.sensitive.hide"
} }
], ],
"path": "app/javascript/mastodon/features/compose/containers/sensitive_button_container.json" "path": "app/javascript/mastodon/features/compose/containers/sensitive_button_container.json"
@ -2262,6 +2262,10 @@
{ {
"defaultMessage": "Next", "defaultMessage": "Next",
"id": "lightbox.next" "id": "lightbox.next"
},
{
"defaultMessage": "View context",
"id": "lightbox.view_context"
} }
], ],
"path": "app/javascript/mastodon/features/ui/components/media_modal.json" "path": "app/javascript/mastodon/features/ui/components/media_modal.json"
@ -2354,6 +2358,15 @@
], ],
"path": "app/javascript/mastodon/features/ui/components/upload_area.json" "path": "app/javascript/mastodon/features/ui/components/upload_area.json"
}, },
{
"descriptors": [
{
"defaultMessage": "View context",
"id": "lightbox.view_context"
}
],
"path": "app/javascript/mastodon/features/ui/components/video_modal.json"
},
{ {
"descriptors": [ "descriptors": [
{ {
@ -2408,10 +2421,6 @@
{ {
"defaultMessage": "Media hidden", "defaultMessage": "Media hidden",
"id": "status.media_hidden" "id": "status.media_hidden"
},
{
"defaultMessage": "Click to view",
"id": "status.sensitive_toggle"
} }
], ],
"path": "app/javascript/mastodon/features/video/index.json" "path": "app/javascript/mastodon/features/video/index.json"

View File

@ -81,6 +81,7 @@
"compose_form.poll.remove_option": "Remove this choice", "compose_form.poll.remove_option": "Remove this choice",
"compose_form.publish": "Toot", "compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.hide": "Mark media as sensitive",
"compose_form.sensitive.marked": "Media is marked as sensitive", "compose_form.sensitive.marked": "Media is marked as sensitive",
"compose_form.sensitive.unmarked": "Media is not marked as sensitive", "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
"compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.marked": "Text is hidden behind warning",
@ -213,6 +214,7 @@
"lightbox.close": "Close", "lightbox.close": "Close",
"lightbox.next": "Next", "lightbox.next": "Next",
"lightbox.previous": "Previous", "lightbox.previous": "Previous",
"lightbox.view_context": "View context",
"lists.account.add": "Add to list", "lists.account.add": "Add to list",
"lists.account.remove": "Remove from list", "lists.account.remove": "Remove from list",
"lists.delete": "Delete list", "lists.delete": "Delete list",
@ -345,7 +347,6 @@
"status.reply": "Reply", "status.reply": "Reply",
"status.replyAll": "Reply to thread", "status.replyAll": "Reply to thread",
"status.report": "Report @{name}", "status.report": "Report @{name}",
"status.sensitive_toggle": "Click to view",
"status.sensitive_warning": "Sensitive content", "status.sensitive_warning": "Sensitive content",
"status.share": "Share", "status.share": "Share",
"status.show_less": "Show less", "status.show_less": "Show less",

View File

@ -81,6 +81,7 @@
"compose_form.poll.remove_option": "この項目を削除", "compose_form.poll.remove_option": "この項目を削除",
"compose_form.publish": "トゥート", "compose_form.publish": "トゥート",
"compose_form.publish_loud": "{publish}", "compose_form.publish_loud": "{publish}",
"compose_form.sensitive.hide": "メディアを閲覧注意にする",
"compose_form.sensitive.marked": "メディアに閲覧注意が設定されています", "compose_form.sensitive.marked": "メディアに閲覧注意が設定されています",
"compose_form.sensitive.unmarked": "メディアに閲覧注意が設定されていません", "compose_form.sensitive.unmarked": "メディアに閲覧注意が設定されていません",
"compose_form.spoiler.marked": "閲覧注意が設定されています", "compose_form.spoiler.marked": "閲覧注意が設定されています",
@ -213,6 +214,7 @@
"lightbox.close": "閉じる", "lightbox.close": "閉じる",
"lightbox.next": "次", "lightbox.next": "次",
"lightbox.previous": "前", "lightbox.previous": "前",
"lightbox.view_context": "トゥートを表示",
"lists.account.add": "リストに追加", "lists.account.add": "リストに追加",
"lists.account.remove": "リストから外す", "lists.account.remove": "リストから外す",
"lists.delete": "リストを削除", "lists.delete": "リストを削除",
@ -345,7 +347,6 @@
"status.reply": "返信", "status.reply": "返信",
"status.replyAll": "全員に返信", "status.replyAll": "全員に返信",
"status.report": "@{name}さんを通報", "status.report": "@{name}さんを通報",
"status.sensitive_toggle": "クリックして表示",
"status.sensitive_warning": "閲覧注意", "status.sensitive_warning": "閲覧注意",
"status.share": "共有", "status.share": "共有",
"status.show_less": "隠す", "status.show_less": "隠す",

View File

@ -77,6 +77,7 @@
"compose_form.poll.remove_option": "Deze keuze verwijderen", "compose_form.poll.remove_option": "Deze keuze verwijderen",
"compose_form.publish": "Toot", "compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.hide": "Media als gevoelig markeren",
"compose_form.sensitive.marked": "Media is als gevoelig gemarkeerd", "compose_form.sensitive.marked": "Media is als gevoelig gemarkeerd",
"compose_form.sensitive.unmarked": "Media is niet als gevoelig gemarkeerd", "compose_form.sensitive.unmarked": "Media is niet als gevoelig gemarkeerd",
"compose_form.spoiler.marked": "Tekst is achter een waarschuwing verborgen", "compose_form.spoiler.marked": "Tekst is achter een waarschuwing verborgen",

View File

@ -131,13 +131,15 @@ function removeMedia(state, mediaId) {
}); });
}; };
const insertSuggestion = (state, position, token, completion) => { const insertSuggestion = (state, position, token, completion, path) => {
return state.withMutations(map => { return state.withMutations(map => {
map.update('text', oldText => `${oldText.slice(0, position)}${completion} ${oldText.slice(position + token.length)}`); map.updateIn(path, oldText => `${oldText.slice(0, position)}${completion} ${oldText.slice(position + token.length)}`);
map.set('suggestion_token', null); map.set('suggestion_token', null);
map.update('suggestions', ImmutableList(), list => list.clear()); map.set('suggestions', ImmutableList());
if (path.length === 1 && path[0] === 'text') {
map.set('focusDate', new Date()); map.set('focusDate', new Date());
map.set('caretPosition', position + completion.length + 1); map.set('caretPosition', position + completion.length + 1);
}
map.set('idempotencyKey', uuid()); map.set('idempotencyKey', uuid());
}); });
}; };
@ -304,7 +306,7 @@ export default function compose(state = initialState, action) {
case COMPOSE_SUGGESTIONS_READY: case COMPOSE_SUGGESTIONS_READY:
return state.set('suggestions', ImmutableList(action.accounts ? action.accounts.map(item => item.id) : action.emojis)).set('suggestion_token', action.token); return state.set('suggestions', ImmutableList(action.accounts ? action.accounts.map(item => item.id) : action.emojis)).set('suggestion_token', action.token);
case COMPOSE_SUGGESTION_SELECT: case COMPOSE_SUGGESTION_SELECT:
return insertSuggestion(state, action.position, action.token, action.completion); return insertSuggestion(state, action.position, action.token, action.completion, action.path);
case COMPOSE_SUGGESTION_TAGS_UPDATE: case COMPOSE_SUGGESTION_TAGS_UPDATE:
return updateSuggestionTags(state, action.token); return updateSuggestionTags(state, action.token);
case COMPOSE_TAG_HISTORY_UPDATE: case COMPOSE_TAG_HISTORY_UPDATE:

View File

@ -67,3 +67,11 @@
text-decoration: none; text-decoration: none;
} }
} }
.nothing-here {
color: $darker-text-color;
}
.public-layout .public-account-header__tabs__tabs .counter.active::after {
border-bottom: 4px solid $ui-highlight-color;
}

View File

@ -319,6 +319,7 @@
} }
.autosuggest-textarea, .autosuggest-textarea,
.autosuggest-input,
.spoiler-input { .spoiler-input {
position: relative; position: relative;
} }
@ -4829,6 +4830,14 @@ a.status-card.compact:hover {
border-radius: 4px; border-radius: 4px;
overflow: hidden; overflow: hidden;
margin: 2px; margin: 2px;
&__icons {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 24px;
}
} }
.notification__filter-bar, .notification__filter-bar,

View File

@ -37,11 +37,14 @@
display: none; display: none;
} }
.autossugest-input {
flex: 1 1 auto;
}
input[type=text] { input[type=text] {
display: block; display: block;
box-sizing: border-box; box-sizing: border-box;
flex: 1 1 auto; width: 100%;
width: 20px;
font-size: 14px; font-size: 14px;
color: $inverted-text-color; color: $inverted-text-color;
display: block; display: block;
@ -64,6 +67,7 @@
&.editable { &.editable {
display: flex; display: flex;
align-items: center; align-items: center;
overflow: visible;
} }
} }
@ -114,11 +118,14 @@
text-decoration: underline; text-decoration: underline;
font-size: inherit; font-size: inherit;
&:hover, &:hover {
&:focus,
&:active {
text-decoration: none; text-decoration: none;
} }
&:active,
&:focus {
background-color: rgba($dark-text-color, .1);
}
} }
.button { .button {

View File

@ -65,9 +65,16 @@ class ActivityPub::TagManager
when 'unlisted', 'private' when 'unlisted', 'private'
[account_followers_url(status.account)] [account_followers_url(status.account)]
when 'direct', 'limited' when 'direct', 'limited'
if status.account.silenced?
# Only notify followers if the account is locally silenced
account_ids = status.active_mentions.pluck(:account_id)
to = status.account.followers.where(id: account_ids).map { |account| uri_for(account) }
to.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).map { |request| uri_for(request.account) })
else
status.active_mentions.map { |mention| uri_for(mention.account) } status.active_mentions.map { |mention| uri_for(mention.account) }
end end
end end
end
# Secondary audience of a status # Secondary audience of a status
# Public statuses go out to followers as well # Public statuses go out to followers as well
@ -86,7 +93,16 @@ class ActivityPub::TagManager
cc << COLLECTIONS[:public] cc << COLLECTIONS[:public]
end end
cc.concat(status.active_mentions.map { |mention| uri_for(mention.account) }) unless status.direct_visibility? || status.limited_visibility? unless status.direct_visibility? || status.limited_visibility?
if status.account.silenced?
# Only notify followers if the account is locally silenced
account_ids = status.active_mentions.pluck(:account_id)
cc.concat(status.account.followers.where(id: account_ids).map { |account| uri_for(account) })
cc.concat(FollowRequest.where(target_account_id: status.account_id, account_id: account_ids).map { |request| uri_for(request.account) })
else
cc.concat(status.active_mentions.map { |mention| uri_for(mention.account) })
end
end
cc cc
end end

View File

@ -35,6 +35,7 @@ class Form::StatusBatch
def delete_statuses def delete_statuses
Status.where(id: status_ids).reorder(nil).find_each do |status| Status.where(id: status_ids).reorder(nil).find_each do |status|
RemovalWorker.perform_async(status.id) RemovalWorker.perform_async(status.id)
Tombstone.find_or_create_by(uri: status.uri, account: status.account, by_moderator: true)
log_action :destroy, status log_action :destroy, status
end end

View File

@ -9,6 +9,7 @@
# uri :string not null # uri :string not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# by_moderator :boolean
# #
class Tombstone < ApplicationRecord class Tombstone < ApplicationRecord

View File

@ -269,6 +269,7 @@ ca:
created_msg: El bloqueig de domini ara s'està processant created_msg: El bloqueig de domini ara s'està processant
destroyed_msg: El bloqueig de domini s'ha desfet destroyed_msg: El bloqueig de domini s'ha desfet
domain: Domini domain: Domini
existing_domain_block_html: Ja has imposat uns limits més estrictes a %{name}, l'hauries de <a href="%{unblock_url}">desbloquejar-lo</a> primer.
new: new:
create: Crea un bloqueig create: Crea un bloqueig
hint: El bloqueig de domini no impedirà la creació de nous comptes en la base de dades, però s'aplicaran de manera retroactiva mètodes de moderació específics sobre aquests comptes. hint: El bloqueig de domini no impedirà la creació de nous comptes en la base de dades, però s'aplicaran de manera retroactiva mètodes de moderació específics sobre aquests comptes.
@ -655,7 +656,7 @@ ca:
invalid_token: Els tokens de Keybase són hashs de signatures i han de tenir 66 caràcters hexadecimals invalid_token: Els tokens de Keybase són hashs de signatures i han de tenir 66 caràcters hexadecimals
verification_failed: Keybase no reconeix aquest token com a signatura del usuari de Keybase %{kb_username}. Si us plau prova des de Keybase. verification_failed: Keybase no reconeix aquest token com a signatura del usuari de Keybase %{kb_username}. Si us plau prova des de Keybase.
wrong_user: No es pot crear una prova per a %{proving} mentre es connectava com a %{current}. Inicia sessió com a %{proving} i prova de nou. wrong_user: No es pot crear una prova per a %{proving} mentre es connectava com a %{current}. Inicia sessió com a %{proving} i prova de nou.
explanation_html: Aquí pots connectar criptogràficament les teves altres identitats com ara el teu perfil de Keybase. Això permet que altres persones t'envïin missatges xifrats i continguts de confiança que els hi enviess. explanation_html: Aquí pots connectar criptogràficament les teves altres identitats com ara el teu perfil de Keybase. Això permet que altres persones t'envïin missatges xifrats i confiar en el contingut que els hi envies.
i_am_html: Sóc %{username} a %{service}. i_am_html: Sóc %{username} a %{service}.
identity: Identitat identity: Identitat
inactive: Inactiu inactive: Inactiu
@ -675,7 +676,7 @@ ca:
blocking: Llista de blocats blocking: Llista de blocats
domain_blocking: Llistat de dominis bloquejats domain_blocking: Llistat de dominis bloquejats
following: Llista de seguits following: Llista de seguits
muting: Llista d'apagats muting: Llista de silenciats
upload: Carregar upload: Carregar
in_memoriam_html: En Memòria. in_memoriam_html: En Memòria.
invites: invites:
@ -778,7 +779,7 @@ ca:
preferences: preferences:
languages: Llengues languages: Llengues
other: Altre other: Altre
publishing: Publicació publishing: Publicant
web: Web web: Web
relationships: relationships:
activity: Activitat del compte activity: Activitat del compte
@ -922,7 +923,7 @@ ca:
sensitive_content: Contingut sensible sensitive_content: Contingut sensible
terms: terms:
body_html: | body_html: |
<h2>Privacy Policy</h2> <h2>Política de Privacitat</h2>
<h3 id="collect">Quina informació recollim?</h3> <h3 id="collect">Quina informació recollim?</h3>
<ul> <ul>

View File

@ -12,7 +12,7 @@ ja:
last_attempt: あと1回失敗するとアカウントがロックされます。 last_attempt: あと1回失敗するとアカウントがロックされます。
locked: アカウントはロックされました。 locked: アカウントはロックされました。
not_found_in_database: "%{authentication_keys}かパスワードが誤っています。" not_found_in_database: "%{authentication_keys}かパスワードが誤っています。"
pending: あなたのアカウントはまだ審査中です。 pending: あなたのアカウントはまだ承認待ちです。
timeout: セッションの有効期限が切れました。続行するには再度ログインしてください。 timeout: セッションの有効期限が切れました。続行するには再度ログインしてください。
unauthenticated: 続行するにはログインするか、アカウントを作成してください。 unauthenticated: 続行するにはログインするか、アカウントを作成してください。
unconfirmed: 続行するにはメールアドレスを確認する必要があります。 unconfirmed: 続行するにはメールアドレスを確認する必要があります。

View File

@ -4,17 +4,25 @@ fr:
about_hashtag_html: Figurent ci-dessous les pouets tagués avec <strong>#%{hashtag}</strong>. Vous pouvez interagir avec eux si vous avez un compte nimporte où dans le Fediverse. about_hashtag_html: Figurent ci-dessous les pouets tagués avec <strong>#%{hashtag}</strong>. Vous pouvez interagir avec eux si vous avez un compte nimporte où dans le Fediverse.
about_mastodon_html: Mastodon est un réseau social utilisant des formats ouverts et des logiciels libres. Comme le courriel, il est décentralisé. about_mastodon_html: Mastodon est un réseau social utilisant des formats ouverts et des logiciels libres. Comme le courriel, il est décentralisé.
about_this: À propos about_this: À propos
active_count_after: actif·ve·s
active_footnote: Utilisateur·rice·s actif·ve·s mensuels (MAU)
administered_by: 'Administrée par:' administered_by: 'Administrée par:'
api: API api: API
apps: Applications mobiles apps: Applications mobiles
apps_platforms: Utilisez Mastodon depuis iOS, Android et dautres plates-formes
browse_directory: Parcourir lannuaire des profils et filtrer par centres dintérêt
browse_public_posts: Parcourir un flux en direct de messages publics sur Mastodon
contact: Contact contact: Contact
contact_missing: Manquant contact_missing: Manquant
contact_unavailable: Non disponible contact_unavailable: Non disponible
discover_users: Découvrez des utilisateur·rice·s
documentation: Documentation documentation: Documentation
extended_description_html: | extended_description_html: |
<h3>Un bon endroit pour les règles</h3> <h3>Un bon endroit pour les règles</h3>
<p>La description étendue na pas été remplie.</p> <p>La description étendue na pas été remplie.</p>
federation_hint_html: Avec un compte sur %{instance}, vous pourrez suivre les gens sur nimporte quel serveur Mastodon et au-delà.
generic_description: "%{domain} est seulement un serveur du réseau" generic_description: "%{domain} est seulement un serveur du réseau"
get_apps: Essayez une application mobile
hosted_on: Serveur Mastodon hébergée par %{domain} hosted_on: Serveur Mastodon hébergée par %{domain}
learn_more: En savoir plus learn_more: En savoir plus
privacy_policy: Politique de vie privée privacy_policy: Politique de vie privée
@ -23,7 +31,8 @@ fr:
one: Statut one: Statut
other: Statuts other: Statuts
status_count_before: Ayant publié status_count_before: Ayant publié
terms: Conditions d'utilisation tagline: Suivez vos ami·e·s et découvrez en de nouveaux·elles
terms: Conditions dutilisation
user_count_after: user_count_after:
one: utilisateur one: utilisateur
other: utilisateurs other: utilisateurs
@ -113,15 +122,18 @@ fr:
moderation: moderation:
active: Actif active: Actif
all: Tous all: Tous
pending: En cours de traitement
silenced: Masqués silenced: Masqués
suspended: Suspendus suspended: Suspendus
title: Modération title: Modération
moderation_notes: Notes de modération moderation_notes: Notes de modération
most_recent_activity: Dernière activité most_recent_activity: Dernière activité
most_recent_ip: Adresse IP la plus récente most_recent_ip: Adresse IP la plus récente
no_account_selected: Aucun compte na été modifié, car aucun na été sélectionné
no_limits_imposed: Aucune limite imposée no_limits_imposed: Aucune limite imposée
not_subscribed: Non abonné not_subscribed: Non abonné
outbox_url: URL de sortie outbox_url: URL de sortie
pending: En attente dapprobation
perform_full_suspension: Suspendre perform_full_suspension: Suspendre
profile_url: URL du profil profile_url: URL du profil
promote: Promouvoir promote: Promouvoir
@ -129,8 +141,10 @@ fr:
public: Publique public: Publique
push_subscription_expires: Expiration de labonnement PuSH push_subscription_expires: Expiration de labonnement PuSH
redownload: Rafraîchir le profil redownload: Rafraîchir le profil
reject: Rejeter
reject_all: Tout rejeter
remove_avatar: Supprimer lavatar remove_avatar: Supprimer lavatar
remove_header: Supprimer l'entête remove_header: Supprimer lentête
resend_confirmation: resend_confirmation:
already_confirmed: Cet·te utilisateur·ice est déjà confirmé·e already_confirmed: Cet·te utilisateur·ice est déjà confirmé·e
send: Renvoyer un courriel de confirmation send: Renvoyer un courriel de confirmation
@ -149,7 +163,7 @@ fr:
shared_inbox_url: URL de la boite de réception partagée shared_inbox_url: URL de la boite de réception partagée
show: show:
created_reports: Signalements faits created_reports: Signalements faits
targeted_reports: Signalés par d'autres targeted_reports: Signalés par dautres
silence: Masquer silence: Masquer
silenced: Silencié silenced: Silencié
statuses: Statuts statuses: Statuts
@ -173,7 +187,7 @@ fr:
create_domain_block: "%{name} a bloqué le domaine %{target}" create_domain_block: "%{name} a bloqué le domaine %{target}"
create_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste noire" create_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste noire"
demote_user: "%{name} a rétrogradé lutilisateur·ice %{target}" demote_user: "%{name} a rétrogradé lutilisateur·ice %{target}"
destroy_custom_emoji: "%{name} a détruit l'émoticône %{target}" destroy_custom_emoji: "%{name} a détruit lémoticône %{target}"
destroy_domain_block: "%{name} a débloqué le domaine %{target}" destroy_domain_block: "%{name} a débloqué le domaine %{target}"
destroy_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste blanche" destroy_email_domain_block: "%{name} a mis le domaine du courriel %{target} sur liste blanche"
destroy_status: "%{name} a enlevé le statut de %{target}" destroy_status: "%{name} a enlevé le statut de %{target}"
@ -230,6 +244,7 @@ fr:
feature_profile_directory: Annuaire des profils feature_profile_directory: Annuaire des profils
feature_registrations: Inscriptions feature_registrations: Inscriptions
feature_relay: Relais de fédération feature_relay: Relais de fédération
feature_timeline_preview: Aperçu du fil public
features: Fonctionnalités features: Fonctionnalités
hidden_service: Fédération avec des services cachés hidden_service: Fédération avec des services cachés
open_reports: signalements non résolus open_reports: signalements non résolus
@ -249,6 +264,7 @@ fr:
created_msg: Le blocage de domaine est désormais activé created_msg: Le blocage de domaine est désormais activé
destroyed_msg: Le blocage de domaine a été désactivé destroyed_msg: Le blocage de domaine a été désactivé
domain: Domaine domain: Domaine
existing_domain_block_html: Vous avez déjà imposé des limites plus strictes à %{name}, vous devez dabord le <a href="%{unblock_url}">débloquer</a>.
new: new:
create: Créer le blocage create: Créer le blocage
hint: Le blocage de domaine nempêchera pas la création de comptes dans la base de données, mais il appliquera automatiquement et rétrospectivement des méthodes de modération spécifiques sur ces comptes. hint: Le blocage de domaine nempêchera pas la création de comptes dans la base de données, mais il appliquera automatiquement et rétrospectivement des méthodes de modération spécifiques sur ces comptes.
@ -314,6 +330,8 @@ fr:
expired: Expiré expired: Expiré
title: Filtre title: Filtre
title: Invitations title: Invitations
pending_accounts:
title: Comptes en attente (%{count})
relays: relays:
add_new: Ajouter un nouveau relais add_new: Ajouter un nouveau relais
delete: Effacer delete: Effacer
@ -324,7 +342,7 @@ fr:
enable_hint: Une fois activé, votre serveur souscrira à tous les pouets publics présents sur ce relais et y enverra ses propres pouets publics. enable_hint: Une fois activé, votre serveur souscrira à tous les pouets publics présents sur ce relais et y enverra ses propres pouets publics.
enabled: Activé enabled: Activé
inbox_url: URL de relais inbox_url: URL de relais
pending: En attente de l'approbation du relai pending: En attente de lapprobation du relai
save_and_enable: Sauvegarder et activer save_and_enable: Sauvegarder et activer
setup: Paramétrer une connexion de relais setup: Paramétrer une connexion de relais
status: Statut status: Statut
@ -373,13 +391,13 @@ fr:
email: Entrez une adresse courriel publique email: Entrez une adresse courriel publique
username: Entrez un nom dutilisateur⋅ice username: Entrez un nom dutilisateur⋅ice
custom_css: custom_css:
desc_html: Modifier l'apparence avec une CSS chargée sur chaque page desc_html: Modifier lapparence avec une CSS chargée sur chaque page
title: CSS personnalisé title: CSS personnalisé
hero: hero:
desc_html: Affichée sur la page daccueil. Au moins 600x100px recommandé. Lorsquelle nest pas définie, se rabat sur la vignette du serveur desc_html: Affichée sur la page daccueil. Au moins 600x100px recommandé. Lorsquelle nest pas définie, se rabat sur la vignette du serveur
title: Image den-tête title: Image den-tête
mascot: mascot:
desc_html: Affiché sur plusieurs pages. Au moins 293×205px recommandé. Lorsqu'il n'est pas défini, retombe à la mascotte par défaut desc_html: Affiché sur plusieurs pages. Au moins 293×205px recommandé. Lorsquil nest pas défini, retombe à la mascotte par défaut
title: Image de la mascotte title: Image de la mascotte
peers_api_enabled: peers_api_enabled:
desc_html: Noms des domaines que ce serveur a découvert dans le fediverse desc_html: Noms des domaines que ce serveur a découvert dans le fediverse
@ -388,8 +406,8 @@ fr:
desc_html: Les liens de prévisualisation sur les autres sites web afficheront une vignette même si le média est sensible desc_html: Les liens de prévisualisation sur les autres sites web afficheront une vignette même si le média est sensible
title: Afficher les médias sensibles dans les prévisualisations OpenGraph title: Afficher les médias sensibles dans les prévisualisations OpenGraph
profile_directory: profile_directory:
desc_html: Permettre aux utilisateurs d'être découverts desc_html: Permettre aux utilisateurs dêtre découverts
title: Activer l'annuaire des profils title: Activer lannuaire des profils
registrations: registrations:
closed_message: closed_message:
desc_html: Affiché sur la page daccueil lorsque les inscriptions sont fermées<br>Vous pouvez utiliser des balises HTML desc_html: Affiché sur la page daccueil lorsque les inscriptions sont fermées<br>Vous pouvez utiliser des balises HTML
@ -400,6 +418,12 @@ fr:
min_invite_role: min_invite_role:
disabled: Personne disabled: Personne
title: Autoriser les invitations par title: Autoriser les invitations par
registrations_mode:
modes:
approved: Approbation requise pour sinscrire
none: Personne ne peut sinscrire
open: Nimporte qui peut sinscrire
title: Mode denregistrement
show_known_fediverse_at_about_page: show_known_fediverse_at_about_page:
desc_html: Lorsque loption est activée, les pouets provenant de toutes les serveurs connues sont affichés dans la prévisualisation. Sinon, seuls les pouets locaux sont affichés. desc_html: Lorsque loption est activée, les pouets provenant de toutes les serveurs connues sont affichés dans la prévisualisation. Sinon, seuls les pouets locaux sont affichés.
title: Afficher le fediverse connu dans la prévisualisation du fil title: Afficher le fediverse connu dans la prévisualisation du fil
@ -410,7 +434,7 @@ fr:
desc_html: Paragraphe introductif sur la page daccueil. Décrivez ce qui rend spécifique ce serveur Mastodon et toute autre chose importante. Vous pouvez utiliser des balises HTML, en particulier <code>&lt;a&gt;</code> et <code>&lt;em&gt;</code>. desc_html: Paragraphe introductif sur la page daccueil. Décrivez ce qui rend spécifique ce serveur Mastodon et toute autre chose importante. Vous pouvez utiliser des balises HTML, en particulier <code>&lt;a&gt;</code> et <code>&lt;em&gt;</code>.
title: Description du serveur title: Description du serveur
site_description_extended: site_description_extended:
desc_html: L'endroit idéal pour afficher votre code de conduite, les règles, les guides et autres choses qui rendent votre serveur différent. Vous pouvez utiliser des balises HTML desc_html: Lendroit idéal pour afficher votre code de conduite, les règles, les guides et autres choses qui rendent votre serveur différent. Vous pouvez utiliser des balises HTML
title: Description étendue du serveur title: Description étendue du serveur
site_short_description: site_short_description:
desc_html: Affichée dans la barre latérale et dans les méta-tags. Décrivez ce qui rend spécifique ce serveur Mastodon en un seul paragraphe. Si laissée vide, la description du serveur sera affiché par défaut. desc_html: Affichée dans la barre latérale et dans les méta-tags. Décrivez ce qui rend spécifique ce serveur Mastodon en un seul paragraphe. Si laissée vide, la description du serveur sera affiché par défaut.
@ -449,19 +473,22 @@ fr:
tags: tags:
accounts: Comptes accounts: Comptes
hidden: Masqué hidden: Masqué
hide: Masquer dans l'annuaire hide: Masquer dans lannuaire
name: Hashtag name: Hashtag
title: Hashtags title: Hashtags
unhide: Afficher dans l'annuaire unhide: Afficher dans lannuaire
visible: Visible visible: Visible
title: Administration title: Administration
warning_presets: warning_presets:
add_new: Ajouter un nouveau add_new: Ajouter un nouveau
delete: Effacer delete: Effacer
edit: Éditer edit: Éditer
edit_preset: Éditer la présélection d'attention edit_preset: Éditer la présélection davertissement
title: Gérer les présélections d'attention title: Gérer les présélections davertissement
admin_mailer: admin_mailer:
new_pending_account:
body: Les détails du nouveau compte se trouvent ci-dessous. Vous pouvez approuver ou rejeter cette demande.
subject: Nouveau compte à examiner sur %{instance} (%{username})
new_report: new_report:
body: "%{reporter} a signalé %{target}" body: "%{reporter} a signalé %{target}"
body_remote: Quelquun de %{domain} a signalé %{target} body_remote: Quelquun de %{domain} a signalé %{target}
@ -482,7 +509,9 @@ fr:
warning: Soyez prudent⋅e avec ces données. Ne les partagez pas! warning: Soyez prudent⋅e avec ces données. Ne les partagez pas!
your_token: Votre jeton daccès your_token: Votre jeton daccès
auth: auth:
apply_for_account: Demander une invitation
change_password: Mot de passe change_password: Mot de passe
checkbox_agreement_html: Jaccepte les <a href="%{rules_path}" target="_blank">règles du serveur</a> et les <a href="%{terms_path}" target="_blank">conditions de service</a>
confirm_email: Confirmer mon adresse mail confirm_email: Confirmer mon adresse mail
delete_account: Supprimer le compte delete_account: Supprimer le compte
delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action. delete_account_html: Si vous désirez supprimer votre compte, vous pouvez <a href="%{path}">cliquer ici</a>. Il vous sera demandé de confirmer cette action.
@ -498,10 +527,12 @@ fr:
cas: CAS cas: CAS
saml: SAML saml: SAML
register: Sinscrire register: Sinscrire
registration_closed: "%{instance} naccepte pas de nouveaux membres"
resend_confirmation: Envoyer à nouveau les consignes de confirmation resend_confirmation: Envoyer à nouveau les consignes de confirmation
reset_password: Réinitialiser le mot de passe reset_password: Réinitialiser le mot de passe
security: Sécurité security: Sécurité
set_new_password: Définir le nouveau mot de passe set_new_password: Définir le nouveau mot de passe
trouble_logging_in: Vous avez un problème pour vous connecter ?
authorize_follow: authorize_follow:
already_following: Vous suivez déjà ce compte already_following: Vous suivez déjà ce compte
error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant
@ -537,11 +568,11 @@ fr:
warning_title: Disponibilité du contenu disséminé warning_title: Disponibilité du contenu disséminé
directories: directories:
directory: Annuaire des profils directory: Annuaire des profils
enabled: Vous êtes actuellement listé dans l'annuaire. enabled: Vous êtes actuellement listé dans lannuaire.
enabled_but_waiting: Vous avez choisi d'être listé dans l'annuaire, mais vous n'avez pas encore le nombre minimum de suiveurs (%{min_followers}) pour y être inscrit. enabled_but_waiting: Vous avez choisi dêtre listé dans lannuaire, mais vous navez pas encore le nombre minimum de suiveurs (%{min_followers}) pour y être inscrit.
explanation: Découvrir des utilisateurs en se basant sur leurs centres d'intérêt explanation: Découvrir des utilisateurs en se basant sur leurs centres dintérêt
explore_mastodon: Explorer %{title} explore_mastodon: Explorer %{title}
how_to_enable: Vous n'êtes pas encore inscrit dans l'annuaire. Vous pouvez vous inscrire ci-dessous. Utilisez des hashtags dans votre texte biographique pour être listé sous des hashtags spécifiques ! how_to_enable: Vous nêtes pas encore inscrit dans lannuaire. Vous pouvez vous inscrire ci-dessous. Utilisez des hashtags dans votre texte biographique pour être listé sous des hashtags spécifiques !
people: people:
one: "%{count} personne" one: "%{count} personne"
other: "%{count} personne" other: "%{count} personne"
@ -557,6 +588,9 @@ fr:
content: Nous sommes désolé·e·s, mais quelque chose sest mal passé de notre côté. content: Nous sommes désolé·e·s, mais quelque chose sest mal passé de notre côté.
title: Cette page nest pas correcte title: Cette page nest pas correcte
noscript_html: Pour utiliser Mastodon, veuillez activer JavaScript. Sinon, essayez lune des <a href="%{apps_path}">applications natives</a> pour Mastodon pour votre plate-forme. noscript_html: Pour utiliser Mastodon, veuillez activer JavaScript. Sinon, essayez lune des <a href="%{apps_path}">applications natives</a> pour Mastodon pour votre plate-forme.
existing_username_validator:
not_found: na pas trouvé dutilisateur·rice local·e avec ce nom
not_found_multiple: na pas trouvé %{usernames}
exports: exports:
archive_takeout: archive_takeout:
date: Date date: Date
@ -597,19 +631,41 @@ fr:
more: Davantage… more: Davantage…
resources: Ressources resources: Ressources
generic: generic:
all: Tous
changes_saved_msg: Les modifications ont été enregistrées avec succès! changes_saved_msg: Les modifications ont été enregistrées avec succès!
copy: Copier copy: Copier
order_by: Classer par
save_changes: Enregistrer les modifications save_changes: Enregistrer les modifications
validation_errors: validation_errors:
one: Quelque chose ne va pas! Vérifiez lerreur ci-dessous one: Quelque chose ne va pas! Vérifiez lerreur ci-dessous
other: Certaines choses ne vont pas! Vérifiez les %{count} erreurs ci-dessous other: Certaines choses ne vont pas! Vérifiez les %{count} erreurs ci-dessous
html_validator:
invalid_markup: 'contient un balisage HTML invalide: %{error}'
identity_proofs:
active: Actif
authorize: Oui, autoriser
authorize_connection_prompt: Autoriser cette connexion chiffrée ?
errors:
failed: La connexion chiffrée a échoué. Veuillez réessayer à partir de %{provider}.
keybase:
invalid_token: Les jetons Keybase sont des hachages de signatures et doivent comporter 66 caractères hexadécimaux
verification_failed: Keybase ne reconnaît pas ce jeton comme une signature de lutilisateur Keybase %{kb_username}. Veuillez réessayer à partir de Keybase.
wrong_user: Impossible de créer une preuve pour %{proving} lorsque vous êtes connecté en tant que %{current}. Connectez-vous en tant que %{proving} et réessayez.
explanation_html: Ici, vous pouvez connecter de manière chiffrée vos autres identités, par exemple un profil Keybase. Cela permet à dautres personnes de vous envoyer des messages chiffrés et de faire confiance au contenu que vous leur envoyez.
i_am_html: Je suis %{username} sur %{service}.
identity: Identité
inactive: Inactif
publicize_checkbox: 'Et le poueter:'
publicize_toot: 'Cest prouvé ! Je suis %{username} sur %{service}: %{url}'
status: Statut de vérification
view_proof: Voir la preuve
imports: imports:
modes: modes:
merge: Fusionner merge: Fusionner
merge_long: Garder les enregistrements existants et ajouter les nouveaux merge_long: Garder les enregistrements existants et ajouter les nouveaux
overwrite: Réécrire overwrite: Réécrire
overwrite_long: Remplacer les enregistrements actuels par les nouveaux overwrite_long: Remplacer les enregistrements actuels par les nouveaux
preface: Vous pouvez importer certaines données que vous avez exporté d'un autre serveur, comme une liste des personnes que vous suivez ou bloquez sur votre compte. preface: Vous pouvez importer certaines données que vous avez exporté dun autre serveur, comme une liste des personnes que vous suivez ou bloquez sur votre compte.
success: Vos données ont été importées avec succès et seront traitées en temps et en heure success: Vos données ont été importées avec succès et seront traitées en temps et en heure
types: types:
blocking: Liste dutilisateur⋅ice⋅s bloqué⋅e⋅s blocking: Liste dutilisateur⋅ice⋅s bloqué⋅e⋅s
@ -713,13 +769,26 @@ fr:
duration_too_short: est trop tôt duration_too_short: est trop tôt
expired: Ce sondage est déjà terminé expired: Ce sondage est déjà terminé
over_character_limit: ne peuvent être plus long que %{max} caractères chacun over_character_limit: ne peuvent être plus long que %{max} caractères chacun
too_few_options: doit avoir plus qu'une proposition too_few_options: doit avoir plus quune proposition
too_many_options: ne peut contenir plus que %{max} propositions too_many_options: ne peut contenir plus que %{max} propositions
preferences: preferences:
languages: Langues languages: Langues
other: Autre other: Autre
publishing: Publication publishing: Publication
web: Web web: Web
relationships:
activity: Activité du compte
dormant: Dormant
last_active: Dernière activité
most_recent: Plus récent
moved: Déménagé
mutual: Mutuel
primary: Primaire
relationship: Relation
remove_selected_domains: Supprimer tous les abonné·e·s des domaines sélectionnés
remove_selected_followers: Supprimer les abonné·e·s sélectionnés
remove_selected_follows: Cesser de suivre les utilisateur·rice·s sélectionné·e·s
status: Statut du compte
remote_follow: remote_follow:
acct: Entrez ladresse profil@serveur depuis laquelle vous voulez vous abonner acct: Entrez ladresse profil@serveur depuis laquelle vous voulez vous abonner
missing_resource: LURL de redirection na pas pu être trouvée missing_resource: LURL de redirection na pas pu être trouvée
@ -729,7 +798,7 @@ fr:
reason_html: "<strong>Pourquoi cette étape est-elle nécessaire?</strong> <code>%{instance}</code> pourrait ne pas être le serveur où vous vous êtes inscrit, et nous devons donc vous rediriger vers votre serveur de base en premier." reason_html: "<strong>Pourquoi cette étape est-elle nécessaire?</strong> <code>%{instance}</code> pourrait ne pas être le serveur où vous vous êtes inscrit, et nous devons donc vous rediriger vers votre serveur de base en premier."
remote_interaction: remote_interaction:
favourite: favourite:
proceed: Confirmer l'ajout aux favoris proceed: Confirmer lajout aux favoris
prompt: 'Vous souhaitez mettre ce pouet en favori :' prompt: 'Vous souhaitez mettre ce pouet en favori :'
reblog: reblog:
proceed: Confirmer le repartage proceed: Confirmer le repartage
@ -787,6 +856,9 @@ fr:
revoke_success: Session révoquée avec succès revoke_success: Session révoquée avec succès
title: Sessions title: Sessions
settings: settings:
account: Compte
account_settings: Paramètres du compte
appearance: Apparence
authorized_apps: Applications autorisées authorized_apps: Applications autorisées
back: Retour vers Mastodon back: Retour vers Mastodon
delete: Suppression de compte delete: Suppression de compte
@ -794,10 +866,13 @@ fr:
edit_profile: Modifier le profil edit_profile: Modifier le profil
export: Export de données export: Export de données
featured_tags: Hashtags mis en avant featured_tags: Hashtags mis en avant
identity_proofs: Preuves didentité
import: Import de données import: Import de données
migrate: Migration de compte migrate: Migration de compte
notifications: Notifications notifications: Notifications
preferences: Préférences preferences: Préférences
profile: Profil
relationships: Abonnements et abonné·e·s
two_factor_authentication: Identification à deux facteurs two_factor_authentication: Identification à deux facteurs
statuses: statuses:
attached: attached:
@ -954,8 +1029,8 @@ fr:
title: Récupération de larchive title: Récupération de larchive
warning: warning:
explanation: explanation:
disable: Lorsque votre compte est gelé, les données de votre compte demeurent intactes, mais vous ne pouvez effectuer aucune action jusqu'à ce qu'il soit débloqué. disable: Lorsque votre compte est gelé, les données de votre compte demeurent intactes, mais vous ne pouvez effectuer aucune action jusqu’à ce quil soit débloqué.
silence: Lorsque votre compte est limité, seulement les utilisateurs qui vous suivent déjà verront vos pouets sur ce serveur, et vous pourriez être exclu de plusieurs listes publiques. Néanmoins, d'autres utilisateurs peuvent vous suivre manuellement. silence: Lorsque votre compte est limité, seulement les utilisateurs qui vous suivent déjà verront vos pouets sur ce serveur, et vous pourriez être exclu de plusieurs listes publiques. Néanmoins, dautres utilisateurs peuvent vous suivre manuellement.
suspend: Votre compte a été suspendu, et tous vos pouets et vos fichiers multimédia téléversés ont été supprimés irréversiblement de ce serveur, et des serveurs où vous aviez des abonné⋅e⋅s. suspend: Votre compte a été suspendu, et tous vos pouets et vos fichiers multimédia téléversés ont été supprimés irréversiblement de ce serveur, et des serveurs où vous aviez des abonné⋅e⋅s.
review_server_policies: Passer en revue les politiques du serveur review_server_policies: Passer en revue les politiques du serveur
subject: subject:
@ -993,5 +1068,5 @@ fr:
seamless_external_login: Vous êtes connecté via un service externe, donc les paramètres concernant le mot de passe et le courriel ne sont pas disponibles. seamless_external_login: Vous êtes connecté via un service externe, donc les paramètres concernant le mot de passe et le courriel ne sont pas disponibles.
signed_in_as: 'Connecté·e en tant que:' signed_in_as: 'Connecté·e en tant que:'
verification: verification:
explanation_html: 'Vous pouvez <strong>vérifier vous-même que vous êtes le propriétaire des liens dans les métadonnées de votre profil</strong>. Pour cela, le site Web lié doit contenir un lien vers votre profil Mastodon. Le lien de retour <strong>doit</strong>avoir un attribut <code>rel="me"</code>. Le contenu textuel du lien n''a pas d''importance. En voici un exemple :' explanation_html: 'Vous pouvez <strong>vérifier vous-même que vous êtes le propriétaire des liens dans les métadonnées de votre profil</strong>. Pour cela, le site Web lié doit contenir un lien vers votre profil Mastodon. Le lien de retour <strong>doit</strong>avoir un attribut <code>rel="me"</code>. Le contenu textuel du lien na pas dimportance. En voici un exemple :'
verification: Vérification verification: Vérification

View File

@ -20,7 +20,7 @@ ja:
extended_description_html: | extended_description_html: |
<h3>ルールを書くのに適した場所</h3> <h3>ルールを書くのに適した場所</h3>
<p>詳細説明が設定されていません。</p> <p>詳細説明が設定されていません。</p>
federation_hint_html: "%{instance} にアカウントがあればどの互換性のあるサーバーのユーザーでもフォローできるでしょう。" federation_hint_html: "%{instance} のアカウントひとつでどんなMastodon互換サーバーのユーザーでもフォローできるでしょう。"
generic_description: "%{domain} は、Mastodon サーバーの一つです" generic_description: "%{domain} は、Mastodon サーバーの一つです"
get_apps: モバイルアプリを試す get_apps: モバイルアプリを試す
hosted_on: Mastodon hosted on %{domain} hosted_on: Mastodon hosted on %{domain}
@ -269,6 +269,7 @@ ja:
created_msg: ドメインブロック処理を完了しました created_msg: ドメインブロック処理を完了しました
destroyed_msg: ドメインブロックを外しました destroyed_msg: ドメインブロックを外しました
domain: ドメイン domain: ドメイン
existing_domain_block_html: 既に%{name}に対しより厳しい制限を課しています 。まずは<a href="%{unblock_url}">それを解除する</a>必要があります。
new: new:
create: ブロックを作成 create: ブロックを作成
hint: ドメインブロックはデータベース中のアカウント項目の作成を妨げませんが、遡って自動的に指定されたモデレーションをそれらのアカウントに適用します。 hint: ドメインブロックはデータベース中のアカウント項目の作成を妨げませんが、遡って自動的に指定されたモデレーションをそれらのアカウントに適用します。
@ -660,7 +661,7 @@ ja:
i_am_html: I am %{username} on %{service}. i_am_html: I am %{username} on %{service}.
identity: Identity identity: Identity
inactive: 非アクティブ inactive: 非アクティブ
publicize_checkbox: 'そしてこれをトゥートしてください:' publicize_checkbox: 'そしてこれをトゥートします:'
publicize_toot: 'It is proven! I am %{username} on %{service}: %{url}' publicize_toot: 'It is proven! I am %{username} on %{service}: %{url}'
status: 認証状態 status: 認証状態
view_proof: 証明を表示 view_proof: 証明を表示
@ -1051,19 +1052,19 @@ ja:
suspend: アカウントが停止されました suspend: アカウントが停止されました
welcome: welcome:
edit_profile_action: プロフィールを設定 edit_profile_action: プロフィールを設定
edit_profile_step: バター画像やヘッダー画像をアップロードしたり、表示名やその他プロフィールを変更しカスタマイズすることができます。新しいフォロワーからのフォローを許可する前に検討したい場合、アカウントを承認制にすることができます。 edit_profile_step: イコンやヘッダーの画像をアップロードしたり、表示名を変更したりして、自分のプロフィールをカスタマイズすることができます。また、誰かからの新規フォローを許可する前にその人の様子を見ておきたい場合、アカウントを承認制にすることもできます。
explanation: 始めるにあたってのアドバイスです explanation: 始めるにあたってのアドバイスです
final_action: 始めましょう final_action: 始めましょう
final_step: 'さあ始めましょう! たとえフォロワーがいなくても、あなたの公開した投稿はローカルタイムラインやハッシュタグなどで誰かの目に止まるかもしれません。自己紹介をしたい時は #introductions ハッシュタグを使うといいかもしれません。' final_step: 'さあ始めましょう! たとえフォロワーがまだいなくても、あなたの公開した投稿はローカルタイムラインやハッシュタグなどを通じて誰かの目にとまるはずです。自己紹介をしたいときには #introductions ハッシュタグが便利かもしれません。'
full_handle: あなたの正式なユーザー full_handle: あなたの正式なユーザーID
full_handle_hint: これは別のサーバーからフォローしてもらったりメッセージのやり取りをする際に、友達に伝えるといいでしょう full_handle_hint: 別のサーバーの友達とフォローやメッセージをやり取りする際には、これを伝えることになります
review_preferences_action: 設定の変更 review_preferences_action: 設定の変更
review_preferences_step: 受け取りたいメールや投稿の公開範囲などの設定を必ず行ってください。不快でないならアニメーション GIF の自動再生を有効にすることもできます review_preferences_step: 受け取りたいメールの種類や投稿のデフォルト公開範囲など、ユーザー設定を必ず済ませておきましょう。目が回らない自信があるなら、アニメーション GIF を自動再生する設定もご検討ください
subject: Mastodon へようこそ subject: Mastodon へようこそ
tip_federated_timeline: 連合タイムラインは Mastodon ネットワークの流れを見られるものです。ただしあなたと同じサーバーの人がフォローしている人だけが含まれるので、それが全てではありません。 tip_federated_timeline: 連合タイムラインは、Mastodon ネットワークによる巨大流しそうめんです。ただし、あなたの「隣人」達がフォローしている人々だけが流れてくる場所なので、決してそこに全てがあるわけではありません。
tip_following: 標準では自動でサーバーの管理者をフォローしています。もっと興味のある人たちを見つけるには、ローカルタイムラインと連合タイムラインを確認してください tip_following: 最初は、サーバーの管理者をフォローした状態になっています。もっと興味のある人たちを見つけるには、ローカルタイムラインと連合タイムラインを確認してみましょう
tip_local_timeline: ローカルタイムラインは %{instance} にいる人々の流れを見られるものです。彼らはあなたと同じサーバーにいる隣人のようなものです! tip_local_timeline: ローカルタイムラインには、%{instance} にいる人々が流しそうめんのごとく流れてきます。彼らはあなたと同じサーバーに暮らす、愛すべき隣人です!
tip_mobile_webapp: もしモバイル端末のブラウザで Mastodon をホーム画面に追加できる場合、プッシュ通知を受け取ることができます。それはまるでネイティブアプリのように動作します! tip_mobile_webapp: お使いのモバイル端末で、ブラウザから Mastodon をホーム画面に追加できますか? もし追加できる場合、プッシュ通知の受け取りなど、まるで「普通の」アプリのような機能が楽しめます!
tips: 豆知識 tips: 豆知識
title: ようこそ、%{name} title: ようこそ、%{name}
users: users:

View File

@ -5,8 +5,8 @@ fr:
account_warning_preset: account_warning_preset:
text: Vous pouvez utiliser la syntaxe des pouets, comme les URLs, les hashtags et les mentions text: Vous pouvez utiliser la syntaxe des pouets, comme les URLs, les hashtags et les mentions
admin_account_action: admin_account_action:
send_email_notification: L'utilisateur recevra une explication de ce qu'il s'est passé avec son compte send_email_notification: Lutilisateur recevra une explication de ce quil sest passé avec son compte
text_html: Optionnel. Vous pouvez utilisez la syntaxe des pouets. Vous pouvez <a href="%{path}">ajouter des présélections d'attention</a> pour économiser du temps text_html: Optionnel. Vous pouvez utilisez la syntaxe des pouets. Vous pouvez <a href="%{path}">ajouter des présélections dattention</a> pour économiser du temps
type_html: Choisir que faire avec <strong>%{acct}</strong> type_html: Choisir que faire avec <strong>%{acct}</strong>
warning_preset_id: Optionnel. Vous pouvez toujours ajouter un texte personnalisé à la fin de la présélection warning_preset_id: Optionnel. Vous pouvez toujours ajouter un texte personnalisé à la fin de la présélection
defaults: defaults:
@ -15,7 +15,7 @@ fr:
bot: Ce compte exécute principalement des actions automatisées et pourrait ne pas être surveillé bot: Ce compte exécute principalement des actions automatisées et pourrait ne pas être surveillé
context: Un ou plusieurs contextes où le filtre devrait sappliquer context: Un ou plusieurs contextes où le filtre devrait sappliquer
digest: Uniquement envoyé après une longue période dinactivité et uniquement si vous avez reçu des messages personnels pendant votre absence digest: Uniquement envoyé après une longue période dinactivité et uniquement si vous avez reçu des messages personnels pendant votre absence
discoverable_html: L'<a href="%{path}" target="_blank">annuaire</a> permet aux gens de trouver des comptes en se basant sur les intérêts et les activités. Nécessite au moins %{min_followers} abonnés discoverable_html: L<a href="%{path}" target="_blank">annuaire</a> permet aux gens de trouver des comptes en se basant sur les intérêts et les activités. Nécessite au moins %{min_followers} abonnés
email: Vous recevrez un courriel de confirmation email: Vous recevrez un courriel de confirmation
fields: Vous pouvez avoir jusquà 4 éléments affichés en tant que tableau sur votre profil fields: Vous pouvez avoir jusquà 4 éléments affichés en tant que tableau sur votre profil
header: Au format PNG, GIF ou JPG. %{size} maximum. Sera réduit à %{dimensions}px header: Au format PNG, GIF ou JPG. %{size} maximum. Sera réduit à %{dimensions}px
@ -26,21 +26,23 @@ fr:
password: Utilisez au moins 8 caractères password: Utilisez au moins 8 caractères
phrase: Sera trouvé sans que la case ou lavertissement de contenu du pouet soit pris en compte phrase: Sera trouvé sans que la case ou lavertissement de contenu du pouet soit pris en compte
scopes: À quelles APIs lapplication sera autorisée à accéder. Si vous sélectionnez un périmètre de haut-niveau, vous navez pas besoin de sélectionner les individuels. scopes: À quelles APIs lapplication sera autorisée à accéder. Si vous sélectionnez un périmètre de haut-niveau, vous navez pas besoin de sélectionner les individuels.
setting_aggregate_reblogs: Ne pas afficher de nouveaux repartagés pour les pouets qui ont été récemment repartagés (n'affecte que les repartagés nouvellement reçus) setting_aggregate_reblogs: Ne pas afficher de nouveaux repartagés pour les pouets qui ont été récemment repartagés (naffecte que les repartagés nouvellement reçus)
setting_default_language: La langue de vos pouets peut être détectée automatiquement, mais ça nest pas toujours pertinent setting_default_language: La langue de vos pouets peut être détectée automatiquement, mais ça nest pas toujours pertinent
setting_display_media_default: Masquer les supports marqués comme sensibles setting_display_media_default: Masquer les supports marqués comme sensibles
setting_display_media_hide_all: Toujours masquer tous les médias setting_display_media_hide_all: Toujours masquer tous les médias
setting_display_media_show_all: Toujours afficher les médias marqués comme sensibles setting_display_media_show_all: Toujours afficher les médias marqués comme sensibles
setting_hide_network: Ceux que vous suivez et ceux qui vous suivent ne seront pas affichés sur votre profil setting_hide_network: Ceux que vous suivez et ceux qui vous suivent ne seront pas affichés sur votre profil
setting_noindex: Affecte votre profil public ainsi que vos statuts setting_noindex: Affecte votre profil public ainsi que vos statuts
setting_show_application: Le nom de l'application que vous utilisez afin d'envoyer des pouets sera affiché dans la vue détaillée de ceux-ci setting_show_application: Le nom de lapplication que vous utilisez afin denvoyer des pouets sera affiché dans la vue détaillée de ceux-ci
setting_theme: Affecte lapparence de Mastodon quand vous êtes connecté·e depuis nimporte quel appareil. setting_theme: Affecte lapparence de Mastodon quand vous êtes connecté·e depuis nimporte quel appareil.
username: Votre nom dutilisateur sera unique sur %{domain} username: Votre nom dutilisateur sera unique sur %{domain}
whole_word: Lorsque le mot-clef ou la phrase-clef est uniquement alphanumérique, ça sera uniquement appliqué sil correspond au mot entier whole_word: Lorsque le mot-clef ou la phrase-clef est uniquement alphanumérique, ça sera uniquement appliqué sil correspond au mot entier
featured_tag: featured_tag:
name: 'Vous pourriez utiliser l''un d''entre eux :' name: 'Vous pourriez vouloir utiliser lun dentre eux :'
imports: imports:
data: Un fichier CSV généré par un autre serveur de Mastodon data: Un fichier CSV généré par un autre serveur de Mastodon
invite_request:
text: Cela nous aidera à considérer votre demande
sessions: sessions:
otp: 'Entrez le code dauthentification à deux facteurs généré par lapplication de votre téléphone ou utilisez un de vos codes de récupération:' otp: 'Entrez le code dauthentification à deux facteurs généré par lapplication de votre téléphone ou utilisez un de vos codes de récupération:'
user: user:
@ -53,7 +55,7 @@ fr:
account_warning_preset: account_warning_preset:
text: Texte de présélection text: Texte de présélection
admin_account_action: admin_account_action:
send_email_notification: Notifier l'utilisateur par courriel send_email_notification: Notifier lutilisateur par courriel
text: Attention personnalisée text: Attention personnalisée
type: Action type: Action
types: types:
@ -61,7 +63,7 @@ fr:
none: Ne rien faire none: Ne rien faire
silence: Silence silence: Silence
suspend: Suspendre et effacer les données du compte de manière irréversible suspend: Suspendre et effacer les données du compte de manière irréversible
warning_preset_id: Utiliser un modèle d'avertissement warning_preset_id: Utiliser un modèle davertissement
defaults: defaults:
autofollow: Invitation à suivre votre compte autofollow: Invitation à suivre votre compte
avatar: Image de profil avatar: Image de profil
@ -72,7 +74,7 @@ fr:
context: Contextes du filtre context: Contextes du filtre
current_password: Mot de passe actuel current_password: Mot de passe actuel
data: Données data: Données
discoverable: Inscrire ce compte dans l'annuaire discoverable: Inscrire ce compte dans lannuaire
display_name: Nom public display_name: Nom public
email: Adresse courriel email: Adresse courriel
expires_in: Expire après expires_in: Expire après
@ -103,7 +105,7 @@ fr:
setting_hide_network: Cacher votre réseau setting_hide_network: Cacher votre réseau
setting_noindex: Demander aux moteurs de recherche de ne pas indexer vos informations personnelles setting_noindex: Demander aux moteurs de recherche de ne pas indexer vos informations personnelles
setting_reduce_motion: Réduire la vitesse des animations setting_reduce_motion: Réduire la vitesse des animations
setting_show_application: Dévoiler le nom de l'application utilisée pour envoyer des pouets setting_show_application: Dévoiler le nom de lapplication utilisée pour envoyer des pouets
setting_system_font_ui: Utiliser la police par défaut du système setting_system_font_ui: Utiliser la police par défaut du système
setting_theme: Thème du site setting_theme: Thème du site
setting_unfollow_modal: Afficher une fenêtre de confirmation avant de vous désabonner dun compte setting_unfollow_modal: Afficher une fenêtre de confirmation avant de vous désabonner dun compte
@ -118,6 +120,8 @@ fr:
must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas
must_be_following: Masquer les notifications des personnes que vous ne suivez pas must_be_following: Masquer les notifications des personnes que vous ne suivez pas
must_be_following_dm: Bloquer les messages directs des personnes que vous ne suivez pas must_be_following_dm: Bloquer les messages directs des personnes que vous ne suivez pas
invite_request:
text: Pourquoi voulez-vous vous inscrire ?
notification_emails: notification_emails:
digest: Envoyer des courriels récapitulatifs digest: Envoyer des courriels récapitulatifs
favourite: Envoyer un courriel lorsque quelquun ajoute mes statuts à ses favoris favourite: Envoyer un courriel lorsque quelquun ajoute mes statuts à ses favoris

View File

@ -115,7 +115,7 @@ sk:
followers: Sledujúci followers: Sledujúci
followers_url: URL adresa sledujúcich followers_url: URL adresa sledujúcich
follows: Sledovania follows: Sledovania
header: Hlavička header: Záhlavie
inbox_url: URL adresa prijatých správ inbox_url: URL adresa prijatých správ
invited_by: Pozvaný/á užívateľom invited_by: Pozvaný/á užívateľom
ip: IP adresa ip: IP adresa
@ -138,6 +138,7 @@ sk:
moderation_notes: Moderátorské poznámky moderation_notes: Moderátorské poznámky
most_recent_activity: Posledná aktivita most_recent_activity: Posledná aktivita
most_recent_ip: Posledná IP adresa most_recent_ip: Posledná IP adresa
no_account_selected: Nedošlo k žiadnému pozmeneniu účtov, keďže žiadne neboli vybrané
no_limits_imposed: Nie sú stanovené žiadné obmedzenia no_limits_imposed: Nie sú stanovené žiadné obmedzenia
not_subscribed: Neodoberá not_subscribed: Neodoberá
outbox_url: URL poslaných outbox_url: URL poslaných
@ -152,7 +153,7 @@ sk:
reject: Zamietni reject: Zamietni
reject_all: Zamietni všetky reject_all: Zamietni všetky
remove_avatar: Vymaž avatar remove_avatar: Vymaž avatar
remove_header: Vymaž hlavičku remove_header: Vymaž záhlavie
resend_confirmation: resend_confirmation:
already_confirmed: Tento užívateľ je už potvrdený already_confirmed: Tento užívateľ je už potvrdený
send: Odošli potvrdzovací email znovu send: Odošli potvrdzovací email znovu
@ -319,7 +320,7 @@ sk:
by_domain: Doména by_domain: Doména
delivery_available: Je v dosahu doručovania delivery_available: Je v dosahu doručovania
known_accounts: known_accounts:
few: "%{count} známe účty" few: "%{count} známych účtov"
one: "%{count} známy účet" one: "%{count} známy účet"
other: "%{count} známe účty" other: "%{count} známe účty"
moderation: moderation:
@ -340,18 +341,20 @@ sk:
expired: Vypršalo expired: Vypršalo
title: Filtruj title: Filtruj
title: Pozvánky title: Pozvánky
pending_accounts:
title: Čakajúcich účtov (%{count})
relays: relays:
add_new: Pridaj nový federovací mostík add_new: Pridaj nový federovací mostík
delete: Vymaž delete: Vymaž
description_html: "<strong>Federovací mostík</strong> je prechodný server ktorý obmieňa veľké množstvá verejných príspevkov medzi tými servermi ktoré na od neho odoberajú, aj doňho prispievajú. <strong>Môže to pomôcť malým a stredným instanciám objavovať federovaný obsah</strong>, čo inak vyžaduje aby miestni užívatelia ručne následovali iných ľudí zo vzdialených instancií." description_html: "<strong>Federovací mostík</strong> je prechodný server, ktorý obmieňa veľké množstvá verejných príspevkov medzi tými servermi ktoré na od neho odoberajú, aj doňho prispievajú. <strong>Môže to pomôcť malým a stredným instanciám objavovať federovaný obsah</strong>, čo inak vyžaduje aby miestni užívatelia ručne následovali iných ľudí zo vzdialených instancií."
disable: Pozastav disable: Vypni
disabled: Zastavené disabled: Vypnutý
enable: Povoľ enable: Povoľ
enable_hint: Ak povolíš, tvoj server bude odoberať všetky verejné príspevky z tohto mostu, a začne posielať verejné príspevky tvojho servera na tento most. enable_hint: Ak povolíš, tvoj server bude odoberať všetky verejné príspevky z tohto mostu, a začne posielať verejné príspevky tvojho servera na tento most.
enabled: Povolené enabled: Povolené
inbox_url: URL adresa mostu inbox_url: URL adresa mostu
pending: Čakám na povolenie od prechodného mostu pending: Čaká sa na povolenie od prechodného mostu
save_and_enable: Uložiť a povoliť save_and_enable: Ulož a povoľ
setup: Nastav prepojenie s mostom setup: Nastav prepojenie s mostom
status: Stav status: Stav
title: Mosty title: Mosty
@ -390,7 +393,7 @@ sk:
updated_at: Aktualizované updated_at: Aktualizované
settings: settings:
activity_api_enabled: activity_api_enabled:
desc_html: Sčítanie lokálne publikovaných príspevkov, aktívnych užívateľov, a nových registrácii, v týždenných intervaloch desc_html: Sčítanie miestne uverejnených príspevkov, aktívnych užívateľov, a nových registrácii, v týždenných intervaloch
title: Vydať hromadné štatistiky o užívateľskej aktivite title: Vydať hromadné štatistiky o užívateľskej aktivite
bootstrap_timeline_accounts: bootstrap_timeline_accounts:
desc_html: Ak je prezývok viacero, každú oddeľte čiarkou. Možno zadať iba miestne, odomknuté účty. Pokiaľ necháte prázdne, je to pre všetkých miestnych administrátorov. desc_html: Ak je prezývok viacero, každú oddeľte čiarkou. Možno zadať iba miestne, odomknuté účty. Pokiaľ necháte prázdne, je to pre všetkých miestnych administrátorov.

View File

@ -0,0 +1,5 @@
class AddByModeratorToTombstone < ActiveRecord::Migration[5.2]
def change
add_column :tombstones, :by_moderator, :boolean
end
end

View File

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2019_04_20_025523) do ActiveRecord::Schema.define(version: 2019_05_09_164208) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "plpgsql" enable_extension "plpgsql"
@ -688,6 +688,7 @@ ActiveRecord::Schema.define(version: 2019_04_20_025523) do
t.string "uri", null: false t.string "uri", null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.boolean "by_moderator"
t.index ["account_id"], name: "index_tombstones_on_account_id" t.index ["account_id"], name: "index_tombstones_on_account_id"
t.index ["uri"], name: "index_tombstones_on_uri" t.index ["uri"], name: "index_tombstones_on_uri"
end end

View File

@ -28,10 +28,15 @@ module Mastodon
say('.', :green, false) say('.', :green, false)
end end
DomainBlock.where(domain: domain).destroy_all DomainBlock.where(domain: domain).destroy_all unless options[:dry_run]
say say
say("Removed #{removed} accounts#{dry_run}", :green) say("Removed #{removed} accounts#{dry_run}", :green)
custom_emojis = CustomEmoji.where(domain: domain)
custom_emojis_count = custom_emojis.count
custom_emojis.destroy_all unless options[:dry_run]
say("Removed #{custom_emojis_count} custom emojis", :green)
end end
option :concurrency, type: :numeric, default: 50, aliases: [:c] option :concurrency, type: :numeric, default: 50, aliases: [:c]

View File

@ -13,7 +13,7 @@ module Mastodon
end end
def patch def patch
1 2
end end
def pre def pre

View File

@ -41,6 +41,22 @@ RSpec.describe ActivityPub::TagManager do
status.mentions.create(account: mentioned) status.mentions.create(account: mentioned)
expect(subject.to(status)).to eq [subject.uri_for(mentioned)] expect(subject.to(status)).to eq [subject.uri_for(mentioned)]
end end
it "returns URIs of mentions for direct silenced author's status only if they are followers or requesting to be" do
bob = Fabricate(:account, username: 'bob')
alice = Fabricate(:account, username: 'alice')
foo = Fabricate(:account)
author = Fabricate(:account, username: 'author', silenced: true)
status = Fabricate(:status, visibility: :direct, account: author)
bob.follow!(author)
FollowRequest.create!(account: foo, target_account: author)
status.mentions.create(account: alice)
status.mentions.create(account: bob)
status.mentions.create(account: foo)
expect(subject.to(status)).to include(subject.uri_for(bob))
expect(subject.to(status)).to include(subject.uri_for(foo))
expect(subject.to(status)).to_not include(subject.uri_for(alice))
end
end end
describe '#cc' do describe '#cc' do
@ -70,6 +86,22 @@ RSpec.describe ActivityPub::TagManager do
status.mentions.create(account: mentioned) status.mentions.create(account: mentioned)
expect(subject.cc(status)).to include(subject.uri_for(mentioned)) expect(subject.cc(status)).to include(subject.uri_for(mentioned))
end end
it "returns URIs of mentions for silenced author's non-direct status only if they are followers or requesting to be" do
bob = Fabricate(:account, username: 'bob')
alice = Fabricate(:account, username: 'alice')
foo = Fabricate(:account)
author = Fabricate(:account, username: 'author', silenced: true)
status = Fabricate(:status, visibility: :public, account: author)
bob.follow!(author)
FollowRequest.create!(account: foo, target_account: author)
status.mentions.create(account: alice)
status.mentions.create(account: bob)
status.mentions.create(account: foo)
expect(subject.cc(status)).to include(subject.uri_for(bob))
expect(subject.cc(status)).to include(subject.uri_for(foo))
expect(subject.cc(status)).to_not include(subject.uri_for(alice))
end
end end
describe '#local_uri?' do describe '#local_uri?' do

View File

@ -2,9 +2,10 @@ require 'rails_helper'
RSpec.describe ProcessMentionsService, type: :service do RSpec.describe ProcessMentionsService, type: :service do
let(:account) { Fabricate(:account, username: 'alice') } let(:account) { Fabricate(:account, username: 'alice') }
let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}") } let(:visibility) { :public }
let(:status) { Fabricate(:status, account: account, text: "Hello @#{remote_user.acct}", visibility: visibility) }
context 'OStatus' do context 'OStatus with public toot' do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') } let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
subject { ProcessMentionsService.new } subject { ProcessMentionsService.new }
@ -23,6 +24,26 @@ RSpec.describe ProcessMentionsService, type: :service do
end end
end end
context 'OStatus with private toot' do
let(:visibility) { :private }
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :ostatus, domain: 'example.com', salmon_url: 'http://salmon.example.com') }
subject { ProcessMentionsService.new }
before do
stub_request(:post, remote_user.salmon_url)
subject.call(status)
end
it 'does not create a mention' do
expect(remote_user.mentions.where(status: status).count).to eq 0
end
it 'does not post to remote user\'s Salmon end point' do
expect(a_request(:post, remote_user.salmon_url)).to_not have_been_made
end
end
context 'ActivityPub' do context 'ActivityPub' do
let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') } let(:remote_user) { Fabricate(:account, username: 'remote_user', protocol: :activitypub, domain: 'example.com', inbox_url: 'http://example.com/inbox') }