Merge branch 'develop' into travis/communities/proto/userinfo

This commit is contained in:
Travis Ralston 2020-09-01 10:51:42 -06:00
commit 92c184385c
65 changed files with 350 additions and 1645 deletions

View file

@ -19,7 +19,7 @@ module.exports = {
}, },
overrides: [{ overrides: [{
"files": ["src/**/*.{ts, tsx}"], "files": ["src/**/*.{ts,tsx}"],
"extends": ["matrix-org/ts"], "extends": ["matrix-org/ts"],
"rules": { "rules": {
// We disable this while we're transitioning // We disable this while we're transitioning

View file

@ -1,3 +1,72 @@
Changes in [3.3.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.3.0) (2020-09-01)
===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.3.0-rc.1...v3.3.0)
* Upgrade to JS SDK 8.2.0
Changes in [3.3.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.3.0-rc.1) (2020-08-26)
=============================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.2.0...v3.3.0-rc.1)
* Upgrade to JS SDK 8.2.0-rc.1
* Update from Weblate
[\#5146](https://github.com/matrix-org/matrix-react-sdk/pull/5146)
* BaseAvatar avoid initial render with default avatar
[\#5142](https://github.com/matrix-org/matrix-react-sdk/pull/5142)
* Enforce Secure Backup completion when requested by HS
[\#5130](https://github.com/matrix-org/matrix-react-sdk/pull/5130)
* Communities v2 prototype: Explore rooms, global state, and default room
[\#5139](https://github.com/matrix-org/matrix-react-sdk/pull/5139)
* Add communities v2 prototyping feature flag + initial tag panel prototypes
[\#5133](https://github.com/matrix-org/matrix-react-sdk/pull/5133)
* Remove some unused components
[\#5134](https://github.com/matrix-org/matrix-react-sdk/pull/5134)
* Allow avatar image view for 1:1 rooms
[\#5137](https://github.com/matrix-org/matrix-react-sdk/pull/5137)
* Send mx_local_settings in rageshake
[\#5136](https://github.com/matrix-org/matrix-react-sdk/pull/5136)
* Run all room leaving behaviour through a single function
[\#5132](https://github.com/matrix-org/matrix-react-sdk/pull/5132)
* Add clarifying comment in media device selection
[\#5131](https://github.com/matrix-org/matrix-react-sdk/pull/5131)
* Settings v3: Feature flag changes
[\#5124](https://github.com/matrix-org/matrix-react-sdk/pull/5124)
* Clear url previews if they all get edited out of the event
[\#5129](https://github.com/matrix-org/matrix-react-sdk/pull/5129)
* Consider tab completions as modifications for editing purposes to unlock
sending
[\#5128](https://github.com/matrix-org/matrix-react-sdk/pull/5128)
* Use matrix-doc for SAS emoji translations
[\#5125](https://github.com/matrix-org/matrix-react-sdk/pull/5125)
* Add a rageshake function to download the logs locally
[\#3849](https://github.com/matrix-org/matrix-react-sdk/pull/3849)
* Room List filtering visual tweaks
[\#5123](https://github.com/matrix-org/matrix-react-sdk/pull/5123)
* Make reply preview not an overlay so you can see new messages
[\#5072](https://github.com/matrix-org/matrix-react-sdk/pull/5072)
* Allow room tile context menu when minimized using right click
[\#5113](https://github.com/matrix-org/matrix-react-sdk/pull/5113)
* Add null guard to group inviter for corrupted groups
[\#5121](https://github.com/matrix-org/matrix-react-sdk/pull/5121)
* Room List styling tweaks
[\#5118](https://github.com/matrix-org/matrix-react-sdk/pull/5118)
* Fix corner rounding on images not always affecting right side
[\#5120](https://github.com/matrix-org/matrix-react-sdk/pull/5120)
* Change add room action for rooms to context menu
[\#5108](https://github.com/matrix-org/matrix-react-sdk/pull/5108)
* Switch out the globe icon and colour it depending on theme
[\#5106](https://github.com/matrix-org/matrix-react-sdk/pull/5106)
* Message Action Bar watch for event send changes
[\#5115](https://github.com/matrix-org/matrix-react-sdk/pull/5115)
* Put message previews for Emoji behind Labs
[\#5110](https://github.com/matrix-org/matrix-react-sdk/pull/5110)
* Fix styling for selected community marker
[\#5107](https://github.com/matrix-org/matrix-react-sdk/pull/5107)
* Fix action bar safe area regression
[\#5111](https://github.com/matrix-org/matrix-react-sdk/pull/5111)
* Fix /op slash command
[\#5109](https://github.com/matrix-org/matrix-react-sdk/pull/5109)
Changes in [3.2.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.2.0) (2020-08-17) Changes in [3.2.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.2.0) (2020-08-17)
=================================================================================================== ===================================================================================================
[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.2.0-rc.1...v3.2.0) [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.2.0-rc.1...v3.2.0)

View file

@ -1,6 +1,6 @@
{ {
"name": "matrix-react-sdk", "name": "matrix-react-sdk",
"version": "3.2.0", "version": "3.3.0",
"description": "SDK for matrix.org using React", "description": "SDK for matrix.org using React",
"author": "matrix.org", "author": "matrix.org",
"repository": { "repository": {
@ -163,9 +163,7 @@
"stylelint-config-standard": "^18.3.0", "stylelint-config-standard": "^18.3.0",
"stylelint-scss": "^3.18.0", "stylelint-scss": "^3.18.0",
"typescript": "^3.9.7", "typescript": "^3.9.7",
"walk": "^2.3.14", "walk": "^2.3.14"
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12"
}, },
"jest": { "jest": {
"testMatch": [ "testMatch": [

View file

@ -70,6 +70,7 @@ interface IContent {
interface IThumbnail { interface IThumbnail {
info: { info: {
// eslint-disable-next-line camelcase
thumbnail_info: { thumbnail_info: {
w: number; w: number;
h: number; h: number;
@ -104,7 +105,12 @@ interface IAbortablePromise<T> extends Promise<T> {
* @return {Promise} A promise that resolves with an object with an info key * @return {Promise} A promise that resolves with an object with an info key
* and a thumbnail key. * and a thumbnail key.
*/ */
function createThumbnail(element: ThumbnailableElement, inputWidth: number, inputHeight: number, mimeType: string): Promise<IThumbnail> { function createThumbnail(
element: ThumbnailableElement,
inputWidth: number,
inputHeight: number,
mimeType: string,
): Promise<IThumbnail> {
return new Promise((resolve) => { return new Promise((resolve) => {
let targetWidth = inputWidth; let targetWidth = inputWidth;
let targetHeight = inputHeight; let targetHeight = inputHeight;
@ -437,11 +443,13 @@ export default class ContentMessages {
for (let i = 0; i < okFiles.length; ++i) { for (let i = 0; i < okFiles.length; ++i) {
const file = okFiles[i]; const file = okFiles[i];
if (!uploadAll) { if (!uploadAll) {
const {finished} = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation', '', UploadConfirmDialog, { const {finished} = Modal.createTrackedDialog<[boolean, boolean]>('Upload Files confirmation',
'', UploadConfirmDialog, {
file, file,
currentIndex: i, currentIndex: i,
totalFiles: okFiles.length, totalFiles: okFiles.length,
}); },
);
const [shouldContinue, shouldUploadAll] = await finished; const [shouldContinue, shouldUploadAll] = await finished;
if (!shouldContinue) break; if (!shouldContinue) break;
if (shouldUploadAll) { if (shouldUploadAll) {

View file

@ -339,33 +339,9 @@ class HtmlHighlighter extends BaseHighlighter<string> {
} }
} }
class TextHighlighter extends BaseHighlighter<React.ReactNode> {
private key = 0;
/* create a <span> node to hold the given content
*
* snippet: content of the span
* highlight: true to highlight as a search match
*
* returns a React node
*/
protected processSnippet(snippet: string, highlight: boolean): React.ReactNode {
const key = this.key++;
let node = <span key={key} className={highlight ? this.highlightClass : null}>
{ snippet }
</span>;
if (highlight && this.highlightLink) {
node = <a key={key} href={this.highlightLink}>{ node }</a>;
}
return node;
}
}
interface IContent { interface IContent {
format?: string; format?: string;
// eslint-disable-next-line camelcase
formatted_body?: string; formatted_body?: string;
body: string; body: string;
} }
@ -474,8 +450,13 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
}); });
return isDisplayedWithHtml ? return isDisplayedWithHtml ?
<span key="body" ref={opts.ref} className={className} dangerouslySetInnerHTML={{ __html: safeBody }} dir="auto" /> : <span
<span key="body" ref={opts.ref} className={className} dir="auto">{ strippedBody }</span>; key="body"
ref={opts.ref}
className={className}
dangerouslySetInnerHTML={{ __html: safeBody }}
dir="auto"
/> : <span key="body" ref={opts.ref} className={className} dir="auto">{ strippedBody }</span>;
} }
/** /**

View file

@ -151,7 +151,7 @@ export class ModalManager {
prom: Promise<React.ComponentType>, prom: Promise<React.ComponentType>,
props?: IProps<T>, props?: IProps<T>,
className?: string, className?: string,
options?: IOptions<T> options?: IOptions<T>,
) { ) {
const modal: IModal<T> = { const modal: IModal<T> = {
onFinished: props ? props.onFinished : null, onFinished: props ? props.onFinished : null,
@ -182,7 +182,7 @@ export class ModalManager {
private getCloseFn<T extends any[]>( private getCloseFn<T extends any[]>(
modal: IModal<T>, modal: IModal<T>,
props: IProps<T> props: IProps<T>,
): [IHandle<T>["close"], IHandle<T>["finished"]] { ): [IHandle<T>["close"], IHandle<T>["finished"]] {
const deferred = defer<T>(); const deferred = defer<T>();
return [async (...args: T) => { return [async (...args: T) => {
@ -264,7 +264,7 @@ export class ModalManager {
className?: string, className?: string,
isPriorityModal = false, isPriorityModal = false,
isStaticModal = false, isStaticModal = false,
options: IOptions<T> = {} options: IOptions<T> = {},
): IHandle<T> { ): IHandle<T> {
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, options); const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, options);
if (isPriorityModal) { if (isPriorityModal) {
@ -287,7 +287,7 @@ export class ModalManager {
private appendDialogAsync<T extends any[]>( private appendDialogAsync<T extends any[]>(
prom: Promise<React.ComponentType>, prom: Promise<React.ComponentType>,
props?: IProps<T>, props?: IProps<T>,
className?: string className?: string,
): IHandle<T> { ): IHandle<T> {
const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, {}); const {modal, closeDialog, onFinishedProm} = this.buildModal<T>(prom, props, className, {});

View file

@ -168,7 +168,7 @@ const shortcuts: Record<Categories, IShortcut[]> = {
key: Key.U, key: Key.U,
}], }],
description: _td("Upload a file"), description: _td("Upload a file"),
} },
], ],
[Categories.ROOM_LIST]: [ [Categories.ROOM_LIST]: [

View file

@ -190,7 +190,7 @@ export const RovingTabIndexProvider: React.FC<IProps> = ({children, handleHomeEn
ev.preventDefault(); ev.preventDefault();
ev.stopPropagation(); ev.stopPropagation();
} else if (onKeyDown) { } else if (onKeyDown) {
return onKeyDown(ev, state); return onKeyDown(ev, context.state);
} }
}, [context.state, onKeyDown, handleHomeEnd]); }, [context.state, onKeyDown, handleHomeEnd]);

View file

@ -30,6 +30,7 @@ const Toolbar: React.FC<IProps> = ({children, ...props}) => {
const target = ev.target as HTMLElement; const target = ev.target as HTMLElement;
let handled = true; let handled = true;
// HOME and END are handled by RovingTabIndexProvider
switch (ev.key) { switch (ev.key) {
case Key.ARROW_UP: case Key.ARROW_UP:
case Key.ARROW_DOWN: case Key.ARROW_DOWN:
@ -47,8 +48,6 @@ const Toolbar: React.FC<IProps> = ({children, ...props}) => {
} }
break; break;
// HOME and END are handled by RovingTabIndexProvider
default: default:
handled = false; handled = false;
} }

View file

@ -20,7 +20,8 @@ import AccessibleTooltipButton from "../../components/views/elements/AccessibleT
import {useRovingTabIndex} from "../RovingTabIndex"; import {useRovingTabIndex} from "../RovingTabIndex";
import {Ref} from "./types"; import {Ref} from "./types";
interface IProps extends Omit<React.ComponentProps<typeof AccessibleTooltipButton>, "onFocus" | "inputRef" | "tabIndex"> { type ATBProps = React.ComponentProps<typeof AccessibleTooltipButton>;
interface IProps extends Omit<ATBProps, "onFocus" | "inputRef" | "tabIndex"> {
inputRef?: Ref; inputRef?: Ref;
} }

View file

@ -16,7 +16,6 @@ limitations under the License.
import React from "react"; import React from "react";
import AccessibleButton from "../../components/views/elements/AccessibleButton";
import {useRovingTabIndex} from "../RovingTabIndex"; import {useRovingTabIndex} from "../RovingTabIndex";
import {FocusHandler, Ref} from "./types"; import {FocusHandler, Ref} from "./types";

View file

@ -89,7 +89,11 @@ export default class CommandProvider extends AutocompleteProvider {
renderCompletions(completions: React.ReactNode[]): React.ReactNode { renderCompletions(completions: React.ReactNode[]): React.ReactNode {
return ( return (
<div className="mx_Autocomplete_Completion_container_block" role="listbox" aria-label={_t("Command Autocomplete")}> <div
className="mx_Autocomplete_Completion_container_block"
role="listbox"
aria-label={_t("Command Autocomplete")}
>
{ completions } { completions }
</div> </div>
); );

View file

@ -91,15 +91,15 @@ export default class CommunityProvider extends AutocompleteProvider {
href: makeGroupPermalink(groupId), href: makeGroupPermalink(groupId),
component: ( component: (
<PillCompletion title={name} description={groupId}> <PillCompletion title={name} description={groupId}>
<BaseAvatar name={name || groupId} <BaseAvatar
name={name || groupId}
width={24} width={24}
height={24} height={24}
url={avatarUrl ? cli.mxcUrlToHttp(avatarUrl, 24, 24) : null} /> url={avatarUrl ? cli.mxcUrlToHttp(avatarUrl, 24, 24) : null} />
</PillCompletion> </PillCompletion>
), ),
range, range,
})) })).slice(0, 4);
.slice(0, 4);
} }
return completions; return completions;
} }

View file

@ -139,7 +139,11 @@ export default class EmojiProvider extends AutocompleteProvider {
renderCompletions(completions: React.ReactNode[]): React.ReactNode { renderCompletions(completions: React.ReactNode[]): React.ReactNode {
return ( return (
<div className="mx_Autocomplete_Completion_container_pill" role="listbox" aria-label={_t("Emoji Autocomplete")}> <div
className="mx_Autocomplete_Completion_container_pill"
role="listbox"
aria-label={_t("Emoji Autocomplete")}
>
{ completions } { completions }
</div> </div>
); );

View file

@ -110,9 +110,7 @@ export default class RoomProvider extends AutocompleteProvider {
), ),
range, range,
}; };
}) }).filter((completion) => !!completion.completion && completion.completion.length > 0).slice(0, 4);
.filter((completion) => !!completion.completion && completion.completion.length > 0)
.slice(0, 4);
} }
return completions; return completions;
} }

View file

@ -71,8 +71,13 @@ export default class UserProvider extends AutocompleteProvider {
} }
} }
private onRoomTimeline = (ev: MatrixEvent, room: Room, toStartOfTimeline: boolean, removed: boolean, private onRoomTimeline = (
data: IRoomTimelineData) => { ev: MatrixEvent,
room: Room,
toStartOfTimeline: boolean,
removed: boolean,
data: IRoomTimelineData,
) => {
if (!room) return; if (!room) return;
if (removed) return; if (removed) return;
if (room.roomId !== this.room.roomId) return; if (room.roomId !== this.room.roomId) return;
@ -171,7 +176,11 @@ export default class UserProvider extends AutocompleteProvider {
renderCompletions(completions: React.ReactNode[]): React.ReactNode { renderCompletions(completions: React.ReactNode[]): React.ReactNode {
return ( return (
<div className="mx_Autocomplete_Completion_container_pill" role="listbox" aria-label={_t("User Autocomplete")}> <div
className="mx_Autocomplete_Completion_container_pill"
role="listbox"
aria-label={_t("User Autocomplete")}
>
{ completions } { completions }
</div> </div>
); );

View file

@ -1,90 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import { _t } from '../../languageHandler';
import SdkConfig from '../../SdkConfig';
export default createReactClass({
displayName: 'CompatibilityPage',
propTypes: {
onAccept: PropTypes.func,
},
getDefaultProps: function() {
return {
onAccept: function() {}, // NOP
};
},
onAccept: function() {
this.props.onAccept();
},
render: function() {
const brand = SdkConfig.get().brand;
return (
<div className="mx_CompatibilityPage">
<div className="mx_CompatibilityPage_box">
<p>{_t(
"Sorry, your browser is <b>not</b> able to run %(brand)s.",
{
brand,
},
{
'b': (sub) => <b>{sub}</b>,
})
}</p>
<p>
{ _t(
"%(brand)s uses many advanced browser features, some of which are not available " +
"or experimental in your current browser.",
{ brand },
) }
</p>
<p>
{ _t(
'Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, ' +
'or <safariLink>Safari</safariLink> for the best experience.',
{},
{
'chromeLink': (sub) => <a href="https://www.google.com/chrome">{sub}</a>,
'firefoxLink': (sub) => <a href="https://firefox.com">{sub}</a>,
'safariLink': (sub) => <a href="https://apple.com/safari">{sub}</a>,
},
)}
</p>
<p>
{ _t(
"With your current browser, the look and feel of the application may be " +
"completely incorrect, and some or all features may not function. " +
"If you want to try it anyway you can continue, but you are on your own in terms " +
"of any issues you may encounter!",
) }
</p>
<button onClick={this.onAccept}>
{ _t("I understand the risks and wish to continue") }
</button>
</div>
</div>
);
},
});

View file

@ -233,8 +233,7 @@ export class ContextMenu extends React.PureComponent<IProps, IState> {
switch (ev.key) { switch (ev.key) {
case Key.TAB: case Key.TAB:
case Key.ESCAPE: case Key.ESCAPE:
// close on left and right arrows too for when it is a context menu on a <Toolbar /> case Key.ARROW_LEFT: // close on left and right arrows too for when it is a context menu on a <Toolbar />
case Key.ARROW_LEFT:
case Key.ARROW_RIGHT: case Key.ARROW_RIGHT:
this.props.onFinished(); this.props.onFinished();
break; break;

View file

@ -377,7 +377,7 @@ export default class LeftPanel extends React.Component<IProps, IState> {
public render(): React.ReactNode { public render(): React.ReactNode {
const tagPanel = !this.state.showTagPanel ? null : ( const tagPanel = !this.state.showTagPanel ? null : (
<div className="mx_LeftPanel_tagPanelContainer"> <div className="mx_LeftPanel_tagPanelContainer">
<TagPanel/> <TagPanel />
{SettingsStore.getValue("feature_custom_tags") ? <CustomRoomTagPanel /> : null} {SettingsStore.getValue("feature_custom_tags") ? <CustomRoomTagPanel /> : null}
</div> </div>
); );

View file

@ -43,11 +43,11 @@ import PlatformPeg from "../../PlatformPeg";
import { DefaultTagID } from "../../stores/room-list/models"; import { DefaultTagID } from "../../stores/room-list/models";
import { import {
showToast as showSetPasswordToast, showToast as showSetPasswordToast,
hideToast as hideSetPasswordToast hideToast as hideSetPasswordToast,
} from "../../toasts/SetPasswordToast"; } from "../../toasts/SetPasswordToast";
import { import {
showToast as showServerLimitToast, showToast as showServerLimitToast,
hideToast as hideServerLimitToast hideToast as hideServerLimitToast,
} from "../../toasts/ServerLimitToast"; } from "../../toasts/ServerLimitToast";
import { Action } from "../../dispatcher/actions"; import { Action } from "../../dispatcher/actions";
import LeftPanel from "./LeftPanel"; import LeftPanel from "./LeftPanel";
@ -79,6 +79,7 @@ interface IProps {
initialEventPixelOffset: number; initialEventPixelOffset: number;
leftDisabled: boolean; leftDisabled: boolean;
rightDisabled: boolean; rightDisabled: boolean;
// eslint-disable-next-line camelcase
page_type: string; page_type: string;
autoJoin: boolean; autoJoin: boolean;
thirdPartyInvite?: object; thirdPartyInvite?: object;
@ -98,7 +99,9 @@ interface IProps {
} }
interface IUsageLimit { interface IUsageLimit {
// eslint-disable-next-line camelcase
limit_type: "monthly_active_user" | string; limit_type: "monthly_active_user" | string;
// eslint-disable-next-line camelcase
admin_contact?: string; admin_contact?: string;
} }
@ -316,10 +319,10 @@ class LoggedInView extends React.Component<IProps, IState> {
} }
}; };
_calculateServerLimitToast(syncErrorData: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) { _calculateServerLimitToast(syncError: IState["syncErrorData"], usageLimitEventContent?: IUsageLimit) {
const error = syncErrorData && syncErrorData.error && syncErrorData.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED"; const error = syncError && syncError.error && syncError.error.errcode === "M_RESOURCE_LIMIT_EXCEEDED";
if (error) { if (error) {
usageLimitEventContent = syncErrorData.error.data; usageLimitEventContent = syncError.error.data;
} }
if (usageLimitEventContent) { if (usageLimitEventContent) {

View file

@ -69,7 +69,7 @@ import { ViewUserPayload } from "../../dispatcher/payloads/ViewUserPayload";
import { Action } from "../../dispatcher/actions"; import { Action } from "../../dispatcher/actions";
import { import {
showToast as showAnalyticsToast, showToast as showAnalyticsToast,
hideToast as hideAnalyticsToast hideToast as hideAnalyticsToast,
} from "../../toasts/AnalyticsToast"; } from "../../toasts/AnalyticsToast";
import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast"; import {showToast as showNotificationsToast} from "../../toasts/DesktopNotificationsToast";
import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload"; import { OpenToTabPayload } from "../../dispatcher/payloads/OpenToTabPayload";
@ -129,6 +129,7 @@ interface IScreen {
params?: object; params?: object;
} }
/* eslint-disable camelcase */
interface IRoomInfo { interface IRoomInfo {
room_id?: string; room_id?: string;
room_alias?: string; room_alias?: string;
@ -140,6 +141,7 @@ interface IRoomInfo {
oob_data?: object; oob_data?: object;
via_servers?: string[]; via_servers?: string[];
} }
/* eslint-enable camelcase */
interface IProps { // TODO type things better interface IProps { // TODO type things better
config: Record<string, any>; config: Record<string, any>;
@ -165,6 +167,7 @@ interface IState {
// the master view we are showing. // the master view we are showing.
view: Views; view: Views;
// What the LoggedInView would be showing if visible // What the LoggedInView would be showing if visible
// eslint-disable-next-line camelcase
page_type?: PageTypes; page_type?: PageTypes;
// The ID of the room we're viewing. This is either populated directly // The ID of the room we're viewing. This is either populated directly
// in the case where we view a room by ID or by RoomView when it resolves // in the case where we view a room by ID or by RoomView when it resolves
@ -180,8 +183,11 @@ interface IState {
middleDisabled: boolean; middleDisabled: boolean;
// the right panel's disabled state is tracked in its store. // the right panel's disabled state is tracked in its store.
// Parameters used in the registration dance with the IS // Parameters used in the registration dance with the IS
// eslint-disable-next-line camelcase
register_client_secret?: string; register_client_secret?: string;
// eslint-disable-next-line camelcase
register_session_id?: string; register_session_id?: string;
// eslint-disable-next-line camelcase
register_id_sid?: string; register_id_sid?: string;
// When showing Modal dialogs we need to set aria-hidden on the root app element // When showing Modal dialogs we need to set aria-hidden on the root app element
// and disable it when there are no dialogs // and disable it when there are no dialogs
@ -341,6 +347,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
} }
// TODO: [REACT-WARNING] Replace with appropriate lifecycle stage // TODO: [REACT-WARNING] Replace with appropriate lifecycle stage
// eslint-disable-next-line camelcase
UNSAFE_componentWillUpdate(props, state) { UNSAFE_componentWillUpdate(props, state) {
if (this.shouldTrackPageChange(this.state, state)) { if (this.shouldTrackPageChange(this.state, state)) {
this.startPageChangeTimer(); this.startPageChangeTimer();
@ -610,8 +617,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog"); const UserSettingsDialog = sdk.getComponent("dialogs.UserSettingsDialog");
Modal.createTrackedDialog('User settings', '', UserSettingsDialog, Modal.createTrackedDialog('User settings', '', UserSettingsDialog,
{initialTabId: tabPayload.initialTabId}, {initialTabId: tabPayload.initialTabId},
/*className=*/null, /*isPriority=*/false, /*isStatic=*/true /*className=*/null, /*isPriority=*/false, /*isStatic=*/true);
);
// View the welcome or home page if we need something to look at // View the welcome or home page if we need something to look at
this.viewSomethingBehindModal(); this.viewSomethingBehindModal();
@ -1433,7 +1439,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
cli.on("crypto.warning", (type) => { cli.on("crypto.warning", (type) => {
switch (type) { switch (type) {
case 'CRYPTO_WARNING_OLD_VERSION_DETECTED': case 'CRYPTO_WARNING_OLD_VERSION_DETECTED':
const brand = SdkConfig.get().brand;
Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, { Modal.createTrackedDialog('Crypto migrated', '', ErrorDialog, {
title: _t('Old cryptography data detected'), title: _t('Old cryptography data detected'),
description: _t( description: _t(
@ -1444,7 +1449,7 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
"in this version. This may also cause messages exchanged with this " + "in this version. This may also cause messages exchanged with this " +
"version to fail. If you experience problems, log out and back in " + "version to fail. If you experience problems, log out and back in " +
"again. To retain message history, export and re-import your keys.", "again. To retain message history, export and re-import your keys.",
{ brand }, { brand: SdkConfig.get().brand },
), ),
}); });
break; break;

View file

@ -20,7 +20,6 @@ import classNames from "classnames";
import defaultDispatcher from "../../dispatcher/dispatcher"; import defaultDispatcher from "../../dispatcher/dispatcher";
import { _t } from "../../languageHandler"; import { _t } from "../../languageHandler";
import { ActionPayload } from "../../dispatcher/payloads"; import { ActionPayload } from "../../dispatcher/payloads";
import { throttle } from 'lodash';
import { Key } from "../../Keyboard"; import { Key } from "../../Keyboard";
import AccessibleButton from "../views/elements/AccessibleButton"; import AccessibleButton from "../views/elements/AccessibleButton";
import { Action } from "../../dispatcher/actions"; import { Action } from "../../dispatcher/actions";
@ -137,7 +136,7 @@ export default class RoomSearch extends React.PureComponent<IProps, IState> {
}); });
let icon = ( let icon = (
<div className='mx_RoomSearch_icon'/> <div className='mx_RoomSearch_icon' />
); );
let input = ( let input = (
<input <input

View file

@ -18,7 +18,6 @@ limitations under the License.
import * as React from "react"; import * as React from "react";
import {_t} from '../../languageHandler'; import {_t} from '../../languageHandler';
import * as PropTypes from "prop-types";
import * as sdk from "../../index"; import * as sdk from "../../index";
import AutoHideScrollbar from './AutoHideScrollbar'; import AutoHideScrollbar from './AutoHideScrollbar';
import { ReactNode } from "react"; import { ReactNode } from "react";

View file

@ -40,7 +40,7 @@ import AccessibleTooltipButton from "../views/elements/AccessibleTooltipButton";
import { SettingLevel } from "../../settings/SettingLevel"; import { SettingLevel } from "../../settings/SettingLevel";
import IconizedContextMenu, { import IconizedContextMenu, {
IconizedContextMenuOption, IconizedContextMenuOption,
IconizedContextMenuOptionList IconizedContextMenuOptionList,
} from "../views/context_menus/IconizedContextMenu"; } from "../views/context_menus/IconizedContextMenu";
import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore"; import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore";
import * as fbEmitter from "fbemitter"; import * as fbEmitter from "fbemitter";

View file

@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import React, {useCallback, useContext, useEffect, useMemo, useState} from 'react'; import React, {useCallback, useContext, useEffect, useState} from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import * as AvatarLogic from '../../../Avatar'; import * as AvatarLogic from '../../../Avatar';
import SettingsStore from "../../../settings/SettingsStore"; import SettingsStore from "../../../settings/SettingsStore";
@ -96,7 +96,7 @@ const BaseAvatar = (props: IProps) => {
urls, urls,
width = 40, width = 40,
height = 40, height = 40,
resizeMethod = "crop", // eslint-disable-line no-unused-vars resizeMethod = "crop", // eslint-disable-line @typescript-eslint/no-unused-vars
defaultToInitialLetter = true, defaultToInitialLetter = true,
onClick, onClick,
inputRef, inputRef,

View file

@ -126,7 +126,7 @@ export default class DecoratedRoomAvatar extends React.PureComponent<IProps, ISt
private onPresenceUpdate = () => { private onPresenceUpdate = () => {
if (this.isUnmounted) return; if (this.isUnmounted) return;
let newIcon = this.getPresenceIcon(); const newIcon = this.getPresenceIcon();
if (newIcon !== this.state.icon) this.setState({icon: newIcon}); if (newIcon !== this.state.icon) this.setState({icon: newIcon});
}; };

View file

@ -47,7 +47,7 @@ export default class GroupAvatar extends React.Component<IProps> {
render() { render() {
// extract the props we use from props so we can pass any others through // extract the props we use from props so we can pass any others through
// should consider adding this as a global rule in js-sdk? // should consider adding this as a global rule in js-sdk?
/*eslint no-unused-vars: ["error", { "ignoreRestSiblings": true }]*/ /* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
const {groupId, groupAvatarUrl, groupName, ...otherProps} = this.props; const {groupId, groupAvatarUrl, groupName, ...otherProps} = this.props;
return ( return (

View file

@ -1,57 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from "react";
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
const Presets = {
PrivateChat: "private_chat",
PublicChat: "public_chat",
Custom: "custom",
};
export default createReactClass({
displayName: 'CreateRoomPresets',
propTypes: {
onChange: PropTypes.func,
preset: PropTypes.string,
},
Presets: Presets,
getDefaultProps: function() {
return {
onChange: function() {},
};
},
onValueChanged: function(ev) {
this.props.onChange(ev.target.value);
},
render: function() {
return (
<select className="mx_Presets" onChange={this.onValueChanged} value={this.props.preset}>
<option value={this.Presets.PrivateChat}>{ _t("Private Chat") }</option>
<option value={this.Presets.PublicChat}>{ _t("Public Chat") }</option>
<option value={this.Presets.Custom}>{ _t("Custom") }</option>
</select>
);
},
});

View file

@ -1,106 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
export default createReactClass({
displayName: 'RoomAlias',
propTypes: {
// Specifying a homeserver will make magical things happen when you,
// e.g. start typing in the room alias box.
homeserver: PropTypes.string,
alias: PropTypes.string,
onChange: PropTypes.func,
},
getDefaultProps: function() {
return {
onChange: function() {},
alias: '',
};
},
getAliasLocalpart: function() {
let room_alias = this.props.alias;
if (room_alias && this.props.homeserver) {
const suffix = ":" + this.props.homeserver;
if (room_alias.startsWith("#") && room_alias.endsWith(suffix)) {
room_alias = room_alias.slice(1, -suffix.length);
}
}
return room_alias;
},
onValueChanged: function(ev) {
this.props.onChange(ev.target.value);
},
onFocus: function(ev) {
const target = ev.target;
const curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "") {
const self = this;
setTimeout(function() {
target.value = "#:" + self.props.homeserver;
target.setSelectionRange(1, 1);
}, 0);
} else {
const suffix = ":" + this.props.homeserver;
setTimeout(function() {
target.setSelectionRange(
curr_val.startsWith("#") ? 1 : 0,
curr_val.endsWith(suffix) ? (target.value.length - suffix.length) : target.value.length,
);
}, 0);
}
}
},
onBlur: function(ev) {
const curr_val = ev.target.value;
if (this.props.homeserver) {
if (curr_val == "#:" + this.props.homeserver) {
ev.target.value = "";
return;
}
if (curr_val != "") {
let new_val = ev.target.value;
const suffix = ":" + this.props.homeserver;
if (!curr_val.startsWith("#")) new_val = "#" + new_val;
if (!curr_val.endsWith(suffix)) new_val = new_val + suffix;
ev.target.value = new_val;
}
}
},
render: function() {
return (
<input type="text" className="mx_RoomAlias" placeholder={_t("Address (optional)")}
onChange={this.onValueChanged} onFocus={this.onFocus} onBlur={this.onBlur}
value={this.props.alias} />
);
},
});

View file

@ -21,9 +21,6 @@ import { IDialogProps } from "./IDialogProps";
import Field from "../elements/Field"; import Field from "../elements/Field";
import AccessibleButton from "../elements/AccessibleButton"; import AccessibleButton from "../elements/AccessibleButton";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import InfoTooltip from "../elements/InfoTooltip";
import dis from "../../../dispatcher/dispatcher";
import {showCommunityRoomInviteDialog} from "../../../RoomInvite";
import { arrayFastClone } from "../../../utils/arrays"; import { arrayFastClone } from "../../../utils/arrays";
import SdkConfig from "../../../SdkConfig"; import SdkConfig from "../../../SdkConfig";
import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { RoomMember } from "matrix-js-sdk/src/models/room-member";
@ -31,7 +28,6 @@ import InviteDialog from "./InviteDialog";
import BaseAvatar from "../avatars/BaseAvatar"; import BaseAvatar from "../avatars/BaseAvatar";
import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo"; import {getHttpUriForMxc} from "matrix-js-sdk/src/content-repo";
import {inviteMultipleToRoom, showAnyInviteErrors} from "../../../RoomInvite"; import {inviteMultipleToRoom, showAnyInviteErrors} from "../../../RoomInvite";
import {humanizeTime} from "../../../utils/humanize";
import StyledCheckbox from "../elements/StyledCheckbox"; import StyledCheckbox from "../elements/StyledCheckbox";
import Modal from "../../../Modal"; import Modal from "../../../Modal";
import ErrorDialog from "./ErrorDialog"; import ErrorDialog from "./ErrorDialog";
@ -171,7 +167,7 @@ export default class CommunityPrototypeInviteDialog extends React.PureComponent<
public render() { public render() {
const emailAddresses = []; const emailAddresses = [];
this.state.emailTargets.forEach((address, i) => { this.state.emailTargets.forEach((address, i) => {
emailAddresses.push( emailAddresses.push((
<Field <Field
key={i} key={i}
value={address} value={address}
@ -180,11 +176,11 @@ export default class CommunityPrototypeInviteDialog extends React.PureComponent<
placeholder={_t("Email address")} placeholder={_t("Email address")}
onBlur={() => this.onAddressBlur(i)} onBlur={() => this.onAddressBlur(i)}
/> />
); ));
}); });
// Push a clean input // Push a clean input
emailAddresses.push( emailAddresses.push((
<Field <Field
key={emailAddresses.length} key={emailAddresses.length}
value={""} value={""}
@ -192,23 +188,23 @@ export default class CommunityPrototypeInviteDialog extends React.PureComponent<
label={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")} label={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")}
placeholder={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")} placeholder={emailAddresses.length > 0 ? _t("Add another email") : _t("Email address")}
/> />
); ));
let peopleIntro = null; let peopleIntro = null;
let people = []; const people = [];
if (this.state.showPeople) { if (this.state.showPeople) {
const humansToPresent = this.state.people.slice(0, this.state.numPeople); const humansToPresent = this.state.people.slice(0, this.state.numPeople);
humansToPresent.forEach((person, i) => { humansToPresent.forEach((person, i) => {
people.push(this.renderPerson(person, i)); people.push(this.renderPerson(person, i));
}); });
if (humansToPresent.length < this.state.people.length) { if (humansToPresent.length < this.state.people.length) {
people.push( people.push((
<AccessibleButton <AccessibleButton
onClick={this.onShowMorePeople} onClick={this.onShowMorePeople}
kind="link" key="more" kind="link" key="more"
className="mx_CommunityPrototypeInviteDialog_morePeople" className="mx_CommunityPrototypeInviteDialog_morePeople"
>{_t("Show more")}</AccessibleButton> >{_t("Show more")}</AccessibleButton>
); ));
} }
} }
if (this.state.people.length > 0) { if (this.state.people.length > 0) {

View file

@ -163,8 +163,9 @@ export default class CreateCommunityPrototypeDialog extends React.PureComponent<
</span> </span>
); );
if (this.state.error) { if (this.state.error) {
const classes = "mx_CreateCommunityPrototypeDialog_subtext mx_CreateCommunityPrototypeDialog_subtext_error";
helpText = ( helpText = (
<span className="mx_CreateCommunityPrototypeDialog_subtext mx_CreateCommunityPrototypeDialog_subtext_error"> <span className={classes}>
{this.state.error} {this.state.error}
</span> </span>
); );
@ -205,7 +206,10 @@ export default class CreateCommunityPrototypeDialog extends React.PureComponent<
ref={this.avatarUploadRef} accept="image/*" ref={this.avatarUploadRef} accept="image/*"
onChange={this.onAvatarChanged} onChange={this.onAvatarChanged}
/> />
<AccessibleButton onClick={this.onChangeAvatar} className="mx_CreateCommunityPrototypeDialog_avatarContainer"> <AccessibleButton
onClick={this.onChangeAvatar}
className="mx_CreateCommunityPrototypeDialog_avatarContainer"
>
{preview} {preview}
</AccessibleButton> </AccessibleButton>
<div className="mx_CreateCommunityPrototypeDialog_tip"> <div className="mx_CreateCommunityPrototypeDialog_tip">

View file

@ -198,14 +198,16 @@ export default class ShareDialog extends React.PureComponent<IProps, IState> {
const encodedUrl = encodeURIComponent(matrixToUrl); const encodedUrl = encodeURIComponent(matrixToUrl);
const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog');
return <BaseDialog title={title} return <BaseDialog
title={title}
className='mx_ShareDialog' className='mx_ShareDialog'
contentId='mx_Dialog_content' contentId='mx_Dialog_content'
onFinished={this.props.onFinished} onFinished={this.props.onFinished}
> >
<div className="mx_ShareDialog_content"> <div className="mx_ShareDialog_content">
<div className="mx_ShareDialog_matrixto"> <div className="mx_ShareDialog_matrixto">
<a href={matrixToUrl} <a
href={matrixToUrl}
onClick={ShareDialog.onLinkClick} onClick={ShareDialog.onLinkClick}
className="mx_ShareDialog_matrixto_link" className="mx_ShareDialog_matrixto_link"
> >

View file

@ -34,7 +34,6 @@ export interface ILocationState {
} }
export default class Draggable extends React.Component<IProps, IState> { export default class Draggable extends React.Component<IProps, IState> {
constructor(props: IProps) { constructor(props: IProps) {
super(props); super(props);
@ -77,5 +76,4 @@ export default class Draggable extends React.Component<IProps, IState> {
render() { render() {
return <div className={this.props.className} onMouseDown={this.onMouseDown.bind(this)} />; return <div className={this.props.className} onMouseDown={this.onMouseDown.bind(this)} />;
} }
} }

View file

@ -39,11 +39,13 @@ interface IProps {
className: string; className: string;
} }
/* eslint-disable camelcase */
interface IState { interface IState {
userId: string; userId: string;
displayname: string; displayname: string;
avatar_url: string; avatar_url: string;
} }
/* eslint-enable camelcase */
const AVATAR_SIZE = 32; const AVATAR_SIZE = 32;
@ -63,19 +65,18 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
const client = MatrixClientPeg.get(); const client = MatrixClientPeg.get();
const userId = client.getUserId(); const userId = client.getUserId();
const profileInfo = await client.getProfileInfo(userId); const profileInfo = await client.getProfileInfo(userId);
const avatar_url = Avatar.avatarUrlForUser( const avatarUrl = Avatar.avatarUrlForUser(
{avatarUrl: profileInfo.avatar_url}, {avatarUrl: profileInfo.avatar_url},
AVATAR_SIZE, AVATAR_SIZE, "crop"); AVATAR_SIZE, AVATAR_SIZE, "crop");
this.setState({ this.setState({
userId, userId,
displayname: profileInfo.displayname, displayname: profileInfo.displayname,
avatar_url, avatar_url: avatarUrl,
}); });
} }
private fakeEvent({userId, displayname, avatar_url}: IState) { private fakeEvent({userId, displayname, avatar_url: avatarUrl}: IState) {
// Fake it till we make it // Fake it till we make it
const event = new MatrixEvent(JSON.parse(`{ const event = new MatrixEvent(JSON.parse(`{
"type": "m.room.message", "type": "m.room.message",
@ -85,12 +86,12 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
"msgtype": "m.text", "msgtype": "m.text",
"body": "${this.props.message}", "body": "${this.props.message}",
"displayname": "${displayname}", "displayname": "${displayname}",
"avatar_url": "${avatar_url}" "avatar_url": "${avatarUrl}"
}, },
"msgtype": "m.text", "msgtype": "m.text",
"body": "${this.props.message}", "body": "${this.props.message}",
"displayname": "${displayname}", "displayname": "${displayname}",
"avatar_url": "${avatar_url}" "avatar_url": "${avatarUrl}"
}, },
"unsigned": { "unsigned": {
"age": 97 "age": 97
@ -104,7 +105,7 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
name: displayname, name: displayname,
userId: userId, userId: userId,
getAvatarUrl: (..._) => { getAvatarUrl: (..._) => {
return avatar_url; return avatarUrl;
}, },
}; };
@ -114,13 +115,10 @@ export default class EventTilePreview extends React.Component<IProps, IState> {
public render() { public render() {
const event = this.fakeEvent(this.state); const event = this.fakeEvent(this.state);
let className = classnames( const className = classnames(this.props.className, {
this.props.className,
{
"mx_IRCLayout": this.props.useIRCLayout, "mx_IRCLayout": this.props.useIRCLayout,
"mx_GroupLayout": !this.props.useIRCLayout, "mx_GroupLayout": !this.props.useIRCLayout,
} });
);
return <div className={className}> return <div className={className}>
<EventTile mxEvent={event} useIRCLayout={this.props.useIRCLayout} /> <EventTile mxEvent={event} useIRCLayout={this.props.useIRCLayout} />

View file

@ -198,11 +198,9 @@ export default class Field extends React.PureComponent<PropShapes, IState> {
} }
} }
public render() { public render() {
const { /* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
element, prefixComponent, postfixComponent, className, onValidate, children, const { element, prefixComponent, postfixComponent, className, onValidate, children,
tooltipContent, forceValidity, tooltipClassName, list, ...inputProps} = this.props; tooltipContent, forceValidity, tooltipClassName, list, ...inputProps} = this.props;
// Set some defaults for the <input> element // Set some defaults for the <input> element

View file

@ -78,7 +78,12 @@ export default class IRCTimelineProfileResizer extends React.Component<IProps, I
private onMoueUp(event: MouseEvent) { private onMoueUp(event: MouseEvent) {
if (this.props.roomId) { if (this.props.roomId) {
SettingsStore.setValue("ircDisplayNameWidth", this.props.roomId, SettingLevel.ROOM_DEVICE, this.state.width); SettingsStore.setValue(
"ircDisplayNameWidth",
this.props.roomId,
SettingLevel.ROOM_DEVICE,
this.state.width,
);
} }
} }

View file

@ -18,7 +18,6 @@ limitations under the License.
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import AccessibleButton from "./AccessibleButton";
import Tooltip from './Tooltip'; import Tooltip from './Tooltip';
import { _t } from "../../../languageHandler"; import { _t } from "../../../languageHandler";

View file

@ -41,7 +41,7 @@ const QRCode: React.FC<IProps> = ({data, className, ...options}) => {
return () => { return () => {
cancelled = true; cancelled = true;
}; };
}, [JSON.stringify(data), options]); }, [JSON.stringify(data), options]); // eslint-disable-line react-hooks/exhaustive-deps
return <div className={classNames("mx_QRCode", className)}> return <div className={classNames("mx_QRCode", className)}>
{ dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> } { dataUri ? <img src={dataUri} className="mx_VerificationQRCode" alt={_t("QR Code")} /> : <Spinner /> }

View file

@ -45,7 +45,7 @@ export default class Slider extends React.Component<IProps> {
// non linear slider. // non linear slider.
private offset(values: number[], value: number): number { private offset(values: number[], value: number): number {
// the index of the first number greater than value. // the index of the first number greater than value.
let closest = values.reduce((prev, curr) => { const closest = values.reduce((prev, curr) => {
return (value > curr ? prev + 1 : prev); return (value > curr ? prev + 1 : prev);
}, 0); }, 0);
@ -68,12 +68,11 @@ export default class Slider extends React.Component<IProps> {
const linearInterpolation = (value - closestLessValue) / (closestGreaterValue - closestLessValue); const linearInterpolation = (value - closestLessValue) / (closestGreaterValue - closestLessValue);
return 100 * (closest - 1 + linearInterpolation) * intervalWidth; return 100 * (closest - 1 + linearInterpolation) * intervalWidth;
} }
render(): React.ReactNode { render(): React.ReactNode {
const dots = this.props.values.map(v => const dots = this.props.values.map(v => <Dot
<Dot active={v <= this.props.value} active={v <= this.props.value}
label={this.props.displayFunc(v)} label={this.props.displayFunc(v)}
onClick={this.props.disabled ? () => {} : () => this.props.onSelectionChange(v)} onClick={this.props.disabled ? () => {} : () => this.props.onSelectionChange(v)}
key={v} key={v}
@ -93,7 +92,7 @@ export default class Slider extends React.Component<IProps> {
return <div className="mx_Slider"> return <div className="mx_Slider">
<div> <div>
<div className="mx_Slider_bar"> <div className="mx_Slider_bar">
<hr onClick={this.props.disabled ? () => {} : this.onClick.bind(this)}/> <hr onClick={this.props.disabled ? () => {} : this.onClick.bind(this)} />
{ selection } { selection }
</div> </div>
<div className="mx_Slider_dotContainer"> <div className="mx_Slider_dotContainer">

View file

@ -17,8 +17,6 @@ limitations under the License.
import React from "react"; import React from "react";
import { randomString } from "matrix-js-sdk/src/randomstring"; import { randomString } from "matrix-js-sdk/src/randomstring";
const CHECK_BOX_SVG = require("../../../../res/img/feather-customised/check.svg");
interface IProps extends React.InputHTMLAttributes<HTMLInputElement> { interface IProps extends React.InputHTMLAttributes<HTMLInputElement> {
} }
@ -39,13 +37,14 @@ export default class StyledCheckbox extends React.PureComponent<IProps, IState>
} }
public render() { public render() {
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
const { children, className, ...otherProps } = this.props; const { children, className, ...otherProps } = this.props;
return <span className={"mx_Checkbox " + className}> return <span className={"mx_Checkbox " + className}>
<input id={this.id} {...otherProps} type="checkbox" /> <input id={this.id} {...otherProps} type="checkbox" />
<label htmlFor={this.id}> <label htmlFor={this.id}>
{/* Using the div to center the image */} {/* Using the div to center the image */}
<div className="mx_Checkbox_background"> <div className="mx_Checkbox_background">
<img src={CHECK_BOX_SVG}/> <img src={require("../../../../res/img/feather-customised/check.svg")} />
</div> </div>
<div> <div>
{ this.props.children } { this.props.children }

View file

@ -1,76 +0,0 @@
/*
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React, {createRef} from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import { _t } from '../../../languageHandler';
export default createReactClass({
displayName: 'UserSelector',
propTypes: {
onChange: PropTypes.func,
selected_users: PropTypes.arrayOf(PropTypes.string),
},
getDefaultProps: function() {
return {
onChange: function() {},
selected: [],
};
},
// TODO: [REACT-WARNING] Replace component with real class, use constructor for refs
UNSAFE_componentWillMount: function() {
this._user_id_input = createRef();
},
addUser: function(user_id) {
if (this.props.selected_users.indexOf(user_id == -1)) {
this.props.onChange(this.props.selected_users.concat([user_id]));
}
},
removeUser: function(user_id) {
this.props.onChange(this.props.selected_users.filter(function(e) {
return e != user_id;
}));
},
onAddUserId: function() {
this.addUser(this._user_id_input.current.value);
this._user_id_input.current.value = "";
},
render: function() {
const self = this;
return (
<div>
<ul className="mx_UserSelector_UserIdList">
{ this.props.selected_users.map(function(user_id, i) {
return <li key={user_id}>{ user_id } - <span onClick={function() {self.removeUser(user_id);}}>X</span></li>;
}) }
</ul>
<input type="text" ref={this._user_id_input} defaultValue="" className="mx_UserSelector_userIdInput" placeholder={_t("ex. @bob:example.com")} />
<button onClick={this.onAddUserId} className="mx_UserSelector_AddUserId">
{ _t("Add User") }
</button>
</div>
);
},
});

View file

@ -15,13 +15,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, {forwardRef} from "react";
export default ({mxEvent}) => { export default forwardRef(({mxEvent}, ref) => {
const text = mxEvent.getContent().body; const text = mxEvent.getContent().body;
return ( return (
<span className="mx_UnknownBody"> <span className="mx_UnknownBody" ref={ref}>
{ text } { text }
</span> </span>
); );
}; });

View file

@ -76,14 +76,16 @@ const EncryptionInfo: React.FC<IProps> = ({
description = ( description = (
<div> <div>
<p>{_t("Messages in this room are end-to-end encrypted.")}</p> <p>{_t("Messages in this room are end-to-end encrypted.")}</p>
<p>{_t("Your messages are secured and only you and the recipient have the unique keys to unlock them.")}</p> <p>{_t("Your messages are secured and only you and the recipient have " +
"the unique keys to unlock them.")}</p>
</div> </div>
); );
} else { } else {
description = ( description = (
<div> <div>
<p>{_t("Messages in this room are not end-to-end encrypted.")}</p> <p>{_t("Messages in this room are not end-to-end encrypted.")}</p>
<p>{_t("In encrypted rooms, your messages are secured and only you and the recipient have the unique keys to unlock them.")}</p> <p>{_t("In encrypted rooms, your messages are secured and only you and the recipient have " +
"the unique keys to unlock them.")}</p>
</div> </div>
); );
} }

View file

@ -23,7 +23,10 @@ import dis from '../../../dispatcher/dispatcher';
import RightPanelStore from "../../../stores/RightPanelStore"; import RightPanelStore from "../../../stores/RightPanelStore";
import {RightPanelPhases} from "../../../stores/RightPanelStorePhases"; import {RightPanelPhases} from "../../../stores/RightPanelStorePhases";
import {Action} from '../../../dispatcher/actions'; import {Action} from '../../../dispatcher/actions';
import {SetRightPanelPhasePayload, SetRightPanelPhaseRefireParams} from '../../../dispatcher/payloads/SetRightPanelPhasePayload'; import {
SetRightPanelPhasePayload,
SetRightPanelPhaseRefireParams,
} from '../../../dispatcher/payloads/SetRightPanelPhasePayload';
import {EventSubscription} from "fbemitter"; import {EventSubscription} from "fbemitter";
export enum HeaderKind { export enum HeaderKind {
@ -38,7 +41,7 @@ interface IState {
interface IProps {} interface IProps {}
export default class HeaderButtons extends React.Component<IProps, IState> { export default abstract class HeaderButtons extends React.Component<IProps, IState> {
private storeToken: EventSubscription; private storeToken: EventSubscription;
private dispatcherRef: string; private dispatcherRef: string;
@ -92,14 +95,7 @@ export default class HeaderButtons extends React.Component<IProps, IState> {
} }
// XXX: Make renderButtons a prop // XXX: Make renderButtons a prop
public renderButtons(): JSX.Element[] { public abstract renderButtons(): JSX.Element[];
// Ignore - intended to be overridden by subclasses
// Return empty fragment to satisfy the type
return [
<React.Fragment>
</React.Fragment>
];
}
public render() { public render() {
// inline style as this will be swapped around in future commits // inline style as this will be swapped around in future commits

View file

@ -30,8 +30,6 @@ import {_t} from "../../../languageHandler";
import SdkConfig from "../../../SdkConfig"; import SdkConfig from "../../../SdkConfig";
import E2EIcon from "../rooms/E2EIcon"; import E2EIcon from "../rooms/E2EIcon";
import { import {
PHASE_UNSENT,
PHASE_REQUESTED,
PHASE_READY, PHASE_READY,
PHASE_DONE, PHASE_DONE,
PHASE_STARTED, PHASE_STARTED,
@ -104,10 +102,11 @@ export default class VerificationPanel extends React.PureComponent<IProps, IStat
</div>; </div>;
} }
if (showSAS) { if (showSAS) {
sasBlockDialog = sasBlockDialog = <div className='mx_VerificationPanel_QRPhase_startOption'>
<div className='mx_VerificationPanel_QRPhase_startOption'>
<p>{_t("Compare unique emoji")}</p> <p>{_t("Compare unique emoji")}</p>
<span className='mx_VerificationPanel_QRPhase_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span> <span className='mx_VerificationPanel_QRPhase_helpText'>
{_t("Compare a unique set of emoji if you don't have a camera on either device")}
</span>
<AccessibleButton disabled={this.state.emojiButtonClicked} onClick={this.startSAS} kind='primary'> <AccessibleButton disabled={this.state.emojiButtonClicked} onClick={this.startSAS} kind='primary'>
{_t("Start")} {_t("Start")}
</AccessibleButton> </AccessibleButton>

View file

@ -92,6 +92,7 @@ export default class NotificationBadge extends React.PureComponent<XOR<IProps, I
}; };
public render(): React.ReactElement { public render(): React.ReactElement {
/* eslint @typescript-eslint/no-unused-vars: ["error", { "ignoreRestSiblings": true }] */
const {notification, forceCount, roomId, onClick, ...props} = this.props; const {notification, forceCount, roomId, onClick, ...props} = this.props;
// Don't show a badge if we don't need to // Don't show a badge if we don't need to

View file

@ -218,7 +218,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
private getRoomDelta = (roomId: string, delta: number, unread = false) => { private getRoomDelta = (roomId: string, delta: number, unread = false) => {
const lists = RoomListStore.instance.orderedLists; const lists = RoomListStore.instance.orderedLists;
let rooms: Room = []; const rooms: Room = [];
TAG_ORDER.forEach(t => { TAG_ORDER.forEach(t => {
let listRooms = lists[t]; let listRooms = lists[t];
@ -346,8 +346,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
: TAG_AESTHETICS[orderedTagId]; : TAG_AESTHETICS[orderedTagId];
if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`); if (!aesthetics) throw new Error(`Tag ${orderedTagId} does not have aesthetics`);
components.push( components.push(<RoomSublist
<RoomSublist
key={`sublist-${orderedTagId}`} key={`sublist-${orderedTagId}`}
tagId={orderedTagId} tagId={orderedTagId}
forRooms={true} forRooms={true}
@ -359,8 +358,7 @@ export default class RoomList extends React.PureComponent<IProps, IState> {
isMinimized={this.props.isMinimized} isMinimized={this.props.isMinimized}
onResize={this.props.onResize} onResize={this.props.onResize}
extraBadTilesThatShouldntExist={extraTiles} extraBadTilesThatShouldntExist={extraTiles}
/> />);
);
} }
return components; return components;

View file

@ -1,80 +0,0 @@
/*
Copyright 2016 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import * as sdk from "../../../index";
import { _t } from '../../../languageHandler';
export default createReactClass({
displayName: 'RoomNameEditor',
propTypes: {
room: PropTypes.object.isRequired,
},
getInitialState: function() {
return {
name: null,
};
},
// TODO: [REACT-WARNING] Move this to constructor
UNSAFE_componentWillMount: function() {
const room = this.props.room;
const name = room.currentState.getStateEvents('m.room.name', '');
const myId = MatrixClientPeg.get().credentials.userId;
const defaultName = room.getDefaultRoomName(myId);
this.setState({
name: name ? name.getContent().name : '',
});
this._placeholderName = _t("Unnamed Room");
if (defaultName && defaultName !== 'Empty room') { // default name from JS SDK, needs no translation as we don't ever show it.
this._placeholderName += " (" + defaultName + ")";
}
},
getRoomName: function() {
return this.state.name;
},
_onValueChanged: function(value, shouldSubmit) {
this.setState({
name: value,
});
},
render: function() {
const EditableText = sdk.getComponent("elements.EditableText");
return (
<div className="mx_RoomHeader_name">
<EditableText
className="mx_RoomHeader_nametext mx_RoomHeader_editable"
placeholderClassName="mx_RoomHeader_placeholder"
placeholder={this._placeholderName}
blurToCancel={false}
initialValue={this.state.name}
onValueChanged={this._onValueChanged}
dir="auto" />
</div>
);
},
});

View file

@ -517,15 +517,13 @@ export default class RoomSublist extends React.Component<IProps, IState> {
if (this.state.rooms) { if (this.state.rooms) {
const visibleRooms = this.state.rooms.slice(0, this.numVisibleTiles); const visibleRooms = this.state.rooms.slice(0, this.numVisibleTiles);
for (const room of visibleRooms) { for (const room of visibleRooms) {
tiles.push( tiles.push(<RoomTile
<RoomTile
room={room} room={room}
key={`room-${room.roomId}`} key={`room-${room.roomId}`}
showMessagePreview={this.layout.showPreviews} showMessagePreview={this.layout.showPreviews}
isMinimized={this.props.isMinimized} isMinimized={this.props.isMinimized}
tag={this.props.tagId} tag={this.props.tagId}
/> />);
);
} }
} }
@ -710,7 +708,12 @@ export default class RoomSublist extends React.Component<IProps, IState> {
// doesn't become sticky. // doesn't become sticky.
// The same applies to the notification badge. // The same applies to the notification badge.
return ( return (
<div className={classes} onKeyDown={this.onHeaderKeyDown} onFocus={onFocus} aria-label={this.props.label}> <div
className={classes}
onKeyDown={this.onHeaderKeyDown}
onFocus={onFocus}
aria-label={this.props.label}
>
<div className="mx_RoomSublist_stickable"> <div className="mx_RoomSublist_stickable">
<Button <Button
onFocus={onFocus} onFocus={onFocus}
@ -762,7 +765,7 @@ export default class RoomSublist extends React.Component<IProps, IState> {
const showMoreAtMinHeight = minTiles < this.numTiles; const showMoreAtMinHeight = minTiles < this.numTiles;
const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0); const minHeightPadding = RESIZE_HANDLE_HEIGHT + (showMoreAtMinHeight ? SHOW_N_BUTTON_HEIGHT : 0);
const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding); const minTilesPx = layout.tilesToPixelsWithPadding(minTiles, minHeightPadding);
let maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding); const maxTilesPx = layout.tilesToPixelsWithPadding(this.numTiles, this.padding);
const showMoreBtnClasses = classNames({ const showMoreBtnClasses = classNames({
'mx_RoomSublist_showNButton': true, 'mx_RoomSublist_showNButton': true,
}); });

View file

@ -31,7 +31,7 @@ import { ChevronFace, ContextMenuTooltipButton } from "../../structures/ContextM
import { DefaultTagID, TagID } from "../../../stores/room-list/models"; import { DefaultTagID, TagID } from "../../../stores/room-list/models";
import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore"; import { MessagePreviewStore, ROOM_PREVIEW_CHANGED } from "../../../stores/room-list/MessagePreviewStore";
import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar"; import DecoratedRoomAvatar from "../avatars/DecoratedRoomAvatar";
import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE, } from "../../../RoomNotifs"; import { ALL_MESSAGES, ALL_MESSAGES_LOUD, MENTIONS_ONLY, MUTE } from "../../../RoomNotifs";
import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MatrixClientPeg } from "../../../MatrixClientPeg";
import NotificationBadge from "./NotificationBadge"; import NotificationBadge from "./NotificationBadge";
import { Volume } from "../../../RoomNotifsTypes"; import { Volume } from "../../../RoomNotifsTypes";
@ -48,7 +48,7 @@ import IconizedContextMenu, {
IconizedContextMenuCheckbox, IconizedContextMenuCheckbox,
IconizedContextMenuOption, IconizedContextMenuOption,
IconizedContextMenuOptionList, IconizedContextMenuOptionList,
IconizedContextMenuRadio IconizedContextMenuRadio,
} from "../context_menus/IconizedContextMenu"; } from "../context_menus/IconizedContextMenu";
import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore"; import { CommunityPrototypeStore, IRoomProfile } from "../../../stores/CommunityPrototypeStore";
import { UPDATE_EVENT } from "../../../stores/AsyncStore"; import { UPDATE_EVENT } from "../../../stores/AsyncStore";
@ -249,7 +249,7 @@ export default class RoomTile extends React.PureComponent<IProps, IState> {
removeTag, removeTag,
addTag, addTag,
undefined, undefined,
0 0,
)); ));
} else { } else {
console.warn(`Unexpected tag ${tagId} applied to ${this.props.room.room_id}`); console.warn(`Unexpected tag ${tagId} applied to ${this.props.room.room_id}`);

View file

@ -1,68 +0,0 @@
/*
Copyright 2016 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import createReactClass from 'create-react-class';
import * as sdk from '../../../index';
import { _t } from "../../../languageHandler";
export default createReactClass({
displayName: 'RoomTopicEditor',
propTypes: {
room: PropTypes.object.isRequired,
},
getInitialState: function() {
return {
topic: null,
};
},
componentDidMount: function() {
const room = this.props.room;
const topic = room.currentState.getStateEvents('m.room.topic', '');
this.setState({
topic: topic ? topic.getContent().topic : '',
});
},
getTopic: function() {
return this.state.topic;
},
_onValueChanged: function(value) {
this.setState({
topic: value,
});
},
render: function() {
const EditableText = sdk.getComponent("elements.EditableText");
return (
<EditableText
className="mx_RoomHeader_topic mx_RoomHeader_editable"
placeholderClassName="mx_RoomHeader_placeholder"
placeholder={_t("Add a topic")}
blurToCancel={false}
initialValue={this.state.topic}
onValueChanged={this._onValueChanged}
dir="auto" />
);
},
});

View file

@ -19,9 +19,7 @@ import classNames from "classnames";
import { import {
RovingAccessibleButton, RovingAccessibleButton,
RovingAccessibleTooltipButton, RovingAccessibleTooltipButton,
RovingTabIndexWrapper
} from "../../../accessibility/RovingTabIndex"; } from "../../../accessibility/RovingTabIndex";
import AccessibleButton from "../../views/elements/AccessibleButton";
import NotificationBadge from "./NotificationBadge"; import NotificationBadge from "./NotificationBadge";
import { NotificationState } from "../../../stores/notifications/NotificationState"; import { NotificationState } from "../../../stores/notifications/NotificationState";

View file

@ -42,7 +42,7 @@ function getStatusText(status: UpdateCheckStatus, errorDetail?: string) {
return _t('Downloading update...'); return _t('Downloading update...');
case UpdateCheckStatus.Ready: case UpdateCheckStatus.Ready:
return _t("New version available. <a>Update now.</a>", {}, { return _t("New version available. <a>Update now.</a>", {}, {
a: sub => <AccessibleButton kind="link" onClick={installUpdate}>{sub}</AccessibleButton> a: sub => <AccessibleButton kind="link" onClick={installUpdate}>{sub}</AccessibleButton>,
}); });
} }
} }

View file

@ -170,7 +170,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
"baseFontSize", "baseFontSize",
null, null,
SettingLevel.DEVICE, SettingLevel.DEVICE,
parseInt(value, 10) - FontWatcher.SIZE_DIFF parseInt(value, 10) - FontWatcher.SIZE_DIFF,
); );
return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})}; return {valid: true, feedback: _t('Use between %(min)s pt and %(max)s pt', {min, max})};

View file

@ -29,7 +29,15 @@ interface IProps extends IGenericToastProps {
const SECOND = 1000; const SECOND = 1000;
const GenericExpiringToast: React.FC<IProps> = ({description, acceptLabel, dismissLabel, onAccept, onDismiss, toastKey, numSeconds}) => { const GenericExpiringToast: React.FC<IProps> = ({
description,
acceptLabel,
dismissLabel,
onAccept,
onDismiss,
toastKey,
numSeconds,
}) => {
const onReject = () => { const onReject = () => {
if (onDismiss) onDismiss(); if (onDismiss) onDismiss();
ToastStore.sharedInstance().dismissToast(toastKey); ToastStore.sharedInstance().dismissToast(toastKey);

View file

@ -31,7 +31,13 @@ interface IPropsExtended extends IProps {
onReject(); onReject();
} }
const GenericToast: React.FC<XOR<IPropsExtended, IProps>> = ({description, acceptLabel, rejectLabel, onAccept, onReject}) => { const GenericToast: React.FC<XOR<IPropsExtended, IProps>> = ({
description,
acceptLabel,
rejectLabel,
onAccept,
onReject,
}) => {
return <div> return <div>
<div className="mx_Toast_description"> <div className="mx_Toast_description">
{ description } { description }

View file

@ -97,10 +97,7 @@ export default class CallView extends React.Component<IProps, IState> {
if (this.props.room) { if (this.props.room) {
const roomId = this.props.room.roomId; const roomId = this.props.room.roomId;
call = CallHandler.getCallForRoom(roomId) || call = CallHandler.getCallForRoom(roomId) ||
(this.props.ConferenceHandler ? (this.props.ConferenceHandler ? this.props.ConferenceHandler.getConferenceCallForRoom(roomId) : null);
this.props.ConferenceHandler.getConferenceCallForRoom(roomId) :
null
);
if (this.call) { if (this.call) {
this.setState({ call: call }); this.setState({ call: call });

View file

@ -51,7 +51,7 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
private onAction = (payload: ActionPayload) => { private onAction = (payload: ActionPayload) => {
switch (payload.action) { switch (payload.action) {
case 'call_state': case 'call_state': {
const call = CallHandler.getCall(payload.room_id); const call = CallHandler.getCall(payload.room_id);
if (call && call.call_state === 'ringing') { if (call && call.call_state === 'ringing') {
this.setState({ this.setState({
@ -63,6 +63,7 @@ export default class IncomingCallBox extends React.Component<IProps, IState> {
}); });
} }
} }
}
}; };
private onAnswerClick: React.MouseEventHandler = (e) => { private onAnswerClick: React.MouseEventHandler = (e) => {

View file

@ -1201,7 +1201,6 @@
"%(count)s unread messages.|other": "%(count)s unread messages.", "%(count)s unread messages.|other": "%(count)s unread messages.",
"%(count)s unread messages.|one": "1 unread message.", "%(count)s unread messages.|one": "1 unread message.",
"Unread messages.": "Unread messages.", "Unread messages.": "Unread messages.",
"Add a topic": "Add a topic",
"Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.", "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.",
"This room has already been upgraded.": "This room has already been upgraded.", "This room has already been upgraded.": "This room has already been upgraded.",
"This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.", "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.": "This room is running room version <roomVersion />, which this homeserver has marked as <i>unstable</i>.",
@ -1556,8 +1555,6 @@
"Room directory": "Room directory", "Room directory": "Room directory",
"Sign in with single sign-on": "Sign in with single sign-on", "Sign in with single sign-on": "Sign in with single sign-on",
"And %(count)s more...|other": "And %(count)s more...", "And %(count)s more...|other": "And %(count)s more...",
"ex. @bob:example.com": "ex. @bob:example.com",
"Add User": "Add User",
"Home": "Home", "Home": "Home",
"Enter a server name": "Enter a server name", "Enter a server name": "Enter a server name",
"Looks good": "Looks good", "Looks good": "Looks good",
@ -1890,10 +1887,6 @@
"<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Warning</b>: You should only set up key backup from a trusted computer.", "<b>Warning</b>: You should only set up key backup from a trusted computer.": "<b>Warning</b>: You should only set up key backup from a trusted computer.",
"Access your secure message history and set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.", "Access your secure message history and set up secure messaging by entering your recovery key.": "Access your secure message history and set up secure messaging by entering your recovery key.",
"If you've forgotten your recovery key you can <button>set up new recovery options</button>": "If you've forgotten your recovery key you can <button>set up new recovery options</button>", "If you've forgotten your recovery key you can <button>set up new recovery options</button>": "If you've forgotten your recovery key you can <button>set up new recovery options</button>",
"Private Chat": "Private Chat",
"Public Chat": "Public Chat",
"Custom": "Custom",
"Address (optional)": "Address (optional)",
"Reject invitation": "Reject invitation", "Reject invitation": "Reject invitation",
"Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?", "Are you sure you want to reject the invitation?": "Are you sure you want to reject the invitation?",
"Unable to reject invite": "Unable to reject invite", "Unable to reject invite": "Unable to reject invite",
@ -1987,11 +1980,6 @@
"Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s", "Sign in to your Matrix account on %(serverName)s": "Sign in to your Matrix account on %(serverName)s",
"Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />", "Sign in to your Matrix account on <underlinedServerName />": "Sign in to your Matrix account on <underlinedServerName />",
"Sign in with SSO": "Sign in with SSO", "Sign in with SSO": "Sign in with SSO",
"Sorry, your browser is <b>not</b> able to run %(brand)s.": "Sorry, your browser is <b>not</b> able to run %(brand)s.",
"%(brand)s uses many advanced browser features, some of which are not available or experimental in your current browser.": "%(brand)s uses many advanced browser features, some of which are not available or experimental in your current browser.",
"Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.": "Please install <chromeLink>Chrome</chromeLink>, <firefoxLink>Firefox</firefoxLink>, or <safariLink>Safari</safariLink> for the best experience.",
"With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!": "With your current browser, the look and feel of the application may be completely incorrect, and some or all features may not function. If you want to try it anyway you can continue, but you are on your own in terms of any issues you may encounter!",
"I understand the risks and wish to continue": "I understand the risks and wish to continue",
"Couldn't load page": "Couldn't load page", "Couldn't load page": "Couldn't load page",
"You must <a>register</a> to use this functionality": "You must <a>register</a> to use this functionality", "You must <a>register</a> to use this functionality": "You must <a>register</a> to use this functionality",
"You must join the room to see its files": "You must join the room to see its files", "You must join the room to see its files": "You must join the room to see its files",

View file

@ -442,7 +442,7 @@ export function pickBestLanguage(langs: string[]): string {
} }
function getLangsJson(): Promise<object> { function getLangsJson(): Promise<object> {
return new Promise(async (resolve, reject) => { return new Promise((resolve, reject) => {
let url; let url;
if (typeof(webpackLangJsonUrl) === 'string') { // in Jest this 'url' isn't a URL, so just fall through if (typeof(webpackLangJsonUrl) === 'string') { // in Jest this 'url' isn't a URL, so just fall through
url = webpackLangJsonUrl; url = webpackLangJsonUrl;
@ -453,7 +453,7 @@ function getLangsJson(): Promise<object> {
{ method: "GET", url }, { method: "GET", url },
(err, response, body) => { (err, response, body) => {
if (err || response.status < 200 || response.status >= 300) { if (err || response.status < 200 || response.status >= 300) {
reject({err: err, response: response}); reject(err);
return; return;
} }
resolve(JSON.parse(body)); resolve(JSON.parse(body));
@ -488,7 +488,7 @@ function getLanguage(langPath: string): object {
{ method: "GET", url: langPath }, { method: "GET", url: langPath },
(err, response, body) => { (err, response, body) => {
if (err || response.status < 200 || response.status >= 300) { if (err || response.status < 200 || response.status >= 300) {
reject({err: err, response: response}); reject(err);
return; return;
} }
resolve(weblateToCounterpart(JSON.parse(body))); resolve(weblateToCounterpart(JSON.parse(body)));

934
yarn.lock

File diff suppressed because it is too large Load diff