diff --git a/src/components/views/elements/Quote.js b/src/components/views/elements/Quote.js
index 761f7aa151..fa571aa063 100644
--- a/src/components/views/elements/Quote.js
+++ b/src/components/views/elements/Quote.js
@@ -21,33 +21,20 @@ import MatrixClientPeg from '../../../MatrixClientPeg';
import {wantsDateSeparator} from '../../../DateUtils';
import {MatrixEvent} from 'matrix-js-sdk';
import {makeUserPermalink} from "../../../matrix-to";
-
-// For URLs of matrix.to links in the timeline which have been reformatted by
-// HttpUtils transformTags to relative links. This excludes event URLs (with `[^\/]*`)
-const REGEX_LOCAL_MATRIXTO = /^#\/room\/([\#\!][^\/]*)\/(\$[^\/]*)$/;
+import SettingsStore from "../../../settings/SettingsStore";
export default class Quote extends React.Component {
- static isMessageUrl(url) {
- return !!REGEX_LOCAL_MATRIXTO.exec(url);
- }
-
- static childContextTypes = {
- matrixClient: PropTypes.object,
- addRichQuote: PropTypes.func,
- };
-
static propTypes = {
- // The matrix.to url of the event
- url: PropTypes.string,
- // The original node that was rendered
- node: PropTypes.instanceOf(Element),
// The parent event
parentEv: PropTypes.instanceOf(MatrixEvent),
+
+ onWidgetLoad: PropTypes.func.isRequired,
};
constructor(props, context) {
super(props, context);
+ /*
this.state = {
// The event related to this quote and their nested rich quotes
events: [],
@@ -55,45 +42,54 @@ export default class Quote extends React.Component {
show: true,
// Whether an error was encountered fetching nested older event, show node if it does
err: false,
+ loading: true,
+ };*/
+
+ this.state = {
+ // The loaded events to be rendered as linear-replies
+ events: [],
+
+ // The latest loaded event which has not yet been shown
+ loadedEv: null,
+ // Whether the component is still loading more events
+ loading: true,
+
+ // Whether as error was encountered fetching a replied to event.
+ err: null,
};
this.onQuoteClick = this.onQuoteClick.bind(this);
- this.addRichQuote = this.addRichQuote.bind(this);
- }
-
- getChildContext() {
- return {
- matrixClient: MatrixClientPeg.get(),
- addRichQuote: this.addRichQuote,
- };
- }
-
- parseUrl(url) {
- if (!url) return;
-
- // Default to the empty array if no match for simplicity
- // resource and prefix will be undefined instead of throwing
- const matrixToMatch = REGEX_LOCAL_MATRIXTO.exec(url) || [];
-
- const [, roomIdentifier, eventId] = matrixToMatch;
- return {roomIdentifier, eventId};
- }
-
- componentWillReceiveProps(nextProps) {
- const {roomIdentifier, eventId} = this.parseUrl(nextProps.url);
- if (!roomIdentifier || !eventId) return;
-
- const room = this.getRoom(roomIdentifier);
- if (!room) return;
-
- // Only try and load the event if we know about the room
- // otherwise we just leave a `Quote` anchor which can be used to navigate/join the room manually.
- this.setState({ events: [] });
- if (room) this.getEvent(room, eventId, true);
}
componentWillMount() {
- this.componentWillReceiveProps(this.props);
+ this.room = this.getRoom(this.props.parentEv.getRoomId());
+ this.initialize();
+ }
+
+ async initialize() {
+ const {parentEv} = this.props;
+ const inReplyTo = Quote.getInReplyTo(parentEv);
+
+ const ev = await this.getEvent(this.room, inReplyTo['event_id']);
+ this.setState({
+ events: [ev],
+ }, this.loadNextEvent);
+ }
+
+ async loadNextEvent() {
+ this.props.onWidgetLoad();
+ const ev = this.state.events[0];
+ const inReplyTo = Quote.getInReplyTo(ev);
+
+ if (!inReplyTo) {
+ this.setState({
+ loading: false,
+ });
+ return;
+ }
+
+ const loadedEv = await this.getEvent(this.room, inReplyTo['event_id']);
+ this.setState({loadedEv});
}
getRoom(id) {
@@ -105,84 +101,84 @@ export default class Quote extends React.Component {
});
}
- async getEvent(room, eventId, show) {
+ async getEvent(room, eventId) {
const event = room.findEventById(eventId);
- if (event) {
- this.addEvent(event, show);
- return;
- }
+ if (event) return event;
await MatrixClientPeg.get().getEventTimeline(room.getUnfilteredTimelineSet(), eventId);
- this.addEvent(room.findEventById(eventId), show);
- }
-
- addEvent(event, show) {
- const events = [event].concat(this.state.events);
- this.setState({events, show});
- }
-
- // addRichQuote(roomId, eventId) {
- addRichQuote(href) {
- const {roomIdentifier, eventId} = this.parseUrl(href);
- if (!roomIdentifier || !eventId) {
- this.setState({ err: true });
- return;
- }
-
- const room = this.getRoom(roomIdentifier);
- if (!room) {
- this.setState({ err: true });
- return;
- }
-
- this.getEvent(room, eventId, false);
+ return room.findEventById(eventId);
}
onQuoteClick() {
- this.setState({ show: true });
+ const events = [this.state.loadedEv].concat(this.state.events);
+
+ this.setState({
+ loadedEv: null,
+ events,
+ }, this.loadNextEvent);
+ }
+
+ static getInReplyTo(ev) {
+ if (ev.isRedacted()) return;
+
+ const {'m.relates_to': mRelatesTo} = ev.getContent();
+ if (mRelatesTo) {
+ return mRelatesTo['m.in_reply_to'];
+ }
+ }
+
+ static getRelationship(ev) {
+ return {
+ 'm.relates_to': {
+ 'm.in_reply_to': {
+ 'event_id': ev.getId(),
+ },
+ },
+ };
+ }
+
+ static getQuote(parentEv, onWidgetLoad) {
+ if (!SettingsStore.isFeatureEnabled("feature_rich_quoting") || !Quote.getInReplyTo(parentEv)) return null;
+ return ;
}
render() {
- const events = this.state.events.slice();
- if (events.length) {
- const evTiles = [];
-
- if (!this.state.show) {
- const oldestEv = events.shift();
- const Pill = sdk.getComponent('elements.Pill');
- const room = MatrixClientPeg.get().getRoom(oldestEv.getRoomId());
-
- evTiles.push(
- { - _t('In reply to); - } - - const EventTile = sdk.getComponent('views.rooms.EventTile'); - const DateSeparator = sdk.getComponent('messages.DateSeparator'); - events.forEach((ev) => { - let dateSep = null; - - if (wantsDateSeparator(this.props.parentEv.getDate(), ev.getDate())) { - dateSep =', {}, { - 'a': (sub) => { sub }, - 'pill': , - }) - } -
+ { + _t('In reply to; + } else if (this.state.loading) { + header =', {}, { + 'a': (sub) => { sub }, + 'pill': , + }) } - - evTiles.push( - { dateSep } -); - }); - - return- { evTiles }; +
LOADING...; } - // Deliberately render nothing if the URL isn't recognised - // in case we get an undefined/falsey node, replace it with null to make React happy - return this.props.node || null; + const EventTile = sdk.getComponent('views.rooms.EventTile'); + const DateSeparator = sdk.getComponent('messages.DateSeparator'); + const evTiles = this.state.events.map((ev) => { + let dateSep = null; + + if (wantsDateSeparator(this.props.parentEv.getDate(), ev.getDate())) { + dateSep =
+ { dateSep } +; + }); + + return+
; - - ReactDOM.render(quote, quoteContainer); - node.parentNode.replaceChild(quoteContainer, node); - node = quoteContainer; - } - pillified = true; } } else if (node.nodeType == Node.TEXT_NODE) { const Pill = sdk.getComponent('elements.Pill'); diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index 1d8df4c7a6..7e9a7a75f1 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -18,6 +18,8 @@ limitations under the License. 'use strict'; +import Quote from "../elements/Quote"; + const React = require('react'); import PropTypes from 'prop-types'; const classNames = require("classnames"); @@ -517,7 +519,7 @@ module.exports = withMatrixClient(React.createClass({ if (needsSenderProfile) { let text = null; - if (!this.props.tileShape || this.props.tileShape === 'quote') { + if (!this.props.tileShape) { if (msgtype === 'm.image') text = _td('%(senderName)s sent an image'); else if (msgtype === 'm.video') text = _td('%(senderName)s sent a video'); else if (msgtype === 'm.file') text = _td('%(senderName)s uploaded a file'); @@ -598,7 +600,6 @@ module.exports = withMatrixClient(React.createClass({ { this._renderE2EPadlock() }