Support m.room.tombstone events

For https://github.com/vector-im/riot-web/issues/7164
pull/21833/head
David Baker 2018-08-21 15:56:56 +01:00
parent c00610cb2c
commit 7f8cd203a8
3 changed files with 73 additions and 2 deletions

View File

@ -1,5 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -22,6 +23,27 @@ limitations under the License.
position: relative; position: relative;
} }
.mx_MessageComposer_replaced_wrapper {
margin-left: auto;
margin-right: auto;
}
.mx_MessageComposer_replaced_valign {
height: 60px;
display: table-cell;
vertical-align: middle;
}
.mx_MessageComposer_roomReplaced_icon {
float: left;
margin-right: 20px;
margin-top: 5px;
}
.mx_MessageComposer_roomReplaced_header {
font-weight: bold;
}
.mx_MessageComposer_autocomplete_wrapper { .mx_MessageComposer_autocomplete_wrapper {
position: relative; position: relative;
height: 0; height: 0;

View File

@ -1,6 +1,6 @@
/* /*
Copyright 2015, 2016 OpenMarket Ltd Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 New Vector Ltd Copyright 2017, 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -25,6 +25,7 @@ import dis from '../../../dispatcher';
import RoomViewStore from '../../../stores/RoomViewStore'; import RoomViewStore from '../../../stores/RoomViewStore';
import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore"; import SettingsStore, {SettingLevel} from "../../../settings/SettingsStore";
import Stickerpicker from './Stickerpicker'; import Stickerpicker from './Stickerpicker';
import { makeRoomPermalink } from '../../../matrix-to';
const formatButtonList = [ const formatButtonList = [
_td("bold"), _td("bold"),
@ -51,7 +52,9 @@ export default class MessageComposer extends React.Component {
this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this); this.onToggleMarkdownClicked = this.onToggleMarkdownClicked.bind(this);
this.onInputStateChanged = this.onInputStateChanged.bind(this); this.onInputStateChanged = this.onInputStateChanged.bind(this);
this.onEvent = this.onEvent.bind(this); this.onEvent = this.onEvent.bind(this);
this._onRoomStateEvents = this._onRoomStateEvents.bind(this);
this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this); this._onRoomViewStoreUpdate = this._onRoomViewStoreUpdate.bind(this);
this._onTombstoneClick = this._onTombstoneClick.bind(this);
this.state = { this.state = {
inputState: { inputState: {
@ -61,6 +64,7 @@ export default class MessageComposer extends React.Component {
}, },
showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'), showFormatting: SettingsStore.getValue('MessageComposer.showFormatting'),
isQuoting: Boolean(RoomViewStore.getQuotingEvent()), isQuoting: Boolean(RoomViewStore.getQuotingEvent()),
tombstone: this._getRoomTombstone(),
}; };
} }
@ -70,12 +74,14 @@ export default class MessageComposer extends React.Component {
// marked as encrypted. // marked as encrypted.
// XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something. // XXX: fragile as all hell - fixme somehow, perhaps with a dedicated Room.encryption event or something.
MatrixClientPeg.get().on("event", this.onEvent); MatrixClientPeg.get().on("event", this.onEvent);
MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents);
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate); this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate);
} }
componentWillUnmount() { componentWillUnmount() {
if (MatrixClientPeg.get()) { if (MatrixClientPeg.get()) {
MatrixClientPeg.get().removeListener("event", this.onEvent); MatrixClientPeg.get().removeListener("event", this.onEvent);
MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents);
} }
if (this._roomStoreToken) { if (this._roomStoreToken) {
this._roomStoreToken.remove(); this._roomStoreToken.remove();
@ -88,6 +94,18 @@ export default class MessageComposer extends React.Component {
this.forceUpdate(); this.forceUpdate();
} }
_onRoomStateEvents(ev, state) {
if (ev.getRoomId() !== this.props.room.roomId) return;
if (ev.getType() === 'm.room.tombstone') {
this.setState({tombstone: this._getRoomTombstone()});
}
}
_getRoomTombstone() {
return this.props.room.currentState.getStateEvents('m.room.tombstone', '');
}
_onRoomViewStoreUpdate() { _onRoomViewStoreUpdate() {
const isQuoting = Boolean(RoomViewStore.getQuotingEvent()); const isQuoting = Boolean(RoomViewStore.getQuotingEvent());
if (this.state.isQuoting === isQuoting) return; if (this.state.isQuoting === isQuoting) return;
@ -207,6 +225,17 @@ export default class MessageComposer extends React.Component {
this.messageComposerInput.enableRichtext(!this.state.inputState.isRichTextEnabled); this.messageComposerInput.enableRichtext(!this.state.inputState.isRichTextEnabled);
} }
_onTombstoneClick(ev) {
ev.preventDefault();
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
dis.dispatch({
action: 'view_room',
highlighted: true,
room_id: replacementRoomId,
});
}
render() { render() {
const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId);
const uploadInputStyle = {display: 'none'}; const uploadInputStyle = {display: 'none'};
@ -262,7 +291,7 @@ export default class MessageComposer extends React.Component {
</div>; </div>;
} }
const canSendMessages = this.props.room.currentState.maySendMessage( const canSendMessages = !this.state.tombstone && this.props.room.currentState.maySendMessage(
MatrixClientPeg.get().credentials.userId); MatrixClientPeg.get().credentials.userId);
if (canSendMessages) { if (canSendMessages) {
@ -322,6 +351,24 @@ export default class MessageComposer extends React.Component {
callButton, callButton,
videoCallButton, videoCallButton,
); );
} else if (this.state.tombstone) {
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
controls.push(<div className="mx_MessageComposer_replaced_wrapper">
<div className="mx_MessageComposer_replaced_valign">
<img className="mx_MessageComposer_roomReplaced_icon" src="img/room_replaced.svg" />
<span className="mx_MessageComposer_roomReplaced_header">
{_t("This room has been replaced and is no longer active.")}
</span><br />
<a href={makeRoomPermalink(replacementRoomId)}
className="mx_MessageComposer_roomReplaced_link"
onClick={this._onTombstoneClick}
>
{_t("The conversation continues here.")}
</a>
</div>
</div>);
} else { } else {
controls.push( controls.push(
<div key="controls_error" className="mx_MessageComposer_noperm_error"> <div key="controls_error" className="mx_MessageComposer_noperm_error">

View File

@ -393,6 +393,8 @@
"At this time it is not possible to reply with a file so this will be sent without being a reply.": "At this time it is not possible to reply with a file so this will be sent without being a reply.", "At this time it is not possible to reply with a file so this will be sent without being a reply.": "At this time it is not possible to reply with a file so this will be sent without being a reply.",
"Upload Files": "Upload Files", "Upload Files": "Upload Files",
"Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?", "Are you sure you want to upload the following files?": "Are you sure you want to upload the following files?",
"This room has been replaced and is no longer active.": "This room has been replaced and is no longer active.",
"The conversation continues here.": "The conversation continues here.",
"Encrypted room": "Encrypted room", "Encrypted room": "Encrypted room",
"Unencrypted room": "Unencrypted room", "Unencrypted room": "Unencrypted room",
"Hangup": "Hangup", "Hangup": "Hangup",