mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-17 05:55:00 +08:00
Add basic types
This commit is contained in:
parent
0e92251f70
commit
d7e6f4b4b5
8
src/@types/global.d.ts
vendored
8
src/@types/global.d.ts
vendored
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2020-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -40,6 +40,8 @@ import { WidgetLayoutStore } from "../stores/widgets/WidgetLayoutStore";
|
|||||||
import VoipUserMapper from "../VoipUserMapper";
|
import VoipUserMapper from "../VoipUserMapper";
|
||||||
import {SpaceStoreClass} from "../stores/SpaceStore";
|
import {SpaceStoreClass} from "../stores/SpaceStore";
|
||||||
import {VoiceRecording} from "../voice/VoiceRecording";
|
import {VoiceRecording} from "../voice/VoiceRecording";
|
||||||
|
import TypingStore from "../stores/TypingStore";
|
||||||
|
import { EventIndexPeg } from "../indexing/EventIndexPeg";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
@ -72,11 +74,15 @@ declare global {
|
|||||||
mxVoipUserMapper: VoipUserMapper;
|
mxVoipUserMapper: VoipUserMapper;
|
||||||
mxSpaceStore: SpaceStoreClass;
|
mxSpaceStore: SpaceStoreClass;
|
||||||
mxVoiceRecorder: typeof VoiceRecording;
|
mxVoiceRecorder: typeof VoiceRecording;
|
||||||
|
mxTypingStore: TypingStore;
|
||||||
|
mxEventIndexPeg: EventIndexPeg;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Document {
|
interface Document {
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/Document/hasStorageAccess
|
// https://developer.mozilla.org/en-US/docs/Web/API/Document/hasStorageAccess
|
||||||
hasStorageAccess?: () => Promise<boolean>;
|
hasStorageAccess?: () => Promise<boolean>;
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/Document/requestStorageAccess
|
||||||
|
requestStorageAccess?: () => Promise<undefined>;
|
||||||
|
|
||||||
// Safari & IE11 only have this prefixed: we used prefixed versions
|
// Safari & IE11 only have this prefixed: we used prefixed versions
|
||||||
// previously so let's continue to support them for now
|
// previously so let's continue to support them for now
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016 OpenMarket Ltd
|
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2017 Vector Creations Ltd
|
|
||||||
Copyright 2018 New Vector Ltd
|
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -59,7 +56,7 @@ export type LoginFlow = ISSOFlow | IPasswordFlow;
|
|||||||
// TODO: Move this to JS SDK
|
// TODO: Move this to JS SDK
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
interface ILoginParams {
|
interface ILoginParams {
|
||||||
identifier?: string;
|
identifier?: object;
|
||||||
password?: string;
|
password?: string;
|
||||||
token?: string;
|
token?: string;
|
||||||
device_id?: string;
|
device_id?: string;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016 OpenMarket Ltd
|
Copyright 2016, 2019, 2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -17,7 +16,7 @@ limitations under the License.
|
|||||||
|
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import SettingsStore from "./settings/SettingsStore";
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
import { Service, startTermsFlow, TermsNotSignedError } from './Terms';
|
import { Service, startTermsFlow, TermsInteractionCallback, TermsNotSignedError } from './Terms';
|
||||||
import {MatrixClientPeg} from "./MatrixClientPeg";
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
||||||
import request from "browser-request";
|
import request from "browser-request";
|
||||||
|
|
||||||
@ -31,6 +30,12 @@ const imApiVersion = "1.1";
|
|||||||
// TODO: Generify the name of this class and all components within - it's not just for Scalar.
|
// TODO: Generify the name of this class and all components within - it's not just for Scalar.
|
||||||
|
|
||||||
export default class ScalarAuthClient {
|
export default class ScalarAuthClient {
|
||||||
|
private apiUrl: string;
|
||||||
|
private uiUrl: string;
|
||||||
|
private scalarToken: string;
|
||||||
|
private termsInteractionCallback: TermsInteractionCallback;
|
||||||
|
private isDefaultManager: boolean;
|
||||||
|
|
||||||
constructor(apiUrl, uiUrl) {
|
constructor(apiUrl, uiUrl) {
|
||||||
this.apiUrl = apiUrl;
|
this.apiUrl = apiUrl;
|
||||||
this.uiUrl = uiUrl;
|
this.uiUrl = uiUrl;
|
||||||
@ -154,7 +159,7 @@ export default class ScalarAuthClient {
|
|||||||
parsedImRestUrl.pathname = '';
|
parsedImRestUrl.pathname = '';
|
||||||
return startTermsFlow([new Service(
|
return startTermsFlow([new Service(
|
||||||
SERVICE_TYPES.IM,
|
SERVICE_TYPES.IM,
|
||||||
parsedImRestUrl.format(),
|
url.format(parsedImRestUrl),
|
||||||
token,
|
token,
|
||||||
)], this.termsInteractionCallback).then(() => {
|
)], this.termsInteractionCallback).then(() => {
|
||||||
return token;
|
return token;
|
||||||
@ -243,7 +248,7 @@ export default class ScalarAuthClient {
|
|||||||
disableWidgetAssets(widgetType: WidgetType, widgetId) {
|
disableWidgetAssets(widgetType: WidgetType, widgetId) {
|
||||||
let url = this.apiUrl + '/widgets/set_assets_state';
|
let url = this.apiUrl + '/widgets/set_assets_state';
|
||||||
url = this.getStarterLink(url);
|
url = this.getStarterLink(url);
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
request({
|
request({
|
||||||
method: 'GET', // XXX: Actions shouldn't be GET requests
|
method: 'GET', // XXX: Actions shouldn't be GET requests
|
||||||
uri: url,
|
uri: url,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import {MatrixClientPeg} from './MatrixClientPeg';
|
import {MatrixClientPeg} from './MatrixClientPeg';
|
||||||
import * as sdk from './';
|
import * as sdk from '.';
|
||||||
import Modal from './Modal';
|
import Modal from './Modal';
|
||||||
|
|
||||||
export class TermsNotSignedError extends Error {}
|
export class TermsNotSignedError extends Error {}
|
||||||
@ -27,6 +27,10 @@ export class TermsNotSignedError extends Error {}
|
|||||||
* require agreement from the user before the user can use that service.
|
* require agreement from the user before the user can use that service.
|
||||||
*/
|
*/
|
||||||
export class Service {
|
export class Service {
|
||||||
|
public serviceType: string;
|
||||||
|
public baseUrl: string;
|
||||||
|
public accessToken: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {MatrixClient.SERVICE_TYPES} serviceType The type of service
|
* @param {MatrixClient.SERVICE_TYPES} serviceType The type of service
|
||||||
* @param {string} baseUrl The Base URL of the service (ie. before '/_matrix')
|
* @param {string} baseUrl The Base URL of the service (ie. before '/_matrix')
|
||||||
@ -39,6 +43,26 @@ export class Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Policy {
|
||||||
|
// @ts-ignore: No great way to express indexed types together with other keys
|
||||||
|
version: string;
|
||||||
|
[lang: string]: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
type Policies = {
|
||||||
|
[policy: string]: Policy,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TermsInteractionCallback = (
|
||||||
|
policiesAndServicePairs: {
|
||||||
|
service: Service,
|
||||||
|
policies: Policies,
|
||||||
|
}[],
|
||||||
|
agreedUrls: string[],
|
||||||
|
extraClassNames?: string,
|
||||||
|
) => Promise<string[]>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start a flow where the user is presented with terms & conditions for some services
|
* Start a flow where the user is presented with terms & conditions for some services
|
||||||
*
|
*
|
||||||
@ -51,8 +75,8 @@ export class Service {
|
|||||||
* if they cancel.
|
* if they cancel.
|
||||||
*/
|
*/
|
||||||
export async function startTermsFlow(
|
export async function startTermsFlow(
|
||||||
services,
|
services: Service[],
|
||||||
interactionCallback = dialogTermsInteractionCallback,
|
interactionCallback: TermsInteractionCallback = dialogTermsInteractionCallback,
|
||||||
) {
|
) {
|
||||||
const termsPromises = services.map(
|
const termsPromises = services.map(
|
||||||
(s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl),
|
(s) => MatrixClientPeg.get().getTerms(s.serviceType, s.baseUrl),
|
||||||
@ -77,7 +101,7 @@ export async function startTermsFlow(
|
|||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const terms = await Promise.all(termsPromises);
|
const terms: { policies: Policies }[] = await Promise.all(termsPromises);
|
||||||
const policiesAndServicePairs = terms.map((t, i) => { return { 'service': services[i], 'policies': t.policies }; });
|
const policiesAndServicePairs = terms.map((t, i) => { return { 'service': services[i], 'policies': t.policies }; });
|
||||||
|
|
||||||
// fetch the set of agreed policy URLs from account data
|
// fetch the set of agreed policy URLs from account data
|
||||||
@ -158,10 +182,13 @@ export async function startTermsFlow(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function dialogTermsInteractionCallback(
|
export function dialogTermsInteractionCallback(
|
||||||
policiesAndServicePairs,
|
policiesAndServicePairs: {
|
||||||
agreedUrls,
|
service: Service,
|
||||||
extraClassNames,
|
policies: { [policy: string]: Policy },
|
||||||
) {
|
}[],
|
||||||
|
agreedUrls: string[],
|
||||||
|
extraClassNames?: string,
|
||||||
|
): Promise<string[]> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
console.log("Terms that need agreement", policiesAndServicePairs);
|
console.log("Terms that need agreement", policiesAndServicePairs);
|
||||||
const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog");
|
const TermsDialog = sdk.getComponent("views.dialogs.TermsDialog");
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2020-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +16,6 @@ limitations under the License.
|
|||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import * as sdk from '../../../../index';
|
import * as sdk from '../../../../index';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { _t } from '../../../../languageHandler';
|
import { _t } from '../../../../languageHandler';
|
||||||
import SdkConfig from '../../../../SdkConfig';
|
import SdkConfig from '../../../../SdkConfig';
|
||||||
import SettingsStore from "../../../../settings/SettingsStore";
|
import SettingsStore from "../../../../settings/SettingsStore";
|
||||||
@ -26,14 +25,23 @@ import {formatBytes, formatCountLong} from "../../../../utils/FormattingUtils";
|
|||||||
import EventIndexPeg from "../../../../indexing/EventIndexPeg";
|
import EventIndexPeg from "../../../../indexing/EventIndexPeg";
|
||||||
import {SettingLevel} from "../../../../settings/SettingLevel";
|
import {SettingLevel} from "../../../../settings/SettingLevel";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
onFinished: (boolean) => {},
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
eventIndexSize: number;
|
||||||
|
eventCount: number;
|
||||||
|
crawlingRoomsCount: number;
|
||||||
|
roomCount: number;
|
||||||
|
currentRoom: string;
|
||||||
|
crawlerSleepTime: number;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allows the user to introspect the event index state and disable it.
|
* Allows the user to introspect the event index state and disable it.
|
||||||
*/
|
*/
|
||||||
export default class ManageEventIndexDialog extends React.Component {
|
export default class ManageEventIndexDialog extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
|
||||||
onFinished: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
@ -84,7 +92,7 @@ export default class ManageEventIndexDialog extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount(): void {
|
async componentDidMount(): Promise<void> {
|
||||||
let eventIndexSize = 0;
|
let eventIndexSize = 0;
|
||||||
let crawlingRoomsCount = 0;
|
let crawlingRoomsCount = 0;
|
||||||
let roomCount = 0;
|
let roomCount = 0;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016, 2017, 2018, 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -94,7 +94,7 @@ interface IState {
|
|||||||
// be seeing.
|
// be seeing.
|
||||||
serverIsAlive: boolean;
|
serverIsAlive: boolean;
|
||||||
serverErrorIsFatal: boolean;
|
serverErrorIsFatal: boolean;
|
||||||
serverDeadError: string;
|
serverDeadError?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015, 2016, 2017, 2018, 2019, 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -95,7 +95,7 @@ interface IState {
|
|||||||
// be seeing.
|
// be seeing.
|
||||||
serverIsAlive: boolean;
|
serverIsAlive: boolean;
|
||||||
serverErrorIsFatal: boolean;
|
serverErrorIsFatal: boolean;
|
||||||
serverDeadError: string;
|
serverDeadError?: ReactNode;
|
||||||
|
|
||||||
// Our matrix client - part of state because we can't render the UI auth
|
// Our matrix client - part of state because we can't render the UI auth
|
||||||
// component without it.
|
// component without it.
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -15,14 +15,13 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {_t} from '../../../languageHandler';
|
import {_t} from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import * as Lifecycle from '../../../Lifecycle';
|
import * as Lifecycle from '../../../Lifecycle';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import {sendLoginRequest} from "../../../Login";
|
import {ISSOFlow, LoginFlow, sendLoginRequest} from "../../../Login";
|
||||||
import AuthPage from "../../views/auth/AuthPage";
|
import AuthPage from "../../views/auth/AuthPage";
|
||||||
import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "../../../BasePlatform";
|
import {SSO_HOMESERVER_URL_KEY, SSO_ID_SERVER_URL_KEY} from "../../../BasePlatform";
|
||||||
import SSOButtons from "../../views/elements/SSOButtons";
|
import SSOButtons from "../../views/elements/SSOButtons";
|
||||||
@ -42,26 +41,38 @@ const FLOWS_TO_VIEWS = {
|
|||||||
"m.login.sso": LOGIN_VIEW.SSO,
|
"m.login.sso": LOGIN_VIEW.SSO,
|
||||||
};
|
};
|
||||||
|
|
||||||
@replaceableComponent("structures.auth.SoftLogout")
|
interface IProps {
|
||||||
export default class SoftLogout extends React.Component {
|
// Query parameters from MatrixChat
|
||||||
static propTypes = {
|
realQueryParams: {
|
||||||
// Query parameters from MatrixChat
|
loginToken?: string;
|
||||||
realQueryParams: PropTypes.object, // {loginToken}
|
|
||||||
|
|
||||||
// Called when the SSO login completes
|
|
||||||
onTokenLoginCompleted: PropTypes.func,
|
|
||||||
};
|
};
|
||||||
|
fragmentAfterLogin?: string;
|
||||||
|
|
||||||
constructor() {
|
// Called when the SSO login completes
|
||||||
super();
|
onTokenLoginCompleted: () => void,
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
loginView: number;
|
||||||
|
keyBackupNeeded: boolean;
|
||||||
|
busy: boolean;
|
||||||
|
password: string;
|
||||||
|
errorText: string;
|
||||||
|
flows: LoginFlow[];
|
||||||
|
}
|
||||||
|
|
||||||
|
@replaceableComponent("structures.auth.SoftLogout")
|
||||||
|
export default class SoftLogout extends React.Component<IProps, IState> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
loginView: LOGIN_VIEW.LOADING,
|
loginView: LOGIN_VIEW.LOADING,
|
||||||
keyBackupNeeded: true, // assume we do while we figure it out (see componentDidMount)
|
keyBackupNeeded: true, // assume we do while we figure it out (see componentDidMount)
|
||||||
|
|
||||||
busy: false,
|
busy: false,
|
||||||
password: "",
|
password: "",
|
||||||
errorText: "",
|
errorText: "",
|
||||||
|
flows: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +258,7 @@ export default class SoftLogout extends React.Component {
|
|||||||
} // else we already have a message and should use it (key backup warning)
|
} // else we already have a message and should use it (key backup warning)
|
||||||
|
|
||||||
const loginType = this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso";
|
const loginType = this.state.loginView === LOGIN_VIEW.CAS ? "cas" : "sso";
|
||||||
const flow = this.state.flows.find(flow => flow.type === "m.login." + loginType);
|
const flow = this.state.flows.find(flow => flow.type === "m.login." + loginType) as ISSOFlow;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2020-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -110,7 +110,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
|
|||||||
console.error(e);
|
console.error(e);
|
||||||
|
|
||||||
const stateForError = AutoDiscoveryUtils.authComponentStateForError(e);
|
const stateForError = AutoDiscoveryUtils.authComponentStateForError(e);
|
||||||
if (stateForError.isFatalError) {
|
if (stateForError.serverErrorIsFatal) {
|
||||||
let error = _t("Unable to validate homeserver");
|
let error = _t("Unable to validate homeserver");
|
||||||
if (e.translatedMessage) {
|
if (e.translatedMessage) {
|
||||||
error = e.translatedMessage;
|
error = e.translatedMessage;
|
||||||
@ -168,7 +168,7 @@ export default class ServerPickerDialog extends React.PureComponent<IProps, ISta
|
|||||||
text = _t("Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.");
|
text = _t("Matrix.org is the biggest public homeserver in the world, so it’s a good place for many.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let defaultServerName = this.defaultServer.hsName;
|
let defaultServerName: React.ReactNode = this.defaultServer.hsName;
|
||||||
if (this.defaultServer.hsNameIsDifferent) {
|
if (this.defaultServer.hsNameIsDifferent) {
|
||||||
defaultServerName = (
|
defaultServerName = (
|
||||||
<TextWithTooltip class="mx_Login_underlinedServerName" tooltip={this.defaultServer.hsUrl}>
|
<TextWithTooltip class="mx_Login_underlinedServerName" tooltip={this.defaultServer.hsUrl}>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2020-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -67,7 +67,7 @@ const ServerPicker = ({ title, dialogTitle, serverConfig, onServerConfigChange }
|
|||||||
</AccessibleButton>;
|
</AccessibleButton>;
|
||||||
}
|
}
|
||||||
|
|
||||||
let serverName = serverConfig.isNameResolvable ? serverConfig.hsName : serverConfig.hsUrl;
|
let serverName: React.ReactNode = serverConfig.isNameResolvable ? serverConfig.hsName : serverConfig.hsUrl;
|
||||||
if (serverConfig.hsNameIsDifferent) {
|
if (serverConfig.hsNameIsDifferent) {
|
||||||
serverName = <TextWithTooltip class="mx_Login_underlinedServerName" tooltip={serverConfig.hsUrl}>
|
serverName = <TextWithTooltip class="mx_Login_underlinedServerName" tooltip={serverConfig.hsUrl}>
|
||||||
{serverConfig.hsName}
|
{serverConfig.hsName}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
|
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -15,11 +15,13 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {createRef} from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import {EventType} from "matrix-js-sdk/src/@types/event";
|
|
||||||
import {EventStatus} from 'matrix-js-sdk/src/models/event';
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { EventStatus, MatrixEvent } from "matrix-js-sdk/src/models/event";
|
||||||
|
import { Relations } from "matrix-js-sdk/src/models/relations";
|
||||||
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
|
|
||||||
import ReplyThread from "../elements/ReplyThread";
|
import ReplyThread from "../elements/ReplyThread";
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
@ -27,7 +29,7 @@ import * as TextForEvent from "../../../TextForEvent";
|
|||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import {Layout, LayoutPropType} from "../../../settings/Layout";
|
import {Layout} from "../../../settings/Layout";
|
||||||
import {formatTime} from "../../../DateUtils";
|
import {formatTime} from "../../../DateUtils";
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import {ALL_RULE_TYPES} from "../../../mjolnir/BanList";
|
import {ALL_RULE_TYPES} from "../../../mjolnir/BanList";
|
||||||
@ -40,6 +42,8 @@ import {WIDGET_LAYOUT_EVENT_TYPE} from "../../../stores/widgets/WidgetLayoutStor
|
|||||||
import {objectHasDiff} from "../../../utils/objects";
|
import {objectHasDiff} from "../../../utils/objects";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
import Tooltip from "../elements/Tooltip";
|
import Tooltip from "../elements/Tooltip";
|
||||||
|
import { EditorStateTransfer } from "../../../utils/EditorStateTransfer";
|
||||||
|
import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||||
|
|
||||||
const eventTileTypes = {
|
const eventTileTypes = {
|
||||||
[EventType.RoomMessage]: 'messages.MessageEvent',
|
[EventType.RoomMessage]: 'messages.MessageEvent',
|
||||||
@ -169,101 +173,130 @@ const MAX_READ_AVATARS = 5;
|
|||||||
// | '--------------------------------------' |
|
// | '--------------------------------------' |
|
||||||
// '----------------------------------------------------------'
|
// '----------------------------------------------------------'
|
||||||
|
|
||||||
|
interface IReadReceiptProps {
|
||||||
|
userId: string;
|
||||||
|
roomMember: RoomMember;
|
||||||
|
ts: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
// the MatrixEvent to show
|
||||||
|
mxEvent: MatrixEvent;
|
||||||
|
|
||||||
|
// true if mxEvent is redacted. This is a prop because using mxEvent.isRedacted()
|
||||||
|
// might not be enough when deciding shouldComponentUpdate - prevProps.mxEvent
|
||||||
|
// references the same this.props.mxEvent.
|
||||||
|
isRedacted?: boolean;
|
||||||
|
|
||||||
|
// true if this is a continuation of the previous event (which has the
|
||||||
|
// effect of not showing another avatar/displayname
|
||||||
|
continuation?: boolean;
|
||||||
|
|
||||||
|
// true if this is the last event in the timeline (which has the effect
|
||||||
|
// of always showing the timestamp)
|
||||||
|
last?: boolean;
|
||||||
|
|
||||||
|
// true if the event is the last event in a section (adds a css class for
|
||||||
|
// targeting)
|
||||||
|
lastInSection?: boolean;
|
||||||
|
|
||||||
|
// True if the event is the last successful (sent) event.
|
||||||
|
lastSuccessful?: boolean;
|
||||||
|
|
||||||
|
// true if this is search context (which has the effect of greying out
|
||||||
|
// the text
|
||||||
|
contextual?: boolean;
|
||||||
|
|
||||||
|
// a list of words to highlight, ordered by longest first
|
||||||
|
highlights?: string[];
|
||||||
|
|
||||||
|
// link URL for the highlights
|
||||||
|
highlightLink?: string;
|
||||||
|
|
||||||
|
// should show URL previews for this event
|
||||||
|
showUrlPreview?: boolean;
|
||||||
|
|
||||||
|
// is this the focused event
|
||||||
|
isSelectedEvent?: boolean;
|
||||||
|
|
||||||
|
// callback called when dynamic content in events are loaded
|
||||||
|
onHeightChanged?: () => void,
|
||||||
|
|
||||||
|
// a list of read-receipts we should show. Each object has a 'roomMember' and 'ts'.
|
||||||
|
readReceipts?: IReadReceiptProps[],
|
||||||
|
|
||||||
|
// opaque readreceipt info for each userId; used by ReadReceiptMarker
|
||||||
|
// to manage its animations. Should be an empty object when the room
|
||||||
|
// first loads
|
||||||
|
readReceiptMap?: any,
|
||||||
|
|
||||||
|
// A function which is used to check if the parent panel is being
|
||||||
|
// unmounted, to avoid unnecessary work. Should return true if we
|
||||||
|
// are being unmounted.
|
||||||
|
checkUnmounting?: () => boolean,
|
||||||
|
|
||||||
|
// the status of this event - ie, mxEvent.status. Denormalised to here so
|
||||||
|
// that we can tell when it changes.
|
||||||
|
eventSendStatus?: string;
|
||||||
|
|
||||||
|
// the shape of the tile. by default, the layout is intended for the
|
||||||
|
// normal room timeline. alternative values are: "file_list", "file_grid"
|
||||||
|
// and "notif". This could be done by CSS, but it'd be horribly inefficient.
|
||||||
|
// It could also be done by subclassing EventTile, but that'd be quite
|
||||||
|
// boiilerplatey. So just make the necessary render decisions conditional
|
||||||
|
// for now.
|
||||||
|
tileShape?: string;
|
||||||
|
|
||||||
|
// show twelve hour timestamps
|
||||||
|
isTwelveHour?: boolean;
|
||||||
|
|
||||||
|
// helper function to access relations for this event
|
||||||
|
getRelationsForEvent?: (eventId: string, relationType: string, eventType: string) => Relations,
|
||||||
|
|
||||||
|
// whether to show reactions for this event
|
||||||
|
showReactions?: boolean;
|
||||||
|
|
||||||
|
// which layout to use
|
||||||
|
layout: Layout,
|
||||||
|
|
||||||
|
// whether or not to show flair at all
|
||||||
|
enableFlair?: boolean;
|
||||||
|
|
||||||
|
// whether or not to show read receipts
|
||||||
|
showReadReceipts?: boolean;
|
||||||
|
|
||||||
|
// Used while editing, to pass the event, and to preserve editor state
|
||||||
|
// from one editor instance to another when remounting the editor
|
||||||
|
// upon receiving the remote echo for an unsent event.
|
||||||
|
editState?: EditorStateTransfer;
|
||||||
|
|
||||||
|
// Event ID of the event replacing the content of this event, if any
|
||||||
|
replacingEventId?: string;
|
||||||
|
|
||||||
|
// Helper to build permalinks for the room
|
||||||
|
permalinkCreator?: RoomPermalinkCreator;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
// Whether the action bar is focused.
|
||||||
|
actionBarFocused: boolean;
|
||||||
|
// Whether all read receipts are being displayed. If not, only display
|
||||||
|
// a truncation of them.
|
||||||
|
allReadAvatars: boolean;
|
||||||
|
// Whether the event's sender has been verified.
|
||||||
|
verified: string;
|
||||||
|
// Whether onRequestKeysClick has been called since mounting.
|
||||||
|
previouslyRequestedKeys: boolean;
|
||||||
|
// The Relations model from the JS SDK for reactions to `mxEvent`
|
||||||
|
reactions: Relations;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.EventTile")
|
@replaceableComponent("views.rooms.EventTile")
|
||||||
export default class EventTile extends React.Component {
|
export default class EventTile extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
private _suppressReadReceiptAnimation: boolean;
|
||||||
/* the MatrixEvent to show */
|
private _isListeningForReceipts: boolean;
|
||||||
mxEvent: PropTypes.object.isRequired,
|
private _tile = React.createRef();
|
||||||
|
private _replyThread = React.createRef();
|
||||||
/* true if mxEvent is redacted. This is a prop because using mxEvent.isRedacted()
|
|
||||||
* might not be enough when deciding shouldComponentUpdate - prevProps.mxEvent
|
|
||||||
* references the same this.props.mxEvent.
|
|
||||||
*/
|
|
||||||
isRedacted: PropTypes.bool,
|
|
||||||
|
|
||||||
/* true if this is a continuation of the previous event (which has the
|
|
||||||
* effect of not showing another avatar/displayname
|
|
||||||
*/
|
|
||||||
continuation: PropTypes.bool,
|
|
||||||
|
|
||||||
/* true if this is the last event in the timeline (which has the effect
|
|
||||||
* of always showing the timestamp)
|
|
||||||
*/
|
|
||||||
last: PropTypes.bool,
|
|
||||||
|
|
||||||
// true if the event is the last event in a section (adds a css class for
|
|
||||||
// targeting)
|
|
||||||
lastInSection: PropTypes.bool,
|
|
||||||
|
|
||||||
// True if the event is the last successful (sent) event.
|
|
||||||
isLastSuccessful: PropTypes.bool,
|
|
||||||
|
|
||||||
/* true if this is search context (which has the effect of greying out
|
|
||||||
* the text
|
|
||||||
*/
|
|
||||||
contextual: PropTypes.bool,
|
|
||||||
|
|
||||||
/* a list of words to highlight, ordered by longest first */
|
|
||||||
highlights: PropTypes.array,
|
|
||||||
|
|
||||||
/* link URL for the highlights */
|
|
||||||
highlightLink: PropTypes.string,
|
|
||||||
|
|
||||||
/* should show URL previews for this event */
|
|
||||||
showUrlPreview: PropTypes.bool,
|
|
||||||
|
|
||||||
/* is this the focused event */
|
|
||||||
isSelectedEvent: PropTypes.bool,
|
|
||||||
|
|
||||||
/* callback called when dynamic content in events are loaded */
|
|
||||||
onHeightChanged: PropTypes.func,
|
|
||||||
|
|
||||||
/* a list of read-receipts we should show. Each object has a 'roomMember' and 'ts'. */
|
|
||||||
readReceipts: PropTypes.arrayOf(PropTypes.object),
|
|
||||||
|
|
||||||
/* opaque readreceipt info for each userId; used by ReadReceiptMarker
|
|
||||||
* to manage its animations. Should be an empty object when the room
|
|
||||||
* first loads
|
|
||||||
*/
|
|
||||||
readReceiptMap: PropTypes.object,
|
|
||||||
|
|
||||||
/* A function which is used to check if the parent panel is being
|
|
||||||
* unmounted, to avoid unnecessary work. Should return true if we
|
|
||||||
* are being unmounted.
|
|
||||||
*/
|
|
||||||
checkUnmounting: PropTypes.func,
|
|
||||||
|
|
||||||
/* the status of this event - ie, mxEvent.status. Denormalised to here so
|
|
||||||
* that we can tell when it changes. */
|
|
||||||
eventSendStatus: PropTypes.string,
|
|
||||||
|
|
||||||
/* the shape of the tile. by default, the layout is intended for the
|
|
||||||
* normal room timeline. alternative values are: "file_list", "file_grid"
|
|
||||||
* and "notif". This could be done by CSS, but it'd be horribly inefficient.
|
|
||||||
* It could also be done by subclassing EventTile, but that'd be quite
|
|
||||||
* boiilerplatey. So just make the necessary render decisions conditional
|
|
||||||
* for now.
|
|
||||||
*/
|
|
||||||
tileShape: PropTypes.string,
|
|
||||||
|
|
||||||
// show twelve hour timestamps
|
|
||||||
isTwelveHour: PropTypes.bool,
|
|
||||||
|
|
||||||
// helper function to access relations for this event
|
|
||||||
getRelationsForEvent: PropTypes.func,
|
|
||||||
|
|
||||||
// whether to show reactions for this event
|
|
||||||
showReactions: PropTypes.bool,
|
|
||||||
|
|
||||||
// which layout to use
|
|
||||||
layout: LayoutPropType,
|
|
||||||
|
|
||||||
// whether or not to show flair at all
|
|
||||||
enableFlair: PropTypes.bool,
|
|
||||||
|
|
||||||
// whether or not to show read receipts
|
|
||||||
showReadReceipts: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
// no-op function because onHeightChanged is optional yet some sub-components assume its existence
|
// no-op function because onHeightChanged is optional yet some sub-components assume its existence
|
||||||
@ -292,9 +325,6 @@ export default class EventTile extends React.Component {
|
|||||||
// don't do RR animations until we are mounted
|
// don't do RR animations until we are mounted
|
||||||
this._suppressReadReceiptAnimation = true;
|
this._suppressReadReceiptAnimation = true;
|
||||||
|
|
||||||
this._tile = createRef();
|
|
||||||
this._replyThread = createRef();
|
|
||||||
|
|
||||||
// Throughout the component we manage a read receipt listener to see if our tile still
|
// Throughout the component we manage a read receipt listener to see if our tile still
|
||||||
// qualifies for a "sent" or "sending" state (based on their relevant conditions). We
|
// qualifies for a "sent" or "sending" state (based on their relevant conditions). We
|
||||||
// don't want to over-subscribe to the read receipt events being fired, so we use a flag
|
// don't want to over-subscribe to the read receipt events being fired, so we use a flag
|
||||||
@ -1190,14 +1220,18 @@ function E2ePadlockUnauthenticated(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class E2ePadlock extends React.Component {
|
interface IE2ePadlockProps {
|
||||||
static propTypes = {
|
icon: string;
|
||||||
icon: PropTypes.string.isRequired,
|
title: string;
|
||||||
title: PropTypes.string.isRequired,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
interface IE2ePadlockState {
|
||||||
super();
|
hover: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class E2ePadlock extends React.Component<IE2ePadlockProps, IE2ePadlockState> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hover: false,
|
hover: false,
|
||||||
@ -1215,14 +1249,13 @@ class E2ePadlock extends React.Component {
|
|||||||
render() {
|
render() {
|
||||||
let tooltip = null;
|
let tooltip = null;
|
||||||
if (this.state.hover) {
|
if (this.state.hover) {
|
||||||
tooltip = <Tooltip className="mx_EventTile_e2eIcon_tooltip" label={this.props.title} dir="auto" />;
|
tooltip = <Tooltip className="mx_EventTile_e2eIcon_tooltip" label={this.props.title} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const classes = `mx_EventTile_e2eIcon mx_EventTile_e2eIcon_${this.props.icon}`;
|
const classes = `mx_EventTile_e2eIcon mx_EventTile_e2eIcon_${this.props.icon}`;
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={classes}
|
className={classes}
|
||||||
onClick={this.onClick}
|
|
||||||
onMouseEnter={this.onHoverStart}
|
onMouseEnter={this.onHoverStart}
|
||||||
onMouseLeave={this.onHoverEnd}
|
onMouseLeave={this.onHoverEnd}
|
||||||
>{tooltip}</div>
|
>{tooltip}</div>
|
||||||
@ -1239,8 +1272,8 @@ interface ISentReceiptState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SentReceipt extends React.PureComponent<ISentReceiptProps, ISentReceiptState> {
|
class SentReceipt extends React.PureComponent<ISentReceiptProps, ISentReceiptState> {
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
hover: false,
|
hover: false,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2015-2018, 2020, 2021 The Matrix.org Foundation C.I.C.
|
Copyright 2015-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -13,15 +13,17 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React, {createRef} from 'react';
|
import React from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
|
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||||
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
|
import {RoomMember} from "matrix-js-sdk/src/models/room-member";
|
||||||
import dis from '../../../dispatcher/dispatcher';
|
import dis from '../../../dispatcher/dispatcher';
|
||||||
import Stickerpicker from './Stickerpicker';
|
import Stickerpicker from './Stickerpicker';
|
||||||
import { makeRoomPermalink } from '../../../utils/permalinks/Permalinks';
|
import { makeRoomPermalink, RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks';
|
||||||
import ContentMessages from '../../../ContentMessages';
|
import ContentMessages from '../../../ContentMessages';
|
||||||
import E2EIcon from './E2EIcon';
|
import E2EIcon from './E2EIcon';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
@ -35,19 +37,26 @@ import VoiceRecordComposerTile from "./VoiceRecordComposerTile";
|
|||||||
import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
|
import {VoiceRecordingStore} from "../../../stores/VoiceRecordingStore";
|
||||||
import {RecordingState} from "../../../voice/VoiceRecording";
|
import {RecordingState} from "../../../voice/VoiceRecording";
|
||||||
import Tooltip, {Alignment} from "../elements/Tooltip";
|
import Tooltip, {Alignment} from "../elements/Tooltip";
|
||||||
|
import ResizeNotifier from "../../../utils/ResizeNotifier";
|
||||||
|
import { E2EStatus } from '../../../utils/ShieldUtils';
|
||||||
|
import SendMessageComposer from "./SendMessageComposer";
|
||||||
|
|
||||||
function ComposerAvatar(props) {
|
interface IComposerAvatarProps {
|
||||||
|
me: object;
|
||||||
|
}
|
||||||
|
|
||||||
|
function ComposerAvatar(props: IComposerAvatarProps) {
|
||||||
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
||||||
return <div className="mx_MessageComposer_avatar">
|
return <div className="mx_MessageComposer_avatar">
|
||||||
<MemberStatusMessageAvatar member={props.me} width={24} height={24} />
|
<MemberStatusMessageAvatar member={props.me} width={24} height={24} />
|
||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
ComposerAvatar.propTypes = {
|
interface ISendButtonProps {
|
||||||
me: PropTypes.object.isRequired,
|
onClick: () => void;
|
||||||
};
|
}
|
||||||
|
|
||||||
function SendButton(props) {
|
function SendButton(props: ISendButtonProps) {
|
||||||
return (
|
return (
|
||||||
<AccessibleTooltipButton
|
<AccessibleTooltipButton
|
||||||
className="mx_MessageComposer_sendMessage"
|
className="mx_MessageComposer_sendMessage"
|
||||||
@ -57,10 +66,6 @@ function SendButton(props) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SendButton.propTypes = {
|
|
||||||
onClick: PropTypes.func.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
const EmojiButton = ({addEmoji}) => {
|
const EmojiButton = ({addEmoji}) => {
|
||||||
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
const [menuDisplayed, button, openMenu, closeMenu] = useContextMenu();
|
||||||
|
|
||||||
@ -68,7 +73,7 @@ const EmojiButton = ({addEmoji}) => {
|
|||||||
if (menuDisplayed) {
|
if (menuDisplayed) {
|
||||||
const buttonRect = button.current.getBoundingClientRect();
|
const buttonRect = button.current.getBoundingClientRect();
|
||||||
const EmojiPicker = sdk.getComponent('emojipicker.EmojiPicker');
|
const EmojiPicker = sdk.getComponent('emojipicker.EmojiPicker');
|
||||||
contextMenu = <ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu} catchTab={false}>
|
contextMenu = <ContextMenu {...aboveLeftOf(buttonRect)} onFinished={closeMenu}>
|
||||||
<EmojiPicker onChoose={addEmoji} showQuickReactions={true} />
|
<EmojiPicker onChoose={addEmoji} showQuickReactions={true} />
|
||||||
</ContextMenu>;
|
</ContextMenu>;
|
||||||
}
|
}
|
||||||
@ -98,17 +103,17 @@ const EmojiButton = ({addEmoji}) => {
|
|||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UploadButton extends React.Component {
|
interface IUploadButtonProps {
|
||||||
static propTypes = {
|
roomId: string;
|
||||||
roomId: PropTypes.string.isRequired,
|
}
|
||||||
}
|
|
||||||
|
class UploadButton extends React.Component<IUploadButtonProps> {
|
||||||
|
private _uploadInput = React.createRef<HTMLInputElement>();
|
||||||
|
private _dispatcherRef: string;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.onUploadClick = this.onUploadClick.bind(this);
|
|
||||||
this.onUploadFileInputChange = this.onUploadFileInputChange.bind(this);
|
|
||||||
|
|
||||||
this._uploadInput = createRef();
|
|
||||||
this._dispatcherRef = dis.register(this.onAction);
|
this._dispatcherRef = dis.register(this.onAction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,13 +121,13 @@ class UploadButton extends React.Component {
|
|||||||
dis.unregister(this._dispatcherRef);
|
dis.unregister(this._dispatcherRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction = payload => {
|
private onAction = payload => {
|
||||||
if (payload.action === "upload_file") {
|
if (payload.action === "upload_file") {
|
||||||
this.onUploadClick();
|
this.onUploadClick();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onUploadClick(ev) {
|
private onUploadClick = () => {
|
||||||
if (MatrixClientPeg.get().isGuest()) {
|
if (MatrixClientPeg.get().isGuest()) {
|
||||||
dis.dispatch({action: 'require_registration'});
|
dis.dispatch({action: 'require_registration'});
|
||||||
return;
|
return;
|
||||||
@ -130,7 +135,7 @@ class UploadButton extends React.Component {
|
|||||||
this._uploadInput.current.click();
|
this._uploadInput.current.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
onUploadFileInputChange(ev) {
|
private onUploadFileInputChange = (ev) => {
|
||||||
if (ev.target.files.length === 0) return;
|
if (ev.target.files.length === 0) return;
|
||||||
|
|
||||||
// take a copy so we can safely reset the value of the form control
|
// take a copy so we can safely reset the value of the form control
|
||||||
@ -171,19 +176,34 @@ class UploadButton extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
room: Room;
|
||||||
|
resizeNotifier: ResizeNotifier;
|
||||||
|
permalinkCreator: RoomPermalinkCreator;
|
||||||
|
replyToEvent?: MatrixEvent;
|
||||||
|
e2eStatus?: E2EStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
tombstone: MatrixEvent;
|
||||||
|
canSendMessages: boolean;
|
||||||
|
isComposerEmpty: boolean;
|
||||||
|
haveRecording: boolean;
|
||||||
|
recordingTimeLeftSeconds?: number;
|
||||||
|
me?: RoomMember;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.MessageComposer")
|
@replaceableComponent("views.rooms.MessageComposer")
|
||||||
export default class MessageComposer extends React.Component {
|
export default class MessageComposer extends React.Component<IProps, IState> {
|
||||||
|
private dispatcherRef: string;
|
||||||
|
private messageComposerInput: SendMessageComposer;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
this.onInputStateChanged = this.onInputStateChanged.bind(this);
|
|
||||||
this._onRoomStateEvents = this._onRoomStateEvents.bind(this);
|
|
||||||
this._onTombstoneClick = this._onTombstoneClick.bind(this);
|
|
||||||
this.renderPlaceholderText = this.renderPlaceholderText.bind(this);
|
|
||||||
VoiceRecordingStore.instance.on(UPDATE_EVENT, this._onVoiceStoreUpdate);
|
VoiceRecordingStore.instance.on(UPDATE_EVENT, this._onVoiceStoreUpdate);
|
||||||
this._dispatcherRef = null;
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
tombstone: this._getRoomTombstone(),
|
tombstone: this.getRoomTombstone(),
|
||||||
canSendMessages: this.props.room.maySendMessage(),
|
canSendMessages: this.props.room.maySendMessage(),
|
||||||
isComposerEmpty: true,
|
isComposerEmpty: true,
|
||||||
haveRecording: false,
|
haveRecording: false,
|
||||||
@ -191,7 +211,13 @@ export default class MessageComposer extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
onAction = (payload) => {
|
componentDidMount() {
|
||||||
|
this.dispatcherRef = dis.register(this.onAction);
|
||||||
|
MatrixClientPeg.get().on("RoomState.events", this.onRoomStateEvents);
|
||||||
|
this.waitForOwnMember();
|
||||||
|
}
|
||||||
|
|
||||||
|
private onAction = (payload) => {
|
||||||
if (payload.action === 'reply_to_event') {
|
if (payload.action === 'reply_to_event') {
|
||||||
// add a timeout for the reply preview to be rendered, so
|
// add a timeout for the reply preview to be rendered, so
|
||||||
// that the ScrollPanel listening to the resizeNotifier can
|
// that the ScrollPanel listening to the resizeNotifier can
|
||||||
@ -203,13 +229,7 @@ export default class MessageComposer extends React.Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount() {
|
private waitForOwnMember() {
|
||||||
this.dispatcherRef = dis.register(this.onAction);
|
|
||||||
MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents);
|
|
||||||
this._waitForOwnMember();
|
|
||||||
}
|
|
||||||
|
|
||||||
_waitForOwnMember() {
|
|
||||||
// if we have the member already, do that
|
// if we have the member already, do that
|
||||||
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
|
const me = this.props.room.getMember(MatrixClientPeg.get().getUserId());
|
||||||
if (me) {
|
if (me) {
|
||||||
@ -227,34 +247,28 @@ export default class MessageComposer extends React.Component {
|
|||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
if (MatrixClientPeg.get()) {
|
if (MatrixClientPeg.get()) {
|
||||||
MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents);
|
MatrixClientPeg.get().removeListener("RoomState.events", this.onRoomStateEvents);
|
||||||
}
|
}
|
||||||
VoiceRecordingStore.instance.off(UPDATE_EVENT, this._onVoiceStoreUpdate);
|
VoiceRecordingStore.instance.off(UPDATE_EVENT, this._onVoiceStoreUpdate);
|
||||||
dis.unregister(this.dispatcherRef);
|
dis.unregister(this.dispatcherRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onRoomStateEvents(ev, state) {
|
private onRoomStateEvents = (ev, state) => {
|
||||||
if (ev.getRoomId() !== this.props.room.roomId) return;
|
if (ev.getRoomId() !== this.props.room.roomId) return;
|
||||||
|
|
||||||
if (ev.getType() === 'm.room.tombstone') {
|
if (ev.getType() === 'm.room.tombstone') {
|
||||||
this.setState({tombstone: this._getRoomTombstone()});
|
this.setState({tombstone: this.getRoomTombstone()});
|
||||||
}
|
}
|
||||||
if (ev.getType() === 'm.room.power_levels') {
|
if (ev.getType() === 'm.room.power_levels') {
|
||||||
this.setState({canSendMessages: this.props.room.maySendMessage()});
|
this.setState({canSendMessages: this.props.room.maySendMessage()});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getRoomTombstone() {
|
private getRoomTombstone() {
|
||||||
return this.props.room.currentState.getStateEvents('m.room.tombstone', '');
|
return this.props.room.currentState.getStateEvents('m.room.tombstone', '');
|
||||||
}
|
}
|
||||||
|
|
||||||
onInputStateChanged(inputState) {
|
private onTombstoneClick = (ev) => {
|
||||||
// Merge the new input state with old to support partial updates
|
|
||||||
inputState = Object.assign({}, this.state.inputState, inputState);
|
|
||||||
this.setState({inputState});
|
|
||||||
}
|
|
||||||
|
|
||||||
_onTombstoneClick(ev) {
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
|
|
||||||
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
|
const replacementRoomId = this.state.tombstone.getContent()['replacement_room'];
|
||||||
@ -284,7 +298,7 @@ export default class MessageComposer extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPlaceholderText() {
|
private renderPlaceholderText = () => {
|
||||||
if (this.props.replyToEvent) {
|
if (this.props.replyToEvent) {
|
||||||
if (this.props.e2eStatus) {
|
if (this.props.e2eStatus) {
|
||||||
return _t('Send an encrypted reply…');
|
return _t('Send an encrypted reply…');
|
||||||
@ -386,7 +400,7 @@ export default class MessageComposer extends React.Component {
|
|||||||
const continuesLink = replacementRoomId ? (
|
const continuesLink = replacementRoomId ? (
|
||||||
<a href={makeRoomPermalink(replacementRoomId)}
|
<a href={makeRoomPermalink(replacementRoomId)}
|
||||||
className="mx_MessageComposer_roomReplaced_link"
|
className="mx_MessageComposer_roomReplaced_link"
|
||||||
onClick={this._onTombstoneClick}
|
onClick={this.onTombstoneClick}
|
||||||
>
|
>
|
||||||
{_t("The conversation continues here.")}
|
{_t("The conversation continues here.")}
|
||||||
</a>
|
</a>
|
||||||
@ -433,14 +447,3 @@ export default class MessageComposer extends React.Component {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageComposer.propTypes = {
|
|
||||||
// js-sdk Room object
|
|
||||||
room: PropTypes.object.isRequired,
|
|
||||||
|
|
||||||
// string representing the current voip call state
|
|
||||||
callState: PropTypes.string,
|
|
||||||
|
|
||||||
// string representing the current room app drawer state
|
|
||||||
showApps: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd.
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -15,9 +15,9 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
import {MatrixEvent} from "matrix-js-sdk/src/models/event";
|
||||||
|
import {Room} from "matrix-js-sdk/src/models/room";
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import dis from "../../../dispatcher/dispatcher";
|
import dis from "../../../dispatcher/dispatcher";
|
||||||
import * as sdk from "../../../index";
|
import * as sdk from "../../../index";
|
||||||
@ -27,11 +27,22 @@ import RoomAvatar from "../avatars/RoomAvatar";
|
|||||||
import RoomName from "../elements/RoomName";
|
import RoomName from "../elements/RoomName";
|
||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
event: MatrixEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
stateKey: string;
|
||||||
|
roomId: string;
|
||||||
|
displayName: string;
|
||||||
|
invited: boolean;
|
||||||
|
canKick: boolean;
|
||||||
|
senderName: string;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.rooms.ThirdPartyMemberInfo")
|
@replaceableComponent("views.rooms.ThirdPartyMemberInfo")
|
||||||
export default class ThirdPartyMemberInfo extends React.Component {
|
export default class ThirdPartyMemberInfo extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
private room: Room;
|
||||||
event: PropTypes.instanceOf(MatrixEvent).isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2020-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -28,10 +28,17 @@ import {SettingLevel} from "../../../settings/SettingLevel";
|
|||||||
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../utils/replaceableComponent";
|
||||||
import SeshatResetDialog from '../dialogs/SeshatResetDialog';
|
import SeshatResetDialog from '../dialogs/SeshatResetDialog';
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
enabling: boolean;
|
||||||
|
eventIndexSize: number;
|
||||||
|
roomCount: number;
|
||||||
|
eventIndexingEnabled: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.EventIndexPanel")
|
@replaceableComponent("views.settings.EventIndexPanel")
|
||||||
export default class EventIndexPanel extends React.Component {
|
export default class EventIndexPanel extends React.Component<{}, IState> {
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
enabling: false,
|
enabling: false,
|
||||||
@ -68,7 +75,7 @@ export default class EventIndexPanel extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
this.updateState();
|
this.updateState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,6 +111,8 @@ export default class EventIndexPanel extends React.Component {
|
|||||||
|
|
||||||
_onManage = async () => {
|
_onManage = async () => {
|
||||||
Modal.createTrackedDialogAsync('Message search', 'Message search',
|
Modal.createTrackedDialogAsync('Message search', 'Message search',
|
||||||
|
// @ts-ignore: TS doesn't seem to like the type of this now that it
|
||||||
|
// has also been converted to TS as well, but I can't figure out why...
|
||||||
import('../../../async-components/views/dialogs/eventindex/ManageEventIndexDialog'),
|
import('../../../async-components/views/dialogs/eventindex/ManageEventIndexDialog'),
|
||||||
{
|
{
|
||||||
onFinished: () => {},
|
onFinished: () => {},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +16,6 @@ limitations under the License.
|
|||||||
|
|
||||||
import url from 'url';
|
import url from 'url';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {_t} from "../../../languageHandler";
|
import {_t} from "../../../languageHandler";
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../MatrixClientPeg";
|
||||||
@ -59,16 +58,28 @@ async function checkIdentityServerUrl(u) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.SetIdServer")
|
interface IProps {
|
||||||
export default class SetIdServer extends React.Component {
|
// Whether or not the ID server is missing terms. This affects the text
|
||||||
static propTypes = {
|
// shown to the user.
|
||||||
// Whether or not the ID server is missing terms. This affects the text
|
missingTerms: boolean;
|
||||||
// shown to the user.
|
}
|
||||||
missingTerms: PropTypes.bool,
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
interface IState {
|
||||||
super();
|
defaultIdServer?: string;
|
||||||
|
currentClientIdServer: string;
|
||||||
|
idServer?: string;
|
||||||
|
error?: string;
|
||||||
|
busy: boolean;
|
||||||
|
disconnectBusy: boolean;
|
||||||
|
checking: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
@replaceableComponent("views.settings.SetIdServer")
|
||||||
|
export default class SetIdServer extends React.Component<IProps, IState> {
|
||||||
|
private dispatcherRef: string;
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
let defaultIdServer = '';
|
let defaultIdServer = '';
|
||||||
if (!MatrixClientPeg.get().getIdentityServerUrl() && getDefaultIdentityServerUrl()) {
|
if (!MatrixClientPeg.get().getIdentityServerUrl() && getDefaultIdentityServerUrl()) {
|
||||||
@ -371,7 +382,7 @@ export default class SetIdServer extends React.Component {
|
|||||||
|
|
||||||
let discoSection;
|
let discoSection;
|
||||||
if (idServerUrl) {
|
if (idServerUrl) {
|
||||||
let discoButtonContent = _t("Disconnect");
|
let discoButtonContent: React.ReactNode = _t("Disconnect");
|
||||||
let discoBodyText = _t(
|
let discoBodyText = _t(
|
||||||
"Disconnecting from your identity server will mean you " +
|
"Disconnecting from your identity server will mean you " +
|
||||||
"won't be discoverable by other users and you won't be " +
|
"won't be discoverable by other users and you won't be " +
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -15,7 +15,6 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {_t, _td} from "../../../../../languageHandler";
|
import {_t, _td} from "../../../../../languageHandler";
|
||||||
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
||||||
import * as sdk from "../../../../..";
|
import * as sdk from "../../../../..";
|
||||||
@ -23,6 +22,7 @@ import AccessibleButton from "../../../elements/AccessibleButton";
|
|||||||
import Modal from "../../../../../Modal";
|
import Modal from "../../../../../Modal";
|
||||||
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
||||||
import {EventType} from "matrix-js-sdk/src/@types/event";
|
import {EventType} from "matrix-js-sdk/src/@types/event";
|
||||||
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
|
|
||||||
const plEventsToLabels = {
|
const plEventsToLabels = {
|
||||||
// These will be translated for us later.
|
// These will be translated for us later.
|
||||||
@ -63,14 +63,14 @@ function parseIntWithDefault(val, def) {
|
|||||||
return isNaN(res) ? def : res;
|
return isNaN(res) ? def : res;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class BannedUser extends React.Component {
|
interface IBannedUserProps {
|
||||||
static propTypes = {
|
canUnban: boolean;
|
||||||
canUnban: PropTypes.bool,
|
member: RoomMember;
|
||||||
member: PropTypes.object.isRequired, // js-sdk RoomMember
|
by: string;
|
||||||
by: PropTypes.string.isRequired,
|
reason: string;
|
||||||
reason: PropTypes.string,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
export class BannedUser extends React.Component<IBannedUserProps> {
|
||||||
_onUnbanClick = (e) => {
|
_onUnbanClick = (e) => {
|
||||||
MatrixClientPeg.get().unban(this.props.member.roomId, this.props.member.userId).catch((err) => {
|
MatrixClientPeg.get().unban(this.props.member.roomId, this.props.member.userId).catch((err) => {
|
||||||
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
const ErrorDialog = sdk.getComponent("dialogs.ErrorDialog");
|
||||||
@ -107,12 +107,12 @@ export class BannedUser extends React.Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.tabs.room.RolesRoomSettingsTab")
|
interface IProps {
|
||||||
export default class RolesRoomSettingsTab extends React.Component {
|
roomId: string;
|
||||||
static propTypes = {
|
}
|
||||||
roomId: PropTypes.string.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
@replaceableComponent("views.settings.tabs.room.RolesRoomSettingsTab")
|
||||||
|
export default class RolesRoomSettingsTab extends React.Component<IProps> {
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
MatrixClientPeg.get().on("RoomState.members", this._onRoomMembership);
|
MatrixClientPeg.get().on("RoomState.members", this._onRoomMembership);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -15,7 +15,6 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {_t} from "../../../../../languageHandler";
|
import {_t} from "../../../../../languageHandler";
|
||||||
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
||||||
import * as sdk from "../../../../..";
|
import * as sdk from "../../../../..";
|
||||||
@ -26,16 +25,28 @@ import StyledRadioGroup from '../../../elements/StyledRadioGroup';
|
|||||||
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
||||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||||
import {UIFeature} from "../../../../../settings/UIFeature";
|
import {UIFeature} from "../../../../../settings/UIFeature";
|
||||||
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
type JoinRule = "public" | "knock" | "invite" | "private";
|
||||||
|
type GuestAccess = "can_join" | "forbidden";
|
||||||
|
type History = "invited" | "joined" | "shared" | "world_readable";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
roomId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
joinRule: JoinRule;
|
||||||
|
guestAccess: GuestAccess;
|
||||||
|
history: History;
|
||||||
|
hasAliases: boolean;
|
||||||
|
encrypted: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.tabs.room.SecurityRoomSettingsTab")
|
@replaceableComponent("views.settings.tabs.room.SecurityRoomSettingsTab")
|
||||||
export default class SecurityRoomSettingsTab extends React.Component {
|
export default class SecurityRoomSettingsTab extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
constructor(props) {
|
||||||
roomId: PropTypes.string.isRequired,
|
super(props);
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
joinRule: "invite",
|
joinRule: "invite",
|
||||||
@ -47,23 +58,23 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: [REACT-WARNING] Move this to constructor
|
// TODO: [REACT-WARNING] Move this to constructor
|
||||||
async UNSAFE_componentWillMount(): void { // eslint-disable-line camelcase
|
async UNSAFE_componentWillMount(): Promise<void> { // eslint-disable-line camelcase
|
||||||
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
|
MatrixClientPeg.get().on("RoomState.events", this._onStateEvent);
|
||||||
|
|
||||||
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
const room = MatrixClientPeg.get().getRoom(this.props.roomId);
|
||||||
const state = room.currentState;
|
const state = room.currentState;
|
||||||
|
|
||||||
const joinRule = this._pullContentPropertyFromEvent(
|
const joinRule: JoinRule = this._pullContentPropertyFromEvent(
|
||||||
state.getStateEvents("m.room.join_rules", ""),
|
state.getStateEvents("m.room.join_rules", ""),
|
||||||
'join_rule',
|
'join_rule',
|
||||||
'invite',
|
'invite',
|
||||||
);
|
);
|
||||||
const guestAccess = this._pullContentPropertyFromEvent(
|
const guestAccess: GuestAccess = this._pullContentPropertyFromEvent(
|
||||||
state.getStateEvents("m.room.guest_access", ""),
|
state.getStateEvents("m.room.guest_access", ""),
|
||||||
'guest_access',
|
'guest_access',
|
||||||
'forbidden',
|
'forbidden',
|
||||||
);
|
);
|
||||||
const history = this._pullContentPropertyFromEvent(
|
const history: History = this._pullContentPropertyFromEvent(
|
||||||
state.getStateEvents("m.room.history_visibility", ""),
|
state.getStateEvents("m.room.history_visibility", ""),
|
||||||
'history_visibility',
|
'history_visibility',
|
||||||
'shared',
|
'shared',
|
||||||
@ -163,8 +174,8 @@ export default class SecurityRoomSettingsTab extends React.Component {
|
|||||||
// invite them, you clearly want them to join, whether they're a
|
// invite them, you clearly want them to join, whether they're a
|
||||||
// guest or not. In practice, guest_access should probably have
|
// guest or not. In practice, guest_access should probably have
|
||||||
// been implemented as part of the join_rules enum.
|
// been implemented as part of the join_rules enum.
|
||||||
let joinRule = "invite";
|
let joinRule: JoinRule = "invite";
|
||||||
let guestAccess = "can_join";
|
let guestAccess: GuestAccess = "can_join";
|
||||||
|
|
||||||
switch (roomAccess) {
|
switch (roomAccess) {
|
||||||
case "invite_only":
|
case "invite_only":
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -16,7 +15,6 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import {_t, getCurrentLanguage} from "../../../../../languageHandler";
|
import {_t, getCurrentLanguage} from "../../../../../languageHandler";
|
||||||
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
||||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||||
@ -27,16 +25,21 @@ import * as sdk from "../../../../..";
|
|||||||
import PlatformPeg from "../../../../../PlatformPeg";
|
import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
|
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
|
||||||
import UpdateCheckButton from "../../UpdateCheckButton";
|
import UpdateCheckButton from "../../UpdateCheckButton";
|
||||||
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
closeSettingsFn: () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
appVersion: string;
|
||||||
|
canUpdate: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.tabs.user.HelpUserSettingsTab")
|
@replaceableComponent("views.settings.tabs.user.HelpUserSettingsTab")
|
||||||
export default class HelpUserSettingsTab extends React.Component {
|
export default class HelpUserSettingsTab extends React.Component<IProps, IState> {
|
||||||
static propTypes = {
|
constructor(props) {
|
||||||
closeSettingsFn: PropTypes.func.isRequired,
|
super(props);
|
||||||
};
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
appVersion: null,
|
appVersion: null,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -25,10 +25,16 @@ import {MatrixClientPeg} from "../../../../../MatrixClientPeg";
|
|||||||
import * as sdk from "../../../../../index";
|
import * as sdk from "../../../../../index";
|
||||||
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
busy: boolean;
|
||||||
|
newPersonalRule: string;
|
||||||
|
newList: string;
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.tabs.user.MjolnirUserSettingsTab")
|
@replaceableComponent("views.settings.tabs.user.MjolnirUserSettingsTab")
|
||||||
export default class MjolnirUserSettingsTab extends React.Component {
|
export default class MjolnirUserSettingsTab extends React.Component<{}, IState> {
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
busy: false,
|
busy: false,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -23,10 +23,24 @@ import Field from "../../../elements/Field";
|
|||||||
import * as sdk from "../../../../..";
|
import * as sdk from "../../../../..";
|
||||||
import PlatformPeg from "../../../../../PlatformPeg";
|
import PlatformPeg from "../../../../../PlatformPeg";
|
||||||
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
||||||
import {replaceableComponent} from "../../../../../utils/replaceableComponent";
|
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
autoLaunch: boolean;
|
||||||
|
autoLaunchSupported: boolean;
|
||||||
|
warnBeforeExit: boolean;
|
||||||
|
warnBeforeExitSupported: boolean;
|
||||||
|
alwaysShowMenuBarSupported: boolean;
|
||||||
|
alwaysShowMenuBar: boolean;
|
||||||
|
minimizeToTraySupported: boolean;
|
||||||
|
minimizeToTray: boolean;
|
||||||
|
autocompleteDelay: string,
|
||||||
|
readMarkerInViewThresholdMs: string,
|
||||||
|
readMarkerOutOfViewThresholdMs: string,
|
||||||
|
}
|
||||||
|
|
||||||
@replaceableComponent("views.settings.tabs.user.PreferencesUserSettingsTab")
|
@replaceableComponent("views.settings.tabs.user.PreferencesUserSettingsTab")
|
||||||
export default class PreferencesUserSettingsTab extends React.Component {
|
export default class PreferencesUserSettingsTab extends React.Component<{}, IState> {
|
||||||
static ROOM_LIST_SETTINGS = [
|
static ROOM_LIST_SETTINGS = [
|
||||||
'breadcrumbs',
|
'breadcrumbs',
|
||||||
];
|
];
|
||||||
@ -68,8 +82,8 @@ export default class PreferencesUserSettingsTab extends React.Component {
|
|||||||
// Autocomplete delay (niche text box)
|
// Autocomplete delay (niche text box)
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor() {
|
constructor(props) {
|
||||||
super();
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
autoLaunch: false,
|
autoLaunch: false,
|
||||||
@ -89,7 +103,7 @@ export default class PreferencesUserSettingsTab extends React.Component {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount(): void {
|
async componentDidMount(): Promise<void> {
|
||||||
const platform = PlatformPeg.get();
|
const platform = PlatformPeg.get();
|
||||||
|
|
||||||
const autoLaunchSupported = await platform.supportsAutoLaunch();
|
const autoLaunchSupported = await platform.supportsAutoLaunch();
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -133,6 +133,10 @@ export default abstract class BaseEventIndexManager {
|
|||||||
throw new Error("Unimplemented");
|
throw new Error("Unimplemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async isEventIndexEmpty(): Promise<boolean> {
|
||||||
|
throw new Error("Unimplemented");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if our event index is empty.
|
* Check if our event index is empty.
|
||||||
*/
|
*/
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -27,11 +27,16 @@ import {SettingLevel} from "../settings/SettingLevel";
|
|||||||
|
|
||||||
const INDEX_VERSION = 1;
|
const INDEX_VERSION = 1;
|
||||||
|
|
||||||
class EventIndexPeg {
|
export class EventIndexPeg {
|
||||||
|
public index: EventIndex;
|
||||||
|
public error: Error;
|
||||||
|
|
||||||
|
private _supportIsInstalled: boolean;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.index = null;
|
this.index = null;
|
||||||
this._supportIsInstalled = false;
|
|
||||||
this.error = null;
|
this.error = null;
|
||||||
|
this._supportIsInstalled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -181,7 +186,7 @@ class EventIndexPeg {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!global.mxEventIndexPeg) {
|
if (!window.mxEventIndexPeg) {
|
||||||
global.mxEventIndexPeg = new EventIndexPeg();
|
window.mxEventIndexPeg = new EventIndexPeg();
|
||||||
}
|
}
|
||||||
export default global.mxEventIndexPeg;
|
export default window.mxEventIndexPeg;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
Copyright 2019, 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -25,15 +25,23 @@ const TYPING_SERVER_TIMEOUT = 30000;
|
|||||||
* Tracks typing state for users.
|
* Tracks typing state for users.
|
||||||
*/
|
*/
|
||||||
export default class TypingStore {
|
export default class TypingStore {
|
||||||
|
private _typingStates: {
|
||||||
|
[roomId: string]: {
|
||||||
|
isTyping: boolean,
|
||||||
|
userTimer: Timer,
|
||||||
|
serverTimer: Timer,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
static sharedInstance(): TypingStore {
|
static sharedInstance(): TypingStore {
|
||||||
if (global.mxTypingStore === undefined) {
|
if (window.mxTypingStore === undefined) {
|
||||||
global.mxTypingStore = new TypingStore();
|
window.mxTypingStore = new TypingStore();
|
||||||
}
|
}
|
||||||
return global.mxTypingStore;
|
return window.mxTypingStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018-2021 The Matrix.org Foundation C.I.C.
|
||||||
Copyright 2019 The Matrix.org Foundation C.I.C.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -16,6 +15,7 @@ limitations under the License.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
import { IWidget } from 'matrix-widget-api';
|
||||||
import {WidgetType} from "../widgets/WidgetType";
|
import {WidgetType} from "../widgets/WidgetType";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,6 +23,12 @@ import {WidgetType} from "../widgets/WidgetType";
|
|||||||
* proxying through state from the js-sdk.
|
* proxying through state from the js-sdk.
|
||||||
*/
|
*/
|
||||||
class WidgetEchoStore extends EventEmitter {
|
class WidgetEchoStore extends EventEmitter {
|
||||||
|
private _roomWidgetEcho: {
|
||||||
|
[roomId: string]: {
|
||||||
|
[widgetId: string]: IWidget,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
@ -65,7 +71,7 @@ class WidgetEchoStore extends EventEmitter {
|
|||||||
return echoedWidgets;
|
return echoedWidgets;
|
||||||
}
|
}
|
||||||
|
|
||||||
roomHasPendingWidgetsOfType(roomId, currentRoomWidgets, type: WidgetType) {
|
roomHasPendingWidgetsOfType(roomId, currentRoomWidgets, type?: WidgetType) {
|
||||||
const roomEchoState = Object.assign({}, this._roomWidgetEcho[roomId]);
|
const roomEchoState = Object.assign({}, this._roomWidgetEcho[roomId]);
|
||||||
|
|
||||||
// any widget IDs that are already in the room are not pending, so
|
// any widget IDs that are already in the room are not pending, so
|
||||||
@ -89,7 +95,7 @@ class WidgetEchoStore extends EventEmitter {
|
|||||||
return this.roomHasPendingWidgetsOfType(roomId, currentRoomWidgets);
|
return this.roomHasPendingWidgetsOfType(roomId, currentRoomWidgets);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRoomWidgetEcho(roomId, widgetId, state) {
|
setRoomWidgetEcho(roomId: string, widgetId: string, state: IWidget) {
|
||||||
if (this._roomWidgetEcho[roomId] === undefined) this._roomWidgetEcho[roomId] = {};
|
if (this._roomWidgetEcho[roomId] === undefined) this._roomWidgetEcho[roomId] = {};
|
||||||
|
|
||||||
this._roomWidgetEcho[roomId][widgetId] = state;
|
this._roomWidgetEcho[roomId][widgetId] = state;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019-2021 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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -15,13 +14,13 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React from 'react';
|
import React, { ReactNode } from 'react';
|
||||||
import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
|
import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery";
|
||||||
import {_t, _td, newTranslatableError} from "../languageHandler";
|
import {_t, _td, newTranslatableError} from "../languageHandler";
|
||||||
import {makeType} from "./TypeUtils";
|
import {makeType} from "./TypeUtils";
|
||||||
import SdkConfig from '../SdkConfig';
|
import SdkConfig from '../SdkConfig';
|
||||||
|
|
||||||
const LIVELINESS_DISCOVERY_ERRORS = [
|
const LIVELINESS_DISCOVERY_ERRORS: string[] = [
|
||||||
AutoDiscovery.ERROR_INVALID_HOMESERVER,
|
AutoDiscovery.ERROR_INVALID_HOMESERVER,
|
||||||
AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER,
|
AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER,
|
||||||
];
|
];
|
||||||
@ -40,17 +39,23 @@ export class ValidatedServerConfig {
|
|||||||
warning: string;
|
warning: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface IAuthComponentState {
|
||||||
|
serverIsAlive: boolean;
|
||||||
|
serverErrorIsFatal: boolean;
|
||||||
|
serverDeadError?: ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
export default class AutoDiscoveryUtils {
|
export default class AutoDiscoveryUtils {
|
||||||
/**
|
/**
|
||||||
* Checks if a given error or error message is considered an error
|
* Checks if a given error or error message is considered an error
|
||||||
* relating to the liveliness of the server. Must be an error returned
|
* relating to the liveliness of the server. Must be an error returned
|
||||||
* from this AutoDiscoveryUtils class.
|
* from this AutoDiscoveryUtils class.
|
||||||
* @param {string|Error} error The error to check
|
* @param {string | Error} error The error to check
|
||||||
* @returns {boolean} True if the error is a liveliness error.
|
* @returns {boolean} True if the error is a liveliness error.
|
||||||
*/
|
*/
|
||||||
static isLivelinessError(error: string|Error): boolean {
|
static isLivelinessError(error: string | Error): boolean {
|
||||||
if (!error) return false;
|
if (!error) return false;
|
||||||
return !!LIVELINESS_DISCOVERY_ERRORS.find(e => e === error || e === error.message);
|
return !!LIVELINESS_DISCOVERY_ERRORS.find(e => typeof error === "string" ? e === error : e === error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,7 +66,7 @@ export default class AutoDiscoveryUtils {
|
|||||||
* implementation for known values.
|
* implementation for known values.
|
||||||
* @returns {*} The state for the component, given the error.
|
* @returns {*} The state for the component, given the error.
|
||||||
*/
|
*/
|
||||||
static authComponentStateForError(err: string | Error | null, pageName = "login"): Object {
|
static authComponentStateForError(err: string | Error | null, pageName = "login"): IAuthComponentState {
|
||||||
if (!err) {
|
if (!err) {
|
||||||
return {
|
return {
|
||||||
serverIsAlive: true,
|
serverIsAlive: true,
|
||||||
@ -70,7 +75,7 @@ export default class AutoDiscoveryUtils {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
let title = _t("Cannot reach homeserver");
|
let title = _t("Cannot reach homeserver");
|
||||||
let body = _t("Ensure you have a stable internet connection, or get in touch with the server admin");
|
let body: ReactNode = _t("Ensure you have a stable internet connection, or get in touch with the server admin");
|
||||||
if (!AutoDiscoveryUtils.isLivelinessError(err)) {
|
if (!AutoDiscoveryUtils.isLivelinessError(err)) {
|
||||||
const brand = SdkConfig.get().brand;
|
const brand = SdkConfig.get().brand;
|
||||||
title = _t("Your %(brand)s is misconfigured", { brand });
|
title = _t("Your %(brand)s is misconfigured", { brand });
|
||||||
@ -92,7 +97,7 @@ export default class AutoDiscoveryUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let isFatalError = true;
|
let isFatalError = true;
|
||||||
const errorMessage = err.message ? err.message : err;
|
const errorMessage = typeof err === "string" ? err : err.message;
|
||||||
if (errorMessage === AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER) {
|
if (errorMessage === AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER) {
|
||||||
isFatalError = false;
|
isFatalError = false;
|
||||||
title = _t("Cannot reach identity server");
|
title = _t("Cannot reach identity server");
|
||||||
@ -141,7 +146,10 @@ export default class AutoDiscoveryUtils {
|
|||||||
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
|
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
|
||||||
*/
|
*/
|
||||||
static async validateServerConfigWithStaticUrls(
|
static async validateServerConfigWithStaticUrls(
|
||||||
homeserverUrl: string, identityUrl: string, syntaxOnly = false): ValidatedServerConfig {
|
homeserverUrl: string,
|
||||||
|
identityUrl?: string,
|
||||||
|
syntaxOnly = false,
|
||||||
|
): Promise<ValidatedServerConfig> {
|
||||||
if (!homeserverUrl) {
|
if (!homeserverUrl) {
|
||||||
throw newTranslatableError(_td("No homeserver URL provided"));
|
throw newTranslatableError(_td("No homeserver URL provided"));
|
||||||
}
|
}
|
||||||
@ -171,7 +179,7 @@ export default class AutoDiscoveryUtils {
|
|||||||
* @param {string} serverName The homeserver domain name (eg: "matrix.org") to validate.
|
* @param {string} serverName The homeserver domain name (eg: "matrix.org") to validate.
|
||||||
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
|
* @returns {Promise<ValidatedServerConfig>} Resolves to the validated configuration.
|
||||||
*/
|
*/
|
||||||
static async validateServerName(serverName: string): ValidatedServerConfig {
|
static async validateServerName(serverName: string): Promise<ValidatedServerConfig> {
|
||||||
const result = await AutoDiscovery.findClientConfig(serverName);
|
const result = await AutoDiscovery.findClientConfig(serverName);
|
||||||
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
|
return AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2019 New Vector Ltd
|
Copyright 2019-2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -36,8 +36,8 @@ function log(msg) {
|
|||||||
console.log(`StorageManager: ${msg}`);
|
console.log(`StorageManager: ${msg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
function error(msg) {
|
function error(msg, ...args) {
|
||||||
console.error(`StorageManager: ${msg}`);
|
console.error(`StorageManager: ${msg}`, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
function track(action) {
|
function track(action) {
|
||||||
@ -73,7 +73,7 @@ export async function checkConsistency() {
|
|||||||
dataInLocalStorage = localStorage.length > 0;
|
dataInLocalStorage = localStorage.length > 0;
|
||||||
log(`Local storage contains data? ${dataInLocalStorage}`);
|
log(`Local storage contains data? ${dataInLocalStorage}`);
|
||||||
|
|
||||||
cryptoInited = localStorage.getItem("mx_crypto_initialised");
|
cryptoInited = !!localStorage.getItem("mx_crypto_initialised");
|
||||||
log(`Crypto initialised? ${cryptoInited}`);
|
log(`Crypto initialised? ${cryptoInited}`);
|
||||||
} else {
|
} else {
|
||||||
healthy = false;
|
healthy = false;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2018 New Vector Ltd
|
Copyright 2018, 2021 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -26,6 +26,13 @@ Once a timer is finished or aborted, it can't be started again
|
|||||||
a new one through `clone()` or `cloneIfRun()`.
|
a new one through `clone()` or `cloneIfRun()`.
|
||||||
*/
|
*/
|
||||||
export default class Timer {
|
export default class Timer {
|
||||||
|
private _timeout: number;
|
||||||
|
private _timerHandle: NodeJS.Timeout;
|
||||||
|
private _startTs: number;
|
||||||
|
private _promise: Promise<void>;
|
||||||
|
private _resolve: () => void;
|
||||||
|
private _reject: (Error) => void;
|
||||||
|
|
||||||
constructor(timeout) {
|
constructor(timeout) {
|
||||||
this._timeout = timeout;
|
this._timeout = timeout;
|
||||||
this._onTimeout = this._onTimeout.bind(this);
|
this._onTimeout = this._onTimeout.bind(this);
|
||||||
@ -35,7 +42,7 @@ export default class Timer {
|
|||||||
_setNotStarted() {
|
_setNotStarted() {
|
||||||
this._timerHandle = null;
|
this._timerHandle = null;
|
||||||
this._startTs = null;
|
this._startTs = null;
|
||||||
this._promise = new Promise((resolve, reject) => {
|
this._promise = new Promise<void>((resolve, reject) => {
|
||||||
this._resolve = resolve;
|
this._resolve = resolve;
|
||||||
this._reject = reject;
|
this._reject = reject;
|
||||||
}).finally(() => {
|
}).finally(() => {
|
@ -20,7 +20,7 @@ import PermalinkConstructor, {PermalinkParts} from "./PermalinkConstructor";
|
|||||||
* Generates permalinks that self-reference the running webapp
|
* Generates permalinks that self-reference the running webapp
|
||||||
*/
|
*/
|
||||||
export default class ElementPermalinkConstructor extends PermalinkConstructor {
|
export default class ElementPermalinkConstructor extends PermalinkConstructor {
|
||||||
_elementUrl: string;
|
private _elementUrl: string;
|
||||||
|
|
||||||
constructor(elementUrl: string) {
|
constructor(elementUrl: string) {
|
||||||
super();
|
super();
|
||||||
@ -35,7 +35,7 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor {
|
|||||||
return `${this._elementUrl}/#/room/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`;
|
return `${this._elementUrl}/#/room/${roomId}/${eventId}${this.encodeServerCandidates(serverCandidates)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
forRoom(roomIdOrAlias: string, serverCandidates: string[]): string {
|
forRoom(roomIdOrAlias: string, serverCandidates?: string[]): string {
|
||||||
return `${this._elementUrl}/#/room/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`;
|
return `${this._elementUrl}/#/room/${roomIdOrAlias}${this.encodeServerCandidates(serverCandidates)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ export default class ElementPermalinkConstructor extends PermalinkConstructor {
|
|||||||
return testHost === (parsedUrl.host || parsedUrl.hostname); // one of the hosts should match
|
return testHost === (parsedUrl.host || parsedUrl.hostname); // one of the hosts should match
|
||||||
}
|
}
|
||||||
|
|
||||||
encodeServerCandidates(candidates: string[]) {
|
encodeServerCandidates(candidates?: string[]) {
|
||||||
if (!candidates || candidates.length === 0) return '';
|
if (!candidates || candidates.length === 0) return '';
|
||||||
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
|
return `?via=${candidates.map(c => encodeURIComponent(c)).join("&via=")}`;
|
||||||
}
|
}
|
||||||
|
@ -74,10 +74,19 @@ const MAX_SERVER_CANDIDATES = 3;
|
|||||||
// the list and magically have the link work.
|
// the list and magically have the link work.
|
||||||
|
|
||||||
export class RoomPermalinkCreator {
|
export class RoomPermalinkCreator {
|
||||||
|
private _room: Room;
|
||||||
|
private _roomId: string;
|
||||||
|
private _highestPlUserId: string;
|
||||||
|
private _populationMap: { [serverName: string]: number };
|
||||||
|
private _bannedHostsRegexps: RegExp[];
|
||||||
|
private _allowedHostsRegexps: RegExp[];
|
||||||
|
private _serverCandidates: string[];
|
||||||
|
private _started: boolean;
|
||||||
|
|
||||||
// We support being given a roomId as a fallback in the event the `room` object
|
// We support being given a roomId as a fallback in the event the `room` object
|
||||||
// doesn't exist or is not healthy for us to rely on. For example, loading a
|
// doesn't exist or is not healthy for us to rely on. For example, loading a
|
||||||
// permalink to a room which the MatrixClient doesn't know about.
|
// permalink to a room which the MatrixClient doesn't know about.
|
||||||
constructor(room, roomId = null) {
|
constructor(room: Room, roomId: string = null) {
|
||||||
this._room = room;
|
this._room = room;
|
||||||
this._roomId = room ? room.roomId : roomId;
|
this._roomId = room ? room.roomId : roomId;
|
||||||
this._highestPlUserId = null;
|
this._highestPlUserId = null;
|
||||||
|
Loading…
Reference in New Issue
Block a user