Merge pull request #3170 from matrix-org/bwindels/handle-associated-failures
Handle associated event send failures
This commit is contained in:
commit
353f5a205c
8 changed files with 153 additions and 13 deletions
|
@ -171,12 +171,19 @@ limitations under the License.
|
||||||
opacity: 0.4;
|
opacity: 0.4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.mx_EventTile_notSent.mx_EventTile_redacted .mx_UnknownBody {
|
||||||
|
--lozenge-color: $event-notsent-color !important;
|
||||||
|
--lozenge-border-color: $event-notsent-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
.mx_EventTile_notSent {
|
.mx_EventTile_notSent {
|
||||||
color: $event-notsent-color;
|
color: $event-notsent-color;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_redacted .mx_EventTile_line .mx_UnknownBody,
|
.mx_EventTile_redacted .mx_EventTile_line .mx_UnknownBody,
|
||||||
.mx_EventTile_redacted .mx_EventTile_reply .mx_UnknownBody {
|
.mx_EventTile_redacted .mx_EventTile_reply .mx_UnknownBody {
|
||||||
|
--lozenge-color: $event-redacted-fg-color;
|
||||||
|
--lozenge-border-color: $event-redacted-border-color;
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 22px;
|
height: 22px;
|
||||||
|
@ -184,18 +191,22 @@ limitations under the License.
|
||||||
border-radius: 11px;
|
border-radius: 11px;
|
||||||
background: repeating-linear-gradient(
|
background: repeating-linear-gradient(
|
||||||
-45deg,
|
-45deg,
|
||||||
$event-redacted-fg-color,
|
var(--lozenge-color),
|
||||||
$event-redacted-fg-color 3px,
|
var(--lozenge-color) 3px,
|
||||||
transparent 3px,
|
transparent 3px,
|
||||||
transparent 6px
|
transparent 6px
|
||||||
);
|
);
|
||||||
box-shadow: 0px 0px 3px $event-redacted-border-color inset;
|
box-shadow: 0px 0px 3px var(--lozenge-border-color) inset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_highlight,
|
.mx_EventTile_highlight,
|
||||||
.mx_EventTile_highlight .markdown-body
|
.mx_EventTile_highlight .markdown-body
|
||||||
{
|
{
|
||||||
color: $warning-color;
|
color: $event-highlight-fg-color;
|
||||||
|
|
||||||
|
.mx_EventTile_line {
|
||||||
|
background-color: $event-highlight-bg-color;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_EventTile_contextual {
|
.mx_EventTile_contextual {
|
||||||
|
|
|
@ -121,6 +121,9 @@ $event-sending-color: $text-secondary-color;
|
||||||
$event-redacted-fg-color: #606060;
|
$event-redacted-fg-color: #606060;
|
||||||
$event-redacted-border-color: #000000;
|
$event-redacted-border-color: #000000;
|
||||||
|
|
||||||
|
$event-highlight-fg-color: $warning-color;
|
||||||
|
$event-highlight-bg-color: $event-selected-color;
|
||||||
|
|
||||||
// event timestamp
|
// event timestamp
|
||||||
$event-timestamp-color: $text-secondary-color;
|
$event-timestamp-color: $text-secondary-color;
|
||||||
|
|
||||||
|
|
|
@ -196,11 +196,19 @@ $widget-menu-bar-bg-color: $secondary-accent-color;
|
||||||
|
|
||||||
// ********************
|
// ********************
|
||||||
|
|
||||||
|
// both $event-highlight-bg-color and $room-warning-bg-color share this value,
|
||||||
|
// so to not make their order dependent on who depends on who, have a shared value
|
||||||
|
// defined before both
|
||||||
|
$yellow-background: #fff8e3;
|
||||||
|
|
||||||
// event tile lifecycle
|
// event tile lifecycle
|
||||||
$event-encrypting-color: #abddbc;
|
$event-encrypting-color: #abddbc;
|
||||||
$event-sending-color: #ddd;
|
$event-sending-color: #ddd;
|
||||||
$event-notsent-color: #f44;
|
$event-notsent-color: #f44;
|
||||||
|
|
||||||
|
$event-highlight-fg-color: $warning-color;
|
||||||
|
$event-highlight-bg-color: $yellow-background;
|
||||||
|
|
||||||
// event redaction
|
// event redaction
|
||||||
$event-redacted-fg-color: #e2e2e2;
|
$event-redacted-fg-color: #e2e2e2;
|
||||||
$event-redacted-border-color: #cccccc;
|
$event-redacted-border-color: #cccccc;
|
||||||
|
@ -244,7 +252,7 @@ $togglesw-ball-color: #fff;
|
||||||
|
|
||||||
$progressbar-color: #000;
|
$progressbar-color: #000;
|
||||||
|
|
||||||
$room-warning-bg-color: #fff8e3;
|
$room-warning-bg-color: $yellow-background;
|
||||||
|
|
||||||
$memberstatus-placeholder-color: $roomtile-name-color;
|
$memberstatus-placeholder-color: $roomtile-name-color;
|
||||||
|
|
||||||
|
|
|
@ -214,6 +214,9 @@ module.exports = React.createClass({
|
||||||
// after an update to the contents of the panel, check that the scroll is
|
// after an update to the contents of the panel, check that the scroll is
|
||||||
// where it ought to be, and set off pagination requests if necessary.
|
// where it ought to be, and set off pagination requests if necessary.
|
||||||
checkScroll: function() {
|
checkScroll: function() {
|
||||||
|
if (this.unmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this._restoreSavedScrollState();
|
this._restoreSavedScrollState();
|
||||||
this.checkFillState();
|
this.checkFillState();
|
||||||
},
|
},
|
||||||
|
|
|
@ -29,6 +29,10 @@ import SettingsStore from '../../../settings/SettingsStore';
|
||||||
import { isUrlPermitted } from '../../../HtmlUtils';
|
import { isUrlPermitted } from '../../../HtmlUtils';
|
||||||
import { isContentActionable } from '../../../utils/EventUtils';
|
import { isContentActionable } from '../../../utils/EventUtils';
|
||||||
|
|
||||||
|
function canCancel(eventStatus) {
|
||||||
|
return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = React.createClass({
|
module.exports = React.createClass({
|
||||||
displayName: 'MessageContextMenu',
|
displayName: 'MessageContextMenu',
|
||||||
|
|
||||||
|
@ -90,6 +94,23 @@ module.exports = React.createClass({
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onResendEditClick: function() {
|
||||||
|
Resend.resend(this.props.mxEvent.replacingEvent());
|
||||||
|
this.closeMenu();
|
||||||
|
},
|
||||||
|
|
||||||
|
onResendRedactionClick: function() {
|
||||||
|
Resend.resend(this.props.mxEvent.localRedactionEvent());
|
||||||
|
this.closeMenu();
|
||||||
|
},
|
||||||
|
|
||||||
|
onResendReactionsClick: function() {
|
||||||
|
for (const reaction of this._getUnsentReactions()) {
|
||||||
|
Resend.resend(reaction);
|
||||||
|
}
|
||||||
|
this.closeMenu();
|
||||||
|
},
|
||||||
|
|
||||||
e2eInfoClicked: function() {
|
e2eInfoClicked: function() {
|
||||||
this.props.e2eInfoCallback();
|
this.props.e2eInfoCallback();
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
|
@ -148,7 +169,25 @@ module.exports = React.createClass({
|
||||||
},
|
},
|
||||||
|
|
||||||
onCancelSendClick: function() {
|
onCancelSendClick: function() {
|
||||||
Resend.removeFromQueue(this.props.mxEvent);
|
const mxEvent = this.props.mxEvent;
|
||||||
|
const editEvent = mxEvent.replacingEvent();
|
||||||
|
const redactEvent = mxEvent.localRedactionEvent();
|
||||||
|
const pendingReactions = this._getPendingReactions();
|
||||||
|
|
||||||
|
if (editEvent && canCancel(editEvent.status)) {
|
||||||
|
Resend.removeFromQueue(editEvent);
|
||||||
|
}
|
||||||
|
if (redactEvent && canCancel(redactEvent.status)) {
|
||||||
|
Resend.removeFromQueue(redactEvent);
|
||||||
|
}
|
||||||
|
if (pendingReactions.length) {
|
||||||
|
for (const reaction of pendingReactions) {
|
||||||
|
Resend.removeFromQueue(reaction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (canCancel(mxEvent.status)) {
|
||||||
|
Resend.removeFromQueue(this.props.mxEvent);
|
||||||
|
}
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -217,10 +256,42 @@ module.exports = React.createClass({
|
||||||
this.closeMenu();
|
this.closeMenu();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getReactions(filter) {
|
||||||
|
const cli = MatrixClientPeg.get();
|
||||||
|
const room = cli.getRoom(this.props.mxEvent.getRoomId());
|
||||||
|
const eventId = this.props.mxEvent.getId();
|
||||||
|
return room.getPendingEvents().filter(e => {
|
||||||
|
const relation = e.getRelation();
|
||||||
|
return relation &&
|
||||||
|
relation.rel_type === "m.annotation" &&
|
||||||
|
relation.event_id === eventId &&
|
||||||
|
filter(e);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
_getPendingReactions() {
|
||||||
|
return this._getReactions(e => canCancel(e.status));
|
||||||
|
},
|
||||||
|
|
||||||
|
_getUnsentReactions() {
|
||||||
|
return this._getReactions(e => e.status === EventStatus.NOT_SENT);
|
||||||
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
const mxEvent = this.props.mxEvent;
|
const mxEvent = this.props.mxEvent;
|
||||||
const eventStatus = mxEvent.status;
|
const eventStatus = mxEvent.status;
|
||||||
|
const editStatus = mxEvent.replacingEvent() && mxEvent.replacingEvent().status;
|
||||||
|
const redactStatus = mxEvent.localRedactionEvent() && mxEvent.localRedactionEvent().status;
|
||||||
|
const unsentReactionsCount = this._getUnsentReactions().length;
|
||||||
|
const pendingReactionsCount = this._getPendingReactions().length;
|
||||||
|
const allowCancel = canCancel(mxEvent.status) ||
|
||||||
|
canCancel(editStatus) ||
|
||||||
|
canCancel(redactStatus) ||
|
||||||
|
pendingReactionsCount !== 0;
|
||||||
let resendButton;
|
let resendButton;
|
||||||
|
let resendEditButton;
|
||||||
|
let resendReactionsButton;
|
||||||
|
let resendRedactionButton;
|
||||||
let redactButton;
|
let redactButton;
|
||||||
let cancelButton;
|
let cancelButton;
|
||||||
let forwardButton;
|
let forwardButton;
|
||||||
|
@ -233,11 +304,36 @@ module.exports = React.createClass({
|
||||||
|
|
||||||
// status is SENT before remote-echo, null after
|
// status is SENT before remote-echo, null after
|
||||||
const isSent = !eventStatus || eventStatus === EventStatus.SENT;
|
const isSent = !eventStatus || eventStatus === EventStatus.SENT;
|
||||||
|
if (!mxEvent.isRedacted()) {
|
||||||
|
if (eventStatus === EventStatus.NOT_SENT) {
|
||||||
|
resendButton = (
|
||||||
|
<div className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
||||||
|
{ _t('Resend') }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (eventStatus === EventStatus.NOT_SENT) {
|
if (editStatus === EventStatus.NOT_SENT) {
|
||||||
resendButton = (
|
resendEditButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onResendClick}>
|
<div className="mx_MessageContextMenu_field" onClick={this.onResendEditClick}>
|
||||||
{ _t('Resend') }
|
{ _t('Resend edit') }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unsentReactionsCount !== 0) {
|
||||||
|
resendReactionsButton = (
|
||||||
|
<div className="mx_MessageContextMenu_field" onClick={this.onResendReactionsClick}>
|
||||||
|
{ _t('Resend %(unsentCount)s reaction(s)', {unsentCount: unsentReactionsCount}) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redactStatus === EventStatus.NOT_SENT) {
|
||||||
|
resendRedactionButton = (
|
||||||
|
<div className="mx_MessageContextMenu_field" onClick={this.onResendRedactionClick}>
|
||||||
|
{ _t('Resend removal') }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -250,7 +346,7 @@ module.exports = React.createClass({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT) {
|
if (allowCancel) {
|
||||||
cancelButton = (
|
cancelButton = (
|
||||||
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
<div className="mx_MessageContextMenu_field" onClick={this.onCancelSendClick}>
|
||||||
{ _t('Cancel Sending') }
|
{ _t('Cancel Sending') }
|
||||||
|
@ -352,6 +448,9 @@ module.exports = React.createClass({
|
||||||
return (
|
return (
|
||||||
<div className="mx_MessageContextMenu">
|
<div className="mx_MessageContextMenu">
|
||||||
{ resendButton }
|
{ resendButton }
|
||||||
|
{ resendEditButton }
|
||||||
|
{ resendReactionsButton }
|
||||||
|
{ resendRedactionButton }
|
||||||
{ redactButton }
|
{ redactButton }
|
||||||
{ cancelButton }
|
{ cancelButton }
|
||||||
{ forwardButton }
|
{ forwardButton }
|
||||||
|
|
|
@ -31,6 +31,7 @@ import {renderModel} from '../../../editor/render';
|
||||||
import EditorStateTransfer from '../../../utils/EditorStateTransfer';
|
import EditorStateTransfer from '../../../utils/EditorStateTransfer';
|
||||||
import {MatrixClient} from 'matrix-js-sdk';
|
import {MatrixClient} from 'matrix-js-sdk';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import {EventStatus} from 'matrix-js-sdk';
|
||||||
|
|
||||||
export default class MessageEditor extends React.Component {
|
export default class MessageEditor extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -195,12 +196,24 @@ export default class MessageEditor extends React.Component {
|
||||||
}, contentBody);
|
}, contentBody);
|
||||||
|
|
||||||
const roomId = this.props.editState.getEvent().getRoomId();
|
const roomId = this.props.editState.getEvent().getRoomId();
|
||||||
|
this._cancelPreviousPendingEdit();
|
||||||
this.context.matrixClient.sendMessage(roomId, content);
|
this.context.matrixClient.sendMessage(roomId, content);
|
||||||
|
|
||||||
dis.dispatch({action: "edit_event", event: null});
|
dis.dispatch({action: "edit_event", event: null});
|
||||||
dis.dispatch({action: 'focus_composer'});
|
dis.dispatch({action: 'focus_composer'});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_cancelPreviousPendingEdit() {
|
||||||
|
const originalEvent = this.props.editState.getEvent();
|
||||||
|
const previousEdit = originalEvent.replacingEvent();
|
||||||
|
if (previousEdit && (
|
||||||
|
previousEdit.status === EventStatus.QUEUED ||
|
||||||
|
previousEdit.status === EventStatus.NOT_SENT
|
||||||
|
)) {
|
||||||
|
this.context.matrixClient.cancelPendingEvent(previousEdit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_onAutoCompleteConfirm = (completion) => {
|
_onAutoCompleteConfirm = (completion) => {
|
||||||
this.model.autoComplete.onComponentConfirm(completion);
|
this.model.autoComplete.onComponentConfirm(completion);
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,7 +681,7 @@ module.exports = withMatrixClient(React.createClass({
|
||||||
</div> : null;
|
</div> : null;
|
||||||
|
|
||||||
let reactionsRow;
|
let reactionsRow;
|
||||||
if (SettingsStore.isFeatureEnabled("feature_reactions")) {
|
if (SettingsStore.isFeatureEnabled("feature_reactions") && !isRedacted) {
|
||||||
const ReactionsRow = sdk.getComponent('messages.ReactionsRow');
|
const ReactionsRow = sdk.getComponent('messages.ReactionsRow');
|
||||||
reactionsRow = <ReactionsRow
|
reactionsRow = <ReactionsRow
|
||||||
mxEvent={this.props.mxEvent}
|
mxEvent={this.props.mxEvent}
|
||||||
|
|
|
@ -1307,6 +1307,9 @@
|
||||||
"Unable to reject invite": "Unable to reject invite",
|
"Unable to reject invite": "Unable to reject invite",
|
||||||
"You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)",
|
"You cannot delete this message. (%(code)s)": "You cannot delete this message. (%(code)s)",
|
||||||
"Resend": "Resend",
|
"Resend": "Resend",
|
||||||
|
"Resend edit": "Resend edit",
|
||||||
|
"Resend %(unsentCount)s reaction(s)": "Resend %(unsentCount)s reaction(s)",
|
||||||
|
"Resend removal": "Resend removal",
|
||||||
"Cancel Sending": "Cancel Sending",
|
"Cancel Sending": "Cancel Sending",
|
||||||
"Forward Message": "Forward Message",
|
"Forward Message": "Forward Message",
|
||||||
"Pin Message": "Pin Message",
|
"Pin Message": "Pin Message",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue