Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into room-preview-spaces

This commit is contained in:
Jaiwanth 2021-05-03 21:53:54 +05:30
commit cf9edb1884
97 changed files with 819 additions and 568 deletions

View file

@ -15,7 +15,6 @@ module.exports = {
"prefer-promise-reject-errors": "off", "prefer-promise-reject-errors": "off",
"no-async-promise-executor": "off", "no-async-promise-executor": "off",
"quotes": "off", "quotes": "off",
"indent": "off",
}, },
overrides: [{ overrides: [{

View file

@ -34,6 +34,10 @@ limitations under the License.
border-color: $reaction-row-button-selected-border-color; border-color: $reaction-row-button-selected-border-color;
} }
&.mx_AccessibleButton_disabled {
cursor: not-allowed;
}
.mx_ReactionsRowButton_content { .mx_ReactionsRowButton_content {
max-width: 100px; max-width: 100px;
overflow: hidden; overflow: hidden;

View file

@ -148,13 +148,15 @@ function _onGroupAddRoomFinished(groupId, addrs, addRoomsPublicly) {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Failed to add the following room to the group', 'Failed to add the following room to the group',
'', ErrorDialog, '',
ErrorDialog,
{ {
title: _t( title: _t(
"Failed to add the following rooms to %(groupId)s:", "Failed to add the following rooms to %(groupId)s:",
{groupId}, {groupId},
), ),
description: errorList.join(", "), description: errorList.join(", "),
}); },
);
}); });
} }

View file

@ -547,17 +547,23 @@ function textForMjolnirEvent(event) {
// else the entity !== prevEntity - count as a removal & add // else the entity !== prevEntity - count as a removal & add
if (USER_RULE_TYPES.includes(event.getType())) { if (USER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " + return _t(
"%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s", "%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason}); {senderName, oldGlob: prevEntity, newGlob: entity, reason},
);
} else if (ROOM_RULE_TYPES.includes(event.getType())) { } else if (ROOM_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " + return _t(
"%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s", "%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason}); {senderName, oldGlob: prevEntity, newGlob: entity, reason},
);
} else if (SERVER_RULE_TYPES.includes(event.getType())) { } else if (SERVER_RULE_TYPES.includes(event.getType())) {
return _t("%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " + return _t(
"%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " +
"%(newGlob)s for %(reason)s", "%(newGlob)s for %(reason)s",
{senderName, oldGlob: prevEntity, newGlob: entity, reason}); {senderName, oldGlob: prevEntity, newGlob: entity, reason},
);
} }
// Unknown type. We'll say something but we shouldn't end up here. // Unknown type. We'll say something but we shouldn't end up here.

View file

@ -170,7 +170,10 @@ export default class ExportE2eKeysDialog extends React.Component {
</div> </div>
</div> </div>
<div className='mx_Dialog_buttons'> <div className='mx_Dialog_buttons'>
<input className='mx_Dialog_primary' type='submit' value={_t('Export')} <input
className='mx_Dialog_primary'
type='submit'
value={_t('Export')}
disabled={disableForm} disabled={disableForm}
/> />
<button onClick={this._onCancelClick} disabled={disableForm}> <button onClick={this._onCancelClick} disabled={disableForm}>

View file

@ -110,14 +110,16 @@ class CategoryRoomList extends React.Component {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Failed to add the following room to the group summary', 'Failed to add the following room to the group summary',
'', ErrorDialog, '',
ErrorDialog,
{ {
title: _t( title: _t(
"Failed to add the following rooms to the summary of %(groupId)s:", "Failed to add the following rooms to the summary of %(groupId)s:",
{groupId: this.props.groupId}, {groupId: this.props.groupId},
), ),
description: errorList.join(", "), description: errorList.join(", "),
}); },
);
}); });
}, },
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
@ -196,7 +198,8 @@ class FeaturedRoom extends React.Component {
{groupId: this.props.groupId}, {groupId: this.props.groupId},
), ),
description: _t("The room '%(roomName)s' could not be removed from the summary.", {roomName}), description: _t("The room '%(roomName)s' could not be removed from the summary.", {roomName}),
}); },
);
}); });
}; };
@ -289,7 +292,8 @@ class RoleUserList extends React.Component {
{groupId: this.props.groupId}, {groupId: this.props.groupId},
), ),
description: errorList.join(", "), description: errorList.join(", "),
}); },
);
}); });
}, },
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
@ -352,14 +356,16 @@ class FeaturedUser extends React.Component {
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog"); const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
Modal.createTrackedDialog( Modal.createTrackedDialog(
'Failed to remove user from community summary', 'Failed to remove user from community summary',
'', ErrorDialog, '',
ErrorDialog,
{ {
title: _t( title: _t(
"Failed to remove a user from the summary of %(groupId)s", "Failed to remove a user from the summary of %(groupId)s",
{groupId: this.props.groupId}, {groupId: this.props.groupId},
), ),
description: _t("The user '%(displayName)s' could not be removed from the summary.", {displayName}), description: _t("The user '%(displayName)s' could not be removed from the summary.", {displayName}),
}); },
);
}); });
}; };
@ -1055,7 +1061,8 @@ export default class GroupView extends React.Component {
return null; return null;
} }
const membershipButtonClasses = classnames([ const membershipButtonClasses = classnames(
[
'mx_RoomHeader_textButton', 'mx_RoomHeader_textButton',
'mx_GroupView_textButton', 'mx_GroupView_textButton',
], ],

View file

@ -347,7 +347,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
if (element) { if (element) {
classes = element.classList; classes = element.classList;
} }
} while (element && !cssClasses.some(c => classes.contains(c))); } while (element && (!cssClasses.some(c => classes.contains(c)) || element.offsetParent === null));
if (element) { if (element) {
element.focus(); element.focus();

View file

@ -427,8 +427,10 @@ export default class MessagePanel extends React.Component {
// we get a new DOM node (restarting the animation) when the ghost // we get a new DOM node (restarting the animation) when the ghost
// moves to a different event. // moves to a different event.
return ( return (
<li key={"_readuptoghost_"+eventId} <li
className="mx_RoomView_myReadMarker_container"> key={"_readuptoghost_"+eventId}
className="mx_RoomView_myReadMarker_container"
>
{ hr } { hr }
</li> </li>
); );

View file

@ -17,6 +17,8 @@ limitations under the License.
import * as React from "react"; import * as React from "react";
import { createRef } from "react"; import { createRef } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { Room } from "matrix-js-sdk/src/models/room";
import defaultDispatcher from "../../dispatcher/dispatcher"; import defaultDispatcher from "../../dispatcher/dispatcher";
import { _t } from "../../languageHandler"; import { _t } from "../../languageHandler";
import { ActionPayload } from "../../dispatcher/payloads"; import { ActionPayload } from "../../dispatcher/payloads";
@ -26,7 +28,7 @@ import RoomListStore from "../../stores/room-list/RoomListStore";
import { NameFilterCondition } from "../../stores/room-list/filters/NameFilterCondition"; import { NameFilterCondition } from "../../stores/room-list/filters/NameFilterCondition";
import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager"; import { getKeyBindingsManager, RoomListAction } from "../../KeyBindingsManager";
import {replaceableComponent} from "../../utils/replaceableComponent"; import {replaceableComponent} from "../../utils/replaceableComponent";
import SpaceStore, {UPDATE_SELECTED_SPACE} from "../../stores/SpaceStore"; import SpaceStore, {UPDATE_SELECTED_SPACE, UPDATE_TOP_LEVEL_SPACES} from "../../stores/SpaceStore";
interface IProps { interface IProps {
isMinimized: boolean; isMinimized: boolean;
@ -40,6 +42,7 @@ interface IProps {
interface IState { interface IState {
query: string; query: string;
focused: boolean; focused: boolean;
inSpaces: boolean;
} }
@replaceableComponent("structures.RoomSearch") @replaceableComponent("structures.RoomSearch")
@ -54,11 +57,13 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
this.state = { this.state = {
query: "", query: "",
focused: false, focused: false,
inSpaces: false,
}; };
this.dispatcherRef = defaultDispatcher.register(this.onAction); this.dispatcherRef = defaultDispatcher.register(this.onAction);
// clear filter when changing spaces, in future we may wish to maintain a filter per-space // clear filter when changing spaces, in future we may wish to maintain a filter per-space
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.clearInput); SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.clearInput);
SpaceStore.instance.on(UPDATE_TOP_LEVEL_SPACES, this.onSpaces);
} }
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>): void { public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>): void {
@ -79,8 +84,15 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
public componentWillUnmount() { public componentWillUnmount() {
defaultDispatcher.unregister(this.dispatcherRef); defaultDispatcher.unregister(this.dispatcherRef);
SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.clearInput); SpaceStore.instance.off(UPDATE_SELECTED_SPACE, this.clearInput);
SpaceStore.instance.off(UPDATE_TOP_LEVEL_SPACES, this.onSpaces);
} }
private onSpaces = (spaces: Room[]) => {
this.setState({
inSpaces: spaces.length > 0,
});
};
private onAction = (payload: ActionPayload) => { private onAction = (payload: ActionPayload) => {
if (payload.action === 'view_room' && payload.clear_search) { if (payload.action === 'view_room' && payload.clear_search) {
this.clearInput(); this.clearInput();
@ -152,6 +164,11 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
'mx_RoomSearch_inputExpanded': this.state.query || this.state.focused, 'mx_RoomSearch_inputExpanded': this.state.query || this.state.focused,
}); });
let placeholder = _t("Filter");
if (this.state.inSpaces) {
placeholder = _t("Filter all spaces");
}
let icon = ( let icon = (
<div className='mx_RoomSearch_icon' /> <div className='mx_RoomSearch_icon' />
); );
@ -165,7 +182,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
onBlur={this.onBlur} onBlur={this.onBlur}
onChange={this.onChange} onChange={this.onChange}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
placeholder={_t("Filter")} placeholder={placeholder}
autoComplete="off" autoComplete="off"
/> />
); );

View file

@ -200,7 +200,8 @@ export default class RoomStatusBar extends React.Component {
} else if (resourceLimitError) { } else if (resourceLimitError) {
title = messageForResourceLimitError( title = messageForResourceLimitError(
resourceLimitError.data.limit_type, resourceLimitError.data.limit_type,
resourceLimitError.data.admin_contact, { resourceLimitError.data.admin_contact,
{
'monthly_active_user': _td( 'monthly_active_user': _td(
"Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " + "Your message wasn't sent because this homeserver has hit its Monthly Active User Limit. " +
"Please <a>contact your service administrator</a> to continue using the service.", "Please <a>contact your service administrator</a> to continue using the service.",
@ -213,7 +214,8 @@ export default class RoomStatusBar extends React.Component {
"Your message wasn't sent because this homeserver has exceeded a resource limit. " + "Your message wasn't sent because this homeserver has exceeded a resource limit. " +
"Please <a>contact your service administrator</a> to continue using the service.", "Please <a>contact your service administrator</a> to continue using the service.",
), ),
}); },
);
} else { } else {
title = _t('Some of your messages have not been sent'); title = _t('Some of your messages have not been sent');
} }

View file

@ -190,6 +190,9 @@ export interface IState {
rejectError?: Error; rejectError?: Error;
hasPinnedWidgets?: boolean; hasPinnedWidgets?: boolean;
dragCounter: number; dragCounter: number;
// whether or not a spaces context switch brought us here,
// if it did we don't want the room to be marked as read as soon as it is loaded.
wasContextSwitch?: boolean;
} }
@replaceableComponent("structures.RoomView") @replaceableComponent("structures.RoomView")
@ -326,6 +329,7 @@ export default class RoomView extends React.Component<IProps, IState> {
shouldPeek: this.state.matrixClientIsReady && RoomViewStore.shouldPeek(), shouldPeek: this.state.matrixClientIsReady && RoomViewStore.shouldPeek(),
showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", roomId), showingPinned: SettingsStore.getValue("PinnedEvents.isOpen", roomId),
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId), showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
wasContextSwitch: RoomViewStore.getWasContextSwitch(),
}; };
if (!initial && this.state.shouldPeek && !newState.shouldPeek) { if (!initial && this.state.shouldPeek && !newState.shouldPeek) {
@ -2014,6 +2018,7 @@ export default class RoomView extends React.Component<IProps, IState> {
timelineSet={this.state.room.getUnfilteredTimelineSet()} timelineSet={this.state.room.getUnfilteredTimelineSet()}
showReadReceipts={this.state.showReadReceipts} showReadReceipts={this.state.showReadReceipts}
manageReadReceipts={!this.state.isPeeking} manageReadReceipts={!this.state.isPeeking}
sendReadReceiptOnLoad={!this.state.wasContextSwitch}
manageReadMarkers={!this.state.isPeeking} manageReadMarkers={!this.state.isPeeking}
hidden={hideMessagePanel} hidden={hideMessagePanel}
highlightedEventId={highlightedEventId} highlightedEventId={highlightedEventId}

View file

@ -884,9 +884,13 @@ export default class ScrollPanel extends React.Component {
// give the <ol> an explicit role=list because Safari+VoiceOver seems to think an ordered-list with // give the <ol> an explicit role=list because Safari+VoiceOver seems to think an ordered-list with
// list-style-type: none; is no longer a list // list-style-type: none; is no longer a list
return (<AutoHideScrollbar wrappedRef={this._collectScroll} return (
<AutoHideScrollbar
wrappedRef={this._collectScroll}
onScroll={this.onScroll} onScroll={this.onScroll}
className={`mx_ScrollPanel ${this.props.className}`} style={this.props.style}> className={`mx_ScrollPanel ${this.props.className}`}
style={this.props.style}
>
{ this.props.fixedChildren } { this.props.fixedChildren }
<div className="mx_RoomView_messageListWrapper"> <div className="mx_RoomView_messageListWrapper">
<ol ref={this._itemlist} className="mx_RoomView_MessageList" aria-live="polite" role="list"> <ol ref={this._itemlist} className="mx_RoomView_MessageList" aria-live="polite" role="list">

View file

@ -68,6 +68,7 @@ class TimelinePanel extends React.Component {
showReadReceipts: PropTypes.bool, showReadReceipts: PropTypes.bool,
// Enable managing RRs and RMs. These require the timelineSet to have a room. // Enable managing RRs and RMs. These require the timelineSet to have a room.
manageReadReceipts: PropTypes.bool, manageReadReceipts: PropTypes.bool,
sendReadReceiptOnLoad: PropTypes.bool,
manageReadMarkers: PropTypes.bool, manageReadMarkers: PropTypes.bool,
// true to give the component a 'display: none' style. // true to give the component a 'display: none' style.
@ -126,6 +127,7 @@ class TimelinePanel extends React.Component {
// event tile heights. (See _unpaginateEvents) // event tile heights. (See _unpaginateEvents)
timelineCap: Number.MAX_VALUE, timelineCap: Number.MAX_VALUE,
className: 'mx_RoomView_messagePanel', className: 'mx_RoomView_messagePanel',
sendReadReceiptOnLoad: true,
}; };
constructor(props) { constructor(props) {
@ -785,8 +787,10 @@ class TimelinePanel extends React.Component {
return; return;
} }
const lastDisplayedEvent = this.state.events[lastDisplayedIndex]; const lastDisplayedEvent = this.state.events[lastDisplayedIndex];
this._setReadMarker(lastDisplayedEvent.getId(), this._setReadMarker(
lastDisplayedEvent.getTs()); lastDisplayedEvent.getId(),
lastDisplayedEvent.getTs(),
);
// the read-marker should become invisible, so that if the user scrolls // the read-marker should become invisible, so that if the user scrolls
// down, they don't see it. // down, they don't see it.
@ -1049,7 +1053,9 @@ class TimelinePanel extends React.Component {
this._messagePanel.current.scrollToBottom(); this._messagePanel.current.scrollToBottom();
} }
if (this.props.sendReadReceiptOnLoad) {
this.sendReadReceipt(); this.sendReadReceipt();
}
}); });
}; };

View file

@ -39,9 +39,12 @@ export default class ConfirmWipeDeviceDialog extends React.Component {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog className='mx_ConfirmWipeDeviceDialog' hasCancel={true} <BaseDialog
className='mx_ConfirmWipeDeviceDialog'
hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Clear all data in this session?")}> title={_t("Clear all data in this session?")}
>
<div className='mx_ConfirmWipeDeviceDialog_content'> <div className='mx_ConfirmWipeDeviceDialog_content'>
<p> <p>
{_t( {_t(

View file

@ -70,8 +70,16 @@ class GenericEditor extends React.PureComponent {
} }
textInput(id, label) { textInput(id, label) {
return <Field id={id} label={label} size="42" autoFocus={true} type="text" autoComplete="on" return <Field
value={this.state[id]} onChange={this._onChange} />; id={id}
label={label}
size="42"
autoFocus={true}
type="text"
autoComplete="on"
value={this.state[id]}
onChange={this._onChange}
/>;
} }
} }

View file

@ -42,9 +42,12 @@ export default class IntegrationsDisabledDialog extends React.Component {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog className='mx_IntegrationsDisabledDialog' hasCancel={true} <BaseDialog
className='mx_IntegrationsDisabledDialog'
hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Integrations are disabled")}> title={_t("Integrations are disabled")}
>
<div className='mx_IntegrationsDisabledDialog_content'> <div className='mx_IntegrationsDisabledDialog_content'>
<p>{_t("Enable 'Manage Integrations' in Settings to do this.")}</p> <p>{_t("Enable 'Manage Integrations' in Settings to do this.")}</p>
</div> </div>

View file

@ -37,9 +37,12 @@ export default class IntegrationsImpossibleDialog extends React.Component {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog className='mx_IntegrationsImpossibleDialog' hasCancel={false} <BaseDialog
className='mx_IntegrationsImpossibleDialog'
hasCancel={false}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Integrations not allowed")}> title={_t("Integrations not allowed")}
>
<div className='mx_IntegrationsImpossibleDialog_content'> <div className='mx_IntegrationsImpossibleDialog_content'>
<p> <p>
{_t( {_t(

View file

@ -164,8 +164,12 @@ export default class MessageEditHistoryDialog extends React.PureComponent {
} }
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<BaseDialog className='mx_MessageEditHistoryDialog' hasCancel={true} <BaseDialog
onFinished={this.props.onFinished} title={_t("Message edits")}> className='mx_MessageEditHistoryDialog'
hasCancel={true}
onFinished={this.props.onFinished}
title={_t("Message edits")}
>
{content} {content}
</BaseDialog> </BaseDialog>
); );

View file

@ -116,8 +116,12 @@ export default class RoomSettingsDialog extends React.Component {
const roomName = MatrixClientPeg.get().getRoom(this.props.roomId).name; const roomName = MatrixClientPeg.get().getRoom(this.props.roomId).name;
return ( return (
<BaseDialog className='mx_RoomSettingsDialog' hasCancel={true} <BaseDialog
onFinished={this.props.onFinished} title={_t("Room Settings - %(roomName)s", {roomName})}> className='mx_RoomSettingsDialog'
hasCancel={true}
onFinished={this.props.onFinished}
title={_t("Room Settings - %(roomName)s", {roomName})}
>
<div className='mx_SettingsDialog_content'> <div className='mx_SettingsDialog_content'>
<TabbedView tabs={this._getTabs()} /> <TabbedView tabs={this._getTabs()} />
</div> </div>

View file

@ -45,10 +45,12 @@ export default class StorageEvictedDialog extends React.Component {
let logRequest; let logRequest;
if (SdkConfig.get().bug_report_endpoint_url) { if (SdkConfig.get().bug_report_endpoint_url) {
logRequest = _t( logRequest = _t(
"To help us prevent this in future, please <a>send us logs</a>.", {}, "To help us prevent this in future, please <a>send us logs</a>.",
{},
{ {
a: text => <a href="#" onClick={this._sendBugReport}>{text}</a>, a: text => <a href="#" onClick={this._sendBugReport}>{text}</a>,
}); },
);
} }
return ( return (

View file

@ -155,8 +155,12 @@ export default class UserSettingsDialog extends React.Component {
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return ( return (
<BaseDialog className='mx_UserSettingsDialog' hasCancel={true} <BaseDialog
onFinished={this.props.onFinished} title={_t("Settings")}> className='mx_UserSettingsDialog'
hasCancel={true}
onFinished={this.props.onFinished}
title={_t("Settings")}
>
<div className='mx_SettingsDialog_content'> <div className='mx_SettingsDialog_content'>
<TabbedView tabs={this._getTabs()} initialTabId={this.props.initialTabId} /> <TabbedView tabs={this._getTabs()} initialTabId={this.props.initialTabId} />
</div> </div>

View file

@ -52,7 +52,9 @@ export default class VerificationRequestDialog extends React.Component {
const title = request && request.isSelfVerification ? const title = request && request.isSelfVerification ?
_t("Verify other login") : _t("Verification Request"); _t("Verify other login") : _t("Verification Request");
return <BaseDialog className="mx_InfoDialog" onFinished={this.props.onFinished} return <BaseDialog
className="mx_InfoDialog"
onFinished={this.props.onFinished}
contentId="mx_Dialog_content" contentId="mx_Dialog_content"
title={title} title={title}
hasCancel={true} hasCancel={true}

View file

@ -70,9 +70,12 @@ export default class WidgetOpenIDPermissionsDialog extends React.Component {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return ( return (
<BaseDialog className='mx_WidgetOpenIDPermissionsDialog' hasCancel={true} <BaseDialog
className='mx_WidgetOpenIDPermissionsDialog'
hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Allow this widget to verify your identity")}> title={_t("Allow this widget to verify your identity")}
>
<div className='mx_WidgetOpenIDPermissionsDialog_content'> <div className='mx_WidgetOpenIDPermissionsDialog_content'>
<p> <p>
{_t("The widget will verify your user ID, but won't be able to perform actions for you:")} {_t("The widget will verify your user ID, but won't be able to perform actions for you:")}

View file

@ -43,7 +43,8 @@ export default class ConfirmDestroyCrossSigningDialog extends React.Component {
className='mx_ConfirmDestroyCrossSigningDialog' className='mx_ConfirmDestroyCrossSigningDialog'
hasCancel={true} hasCancel={true}
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
title={_t("Destroy cross-signing keys?")}> title={_t("Destroy cross-signing keys?")}
>
<div className='mx_ConfirmDestroyCrossSigningDialog_content'> <div className='mx_ConfirmDestroyCrossSigningDialog_content'>
<p> <p>
{_t( {_t(

View file

@ -373,15 +373,18 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
{_t( {_t(
"If you've forgotten your Security Phrase you can "+ "If you've forgotten your Security Phrase you can "+
"<button1>use your Security Key</button1> or " + "<button1>use your Security Key</button1> or " +
"<button2>set up new recovery options</button2>" "<button2>set up new recovery options</button2>",
, {}, { {},
button1: s => <AccessibleButton className="mx_linkButton" {
button1: s => <AccessibleButton
className="mx_linkButton"
element="span" element="span"
onClick={this._onUseRecoveryKeyClick} onClick={this._onUseRecoveryKeyClick}
> >
{s} {s}
</AccessibleButton>, </AccessibleButton>,
button2: s => <AccessibleButton className="mx_linkButton" button2: s => <AccessibleButton
className="mx_linkButton"
element="span" element="span"
onClick={this._onResetRecoveryClick} onClick={this._onResetRecoveryClick}
> >
@ -435,15 +438,17 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
</div> </div>
{_t( {_t(
"If you've forgotten your Security Key you can "+ "If you've forgotten your Security Key you can "+
"<button>set up new recovery options</button>" "<button>set up new recovery options</button>",
, {}, { {},
{
button: s => <AccessibleButton className="mx_linkButton" button: s => <AccessibleButton className="mx_linkButton"
element="span" element="span"
onClick={this._onResetRecoveryClick} onClick={this._onResetRecoveryClick}
> >
{s} {s}
</AccessibleButton>, </AccessibleButton>,
})} },
)}
</div>; </div>;
} }

View file

@ -65,12 +65,18 @@ export class EditableItem extends React.Component {
<span className="mx_EditableItem_promptText"> <span className="mx_EditableItem_promptText">
{_t("Are you sure?")} {_t("Are you sure?")}
</span> </span>
<AccessibleButton onClick={this._onActuallyRemove} kind="primary_sm" <AccessibleButton
className="mx_EditableItem_confirmBtn"> onClick={this._onActuallyRemove}
kind="primary_sm"
className="mx_EditableItem_confirmBtn"
>
{_t("Yes")} {_t("Yes")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton onClick={this._onDontRemove} kind="danger_sm" <AccessibleButton
className="mx_EditableItem_confirmBtn"> onClick={this._onDontRemove}
kind="danger_sm"
className="mx_EditableItem_confirmBtn"
>
{_t("No")} {_t("No")}
</AccessibleButton> </AccessibleButton>
</div> </div>
@ -121,8 +127,12 @@ export default class EditableItemList extends React.Component {
_renderNewItemField() { _renderNewItemField() {
return ( return (
<form onSubmit={this._onItemAdded} autoComplete="off" <form
noValidate={true} className="mx_EditableItemList_newItem"> onSubmit={this._onItemAdded}
autoComplete="off"
noValidate={true}
className="mx_EditableItemList_newItem"
>
<Field label={this.props.placeholder} type="text" <Field label={this.props.placeholder} type="text"
autoComplete="off" value={this.props.newItem || ""} onChange={this._onNewItemChanged} autoComplete="off" value={this.props.newItem || ""} onChange={this._onNewItemChanged}
list={this.props.suggestionsListId} /> list={this.props.suggestionsListId} />

View file

@ -221,13 +221,15 @@ export default class EditableText extends React.Component {
</div>; </div>;
} else { } else {
// show the content editable div, but manually manage its contents as react and contentEditable don't play nice together // show the content editable div, but manually manage its contents as react and contentEditable don't play nice together
editableEl = <div ref={this._editable_div} editableEl = <div
ref={this._editable_div}
contentEditable={true} contentEditable={true}
className={className} className={className}
onKeyDown={this.onKeyDown} onKeyDown={this.onKeyDown}
onKeyUp={this.onKeyUp} onKeyUp={this.onKeyUp}
onFocus={this.onFocus} onFocus={this.onFocus}
onBlur={this.onBlur} />; onBlur={this.onBlur}
/>;
} }
return editableEl; return editableEl;

View file

@ -46,8 +46,12 @@ export default class LabelledToggleSwitch extends React.Component {
// This is a minimal version of a SettingsFlag // This is a minimal version of a SettingsFlag
let firstPart = <span className="mx_SettingsFlag_label">{this.props.label}</span>; let firstPart = <span className="mx_SettingsFlag_label">{this.props.label}</span>;
let secondPart = <ToggleSwitch checked={this.props.value} disabled={this.props.disabled} let secondPart = <ToggleSwitch
onChange={this.props.onChange} aria-label={this.props.label} />; checked={this.props.value}
disabled={this.props.disabled}
onChange={this.props.onChange}
aria-label={this.props.label}
/>;
if (this.props.toggleInFront) { if (this.props.toggleInFront) {
const temp = firstPart; const temp = firstPart;

View file

@ -136,8 +136,12 @@ export default class PowerSelector extends React.Component {
picker = ( picker = (
<Field type="number" <Field type="number"
label={label} max={this.props.maxValue} label={label} max={this.props.maxValue}
onBlur={this.onCustomBlur} onKeyDown={this.onCustomKeyDown} onChange={this.onCustomChange} onBlur={this.onCustomBlur}
value={String(this.state.customValue)} disabled={this.props.disabled} /> onKeyDown={this.onCustomKeyDown}
onChange={this.onCustomChange}
value={String(this.state.customValue)}
disabled={this.props.disabled}
/>
); );
} else { } else {
// Each level must have a definition in this.state.levelRoleMap // Each level must have a definition in this.state.levelRoleMap
@ -155,7 +159,8 @@ export default class PowerSelector extends React.Component {
picker = ( picker = (
<Field element="select" <Field element="select"
label={label} onChange={this.onSelectChange} label={label} onChange={this.onSelectChange}
value={String(this.state.selectValue)} disabled={this.props.disabled}> value={String(this.state.selectValue)} disabled={this.props.disabled}
>
{options} {options}
</Field> </Field>
); );

View file

@ -56,7 +56,8 @@ export default class RoomAliasField extends React.PureComponent {
placeholder={_t("e.g. my-room")} placeholder={_t("e.g. my-room")}
onChange={this._onChange} onChange={this._onChange}
value={this.props.value.substring(1, this.props.value.length - this.props.domain.length - 1)} value={this.props.value.substring(1, this.props.value.length - this.props.domain.length - 1)}
maxLength={maxlength} /> maxLength={maxlength}
/>
); );
} }

View file

@ -178,9 +178,15 @@ export default class GroupMemberList extends React.Component {
} }
const inputBox = ( const inputBox = (
<input className="mx_GroupMemberList_query mx_textinput" id="mx_GroupMemberList_query" type="text" <input
onChange={this.onSearchQueryChanged} value={this.state.searchQuery} className="mx_GroupMemberList_query mx_textinput"
placeholder={_t('Filter community members')} autoComplete="off" /> id="mx_GroupMemberList_query"
type="text"
onChange={this.onSearchQueryChanged}
value={this.state.searchQuery}
placeholder={_t('Filter community members')}
autoComplete="off"
/>
); );
const joined = this.state.members ? <div className="mx_MemberList_joined"> const joined = this.state.members ? <div className="mx_MemberList_joined">

View file

@ -141,9 +141,14 @@ export default class GroupRoomList extends React.Component {
); );
} }
const inputBox = ( const inputBox = (
<input className="mx_GroupRoomList_query mx_textinput" id="mx_GroupRoomList_query" type="text" <input
onChange={this.onSearchQueryChanged} value={this.state.searchQuery} className="mx_GroupRoomList_query mx_textinput" id="mx_GroupRoomList_query"
placeholder={_t('Filter community rooms')} autoComplete="off" /> type="text"
onChange={this.onSearchQueryChanged}
value={this.state.searchQuery}
placeholder={_t('Filter community rooms')}
autoComplete="off"
/>
); );
const TruncatedList = sdk.getComponent("elements.TruncatedList"); const TruncatedList = sdk.getComponent("elements.TruncatedList");

View file

@ -82,9 +82,7 @@ export default class MKeyVerificationConclusion extends React.Component {
} }
// User isn't actually verified // User isn't actually verified
if (!MatrixClientPeg.get() if (!MatrixClientPeg.get().checkUserTrust(request.otherUserId).isCrossSigningVerified()) {
.checkUserTrust(request.otherUserId)
.isCrossSigningVerified()) {
return false; return false;
} }

View file

@ -129,12 +129,13 @@ export default class ReactionsRowButton extends React.PureComponent {
}, },
); );
} }
const isPeeking = room.getMyMembership() !== "join";
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
return <AccessibleButton return <AccessibleButton
className={classes} className={classes}
aria-label={label} aria-label={label}
onClick={this.onClick} onClick={this.onClick}
disabled={isPeeking}
onMouseOver={this.onMouseOver} onMouseOver={this.onMouseOver}
onMouseLeave={this.onMouseLeave} onMouseLeave={this.onMouseLeave}
> >

View file

@ -525,7 +525,8 @@ export default class TextualBody extends React.Component {
link={link} link={link}
mxEvent={this.props.mxEvent} mxEvent={this.props.mxEvent}
onCancelClick={this.onCancelClick} onCancelClick={this.onCancelClick}
onHeightChanged={this.props.onHeightChanged} />; onHeightChanged={this.props.onHeightChanged}
/>;
}); });
} }

View file

@ -310,9 +310,14 @@ export default class AliasSettings extends React.Component {
let found = false; let found = false;
const canonicalValue = this.state.canonicalAlias || ""; const canonicalValue = this.state.canonicalAlias || "";
const canonicalAliasSection = ( const canonicalAliasSection = (
<Field onChange={this.onCanonicalAliasChange} value={canonicalValue} <Field
onChange={this.onCanonicalAliasChange}
value={canonicalValue}
disabled={this.state.updatingCanonicalAlias || !this.props.canSetCanonicalAlias} disabled={this.state.updatingCanonicalAlias || !this.props.canSetCanonicalAlias}
element='select' id='canonicalAlias' label={_t('Main address')}> element='select'
id='canonicalAlias'
label={_t('Main address')}
>
<option value="" key="unset">{ _t('not specified') }</option> <option value="" key="unset">{ _t('not specified') }</option>
{ {
this._getAliases().map((alias, i) => { this._getAliases().map((alias, i) => {

View file

@ -205,16 +205,34 @@ export default class RoomProfileSettings extends React.Component {
noValidate={true} noValidate={true}
className="mx_ProfileSettings_profileForm" className="mx_ProfileSettings_profileForm"
> >
<input type="file" ref={this._avatarUpload} className="mx_ProfileSettings_avatarUpload" <input
onChange={this._onAvatarChanged} accept="image/*" /> type="file"
ref={this._avatarUpload}
className="mx_ProfileSettings_avatarUpload"
onChange={this._onAvatarChanged}
accept="image/*"
/>
<div className="mx_ProfileSettings_profile"> <div className="mx_ProfileSettings_profile">
<div className="mx_ProfileSettings_controls"> <div className="mx_ProfileSettings_controls">
<Field label={_t("Room Name")} <Field
type="text" value={this.state.displayName} autoComplete="off" label={_t("Room Name")}
onChange={this._onDisplayNameChanged} disabled={!this.state.canSetName} /> type="text"
<Field className="mx_ProfileSettings_controls_topic" id="profileTopic" label={_t("Room Topic")} disabled={!this.state.canSetTopic} value={this.state.displayName}
type="text" value={this.state.topic} autoComplete="off" autoComplete="off"
onChange={this._onTopicChanged} element="textarea" /> onChange={this._onDisplayNameChanged}
disabled={!this.state.canSetName}
/>
<Field
className="mx_ProfileSettings_controls_topic"
id="profileTopic"
label={_t("Room Topic")}
disabled={!this.state.canSetTopic}
type="text"
value={this.state.topic}
autoComplete="off"
onChange={this._onTopicChanged}
element="textarea"
/>
</div> </div>
<AvatarSetting <AvatarSetting
avatarUrl={this.state.avatarUrl} avatarUrl={this.state.avatarUrl}

View file

@ -68,10 +68,12 @@ export default class UrlPreviewSettings extends React.Component {
if (SettingsStore.canSetValue("urlPreviewsEnabled", roomId, "room")) { if (SettingsStore.canSetValue("urlPreviewsEnabled", roomId, "room")) {
previewsForRoom = ( previewsForRoom = (
<label> <label>
<SettingsFlag name="urlPreviewsEnabled" <SettingsFlag
name="urlPreviewsEnabled"
level={SettingLevel.ROOM} level={SettingLevel.ROOM}
roomId={roomId} roomId={roomId}
isExplicit={true} /> isExplicit={true}
/>
</label> </label>
); );
} else { } else {

View file

@ -176,8 +176,11 @@ class EntityTile extends React.Component {
// The wrapping div is required to make the magic mouse listener work, for some reason. // The wrapping div is required to make the magic mouse listener work, for some reason.
return ( return (
<div ref={(c) => this.container = c} > <div ref={(c) => this.container = c} >
<AccessibleButton className={classNames(mainClassNames)} title={this.props.title} <AccessibleButton
onClick={this.props.onClick}> className={classNames(mainClassNames)}
title={this.props.title}
onClick={this.props.onClick}
>
<div className="mx_EntityTile_avatar"> <div className="mx_EntityTile_avatar">
{ av } { av }
{ e2eIcon } { e2eIcon }

View file

@ -98,7 +98,10 @@ export default class PinnedEventTile extends React.Component {
{ formatFullDate(new Date(this.props.mxEvent.getTs())) } { formatFullDate(new Date(this.props.mxEvent.getTs())) }
</span> </span>
<div className="mx_PinnedEventTile_message"> <div className="mx_PinnedEventTile_message">
<MessageEvent mxEvent={this.props.mxEvent} className="mx_PinnedEventTile_body" maxImageHeight={150} <MessageEvent
mxEvent={this.props.mxEvent}
className="mx_PinnedEventTile_body"
maxImageHeight={150}
onHeightChanged={() => {}} // we need to give this, apparently onHeightChanged={() => {}} // we need to give this, apparently
/> />
</div> </div>

View file

@ -113,10 +113,14 @@ export default class PinnedEventsPanel extends React.Component {
} }
return this.state.pinned.map((context) => { return this.state.pinned.map((context) => {
return (<PinnedEventTile key={context.event.getId()} return (
<PinnedEventTile
key={context.event.getId()}
mxRoom={this.props.room} mxRoom={this.props.room}
mxEvent={context.event} mxEvent={context.event}
onUnpinned={this._updatePinnedMessages} />); onUnpinned={this._updatePinnedMessages}
/>
);
}); });
} }

View file

@ -187,8 +187,7 @@ export default class ReadReceiptMarker extends React.PureComponent {
} }
return ( return (
<NodeAnimator <NodeAnimator startStyles={this.state.startStyles}>
startStyles={this.state.startStyles} >
<MemberAvatar <MemberAvatar
member={this.props.member} member={this.props.member}
fallbackUserId={this.props.fallbackUserId} fallbackUserId={this.props.fallbackUserId}

View file

@ -79,8 +79,13 @@ export default class ReplyPreview extends React.Component {
{ _t('Replying') } { _t('Replying') }
</div> </div>
<div className="mx_ReplyPreview_header mx_ReplyPreview_cancel"> <div className="mx_ReplyPreview_header mx_ReplyPreview_cancel">
<img className="mx_filterFlipColor" src={require("../../../../res/img/cancel.svg")} width="18" height="18" <img
onClick={cancelQuoting} /> className="mx_filterFlipColor"
src={require("../../../../res/img/cancel.svg")}
width="18"
height="18"
onClick={cancelQuoting}
/>
</div> </div>
<div className="mx_ReplyPreview_clear" /> <div className="mx_ReplyPreview_clear" />
<EventTile <EventTile

View file

@ -19,6 +19,7 @@ import React, {useState} from "react";
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";
import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore"; import RoomListStore, { LISTS_UPDATE_EVENT } from "../../../stores/room-list/RoomListStore";
import {useEventEmitter} from "../../../hooks/useEventEmitter"; import {useEventEmitter} from "../../../hooks/useEventEmitter";
import SpaceStore from "../../../stores/SpaceStore";
const RoomListNumResults: React.FC = () => { const RoomListNumResults: React.FC = () => {
const [count, setCount] = useState<number>(null); const [count, setCount] = useState<number>(null);
@ -34,7 +35,10 @@ const RoomListNumResults: React.FC = () => {
if (typeof count !== "number") return null; if (typeof count !== "number") return null;
return <div className="mx_LeftPanel_roomListFilterCount"> return <div className="mx_LeftPanel_roomListFilterCount">
{_t("%(count)s results", { count })} { SpaceStore.instance.spacePanelSpaces.length
? _t("%(count)s results in all spaces", { count })
: _t("%(count)s results", { count })
}
</div>; </div>;
}; };

View file

@ -170,8 +170,12 @@ export default class ProfileSettings extends React.Component {
noValidate={true} noValidate={true}
className="mx_ProfileSettings_profileForm" className="mx_ProfileSettings_profileForm"
> >
<input type="file" ref={this._avatarUpload} className="mx_ProfileSettings_avatarUpload" <input
onChange={this._onAvatarChanged} accept="image/*" /> type="file"
ref={this._avatarUpload} className="mx_ProfileSettings_avatarUpload"
onChange={this._onAvatarChanged}
accept="image/*"
/>
<div className="mx_ProfileSettings_profile"> <div className="mx_ProfileSettings_profile">
<div className="mx_ProfileSettings_controls"> <div className="mx_ProfileSettings_controls">
<span className="mx_SettingsTab_subheading">{_t("Profile")}</span> <span className="mx_SettingsTab_subheading">{_t("Profile")}</span>

View file

@ -90,12 +90,18 @@ export class ExistingEmailAddress extends React.Component {
<span className="mx_ExistingEmailAddress_promptText"> <span className="mx_ExistingEmailAddress_promptText">
{_t("Remove %(email)s?", {email: this.props.email.address} )} {_t("Remove %(email)s?", {email: this.props.email.address} )}
</span> </span>
<AccessibleButton onClick={this._onActuallyRemove} kind="danger_sm" <AccessibleButton
className="mx_ExistingEmailAddress_confirmBtn"> onClick={this._onActuallyRemove}
kind="danger_sm"
className="mx_ExistingEmailAddress_confirmBtn"
>
{_t("Remove")} {_t("Remove")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton onClick={this._onDontRemove} kind="link_sm" <AccessibleButton
className="mx_ExistingEmailAddress_confirmBtn"> onClick={this._onDontRemove}
kind="link_sm"
className="mx_ExistingEmailAddress_confirmBtn"
>
{_t("Cancel")} {_t("Cancel")}
</AccessibleButton> </AccessibleButton>
</div> </div>
@ -230,8 +236,11 @@ export default class EmailAddresses extends React.Component {
addButton = ( addButton = (
<div> <div>
<div>{_t("We've sent you an email to verify your address. Please follow the instructions there and then click the button below.")}</div> <div>{_t("We've sent you an email to verify your address. Please follow the instructions there and then click the button below.")}</div>
<AccessibleButton onClick={this._onContinueClick} kind="primary" <AccessibleButton
disabled={this.state.continueDisabled}> onClick={this._onContinueClick}
kind="primary"
disabled={this.state.continueDisabled}
>
{_t("Continue")} {_t("Continue")}
</AccessibleButton> </AccessibleButton>
</div> </div>
@ -241,8 +250,12 @@ export default class EmailAddresses extends React.Component {
return ( return (
<div className="mx_EmailAddresses"> <div className="mx_EmailAddresses">
{existingEmailElements} {existingEmailElements}
<form onSubmit={this._onAddClick} autoComplete="off" <form
noValidate={true} className="mx_EmailAddresses_new"> onSubmit={this._onAddClick}
autoComplete="off"
noValidate={true}
className="mx_EmailAddresses_new"
>
<Field <Field
type="text" type="text"
label={_t("Email Address")} label={_t("Email Address")}

View file

@ -85,12 +85,18 @@ export class ExistingPhoneNumber extends React.Component {
<span className="mx_ExistingPhoneNumber_promptText"> <span className="mx_ExistingPhoneNumber_promptText">
{_t("Remove %(phone)s?", {phone: this.props.msisdn.address})} {_t("Remove %(phone)s?", {phone: this.props.msisdn.address})}
</span> </span>
<AccessibleButton onClick={this._onActuallyRemove} kind="danger_sm" <AccessibleButton
className="mx_ExistingPhoneNumber_confirmBtn"> onClick={this._onActuallyRemove}
kind="danger_sm"
className="mx_ExistingPhoneNumber_confirmBtn"
>
{_t("Remove")} {_t("Remove")}
</AccessibleButton> </AccessibleButton>
<AccessibleButton onClick={this._onDontRemove} kind="link_sm" <AccessibleButton
className="mx_ExistingPhoneNumber_confirmBtn"> onClick={this._onDontRemove}
kind="link_sm"
className="mx_ExistingPhoneNumber_confirmBtn"
>
{_t("Cancel")} {_t("Cancel")}
</AccessibleButton> </AccessibleButton>
</div> </div>
@ -246,8 +252,11 @@ export default class PhoneNumbers extends React.Component {
value={this.state.newPhoneNumberCode} value={this.state.newPhoneNumberCode}
onChange={this._onChangeNewPhoneNumberCode} onChange={this._onChangeNewPhoneNumberCode}
/> />
<AccessibleButton onClick={this._onContinueClick} kind="primary" <AccessibleButton
disabled={this.state.continueDisabled}> onClick={this._onContinueClick}
kind="primary"
disabled={this.state.continueDisabled}
>
{_t("Continue")} {_t("Continue")}
</AccessibleButton> </AccessibleButton>
</form> </form>

View file

@ -80,9 +80,11 @@ export default class GeneralRoomSettingsTab extends React.Component {
flairSection = <> flairSection = <>
<span className='mx_SettingsTab_subheading'>{_t("Flair")}</span> <span className='mx_SettingsTab_subheading'>{_t("Flair")}</span>
<div className='mx_SettingsTab_section mx_SettingsTab_subsectionText'> <div className='mx_SettingsTab_section mx_SettingsTab_subsectionText'>
<RelatedGroupSettings roomId={room.roomId} <RelatedGroupSettings
roomId={room.roomId}
canSetRelatedGroups={canChangeGroups} canSetRelatedGroups={canChangeGroups}
relatedGroupsEvent={groupsEvent} /> relatedGroupsEvent={groupsEvent}
/>
</div> </div>
</>; </>;
} }

View file

@ -323,8 +323,11 @@ export default class GeneralUserSettingsTab extends React.Component {
return ( return (
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Language and region")}</span> <span className="mx_SettingsTab_subheading">{_t("Language and region")}</span>
<LanguageDropdown className="mx_GeneralUserSettingsTab_languageInput" <LanguageDropdown
onOptionChange={this._onLanguageChange} value={this.state.language} /> className="mx_GeneralUserSettingsTab_languageInput"
onOptionChange={this._onLanguageChange}
value={this.state.language}
/>
</div> </div>
); );
} }
@ -333,8 +336,10 @@ export default class GeneralUserSettingsTab extends React.Component {
return ( return (
<div className="mx_SettingsTab_section"> <div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Spell check dictionaries")}</span> <span className="mx_SettingsTab_subheading">{_t("Spell check dictionaries")}</span>
<SpellCheckSettings languages={this.state.spellCheckLanguages} <SpellCheckSettings
onLanguagesChange={this._onSpellCheckLanguagesChange} /> languages={this.state.spellCheckLanguages}
onLanguagesChange={this._onSpellCheckLanguagesChange}
/>
</div> </div>
); );
} }

View file

@ -257,12 +257,16 @@ export default class SecurityUserSettingsTab extends React.Component {
const userIds = !ignoredUserIds?.length const userIds = !ignoredUserIds?.length
? _t('You have no ignored users.') ? _t('You have no ignored users.')
: ignoredUserIds.map((u) => <IgnoredUser : ignoredUserIds.map((u) => {
return (
<IgnoredUser
userId={u} userId={u}
onUnignored={this._onUserUnignored} onUnignored={this._onUserUnignored}
key={u} key={u}
inProgress={waitingUnignored.includes(u)} inProgress={waitingUnignored.includes(u)}
/>); />
);
});
return ( return (
<div className='mx_SettingsTab_section'> <div className='mx_SettingsTab_section'>

View file

@ -61,9 +61,9 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} =
// const inlinePattern = "(?:^|\\s)(?<!\\\\)\\$(?!\\s)(([^$]|\\\\\\$)+?)(?<!\\\\|\\s)\\$"; // const inlinePattern = "(?:^|\\s)(?<!\\\\)\\$(?!\\s)(([^$]|\\\\\\$)+?)(?<!\\\\|\\s)\\$";
// conditions for display math detection $$...$$: // conditions for display math detection $$...$$:
// - pattern starts at beginning of line or is not prefixed with backslash or dollar // - pattern starts and ends on a new line
// - left delimiter ($$) is not escaped by backslash // - left delimiter ($$) is not escaped by backslash
"display": "(^|[^\\\\$])\\$\\$(([^$]|\\\\\\$)+?)\\$\\$", "display": "(^)\\$\\$(([^$]|\\\\\\$)+?)\\$\\$$",
// conditions for inline math detection $...$: // conditions for inline math detection $...$:
// - pattern starts at beginning of line, follows whitespace character or punctuation // - pattern starts at beginning of line, follows whitespace character or punctuation
@ -78,9 +78,9 @@ export function htmlSerializeIfNeeded(model: EditorModel, {forceHTML = false} =
// detect math with latex delimiters, inline: \(...\), display \[...\] // detect math with latex delimiters, inline: \(...\), display \[...\]
// conditions for display math detection \[...\]: // conditions for display math detection \[...\]:
// - pattern starts at beginning of line or is not prefixed with backslash // - pattern starts and ends on a new line
// - pattern is not empty // - pattern is not empty
"display": "(^|[^\\\\])\\\\\\[(?!\\\\\\])(.*?)\\\\\\]", "display": "(^)\\\\\\[(?!\\\\\\])(.*?)\\\\\\]$",
// conditions for inline math detection \(...\): // conditions for inline math detection \(...\):
// - pattern starts at beginning of line or is not prefixed with backslash // - pattern starts at beginning of line or is not prefixed with backslash

View file

@ -1552,6 +1552,8 @@
"Explore all public rooms": "Explore all public rooms", "Explore all public rooms": "Explore all public rooms",
"Quick actions": "Quick actions", "Quick actions": "Quick actions",
"Use the + to make a new room or explore existing ones below": "Use the + to make a new room or explore existing ones below", "Use the + to make a new room or explore existing ones below": "Use the + to make a new room or explore existing ones below",
"%(count)s results in all spaces|other": "%(count)s results in all spaces",
"%(count)s results in all spaces|one": "%(count)s result in all spaces",
"%(count)s results|other": "%(count)s results", "%(count)s results|other": "%(count)s results",
"%(count)s results|one": "%(count)s result", "%(count)s results|one": "%(count)s result",
"This room": "This room", "This room": "This room",
@ -2612,6 +2614,7 @@
"If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.": "If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.", "If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.": "If you can't find the room you're looking for, ask for an invite or <a>Create a new room</a>.",
"Explore rooms in %(communityName)s": "Explore rooms in %(communityName)s", "Explore rooms in %(communityName)s": "Explore rooms in %(communityName)s",
"Filter": "Filter", "Filter": "Filter",
"Filter all spaces": "Filter all spaces",
"Clear filter": "Clear filter", "Clear filter": "Clear filter",
"Filter rooms and people": "Filter rooms and people", "Filter rooms and people": "Filter rooms and people",
"You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.", "You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.": "You can't send any messages until you review and agree to <consentLink>our terms and conditions</consentLink>.",

View file

@ -127,8 +127,13 @@ export default class EventIndex extends EventEmitter {
this.crawlerCheckpoints.push(forwardCheckpoint); this.crawlerCheckpoints.push(forwardCheckpoint);
} }
} catch (e) { } catch (e) {
console.log("EventIndex: Error adding initial checkpoints for room", console.log(
room.roomId, backCheckpoint, forwardCheckpoint, e); "EventIndex: Error adding initial checkpoints for room",
room.roomId,
backCheckpoint,
forwardCheckpoint,
e,
);
} }
})); }));
} }
@ -379,8 +384,12 @@ export default class EventIndex extends EventEmitter {
try { try {
await indexManager.addCrawlerCheckpoint(checkpoint); await indexManager.addCrawlerCheckpoint(checkpoint);
} catch (e) { } catch (e) {
console.log("EventIndex: Error adding new checkpoint for room", console.log(
room.roomId, checkpoint, e); "EventIndex: Error adding new checkpoint for room",
room.roomId,
checkpoint,
e,
);
} }
this.crawlerCheckpoints.push(checkpoint); this.crawlerCheckpoints.push(checkpoint);
@ -776,8 +785,14 @@ export default class EventIndex extends EventEmitter {
* @returns {Promise<boolean>} Resolves to true if events were added to the * @returns {Promise<boolean>} Resolves to true if events were added to the
* timeline, false otherwise. * timeline, false otherwise.
*/ */
async populateFileTimeline(timelineSet, timeline, room, limit = 10, async populateFileTimeline(
fromEvent = null, direction = EventTimeline.BACKWARDS) { timelineSet,
timeline,
room,
limit = 10,
fromEvent = null,
direction = EventTimeline.BACKWARDS,
) {
const matrixEvents = await this.loadFileEvents(room, limit, fromEvent, direction); const matrixEvents = await this.loadFileEvents(room, limit, fromEvent, direction);
// If this is a normal fill request, not a pagination request, we need // If this is a normal fill request, not a pagination request, we need

View file

@ -124,17 +124,17 @@ class CustomRoomTagStore extends EventEmitter {
const tags = Object.assign({}, oldTags, tag); const tags = Object.assign({}, oldTags, tag);
this._setState({tags}); this._setState({tags});
} }
}
break; break;
}
case 'on_client_not_viable': case 'on_client_not_viable':
case 'on_logged_out': { case 'on_logged_out': {
// we assume to always have a tags object in the state // we assume to always have a tags object in the state
this._state = {tags: {}}; this._state = {tags: {}};
RoomListStore.instance.off(LISTS_UPDATE_EVENT, this._onListsUpdated); RoomListStore.instance.off(LISTS_UPDATE_EVENT, this._onListsUpdated);
}
break; break;
} }
} }
}
_getUpdatedTags() { _getUpdatedTags() {
if (!SettingsStore.getValue("feature_custom_tags")) { if (!SettingsStore.getValue("feature_custom_tags")) {

View file

@ -62,6 +62,8 @@ const INITIAL_STATE = {
shouldPeek: false, shouldPeek: false,
viaServers: [], viaServers: [],
wasContextSwitch: false,
}; };
/** /**
@ -116,6 +118,7 @@ class RoomViewStore extends Store<ActionPayload> {
roomId: null, roomId: null,
roomAlias: null, roomAlias: null,
viaServers: [], viaServers: [],
wasContextSwitch: false,
}); });
break; break;
case 'view_room_error': case 'view_room_error':
@ -195,6 +198,7 @@ class RoomViewStore extends Store<ActionPayload> {
// pull the user out of Room Settings // pull the user out of Room Settings
isEditingSettings: false, isEditingSettings: false,
viaServers: payload.via_servers, viaServers: payload.via_servers,
wasContextSwitch: payload.context_switch,
}; };
// Allow being given an event to be replied to when switching rooms but sanity check its for this room // Allow being given an event to be replied to when switching rooms but sanity check its for this room
@ -231,6 +235,7 @@ class RoomViewStore extends Store<ActionPayload> {
roomLoading: true, roomLoading: true,
roomLoadError: null, roomLoadError: null,
viaServers: payload.via_servers, viaServers: payload.via_servers,
wasContextSwitch: payload.context_switch,
}); });
try { try {
const result = await MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias); const result = await MatrixClientPeg.get().getRoomIdForAlias(payload.room_alias);
@ -256,6 +261,8 @@ class RoomViewStore extends Store<ActionPayload> {
room_alias: payload.room_alias, room_alias: payload.room_alias,
auto_join: payload.auto_join, auto_join: payload.auto_join,
oob_data: payload.oob_data, oob_data: payload.oob_data,
viaServers: payload.via_servers,
wasContextSwitch: payload.context_switch,
}); });
} }
} }
@ -266,7 +273,6 @@ class RoomViewStore extends Store<ActionPayload> {
roomAlias: payload.room_alias, roomAlias: payload.room_alias,
roomLoading: false, roomLoading: false,
roomLoadError: payload.err, roomLoadError: payload.err,
viaServers: [],
}); });
} }
@ -426,6 +432,10 @@ class RoomViewStore extends Store<ActionPayload> {
public shouldPeek() { public shouldPeek() {
return this.state.shouldPeek; return this.state.shouldPeek;
} }
public getWasContextSwitch() {
return this.state.wasContextSwitch;
}
} }
let singletonRoomViewStore = null; let singletonRoomViewStore = null;

View file

@ -601,7 +601,11 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
let rooms = this.matrixClient.getVisibleRooms().filter(r => VisibilityProvider.instance.isRoomVisible(r)); let rooms = this.matrixClient.getVisibleRooms().filter(r => VisibilityProvider.instance.isRoomVisible(r));
if (this.prefilterConditions.length > 0) { // if spaces are enabled only consider the prefilter conditions when there are no runtime conditions
// for the search all spaces feature
if (this.prefilterConditions.length > 0
&& (!SettingsStore.getValue("feature_spaces") || !this.filterConditions.length)
) {
rooms = rooms.filter(r => { rooms = rooms.filter(r => {
for (const filter of this.prefilterConditions) { for (const filter of this.prefilterConditions) {
if (!filter.isVisible(r)) { if (!filter.isVisible(r)) {
@ -675,6 +679,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
if (this.algorithm) { if (this.algorithm) {
this.algorithm.addFilterCondition(filter); this.algorithm.addFilterCondition(filter);
} }
// Runtime filters with spaces disable prefiltering for the search all spaces effect
if (SettingsStore.getValue("feature_spaces")) {
promise = this.recalculatePrefiltering();
}
} }
promise.then(() => this.updateFn.trigger()); promise.then(() => this.updateFn.trigger());
} }
@ -698,6 +706,10 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
if (this.algorithm) { if (this.algorithm) {
this.algorithm.removeFilterCondition(filter); this.algorithm.removeFilterCondition(filter);
// Runtime filters with spaces disable prefiltering for the search all spaces effect
if (SettingsStore.getValue("feature_spaces")) {
promise = this.recalculatePrefiltering();
}
} }
} }
idx = this.prefilterConditions.indexOf(filter); idx = this.prefilterConditions.indexOf(filter);

View file

@ -310,8 +310,7 @@ function unpackMegolmKeyFile(data) {
// look for the end line // look for the end line
while (1) { while (1) {
const lineEnd = fileStr.indexOf('\n', lineStart); const lineEnd = fileStr.indexOf('\n', lineStart);
const line = fileStr.slice(lineStart, lineEnd < 0 ? undefined : lineEnd) const line = fileStr.slice(lineStart, lineEnd < 0 ? undefined : lineEnd).trim();
.trim();
if (line === TRAILER_LINE) { if (line === TRAILER_LINE) {
break; break;
} }

View file

@ -89,10 +89,12 @@ describe("AccessSecretStorageDialog", function() {
<AccessSecretStorageDialog <AccessSecretStorageDialog
checkPrivateKey={() => false} checkPrivateKey={() => false}
onFinished={() => {}} onFinished={() => {}}
keyInfo={ { passphrase: { keyInfo={{
passphrase: {
salt: 'nonempty', salt: 'nonempty',
iterations: 2, iterations: 2,
} } } },
}}
/>, />,
); );
const e = { target: { value: "a" } }; const e = { target: { value: "a" } };

View file

@ -245,8 +245,7 @@ describe('MemberEventListSummary', function() {
); );
}); });
it('truncates multiple sequences of repetitions with other events between', it('truncates multiple sequences of repetitions with other events between', function() {
function() {
const events = generateEvents([ const events = generateEvents([
{ {
userId: "@user_1:some.domain", userId: "@user_1:some.domain",
@ -395,8 +394,7 @@ describe('MemberEventListSummary', function() {
); );
}); });
it('correctly orders sequences of transitions by the order of their first event', it('correctly orders sequences of transitions by the order of their first event', function() {
function() {
const events = generateEvents([ const events = generateEvents([
{ {
userId: "@user_2:some.domain", userId: "@user_2:some.domain",
@ -568,8 +566,7 @@ describe('MemberEventListSummary', function() {
); );
}); });
it('handles invitation plurals correctly when there are multiple invites', it('handles invitation plurals correctly when there are multiple invites', function() {
function() {
const events = generateEvents([ const events = generateEvents([
{ {
userId: "@user_1:some.domain", userId: "@user_1:some.domain",

View file

@ -70,8 +70,9 @@ describe('RoomList', () => {
root = ReactDOM.render( root = ReactDOM.render(
<DragDropContext> <DragDropContext>
<WrappedRoomList searchFilter="" onResize={() => {}} /> <WrappedRoomList searchFilter="" onResize={() => {}} />
</DragDropContext> </DragDropContext>,
, parentDiv); parentDiv,
);
ReactTestUtils.findRenderedComponentWithType(root, RoomList); ReactTestUtils.findRenderedComponentWithType(root, RoomList);
movingRoom = createRoom({name: 'Moving room'}); movingRoom = createRoom({name: 'Moving room'});