Merge branch 'master' into glitch-soc/merge-upstream
Conflicts: - `config/webpack/shared.js`: Upstream has changed how Tesseract.js gets included and dropped a dependency. The conflict is caused by glitch-soc having different code due to its theming system. Ported upstream changes. - `lib/mastodon/version.rb`: Upstream refactor/code style change in a place we replaced upstream's repo URL with ours. Ported upstram changes, keeping our repo URL. - `yarn.lock`: Upstream dropped dependencies, one of which was textually too close to a glitch-soc-specific dependency. Not a real conflict.
This commit is contained in:
@ -150,10 +150,10 @@ export const createListFail = error => ({
|
||||
error,
|
||||
});
|
||||
|
||||
export const updateList = (id, title, shouldReset) => (dispatch, getState) => {
|
||||
export const updateList = (id, title, shouldReset, replies_policy) => (dispatch, getState) => {
|
||||
dispatch(updateListRequest(id));
|
||||
|
||||
api(getState).put(`/api/v1/lists/${id}`, { title }).then(({ data }) => {
|
||||
api(getState).put(`/api/v1/lists/${id}`, { title, replies_policy }).then(({ data }) => {
|
||||
dispatch(updateListSuccess(data));
|
||||
|
||||
if (shouldReset) {
|
||||
|
@ -12,7 +12,7 @@ const emojiFilenames = (emojis) => {
|
||||
};
|
||||
|
||||
// Emoji requiring extra borders depending on theme
|
||||
const darkEmoji = emojiFilenames(['🎱', '🐜', '⚫', '🖤', '⬛', '◼️', '◾', '◼️', '✒️', '▪️', '💣', '🎳', '📷', '📸', '♣️', '🕶️', '✴️', '🔌', '💂♀️', '📽️', '🍳', '🦍', '💂', '🔪', '🕳️', '🕹️', '🕋', '🖊️', '🖋️', '💂♂️', '🎤', '🎓', '🎥', '🎼', '♠️', '🎩', '🦃', '📼', '📹', '🎮', '🐃', '🏴']);
|
||||
const darkEmoji = emojiFilenames(['🎱', '🐜', '⚫', '🖤', '⬛', '◼️', '◾', '◼️', '✒️', '▪️', '💣', '🎳', '📷', '📸', '♣️', '🕶️', '✴️', '🔌', '💂♀️', '📽️', '🍳', '🦍', '💂', '🔪', '🕳️', '🕹️', '🕋', '🖊️', '🖋️', '💂♂️', '🎤', '🎓', '🎥', '🎼', '♠️', '🎩', '🦃', '📼', '📹', '🎮', '🐃', '🏴', '🐞']);
|
||||
const lightEmoji = emojiFilenames(['👽', '⚾', '🐔', '☁️', '💨', '🕊️', '👀', '🍥', '👻', '🐐', '❕', '❔', '⛸️', '🌩️', '🔊', '🔇', '📃', '🌧️', '🐏', '🍚', '🍙', '🐓', '🐑', '💀', '☠️', '🌨️', '🔉', '🔈', '💬', '💭', '🏐', '🏳️', '⚪', '⬜', '◽', '◻️', '▫️']);
|
||||
|
||||
const emojiFilename = (filename) => {
|
||||
|
@ -40,6 +40,7 @@ const messages = defineMessages({
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
myAccount: state.getIn(['accounts', me]),
|
||||
columns: state.getIn(['settings', 'columns']),
|
||||
unreadFollowRequests: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
|
||||
});
|
||||
|
||||
@ -89,60 +90,66 @@ class GettingStarted extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { intl, myAccount, multiColumn, unreadFollowRequests } = this.props;
|
||||
const { intl, myAccount, columns, multiColumn, unreadFollowRequests } = this.props;
|
||||
|
||||
const navItems = [];
|
||||
let i = 1;
|
||||
let height = (multiColumn) ? 0 : 60;
|
||||
|
||||
if (multiColumn) {
|
||||
navItems.push(
|
||||
<ColumnSubheading key={i++} text={intl.formatMessage(messages.discover)} />,
|
||||
<ColumnLink key={i++} icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />,
|
||||
<ColumnLink key={i++} icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />,
|
||||
<ColumnSubheading key='header-discover' text={intl.formatMessage(messages.discover)} />,
|
||||
<ColumnLink key='community_timeline' icon='users' text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />,
|
||||
<ColumnLink key='public_timeline' icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />,
|
||||
);
|
||||
|
||||
height += 34 + 48*2;
|
||||
|
||||
if (profile_directory) {
|
||||
navItems.push(
|
||||
<ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
|
||||
<ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
|
||||
);
|
||||
|
||||
height += 48;
|
||||
}
|
||||
|
||||
navItems.push(
|
||||
<ColumnSubheading key={i++} text={intl.formatMessage(messages.personal)} />,
|
||||
<ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />,
|
||||
);
|
||||
|
||||
height += 34;
|
||||
} else if (profile_directory) {
|
||||
navItems.push(
|
||||
<ColumnLink key={i++} icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
|
||||
<ColumnLink key='directory' icon='address-book' text={intl.formatMessage(messages.profile_directory)} to='/directory' />,
|
||||
);
|
||||
|
||||
height += 48;
|
||||
}
|
||||
|
||||
if (multiColumn && !columns.find(item => item.get('id') === 'HOME')) {
|
||||
navItems.push(
|
||||
<ColumnLink key='home' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/timelines/home' />,
|
||||
);
|
||||
height += 48;
|
||||
}
|
||||
|
||||
navItems.push(
|
||||
<ColumnLink key={i++} icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />,
|
||||
<ColumnLink key={i++} icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />,
|
||||
<ColumnLink key={i++} icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
|
||||
<ColumnLink key={i++} icon='list-ul' text={intl.formatMessage(messages.lists)} to='/lists' />,
|
||||
<ColumnLink key='direct' icon='envelope' text={intl.formatMessage(messages.direct)} to='/timelines/direct' />,
|
||||
<ColumnLink key='bookmark' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />,
|
||||
<ColumnLink key='favourites' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
|
||||
<ColumnLink key='lists' icon='list-ul' text={intl.formatMessage(messages.lists)} to='/lists' />,
|
||||
);
|
||||
|
||||
height += 48*4;
|
||||
|
||||
if (myAccount.get('locked') || unreadFollowRequests > 0) {
|
||||
navItems.push(<ColumnLink key={i++} icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
|
||||
navItems.push(<ColumnLink key='follow_requests' icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
|
||||
height += 48;
|
||||
}
|
||||
|
||||
if (!multiColumn) {
|
||||
navItems.push(
|
||||
<ColumnSubheading key={i++} text={intl.formatMessage(messages.settings_subheading)} />,
|
||||
<ColumnLink key={i++} icon='gears' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />,
|
||||
<ColumnSubheading key='header-settings' text={intl.formatMessage(messages.settings_subheading)} />,
|
||||
<ColumnLink key='preferences' icon='gears' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />,
|
||||
);
|
||||
|
||||
height += 34 + 48;
|
||||
|
@ -10,15 +10,19 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
|
||||
import { connectListStream } from '../../actions/streaming';
|
||||
import { expandListTimeline } from '../../actions/timelines';
|
||||
import { fetchList, deleteList } from '../../actions/lists';
|
||||
import { fetchList, deleteList, updateList } from '../../actions/lists';
|
||||
import { openModal } from '../../actions/modal';
|
||||
import MissingIndicator from '../../components/missing_indicator';
|
||||
import LoadingIndicator from '../../components/loading_indicator';
|
||||
import Icon from 'mastodon/components/icon';
|
||||
import RadioButton from 'mastodon/components/radio_button';
|
||||
|
||||
const messages = defineMessages({
|
||||
deleteMessage: { id: 'confirmations.delete_list.message', defaultMessage: 'Are you sure you want to permanently delete this list?' },
|
||||
deleteConfirm: { id: 'confirmations.delete_list.confirm', defaultMessage: 'Delete' },
|
||||
all_replies: { id: 'lists.replies_policy.all_replies', defaultMessage: 'Any followed user' },
|
||||
no_replies: { id: 'lists.replies_policy.no_replies', defaultMessage: 'No one' },
|
||||
list_replies: { id: 'lists.replies_policy.list_replies', defaultMessage: 'Members of the list' },
|
||||
});
|
||||
|
||||
const mapStateToProps = (state, props) => ({
|
||||
@ -131,11 +135,18 @@ class ListTimeline extends React.PureComponent {
|
||||
}));
|
||||
}
|
||||
|
||||
handleRepliesPolicyChange = ({ target }) => {
|
||||
const { dispatch } = this.props;
|
||||
const { id } = this.props.params;
|
||||
dispatch(updateList(id, undefined, false, target.value));
|
||||
}
|
||||
|
||||
render () {
|
||||
const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list } = this.props;
|
||||
const { shouldUpdateScroll, hasUnread, columnId, multiColumn, list, intl } = this.props;
|
||||
const { id } = this.props.params;
|
||||
const pinned = !!columnId;
|
||||
const title = list ? list.get('title') : id;
|
||||
const replies_policy = list ? list.get('replies_policy') : undefined;
|
||||
|
||||
if (typeof list === 'undefined') {
|
||||
return (
|
||||
@ -166,7 +177,7 @@ class ListTimeline extends React.PureComponent {
|
||||
pinned={pinned}
|
||||
multiColumn={multiColumn}
|
||||
>
|
||||
<div className='column-header__links'>
|
||||
<div className='column-settings__row column-header__links'>
|
||||
<button className='text-btn column-header__setting-btn' tabIndex='0' onClick={this.handleEditClick}>
|
||||
<Icon id='pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' />
|
||||
</button>
|
||||
@ -175,6 +186,19 @@ class ListTimeline extends React.PureComponent {
|
||||
<Icon id='trash' /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{ replies_policy !== undefined && (
|
||||
<div role='group' aria-labelledby={`list-${id}-replies-policy`}>
|
||||
<span id={`list-${id}-replies-policy`} className='column-settings__section'>
|
||||
<FormattedMessage id='lists.replies_policy.title' defaultMessage='Show replies to:' />
|
||||
</span>
|
||||
<div className='column-settings__row'>
|
||||
{ ['no_replies', 'list_replies', 'all_replies'].map(policy => (
|
||||
<RadioButton name='order' value={policy} label={intl.formatMessage(messages[policy])} checked={replies_policy === policy} onChange={this.handleRepliesPolicyChange} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</ColumnHeader>
|
||||
|
||||
<StatusListContainer
|
||||
|
@ -18,6 +18,8 @@ import { length } from 'stringz';
|
||||
import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
|
||||
import GIFV from 'mastodon/components/gifv';
|
||||
import { me } from 'mastodon/initial_state';
|
||||
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
|
||||
import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
|
||||
|
||||
const messages = defineMessages({
|
||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||
@ -104,6 +106,7 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
dirty: false,
|
||||
progress: 0,
|
||||
loading: true,
|
||||
ocrStatus: '',
|
||||
};
|
||||
|
||||
componentWillMount () {
|
||||
@ -219,11 +222,18 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
|
||||
this.setState({ detecting: true });
|
||||
|
||||
fetchTesseract().then(({ TesseractWorker }) => {
|
||||
const worker = new TesseractWorker({
|
||||
workerPath: `${assetHost}/packs/ocr/worker.min.js`,
|
||||
corePath: `${assetHost}/packs/ocr/tesseract-core.wasm.js`,
|
||||
langPath: `${assetHost}/ocr/lang-data`,
|
||||
fetchTesseract().then(({ createWorker }) => {
|
||||
const worker = createWorker({
|
||||
workerPath: tesseractWorkerPath,
|
||||
corePath: tesseractCorePath,
|
||||
langPath: assetHost,
|
||||
logger: ({ status, progress }) => {
|
||||
if (status === 'recognizing text') {
|
||||
this.setState({ ocrStatus: 'detecting', progress });
|
||||
} else {
|
||||
this.setState({ ocrStatus: 'preparing', progress });
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
let media_url = media.get('url');
|
||||
@ -236,12 +246,18 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
worker.recognize(media_url)
|
||||
.progress(({ progress }) => this.setState({ progress }))
|
||||
.finally(() => worker.terminate())
|
||||
.then(({ text }) => this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false }))
|
||||
.catch(() => this.setState({ detecting: false }));
|
||||
}).catch(() => this.setState({ detecting: false }));
|
||||
(async () => {
|
||||
await worker.load();
|
||||
await worker.loadLanguage('eng');
|
||||
await worker.initialize('eng');
|
||||
const { data: { text } } = await worker.recognize(media_url);
|
||||
this.setState({ description: removeExtraLineBreaks(text), dirty: true, detecting: false });
|
||||
await worker.terminate();
|
||||
})();
|
||||
}).catch((e) => {
|
||||
console.error(e);
|
||||
this.setState({ detecting: false });
|
||||
});
|
||||
}
|
||||
|
||||
handleThumbnailChange = e => {
|
||||
@ -261,7 +277,7 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
|
||||
render () {
|
||||
const { media, intl, account, onClose, isUploadingThumbnail } = this.props;
|
||||
const { x, y, dragging, description, dirty, detecting, progress } = this.state;
|
||||
const { x, y, dragging, description, dirty, detecting, progress, ocrStatus } = this.state;
|
||||
|
||||
const width = media.getIn(['meta', 'original', 'width']) || null;
|
||||
const height = media.getIn(['meta', 'original', 'height']) || null;
|
||||
@ -282,6 +298,13 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
descriptionLabel = <FormattedMessage id='upload_form.description' defaultMessage='Describe for the visually impaired' />;
|
||||
}
|
||||
|
||||
let ocrMessage = '';
|
||||
if (ocrStatus === 'detecting') {
|
||||
ocrMessage = <FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />;
|
||||
} else {
|
||||
ocrMessage = <FormattedMessage id='upload_modal.preparing_ocr' defaultMessage='Preparing OCR…' />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='modal-root__modal report-modal' style={{ maxWidth: 960 }}>
|
||||
<div className='report-modal__target'>
|
||||
@ -333,7 +356,7 @@ class FocalPointModal extends ImmutablePureComponent {
|
||||
/>
|
||||
|
||||
<div className='setting-text__modifiers'>
|
||||
<UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={<FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />} />
|
||||
<UploadProgress progress={progress * 100} active={detecting} icon='file-text-o' message={ocrMessage} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -980,14 +980,6 @@
|
||||
outline: 0;
|
||||
background: lighten($ui-base-color, 4%);
|
||||
|
||||
.status.status-direct {
|
||||
background: lighten($ui-base-color, 12%);
|
||||
|
||||
&.muted {
|
||||
background: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.detailed-status,
|
||||
.detailed-status__action-bar {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
@ -1022,11 +1014,6 @@
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
&.status-direct:not(.read) {
|
||||
background: lighten($ui-base-color, 8%);
|
||||
border-bottom-color: lighten($ui-base-color, 12%);
|
||||
}
|
||||
|
||||
&.light {
|
||||
.status__relative-time,
|
||||
.status__visibility-icon {
|
||||
@ -1064,16 +1051,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.notification-favourite {
|
||||
.status.status-direct {
|
||||
background: transparent;
|
||||
|
||||
.icon-button.disabled {
|
||||
color: lighten($action-button-color, 13%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.status__relative-time,
|
||||
.status__visibility-icon,
|
||||
.notification__relative_time {
|
||||
@ -5957,6 +5934,10 @@ a.status-card.compact:hover {
|
||||
}
|
||||
}
|
||||
|
||||
.column-settings__row .radio-button {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.radio-button {
|
||||
font-size: 14px;
|
||||
position: relative;
|
||||
|
Reference in New Issue
Block a user