Add reply fallback and run gen-i18n

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski 2018-03-04 12:39:34 +00:00
parent 665ddccb0d
commit df56a67fda
No known key found for this signature in database
GPG key ID: 3F879DA5AD802A5E
5 changed files with 123 additions and 55 deletions

View file

@ -21,13 +21,13 @@ import dis from '../../../dispatcher';
import MatrixClientPeg from '../../../MatrixClientPeg';
import {wantsDateSeparator} from '../../../DateUtils';
import {MatrixEvent} from 'matrix-js-sdk';
import {makeUserPermalink} from "../../../matrix-to";
import {makeEventPermalink, makeUserPermalink} from "../../../matrix-to";
import SettingsStore from "../../../settings/SettingsStore";
// This component does no cycle detection, simply because the only way to make such a cycle would be to
// craft event_id's, using a homeserver that generates predictable event IDs; even then the impact would
// be low as each event being loaded (after the first) is triggered by an explicit user action.
export default class Reply extends React.Component {
export default class ReplyThread extends React.Component {
static propTypes = {
// the latest event in this chain of replies
parentEv: PropTypes.instanceOf(MatrixEvent),
@ -66,13 +66,13 @@ export default class Reply extends React.Component {
async initialize() {
const {parentEv} = this.props;
const inReplyTo = Reply.getInReplyTo(parentEv);
const inReplyTo = ReplyThread.getInReplyTo(parentEv);
if (!inReplyTo) {
this.setState({err: true});
return;
}
const ev = await Reply.getEvent(this.room, inReplyTo['event_id']);
const ev = await ReplyThread.getEvent(this.room, inReplyTo['event_id']);
if (this.unmounted) return;
if (ev) {
@ -87,7 +87,7 @@ export default class Reply extends React.Component {
async loadNextEvent() {
if (this.unmounted) return;
const ev = this.state.events[0];
const inReplyTo = Reply.getInReplyTo(ev);
const inReplyTo = ReplyThread.getInReplyTo(ev);
if (!inReplyTo) {
this.setState({
@ -96,7 +96,7 @@ export default class Reply extends React.Component {
return;
}
const loadedEv = await Reply.getEvent(this.room, inReplyTo['event_id']);
const loadedEv = await ReplyThread.getEvent(this.room, inReplyTo['event_id']);
if (this.unmounted) return;
if (loadedEv) {
@ -130,7 +130,7 @@ export default class Reply extends React.Component {
}
static getInReplyTo(ev) {
if (ev.isRedacted()) return;
if (!ev || ev.isRedacted()) return;
const mRelatesTo = ev.getWireContent()['m.relates_to'];
if (mRelatesTo && mRelatesTo['m.in_reply_to']) {
@ -139,7 +139,39 @@ export default class Reply extends React.Component {
}
}
static getMRelatesTo(ev) {
// Part of Replies fallback support
static stripPlainReply(body) {
const lines = body.split('\n');
while (lines[0].startsWith('> ')) lines.shift();
return lines.join('\n');
}
// Part of Replies fallback support
static stripHTMLReply(html) {
return html.replace(/^<blockquote data-mx-reply>[\s\S]+?<\/blockquote>/, '');
}
// Part of Replies fallback support
static getNestedReplyText(ev) {
if (!ev) return null;
let {body, formatted_body: html} = ev.getContent();
if (this.getInReplyTo(ev)) {
if (body) body = this.stripPlainReply(body);
if (html) html = this.stripHTMLReply(html);
}
html = `<blockquote data-mx-reply><a href="${makeEventPermalink(ev.getRoomId(), ev.getId())}">In reply to</a> `
+ `<a href="${makeUserPermalink(ev.getSender())}">${ev.getSender()}</a> ${html || body}</blockquote>`;
// `&lt;${ev.getSender()}&gt; ${html || body}</blockquote>`;
const lines = body.split('\n');
const first = `> <${ev.getSender()}> ${lines.shift()}`;
body = first + lines.map((line) => `> ${line}`).join('\n') + '\n';
return {body, html};
}
static getReplyEvContent(ev) {
if (!ev) return {};
return {
'm.relates_to': {
@ -151,8 +183,10 @@ export default class Reply extends React.Component {
}
static getQuote(parentEv, onWidgetLoad) {
if (!SettingsStore.isFeatureEnabled("feature_rich_quoting") || !Reply.getInReplyTo(parentEv)) return <div />;
return <Reply parentEv={parentEv} onWidgetLoad={onWidgetLoad} />;
if (!SettingsStore.isFeatureEnabled("feature_rich_quoting") || !ReplyThread.getInReplyTo(parentEv)) {
return <div />;
}
return <ReplyThread parentEv={parentEv} onWidgetLoad={onWidgetLoad} />;
}
render() {

View file

@ -35,6 +35,7 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import ContextualMenu from '../../structures/ContextualMenu';
import SettingsStore from "../../../settings/SettingsStore";
import PushProcessor from 'matrix-js-sdk/lib/pushprocessor';
import ReplyThread from "../elements/ReplyThread";
linkifyMatrix(linkify);
@ -423,6 +424,8 @@ module.exports = React.createClass({
let body = HtmlUtils.bodyToHtml(content, this.props.highlights, {
disableBigEmoji: SettingsStore.getValue('TextualBody.disableBigEmoji'),
// Part of Replies fallback support
stripReplyFallback: Boolean(ReplyThread.getInReplyTo(mxEvent)),
});
if (this.props.highlightLink) {

View file

@ -54,7 +54,7 @@ import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import {makeUserPermalink} from "../../../matrix-to";
import ReplyPreview from "./ReplyPreview";
import RoomViewStore from '../../../stores/RoomViewStore';
import Reply from "../elements/ReplyThread";
import ReplyThread from "../elements/ReplyThread";
import {ContentHelpers} from 'matrix-js-sdk';
const EMOJI_SHORTNAMES = Object.keys(emojioneList);
@ -830,7 +830,18 @@ export default class MessageComposerInput extends React.Component {
this.state.isRichtextEnabled ? 'html' : 'markdown',
);
const replyingToEv = RoomViewStore.getQuotingEvent();
if (contentText.startsWith('/me')) {
if (replyingToEv) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog('Emote Reply Fail', '', ErrorDialog, {
title: _t("Unable to reply"),
description: _t("At this time it is not possible to reply with an emote."),
});
return false;
}
contentText = contentText.substring(4);
// bit of a hack, but the alternative would be quite complicated
if (contentHTML) contentHTML = contentHTML.replace(/\/me ?/, '');
@ -838,18 +849,24 @@ export default class MessageComposerInput extends React.Component {
sendTextFn = ContentHelpers.makeEmoteMessage;
}
const content = Reply.getMRelatesTo(RoomViewStore.getQuotingEvent());
let sendMessagePromise;
if (contentHTML) {
Object.assign(content, sendHtmlFn(contentText, contentHTML));
sendMessagePromise = this.client.sendMessage(this.props.room.roomId, content);
} else {
Object.assign(content, sendTextFn(contentText));
sendMessagePromise = this.client.sendMessage(this.props.room.roomId, content);
let content = contentHTML || replyingToEv ? sendHtmlFn(contentText, contentHTML) : sendTextFn(contentText);
if (replyingToEv) {
const replyContent = ReplyThread.getReplyEvContent(replyingToEv);
content = Object.assign(replyContent, content);
// Part of Replies fallback support - prepend the text we're sending with the text we're replying to
const nestedReply = ReplyThread.getNestedReplyText(replyingToEv);
if (nestedReply) {
if (content.formatted_body) {
content.formatted_body = nestedReply.html + content.formatted_body;
}
content.body = nestedReply.body + content.body;
}
}
sendMessagePromise.done((res) => {
this.client.sendMessage(this.props.room.roomId, content).done((res) => {
dis.dispatch({
action: 'message_sent',
});