Convert ReplyThread to TS
Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
parent
75fc1299fb
commit
03ce480066
1 changed files with 56 additions and 45 deletions
|
@ -14,14 +14,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
import { MatrixEvent } from 'matrix-js-sdk/src/models/event';
|
||||||
import { makeUserPermalink, RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
import { makeUserPermalink, RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks";
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import { LayoutPropType } from "../../../settings/Layout";
|
import { Layout } from "../../../settings/Layout";
|
||||||
import escapeHtml from "escape-html";
|
import escapeHtml from "escape-html";
|
||||||
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
import MatrixClientContext from "../../../contexts/MatrixClientContext";
|
||||||
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
|
import { getUserNameColorClass } from "../../../utils/FormattingUtils";
|
||||||
|
@ -32,51 +32,54 @@ import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||||
import Spinner from './Spinner';
|
import Spinner from './Spinner';
|
||||||
import ReplyTile from "../rooms/ReplyTile";
|
import ReplyTile from "../rooms/ReplyTile";
|
||||||
import Pill from './Pill';
|
import Pill from './Pill';
|
||||||
|
import { Room } from 'matrix-js-sdk/src/models/room';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
// the latest event in this chain of replies
|
||||||
|
parentEv?: MatrixEvent,
|
||||||
|
// called when the ReplyThread contents has changed, including EventTiles thereof
|
||||||
|
onHeightChanged: () => void,
|
||||||
|
permalinkCreator: RoomPermalinkCreator,
|
||||||
|
// Specifies which layout to use.
|
||||||
|
layout?: Layout,
|
||||||
|
// Whether to always show a timestamp
|
||||||
|
alwaysShowTimestamps?: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
// The loaded events to be rendered as linear-replies
|
||||||
|
events: MatrixEvent[],
|
||||||
|
// The latest loaded event which has not yet been shown
|
||||||
|
loadedEv: MatrixEvent,
|
||||||
|
// Whether the component is still loading more events
|
||||||
|
loading: boolean,
|
||||||
|
// Whether as error was encountered fetching a replied to event.
|
||||||
|
err: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
// This component does no cycle detection, simply because the only way to make such a cycle would be to
|
// 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
|
// 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.
|
// be low as each event being loaded (after the first) is triggered by an explicit user action.
|
||||||
@replaceableComponent("views.elements.ReplyThread")
|
@replaceableComponent("views.elements.ReplyThread")
|
||||||
export default class ReplyThread extends React.Component {
|
export default class ReplyThread extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
|
||||||
// the latest event in this chain of replies
|
|
||||||
parentEv: PropTypes.instanceOf(MatrixEvent),
|
|
||||||
// called when the ReplyThread contents has changed, including EventTiles thereof
|
|
||||||
onHeightChanged: PropTypes.func.isRequired,
|
|
||||||
permalinkCreator: PropTypes.instanceOf(RoomPermalinkCreator).isRequired,
|
|
||||||
// Specifies which layout to use.
|
|
||||||
layout: LayoutPropType,
|
|
||||||
// Whether to always show a timestamp
|
|
||||||
alwaysShowTimestamps: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static contextType = MatrixClientContext;
|
static contextType = MatrixClientContext;
|
||||||
|
private unmounted = false;
|
||||||
|
private room: Room;
|
||||||
|
|
||||||
constructor(props, context) {
|
constructor(props, context) {
|
||||||
super(props, context);
|
super(props, context);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
// The loaded events to be rendered as linear-replies
|
|
||||||
events: [],
|
events: [],
|
||||||
|
|
||||||
// The latest loaded event which has not yet been shown
|
|
||||||
loadedEv: null,
|
loadedEv: null,
|
||||||
// Whether the component is still loading more events
|
|
||||||
loading: true,
|
loading: true,
|
||||||
|
|
||||||
// Whether as error was encountered fetching a replied to event.
|
|
||||||
err: false,
|
err: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.unmounted = false;
|
|
||||||
this.room = this.context.getRoom(this.props.parentEv.getRoomId());
|
this.room = this.context.getRoom(this.props.parentEv.getRoomId());
|
||||||
|
|
||||||
this.onQuoteClick = this.onQuoteClick.bind(this);
|
|
||||||
this.canCollapse = this.canCollapse.bind(this);
|
|
||||||
this.collapse = this.collapse.bind(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static getParentEventId(ev) {
|
public static getParentEventId(ev: MatrixEvent): string {
|
||||||
if (!ev || ev.isRedacted()) return;
|
if (!ev || ev.isRedacted()) return;
|
||||||
|
|
||||||
// XXX: For newer relations (annotations, replacements, etc.), we now
|
// XXX: For newer relations (annotations, replacements, etc.), we now
|
||||||
|
@ -92,7 +95,7 @@ export default class ReplyThread extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part of Replies fallback support
|
// Part of Replies fallback support
|
||||||
static stripPlainReply(body) {
|
public static stripPlainReply(body: string): string {
|
||||||
// Removes lines beginning with `> ` until you reach one that doesn't.
|
// Removes lines beginning with `> ` until you reach one that doesn't.
|
||||||
const lines = body.split('\n');
|
const lines = body.split('\n');
|
||||||
while (lines.length && lines[0].startsWith('> ')) lines.shift();
|
while (lines.length && lines[0].startsWith('> ')) lines.shift();
|
||||||
|
@ -102,7 +105,7 @@ export default class ReplyThread extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part of Replies fallback support
|
// Part of Replies fallback support
|
||||||
static stripHTMLReply(html) {
|
public static stripHTMLReply(html: string): string {
|
||||||
// Sanitize the original HTML for inclusion in <mx-reply>. We allow
|
// Sanitize the original HTML for inclusion in <mx-reply>. We allow
|
||||||
// any HTML, since the original sender could use special tags that we
|
// any HTML, since the original sender could use special tags that we
|
||||||
// don't recognize, but want to pass along to any recipients who do
|
// don't recognize, but want to pass along to any recipients who do
|
||||||
|
@ -124,7 +127,10 @@ export default class ReplyThread extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Part of Replies fallback support
|
// Part of Replies fallback support
|
||||||
static getNestedReplyText(ev, permalinkCreator) {
|
public static getNestedReplyText(
|
||||||
|
ev: MatrixEvent,
|
||||||
|
permalinkCreator: RoomPermalinkCreator,
|
||||||
|
): { body: string, html: string } {
|
||||||
if (!ev) return null;
|
if (!ev) return null;
|
||||||
|
|
||||||
let { body, formatted_body: html } = ev.getContent();
|
let { body, formatted_body: html } = ev.getContent();
|
||||||
|
@ -200,7 +206,7 @@ export default class ReplyThread extends React.Component {
|
||||||
return { body, html };
|
return { body, html };
|
||||||
}
|
}
|
||||||
|
|
||||||
static makeReplyMixIn(ev) {
|
public static makeReplyMixIn(ev: MatrixEvent) {
|
||||||
if (!ev) return {};
|
if (!ev) return {};
|
||||||
return {
|
return {
|
||||||
'm.relates_to': {
|
'm.relates_to': {
|
||||||
|
@ -211,10 +217,15 @@ export default class ReplyThread extends React.Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static makeThread(parentEv, onHeightChanged, permalinkCreator, ref, layout, alwaysShowTimestamps) {
|
public static makeThread(
|
||||||
if (!ReplyThread.getParentEventId(parentEv)) {
|
parentEv: MatrixEvent,
|
||||||
return null;
|
onHeightChanged: () => void,
|
||||||
}
|
permalinkCreator: RoomPermalinkCreator,
|
||||||
|
ref: React.RefObject<ReplyThread>,
|
||||||
|
layout: Layout,
|
||||||
|
alwaysShowTimestamps: boolean,
|
||||||
|
): JSX.Element {
|
||||||
|
if (!ReplyThread.getParentEventId(parentEv)) return null;
|
||||||
return <ReplyThread
|
return <ReplyThread
|
||||||
parentEv={parentEv}
|
parentEv={parentEv}
|
||||||
onHeightChanged={onHeightChanged}
|
onHeightChanged={onHeightChanged}
|
||||||
|
@ -237,7 +248,7 @@ export default class ReplyThread extends React.Component {
|
||||||
this.unmounted = true;
|
this.unmounted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
async initialize() {
|
private async initialize(): Promise<void> {
|
||||||
const { parentEv } = this.props;
|
const { parentEv } = this.props;
|
||||||
// at time of making this component we checked that props.parentEv has a parentEventId
|
// at time of making this component we checked that props.parentEv has a parentEventId
|
||||||
const ev = await this.getEvent(ReplyThread.getParentEventId(parentEv));
|
const ev = await this.getEvent(ReplyThread.getParentEventId(parentEv));
|
||||||
|
@ -256,7 +267,7 @@ export default class ReplyThread extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getNextEvent(ev) {
|
private async getNextEvent(ev: MatrixEvent): Promise<MatrixEvent> {
|
||||||
try {
|
try {
|
||||||
const inReplyToEventId = ReplyThread.getParentEventId(ev);
|
const inReplyToEventId = ReplyThread.getParentEventId(ev);
|
||||||
return await this.getEvent(inReplyToEventId);
|
return await this.getEvent(inReplyToEventId);
|
||||||
|
@ -265,7 +276,7 @@ export default class ReplyThread extends React.Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEvent(eventId) {
|
private async getEvent(eventId: string): Promise<MatrixEvent> {
|
||||||
if (!eventId) return null;
|
if (!eventId) return null;
|
||||||
const event = this.room.findEventById(eventId);
|
const event = this.room.findEventById(eventId);
|
||||||
if (event) return event;
|
if (event) return event;
|
||||||
|
@ -282,15 +293,15 @@ export default class ReplyThread extends React.Component {
|
||||||
return this.room.findEventById(eventId);
|
return this.room.findEventById(eventId);
|
||||||
}
|
}
|
||||||
|
|
||||||
canCollapse() {
|
public canCollapse = (): boolean => {
|
||||||
return this.state.events.length > 1;
|
return this.state.events.length > 1;
|
||||||
}
|
};
|
||||||
|
|
||||||
collapse() {
|
public collapse = (): void => {
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
};
|
||||||
|
|
||||||
async onQuoteClick() {
|
private onQuoteClick = async (): Promise<void> => {
|
||||||
const events = [this.state.loadedEv, ...this.state.events];
|
const events = [this.state.loadedEv, ...this.state.events];
|
||||||
|
|
||||||
let loadedEv = null;
|
let loadedEv = null;
|
||||||
|
@ -304,9 +315,9 @@ export default class ReplyThread extends React.Component {
|
||||||
});
|
});
|
||||||
|
|
||||||
dis.fire(Action.FocusSendMessageComposer);
|
dis.fire(Action.FocusSendMessageComposer);
|
||||||
}
|
};
|
||||||
|
|
||||||
getReplyThreadColorClass(ev) {
|
private getReplyThreadColorClass(ev: MatrixEvent): string {
|
||||||
return getUserNameColorClass(ev.getSender()).replace("Username", "ReplyThread");
|
return getUserNameColorClass(ev.getSender()).replace("Username", "ReplyThread");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue