Merge remote-tracking branch 'origin/develop' into dbkr/recovery_keys_over_passphrases
This commit is contained in:
commit
b82a893a79
54 changed files with 996 additions and 227 deletions
|
@ -92,12 +92,23 @@ interface IProps {
|
|||
currentGroupIsNew?: boolean;
|
||||
}
|
||||
|
||||
interface IUsageLimit {
|
||||
limit_type: "monthly_active_user" | string;
|
||||
admin_contact?: string;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
mouseDown?: {
|
||||
x: number;
|
||||
y: number;
|
||||
};
|
||||
syncErrorData: any;
|
||||
syncErrorData?: {
|
||||
error: {
|
||||
data: IUsageLimit;
|
||||
errcode: string;
|
||||
};
|
||||
};
|
||||
usageLimitEventContent?: IUsageLimit;
|
||||
useCompactLayout: boolean;
|
||||
}
|
||||
|
||||
|
@ -282,7 +293,7 @@ class LoggedInView extends React.PureComponent<IProps, IState> {
|
|||
if (oldSyncState === 'PREPARED' && syncState === 'SYNCING') {
|
||||
this._updateServerNoticeEvents();
|
||||
} else {
|
||||
this._calculateServerLimitToast(data);
|
||||
this._calculateServerLimitToast(this.state.syncErrorData, this.state.usageLimitEventContent);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -293,7 +304,7 @@ class LoggedInView extends React.PureComponent<IProps, IState> {
|
|||
}
|
||||
};
|
||||
|
||||
_calculateServerLimitToast(syncErrorData, usageLimitEventContent?) {
|
||||
_calculateServerLimitToast(syncErrorData: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) {
|
||||
const error = syncErrorData && syncErrorData.error && syncErrorData.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED";
|
||||
if (error) {
|
||||
usageLimitEventContent = syncErrorData.error.data;
|
||||
|
@ -330,8 +341,9 @@ class LoggedInView extends React.PureComponent<IProps, IState> {
|
|||
e.getContent()['server_notice_type'] === 'm.server_notice.usage_limit_reached'
|
||||
);
|
||||
});
|
||||
|
||||
this._calculateServerLimitToast(this.state.syncErrorData, usageLimitEvent && usageLimitEvent.getContent());
|
||||
const usageLimitEventContent = usageLimitEvent && usageLimitEvent.getContent();
|
||||
this._calculateServerLimitToast(this.state.syncErrorData, usageLimitEventContent);
|
||||
this.setState({ usageLimitEventContent });
|
||||
};
|
||||
|
||||
_onPaste = (ev) => {
|
||||
|
|
|
@ -1900,7 +1900,12 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
return setLoggedInPromise;
|
||||
}
|
||||
|
||||
if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
|
||||
// Test for the master cross-signing key in SSSS as a quick proxy for
|
||||
// whether cross-signing has been set up on the account.
|
||||
const masterKeyInStorage = !!cli.getAccountData("m.cross_signing.master");
|
||||
if (masterKeyInStorage) {
|
||||
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
|
||||
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
|
||||
this.setStateForNewView({ view: Views.E2E_SETUP });
|
||||
} else {
|
||||
this.onLoggedIn();
|
||||
|
@ -1919,7 +1924,10 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
|||
// console.log(`Rendering MatrixChat with view ${this.state.view}`);
|
||||
|
||||
let fragmentAfterLogin = "";
|
||||
if (this.props.initialScreenAfterLogin) {
|
||||
if (this.props.initialScreenAfterLogin &&
|
||||
// XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop
|
||||
!["welcome", "login", "register"].includes(this.props.initialScreenAfterLogin.screen)
|
||||
) {
|
||||
fragmentAfterLogin = `/${this.props.initialScreenAfterLogin.screen}`;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,6 +34,30 @@ import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResiz
|
|||
const CONTINUATION_MAX_INTERVAL = 5 * 60 * 1000; // 5 minutes
|
||||
const continuedTypes = ['m.sticker', 'm.room.message'];
|
||||
|
||||
// check if there is a previous event and it has the same sender as this event
|
||||
// and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL
|
||||
function shouldFormContinuation(prevEvent, mxEvent) {
|
||||
// sanity check inputs
|
||||
if (!prevEvent || !prevEvent.sender || !mxEvent.sender) return false;
|
||||
// check if within the max continuation period
|
||||
if (mxEvent.getTs() - prevEvent.getTs() > CONTINUATION_MAX_INTERVAL) return false;
|
||||
|
||||
// Some events should appear as continuations from previous events of different types.
|
||||
if (mxEvent.getType() !== prevEvent.getType() &&
|
||||
(!continuedTypes.includes(mxEvent.getType()) ||
|
||||
!continuedTypes.includes(prevEvent.getType()))) return false;
|
||||
|
||||
// Check if the sender is the same and hasn't changed their displayname/avatar between these events
|
||||
if (mxEvent.sender.userId !== prevEvent.sender.userId ||
|
||||
mxEvent.sender.name !== prevEvent.sender.name ||
|
||||
mxEvent.sender.getMxcAvatarUrl() !== prevEvent.sender.getMxcAvatarUrl()) return false;
|
||||
|
||||
// if we don't have tile for previous event then it was shown by showHiddenEvents and has no SenderProfile
|
||||
if (!haveTileForEvent(prevEvent)) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const isMembershipChange = (e) => e.getType() === 'm.room.member' || e.getType() === 'm.room.third_party_invite';
|
||||
|
||||
/* (almost) stateless UI component which builds the event tiles in the room timeline.
|
||||
|
@ -515,39 +539,6 @@ export default class MessagePanel extends React.Component {
|
|||
|
||||
const isEditing = this.props.editState &&
|
||||
this.props.editState.getEvent().getId() === mxEv.getId();
|
||||
// is this a continuation of the previous message?
|
||||
let continuation = false;
|
||||
|
||||
// Some events should appear as continuations from previous events of
|
||||
// different types.
|
||||
|
||||
const eventTypeContinues =
|
||||
prevEvent !== null &&
|
||||
continuedTypes.includes(mxEv.getType()) &&
|
||||
continuedTypes.includes(prevEvent.getType());
|
||||
|
||||
// if there is a previous event and it has the same sender as this event
|
||||
// and the types are the same/is in continuedTypes and the time between them is <= CONTINUATION_MAX_INTERVAL
|
||||
if (prevEvent !== null && prevEvent.sender && mxEv.sender && mxEv.sender.userId === prevEvent.sender.userId &&
|
||||
// if we don't have tile for previous event then it was shown by showHiddenEvents and has no SenderProfile
|
||||
haveTileForEvent(prevEvent) && (mxEv.getType() === prevEvent.getType() || eventTypeContinues) &&
|
||||
(mxEv.getTs() - prevEvent.getTs() <= CONTINUATION_MAX_INTERVAL)) {
|
||||
continuation = true;
|
||||
}
|
||||
|
||||
/*
|
||||
// Work out if this is still a continuation, as we are now showing commands
|
||||
// and /me messages with their own little avatar. The case of a change of
|
||||
// event type (commands) is handled above, but we need to handle the /me
|
||||
// messages seperately as they have a msgtype of 'm.emote' but are classed
|
||||
// as normal messages
|
||||
if (prevEvent !== null && prevEvent.sender && mxEv.sender
|
||||
&& mxEv.sender.userId === prevEvent.sender.userId
|
||||
&& mxEv.getType() == prevEvent.getType()
|
||||
&& prevEvent.getContent().msgtype === 'm.emote') {
|
||||
continuation = false;
|
||||
}
|
||||
*/
|
||||
|
||||
// local echoes have a fake date, which could even be yesterday. Treat them
|
||||
// as 'today' for the date separators.
|
||||
|
@ -559,12 +550,15 @@ export default class MessagePanel extends React.Component {
|
|||
}
|
||||
|
||||
// do we need a date separator since the last event?
|
||||
if (this._wantsDateSeparator(prevEvent, eventDate)) {
|
||||
const wantsDateSeparator = this._wantsDateSeparator(prevEvent, eventDate);
|
||||
if (wantsDateSeparator) {
|
||||
const dateSeparator = <li key={ts1}><DateSeparator key={ts1} ts={ts1} /></li>;
|
||||
ret.push(dateSeparator);
|
||||
continuation = false;
|
||||
}
|
||||
|
||||
// is this a continuation of the previous message?
|
||||
const continuation = !wantsDateSeparator && shouldFormContinuation(prevEvent, mxEv);
|
||||
|
||||
const eventId = mxEv.getId();
|
||||
const highlight = (eventId === this.props.highlightedEventId);
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
|||
import {sendLoginRequest} from "../../../Login";
|
||||
import AuthPage from "../../views/auth/AuthPage";
|
||||
import SSOButton from "../../views/elements/SSOButton";
|
||||
import {HOMESERVER_URL_KEY, ID_SERVER_URL_KEY} from "../../../BasePlatform";
|
||||
|
||||
const LOGIN_VIEW = {
|
||||
LOADING: 1,
|
||||
|
@ -43,7 +44,7 @@ const FLOWS_TO_VIEWS = {
|
|||
export default class SoftLogout extends React.Component {
|
||||
static propTypes = {
|
||||
// Query parameters from MatrixChat
|
||||
realQueryParams: PropTypes.object, // {homeserver, identityServer, loginToken}
|
||||
realQueryParams: PropTypes.object, // {loginToken}
|
||||
|
||||
// Called when the SSO login completes
|
||||
onTokenLoginCompleted: PropTypes.func,
|
||||
|
@ -90,7 +91,7 @@ export default class SoftLogout extends React.Component {
|
|||
|
||||
async _initLogin() {
|
||||
const queryParams = this.props.realQueryParams;
|
||||
const hasAllParams = queryParams && queryParams['homeserver'] && queryParams['loginToken'];
|
||||
const hasAllParams = queryParams && queryParams['loginToken'];
|
||||
if (hasAllParams) {
|
||||
this.setState({loginView: LOGIN_VIEW.LOADING});
|
||||
this.trySsoLogin();
|
||||
|
@ -157,8 +158,8 @@ export default class SoftLogout extends React.Component {
|
|||
async trySsoLogin() {
|
||||
this.setState({busy: true});
|
||||
|
||||
const hsUrl = this.props.realQueryParams['homeserver'];
|
||||
const isUrl = this.props.realQueryParams['identityServer'] || MatrixClientPeg.get().getIdentityServerUrl();
|
||||
const hsUrl = localStorage.getItem(HOMESERVER_URL_KEY);
|
||||
const isUrl = localStorage.getItem(ID_SERVER_URL_KEY) || MatrixClientPeg.get().getIdentityServerUrl();
|
||||
const loginType = "m.login.token";
|
||||
const loginParams = {
|
||||
token: this.props.realQueryParams['loginToken'],
|
||||
|
|
|
@ -24,6 +24,7 @@ import withValidation from '../elements/Validation';
|
|||
import { _t } from '../../../languageHandler';
|
||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||
import {Key} from "../../../Keyboard";
|
||||
import {privateShouldBeEncrypted} from "../../../createRoom";
|
||||
|
||||
export default createReactClass({
|
||||
displayName: 'CreateRoomDialog',
|
||||
|
@ -36,7 +37,7 @@ export default createReactClass({
|
|||
const config = SdkConfig.get();
|
||||
return {
|
||||
isPublic: this.props.defaultPublic || false,
|
||||
isEncrypted: true,
|
||||
isEncrypted: privateShouldBeEncrypted(),
|
||||
name: "",
|
||||
topic: "",
|
||||
alias: "",
|
||||
|
@ -193,6 +194,13 @@ export default createReactClass({
|
|||
|
||||
let e2eeSection;
|
||||
if (!this.state.isPublic) {
|
||||
let microcopy;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
microcopy = _t("You can’t disable this later. Bridges & most bots won’t work yet.");
|
||||
} else {
|
||||
microcopy = _t("Your server admin has disabled end-to-end encryption by default " +
|
||||
"in private rooms & Direct Messages.");
|
||||
}
|
||||
e2eeSection = <React.Fragment>
|
||||
<LabelledToggleSwitch
|
||||
label={ _t("Enable end-to-end encryption")}
|
||||
|
@ -200,7 +208,7 @@ export default createReactClass({
|
|||
value={this.state.isEncrypted}
|
||||
className='mx_CreateRoomDialog_e2eSwitch' // for end-to-end tests
|
||||
/>
|
||||
<p>{ _t("You can’t disable this later. Bridges & most bots won’t work yet.") }</p>
|
||||
<p>{ microcopy }</p>
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ import dis from "../../../dispatcher/dispatcher";
|
|||
import IdentityAuthClient from "../../../IdentityAuthClient";
|
||||
import Modal from "../../../Modal";
|
||||
import {humanizeTime} from "../../../utils/humanize";
|
||||
import createRoom, {canEncryptToAllUsers} from "../../../createRoom";
|
||||
import createRoom, {canEncryptToAllUsers, privateShouldBeEncrypted} from "../../../createRoom";
|
||||
import {inviteMultipleToRoom} from "../../../RoomInvite";
|
||||
import {Key} from "../../../Keyboard";
|
||||
import {Action} from "../../../dispatcher/actions";
|
||||
|
@ -575,14 +575,16 @@ export default class InviteDialog extends React.PureComponent {
|
|||
|
||||
const createRoomOptions = {inlineErrors: true};
|
||||
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
|
||||
if (!has3PidMembers) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const has3PidMembers = targets.some(t => t instanceof ThreepidMember);
|
||||
if (!has3PidMembers) {
|
||||
const client = MatrixClientPeg.get();
|
||||
const allHaveDeviceKeys = await canEncryptToAllUsers(client, targetIds);
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ export default class UploadConfirmDialog extends React.Component {
|
|||
preview = <div>
|
||||
<div>
|
||||
<img className="mx_UploadConfirmDialog_fileIcon"
|
||||
src={require("../../../../res/img/files.png")}
|
||||
src={require("../../../../res/img/feather-customised/files.svg")}
|
||||
/>
|
||||
{this.props.file.name} ({filesize(this.props.file.size)})
|
||||
</div>
|
||||
|
|
|
@ -227,8 +227,10 @@ export default class RestoreKeyBackupDialog extends React.PureComponent {
|
|||
loadError: null,
|
||||
});
|
||||
try {
|
||||
const backupInfo = await MatrixClientPeg.get().getKeyBackupVersion();
|
||||
const backupKeyStored = await MatrixClientPeg.get().isKeyBackupKeyStored();
|
||||
const cli = MatrixClientPeg.get();
|
||||
const backupInfo = await cli.getKeyBackupVersion();
|
||||
const has4S = await cli.hasSecretStorageKey();
|
||||
const backupKeyStored = has4S && await cli.isKeyBackupKeyStored();
|
||||
this.setState({
|
||||
backupInfo,
|
||||
backupKeyStored,
|
||||
|
|
|
@ -15,8 +15,6 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||
|
@ -27,6 +25,7 @@ import AccessibleButton from "./AccessibleButton";
|
|||
import Modal from "../../../Modal";
|
||||
import * as sdk from "../../../index";
|
||||
import {Key} from "../../../Keyboard";
|
||||
import FocusLock from "react-focus-lock";
|
||||
|
||||
export default class ImageView extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -50,16 +49,6 @@ export default class ImageView extends React.Component {
|
|||
this.state = { rotationDegrees: 0 };
|
||||
}
|
||||
|
||||
// XXX: keyboard shortcuts for managing dialogs should be done by the modal
|
||||
// dialog base class somehow, surely...
|
||||
componentDidMount() {
|
||||
document.addEventListener("keydown", this.onKeyDown);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener("keydown", this.onKeyDown);
|
||||
}
|
||||
|
||||
onKeyDown = (ev) => {
|
||||
if (ev.key === Key.ESCAPE) {
|
||||
ev.stopPropagation();
|
||||
|
@ -195,7 +184,14 @@ export default class ImageView extends React.Component {
|
|||
const effectiveStyle = {transform: `rotate(${rotationDegrees}deg)`, ...style};
|
||||
|
||||
return (
|
||||
<div className="mx_ImageView">
|
||||
<FocusLock
|
||||
returnFocus={true}
|
||||
lockProps={{
|
||||
onKeyDown: this.onKeyDown,
|
||||
role: "dialog",
|
||||
}}
|
||||
className="mx_ImageView"
|
||||
>
|
||||
<div className="mx_ImageView_lhs">
|
||||
</div>
|
||||
<div className="mx_ImageView_content">
|
||||
|
@ -231,7 +227,7 @@ export default class ImageView extends React.Component {
|
|||
</div>
|
||||
<div className="mx_ImageView_rhs">
|
||||
</div>
|
||||
</div>
|
||||
</FocusLock>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,7 +94,7 @@ export default class Tooltip extends React.Component<IProps> {
|
|||
return style;
|
||||
}
|
||||
|
||||
private renderTooltip() {
|
||||
private renderTooltip = () => {
|
||||
// Add the parent's position to the tooltips, so it's correctly
|
||||
// positioned, also taking into account any window zoom
|
||||
// NOTE: The additional 6 pixels for the left position, is to take account of the
|
||||
|
|
|
@ -27,8 +27,7 @@ const QUICK_REACTIONS = ["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀
|
|||
if (!data) {
|
||||
throw new Error(`Emoji ${emoji} doesn't exist in emojibase`);
|
||||
}
|
||||
// Prefer our unicode value for quick reactions as we sometimes use variation selectors.
|
||||
return Object.assign({}, data, { unicode: emoji });
|
||||
return data;
|
||||
});
|
||||
|
||||
class QuickReactions extends React.Component {
|
||||
|
|
|
@ -25,7 +25,7 @@ import dis from '../../../dispatcher/dispatcher';
|
|||
import Modal from '../../../Modal';
|
||||
import * as sdk from '../../../index';
|
||||
import { _t } from '../../../languageHandler';
|
||||
import createRoom from '../../../createRoom';
|
||||
import createRoom, {privateShouldBeEncrypted} from '../../../createRoom';
|
||||
import DMRoomMap from '../../../utils/DMRoomMap';
|
||||
import AccessibleButton from '../elements/AccessibleButton';
|
||||
import SdkConfig from '../../../SdkConfig';
|
||||
|
@ -108,15 +108,17 @@ async function openDMForUser(matrixClient, userId) {
|
|||
dmUserId: userId,
|
||||
};
|
||||
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
|
||||
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
|
||||
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
|
||||
return Object.keys(devices).length > 0;
|
||||
});
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
if (privateShouldBeEncrypted()) {
|
||||
// Check whether all users have uploaded device keys before.
|
||||
// If so, enable encryption in the new room.
|
||||
const usersToDevicesMap = await matrixClient.downloadKeys([userId]);
|
||||
const allHaveDeviceKeys = Object.values(usersToDevicesMap).every(devices => {
|
||||
// `devices` is an object of the form { deviceId: deviceInfo, ... }.
|
||||
return Object.keys(devices).length > 0;
|
||||
});
|
||||
if (allHaveDeviceKeys) {
|
||||
createRoomOptions.encryption = true;
|
||||
}
|
||||
}
|
||||
|
||||
createRoom(createRoomOptions);
|
||||
|
|
|
@ -260,7 +260,7 @@ export default class Autocomplete extends React.PureComponent<IProps, IState> {
|
|||
behavior: "auto",
|
||||
block: "nearest",
|
||||
});
|
||||
} else {
|
||||
} else if (this.containerRef.current) {
|
||||
this.containerRef.current.scrollTo({ top: 0 });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,7 @@ export function getHandlerTile(ev) {
|
|||
// fall back to showing hidden events, if we're viewing hidden events
|
||||
// XXX: This is extremely a hack. Possibly these components should have an interface for
|
||||
// declining to render?
|
||||
if (type === "m.key.verification.cancel" && SettingsStore.getValue("showHiddenEventsInTimeline")) {
|
||||
if (type === "m.key.verification.cancel" || type === "m.key.verification.done") {
|
||||
const MKeyVerificationConclusion = sdk.getComponent("messages.MKeyVerificationConclusion");
|
||||
if (!MKeyVerificationConclusion.prototype._shouldRender.call(null, ev, ev.request)) {
|
||||
return;
|
||||
|
|
|
@ -28,6 +28,8 @@ import { Dispatcher } from "flux";
|
|||
import dis from "../../../dispatcher/dispatcher";
|
||||
import RoomSublist2 from "./RoomSublist2";
|
||||
import { ActionPayload } from "../../../dispatcher/payloads";
|
||||
import { IFilterCondition } from "../../../stores/room-list/filters/IFilterCondition";
|
||||
import { NameFilterCondition } from "../../../stores/room-list/filters/NameFilterCondition";
|
||||
|
||||
/*******************************************************************
|
||||
* CAUTION *
|
||||
|
@ -130,6 +132,7 @@ export default class RoomList2 extends React.Component<IProps, IState> {
|
|||
private sublistCollapseStates: { [tagId: string]: boolean } = {};
|
||||
private unfilteredLayout: Layout;
|
||||
private filteredLayout: Layout;
|
||||
private searchFilter: NameFilterCondition = new NameFilterCondition();
|
||||
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
@ -139,6 +142,21 @@ export default class RoomList2 extends React.Component<IProps, IState> {
|
|||
this.prepareLayouts();
|
||||
}
|
||||
|
||||
public componentDidUpdate(prevProps: Readonly<IProps>): void {
|
||||
if (prevProps.searchFilter !== this.props.searchFilter) {
|
||||
const hadSearch = !!this.searchFilter.search.trim();
|
||||
const haveSearch = !!this.props.searchFilter.trim();
|
||||
this.searchFilter.search = this.props.searchFilter;
|
||||
if (!hadSearch && haveSearch) {
|
||||
// started a new filter - add the condition
|
||||
RoomListStore.instance.addFilter(this.searchFilter);
|
||||
} else if (hadSearch && !haveSearch) {
|
||||
// cleared a filter - remove the condition
|
||||
RoomListStore.instance.removeFilter(this.searchFilter);
|
||||
} // else the filter hasn't changed enough for us to care here
|
||||
}
|
||||
}
|
||||
|
||||
public componentDidMount(): void {
|
||||
RoomListStore.instance.on(LISTS_UPDATE_EVENT, (store) => {
|
||||
console.log("new lists", store.orderedLists);
|
||||
|
|
|
@ -40,8 +40,8 @@ export default class ProfileSettings extends React.Component {
|
|||
if (avatarUrl) avatarUrl = client.mxcUrlToHttp(avatarUrl, 96, 96, 'crop', false);
|
||||
this.state = {
|
||||
userId: user.userId,
|
||||
originalDisplayName: user.displayName,
|
||||
displayName: user.displayName,
|
||||
originalDisplayName: user.rawDisplayName,
|
||||
displayName: user.rawDisplayName,
|
||||
originalAvatarUrl: avatarUrl,
|
||||
avatarUrl: avatarUrl,
|
||||
avatarFile: null,
|
||||
|
|
|
@ -26,6 +26,7 @@ import Modal from "../../../../../Modal";
|
|||
import * as sdk from "../../../../..";
|
||||
import {sleep} from "../../../../../utils/promise";
|
||||
import dis from "../../../../../dispatcher/dispatcher";
|
||||
import {privateShouldBeEncrypted} from "../../../../../createRoom";
|
||||
|
||||
export class IgnoredUser extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -317,8 +318,17 @@ export default class SecurityUserSettingsTab extends React.Component {
|
|||
|
||||
const E2eAdvancedPanel = sdk.getComponent('views.settings.E2eAdvancedPanel');
|
||||
|
||||
let warning;
|
||||
if (!privateShouldBeEncrypted()) {
|
||||
warning = <div className="mx_SecurityUserSettingsTab_warning">
|
||||
{ _t("Your server admin has disabled end-to-end encryption by default " +
|
||||
"in private rooms & Direct Messages.") }
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab mx_SecurityUserSettingsTab">
|
||||
{warning}
|
||||
<div className="mx_SettingsTab_heading">{_t("Security & Privacy")}</div>
|
||||
<div className="mx_SettingsTab_section">
|
||||
<span className="mx_SettingsTab_subheading">{_t("Where you’re logged in")}</span>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue