Merge branch 'master' into glitch-soc/merge-upstream
Conflicts: - `package.json`
This commit is contained in:
@@ -10,6 +10,12 @@ const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => {
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
export function searchTextFromRawStatus (status) {
|
||||
const spoilerText = status.spoiler_text || '';
|
||||
const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n');
|
||||
return domParser.parseFromString(searchContent, 'text/html').documentElement.textContent;
|
||||
}
|
||||
|
||||
export function normalizeAccount(account) {
|
||||
account = { ...account };
|
||||
|
||||
|
@@ -14,6 +14,7 @@ import { unescapeHTML } from '../utils/html';
|
||||
import { getFiltersRegex } from '../selectors';
|
||||
import { usePendingItems as preferPendingItems } from 'mastodon/initial_state';
|
||||
import compareId from 'mastodon/compare_id';
|
||||
import { searchTextFromRawStatus } from 'mastodon/actions/importer/normalizer';
|
||||
|
||||
export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE';
|
||||
export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP';
|
||||
@@ -60,7 +61,7 @@ export function updateNotifications(notification, intlMessages, intlLocale) {
|
||||
if (notification.type === 'mention') {
|
||||
const dropRegex = filters[0];
|
||||
const regex = filters[1];
|
||||
const searchIndex = notification.status.spoiler_text + '\n' + unescapeHTML(notification.status.content);
|
||||
const searchIndex = searchTextFromRawStatus(notification.status);
|
||||
|
||||
if (dropRegex && dropRegex.test(searchIndex)) {
|
||||
return;
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import 'wicg-inert';
|
||||
|
||||
export default class ModalRoot extends React.PureComponent {
|
||||
|
||||
|
@@ -39,7 +39,8 @@ class Poll extends ImmutablePureComponent {
|
||||
|
||||
static getDerivedStateFromProps (props, state) {
|
||||
const { poll, intl } = props;
|
||||
const expired = poll.get('expired') || (new Date(poll.get('expires_at'))).getTime() < intl.now();
|
||||
const expires_at = poll.get('expires_at');
|
||||
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < intl.now();
|
||||
return (expired === state.expired) ? null : { expired };
|
||||
}
|
||||
|
||||
|
@@ -1,13 +1,22 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import StatusContainer from '../../../containers/status_container';
|
||||
import AccountContainer from '../../../containers/account_container';
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import Permalink from '../../../components/permalink';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { injectIntl, FormattedMessage, defineMessages } from 'react-intl';
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import StatusContainer from 'mastodon/containers/status_container';
|
||||
import AccountContainer from 'mastodon/containers/account_container';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import Permalink from 'mastodon/components/permalink';
|
||||
|
||||
const messages = defineMessages({
|
||||
favourite: { id: 'notification.favourite', defaultMessage: '{name} favourited your status' },
|
||||
follow: { id: 'notification.follow', defaultMessage: '{name} followed you' },
|
||||
ownPoll: { id: 'notification.own_poll', defaultMessage: 'Your poll has ended' },
|
||||
poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
|
||||
reblog: { id: 'notification.reblog', defaultMessage: '{name} boosted your status' },
|
||||
});
|
||||
|
||||
const notificationForScreenReader = (intl, message, timestamp) => {
|
||||
const output = [message];
|
||||
@@ -107,7 +116,7 @@ class Notification extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-follow focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow', defaultMessage: '{name} followed you' }, { name: account.get('acct') }), notification.get('created_at'))}>
|
||||
<div className='notification notification-follow focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='user-plus' fixedWidth />
|
||||
@@ -146,7 +155,7 @@ class Notification extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-favourite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.favourite', defaultMessage: '{name} favourited your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification notification-favourite focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.favourite, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='star' className='star-icon' fixedWidth />
|
||||
@@ -178,7 +187,7 @@ class Notification extends ImmutablePureComponent {
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-reblog focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.reblog', defaultMessage: '{name} boosted your status' }, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification notification-reblog focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='retweet' fixedWidth />
|
||||
@@ -205,25 +214,31 @@ class Notification extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
renderPoll (notification) {
|
||||
renderPoll (notification, account) {
|
||||
const { intl } = this.props;
|
||||
const ownPoll = me === account.get('id');
|
||||
const message = ownPoll ? intl.formatMessage(messages.ownPoll) : intl.formatMessage(messages.poll);
|
||||
|
||||
return (
|
||||
<HotKeys handlers={this.getHandlers()}>
|
||||
<div className='notification notification-poll focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' }), notification.get('created_at'))}>
|
||||
<div className='notification notification-poll focusable' tabIndex='0' aria-label={notificationForScreenReader(intl, message, notification.get('created_at'))}>
|
||||
<div className='notification__message'>
|
||||
<div className='notification__favourite-icon-wrapper'>
|
||||
<Icon id='tasks' fixedWidth />
|
||||
</div>
|
||||
|
||||
<span title={notification.get('created_at')}>
|
||||
<FormattedMessage id='notification.poll' defaultMessage='A poll you have voted in has ended' />
|
||||
{ownPoll ? (
|
||||
<FormattedMessage id='notification.own_poll' defaultMessage='Your poll has ended' />
|
||||
) : (
|
||||
<FormattedMessage id='notification.poll' defaultMessage='A poll you have voted in has ended' />
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<StatusContainer
|
||||
id={notification.get('status')}
|
||||
account={notification.get('account')}
|
||||
account={account}
|
||||
muted
|
||||
withDismiss
|
||||
hidden={this.props.hidden}
|
||||
@@ -253,7 +268,7 @@ class Notification extends ImmutablePureComponent {
|
||||
case 'reblog':
|
||||
return this.renderReblog(notification, link);
|
||||
case 'poll':
|
||||
return this.renderPoll(notification);
|
||||
return this.renderPoll(notification, account);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@@ -184,6 +184,15 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
this.setState({ description: e.target.value, dirty: true });
|
||||
}
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.setState({ description: e.target.value, dirty: true });
|
||||
this.handleSubmit();
|
||||
}
|
||||
}
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.onSave(this.state.description, this.state.focusX, this.state.focusY);
|
||||
this.props.onClose();
|
||||
@@ -254,6 +263,7 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
className='setting-text light'
|
||||
value={detecting ? '…' : description}
|
||||
onChange={this.handleChange}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
disabled={detecting}
|
||||
autoFocus
|
||||
/>
|
||||
|
@@ -6,9 +6,9 @@ import { createSelector } from 'reselect';
|
||||
import { debounce } from 'lodash';
|
||||
import { me } from '../../../initial_state';
|
||||
|
||||
const makeGetStatusIds = () => createSelector([
|
||||
const makeGetStatusIds = (pending = false) => createSelector([
|
||||
(state, { type }) => state.getIn(['settings', type], ImmutableMap()),
|
||||
(state, { type }) => state.getIn(['timelines', type, 'items'], ImmutableList()),
|
||||
(state, { type }) => state.getIn(['timelines', type, pending ? 'pendingItems' : 'items'], ImmutableList()),
|
||||
(state) => state.get('statuses'),
|
||||
], (columnSettings, statusIds, statuses) => {
|
||||
return statusIds.filter(id => {
|
||||
@@ -31,13 +31,14 @@ const makeGetStatusIds = () => createSelector([
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getStatusIds = makeGetStatusIds();
|
||||
const getPendingStatusIds = makeGetStatusIds(true);
|
||||
|
||||
const mapStateToProps = (state, { timelineId }) => ({
|
||||
statusIds: getStatusIds(state, { type: timelineId }),
|
||||
isLoading: state.getIn(['timelines', timelineId, 'isLoading'], true),
|
||||
isPartial: state.getIn(['timelines', timelineId, 'isPartial'], false),
|
||||
hasMore: state.getIn(['timelines', timelineId, 'hasMore']),
|
||||
numPending: state.getIn(['timelines', timelineId, 'pendingItems'], ImmutableList()).size,
|
||||
numPending: getPendingStatusIds(state, { type: timelineId }).size,
|
||||
});
|
||||
|
||||
return mapStateToProps;
|
||||
|
@@ -164,7 +164,9 @@ class SwitchingColumnsArea extends React.PureComponent {
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
this.node = c.getWrappedInstance();
|
||||
if (c) {
|
||||
this.node = c.getWrappedInstance();
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
|
16
app/javascript/mastodon/load_keyboard_extensions.js
Normal file
16
app/javascript/mastodon/load_keyboard_extensions.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// On KaiOS, we may not be able to use a mouse cursor or navigate using Tab-based focus, so we install
|
||||
// special left/right focus navigation keyboard listeners, at least on public pages (i.e. so folks
|
||||
// can at least log in using KaiOS devices).
|
||||
|
||||
function importArrowKeyNavigation() {
|
||||
return import(/* webpackChunkName: "arrow-key-navigation" */ 'arrow-key-navigation');
|
||||
}
|
||||
|
||||
export default function loadKeyboardExtensions() {
|
||||
if (/KAIOS/.test(navigator.userAgent)) {
|
||||
return importArrowKeyNavigation().then(arrowKeyNav => {
|
||||
arrowKeyNav.register();
|
||||
});
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
@@ -2078,21 +2078,25 @@
|
||||
},
|
||||
{
|
||||
"descriptors": [
|
||||
{
|
||||
"defaultMessage": "{name} followed you",
|
||||
"id": "notification.follow"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "{name} favourited your status",
|
||||
"id": "notification.favourite"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "{name} boosted your status",
|
||||
"id": "notification.reblog"
|
||||
"defaultMessage": "{name} followed you",
|
||||
"id": "notification.follow"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Your poll has ended",
|
||||
"id": "notification.own_poll"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "A poll you have voted in has ended",
|
||||
"id": "notification.poll"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "{name} boosted your status",
|
||||
"id": "notification.reblog"
|
||||
}
|
||||
],
|
||||
"path": "app/javascript/mastodon/features/notifications/components/notification.json"
|
||||
@@ -2771,6 +2775,10 @@
|
||||
"defaultMessage": "Exit full screen",
|
||||
"id": "video.exit_fullscreen"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Download file",
|
||||
"id": "video.download"
|
||||
},
|
||||
{
|
||||
"defaultMessage": "Sensitive content",
|
||||
"id": "status.sensitive_warning"
|
||||
|
@@ -279,6 +279,7 @@
|
||||
"notification.favourite": "{name} favourited your status",
|
||||
"notification.follow": "{name} followed you",
|
||||
"notification.mention": "{name} mentioned you",
|
||||
"notification.own_poll": "Your poll has ended",
|
||||
"notification.poll": "A poll you have voted in has ended",
|
||||
"notification.reblog": "{name} boosted your status",
|
||||
"notifications.clear": "Clear notifications",
|
||||
@@ -417,6 +418,7 @@
|
||||
"upload_modal.preview_label": "Preview ({ratio})",
|
||||
"upload_progress.label": "Uploading...",
|
||||
"video.close": "Close video",
|
||||
"video.download": "Download file",
|
||||
"video.exit_fullscreen": "Exit full screen",
|
||||
"video.expand": "Expand video",
|
||||
"video.fullscreen": "Full screen",
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import loadPolyfills from '../mastodon/load_polyfills';
|
||||
import ready from '../mastodon/ready';
|
||||
import { start } from '../mastodon/common';
|
||||
import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions';
|
||||
|
||||
start();
|
||||
|
||||
@@ -122,6 +123,9 @@ function main() {
|
||||
});
|
||||
}
|
||||
|
||||
loadPolyfills().then(main).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
loadPolyfills()
|
||||
.then(main)
|
||||
.then(loadKeyboardExtensions)
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
|
Reference in New Issue
Block a user