Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into travis/download-logs

This commit is contained in:
Michael Telatynski 2020-08-12 22:48:03 +01:00
commit df980dbf92
173 changed files with 1409 additions and 1502 deletions

View file

@ -19,13 +19,14 @@ import ContentMessages from "../ContentMessages";
import { IMatrixClientPeg } from "../MatrixClientPeg";
import ToastStore from "../stores/ToastStore";
import DeviceListener from "../DeviceListener";
import RebrandListener from "../RebrandListener";
import { RoomListStoreClass } from "../stores/room-list/RoomListStore";
import { PlatformPeg } from "../PlatformPeg";
import RoomListLayoutStore from "../stores/room-list/RoomListLayoutStore";
import {IntegrationManagers} from "../integrations/IntegrationManagers";
import {ModalManager} from "../Modal";
import SettingsStore from "../settings/SettingsStore";
import {ActiveRoomObserver} from "../ActiveRoomObserver";
import {Notifier} from "../Notifier";
declare global {
interface Window {
@ -38,13 +39,14 @@ declare global {
mxContentMessages: ContentMessages;
mxToastStore: ToastStore;
mxDeviceListener: DeviceListener;
mxRebrandListener: RebrandListener;
mxRoomListStore: RoomListStoreClass;
mxRoomListLayoutStore: RoomListLayoutStore;
mxActiveRoomObserver: ActiveRoomObserver;
mxPlatformPeg: PlatformPeg;
mxIntegrationManagers: typeof IntegrationManagers;
singletonModalManager: ModalManager;
mxSettingsStore: SettingsStore;
mxNotifier: typeof Notifier;
}
// workaround for https://github.com/microsoft/TypeScript/issues/30933
@ -77,4 +79,8 @@ declare global {
interface PromiseConstructor {
allSettled<T>(promises: Promise<T>[]): Promise<Array<ISettledFulfilled<T> | ISettledRejected>>;
}
interface HTMLAudioElement {
type?: string;
}
}

View file

@ -16,6 +16,8 @@ limitations under the License.
import RoomViewStore from './stores/RoomViewStore';
type Listener = (isActive: boolean) => void;
/**
* Consumes changes from the RoomViewStore and notifies specific things
* about when the active room changes. Unlike listening for RoomViewStore
@ -25,57 +27,57 @@ import RoomViewStore from './stores/RoomViewStore';
* TODO: If we introduce an observer for something else, factor out
* the adding / removing of listeners & emitting into a common class.
*/
class ActiveRoomObserver {
constructor() {
this._listeners = {}; // key=roomId, value=function(isActive:boolean)
export class ActiveRoomObserver {
private listeners: {[key: string]: Listener[]} = {};
private _activeRoomId = RoomViewStore.getRoomId();
private readonly roomStoreToken: string;
this._activeRoomId = RoomViewStore.getRoomId();
// TODO: We could self-destruct when the last listener goes away, or at least
// stop listening.
this._roomStoreToken = RoomViewStore.addListener(this._onRoomViewStoreUpdate.bind(this));
constructor() {
// TODO: We could self-destruct when the last listener goes away, or at least stop listening.
this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate);
}
get activeRoomId(): string {
public get activeRoomId(): string {
return this._activeRoomId;
}
addListener(roomId, listener) {
if (!this._listeners[roomId]) this._listeners[roomId] = [];
this._listeners[roomId].push(listener);
public addListener(roomId, listener) {
if (!this.listeners[roomId]) this.listeners[roomId] = [];
this.listeners[roomId].push(listener);
}
removeListener(roomId, listener) {
if (this._listeners[roomId]) {
const i = this._listeners[roomId].indexOf(listener);
public removeListener(roomId, listener) {
if (this.listeners[roomId]) {
const i = this.listeners[roomId].indexOf(listener);
if (i > -1) {
this._listeners[roomId].splice(i, 1);
this.listeners[roomId].splice(i, 1);
}
} else {
console.warn("Unregistering unrecognised listener (roomId=" + roomId + ")");
}
}
_emit(roomId, isActive: boolean) {
if (!this._listeners[roomId]) return;
private emit(roomId, isActive: boolean) {
if (!this.listeners[roomId]) return;
for (const l of this._listeners[roomId]) {
for (const l of this.listeners[roomId]) {
l.call(null, isActive);
}
}
_onRoomViewStoreUpdate() {
private onRoomViewStoreUpdate = () => {
// emit for the old room ID
if (this._activeRoomId) this._emit(this._activeRoomId, false);
if (this._activeRoomId) this.emit(this._activeRoomId, false);
// update our cache
this._activeRoomId = RoomViewStore.getRoomId();
// and emit for the new one
if (this._activeRoomId) this._emit(this._activeRoomId, true);
}
if (this._activeRoomId) this.emit(this._activeRoomId, true);
};
}
if (global.mx_ActiveRoomObserver === undefined) {
global.mx_ActiveRoomObserver = new ActiveRoomObserver();
if (window.mxActiveRoomObserver === undefined) {
window.mxActiveRoomObserver = new ActiveRoomObserver();
}
export default global.mx_ActiveRoomObserver;
export default window.mxActiveRoomObserver;

View file

@ -41,7 +41,7 @@ export default createReactClass({
componentDidMount: function() {
this._unmounted = false;
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
// https://github.com/vector-im/element-web/issues/3148
console.log('Starting load of AsyncWrapper for modal');
this.props.prom.then((result) => {
if (this._unmounted) {

View file

@ -156,6 +156,14 @@ export default abstract class BasePlatform {
loudNotification(ev: Event, room: Object) {
}
clearNotification(notif: Notification) {
// Some browsers don't support this, e.g Safari on iOS
// https://developer.mozilla.org/en-US/docs/Web/API/Notification/close
if (notif.close) {
notif.close();
}
}
/**
* Returns a promise that resolves to a string representing the current version of the application.
*/

View file

@ -90,7 +90,7 @@ function play(audioId) {
// This is usually because the user hasn't interacted with the document,
// or chrome doesn't think so and is denying the request. Not sure what
// we can really do here...
// https://github.com/vector-im/riot-web/issues/7657
// https://github.com/vector-im/element-web/issues/7657
console.log("Unable to play audio clip", e);
}
};
@ -474,15 +474,15 @@ const callHandler = {
/**
* The conference handler is a module that deals with implementation-specific
* multi-party calling implementations. Riot passes in its own which creates
* multi-party calling implementations. Element passes in its own which creates
* a one-to-one call with a freeswitch conference bridge. As of July 2018,
* the de-facto way of conference calling is a Jitsi widget, so this is
* deprecated. It reamins here for two reasons:
* 1. So Riot still supports joining existing freeswitch conference calls
* 1. So Element still supports joining existing freeswitch conference calls
* (but doesn't support creating them). After a transition period, we can
* remove support for joining them too.
* 2. To hide the one-to-one rooms that old-style conferencing creates. This
* is much harder to remove: probably either we make Riot leave & forget these
* is much harder to remove: probably either we make Element leave & forget these
* rooms after we remove support for joining freeswitch conferences, or we
* accept that random rooms with cryptic users will suddently appear for
* anyone who's ever used conference calling, or we are stuck with this

View file

@ -129,27 +129,21 @@ const onSecretRequested = async function({
console.log(`CrossSigningManager: Ignoring request from untrusted device ${deviceId}`);
return;
}
if (name.startsWith("m.cross_signing")) {
if (
name === "m.cross_signing.master" ||
name === "m.cross_signing.self_signing" ||
name === "m.cross_signing.user_signing"
) {
const callbacks = client.getCrossSigningCacheCallbacks();
if (!callbacks.getCrossSigningKeyCache) return;
/* Explicit enumeration here is deliberate never share the master key! */
if (name === "m.cross_signing.self_signing") {
const key = await callbacks.getCrossSigningKeyCache("self_signing");
if (!key) {
console.log(
`self_signing requested by ${deviceId}, but not found in cache`,
);
}
return key && encodeBase64(key);
} else if (name === "m.cross_signing.user_signing") {
const key = await callbacks.getCrossSigningKeyCache("user_signing");
if (!key) {
console.log(
`user_signing requested by ${deviceId}, but not found in cache`,
);
}
return key && encodeBase64(key);
const keyId = name.replace("m.cross_signing.", "");
const key = await callbacks.getCrossSigningKeyCache(keyId);
if (!key) {
console.log(
`${keyId} requested by ${deviceId}, but not found in cache`,
);
}
return key && encodeBase64(key);
} else if (name === "m.megolm_backup.v1") {
const key = await client._crypto.getSessionBackupPrivateKey();
if (!key) {

View file

@ -421,7 +421,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
}
let formattedBody = typeof content.formatted_body === 'string' ? content.formatted_body : null;
const plainBody = typeof content.body === 'string' ? content.body : null;
const plainBody = typeof content.body === 'string' ? content.body : "";
if (opts.stripReplyFallback && formattedBody) formattedBody = ReplyThread.stripHTMLReply(formattedBody);
strippedBody = opts.stripReplyFallback ? ReplyThread.stripPlainReply(plainBody) : plainBody;

View file

@ -177,7 +177,7 @@ export default class IdentityAuthClient {
// appropriately. We already clear storage on sign out, but we'll need
// additional clearing when changing ISes in settings as part of future
// privacy work.
// See also https://github.com/vector-im/riot-web/issues/10455.
// See also https://github.com/vector-im/element-web/issues/10455.
}
async registerForToken(check=true) {

View file

@ -40,7 +40,6 @@ import ToastStore from "./stores/ToastStore";
import {IntegrationManagers} from "./integrations/IntegrationManagers";
import {Mjolnir} from "./mjolnir/Mjolnir";
import DeviceListener from "./DeviceListener";
import RebrandListener from "./RebrandListener";
import {Jitsi} from "./widgets/Jitsi";
import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "./BasePlatform";
@ -647,8 +646,6 @@ async function startMatrixClient(startSyncing=true) {
// Now that we have a MatrixClientPeg, update the Jitsi info
await Jitsi.getInstance().start();
RebrandListener.sharedInstance().start();
// dispatch that we finished starting up to wire up any other bits
// of the matrix client that cannot be set prior to starting up.
dis.dispatch({action: 'client_started'});
@ -710,7 +707,6 @@ export function stopMatrixClient(unsetClient=true) {
IntegrationManagers.sharedInstance().stopWatching();
Mjolnir.sharedInstance().stop();
DeviceListener.sharedInstance().stop();
RebrandListener.sharedInstance().stop();
if (DMRoomMap.shared()) DMRoomMap.shared().stop();
EventIndexPeg.stop();
const cli = MatrixClientPeg.get();

View file

@ -99,7 +99,7 @@ export default class Markdown {
// puts softbreaks in for multiple lines in a blockquote,
// so if these are just newline characters then the
// block quote ends up all on one line
// (https://github.com/vector-im/riot-web/issues/3154)
// (https://github.com/vector-im/element-web/issues/3154)
softbreak: '<br />',
});
@ -166,7 +166,7 @@ export default class Markdown {
* Render the markdown message to plain text. That is, essentially
* just remove any backslashes escaping what would otherwise be
* markdown syntax
* (to fix https://github.com/vector-im/riot-web/issues/2870).
* (to fix https://github.com/vector-im/element-web/issues/2870).
*
* N.B. this does **NOT** render arbitrary MD to plain text - only MD
* which has no formatting. Otherwise it emits HTML(!).

View file

@ -319,7 +319,7 @@ export class ModalManager {
private reRender() {
if (this.modals.length === 0 && !this.priorityModal && !this.staticModal) {
// If there is no modal to render, make all of Riot available
// If there is no modal to render, make all of Element available
// to screen reader users again
dis.dispatch({
action: 'aria_unhide_main_app',

View file

@ -17,6 +17,9 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { Room } from "matrix-js-sdk/src/models/room";
import { MatrixClientPeg } from './MatrixClientPeg';
import SdkConfig from './SdkConfig';
import PlatformPeg from './PlatformPeg';
@ -28,9 +31,7 @@ import * as sdk from './index';
import { _t } from './languageHandler';
import Modal from './Modal';
import SettingsStore from "./settings/SettingsStore";
import {
hideToast as hideNotificationsToast,
} from "./toasts/DesktopNotificationsToast";
import { hideToast as hideNotificationsToast } from "./toasts/DesktopNotificationsToast";
import {SettingLevel} from "./settings/SettingLevel";
/*
@ -55,7 +56,7 @@ const typehandlers = {
},
};
const Notifier = {
export const Notifier = {
notifsByRoom: {},
// A list of event IDs that we've received but need to wait until
@ -63,14 +64,14 @@ const Notifier = {
// or not
pendingEncryptedEventIds: [],
notificationMessageForEvent: function(ev) {
notificationMessageForEvent: function(ev: MatrixEvent) {
if (typehandlers.hasOwnProperty(ev.getContent().msgtype)) {
return typehandlers[ev.getContent().msgtype](ev);
}
return TextForEvent.textForEvent(ev);
},
_displayPopupNotification: function(ev, room) {
_displayPopupNotification: function(ev: MatrixEvent, room: Room) {
const plaf = PlatformPeg.get();
if (!plaf) {
return;
@ -125,7 +126,7 @@ const Notifier = {
}
},
getSoundForRoom: function(roomId) {
getSoundForRoom: function(roomId: string) {
// We do no caching here because the SDK caches setting
// and the browser will cache the sound.
const content = SettingsStore.getValue("notificationSound", roomId);
@ -153,12 +154,13 @@ const Notifier = {
};
},
_playAudioNotification: async function(ev, room) {
_playAudioNotification: async function(ev: MatrixEvent, room: Room) {
const sound = this.getSoundForRoom(room.roomId);
console.log(`Got sound ${sound && sound.name || "default"} for ${room.roomId}`);
try {
const selector = document.querySelector(sound ? `audio[src='${sound.url}']` : "#messageAudio");
const selector =
document.querySelector<HTMLAudioElement>(sound ? `audio[src='${sound.url}']` : "#messageAudio");
let audioElement = selector;
if (!selector) {
if (!sound) {
@ -207,7 +209,7 @@ const Notifier = {
return plaf && plaf.supportsNotifications();
},
setEnabled: function(enable, callback) {
setEnabled: function(enable: boolean, callback?: () => void) {
const plaf = PlatformPeg.get();
if (!plaf) return;
@ -277,10 +279,11 @@ const Notifier = {
},
isAudioEnabled: function() {
return this.isEnabled() && SettingsStore.getValue("audioNotificationsEnabled");
// We don't route Audio via the HTML Notifications API so it is possible regardless of other things
return SettingsStore.getValue("audioNotificationsEnabled");
},
setToolbarHidden: function(hidden, persistent = true) {
setToolbarHidden: function(hidden: boolean, persistent = true) {
this.toolbarHidden = hidden;
Analytics.trackEvent('Notifier', 'Set Toolbar Hidden', hidden);
@ -289,7 +292,7 @@ const Notifier = {
// update the info to localStorage for persistent settings
if (persistent && global.localStorage) {
global.localStorage.setItem("notifications_hidden", hidden);
global.localStorage.setItem("notifications_hidden", String(hidden));
}
},
@ -312,7 +315,7 @@ const Notifier = {
return this.toolbarHidden;
},
onSyncStateChange: function(state) {
onSyncStateChange: function(state: string) {
if (state === "SYNCING") {
this.isSyncing = true;
} else if (state === "STOPPED" || state === "ERROR") {
@ -320,7 +323,7 @@ const Notifier = {
}
},
onEvent: function(ev) {
onEvent: function(ev: MatrixEvent) {
if (!this.isSyncing) return; // don't alert for any messages initially
if (ev.sender && ev.sender.userId === MatrixClientPeg.get().credentials.userId) return;
@ -338,7 +341,7 @@ const Notifier = {
this._evaluateEvent(ev);
},
onEventDecrypted: function(ev) {
onEventDecrypted: function(ev: MatrixEvent) {
// 'decrypted' means the decryption process has finished: it may have failed,
// in which case it might decrypt soon if the keys arrive
if (ev.isDecryptionFailure()) return;
@ -350,7 +353,7 @@ const Notifier = {
this._evaluateEvent(ev);
},
onRoomReceipt: function(ev, room) {
onRoomReceipt: function(ev: MatrixEvent, room: Room) {
if (room.getUnreadNotificationCount() === 0) {
// ideally we would clear each notification when it was read,
// but we have no way, given a read receipt, to know whether
@ -383,8 +386,8 @@ const Notifier = {
},
};
if (!global.mxNotifier) {
global.mxNotifier = Notifier;
if (!window.mxNotifier) {
window.mxNotifier = Notifier;
}
export default global.mxNotifier;
export default window.mxNotifier;

View file

@ -1,184 +0,0 @@
/*
Copyright 2020 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import SdkConfig from "./SdkConfig";
import ToastStore from "./stores/ToastStore";
import GenericToast from "./components/views/toasts/GenericToast";
import RebrandDialog from "./components/views/dialogs/RebrandDialog";
import { RebrandDialogKind } from "./components/views/dialogs/RebrandDialog";
import Modal from './Modal';
import { _t } from './languageHandler';
const TOAST_KEY = 'rebrand';
const NAG_INTERVAL = 24 * 60 * 60 * 1000;
function getRedirectUrl(url): string {
const redirectUrl = new URL(url);
redirectUrl.hash = '';
if (SdkConfig.get()['redirectToNewBrandUrl']) {
const newUrl = new URL(SdkConfig.get()['redirectToNewBrandUrl']);
if (url.hostname !== newUrl.hostname || url.pathname !== newUrl.pathname) {
redirectUrl.hostname = newUrl.hostname;
redirectUrl.pathname = newUrl.pathname;
return redirectUrl.toString();
}
return null;
} else if (url.hostname === 'riot.im') {
if (url.pathname.startsWith('/app')) {
redirectUrl.hostname = 'app.element.io';
redirectUrl.pathname = '/';
} else if (url.pathname.startsWith('/staging')) {
redirectUrl.hostname = 'staging.element.io';
redirectUrl.pathname = '/';
} else if (url.pathname.startsWith('/develop')) {
redirectUrl.hostname = 'develop.element.io';
redirectUrl.pathname = '/';
}
return redirectUrl.href;
} else if (url.hostname.endsWith('.riot.im')) {
redirectUrl.hostname = url.hostname.substr(0, url.hostname.length - '.riot.im'.length) + '.element.io';
return redirectUrl.href;
} else {
return null;
}
}
/**
* Shows toasts informing the user that the name of the app has changed and,
* potentially, that they should head to a different URL and log in there
*/
export default class RebrandListener {
private _reshowTimer?: number;
private nagAgainAt?: number = null;
static sharedInstance() {
if (!window.mxRebrandListener) window.mxRebrandListener = new RebrandListener();
return window.mxRebrandListener;
}
constructor() {
this._reshowTimer = null;
}
start() {
this.recheck();
}
stop() {
if (this._reshowTimer) {
clearTimeout(this._reshowTimer);
this._reshowTimer = null;
}
}
onNagToastLearnMore = async () => {
const [doneClicked] = await Modal.createDialog(RebrandDialog, {
kind: RebrandDialogKind.NAG,
targetUrl: getRedirectUrl(window.location),
}).finished;
if (doneClicked) {
// open in new tab: they should come back here & log out
window.open(getRedirectUrl(window.location), '_blank');
}
// whatever the user clicks, we go away & nag again after however long:
// If they went to the new URL, we want to nag them to log out if they
// come back to this tab, and if they clicked, 'remind me later' we want
// to, well, remind them later.
this.nagAgainAt = Date.now() + NAG_INTERVAL;
this.recheck();
};
onOneTimeToastLearnMore = async () => {
const [doneClicked] = await Modal.createDialog(RebrandDialog, {
kind: RebrandDialogKind.ONE_TIME,
}).finished;
if (doneClicked) {
localStorage.setItem('mx_rename_dialog_dismissed', 'true');
this.recheck();
}
};
onOneTimeToastDismiss = async () => {
localStorage.setItem('mx_rename_dialog_dismissed', 'true');
this.recheck();
};
onNagTimerFired = () => {
this._reshowTimer = null;
this.nagAgainAt = null;
this.recheck();
};
private async recheck() {
// There are two types of toast/dialog we show: a 'one time' informing the user that
// the app is now called a different thing but no action is required from them (they
// may need to look for a different name name/icon to launch the app but don't need to
// log in again) and a nag toast where they need to log in to the app on a different domain.
let nagToast = false;
let oneTimeToast = false;
if (getRedirectUrl(window.location)) {
if (!this.nagAgainAt) {
// if we have redirectUrl, show the nag toast
nagToast = true;
}
} else {
// otherwise we show the 'one time' toast / dialog
const renameDialogDismissed = localStorage.getItem('mx_rename_dialog_dismissed');
if (renameDialogDismissed !== 'true') {
oneTimeToast = true;
}
}
if (nagToast || oneTimeToast) {
let description;
let rejectLabel = null;
let onReject = null;
if (nagToast) {
description = _t("Use your account to sign in to the latest version");
} else {
description = _t("Were excited to announce Riot is now Element");
rejectLabel = _t("Dismiss");
onReject = this.onOneTimeToastDismiss;
}
ToastStore.sharedInstance().addOrReplaceToast({
key: TOAST_KEY,
title: _t("Riot is now Element!"),
icon: 'element_logo',
props: {
description,
acceptLabel: _t("Learn More"),
onAccept: nagToast ? this.onNagToastLearnMore : this.onOneTimeToastLearnMore,
rejectLabel,
onReject,
},
component: GenericToast,
priority: 20,
});
} else {
ToastStore.sharedInstance().dismissToast(TOAST_KEY);
}
if (!this._reshowTimer && this.nagAgainAt) {
// XXX: Our build system picks up NodeJS bindings when we need browser bindings.
this._reshowTimer = setTimeout(this.onNagTimerFired, (this.nagAgainAt - Date.now()) + 100) as any as number;
}
}
}

View file

@ -52,7 +52,7 @@ export async function startAnyRegistrationFlow(options) {
// caution though.
// XXX: ILAG is disabled for now,
// see https://github.com/vector-im/riot-web/issues/8222
// see https://github.com/vector-im/element-web/issues/8222
// const flows = await _getRegistrationFlows();
// const hasIlagFlow = flows.some((flow) => {

View file

@ -45,7 +45,7 @@ export default class Resend {
});
}, function(err) {
// XXX: temporary logging to try to diagnose
// https://github.com/vector-im/riot-web/issues/3148
// https://github.com/vector-im/element-web/issues/3148
console.log('Resend got send failure: ' + err.name + '(' + err + ')');
dis.dispatch({

View file

@ -174,7 +174,7 @@ Request:
Response:
[
{
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
type: "im.vector.modular.widgets",
state_key: "wid1",
content: {
@ -193,7 +193,7 @@ Example:
room_id: "!foo:bar",
response: [
{
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
type: "im.vector.modular.widgets",
state_key: "wid1",
content: {

View file

@ -479,7 +479,7 @@ export const Commands = [
const parsedUrl = new URL(params[0]);
const hostname = parsedUrl.host || parsedUrl.hostname; // takes first non-falsey value
// if we're using a Riot permalink handler, this will catch it before we get much further.
// if we're using a Element permalink handler, this will catch it before we get much further.
// see below where we make assumptions about parsing the URL.
if (isPermalinkHost(hostname)) {
isPermalink = true;

View file

@ -345,7 +345,7 @@ function textForCallHangupEvent(event) {
} else if (eventContent.reason === "invite_timeout") {
reason = _t('(no answer)');
} else if (eventContent.reason === "user hangup") {
// workaround for https://github.com/vector-im/riot-web/issues/5178
// workaround for https://github.com/vector-im/element-web/issues/5178
// it seems Android randomly sets a reason of "user hangup" which is
// interpreted as an error code :(
// https://github.com/vector-im/riot-android/issues/2623
@ -603,7 +603,7 @@ const stateHandlers = {
'm.room.guest_access': textForGuestAccessEvent,
'm.room.related_groups': textForRelatedGroupsEvent,
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
'im.vector.modular.widgets': textForWidgetEvent,
};

View file

@ -327,7 +327,7 @@ class Tinter {
// Vector Green as its primary color?
// --richvdh
// Yes, tinting assumes that you are using the Riot skin for now.
// Yes, tinting assumes that you are using the Element skin for now.
// The right solution will be to move the CSS over to react-sdk.
// And yes, the default assets for the base skin might as well use
// Vector Green as any other colour.

View file

@ -52,10 +52,10 @@ export function doesRoomHaveUnreadMessages(room) {
// as we don't send RRs for our own messages, make sure we special case that
// if *we* sent the last message into the room, we consider it not unread!
// Should fix: https://github.com/vector-im/riot-web/issues/3263
// https://github.com/vector-im/riot-web/issues/2427
// Should fix: https://github.com/vector-im/element-web/issues/3263
// https://github.com/vector-im/element-web/issues/2427
// ...and possibly some of the others at
// https://github.com/vector-im/riot-web/issues/3363
// https://github.com/vector-im/element-web/issues/3363
if (room.timeline.length &&
room.timeline[room.timeline.length - 1].sender &&
room.timeline[room.timeline.length - 1].sender.userId === myUserId) {

View file

@ -19,13 +19,13 @@ import {createNewMatrixCall as jsCreateNewMatrixCall, Room} from "matrix-js-sdk"
import CallHandler from './CallHandler';
import {MatrixClientPeg} from "./MatrixClientPeg";
// FIXME: this is Riot (Vector) specific code, but will be removed shortly when
// we switch over to jitsi entirely for video conferencing.
// FIXME: this is Element specific code, but will be removed shortly when we
// switch over to Jitsi entirely for video conferencing.
// FIXME: This currently forces Vector to try to hit the matrix.org AS for conferencing.
// This is bad because it prevents people running their own ASes from being used.
// This isn't permanent and will be customisable in the future: see the proposal
// at docs/conferencing.md for more info.
// FIXME: This currently forces Element to try to hit the matrix.org AS for
// conferencing. This is bad because it prevents people running their own ASes
// from being used. This isn't permanent and will be customisable in the future:
// see the proposal at docs/conferencing.md for more info.
const USER_PREFIX = "fs_";
const DOMAIN = "matrix.org";

View file

@ -76,7 +76,7 @@ export default class WidgetMessaging {
console.error(err._error);
}
// Potential XSS attack if 'msg' is not appropriately sanitized,
// as it is untrusted input by our parent window (which we assume is Riot).
// as it is untrusted input by our parent window (which we assume is Element).
// We can't aggressively sanitize [A-z0-9] since it might be a translation.
throw new Error(msg);
}

View file

@ -84,7 +84,7 @@ export default createReactClass({
const blob = new Blob([f], {
type: 'text/plain;charset=us-ascii',
});
FileSaver.saveAs(blob, 'riot-keys.txt');
FileSaver.saveAs(blob, 'element-keys.txt');
this.props.onFinished(true);
}).catch((e) => {
console.error("Error exporting e2e keys:", e);

View file

@ -286,7 +286,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
changeText = _t("Use a different passphrase?");
} else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) {
// only tell them they're wrong if they've actually gone wrong.
// Security concious readers will note that if you left riot-web unattended
// Security concious readers will note that if you left element-web unattended
// on this screen, this would make it easy for a malicious person to guess
// your passphrase one letter at a time, but they could get this faster by
// just opening the browser's developer tools and reading it.

View file

@ -480,7 +480,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
// click the button are aware they're making a change to their account.
// Once we're confident enough in this (and it's supported enough) we can do
// it automatically.
// https://github.com/vector-im/riot-web/issues/11696
// https://github.com/vector-im/element-web/issues/11696
const Field = sdk.getComponent('views.elements.Field');
let authPrompt;
@ -575,7 +575,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent {
changeText = _t("Use a different passphrase?");
} else if (!this.state.passPhrase.startsWith(this.state.passPhraseConfirm)) {
// only tell them they're wrong if they've actually gone wrong.
// Security concious readers will note that if you left riot-web unattended
// Security concious readers will note that if you left element-web unattended
// on this screen, this would make it easy for a malicious person to guess
// your passphrase one letter at a time, but they could get this faster by
// just opening the browser's developer tools and reading it.

View file

@ -53,7 +53,7 @@ export default class CommunityProvider extends AutocompleteProvider {
const BaseAvatar = sdk.getComponent('views.avatars.BaseAvatar');
// Disable autocompletions when composing commands because of various issues
// (see https://github.com/vector-im/riot-web/issues/4762)
// (see https://github.com/vector-im/element-web/issues/4762)
if (/^(\/join|\/leave)/.test(query)) {
return [];
}

View file

@ -118,7 +118,7 @@ export default class RoomProvider extends AutocompleteProvider {
}
getName() {
return '💬 ' + _t('Rooms');
return _t('Rooms');
}
renderCompletions(completions: React.ReactNode[]): React.ReactNode {

View file

@ -137,7 +137,7 @@ export default class UserProvider extends AutocompleteProvider {
}
getName(): string {
return '👥 ' + _t('Users');
return _t('Users');
}
_makeUsers() {

View file

@ -58,7 +58,7 @@ export enum ChevronFace {
None = "none",
}
interface IProps extends IPosition {
export interface IProps extends IPosition {
menuWidth?: number;
menuHeight?: number;

View file

@ -210,6 +210,11 @@ const FilePanel = createReactClass({
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
const emptyState = (<div className="mx_RightPanel_empty mx_FilePanel_empty">
<h2>{_t('No files visible in this room')}</h2>
<p>{_t('Attach files from chat or just drag and drop them anywhere in a room.')}</p>
</div>);
if (this.state.timelineSet) {
// console.log("rendering TimelinePanel for timelineSet " + this.state.timelineSet.room.roomId + " " +
// "(" + this.state.timelineSet._timelines.join(", ") + ")" + " with key " + this.props.roomId);
@ -223,7 +228,7 @@ const FilePanel = createReactClass({
onPaginationRequest={this.onPaginationRequest}
tileShape="file_grid"
resizeNotifier={this.props.resizeNotifier}
empty={_t('There are no visible files in this room')}
empty={emptyState}
/>
</div>
);

View file

@ -46,8 +46,8 @@ const HomePage = () => {
const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
return <AutoHideScrollbar className="mx_HomePage mx_HomePage_default">
<div className="mx_HomePage_default_wrapper">
<img src={logoUrl} alt={config.brand || "Riot"} />
<h1>{ _t("Welcome to %(appName)s", { appName: config.brand || "Riot" }) }</h1>
<img src={logoUrl} alt={config.brand || "Element"} />
<h1>{ _t("Welcome to %(appName)s", { appName: config.brand || "Element" }) }</h1>
<h4>{ _t("Liberate your communication") }</h4>
<div className="mx_HomePage_default_buttons">
<AccessibleButton onClick={onClickSendDm} className="mx_HomePage_button_sendDm">

View file

@ -158,7 +158,7 @@ export default class IndicatorScrollbar extends React.Component {
}
// don't mess with the horizontal scroll for trackpad users
// See https://github.com/vector-im/riot-web/issues/10005
// See https://github.com/vector-im/element-web/issues/10005
if (this._likelyTrackpadUser) {
return;
}

View file

@ -203,7 +203,7 @@ export default createReactClass({
// the UI layer, so we ignore this signal and show a spinner until
// there's a new screen to show the user. This is implemented by setting
// `busy: false` in `_authStateUpdated`.
// See also https://github.com/vector-im/riot-web/issues/12546
// See also https://github.com/vector-im/element-web/issues/12546
},
_setFocus: function() {

View file

@ -596,7 +596,7 @@ class LoggedInView extends React.Component<IProps, IState> {
const maxRadius = 5; // People shouldn't be straying too far, hopefully
// Note: we track how far the user moved their mouse to help
// combat against https://github.com/vector-im/riot-web/issues/7158
// combat against https://github.com/vector-im/element-web/issues/7158
if (distance < maxRadius) {
// This is probably a real click, and not a drag

View file

@ -415,7 +415,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return;
}
this.pageChanging = true;
performance.mark('riot_MatrixChat_page_change_start');
performance.mark('element_MatrixChat_page_change_start');
}
stopPageChangeTimer() {
@ -427,15 +427,15 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
return;
}
this.pageChanging = false;
performance.mark('riot_MatrixChat_page_change_stop');
performance.mark('element_MatrixChat_page_change_stop');
performance.measure(
'riot_MatrixChat_page_change_delta',
'riot_MatrixChat_page_change_start',
'riot_MatrixChat_page_change_stop',
'element_MatrixChat_page_change_delta',
'element_MatrixChat_page_change_start',
'element_MatrixChat_page_change_stop',
);
performance.clearMarks('riot_MatrixChat_page_change_start');
performance.clearMarks('riot_MatrixChat_page_change_stop');
const measurement = performance.getEntriesByName('riot_MatrixChat_page_change_delta').pop();
performance.clearMarks('element_MatrixChat_page_change_start');
performance.clearMarks('element_MatrixChat_page_change_stop');
const measurement = performance.getEntriesByName('element_MatrixChat_page_change_delta').pop();
// In practice, sometimes the entries list is empty, so we get no measurement
if (!measurement) return null;
@ -1323,7 +1323,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// state (each of which can be 10s of MBs) for each DISJOINT timeline. This is
// particularly noticeable when there are lots of 'limited' /sync responses
// such as when laptops unsleep.
// https://github.com/vector-im/riot-web/issues/3307#issuecomment-282895568
// https://github.com/vector-im/element-web/issues/3307#issuecomment-282895568
cli.setCanResetTimelineCallback((roomId) => {
console.log("Request to reset timeline in room ", roomId, " viewing:", this.state.currentRoomId);
if (roomId !== this.state.currentRoomId) {
@ -1661,7 +1661,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
// of the app, we coerce the eventId to be undefined where applicable.
if (!eventId) eventId = undefined;
// TODO: Handle encoded room/event IDs: https://github.com/vector-im/riot-web/issues/9149
// TODO: Handle encoded room/event IDs: https://github.com/vector-im/element-web/issues/9149
// FIXME: sort_out caseConsistency
const thirdPartyInvite = {
@ -1935,7 +1935,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
let fragmentAfterLogin = "";
const initialScreenAfterLogin = this.props.initialScreenAfterLogin;
if (initialScreenAfterLogin &&
// XXX: workaround for https://github.com/vector-im/riot-web/issues/11643 causing a login-loop
// XXX: workaround for https://github.com/vector-im/element-web/issues/11643 causing a login-loop
!["welcome", "login", "register", "start_sso", "start_cas"].includes(initialScreenAfterLogin.screen)
) {
fragmentAfterLogin = `/${initialScreenAfterLogin.screen}`;

View file

@ -36,6 +36,11 @@ const NotificationPanel = createReactClass({
const TimelinePanel = sdk.getComponent("structures.TimelinePanel");
const Loader = sdk.getComponent("elements.Spinner");
const emptyState = (<div className="mx_RightPanel_empty mx_NotificationPanel_empty">
<h2>{_t('Youre all caught up')}</h2>
<p>{_t('You have no visible notifications in this room.')}</p>
</div>);
const timelineSet = MatrixClientPeg.get().getNotifTimelineSet();
if (timelineSet) {
return (
@ -46,7 +51,7 @@ const NotificationPanel = createReactClass({
timelineSet={timelineSet}
showUrlPreview={false}
tileShape="notif"
empty={_t('You have no visible notifications')}
empty={emptyState}
/>
</div>
);

View file

@ -251,7 +251,7 @@ export default createReactClass({
this.context.stopPeeking();
}
// Temporary logging to diagnose https://github.com/vector-im/riot-web/issues/4307
// Temporary logging to diagnose https://github.com/vector-im/element-web/issues/4307
console.log(
'RVS update:',
newState.roomId,
@ -1078,7 +1078,7 @@ export default createReactClass({
// Do this by indicating our intention to join
// XXX: ILAG is disabled for now,
// see https://github.com/vector-im/riot-web/issues/8222
// see https://github.com/vector-im/element-web/issues/8222
dis.dispatch({action: 'require_registration'});
// dis.dispatch({
// action: 'will_join',

View file

@ -141,7 +141,7 @@ const TagPanel = createReactClass({
<AutoHideScrollbar
className="mx_TagPanel_scroller"
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6253
// instead of onClick. Otherwise we experience https://github.com/vector-im/element-web/issues/6253
onMouseDown={this.onMouseDown}
>
<Droppable

View file

@ -1186,7 +1186,7 @@ const TimelinePanel = createReactClass({
if (!timeline) {
// Somehow, it seems to be possible for live events to not have
// a timeline, even though that should not happen. :(
// https://github.com/vector-im/riot-web/issues/12120
// https://github.com/vector-im/element-web/issues/12120
console.warn(
`Event ${events[i].getId()} in room ${room.roomId} is live, ` +
`but it does not have a timeline`,

View file

@ -20,7 +20,7 @@ import defaultDispatcher from "../../dispatcher/dispatcher";
import { ActionPayload } from "../../dispatcher/payloads";
import { Action } from "../../dispatcher/actions";
import { _t } from "../../languageHandler";
import { ChevronFace, ContextMenu, ContextMenuButton, MenuItem } from "./ContextMenu";
import { ContextMenuButton } from "./ContextMenu";
import {USER_NOTIFICATIONS_TAB, USER_SECURITY_TAB} from "../views/dialogs/UserSettingsDialog";
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
import RedesignFeedbackDialog from "../views/dialogs/RedesignFeedbackDialog";
@ -38,6 +38,10 @@ import BaseAvatar from '../views/avatars/BaseAvatar';
import classNames from "classnames";
import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { SettingLevel } from "../../settings/SettingLevel";
import IconizedContextMenu, {
IconizedContextMenuOption,
IconizedContextMenuOptionList
} from "../views/context_menus/IconizedContextMenu";
interface IProps {
isMinimized: boolean;
@ -50,19 +54,6 @@ interface IState {
isDarkTheme: boolean;
}
interface IMenuButtonProps {
iconClassName: string;
label: string;
onClick(ev: ButtonEvent);
}
const MenuButton: React.FC<IMenuButtonProps> = ({iconClassName, label, onClick}) => {
return <MenuItem label={label} onClick={onClick}>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
</MenuItem>;
};
export default class UserMenu extends React.Component<IProps, IState> {
private dispatcherRef: string;
private themeWatcherRef: string;
@ -170,7 +161,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
ev.preventDefault();
ev.stopPropagation();
// TODO: Archived room view: https://github.com/vector-im/riot-web/issues/14038
// TODO: Archived room view: https://github.com/vector-im/element-web/issues/14038
// Note: You'll need to uncomment the button too.
console.log("TODO: Show archived rooms");
};
@ -226,7 +217,7 @@ export default class UserMenu extends React.Component<IProps, IState> {
let homeButton = null;
if (this.hasHomePage) {
homeButton = (
<MenuButton
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconHome"
label={_t("Home")}
onClick={this.onHomeClick}
@ -234,75 +225,71 @@ export default class UserMenu extends React.Component<IProps, IState> {
);
}
return (
<ContextMenu
chevronFace={ChevronFace.None}
// -20 to overlap the context menu by just over the width of the `...` icon and make it look connected
left={this.state.contextMenuPosition.width + this.state.contextMenuPosition.left - 20}
top={this.state.contextMenuPosition.top + this.state.contextMenuPosition.height}
onFinished={this.onCloseMenu}
>
<div className="mx_IconizedContextMenu mx_UserMenu_contextMenu">
<div className="mx_UserMenu_contextMenu_header">
<div className="mx_UserMenu_contextMenu_name">
<span className="mx_UserMenu_contextMenu_displayName">
{OwnProfileStore.instance.displayName}
</span>
<span className="mx_UserMenu_contextMenu_userId">
{MatrixClientPeg.get().getUserId()}
</span>
</div>
<AccessibleTooltipButton
className="mx_UserMenu_contextMenu_themeButton"
onClick={this.onSwitchThemeClick}
title={this.state.isDarkTheme ? _t("Switch to light mode") : _t("Switch to dark mode")}
>
<img
src={require("../../../res/img/element-icons/roomlist/dark-light-mode.svg")}
alt={_t("Switch theme")}
width={16}
/>
</AccessibleTooltipButton>
</div>
{hostingLink}
<div className="mx_IconizedContextMenu_optionList mx_IconizedContextMenu_optionList_notFirst">
{homeButton}
<MenuButton
iconClassName="mx_UserMenu_iconBell"
label={_t("Notification settings")}
onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}
/>
<MenuButton
iconClassName="mx_UserMenu_iconLock"
label={_t("Security & privacy")}
onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}
/>
<MenuButton
iconClassName="mx_UserMenu_iconSettings"
label={_t("All settings")}
onClick={(e) => this.onSettingsOpen(e, null)}
/>
{/* <MenuButton
iconClassName="mx_UserMenu_iconArchive"
label={_t("Archived rooms")}
onClick={this.onShowArchived}
/> */}
<MenuButton
iconClassName="mx_UserMenu_iconMessage"
label={_t("Feedback")}
onClick={this.onProvideFeedback}
/>
</div>
<div className="mx_IconizedContextMenu_optionList mx_UserMenu_contextMenu_redRow">
<MenuButton
iconClassName="mx_UserMenu_iconSignOut"
label={_t("Sign out")}
onClick={this.onSignOutClick}
/>
</div>
return <IconizedContextMenu
// -20 to overlap the context menu by just over the width of the `...` icon and make it look connected
left={this.state.contextMenuPosition.width + this.state.contextMenuPosition.left - 20}
top={this.state.contextMenuPosition.top + this.state.contextMenuPosition.height}
onFinished={this.onCloseMenu}
className="mx_UserMenu_contextMenu"
>
<div className="mx_UserMenu_contextMenu_header">
<div className="mx_UserMenu_contextMenu_name">
<span className="mx_UserMenu_contextMenu_displayName">
{OwnProfileStore.instance.displayName}
</span>
<span className="mx_UserMenu_contextMenu_userId">
{MatrixClientPeg.get().getUserId()}
</span>
</div>
</ContextMenu>
);
<AccessibleTooltipButton
className="mx_UserMenu_contextMenu_themeButton"
onClick={this.onSwitchThemeClick}
title={this.state.isDarkTheme ? _t("Switch to light mode") : _t("Switch to dark mode")}
>
<img
src={require("../../../res/img/element-icons/roomlist/dark-light-mode.svg")}
alt={_t("Switch theme")}
width={16}
/>
</AccessibleTooltipButton>
</div>
{hostingLink}
<IconizedContextMenuOptionList>
{homeButton}
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconBell"
label={_t("Notification settings")}
onClick={(e) => this.onSettingsOpen(e, USER_NOTIFICATIONS_TAB)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconLock"
label={_t("Security & privacy")}
onClick={(e) => this.onSettingsOpen(e, USER_SECURITY_TAB)}
/>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSettings"
label={_t("All settings")}
onClick={(e) => this.onSettingsOpen(e, null)}
/>
{/* <IconizedContextMenuOption
iconClassName="mx_UserMenu_iconArchive"
label={_t("Archived rooms")}
onClick={this.onShowArchived}
/> */}
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconMessage"
label={_t("Feedback")}
onClick={this.onProvideFeedback}
/>
</IconizedContextMenuOptionList>
<IconizedContextMenuOptionList red>
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconSignOut"
label={_t("Sign out")}
onClick={this.onSignOutClick}
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
};
public render() {

View file

@ -285,7 +285,7 @@ export default createReactClass({
// We'd like to rely on new props coming in via `onServerConfigChange`
// so that we know the servers have definitely updated before clearing
// the busy state. In the case of a full MXID that resolves to the same
// HS as Riot's default HS though, there may not be any server change.
// HS as Element's default HS though, there may not be any server change.
// To avoid this trap, we clear busy here. For cases where the server
// actually has changed, `_initLoginLogic` will be called and manages
// busy state for its own liveness check.
@ -615,8 +615,8 @@ export default createReactClass({
}
// XXX: This link does *not* have a target="_blank" because single sign-on relies on
// redirecting the user back to a URI once they're logged in. On the web, this means
// we use the same window and redirect back to riot. On electron, this actually
// opens the SSO page in the electron app itself due to
// we use the same window and redirect back to Element. On Electron, this actually
// opens the SSO page in the Electron app itself due to
// https://github.com/electron/electron/issues/8841 and so happens to work.
// If this bug gets fixed, it will break SSO since it will open the SSO page in the
// user's browser, let them log into their SSO provider, then redirect their browser

View file

@ -262,7 +262,7 @@ export default createReactClass({
// the user off to the login page to figure their account out.
try {
const loginLogic = new Login(hsUrl, isUrl, null, {
defaultDeviceDisplayName: "riot login check", // We shouldn't ever be used
defaultDeviceDisplayName: "Element login check", // We shouldn't ever be used
});
const flows = await loginLogic.getFlows();
const hasSsoFlow = flows.find(f => f.type === 'm.login.sso' || f.type === 'm.login.cas');

View file

@ -152,7 +152,7 @@ export default class SetupEncryptionBody extends React.Component {
</div>
<div className="mx_CompleteSecurity_clients_mobile">
<div>{_t("%(brand)s iOS", { brand })}</div>
<div>{_t("%(brand)s X for Android", { brand })}</div>
<div>{_t("%(brand)s Android", { brand })}</div>
</div>
<p>{_t("or another cross-signing capable Matrix client")}</p>
</div>

View file

@ -109,7 +109,7 @@ export const PasswordAuthEntry = createReactClass({
this.props.submitAuthDict({
type: PasswordAuthEntry.LOGIN_TYPE,
// TODO: Remove `user` once servers support proper UIA
// See https://github.com/vector-im/riot-web/issues/10312
// See https://github.com/vector-im/element-web/issues/10312
user: this.props.matrixClient.credentials.userId,
identifier: {
type: "m.id.user",
@ -538,7 +538,7 @@ export const MsisdnAuthEntry = createReactClass({
this.props.submitAuthDict({
type: MsisdnAuthEntry.LOGIN_TYPE,
// TODO: Remove `threepid_creds` once servers support proper UIA
// See https://github.com/vector-im/riot-web/issues/10312
// See https://github.com/vector-im/element-web/issues/10312
// See https://github.com/matrix-org/matrix-doc/issues/2220
threepid_creds: creds,
threepidCreds: creds,

View file

@ -0,0 +1,124 @@
/*
Copyright 2020 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import classNames from "classnames";
import {
ChevronFace,
ContextMenu,
IProps as IContextMenuProps,
MenuItem,
MenuItemCheckbox, MenuItemRadio,
} from "../../structures/ContextMenu";
interface IProps extends IContextMenuProps {
className?: string;
compact?: boolean;
}
interface IOptionListProps {
first?: boolean;
red?: boolean;
className?: string;
}
interface IOptionProps extends React.ComponentProps<typeof MenuItem> {
iconClassName: string;
}
interface ICheckboxProps extends React.ComponentProps<typeof MenuItemCheckbox> {
iconClassName: string;
}
interface IRadioProps extends React.ComponentProps<typeof MenuItemRadio> {
iconClassName: string;
}
export const IconizedContextMenuRadio: React.FC<IRadioProps> = ({
label,
iconClassName,
active,
className,
...props
}) => {
return <MenuItemRadio
{...props}
className={classNames(className, {
mx_IconizedContextMenu_active: active,
})}
active={active}
label={label}
>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
</MenuItemRadio>;
};
export const IconizedContextMenuCheckbox: React.FC<ICheckboxProps> = ({
label,
iconClassName,
active,
className,
...props
}) => {
return <MenuItemCheckbox
{...props}
className={classNames(className, {
mx_IconizedContextMenu_active: active,
})}
active={active}
label={label}
>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
{active && <span className="mx_IconizedContextMenu_icon mx_IconizedContextMenu_checked" />}
</MenuItemCheckbox>;
};
export const IconizedContextMenuOption: React.FC<IOptionProps> = ({label, iconClassName, ...props}) => {
return <MenuItem {...props} label={label}>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{label}</span>
</MenuItem>;
};
export const IconizedContextMenuOptionList: React.FC<IOptionListProps> = ({first, red, className, children}) => {
const classes = classNames("mx_IconizedContextMenu_optionList", className, {
mx_IconizedContextMenu_optionList_notFirst: !first,
mx_IconizedContextMenu_optionList_red: red,
});
return <div className={classes}>
{children}
</div>;
};
const IconizedContextMenu: React.FC<IProps> = ({className, children, compact, ...props}) => {
const classes = classNames("mx_IconizedContextMenu", className, {
mx_IconizedContextMenu_compact: compact,
});
return <ContextMenu chevronFace={ChevronFace.None} {...props}>
<div className={classes}>
{ children }
</div>
</ContextMenu>;
};
export default IconizedContextMenu;

View file

@ -193,7 +193,7 @@ export default class BugReportDialog extends React.Component {
{
a: (sub) => <a
target="_blank"
href="https://github.com/vector-im/riot-web/issues/new"
href="https://github.com/vector-im/element-web/issues/new"
>
{ sub }
</a>,
@ -211,7 +211,7 @@ export default class BugReportDialog extends React.Component {
label={_t("GitHub issue")}
onChange={this._onIssueUrlChange}
value={this.state.issueUrl}
placeholder="https://github.com/vector-im/riot-web/issues/..."
placeholder="https://github.com/vector-im/element-web/issues/..."
/>
<Field
className="mx_BugReportDialog_field_input"

View file

@ -21,7 +21,7 @@ import * as sdk from '../../../index';
import request from 'browser-request';
import { _t } from '../../../languageHandler';
const REPOS = ['vector-im/riot-web', 'matrix-org/matrix-react-sdk', 'matrix-org/matrix-js-sdk'];
const REPOS = ['vector-im/element-web', 'matrix-org/matrix-react-sdk', 'matrix-org/matrix-js-sdk'];
export default class ChangelogDialog extends React.Component {
constructor(props) {

View file

@ -349,7 +349,7 @@ export default class InviteDialog extends React.PureComponent {
// Also pull in all the rooms tagged as DefaultTagID.DM so we don't miss anything. Sometimes the
// room list doesn't tag the room for the DMRoomMap, but does for the room list.
const dmTaggedRooms = RoomListStore.instance.orderedLists[DefaultTagID.DM];
const dmTaggedRooms = RoomListStore.instance.orderedLists[DefaultTagID.DM] || [];
const myUserId = MatrixClientPeg.get().getUserId();
for (const dmRoom of dmTaggedRooms) {
const otherMembers = dmRoom.getJoinedMembers().filter(u => u.userId !== myUserId);

View file

@ -1,116 +0,0 @@
/*
Copyright 2020 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.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as React from 'react';
import * as PropTypes from 'prop-types';
import BaseDialog from './BaseDialog';
import { _t } from '../../../languageHandler';
import DialogButtons from '../elements/DialogButtons';
export enum RebrandDialogKind {
NAG,
ONE_TIME,
}
interface IProps {
onFinished: (bool) => void;
kind: RebrandDialogKind;
targetUrl?: string;
}
export default class RebrandDialog extends React.PureComponent<IProps> {
private onDoneClick = () => {
this.props.onFinished(true);
};
private onGoToElementClick = () => {
this.props.onFinished(true);
};
private onRemindMeLaterClick = () => {
this.props.onFinished(false);
};
private getPrettyTargetUrl() {
const u = new URL(this.props.targetUrl);
let ret = u.host;
if (u.pathname !== '/') ret += u.pathname;
return ret;
}
getBodyText() {
if (this.props.kind === RebrandDialogKind.NAG) {
return _t(
"Use your account to sign in to the latest version of the app at <a />", {},
{
a: sub => <a href={this.props.targetUrl} rel="noopener noreferrer" target="_blank">{this.getPrettyTargetUrl()}</a>,
},
);
} else {
return _t(
"Youre already signed in and good to go here, but you can also grab the latest " +
"versions of the app on all platforms at <a>element.io/get-started</a>.", {},
{
a: sub => <a href="https://element.io/get-started" rel="noopener noreferrer" target="_blank">{sub}</a>,
},
);
}
}
getDialogButtons() {
if (this.props.kind === RebrandDialogKind.NAG) {
return <DialogButtons primaryButton={_t("Go to Element")}
primaryButtonClass='primary'
onPrimaryButtonClick={this.onGoToElementClick}
hasCancel={true}
cancelButton={"Remind me later"}
onCancel={this.onRemindMeLaterClick}
focus={true}
/>;
} else {
return <DialogButtons primaryButton={_t("Done")}
primaryButtonClass='primary'
hasCancel={false}
onPrimaryButtonClick={this.onDoneClick}
focus={true}
/>;
}
}
render() {
return <BaseDialog title={_t("Were excited to announce Riot is now Element!")}
className='mx_RebrandDialog'
contentId='mx_Dialog_content'
onFinished={this.props.onFinished}
hasCancel={false}
>
<div className="mx_RebrandDialog_body">{this.getBodyText()}</div>
<div className="mx_RebrandDialog_logoContainer">
<img className="mx_RebrandDialog_logo" src={require("../../../../res/img/riot-logo.svg")} alt="Riot Logo" />
<span className="mx_RebrandDialog_chevron" />
<img className="mx_RebrandDialog_logo" src={require("../../../../res/img/element-logo.svg")} alt="Element Logo" />
</div>
<div>
{_t(
"Learn more at <a>element.io/previously-riot</a>", {}, {
a: sub => <a href="https://element.io/previously-riot" rel="noopener noreferrer" target="_blank">{sub}</a>,
}
)}
</div>
{this.getDialogButtons()}
</BaseDialog>;
}
}

View file

@ -19,9 +19,9 @@ import QuestionDialog from './QuestionDialog';
import { _t } from '../../../languageHandler';
export default (props) => {
const existingIssuesUrl = "https://github.com/vector-im/riot-web/issues" +
const existingIssuesUrl = "https://github.com/vector-im/element-web/issues" +
"?q=is%3Aopen+is%3Aissue+sort%3Areactions-%2B1-desc";
const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new";
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
const description1 =
_t("If you run into any bugs or have feedback you'd like to share, " +

View file

@ -30,6 +30,7 @@ import * as ContextMenu from "../../structures/ContextMenu";
import {toRightOf} from "../../structures/ContextMenu";
import {copyPlaintext, selectText} from "../../../utils/strings";
import StyledCheckbox from '../elements/StyledCheckbox';
import AccessibleTooltipButton from '../elements/AccessibleTooltipButton';
const socials = [
{
@ -210,10 +211,11 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
>
{ matrixToUrl }
</a>
<a href={matrixToUrl} className="mx_ShareDialog_matrixto_copy" onClick={this.onCopyClick}>
{ _t('COPY') }
<div>&nbsp;</div>
</a>
<AccessibleTooltipButton
title={_t("Copy")}
onClick={this.onCopyClick}
className="mx_ShareDialog_matrixto_copy"
/>
</div>
{ checkbox }
<hr />

View file

@ -314,13 +314,13 @@ export default class AppTile extends React.Component {
if (SettingsStore.isFeatureEnabled("feature_many_integration_managers")) {
IntegrationManagers.sharedInstance().openAll(
this.props.room,
'type_' + this.props.type,
'type_' + this.props.app.type,
this.props.app.id,
);
} else {
IntegrationManagers.sharedInstance().getPrimaryManager().open(
this.props.room,
'type_' + this.props.type,
'type_' + this.props.app.type,
this.props.app.id,
);
}
@ -361,14 +361,14 @@ export default class AppTile extends React.Component {
return terminationPromise.finally(() => {
// HACK: This is a really dirty way to ensure that Jitsi cleans up
// its hold on the webcam. Without this, the widget holds a media
// stream open, even after death. See https://github.com/vector-im/riot-web/issues/7351
// stream open, even after death. See https://github.com/vector-im/element-web/issues/7351
if (this._appFrame.current) {
// In practice we could just do `+= ''` to trick the browser
// into thinking the URL changed, however I can foresee this
// being optimized out by a browser. Instead, we'll just point
// the iframe at a page that is reasonably safe to use in the
// event the iframe doesn't wink away.
// This is relative to where the Riot instance is located.
// This is relative to where the Element instance is located.
this._appFrame.current.src = 'about:blank';
}
@ -727,7 +727,7 @@ export default class AppTile extends React.Component {
// Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin
// because that would allow the iframe to programmatically remove the sandbox attribute, but
// this would only be for content hosted on the same origin as the riot client: anything
// this would only be for content hosted on the same origin as the element client: anything
// hosted on the same origin as the client will get the same access as if you clicked
// a link to it.
const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox "+
@ -924,7 +924,7 @@ AppTile.propTypes = {
// Optionally show the reload widget icon
// This is not currently intended for use with production widgets. However
// it can be useful when developing persistent widgets in order to avoid
// having to reload all of riot to get new widget content.
// having to reload all of Element to get new widget content.
showReload: PropTypes.bool,
// Widget capabilities to allow by default (without user confirmation)
// NOTE -- Use with caution. This is intended to aid better integration / UX

View file

@ -72,7 +72,7 @@ export default class ErrorBoundary extends React.PureComponent {
render() {
if (this.state.error) {
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
const newIssueUrl = "https://github.com/vector-im/riot-web/issues/new";
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
return <div className="mx_ErrorBoundary">
<div className="mx_ErrorBoundary_body">
<h1>{_t("Something went wrong!")}</h1>

View file

@ -25,6 +25,7 @@ interface IDefinition<T extends string> {
disabled?: boolean;
label: React.ReactChild;
description?: React.ReactChild;
checked?: boolean; // If provided it will override the value comparison done in the group
}
interface IProps<T extends string> {
@ -33,7 +34,7 @@ interface IProps<T extends string> {
definitions: IDefinition<T>[];
value?: T; // if not provided no options will be selected
outlined?: boolean;
onChange(newValue: T);
onChange(newValue: T): void;
}
function StyledRadioGroup<T extends string>({name, definitions, value, className, outlined, onChange}: IProps<T>) {
@ -46,7 +47,7 @@ function StyledRadioGroup<T extends string>({name, definitions, value, className
<StyledRadioButton
className={classNames(className, d.className)}
onChange={_onChange}
checked={d.value === value}
checked={d.checked !== undefined ? d.checked : d.value === value}
name={name}
value={d.value}
disabled={d.disabled}

View file

@ -126,7 +126,7 @@ const GroupTile = createReactClass({
}
// XXX: Use onMouseDown as a workaround for https://github.com/atlassian/react-beautiful-dnd/issues/273
// instead of onClick. Otherwise we experience https://github.com/vector-im/riot-web/issues/6156
// instead of onClick. Otherwise we experience https://github.com/vector-im/element-web/issues/6156
return <AccessibleButton className="mx_GroupTile" onMouseDown={this.onMouseDown} onClick={nop}>
{ avatarElement }
<div className="mx_GroupTile_profile">

View file

@ -39,6 +39,8 @@ interface IProps {
title: string;
}
// TODO: replace this, the composer buttons and the right panel buttons with a unified
// representation
export default class HeaderButton extends React.Component<IProps> {
constructor(props: IProps) {
super(props);

View file

@ -550,7 +550,9 @@ const RedactMessagesButton = ({member}) => {
let eventsToRedact = [];
while (timeline) {
eventsToRedact = timeline.getEvents().reduce((events, event) => {
if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction()) {
if (event.getSender() === userId && !event.isRedacted() && !event.isRedaction() &&
event.getType() !== "m.room.create"
) {
return events.concat(event);
} else {
return events;

View file

@ -208,7 +208,7 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
});
let body: JSX.Element;
if (this.state.reciprocateQREvent) {
// riot web doesn't support scanning yet, so assume here we're the client being scanned.
// Element Web doesn't support scanning yet, so assume here we're the client being scanned.
//
// we're passing both a label and a child string to Button as
// FormButton and AccessibleButton expect this differently

View file

@ -233,7 +233,7 @@ export default class AliasSettings extends React.Component {
onLocalAliasDeleted = (index) => {
const alias = this.state.localAliases[index];
// TODO: In future, we should probably be making sure that the alias actually belongs
// to this room. See https://github.com/vector-im/riot-web/issues/7353
// to this room. See https://github.com/vector-im/element-web/issues/7353
MatrixClientPeg.get().deleteAlias(alias).then(() => {
const localAliases = this.state.localAliases.filter(a => a !== alias);
this.setState({localAliases});

View file

@ -39,7 +39,7 @@ const ROOM_COLORS = [
// Dev note: this component is not attached anywhere, but is left here as it
// has a high possibility of being used in the nearish future.
// Ref: https://github.com/vector-im/riot-web/issues/8421
// Ref: https://github.com/vector-im/element-web/issues/8421
export default createReactClass({
displayName: 'ColorSettings',

View file

@ -242,7 +242,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
// so trigger a model update after the composition is done by calling the input handler.
// however, modifying the DOM (caused by the editor model update) from the compositionend handler seems
// to confuse the IME in Chrome, likely causing https://github.com/vector-im/riot-web/issues/10913 ,
// to confuse the IME in Chrome, likely causing https://github.com/vector-im/element-web/issues/10913 ,
// so we do it async
// however, doing this async seems to break things in Safari for some reason, so browser sniff.
@ -273,7 +273,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
const {model} = this.props;
const range = getRangeForSelection(this.editorRef.current, model, selection);
const selectedParts = range.parts.map(p => p.serialize());
event.clipboardData.setData("application/x-riot-composer", JSON.stringify(selectedParts));
event.clipboardData.setData("application/x-element-composer", JSON.stringify(selectedParts));
event.clipboardData.setData("text/plain", text); // so plain copy/paste works
if (type === "cut") {
// Remove the text, updating the model as appropriate
@ -301,7 +301,7 @@ export default class BasicMessageEditor extends React.Component<IProps, IState>
const {model} = this.props;
const {partCreator} = model;
const partsText = event.clipboardData.getData("application/x-riot-composer");
const partsText = event.clipboardData.getData("application/x-element-composer");
let parts;
if (partsText) {
const serializedTextParts = JSON.parse(partsText);

View file

@ -60,7 +60,7 @@ const stateEventTileTypes = {
'm.room.power_levels': 'messages.TextualEvent',
'm.room.pinned_events': 'messages.TextualEvent',
'm.room.server_acl': 'messages.TextualEvent',
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
'im.vector.modular.widgets': 'messages.TextualEvent',
'm.room.tombstone': 'messages.TextualEvent',
'm.room.join_rules': 'messages.TextualEvent',
@ -519,7 +519,7 @@ export default createReactClass({
onPermalinkClicked: function(e) {
// This allows the permalink to be opened in a new tab/window or copied as
// matrix.to, but also for it to enable routing within Riot when clicked.
// matrix.to, but also for it to enable routing within Element when clicked.
e.preventDefault();
dis.dispatch({
action: 'view_room',
@ -595,11 +595,11 @@ export default createReactClass({
}
const eventId = this.props.mxEvent.getId();
if (!eventId) {
// XXX: Temporary diagnostic logging for https://github.com/vector-im/riot-web/issues/11120
// XXX: Temporary diagnostic logging for https://github.com/vector-im/element-web/issues/11120
console.error("EventTile attempted to get relations for an event without an ID");
// Use event's special `toJSON` method to log key data.
console.log(JSON.stringify(this.props.mxEvent, null, 4));
console.trace("Stacktrace for https://github.com/vector-im/riot-web/issues/11120");
console.trace("Stacktrace for https://github.com/vector-im/element-web/issues/11120");
}
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction");
},

View file

@ -15,6 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import CallHandler from '../../../CallHandler';
@ -29,7 +30,6 @@ import E2EIcon from './E2EIcon';
import SettingsStore from "../../../settings/SettingsStore";
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu";
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
function ComposerAvatar(props) {
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
return <div className="mx_MessageComposer_avatar">
@ -117,9 +117,19 @@ const EmojiButton = ({addEmoji}) => {
</ContextMenu>;
}
const className = classNames(
"mx_MessageComposer_button",
"mx_MessageComposer_emoji",
{
"mx_MessageComposer_button_highlight": menuDisplayed,
},
);
// TODO: replace ContextMenuTooltipButton with a unified representation of
// the header buttons and the right panel buttons
return <React.Fragment>
<ContextMenuTooltipButton
className="mx_MessageComposer_button mx_MessageComposer_emoji"
className={className}
onClick={openMenu}
isExpanded={menuDisplayed}
title={_t('Emoji picker')}

View file

@ -97,7 +97,7 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
// Don't show a badge if we don't need to
if (notification.isIdle) return null;
// TODO: Update these booleans for FTUE Notifications: https://github.com/vector-im/riot-web/issues/14261
// TODO: Update these booleans for FTUE Notifications: https://github.com/vector-im/element-web/issues/14261
// As of writing, that is "if red, show count always" and "optionally show counts instead of dots".
// See git diff for what that boolean state looks like.
// XXX: We ignore this.state.showCounts (the setting which controls counts vs dots).

View file

@ -73,7 +73,7 @@ export default class ReplyPreview extends React.Component {
return <div className="mx_ReplyPreview">
<div className="mx_ReplyPreview_section">
<div className="mx_ReplyPreview_header mx_ReplyPreview_title">
{ '💬 ' + _t('Replying') }
{ _t('Replying') }
</div>
<div className="mx_ReplyPreview_header mx_ReplyPreview_cancel">
<img className="mx_filterFlipColor" src={require("../../../../res/img/cancel.svg")} width="18" height="18"

View file

@ -128,7 +128,7 @@ const TAG_AESTHETICS: {
defaultHidden: false,
},
// TODO: Replace with archived view: https://github.com/vector-im/riot-web/issues/14038
// TODO: Replace with archived view: https://github.com/vector-im/element-web/issues/14038
[DefaultTagID.Archived]: {
sectionLabel: _td("Historical"),
isInvite: false,
@ -215,7 +215,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
private updateLists = () => {
const newLists = RoomListStore.instance.orderedLists;
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log("new lists", newLists);
}
@ -245,6 +245,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
if (doUpdate) {
// We have to break our reference to the room list store if we want to be able to
// diff the object for changes, so do that.
// @ts-ignore - ITagMap is ts-ignored so this will have to be too
const newSublists = objectWithOnly(newLists, newListIds);
const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v));
@ -256,7 +257,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
private renderCommunityInvites(): TemporaryTile[] {
// TODO: Put community invites in a more sensible place (not in the room list)
// See https://github.com/vector-im/riot-web/issues/14456
// See https://github.com/vector-im/element-web/issues/14456
return MatrixClientPeg.get().getGroups().filter(g => {
return g.myMembership === 'invite';
}).map(g => {

View file

@ -511,7 +511,7 @@ export default createReactClass({
"If you think you're seeing this message in error, please " +
"<issueLink>submit a bug report</issueLink>.",
{ errcode: this.props.error.errcode },
{ issueLink: label => <a href="https://github.com/vector-im/riot-web/issues/new/choose"
{ issueLink: label => <a href="https://github.com/vector-im/element-web/issues/new/choose"
target="_blank" rel="noreferrer noopener">{ label }</a> },
),
];

View file

@ -74,7 +74,7 @@ interface IProps {
// You should feel bad if you use this.
extraBadTilesThatShouldntExist?: TemporaryTile[];
// TODO: Account for https://github.com/vector-im/riot-web/issues/14179
// TODO: Account for https://github.com/vector-im/element-web/issues/14179
}
// TODO: Use re-resizer's NumberSize when it is exposed as the type
@ -703,7 +703,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
private onScrollPrevent(e: React.UIEvent<HTMLDivElement>) {
// the RoomTile calls scrollIntoView and the browser may scroll a div we do not wish to be scrollable
// this fixes https://github.com/vector-im/riot-web/issues/14413
// this fixes https://github.com/vector-im/element-web/issues/14413
(e.target as HTMLDivElement).scrollTop = 0;
}
@ -738,14 +738,20 @@ export default class RoomSublist extends React.Component<IProps, IState> {
const nonPaddedHeight = this.state.height - RESIZE_HANDLE_HEIGHT - SHOW_N_BUTTON_HEIGHT;
const amountFullyShown = Math.floor(nonPaddedHeight / this.layout.tileHeight);
const numMissing = this.numTiles - amountFullyShown;
const label = _t("Show %(count)s more", {count: numMissing});
let showMoreText = (
<span className='mx_RoomSublist_showNButtonText'>
{_t("Show %(count)s more", {count: numMissing})}
{label}
</span>
);
if (this.props.isMinimized) showMoreText = null;
showNButton = (
<RovingAccessibleButton onClick={this.onShowAllClick} className={showMoreBtnClasses}>
<RovingAccessibleButton
role="treeitem"
onClick={this.onShowAllClick}
className={showMoreBtnClasses}
aria-label={label}
>
<span className='mx_RoomSublist_showMoreButtonChevron mx_RoomSublist_showNButtonChevron'>
{/* set by CSS masking */}
</span>
@ -754,14 +760,20 @@ export default class RoomSublist extends React.Component<IProps, IState> {
);
} else if (this.numTiles > this.layout.defaultVisibleTiles) {
// we have all tiles visible - add a button to show less
const label = _t("Show less");
let showLessText = (
<span className='mx_RoomSublist_showNButtonText'>
{_t("Show less")}
{label}
</span>
);
if (this.props.isMinimized) showLessText = null;
showNButton = (
<RovingAccessibleButton onClick={this.onShowLessClick} className={showMoreBtnClasses}>
<RovingAccessibleButton
role="treeitem"
onClick={this.onShowLessClick}
className={showMoreBtnClasses}
aria-label={label}
>
<span className='mx_RoomSublist_showLessButtonChevron mx_RoomSublist_showNButtonChevron'>
{/* set by CSS masking */}
</span>

View file

@ -27,14 +27,7 @@ import defaultDispatcher from '../../../dispatcher/dispatcher';
import { Key } from "../../../Keyboard";
import ActiveRoomObserver from "../../../ActiveRoomObserver";
import { _t } from "../../../languageHandler";
import {
ChevronFace,
ContextMenu,
ContextMenuTooltipButton,
MenuItem,
MenuItemCheckbox,
MenuItemRadio,
} from "../../structures/ContextMenu";
import { ChevronFace, ContextMenuTooltipButton, MenuItemRadio } from "../../structures/ContextMenu";
import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
@ -51,6 +44,11 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
import { EchoChamber } from "../../../stores/local-echo/EchoChamber";
import { CachedRoomKey, RoomEchoChamber } from "../../../stores/local-echo/RoomEchoChamber";
import { PROPERTY_UPDATED } from "../../../stores/local-echo/GenericEchoChamber";
import IconizedContextMenu, {
IconizedContextMenuCheckbox,
IconizedContextMenuOption,
IconizedContextMenuOptionList, IconizedContextMenuRadio
} from "../context_menus/IconizedContextMenu";
interface IProps {
room: Room;
@ -78,32 +76,6 @@ const contextMenuBelow = (elementRect: PartialDOMRect) => {
return {left, top, chevronFace};
};
interface INotifOptionProps {
active: boolean;
iconClassName: string;
label: string;
onClick(ev: ButtonEvent);
}
const NotifOption: React.FC<INotifOptionProps> = ({active, onClick, iconClassName, label}) => {
const classes = classNames({
mx_RoomTile_contextMenu_activeRow: active,
});
let activeIcon;
if (active) {
activeIcon = <span className="mx_IconizedContextMenu_icon mx_RoomTile_iconCheck" />;
}
return (
<MenuItemRadio className={classes} onClick={onClick} active={active} label={label}>
<span className={classNames("mx_IconizedContextMenu_icon", iconClassName)} />
<span className="mx_IconizedContextMenu_label">{ label }</span>
{ activeIcon }
</MenuItemRadio>
);
};
export default class RoomTile extends React.PureComponent<IProps, IState> {
private dispatcherRef: string;
private roomTileRef = createRef<HTMLDivElement>();
@ -148,6 +120,12 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
return !this.props.isMinimized && this.props.showMessagePreview;
}
public componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>) {
if (prevProps.showMessagePreview !== this.props.showMessagePreview && this.showMessagePreview) {
this.setState({messagePreview: this.generatePreview()});
}
}
public componentDidMount() {
// when we're first rendered (or our sublist is expanded) make sure we are visible if we're active
if (this.state.selected) {
@ -335,38 +313,39 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
let contextMenu = null;
if (this.state.notificationsMenuPosition) {
contextMenu = (
<ContextMenu {...contextMenuBelow(this.state.notificationsMenuPosition)} onFinished={this.onCloseNotificationsMenu}>
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
<div className="mx_IconizedContextMenu_optionList">
<NotifOption
label={_t("Use default")}
active={state === ALL_MESSAGES}
iconClassName="mx_RoomTile_iconBell"
onClick={this.onClickAllNotifs}
/>
<NotifOption
label={_t("All messages")}
active={state === ALL_MESSAGES_LOUD}
iconClassName="mx_RoomTile_iconBellDot"
onClick={this.onClickAlertMe}
/>
<NotifOption
label={_t("Mentions & Keywords")}
active={state === MENTIONS_ONLY}
iconClassName="mx_RoomTile_iconBellMentions"
onClick={this.onClickMentions}
/>
<NotifOption
label={_t("None")}
active={state === MUTE}
iconClassName="mx_RoomTile_iconBellCrossed"
onClick={this.onClickMute}
/>
</div>
</div>
</ContextMenu>
);
contextMenu = <IconizedContextMenu
{...contextMenuBelow(this.state.notificationsMenuPosition)}
onFinished={this.onCloseNotificationsMenu}
className="mx_RoomTile_contextMenu"
compact
>
<IconizedContextMenuOptionList first>
<IconizedContextMenuRadio
label={_t("Use default")}
active={state === ALL_MESSAGES}
iconClassName="mx_RoomTile_iconBell"
onClick={this.onClickAllNotifs}
/>
<IconizedContextMenuRadio
label={_t("All messages")}
active={state === ALL_MESSAGES_LOUD}
iconClassName="mx_RoomTile_iconBellDot"
onClick={this.onClickAlertMe}
/>
<IconizedContextMenuRadio
label={_t("Mentions & Keywords")}
active={state === MENTIONS_ONLY}
iconClassName="mx_RoomTile_iconBellMentions"
onClick={this.onClickMentions}
/>
<IconizedContextMenuRadio
label={_t("None")}
active={state === MUTE}
iconClassName="mx_RoomTile_iconBellCrossed"
onClick={this.onClickMute}
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
}
const classes = classNames("mx_RoomTile_notificationsButton", {
@ -400,18 +379,20 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
let contextMenu = null;
if (this.state.generalMenuPosition && this.props.tag === DefaultTagID.Archived) {
contextMenu = (
<ContextMenu {...contextMenuBelow(this.state.generalMenuPosition)} onFinished={this.onCloseGeneralMenu}>
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
<div className="mx_IconizedContextMenu_optionList mx_RoomTile_contextMenu_redRow">
<MenuItem onClick={this.onForgetRoomClick} label={_t("Leave Room")}>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSignOut" />
<span className="mx_IconizedContextMenu_label">{_t("Forget Room")}</span>
</MenuItem>
</div>
</div>
</ContextMenu>
);
contextMenu = <IconizedContextMenu
{...contextMenuBelow(this.state.generalMenuPosition)}
onFinished={this.onCloseGeneralMenu}
className="mx_RoomTile_contextMenu"
compact
>
<IconizedContextMenuOptionList red>
<IconizedContextMenuOption
iconClassName="mx_RoomTile_iconSignOut"
label={_t("Forget Room")}
onClick={this.onForgetRoomClick}
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
} else if (this.state.generalMenuPosition) {
const roomTags = RoomListStore.instance.getTagsForRoom(this.props.room);
@ -421,42 +402,40 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
const isLowPriority = roomTags.includes(DefaultTagID.LowPriority);
const lowPriorityLabel = _t("Low Priority");
contextMenu = (
<ContextMenu {...contextMenuBelow(this.state.generalMenuPosition)} onFinished={this.onCloseGeneralMenu}>
<div className="mx_IconizedContextMenu mx_IconizedContextMenu_compact mx_RoomTile_contextMenu">
<div className="mx_IconizedContextMenu_optionList">
<MenuItemCheckbox
className={isFavorite ? "mx_RoomTile_contextMenu_activeRow" : ""}
onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}
active={isFavorite}
label={favouriteLabel}
>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconStar" />
<span className="mx_IconizedContextMenu_label">{favouriteLabel}</span>
</MenuItemCheckbox>
<MenuItemCheckbox
className={isLowPriority ? "mx_RoomTile_contextMenu_activeRow" : ""}
onClick={(e) => this.onTagRoom(e, DefaultTagID.LowPriority)}
active={isLowPriority}
label={lowPriorityLabel}
>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconArrowDown" />
<span className="mx_IconizedContextMenu_label">{lowPriorityLabel}</span>
</MenuItemCheckbox>
<MenuItem onClick={this.onOpenRoomSettings} label={_t("Settings")}>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSettings" />
<span className="mx_IconizedContextMenu_label">{_t("Settings")}</span>
</MenuItem>
</div>
<div className="mx_IconizedContextMenu_optionList mx_RoomTile_contextMenu_redRow">
<MenuItem onClick={this.onLeaveRoomClick} label={_t("Leave Room")}>
<span className="mx_IconizedContextMenu_icon mx_RoomTile_iconSignOut" />
<span className="mx_IconizedContextMenu_label">{_t("Leave Room")}</span>
</MenuItem>
</div>
</div>
</ContextMenu>
);
contextMenu = <IconizedContextMenu
{...contextMenuBelow(this.state.generalMenuPosition)}
onFinished={this.onCloseGeneralMenu}
className="mx_RoomTile_contextMenu"
compact
>
<IconizedContextMenuOptionList>
<IconizedContextMenuCheckbox
onClick={(e) => this.onTagRoom(e, DefaultTagID.Favourite)}
active={isFavorite}
label={favouriteLabel}
iconClassName="mx_RoomTile_iconStar"
/>
<IconizedContextMenuCheckbox
onClick={(e) => this.onTagRoom(e, DefaultTagID.LowPriority)}
active={isLowPriority}
label={lowPriorityLabel}
iconClassName="mx_RoomTile_iconArrowDown"
/>
<IconizedContextMenuOption
onClick={this.onOpenRoomSettings}
label={_t("Settings")}
iconClassName="mx_RoomTile_iconSettings"
/>
</IconizedContextMenuOptionList>
<IconizedContextMenuOptionList red>
<IconizedContextMenuOption
onClick={this.onLeaveRoomClick}
label={_t("Leave Room")}
iconClassName="mx_RoomTile_iconSignOut"
/>
</IconizedContextMenuOptionList>
</IconizedContextMenu>;
}
return (

View file

@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import classNames from 'classnames';
import {_t, _td} from '../../../languageHandler';
import AppTile from '../elements/AppTile';
import {MatrixClientPeg} from '../../../MatrixClientPeg';
@ -380,14 +381,21 @@ export default class Stickerpicker extends React.Component {
render() {
let stickerPicker;
let stickersButton;
const className = classNames(
"mx_MessageComposer_button",
"mx_MessageComposer_stickers",
"mx_Stickers_hideStickers",
"mx_MessageComposer_button_highlight",
);
if (this.state.showStickers) {
// Show hide-stickers button
stickersButton =
<AccessibleButton
id='stickersButton'
key="controls_hide_stickers"
className="mx_MessageComposer_button mx_MessageComposer_stickers mx_Stickers_hideStickers"
className={className}
onClick={this._onHideStickersClick}
active={this.state.showStickers}
title={_t("Hide Stickers")}
>
</AccessibleButton>;

View file

@ -38,7 +38,7 @@ interface IState {
hover: boolean;
}
// TODO: Remove with community invites in the room list: https://github.com/vector-im/riot-web/issues/14456
// TODO: Remove with community invites in the room list: https://github.com/vector-im/element-web/issues/14456
export default class TemporaryTile extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);

View file

@ -119,8 +119,8 @@ export default createReactClass({
'In future this will be improved.',
) }
{' '}
<a href="https://github.com/vector-im/riot-web/issues/2671" target="_blank" rel="noreferrer noopener">
https://github.com/vector-im/riot-web/issues/2671
<a href="https://github.com/vector-im/element-web/issues/2671" target="_blank" rel="noreferrer noopener">
https://github.com/vector-im/element-web/issues/2671
</a>
</div>,
button: _t("Continue"),

View file

@ -32,6 +32,7 @@ export default class CrossSigningPanel extends React.PureComponent {
error: null,
crossSigningPublicKeysOnDevice: false,
crossSigningPrivateKeysInStorage: false,
masterPrivateKeyCached: false,
selfSigningPrivateKeyCached: false,
userSigningPrivateKeyCached: false,
sessionBackupKeyCached: false,
@ -78,6 +79,7 @@ export default class CrossSigningPanel extends React.PureComponent {
const secretStorage = cli._crypto._secretStorage;
const crossSigningPublicKeysOnDevice = crossSigning.getId();
const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
const masterPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("master"));
const selfSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing"));
const userSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing"));
const sessionBackupKeyFromCache = await cli._crypto.getSessionBackupPrivateKey();
@ -91,6 +93,7 @@ export default class CrossSigningPanel extends React.PureComponent {
this.setState({
crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage,
masterPrivateKeyCached,
selfSigningPrivateKeyCached,
userSigningPrivateKeyCached,
sessionBackupKeyCached,
@ -140,6 +143,7 @@ export default class CrossSigningPanel extends React.PureComponent {
error,
crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage,
masterPrivateKeyCached,
selfSigningPrivateKeyCached,
userSigningPrivateKeyCached,
sessionBackupKeyCached,
@ -235,6 +239,10 @@ export default class CrossSigningPanel extends React.PureComponent {
<td>{_t("Cross-signing private keys:")}</td>
<td>{crossSigningPrivateKeysInStorage ? _t("in secret storage") : _t("not found")}</td>
</tr>
<tr>
<td>{_t("Master private key:")}</td>
<td>{masterPrivateKeyCached ? _t("cached locally") : _t("not found locally")}</td>
</tr>
<tr>
<td>{_t("Self signing private key:")}</td>
<td>{selfSigningPrivateKeyCached ? _t("cached locally") : _t("not found locally")}</td>

View file

@ -160,7 +160,7 @@ export default class EventIndexPanel extends React.Component {
);
} else if (EventIndexPeg.platformHasSupport() && !EventIndexPeg.supportIsInstalled()) {
const nativeLink = (
"https://github.com/vector-im/riot-web/blob/develop/" +
"https://github.com/vector-im/element-web/blob/develop/" +
"docs/native-node-modules.md#" +
"adding-seshat-for-search-in-e2e-encrypted-rooms"
);
@ -194,7 +194,7 @@ export default class EventIndexPanel extends React.Component {
brand,
},
{
'desktopLink': (sub) => <a href="https://riot.im/download/desktop"
'desktopLink': (sub) => <a href="https://element.io/get-started"
target="_blank" rel="noreferrer noopener">{sub}</a>,
},
)

View file

@ -1,5 +1,5 @@
/*
Copyright 2019 The Matrix.org Foundation C.I.C.
Copyright 2019, 2020 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.
@ -14,8 +14,10 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import React from "react";
import {Room} from "matrix-js-sdk/src/models/room";
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
import {_t} from "../../../../../languageHandler";
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
import BridgeTile from "../../BridgeTile";
@ -27,28 +29,26 @@ const BRIDGE_EVENT_TYPES = [
const BRIDGES_LINK = "https://matrix.org/bridges/";
export default class BridgeSettingsTab extends React.Component {
static propTypes = {
roomId: PropTypes.string.isRequired,
};
interface IProps {
roomId: string;
}
_renderBridgeCard(event, room) {
export default class BridgeSettingsTab extends React.Component<IProps> {
private renderBridgeCard(event: MatrixEvent, room: Room) {
const content = event.getContent();
if (!content || !content.channel || !content.protocol) {
return null;
}
return <BridgeTile room={room} ev={event}></BridgeTile>;
return <BridgeTile key={event.getId()} room={room} ev={event} />;
}
static getBridgeStateEvents(roomId) {
static getBridgeStateEvents(roomId: string) {
const client = MatrixClientPeg.get();
const roomState = (client.getRoom(roomId)).currentState;
const roomState = client.getRoom(roomId).currentState;
const bridgeEvents = [].concat(...BRIDGE_EVENT_TYPES.map((typeName) =>
Object.values(roomState.events[typeName] || {}),
return [].concat(...BRIDGE_EVENT_TYPES.map((typeName) =>
Array.from(roomState.events.get(typeName).values()),
));
return bridgeEvents;
}
render() {
@ -58,8 +58,7 @@ export default class BridgeSettingsTab extends React.Component {
const client = MatrixClientPeg.get();
const room = client.getRoom(this.props.roomId);
let content = null;
let content: JSX.Element;
if (bridgeEvents.length > 0) {
content = <div>
<p>{_t(
@ -72,7 +71,7 @@ export default class BridgeSettingsTab extends React.Component {
},
)}</p>
<ul className="mx_RoomSettingsDialog_BridgeList">
{ bridgeEvents.map((event) => this._renderBridgeCard(event, room)) }
{ bridgeEvents.map((event) => this.renderBridgeCard(event, room)) }
</ul>
</div>;
} else {

View file

@ -33,7 +33,7 @@ const plEventsToLabels = {
"m.room.tombstone": _td("Upgrade the room"),
"m.room.encryption": _td("Enable room encryption"),
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
"im.vector.modular.widgets": _td("Modify widgets"),
};
@ -48,7 +48,7 @@ const plEventsToShow = {
"m.room.tombstone": {isState: true},
"m.room.encryption": {isState: true},
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
"im.vector.modular.widgets": {isState: true},
};

View file

@ -22,6 +22,7 @@ import * as sdk from "../../../../..";
import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
import Modal from "../../../../../Modal";
import QuestionDialog from "../../../dialogs/QuestionDialog";
import StyledRadioGroup from '../../../elements/StyledRadioGroup';
import {SettingLevel} from "../../../../../settings/SettingLevel";
export default class SecurityRoomSettingsTab extends React.Component {
@ -99,7 +100,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
{
'a': (sub) => {
return <a rel='noreferrer noopener' target='_blank'
href='https://about.riot.im/help#end-to-end-encryption'>{sub}</a>;
href='https://element.io/help#encryption'>{sub}</a>;
},
},
),
@ -144,7 +145,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
});
};
_onRoomAccessRadioToggle = (ev) => {
_onRoomAccessRadioToggle = (roomAccess) => {
// join_rule
// INVITE | PUBLIC
// ----------------------+----------------
@ -161,7 +162,7 @@ export default class SecurityRoomSettingsTab extends React.Component {
let joinRule = "invite";
let guestAccess = "can_join";
switch (ev.target.value) {
switch (roomAccess) {
case "invite_only":
// no change - use defaults above
break;
@ -190,11 +191,11 @@ export default class SecurityRoomSettingsTab extends React.Component {
});
};
_onHistoryRadioToggle = (ev) => {
_onHistoryRadioToggle = (history) => {
const beforeHistory = this.state.history;
this.setState({history: ev.target.value});
this.setState({history: history});
MatrixClientPeg.get().sendStateEvent(this.props.roomId, "m.room.history_visibility", {
history_visibility: ev.target.value,
history_visibility: history,
}, "").catch((e) => {
console.error(e);
this.setState({history: beforeHistory});
@ -257,27 +258,31 @@ export default class SecurityRoomSettingsTab extends React.Component {
<div>
{guestWarning}
{aliasWarning}
<label>
<input type="radio" name="roomVis" value="invite_only"
disabled={!canChangeAccess}
onChange={this._onRoomAccessRadioToggle}
checked={joinRule !== "public"} />
{_t('Only people who have been invited')}
</label>
<label>
<input type="radio" name="roomVis" value="public_no_guests"
disabled={!canChangeAccess}
onChange={this._onRoomAccessRadioToggle}
checked={joinRule === "public" && guestAccess !== "can_join"} />
{_t('Anyone who knows the room\'s link, apart from guests')}
</label>
<label>
<input type="radio" name="roomVis" value="public_with_guests"
disabled={!canChangeAccess}
onChange={this._onRoomAccessRadioToggle}
checked={joinRule === "public" && guestAccess === "can_join"} />
{_t("Anyone who knows the room's link, including guests")}
</label>
<StyledRadioGroup
name="roomVis"
value={joinRule}
onChange={this._onRoomAccessRadioToggle}
definitions={[
{
value: "invite_only",
disabled: !canChangeAccess,
label: _t('Only people who have been invited'),
checked: joinRule !== "public",
},
{
value: "public_no_guests",
disabled: !canChangeAccess,
label: _t('Anyone who knows the room\'s link, apart from guests'),
checked: joinRule === "public" && guestAccess !== "can_join",
},
{
value: "public_with_guests",
disabled: !canChangeAccess,
label: _t("Anyone who knows the room's link, including guests"),
checked: joinRule === "public" && guestAccess === "can_join",
},
]}
/>
</div>
);
}
@ -294,34 +299,33 @@ export default class SecurityRoomSettingsTab extends React.Component {
{_t('Changes to who can read history will only apply to future messages in this room. ' +
'The visibility of existing history will be unchanged.')}
</div>
<label>
<input type="radio" name="historyVis" value="world_readable"
disabled={!canChangeHistory}
checked={history === "world_readable"}
onChange={this._onHistoryRadioToggle} />
{_t("Anyone")}
</label>
<label>
<input type="radio" name="historyVis" value="shared"
disabled={!canChangeHistory}
checked={history === "shared"}
onChange={this._onHistoryRadioToggle} />
{_t('Members only (since the point in time of selecting this option)')}
</label>
<label>
<input type="radio" name="historyVis" value="invited"
disabled={!canChangeHistory}
checked={history === "invited"}
onChange={this._onHistoryRadioToggle} />
{_t('Members only (since they were invited)')}
</label>
<label >
<input type="radio" name="historyVis" value="joined"
disabled={!canChangeHistory}
checked={history === "joined"}
onChange={this._onHistoryRadioToggle} />
{_t('Members only (since they joined)')}
</label>
<StyledRadioGroup
name="historyVis"
value={history}
onChange={this._onHistoryRadioToggle}
definitions={[
{
value: "world_readable",
disabled: !canChangeHistory,
label: _t("Anyone"),
},
{
value: "shared",
disabled: !canChangeHistory,
label: _t('Members only (since the point in time of selecting this option)'),
},
{
value: "invited",
disabled: !canChangeHistory,
label: _t('Members only (since they were invited)'),
},
{
value: "joined",
disabled: !canChangeHistory,
label: _t('Members only (since they joined)'),
},
]}
/>
</div>
);
}

View file

@ -55,7 +55,7 @@ export default class LabsUserSettingsTab extends React.Component {
_t('Customise your experience with experimental labs features. ' +
'<a>Learn more</a>.', {}, {
'a': (sub) => {
return <a href="https://github.com/vector-im/riot-web/blob/develop/docs/labs.md"
return <a href="https://github.com/vector-im/element-web/blob/develop/docs/labs.md"
rel='noreferrer noopener' target='_blank'>{sub}</a>;
},
})

View file

@ -59,7 +59,7 @@ export function setCaretPosition(editor: HTMLDivElement, model: EditorModel, car
// If the selection matches, it's important to leave it alone.
// Recreating the selection state in at least Chrome can cause
// strange side effects, like touch bar flickering on every key.
// See https://github.com/vector-im/riot-web/issues/9299
// See https://github.com/vector-im/element-web/issues/9299
return;
}
}

View file

@ -158,7 +158,7 @@ function checkDescendInto(node) {
function checkIgnored(n) {
if (n.nodeType === Node.TEXT_NODE) {
// riot adds \n text nodes in a lot of places,
// Element adds \n text nodes in a lot of places,
// which should be ignored
return n.nodeValue === "\n";
} else if (n.nodeType === Node.ELEMENT_NODE) {

View file

@ -14,9 +14,11 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState, useEffect } from 'react';
import {useState, useEffect, DependencyList} from 'react';
export const useAsyncMemo = (fn, deps, initialValue) => {
type Fn<T> = () => Promise<T>;
export const useAsyncMemo = <T>(fn: Fn<T>, deps: DependencyList, initialValue?: T) => {
const [value, setValue] = useState(initialValue);
useEffect(() => {
fn().then(setValue);

View file

@ -15,11 +15,14 @@ limitations under the License.
*/
import {useRef, useEffect} from "react";
import type {EventEmitter} from "events";
type Handler = (...args: any[]) => void;
// Hook to wrap event emitter on and removeListener in hook lifecycle
export const useEventEmitter = (emitter, eventName, handler) => {
export const useEventEmitter = (emitter: EventEmitter, eventName: string | symbol, handler: Handler) => {
// Create a ref that stores handler
const savedHandler = useRef();
const savedHandler = useRef(handler);
// Update ref.current value if handler changes.
useEffect(() => {

View file

@ -18,7 +18,7 @@ import {useEffect, useState} from "react";
import SettingsStore from '../settings/SettingsStore';
// Hook to fetch the value of a setting and dynamically update when it changes
export const useSettingValue = (settingName, roomId = null, excludeDefault = false) => {
export const useSettingValue = (settingName: string, roomId: string = null, excludeDefault = false) => {
const [value, setValue] = useState(SettingsStore.getValue(settingName, roomId, excludeDefault));
useEffect(() => {
@ -35,7 +35,7 @@ export const useSettingValue = (settingName, roomId = null, excludeDefault = fal
};
// Hook to fetch whether a feature is enabled and dynamically update when that changes
export const useFeatureEnabled = (featureName, roomId = null) => {
export const useFeatureEnabled = (featureName: string, roomId: string = null) => {
const [enabled, setEnabled] = useState(SettingsStore.isFeatureEnabled(featureName, roomId));
useEffect(() => {

View file

@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import {useState} from 'react';
import {useState} from "react";
// Hook to simplify toggling of a boolean state value
// Returns value, method to toggle boolean value and method to set the boolean value
export const useStateToggle = (initialValue) => {
const [value, setValue] = useState(Boolean(initialValue));
export const useStateToggle = (initialValue: boolean) => {
const [value, setValue] = useState(initialValue);
const toggleValue = () => {
setValue(!value);
};

View file

@ -117,10 +117,6 @@
"Unable to enable Notifications": "Unable to enable Notifications",
"This email address was not found": "This email address was not found",
"Your email address does not appear to be associated with a Matrix ID on this Homeserver.": "Your email address does not appear to be associated with a Matrix ID on this Homeserver.",
"Use your account to sign in to the latest version": "Use your account to sign in to the latest version",
"Were excited to announce Riot is now Element": "Were excited to announce Riot is now Element",
"Riot is now Element!": "Riot is now Element!",
"Learn More": "Learn More",
"Sign In or Create Account": "Sign In or Create Account",
"Use your account or create a new one to continue.": "Use your account or create a new one to continue.",
"Create Account": "Create Account",
@ -654,9 +650,10 @@
"not found": "not found",
"Cross-signing private keys:": "Cross-signing private keys:",
"in secret storage": "in secret storage",
"Self signing private key:": "Self signing private key:",
"Master private key:": "Master private key:",
"cached locally": "cached locally",
"not found locally": "not found locally",
"Self signing private key:": "Self signing private key:",
"User signing private key:": "User signing private key:",
"Session backup key:": "Session backup key:",
"Secret storage public key:": "Secret storage public key:",
@ -1179,11 +1176,11 @@
"All messages": "All messages",
"Mentions & Keywords": "Mentions & Keywords",
"Notification options": "Notification options",
"Leave Room": "Leave Room",
"Forget Room": "Forget Room",
"Favourited": "Favourited",
"Favourite": "Favourite",
"Low Priority": "Low Priority",
"Leave Room": "Leave Room",
"Room options": "Room options",
"%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.",
"%(count)s unread messages including mentions.|one": "1 unread mention.",
@ -1722,11 +1719,6 @@
"Use this session to verify your new one, granting it access to encrypted messages:": "Use this session to verify your new one, granting it access to encrypted messages:",
"If you didnt sign in to this session, your account may be compromised.": "If you didnt sign in to this session, your account may be compromised.",
"This wasn't me": "This wasn't me",
"Use your account to sign in to the latest version of the app at <a />": "Use your account to sign in to the latest version of the app at <a />",
"Youre already signed in and good to go here, but you can also grab the latest versions of the app on all platforms at <a>element.io/get-started</a>.": "Youre already signed in and good to go here, but you can also grab the latest versions of the app on all platforms at <a>element.io/get-started</a>.",
"Go to Element": "Go to Element",
"Were excited to announce Riot is now Element!": "Were excited to announce Riot is now Element!",
"Learn more at <a>element.io/previously-riot</a>": "Learn more at <a>element.io/previously-riot</a>",
"If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.": "If you run into any bugs or have feedback you'd like to share, please let us know on GitHub.",
"To help avoid duplicate issues, please <existingIssuesLink>view existing issues</existingIssuesLink> first (and add a +1) or <newIssueLink>create a new issue</newIssueLink> if you can't find it.": "To help avoid duplicate issues, please <existingIssuesLink>view existing issues</existingIssuesLink> first (and add a +1) or <newIssueLink>create a new issue</newIssueLink> if you can't find it.",
"Report bugs & give feedback": "Report bugs & give feedback",
@ -1798,7 +1790,7 @@
"Share Community": "Share Community",
"Share Room Message": "Share Room Message",
"Link to selected message": "Link to selected message",
"COPY": "COPY",
"Copy": "Copy",
"Command Help": "Command Help",
"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>.",
"Missing session data": "Missing session data",
@ -1977,7 +1969,8 @@
"Couldn't load page": "Couldn't load page",
"You must <a>register</a> to use this functionality": "You must <a>register</a> to use this functionality",
"You must join the room to see its files": "You must join the room to see its files",
"There are no visible files in this room": "There are no visible files in this room",
"No files visible in this room": "No files visible in this room",
"Attach files from chat or just drag and drop them anywhere in a room.": "Attach files from chat or just drag and drop them anywhere in a room.",
"<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even use 'img' tags\n</p>\n": "<h1>HTML for your community's page</h1>\n<p>\n Use the long description to introduce new members to the community, or distribute\n some important <a href=\"foo\">links</a>\n</p>\n<p>\n You can even use 'img' tags\n</p>\n",
"Add rooms to the community summary": "Add rooms to the community summary",
"Which rooms would you like to add to this summary?": "Which rooms would you like to add to this summary?",
@ -2050,7 +2043,8 @@
"Communities": "Communities",
"Create a new community": "Create a new community",
"Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.": "Create a community to group together users and rooms! Build a custom homepage to mark out your space in the Matrix universe.",
"You have no visible notifications": "You have no visible notifications",
"Youre all caught up": "Youre all caught up",
"You have no visible notifications in this room.": "You have no visible notifications in this room.",
"%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.": "%(brand)s failed to get the protocol list from the homeserver. The homeserver may be too old to support third party networks.",
"%(brand)s failed to get the public room list.": "%(brand)s failed to get the public room list.",
"The homeserver may be unavailable or overloaded.": "The homeserver may be unavailable or overloaded.",
@ -2172,7 +2166,7 @@
"%(brand)s Web": "%(brand)s Web",
"%(brand)s Desktop": "%(brand)s Desktop",
"%(brand)s iOS": "%(brand)s iOS",
"%(brand)s X for Android": "%(brand)s X for Android",
"%(brand)s Android": "%(brand)s Android",
"or another cross-signing capable Matrix client": "or another cross-signing capable Matrix client",
"Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.",
"Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.",
@ -2237,7 +2231,6 @@
"Confirm your recovery passphrase": "Confirm your recovery passphrase",
"Store your Security Key somewhere safe, like a password manager or a safe, as its used to safeguard your encrypted data.": "Store your Security Key somewhere safe, like a password manager or a safe, as its used to safeguard your encrypted data.",
"Download": "Download",
"Copy": "Copy",
"Unable to query secret storage status": "Unable to query secret storage status",
"Retry": "Retry",
"If you cancel now, you may lose encrypted messages & data if you lose access to your logins.": "If you cancel now, you may lose encrypted messages & data if you lose access to your logins.",

View file

@ -145,7 +145,7 @@ export default class EventIndex extends EventEmitter {
const indexManager = PlatformPeg.get().getEventIndexingManager();
if (prevState === "PREPARED" && state === "SYNCING") {
// If our indexer is empty we're most likely running Riot the
// If our indexer is empty we're most likely running Element the
// first time with indexing support or running it with an
// initial sync. Add checkpoints to crawl our encrypted rooms.
const eventIndexWasEmpty = await indexManager.isEventIndexEmpty();

View file

@ -64,7 +64,7 @@ export function _td(s: string): string {
// Wrapper for counterpart's translation function so that it handles nulls and undefineds properly
// Takes the same arguments as counterpart.translate()
function safeCounterpartTranslate(text: string, options?: object) {
// Horrible hack to avoid https://github.com/vector-im/riot-web/issues/4191
// Horrible hack to avoid https://github.com/vector-im/element-web/issues/4191
// The interpolation library that counterpart uses does not support undefined/null
// values and instead will throw an error. This is a problem since everywhere else
// in JS land passing undefined/null will simply stringify instead, and when converting
@ -295,7 +295,7 @@ export function replaceByRegexes(text: string, mapping: IVariables | Tags): stri
// Allow overriding the text displayed when no translation exists
// Currently only used in unit tests to avoid having to load
// the translations in riot-web
// the translations in element-web
export function setMissingEntryGenerator(f: (value: string) => void) {
counterpart.setMissingEntryGenerator(f);
}

View file

@ -72,7 +72,7 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) {
const body = new FormData();
body.append('text', opts.userText || "User did not supply any additional text.");
body.append('app', 'riot-web');
body.append('app', 'element-web');
body.append('version', version);
body.append('user_agent', userAgent);
body.append('installed_pwa', installedPWA);
@ -102,6 +102,8 @@ async function collectBugReport(opts: IOpts = {}, gzipLogs = true) {
body.append("ssss_key_in_account", String(!!(await secretStorage.hasKey())));
const pkCache = client.getCrossSigningCacheCallbacks();
body.append("master_pk_cached",
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("master"))));
body.append("self_signing_pk_cached",
String(!!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing"))));
body.append("user_signing_pk_cached",

View file

@ -19,7 +19,6 @@ import { MatrixClient } from 'matrix-js-sdk/src/client';
import { _td } from '../languageHandler';
import {
AudioNotificationsEnabledController,
NotificationBodyEnabledController,
NotificationsEnabledController,
} from "./controllers/NotificationControllers";
@ -160,7 +159,7 @@ export const SETTINGS: {[setting: string]: ISetting} = {
default: false,
},
"advancedRoomListLogging": {
// TODO: Remove flag before launch: https://github.com/vector-im/riot-web/issues/14231
// TODO: Remove flag before launch: https://github.com/vector-im/element-web/issues/14231
displayName: _td("Enable advanced debugging for the room list"),
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: false,
@ -460,7 +459,6 @@ export const SETTINGS: {[setting: string]: ISetting} = {
"audioNotificationsEnabled": {
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS,
default: true,
controller: new AudioNotificationsEnabledController(),
},
"enableWidgetScreenshots": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
@ -488,13 +486,13 @@ export const SETTINGS: {[setting: string]: ISetting} = {
deny: [],
},
},
// TODO: Remove setting: https://github.com/vector-im/riot-web/issues/14373
// TODO: Remove setting: https://github.com/vector-im/element-web/issues/14373
"RoomList.orderAlphabetically": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
displayName: _td("Order rooms by name"),
default: false,
},
// TODO: Remove setting: https://github.com/vector-im/riot-web/issues/14373
// TODO: Remove setting: https://github.com/vector-im/element-web/issues/14373
"RoomList.orderByImportance": {
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
displayName: _td("Show rooms with unread notifications first"),

View file

@ -79,12 +79,3 @@ export class NotificationBodyEnabledController extends SettingController {
return calculatedValue;
}
}
export class AudioNotificationsEnabledController extends SettingController {
public getValueOverride(level: SettingLevel, roomId: string, calculatedValue: any): any {
if (!getNotifier().isPossible()) return false;
// Note: Audio notifications are *not* enabled by default.
return calculatedValue;
}
}

View file

@ -59,7 +59,7 @@ export default class AccountSettingsHandler extends MatrixClientBackedSettingsHa
} else if (event.getType() === "im.vector.web.settings") {
// Figure out what changed and fire those updates
const prevContent = prevEvent ? prevEvent.getContent() : {};
const changedSettings = objectKeyChanges(prevContent, event.getContent());
const changedSettings = objectKeyChanges<Record<string, any>>(prevContent, event.getContent());
for (const settingName of changedSettings) {
const val = event.getContent()[settingName];
this.watchers.notifyUpdate(settingName, null, SettingLevel.ACCOUNT, val);

View file

@ -59,7 +59,7 @@ export default class RoomAccountSettingsHandler extends MatrixClientBackedSettin
} else if (event.getType() === "im.vector.web.settings") {
// Figure out what changed and fire those updates
const prevContent = prevEvent ? prevEvent.getContent() : {};
const changedSettings = objectKeyChanges(prevContent, event.getContent());
const changedSettings = objectKeyChanges<Record<string, any>>(prevContent, event.getContent());
for (const settingName of changedSettings) {
const val = event.getContent()[settingName];
this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM_ACCOUNT, val);

View file

@ -65,9 +65,10 @@ export default class RoomSettingsHandler extends MatrixClientBackedSettingsHandl
} else if (event.getType() === "im.vector.web.settings") {
// Figure out what changed and fire those updates
const prevContent = prevEvent ? prevEvent.getContent() : {};
const changedSettings = objectKeyChanges(prevContent, event.getContent());
const changedSettings = objectKeyChanges<Record<string, any>>(prevContent, event.getContent());
for (const settingName of changedSettings) {
this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM, event.getContent()[settingName]);
this.watchers.notifyUpdate(settingName, roomId, SettingLevel.ROOM,
event.getContent()[settingName]);
}
}
};

View file

@ -78,7 +78,7 @@ export default class ThemeWatcher {
};
// XXX: forceTheme param added here as local echo appears to be unreliable
// https://github.com/vector-im/riot-web/issues/11443
// https://github.com/vector-im/element-web/issues/11443
public recheck(forceTheme?: string) {
const oldTheme = this.currentTheme;
this.currentTheme = forceTheme === undefined ? this.getEffectiveTheme() : forceTheme;

View file

@ -64,7 +64,7 @@ class ActiveWidgetStore extends EventEmitter {
// Everything else relies on views listening for events and calling setters
// on this class which is terrible. This store should just listen for events
// and keep itself up to date.
// TODO: Enable support for m.widget event type (https://github.com/vector-im/riot-web/issues/13111)
// TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111)
if (ev.getType() !== 'im.vector.modular.widgets') return;
if (ev.getStateKey() === this._persistentWidgetId) {

View file

@ -15,13 +15,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import dis from '../dispatcher/dispatcher';
import React from "react";
import {Store} from 'flux/utils';
import dis from '../dispatcher/dispatcher';
import {MatrixClientPeg} from '../MatrixClientPeg';
import * as sdk from '../index';
import Modal from '../Modal';
import { _t } from '../languageHandler';
import { getCachedRoomIDForAlias, storeRoomAliasInCache } from '../RoomAliasCache';
import {ActionPayload} from "../dispatcher/payloads";
const INITIAL_STATE = {
// Whether we're joining the currently viewed room (see isJoining())
@ -33,6 +37,7 @@ const INITIAL_STATE = {
// The event to scroll to when the room is first viewed
initialEventId: null,
initialEventPixelOffset: null,
// Whether to highlight the initial event
isInitialEventHighlighted: false,
@ -46,6 +51,10 @@ const INITIAL_STATE = {
forwardingEvent: null,
quotingEvent: null,
replyingToEvent: null,
shouldPeek: false,
};
/**
@ -53,21 +62,20 @@ const INITIAL_STATE = {
* with a subset of the js-sdk.
* ```
*/
class RoomViewStore extends Store {
class RoomViewStore extends Store<ActionPayload> {
private state = INITIAL_STATE; // initialize state
constructor() {
super(dis);
// Initialise state
this._state = INITIAL_STATE;
}
_setState(newState) {
setState(newState: Partial<typeof INITIAL_STATE>) {
// If values haven't changed, there's nothing to do.
// This only tries a shallow comparison, so unchanged objects will slip
// through, but that's probably okay for now.
let stateChanged = false;
for (const key of Object.keys(newState)) {
if (this._state[key] !== newState[key]) {
if (this.state[key] !== newState[key]) {
stateChanged = true;
break;
}
@ -76,7 +84,7 @@ class RoomViewStore extends Store {
return;
}
this._state = Object.assign(this._state, newState);
this.state = Object.assign(this.state, newState);
this.__emitChange();
}
@ -89,59 +97,63 @@ class RoomViewStore extends Store {
// - event_offset: 100
// - highlighted: true
case 'view_room':
this._viewRoom(payload);
this.viewRoom(payload);
break;
// for these events blank out the roomId as we are no longer in the RoomView
case 'view_create_group':
case 'view_welcome_page':
case 'view_home_page':
case 'view_my_groups':
case 'view_group':
this._setState({
this.setState({
roomId: null,
roomAlias: null,
});
break;
case 'view_room_error':
this._viewRoomError(payload);
this.viewRoomError(payload);
break;
case 'will_join':
this._setState({
this.setState({
joining: true,
});
break;
case 'cancel_join':
this._setState({
this.setState({
joining: false,
});
break;
// join_room:
// - opts: options for joinRoom
case 'join_room':
this._joinRoom(payload);
this.joinRoom(payload);
break;
case 'join_room_error':
this._joinRoomError(payload);
this.joinRoomError(payload);
break;
case 'join_room_ready':
this._setState({ shouldPeek: false });
this.setState({ shouldPeek: false });
break;
case 'on_client_not_viable':
case 'on_logged_out':
this.reset();
break;
case 'forward_event':
this._setState({
this.setState({
forwardingEvent: payload.event,
});
break;
case 'reply_to_event':
// If currently viewed room does not match the room in which we wish to reply then change rooms
// this can happen when performing a search across all rooms
if (payload.event && payload.event.getRoomId() !== this._state.roomId) {
if (payload.event && payload.event.getRoomId() !== this.state.roomId) {
dis.dispatch({
action: 'view_room',
room_id: payload.event.getRoomId(),
replyingToEvent: payload.event,
});
} else {
this._setState({
this.setState({
replyingToEvent: payload.event,
});
}
@ -149,14 +161,14 @@ class RoomViewStore extends Store {
case 'open_room_settings': {
const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog");
Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, {
roomId: payload.room_id || this._state.roomId,
roomId: payload.room_id || this.state.roomId,
}, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
break;
}
}
}
async _viewRoom(payload) {
private async viewRoom(payload: ActionPayload) {
if (payload.room_id) {
const newState = {
roomId: payload.room_id,
@ -181,18 +193,18 @@ class RoomViewStore extends Store {
newState.replyingToEvent = payload.replyingToEvent;
}
if (this._state.forwardingEvent) {
if (this.state.forwardingEvent) {
dis.dispatch({
action: 'send_event',
room_id: newState.roomId,
event: this._state.forwardingEvent,
event: this.state.forwardingEvent,
});
}
this._setState(newState);
this.setState(newState);
if (payload.auto_join) {
this._joinRoom(payload);
this.joinRoom(payload);
}
} else if (payload.room_alias) {
// Try the room alias to room ID navigation cache first to avoid
@ -201,7 +213,7 @@ class RoomViewStore extends Store {
if (!roomId) {
// Room alias cache miss, so let's ask the homeserver. Resolve the alias
// and then do a second dispatch with the room ID acquired.
this._setState({
this.setState({
roomId: null,
initialEventId: null,
initialEventPixelOffset: null,
@ -238,8 +250,8 @@ class RoomViewStore extends Store {
}
}
_viewRoomError(payload) {
this._setState({
private viewRoomError(payload: ActionPayload) {
this.setState({
roomId: payload.room_id,
roomAlias: payload.room_alias,
roomLoading: false,
@ -247,12 +259,12 @@ class RoomViewStore extends Store {
});
}
_joinRoom(payload) {
this._setState({
private joinRoom(payload: ActionPayload) {
this.setState({
joining: true,
});
MatrixClientPeg.get().joinRoom(
this._state.roomAlias || this._state.roomId, payload.opts,
this.state.roomAlias || this.state.roomId, payload.opts,
).then(() => {
// We do *not* clear the 'joining' flag because the Room object and/or our 'joined' member event may not
// have come down the sync stream yet, and that's the point at which we'd consider the user joined to the
@ -273,7 +285,7 @@ class RoomViewStore extends Store {
{_t("Please contact your homeserver administrator.")}
</div>;
} else if (err.httpStatus === 404) {
const invitingUserId = this._getInvitingUserId(this._state.roomId);
const invitingUserId = this.getInvitingUserId(this.state.roomId);
// only provide a better error message for invites
if (invitingUserId) {
// if the inviting user is on the same HS, there can only be one cause: they left.
@ -292,7 +304,7 @@ class RoomViewStore extends Store {
});
}
_getInvitingUserId(roomId) {
private getInvitingUserId(roomId: string): string {
const cli = MatrixClientPeg.get();
const room = cli.getRoom(roomId);
if (room && room.getMyMembership() === "invite") {
@ -302,45 +314,45 @@ class RoomViewStore extends Store {
}
}
_joinRoomError(payload) {
this._setState({
private joinRoomError(payload: ActionPayload) {
this.setState({
joining: false,
joinError: payload.err,
});
}
reset() {
this._state = Object.assign({}, INITIAL_STATE);
public reset() {
this.state = Object.assign({}, INITIAL_STATE);
}
// The room ID of the room currently being viewed
getRoomId() {
return this._state.roomId;
public getRoomId() {
return this.state.roomId;
}
// The event to scroll to when the room is first viewed
getInitialEventId() {
return this._state.initialEventId;
public getInitialEventId() {
return this.state.initialEventId;
}
// Whether to highlight the initial event
isInitialEventHighlighted() {
return this._state.isInitialEventHighlighted;
public isInitialEventHighlighted() {
return this.state.isInitialEventHighlighted;
}
// The room alias of the room (or null if not originally specified in view_room)
getRoomAlias() {
return this._state.roomAlias;
public getRoomAlias() {
return this.state.roomAlias;
}
// Whether the current room is loading (true whilst resolving an alias)
isRoomLoading() {
return this._state.roomLoading;
public isRoomLoading() {
return this.state.roomLoading;
}
// Any error that has occurred during loading
getRoomLoadError() {
return this._state.roomLoadError;
public getRoomLoadError() {
return this.state.roomLoadError;
}
// True if we're expecting the user to be joined to the room currently being
@ -366,27 +378,27 @@ class RoomViewStore extends Store {
// // show join prompt
// }
// }
isJoining() {
return this._state.joining;
public isJoining() {
return this.state.joining;
}
// Any error that has occurred during joining
getJoinError() {
return this._state.joinError;
public getJoinError() {
return this.state.joinError;
}
// The mxEvent if one is about to be forwarded
getForwardingEvent() {
return this._state.forwardingEvent;
public getForwardingEvent() {
return this.state.forwardingEvent;
}
// The mxEvent if one is currently being replied to/quoted
getQuotingEvent() {
return this._state.replyingToEvent;
public getQuotingEvent() {
return this.state.replyingToEvent;
}
shouldPeek() {
return this._state.shouldPeek;
public shouldPeek() {
return this.state.shouldPeek;
}
}

View file

@ -17,7 +17,7 @@ limitations under the License.
export enum NotificationColor {
// Inverted (None -> Red) because we do integer comparisons on this
None, // nothing special
// TODO: Remove bold with notifications: https://github.com/vector-im/riot-web/issues/14227
// TODO: Remove bold with notifications: https://github.com/vector-im/element-web/issues/14227
Bold, // no badge, show as unread
Grey, // unread notified messages
Red, // unread pings

View file

@ -65,7 +65,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
private readonly watchedSettings = [
'feature_custom_tags',
'advancedRoomListLogging', // TODO: Remove watch: https://github.com/vector-im/riot-web/issues/14602
'advancedRoomListLogging', // TODO: Remove watch: https://github.com/vector-im/element-web/issues/14602
];
constructor() {
@ -161,7 +161,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
await this.algorithm.setStickyRoom(null);
} else if (activeRoom !== this.algorithm.stickyRoom) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Changing sticky room to ${activeRoomId}`);
}
await this.algorithm.setStickyRoom(activeRoom);
@ -205,7 +205,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
if (payload.action === 'setting_updated') {
if (this.watchedSettings.includes(payload.settingName)) {
// TODO: Remove with https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove with https://github.com/vector-im/element-web/issues/14602
if (payload.settingName === "advancedRoomListLogging") {
// Log when the setting changes so we know when it was turned on in the rageshake
const enabled = SettingsStore.getValue("advancedRoomListLogging");
@ -236,7 +236,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
return;
}
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Got own read receipt in ${room.roomId}`);
}
await this.handleRoomUpdate(room, RoomUpdateCause.ReadReceipt);
@ -246,7 +246,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
} else if (payload.action === 'MatrixActions.Room.tags') {
const roomPayload = (<any>payload); // TODO: Type out the dispatcher types
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Got tag change in ${roomPayload.room.roomId}`);
}
await this.handleRoomUpdate(roomPayload.room, RoomUpdateCause.PossibleTagChange);
@ -261,13 +261,13 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
const room = this.matrixClient.getRoom(roomId);
const tryUpdate = async (updatedRoom: Room) => {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Live timeline event ${eventPayload.event.getId()}` +
` in ${updatedRoom.roomId}`);
}
if (eventPayload.event.getType() === 'm.room.tombstone' && eventPayload.event.getStateKey() === '') {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Got tombstone event - trying to remove now-dead room`);
}
const newRoom = this.matrixClient.getRoom(eventPayload.event.getContent()['replacement_room']);
@ -300,7 +300,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
return;
}
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Decrypted timeline event ${eventPayload.event.getId()} in ${roomId}`);
}
await this.handleRoomUpdate(room, RoomUpdateCause.Timeline);
@ -308,7 +308,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
} else if (payload.action === 'MatrixActions.accountData' && payload.event_type === 'm.direct') {
const eventPayload = (<any>payload); // TODO: Type out the dispatcher types
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Received updated DM map`);
}
const dmMap = eventPayload.event.getContent();
@ -335,7 +335,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
const newMembership = getEffectiveMembership(membershipPayload.membership);
if (oldMembership !== EffectiveMembership.Join && newMembership === EffectiveMembership.Join) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Handling new room ${membershipPayload.room.roomId}`);
}
@ -344,7 +344,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
const createEvent = membershipPayload.room.currentState.getStateEvents("m.room.create", "");
if (createEvent && createEvent.getContent()['predecessor']) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Room has a predecessor`);
}
const prevRoom = this.matrixClient.getRoom(createEvent.getContent()['predecessor']['room_id']);
@ -352,7 +352,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
const isSticky = this.algorithm.stickyRoom === prevRoom;
if (isSticky) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Clearing sticky room due to room upgrade`);
}
await this.algorithm.setStickyRoom(null);
@ -361,7 +361,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
// Note: we hit the algorithm instead of our handleRoomUpdate() function to
// avoid redundant updates.
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Removing previous room from room list`);
}
await this.algorithm.handleRoomUpdate(prevRoom, RoomUpdateCause.RoomRemoved);
@ -369,7 +369,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
}
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Adding new room to room list`);
}
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
@ -379,7 +379,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
if (oldMembership !== EffectiveMembership.Invite && newMembership === EffectiveMembership.Invite) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Handling invite to ${membershipPayload.room.roomId}`);
}
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.NewRoom);
@ -390,7 +390,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
// If it's not a join, it's transitioning into a different list (possibly historical)
if (oldMembership !== newMembership) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Handling membership change in ${membershipPayload.room.roomId}`);
}
await this.handleRoomUpdate(membershipPayload.room, RoomUpdateCause.PossibleTagChange);
@ -404,7 +404,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
const shouldUpdate = await this.algorithm.handleRoomUpdate(room, cause);
if (shouldUpdate) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[DEBUG] Room "${room.name}" (${room.roomId}) triggered by ${cause} requires list update`);
}
this.updateFn.mark();
@ -418,7 +418,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
private async setAndPersistTagSorting(tagId: TagID, sort: SortAlgorithm) {
await this.algorithm.setTagSorting(tagId, sort);
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
// TODO: Per-account? https://github.com/vector-im/element-web/issues/14114
localStorage.setItem(`mx_tagSort_${tagId}`, sort);
}
@ -428,7 +428,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
// noinspection JSMethodCanBeStatic
private getStoredTagSorting(tagId: TagID): SortAlgorithm {
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
// TODO: Per-account? https://github.com/vector-im/element-web/issues/14114
return <SortAlgorithm>localStorage.getItem(`mx_tagSort_${tagId}`);
}
@ -462,7 +462,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
private async setAndPersistListOrder(tagId: TagID, order: ListAlgorithm) {
await this.algorithm.setListOrdering(tagId, order);
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
// TODO: Per-account? https://github.com/vector-im/element-web/issues/14114
localStorage.setItem(`mx_listOrder_${tagId}`, order);
}
@ -472,7 +472,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
// noinspection JSMethodCanBeStatic
private getStoredListOrder(tagId: TagID): ListAlgorithm {
// TODO: Per-account? https://github.com/vector-im/riot-web/issues/14114
// TODO: Per-account? https://github.com/vector-im/element-web/issues/14114
return <ListAlgorithm>localStorage.getItem(`mx_listOrder_${tagId}`);
}
@ -521,7 +521,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
private onAlgorithmListUpdated = () => {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log("Underlying algorithm has triggered a list update - marking");
}
this.updateFn.mark();
@ -574,7 +574,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
public addFilter(filter: IFilterCondition): void {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log("Adding filter condition:", filter);
}
this.filterConditions.push(filter);
@ -586,7 +586,7 @@ export class RoomListStoreClass extends AsyncStoreWithClient<IState> {
public removeFilter(filter: IFilterCondition): void {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log("Removing filter condition:", filter);
}
const idx = this.filterConditions.indexOf(filter);

View file

@ -334,7 +334,7 @@ export class Algorithm extends EventEmitter {
newMap[tagId] = allowedRoomsInThisTag;
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[DEBUG] ${newMap[tagId].length}/${rooms.length} rooms filtered into ${tagId}`);
}
}
@ -349,7 +349,7 @@ export class Algorithm extends EventEmitter {
if (!this.hasFilters) return; // don't bother doing work if there's nothing to do
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Recalculating filtered rooms for ${tagId}`);
}
delete this.filteredRooms[tagId];
@ -361,7 +361,7 @@ export class Algorithm extends EventEmitter {
}
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[DEBUG] ${filteredRooms.length}/${rooms.length} rooms filtered into ${tagId}`);
}
}
@ -403,7 +403,7 @@ export class Algorithm extends EventEmitter {
if (!this._cachedStickyRooms || !updatedTag) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Generating clone of cached rooms for sticky room handling`);
}
const stickiedTagMap: ITagMap = {};
@ -417,7 +417,7 @@ export class Algorithm extends EventEmitter {
// Update the tag indicated by the caller, if possible. This is mostly to ensure
// our cache is up to date.
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Replacing cached sticky rooms for ${updatedTag}`);
}
this._cachedStickyRooms[updatedTag] = this.cachedRooms[updatedTag].map(r => r); // shallow clone
@ -429,7 +429,7 @@ export class Algorithm extends EventEmitter {
const sticky = this._stickyRoom;
if (!updatedTag || updatedTag === sticky.tag) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(
`Inserting sticky room ${sticky.room.roomId} at position ${sticky.position} in ${sticky.tag}`,
);
@ -660,7 +660,7 @@ export class Algorithm extends EventEmitter {
*/
public async handleRoomUpdate(room: Room, cause: RoomUpdateCause): Promise<boolean> {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Handle room update for ${room.roomId} called with cause ${cause}`);
}
if (!this.algorithms) throw new Error("Not ready: no algorithms to determine tags from");
@ -720,7 +720,7 @@ export class Algorithm extends EventEmitter {
if (diff.removed.length > 0 || diff.added.length > 0) {
for (const rmTag of diff.removed) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Removing ${room.roomId} from ${rmTag}`);
}
const algorithm: OrderingAlgorithm = this.algorithms[rmTag];
@ -732,7 +732,7 @@ export class Algorithm extends EventEmitter {
}
for (const addTag of diff.added) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Adding ${room.roomId} to ${addTag}`);
}
const algorithm: OrderingAlgorithm = this.algorithms[addTag];
@ -745,14 +745,14 @@ export class Algorithm extends EventEmitter {
this.roomIdsToTags[room.roomId] = newTags;
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Changing update cause for ${room.roomId} to Timeline to sort rooms`);
}
cause = RoomUpdateCause.Timeline;
didTagChange = true;
} else {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`Received no-op update for ${room.roomId} - changing to Timeline update`);
}
cause = RoomUpdateCause.Timeline;
@ -781,7 +781,7 @@ export class Algorithm extends EventEmitter {
if (cause !== RoomUpdateCause.NewRoom && cause !== RoomUpdateCause.RoomRemoved) {
if (this.stickyRoom === room) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.warn(`[RoomListDebug] Received ${cause} update for sticky room ${room.roomId} - ignoring`);
}
return false;
@ -791,14 +791,14 @@ export class Algorithm extends EventEmitter {
if (!this.roomIdsToTags[room.roomId]) {
if (CAUSES_REQUIRING_ROOM.includes(cause)) {
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.warn(`Skipping tag update for ${room.roomId} because we don't know about the room`);
}
return false;
}
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Updating tags for room ${room.roomId} (${room.name})`);
}
@ -812,13 +812,13 @@ export class Algorithm extends EventEmitter {
this.roomIdsToTags[room.roomId] = roomTags;
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Updated tags for ${room.roomId}:`, roomTags);
}
}
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Reached algorithmic handling for ${room.roomId} and cause ${cause}`);
}
@ -843,7 +843,7 @@ export class Algorithm extends EventEmitter {
}
if (SettingsStore.getValue("advancedRoomListLogging")) {
// TODO: Remove debug: https://github.com/vector-im/riot-web/issues/14602
// TODO: Remove debug: https://github.com/vector-im/element-web/issues/14602
console.log(`[RoomListDebug] Finished handling ${room.roomId} with cause ${cause} (changed=${changed})`);
}
return changed;

View file

@ -270,7 +270,7 @@ export class ImportanceAlgorithm extends OrderingAlgorithm {
`!! Room list index corruption: ${lastCat} (i:${indices[lastCat]}) is greater ` +
`than ${thisCat} (i:${indices[thisCat]}) - category indices are likely desynced from reality`);
// TODO: Regenerate index when this happens: https://github.com/vector-im/riot-web/issues/14234
// TODO: Regenerate index when this happens: https://github.com/vector-im/element-web/issues/14234
}
}
}

Some files were not shown because too many files have changed in this diff Show more