Merge branch 'develop' into revert-6752-revert-6682-gsouquet/compact-composer-18533
This commit is contained in:
commit
2d45bfb976
22 changed files with 954 additions and 331 deletions
|
@ -275,8 +275,8 @@ export default class ScrollPanel extends React.Component<IProps> {
|
|||
// fractional values (both too big and too small)
|
||||
// for scrollTop happen on certain browsers/platforms
|
||||
// when scrolled all the way down. E.g. Chrome 72 on debian.
|
||||
// so check difference <= 1;
|
||||
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1;
|
||||
// so check difference < 1;
|
||||
return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) < 1;
|
||||
};
|
||||
|
||||
// returns the vertical height in the given direction that can be removed from
|
||||
|
|
|
@ -89,7 +89,7 @@ interface IProps {
|
|||
|
||||
interface IState {
|
||||
phase: Phase;
|
||||
createdRooms?: boolean; // internal state for the creation wizard
|
||||
firstRoomId?: string; // internal state for the creation wizard
|
||||
showRightPanel: boolean;
|
||||
myMembership: string;
|
||||
}
|
||||
|
@ -508,7 +508,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
|||
try {
|
||||
const isPublic = space.getJoinRule() === JoinRule.Public;
|
||||
const filteredRoomNames = roomNames.map(name => name.trim()).filter(Boolean);
|
||||
await Promise.all(filteredRoomNames.map(name => {
|
||||
const roomIds = await Promise.all(filteredRoomNames.map(name => {
|
||||
return createRoom({
|
||||
createOpts: {
|
||||
preset: isPublic ? Preset.PublicChat : Preset.PrivateChat,
|
||||
|
@ -523,7 +523,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
|||
suggested: true,
|
||||
});
|
||||
}));
|
||||
onFinished(filteredRoomNames.length > 0);
|
||||
onFinished(roomIds[0]);
|
||||
} catch (e) {
|
||||
console.error("Failed to create initial space rooms", e);
|
||||
setError(_t("Failed to create initial space rooms"));
|
||||
|
@ -533,7 +533,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => {
|
|||
|
||||
let onClick = (ev) => {
|
||||
ev.preventDefault();
|
||||
onFinished(false);
|
||||
onFinished();
|
||||
};
|
||||
let buttonLabel = _t("Skip for now");
|
||||
if (roomNames.some(name => name.trim())) {
|
||||
|
@ -588,7 +588,11 @@ const SpaceAddExistingRooms = ({ space, onFinished }) => {
|
|||
</div>;
|
||||
};
|
||||
|
||||
const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, createdRooms }) => {
|
||||
interface ISpaceSetupPublicShareProps extends Pick<IProps & IState, "justCreatedOpts" | "space" | "firstRoomId"> {
|
||||
onFinished(): void;
|
||||
}
|
||||
|
||||
const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, firstRoomId }: ISpaceSetupPublicShareProps) => {
|
||||
return <div className="mx_SpaceRoomView_publicShare">
|
||||
<h1>{ _t("Share %(name)s", {
|
||||
name: justCreatedOpts?.createOpts?.name || space.name,
|
||||
|
@ -601,7 +605,7 @@ const SpaceSetupPublicShare = ({ justCreatedOpts, space, onFinished, createdRoom
|
|||
|
||||
<div className="mx_SpaceRoomView_buttons">
|
||||
<AccessibleButton kind="primary" onClick={onFinished}>
|
||||
{ createdRooms ? _t("Go to my first room") : _t("Go to my space") }
|
||||
{ firstRoomId ? _t("Go to my first room") : _t("Go to my space") }
|
||||
</AccessibleButton>
|
||||
</div>
|
||||
</div>;
|
||||
|
@ -844,35 +848,10 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||
};
|
||||
|
||||
private goToFirstRoom = async () => {
|
||||
// TODO actually go to the first room
|
||||
|
||||
const childRooms = SpaceStore.instance.getChildRooms(this.props.space.roomId);
|
||||
if (childRooms.length) {
|
||||
const room = childRooms[0];
|
||||
if (this.state.firstRoomId) {
|
||||
defaultDispatcher.dispatch({
|
||||
action: "view_room",
|
||||
room_id: room.roomId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let suggestedRooms = SpaceStore.instance.suggestedRooms;
|
||||
if (SpaceStore.instance.activeSpace !== this.props.space) {
|
||||
// the space store has the suggested rooms loaded for a different space, fetch the right ones
|
||||
suggestedRooms = (await SpaceStore.instance.fetchSuggestedRooms(this.props.space, 1));
|
||||
}
|
||||
|
||||
if (suggestedRooms.length) {
|
||||
const room = suggestedRooms[0];
|
||||
defaultDispatcher.dispatch({
|
||||
action: "view_room",
|
||||
room_id: room.room_id,
|
||||
room_alias: room.canonical_alias || room.aliases?.[0],
|
||||
via_servers: room.viaServers,
|
||||
oobData: {
|
||||
avatarUrl: room.avatar_url,
|
||||
name: room.name || room.canonical_alias || room.aliases?.[0] || _t("Empty room"),
|
||||
},
|
||||
room_id: this.state.firstRoomId,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
@ -902,14 +881,14 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||
_t("Let's create a room for each of them.") + "\n" +
|
||||
_t("You can add more later too, including already existing ones.")
|
||||
}
|
||||
onFinished={(createdRooms: boolean) => this.setState({ phase: Phase.PublicShare, createdRooms })}
|
||||
onFinished={(firstRoomId: string) => this.setState({ phase: Phase.PublicShare, firstRoomId })}
|
||||
/>;
|
||||
case Phase.PublicShare:
|
||||
return <SpaceSetupPublicShare
|
||||
justCreatedOpts={this.props.justCreatedOpts}
|
||||
space={this.props.space}
|
||||
onFinished={this.goToFirstRoom}
|
||||
createdRooms={this.state.createdRooms}
|
||||
firstRoomId={this.state.firstRoomId}
|
||||
/>;
|
||||
|
||||
case Phase.PrivateScope:
|
||||
|
@ -931,7 +910,7 @@ export default class SpaceRoomView extends React.PureComponent<IProps, IState> {
|
|||
title={_t("What projects are you working on?")}
|
||||
description={_t("We'll create rooms for each of them. " +
|
||||
"You can add more later too, including already existing ones.")}
|
||||
onFinished={(createdRooms: boolean) => this.setState({ phase: Phase.Landing, createdRooms })}
|
||||
onFinished={(firstRoomId: string) => this.setState({ phase: Phase.Landing, firstRoomId })}
|
||||
/>;
|
||||
case Phase.PrivateExistingRooms:
|
||||
return <SpaceAddExistingRooms
|
||||
|
|
|
@ -32,6 +32,7 @@ import dis from "../../dispatcher/dispatcher";
|
|||
import { ActionPayload } from '../../dispatcher/payloads';
|
||||
import { SetRightPanelPhasePayload } from '../../dispatcher/payloads/SetRightPanelPhasePayload';
|
||||
import { Action } from '../../dispatcher/actions';
|
||||
import { MatrixClientPeg } from '../../MatrixClientPeg';
|
||||
|
||||
interface IProps {
|
||||
room: Room;
|
||||
|
@ -89,12 +90,15 @@ export default class ThreadView extends React.Component<IProps, IState> {
|
|||
};
|
||||
|
||||
private setupThread = (mxEv: MatrixEvent) => {
|
||||
const thread = mxEv.getThread();
|
||||
if (thread) {
|
||||
thread.on("Thread.update", this.updateThread);
|
||||
thread.once("Thread.ready", this.updateThread);
|
||||
this.updateThread(thread);
|
||||
let thread = mxEv.getThread();
|
||||
if (!thread) {
|
||||
const client = MatrixClientPeg.get();
|
||||
thread = new Thread([mxEv], this.props.room, client);
|
||||
mxEv.setThread(thread);
|
||||
}
|
||||
thread.on("Thread.update", this.updateThread);
|
||||
thread.once("Thread.ready", this.updateThread);
|
||||
this.updateThread(thread);
|
||||
};
|
||||
|
||||
private teardownThread = () => {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
Copyright 2019 Travis Ralston
|
||||
Copyright 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -15,42 +16,46 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { _t } from "../../../languageHandler";
|
||||
import * as sdk from "../../../index";
|
||||
import LabelledToggleSwitch from "../elements/LabelledToggleSwitch";
|
||||
import { Widget } from "matrix-widget-api";
|
||||
import { Widget, WidgetKind } from "matrix-widget-api";
|
||||
import { OIDCState, WidgetPermissionStore } from "../../../stores/widgets/WidgetPermissionStore";
|
||||
import { replaceableComponent } from "../../../utils/replaceableComponent";
|
||||
import { IDialogProps } from "./IDialogProps";
|
||||
import BaseDialog from "./BaseDialog";
|
||||
import DialogButtons from "../elements/DialogButtons";
|
||||
|
||||
interface IProps extends IDialogProps {
|
||||
widget: Widget;
|
||||
widgetKind: WidgetKind;
|
||||
inRoomId?: string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
rememberSelection: boolean;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.dialogs.WidgetOpenIDPermissionsDialog")
|
||||
export default class WidgetOpenIDPermissionsDialog extends React.Component {
|
||||
static propTypes = {
|
||||
onFinished: PropTypes.func.isRequired,
|
||||
widget: PropTypes.objectOf(Widget).isRequired,
|
||||
widgetKind: PropTypes.string.isRequired, // WidgetKind from widget-api
|
||||
inRoomId: PropTypes.string,
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
export default class WidgetOpenIDPermissionsDialog extends React.PureComponent<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rememberSelection: false,
|
||||
};
|
||||
}
|
||||
|
||||
_onAllow = () => {
|
||||
this._onPermissionSelection(true);
|
||||
private onAllow = () => {
|
||||
this.onPermissionSelection(true);
|
||||
};
|
||||
|
||||
_onDeny = () => {
|
||||
this._onPermissionSelection(false);
|
||||
private onDeny = () => {
|
||||
this.onPermissionSelection(false);
|
||||
};
|
||||
|
||||
_onPermissionSelection(allowed) {
|
||||
private onPermissionSelection(allowed: boolean) {
|
||||
if (this.state.rememberSelection) {
|
||||
console.log(`Remembering ${this.props.widgetId} as allowed=${allowed} for OpenID`);
|
||||
console.log(`Remembering ${this.props.widget.id} as allowed=${allowed} for OpenID`);
|
||||
|
||||
WidgetPermissionStore.instance.setOIDCState(
|
||||
this.props.widget, this.props.widgetKind, this.props.inRoomId,
|
||||
|
@ -61,14 +66,11 @@ export default class WidgetOpenIDPermissionsDialog extends React.Component {
|
|||
this.props.onFinished(allowed);
|
||||
}
|
||||
|
||||
_onRememberSelectionChange = (newVal) => {
|
||||
private onRememberSelectionChange = (newVal: boolean) => {
|
||||
this.setState({ rememberSelection: newVal });
|
||||
};
|
||||
|
||||
render() {
|
||||
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
|
||||
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<BaseDialog
|
||||
className='mx_WidgetOpenIDPermissionsDialog'
|
||||
|
@ -87,13 +89,13 @@ export default class WidgetOpenIDPermissionsDialog extends React.Component {
|
|||
</div>
|
||||
<DialogButtons
|
||||
primaryButton={_t("Continue")}
|
||||
onPrimaryButtonClick={this._onAllow}
|
||||
onCancel={this._onDeny}
|
||||
onPrimaryButtonClick={this.onAllow}
|
||||
onCancel={this.onDeny}
|
||||
additive={
|
||||
<LabelledToggleSwitch
|
||||
value={this.state.rememberSelection}
|
||||
toggleInFront={true}
|
||||
onChange={this._onRememberSelectionChange}
|
||||
onChange={this.onRememberSelectionChange}
|
||||
label={_t("Remember this")} />}
|
||||
/>
|
||||
</BaseDialog>
|
|
@ -17,8 +17,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { EventStatus } from 'matrix-js-sdk/src/models/event';
|
||||
import { MatrixEvent, EventStatus } from 'matrix-js-sdk/src/models/event';
|
||||
|
||||
import { _t } from '../../../languageHandler';
|
||||
import * as sdk from '../../../index';
|
||||
|
@ -37,48 +36,65 @@ import { MatrixClientPeg } from "../../../MatrixClientPeg";
|
|||
import { MediaEventHelper } from "../../../utils/MediaEventHelper";
|
||||
import DownloadActionButton from "./DownloadActionButton";
|
||||
import SettingsStore from '../../../settings/SettingsStore';
|
||||
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||
import ReplyThread from '../elements/ReplyThread';
|
||||
|
||||
const OptionsButton = ({ mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange }) => {
|
||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||
const [onFocus, isActive, ref] = useRovingTabIndex(button);
|
||||
useEffect(() => {
|
||||
onFocusChange(menuDisplayed);
|
||||
}, [onFocusChange, menuDisplayed]);
|
||||
interface IOptionsButtonProps {
|
||||
mxEvent: MatrixEvent;
|
||||
getTile: () => any; // TODO: FIXME, haven't figured out what the return type is here
|
||||
getReplyThread: () => ReplyThread;
|
||||
permalinkCreator: RoomPermalinkCreator;
|
||||
onFocusChange: (menuDisplayed: boolean) => void;
|
||||
}
|
||||
|
||||
let contextMenu;
|
||||
if (menuDisplayed) {
|
||||
const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
|
||||
const OptionsButton: React.FC<IOptionsButtonProps> =
|
||||
({ mxEvent, getTile, getReplyThread, permalinkCreator, onFocusChange }) => {
|
||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||
const [onFocus, isActive, ref] = useRovingTabIndex(button);
|
||||
useEffect(() => {
|
||||
onFocusChange(menuDisplayed);
|
||||
}, [onFocusChange, menuDisplayed]);
|
||||
|
||||
const tile = getTile && getTile();
|
||||
const replyThread = getReplyThread && getReplyThread();
|
||||
let contextMenu;
|
||||
if (menuDisplayed) {
|
||||
const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu');
|
||||
|
||||
const buttonRect = button.current.getBoundingClientRect();
|
||||
contextMenu = <MessageContextMenu
|
||||
{...aboveLeftOf(buttonRect)}
|
||||
mxEvent={mxEvent}
|
||||
permalinkCreator={permalinkCreator}
|
||||
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
|
||||
collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
|
||||
onFinished={closeMenu}
|
||||
/>;
|
||||
}
|
||||
const tile = getTile && getTile();
|
||||
const replyThread = getReplyThread && getReplyThread();
|
||||
|
||||
return <React.Fragment>
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton"
|
||||
title={_t("Options")}
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
inputRef={ref}
|
||||
onFocus={onFocus}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
/>
|
||||
const buttonRect = button.current.getBoundingClientRect();
|
||||
contextMenu = <MessageContextMenu
|
||||
{...aboveLeftOf(buttonRect)}
|
||||
mxEvent={mxEvent}
|
||||
permalinkCreator={permalinkCreator}
|
||||
eventTileOps={tile && tile.getEventTileOps ? tile.getEventTileOps() : undefined}
|
||||
collapseReplyThread={replyThread && replyThread.canCollapse() ? replyThread.collapse : undefined}
|
||||
onFinished={closeMenu}
|
||||
/>;
|
||||
}
|
||||
|
||||
{ contextMenu }
|
||||
</React.Fragment>;
|
||||
};
|
||||
return <React.Fragment>
|
||||
<ContextMenuTooltipButton
|
||||
className="mx_MessageActionBar_maskButton mx_MessageActionBar_optionsButton"
|
||||
title={_t("Options")}
|
||||
onClick={openMenu}
|
||||
isExpanded={menuDisplayed}
|
||||
inputRef={ref}
|
||||
onFocus={onFocus}
|
||||
tabIndex={isActive ? 0 : -1}
|
||||
/>
|
||||
|
||||
const ReactButton = ({ mxEvent, reactions, onFocusChange }) => {
|
||||
{ contextMenu }
|
||||
</React.Fragment>;
|
||||
};
|
||||
|
||||
interface IReactButtonProps {
|
||||
mxEvent: MatrixEvent;
|
||||
reactions: any; // TODO: types
|
||||
onFocusChange: (menuDisplayed: boolean) => void;
|
||||
}
|
||||
|
||||
const ReactButton: React.FC<IReactButtonProps> = ({ mxEvent, reactions, onFocusChange }) => {
|
||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||
const [onFocus, isActive, ref] = useRovingTabIndex(button);
|
||||
useEffect(() => {
|
||||
|
@ -109,21 +125,21 @@ const ReactButton = ({ mxEvent, reactions, onFocusChange }) => {
|
|||
</React.Fragment>;
|
||||
};
|
||||
|
||||
interface IMessageActionBarProps {
|
||||
mxEvent: MatrixEvent;
|
||||
// The Relations model from the JS SDK for reactions to `mxEvent`
|
||||
reactions?: any; // TODO: types
|
||||
permalinkCreator?: RoomPermalinkCreator;
|
||||
getTile: () => any; // TODO: FIXME, haven't figured out what the return type is here
|
||||
getReplyThread?: () => ReplyThread;
|
||||
onFocusChange?: (menuDisplayed: boolean) => void;
|
||||
}
|
||||
|
||||
@replaceableComponent("views.messages.MessageActionBar")
|
||||
export default class MessageActionBar extends React.PureComponent {
|
||||
static propTypes = {
|
||||
mxEvent: PropTypes.object.isRequired,
|
||||
// The Relations model from the JS SDK for reactions to `mxEvent`
|
||||
reactions: PropTypes.object,
|
||||
permalinkCreator: PropTypes.object,
|
||||
getTile: PropTypes.func,
|
||||
getReplyThread: PropTypes.func,
|
||||
onFocusChange: PropTypes.func,
|
||||
};
|
||||
export default class MessageActionBar extends React.PureComponent<IMessageActionBarProps> {
|
||||
public static contextType = RoomContext;
|
||||
|
||||
static contextType = RoomContext;
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount(): void {
|
||||
if (this.props.mxEvent.status && this.props.mxEvent.status !== EventStatus.SENT) {
|
||||
this.props.mxEvent.on("Event.status", this.onSent);
|
||||
}
|
||||
|
@ -137,43 +153,43 @@ export default class MessageActionBar extends React.PureComponent {
|
|||
this.props.mxEvent.on("Event.beforeRedaction", this.onBeforeRedaction);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
public componentWillUnmount(): void {
|
||||
this.props.mxEvent.off("Event.status", this.onSent);
|
||||
this.props.mxEvent.off("Event.decrypted", this.onDecrypted);
|
||||
this.props.mxEvent.off("Event.beforeRedaction", this.onBeforeRedaction);
|
||||
}
|
||||
|
||||
onDecrypted = () => {
|
||||
private onDecrypted = (): void => {
|
||||
// When an event decrypts, it is likely to change the set of available
|
||||
// actions, so we force an update to check again.
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
onBeforeRedaction = () => {
|
||||
private onBeforeRedaction = (): void => {
|
||||
// When an event is redacted, we can't edit it so update the available actions.
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
onSent = () => {
|
||||
private onSent = (): void => {
|
||||
// When an event is sent and echoed the possible actions change.
|
||||
this.forceUpdate();
|
||||
};
|
||||
|
||||
onFocusChange = (focused) => {
|
||||
private onFocusChange = (focused: boolean): void => {
|
||||
if (!this.props.onFocusChange) {
|
||||
return;
|
||||
}
|
||||
this.props.onFocusChange(focused);
|
||||
};
|
||||
|
||||
onReplyClick = (ev) => {
|
||||
private onReplyClick = (ev: React.MouseEvent): void => {
|
||||
dis.dispatch({
|
||||
action: 'reply_to_event',
|
||||
event: this.props.mxEvent,
|
||||
});
|
||||
};
|
||||
|
||||
onThreadClick = () => {
|
||||
private onThreadClick = (): void => {
|
||||
dis.dispatch({
|
||||
action: Action.SetRightPanelPhase,
|
||||
phase: RightPanelPhases.ThreadView,
|
||||
|
@ -182,9 +198,9 @@ export default class MessageActionBar extends React.PureComponent {
|
|||
event: this.props.mxEvent,
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
onEditClick = (ev) => {
|
||||
private onEditClick = (ev: React.MouseEvent): void => {
|
||||
dis.dispatch({
|
||||
action: 'edit_event',
|
||||
event: this.props.mxEvent,
|
||||
|
@ -200,7 +216,7 @@ export default class MessageActionBar extends React.PureComponent {
|
|||
* @param {Function} fn The execution function.
|
||||
* @param {Function} checkFn The test function.
|
||||
*/
|
||||
runActionOnFailedEv(fn, checkFn) {
|
||||
private runActionOnFailedEv(fn: (ev: MatrixEvent) => void, checkFn?: (ev: MatrixEvent) => boolean): void {
|
||||
if (!checkFn) checkFn = () => true;
|
||||
|
||||
const mxEvent = this.props.mxEvent;
|
||||
|
@ -215,18 +231,18 @@ export default class MessageActionBar extends React.PureComponent {
|
|||
}
|
||||
}
|
||||
|
||||
onResendClick = (ev) => {
|
||||
private onResendClick = (ev: React.MouseEvent): void => {
|
||||
this.runActionOnFailedEv((tarEv) => Resend.resend(tarEv));
|
||||
};
|
||||
|
||||
onCancelClick = (ev) => {
|
||||
private onCancelClick = (ev: React.MouseEvent): void => {
|
||||
this.runActionOnFailedEv(
|
||||
(tarEv) => Resend.removeFromQueue(tarEv),
|
||||
(testEv) => canCancel(testEv.status),
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
public render(): JSX.Element {
|
||||
const toolbarOpts = [];
|
||||
if (canEditContent(this.props.mxEvent)) {
|
||||
toolbarOpts.push(<RovingAccessibleTooltipButton
|
||||
|
@ -249,7 +265,7 @@ export default class MessageActionBar extends React.PureComponent {
|
|||
const editStatus = mxEvent.replacingEvent() && mxEvent.replacingEvent().status;
|
||||
const redactStatus = mxEvent.localRedactionEvent() && mxEvent.localRedactionEvent().status;
|
||||
const allowCancel = canCancel(mxEvent.status) || canCancel(editStatus) || canCancel(redactStatus);
|
||||
const isFailed = [mxEvent.status, editStatus, redactStatus].includes("not_sent");
|
||||
const isFailed = [mxEvent.status, editStatus, redactStatus].includes(EventStatus.NOT_SENT);
|
||||
if (allowCancel && isFailed) {
|
||||
// The resend button needs to appear ahead of the edit button, so insert to the
|
||||
// start of the opts
|
|
@ -72,8 +72,10 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
private getVersionInfo(): { appVersion: string, olmVersion: string } {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const appVersion = this.state.appVersion || 'unknown';
|
||||
let olmVersion = MatrixClientPeg.get().olmVersion;
|
||||
olmVersion = olmVersion ? `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}` : '<not-enabled>';
|
||||
const olmVersionTuple = MatrixClientPeg.get().olmVersion;
|
||||
const olmVersion = olmVersionTuple
|
||||
? `${olmVersionTuple[0]}.${olmVersionTuple[1]}.${olmVersionTuple[2]}`
|
||||
: '<not-enabled>';
|
||||
|
||||
return {
|
||||
appVersion: `${_t("%(brand)s version:", { brand })} ${appVersion}`,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue