Merge branch 'develop' of https://github.com/matrix-org/matrix-react-sdk into travis/download-logs
This commit is contained in:
commit
df980dbf92
173 changed files with 1409 additions and 1502 deletions
10
src/@types/global.d.ts
vendored
10
src/@types/global.d.ts
vendored
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(!).
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
|
@ -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("We’re 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) => {
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 [];
|
||||
}
|
||||
|
|
|
@ -118,7 +118,7 @@ export default class RoomProvider extends AutocompleteProvider {
|
|||
}
|
||||
|
||||
getName() {
|
||||
return '💬 ' + _t('Rooms');
|
||||
return _t('Rooms');
|
||||
}
|
||||
|
||||
renderCompletions(completions: React.ReactNode[]): React.ReactNode {
|
||||
|
|
|
@ -137,7 +137,7 @@ export default class UserProvider extends AutocompleteProvider {
|
|||
}
|
||||
|
||||
getName(): string {
|
||||
return '👥 ' + _t('Users');
|
||||
return _t('Users');
|
||||
}
|
||||
|
||||
_makeUsers() {
|
||||
|
|
|
@ -58,7 +58,7 @@ export enum ChevronFace {
|
|||
None = "none",
|
||||
}
|
||||
|
||||
interface IProps extends IPosition {
|
||||
export interface IProps extends IPosition {
|
||||
menuWidth?: number;
|
||||
menuHeight?: number;
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}`;
|
||||
|
|
|
@ -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('You’re 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>
|
||||
);
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
124
src/components/views/context_menus/IconizedContextMenu.tsx
Normal file
124
src/components/views/context_menus/IconizedContextMenu.tsx
Normal 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;
|
||||
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(
|
||||
"You’re 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("We’re 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>;
|
||||
}
|
||||
}
|
|
@ -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, " +
|
||||
|
|
|
@ -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> </div>
|
||||
</a>
|
||||
<AccessibleTooltipButton
|
||||
title={_t("Copy")}
|
||||
onClick={this.onCopyClick}
|
||||
className="mx_ShareDialog_matrixto_copy"
|
||||
/>
|
||||
</div>
|
||||
{ checkbox }
|
||||
<hr />
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
},
|
||||
|
|
|
@ -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')}
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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> },
|
||||
),
|
||||
];
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>,
|
||||
},
|
||||
)
|
||||
|
|
|
@ -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 {
|
|
@ -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},
|
||||
};
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>;
|
||||
},
|
||||
})
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
|
@ -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(() => {
|
|
@ -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(() => {
|
|
@ -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);
|
||||
};
|
|
@ -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",
|
||||
"We’re excited to announce Riot is now Element": "We’re 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 didn’t sign in to this session, your account may be compromised.": "If you didn’t 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 />",
|
||||
"You’re 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>.": "You’re 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",
|
||||
"We’re excited to announce Riot is now Element!": "We’re 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",
|
||||
"You’re all caught up": "You’re 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 it’s used to safeguard your encrypted data.": "Store your Security Key somewhere safe, like a password manager or a safe, as it’s 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.",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue