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

Conflicts:
- `app/controllers/accounts_controller.rb`:
  Upstream change too close to a glitch-soc change related to
  instance-local toots. Merged upstream changes.
- `app/services/fan_out_on_write_service.rb`:
  Minor conflict due to glitch-soc's handling of Direct Messages,
  merged upstream changes.
- `yarn.lock`:
  Not really a conflict, caused by glitch-soc-only dependencies
  being textually too close to updated upstream dependencies.
  Merged upstream changes.
This commit is contained in:
Thibaut Girka
2020-08-30 16:13:08 +02:00
109 changed files with 2948 additions and 1238 deletions

View File

@ -205,7 +205,7 @@ export default class Dropdown extends React.PureComponent {
handleClose = () => {
if (this.activeElement) {
this.activeElement.focus();
this.activeElement.focus({ preventScroll: true });
this.activeElement = null;
}
this.props.onClose(this.state.id);

View File

@ -54,8 +54,6 @@ export default class GIFV extends React.PureComponent {
<video
src={src}
width={width}
height={height}
role='button'
tabIndex='0'
aria-label={alt}

View File

@ -7,6 +7,7 @@ import DropdownMenuContainer from '../containers/dropdown_menu_container';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, isStaff } from '../initial_state';
import classNames from 'classnames';
const messages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' },
@ -20,7 +21,7 @@ const messages = defineMessages({
more: { id: 'status.more', defaultMessage: 'More' },
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
@ -329,7 +330,7 @@ class StatusActionBar extends ImmutablePureComponent {
return (
<div className='status__action-bar'>
<div className='status__action-bar__counter'><IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /><span className='status__action-bar__counter__label' >{obfuscatedCount(status.get('replies_count'))}</span></div>
<IconButton className='status__action-bar-button' disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} />
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} pressed={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} />
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} pressed={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} />
{shareButton}

View File

@ -179,7 +179,7 @@ class PrivacyDropdown extends React.PureComponent {
} else {
const { top } = target.getBoundingClientRect();
if (this.state.open && this.activeElement) {
this.activeElement.focus();
this.activeElement.focus({ preventScroll: true });
}
this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' });
this.setState({ open: !this.state.open });
@ -220,7 +220,7 @@ class PrivacyDropdown extends React.PureComponent {
handleClose = () => {
if (this.state.open && this.activeElement) {
this.activeElement.focus();
this.activeElement.focus({ preventScroll: true });
}
this.setState({ open: false });
}

View File

@ -6,6 +6,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
import { defineMessages, injectIntl } from 'react-intl';
import { me, isStaff } from '../../../initial_state';
import classNames from 'classnames';
const messages = defineMessages({
delete: { id: 'status.delete', defaultMessage: 'Delete' },
@ -14,7 +15,7 @@ const messages = defineMessages({
mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
reply: { id: 'status.reply', defaultMessage: 'Reply' },
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' },
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
favourite: { id: 'status.favourite', defaultMessage: 'Favourite' },
@ -273,7 +274,7 @@ class ActionBar extends React.PureComponent {
return (
<div className='detailed-status__action-bar'>
<div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /></div>
<div className='detailed-status__button'><IconButton disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} /></div>
<div className='detailed-status__button' ><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} /></div>
<div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} /></div>
{shareButton}
<div className='detailed-status__button'><IconButton className='bookmark-icon' active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div>

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Споделяне",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} сподели",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Toud spilhennet",
"status.read_more": "Lenn muioc'h",
"status.reblog": "Skignañ",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -421,7 +421,7 @@
"id": "status.reblog"
},
{
"defaultMessage": "Boost to original audience",
"defaultMessage": "Boost with original visibility",
"id": "status.reblog_private"
},
{
@ -2421,7 +2421,7 @@
"id": "status.reblog"
},
{
"defaultMessage": "Boost to original audience",
"defaultMessage": "Boost with original visibility",
"id": "status.reblog_private"
},
{

View File

@ -394,7 +394,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "הדהוד",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "הודהד על ידי {name}",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "बूस्ट",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Podigni",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} je podigao",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Repetar",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} repetita",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Tijewwiqin yettwasentḍen",
"status.read_more": "Issin ugar",
"status.reblog": "Bḍu",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "Yebḍa-tt {name}",
"status.reblogs.empty": "Ula yiwen ur yebḍi tajewwiqt-agi ar tura. Ticki yebḍa-tt yiwen, ad d-iban da.",
"status.redraft": "Kkes tɛiwdeḍ tira",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Podrži",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} podržao(la)",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -389,7 +389,7 @@
"status.pinned": "Pinned toot",
"status.read_more": "Read more",
"status.reblog": "Boost",
"status.reblog_private": "Boost to original audience",
"status.reblog_private": "Boost with original visibility",
"status.reblogged_by": "{name} boosted",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",

View File

@ -112,11 +112,10 @@ const sharedCallbacks = {
},
disconnected () {
subscriptions.forEach(({ onDisconnect }) => onDisconnect());
subscriptions.forEach(subscription => unsubscribe(subscription));
},
reconnected () {
subscriptions.forEach(subscription => subscribe(subscription));
},
};
@ -252,15 +251,8 @@ const createConnection = (streamingAPIBaseURL, accessToken, channelName, { conne
const es = new EventSource(`${streamingAPIBaseURL}/api/v1/streaming/${channelName}?${params.join('&')}`);
let firstConnect = true;
es.onopen = () => {
if (firstConnect) {
firstConnect = false;
connected();
} else {
reconnected();
}
connected();
};
KNOWN_EVENT_TYPES.forEach(type => {

View File

@ -0,0 +1,118 @@
import axios from 'axios';
import * as WebAuthnJSON from '@github/webauthn-json';
import ready from '../mastodon/ready';
import 'regenerator-runtime/runtime';
function getCSRFToken() {
var CSRFSelector = document.querySelector('meta[name="csrf-token"]');
if (CSRFSelector) {
return CSRFSelector.getAttribute('content');
} else {
return null;
}
}
function hideFlashMessages() {
Array.from(document.getElementsByClassName('flash-message')).forEach(function(flashMessage) {
flashMessage.classList.add('hidden');
});
}
function callback(url, body) {
axios.post(url, JSON.stringify(body), {
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
'X-CSRF-Token': getCSRFToken(),
},
credentials: 'same-origin',
}).then(function(response) {
window.location.replace(response.data.redirect_path);
}).catch(function(error) {
if (error.response.status === 422) {
const errorMessage = document.getElementById('security-key-error-message');
errorMessage.classList.remove('hidden');
console.error(error.response.data.error);
} else {
console.error(error);
}
});
}
ready(() => {
if (!WebAuthnJSON.supported()) {
const unsupported_browser_message = document.getElementById('unsupported-browser-message');
if (unsupported_browser_message) {
unsupported_browser_message.classList.remove('hidden');
document.querySelector('.btn.js-webauthn').disabled = true;
}
}
const webAuthnCredentialRegistrationForm = document.getElementById('new_webauthn_credential');
if (webAuthnCredentialRegistrationForm) {
webAuthnCredentialRegistrationForm.addEventListener('submit', (event) => {
event.preventDefault();
var nickname = event.target.querySelector('input[name="new_webauthn_credential[nickname]"]');
if (nickname.value) {
axios.get('/settings/security_keys/options')
.then((response) => {
const credentialOptions = response.data;
WebAuthnJSON.create({ 'publicKey': credentialOptions }).then((credential) => {
var params = { 'credential': credential, 'nickname': nickname.value };
callback('/settings/security_keys', params);
}).catch((error) => {
const errorMessage = document.getElementById('security-key-error-message');
errorMessage.classList.remove('hidden');
console.error(error);
});
}).catch((error) => {
console.error(error.response.data.error);
});
} else {
nickname.focus();
}
});
}
const webAuthnCredentialAuthenticationForm = document.getElementById('webauthn-form');
if (webAuthnCredentialAuthenticationForm) {
webAuthnCredentialAuthenticationForm.addEventListener('submit', (event) => {
event.preventDefault();
axios.get('sessions/security_key_options')
.then((response) => {
const credentialOptions = response.data;
WebAuthnJSON.get({ 'publicKey': credentialOptions }).then((credential) => {
var params = { 'user': { 'credential': credential } };
callback('sign_in', params);
}).catch((error) => {
const errorMessage = document.getElementById('security-key-error-message');
errorMessage.classList.remove('hidden');
console.error(error);
});
}).catch((error) => {
console.error(error.response.data.error);
});
});
const otpAuthenticationForm = document.getElementById('otp-authentication-form');
const linkToOtp = document.getElementById('link-to-otp');
linkToOtp.addEventListener('click', () => {
webAuthnCredentialAuthenticationForm.classList.add('hidden');
otpAuthenticationForm.classList.remove('hidden');
hideFlashMessages();
});
const linkToWebAuthn = document.getElementById('link-to-webauthn');
linkToWebAuthn.addEventListener('click', () => {
otpAuthenticationForm.classList.add('hidden');
webAuthnCredentialAuthenticationForm.classList.remove('hidden');
hideFlashMessages();
});
}
});

File diff suppressed because one or more lines are too long

View File

@ -12,6 +12,10 @@ code {
}
.simple_form {
&.hidden {
display: none;
}
.input {
margin-bottom: 15px;
overflow: hidden;
@ -100,6 +104,14 @@ code {
}
}
.title {
color: #d9e1e8;
font-size: 20px;
line-height: 28px;
font-weight: 400;
margin-bottom: 30px;
}
.hint {
color: $darker-text-color;
@ -142,7 +154,7 @@ code {
}
}
.otp-hint {
.authentication-hint {
margin-bottom: 25px;
}
@ -592,6 +604,10 @@ code {
color: $error-value-color;
}
&.hidden {
display: none;
}
a {
display: inline-block;
color: $darker-text-color;