Merge remote-tracking branch 'upstream/develop' into burn-sdk-get-comp-with-fire

Signed-off-by: Šimon Brandner <simon.bra.ag@gmail.com>
This commit is contained in:
Šimon Brandner 2021-07-07 17:12:19 +02:00
commit f91b35a0a0
No known key found for this signature in database
GPG key ID: 9760693FDD98A790
29 changed files with 599 additions and 342 deletions

View file

@ -24,7 +24,7 @@ import FocusLock from "react-focus-lock";
import MemberAvatar from "../avatars/MemberAvatar";
import { ContextMenuTooltipButton } from "../../../accessibility/context_menu/ContextMenuTooltipButton";
import MessageContextMenu from "../context_menus/MessageContextMenu";
import { aboveLeftOf, ContextMenu } from '../../structures/ContextMenu';
import { aboveLeftOf } from '../../structures/ContextMenu';
import MessageTimestamp from "../messages/MessageTimestamp";
import SettingsStore from "../../../settings/SettingsStore";
import { formatFullDate } from "../../../DateUtils";
@ -122,7 +122,7 @@ export default class ImageView extends React.Component<IProps, IState> {
const image = this.image.current;
const imageWrapper = this.imageWrapper.current;
const rotation = inputRotation || this.state.rotation;
const rotation = inputRotation ?? this.state.rotation;
const imageIsNotFlipped = rotation % 180 === 0;
@ -304,17 +304,13 @@ export default class ImageView extends React.Component<IProps, IState> {
let contextMenu = null;
if (this.state.contextMenuDisplayed) {
contextMenu = (
<ContextMenu
<MessageContextMenu
{...aboveLeftOf(this.contextMenuButton.current.getBoundingClientRect())}
mxEvent={this.props.mxEvent}
permalinkCreator={this.props.permalinkCreator}
onFinished={this.onCloseContextMenu}
>
<MessageContextMenu
mxEvent={this.props.mxEvent}
permalinkCreator={this.props.permalinkCreator}
onFinished={this.onCloseContextMenu}
onCloseDialog={this.props.onFinished}
/>
</ContextMenu>
onCloseDialog={this.props.onFinished}
/>
);
}

View file

@ -17,15 +17,25 @@ limitations under the License.
import React from 'react';
import { _t } from "../../../languageHandler";
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import { IntegrationManagerInstance } from "../../../integrations/IntegrationManagerInstance";
import * as sdk from '../../../index';
import SettingsStore from "../../../settings/SettingsStore";
import { SettingLevel } from "../../../settings/SettingLevel";
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
}
interface IState {
currentManager: IntegrationManagerInstance;
provisioningEnabled: boolean;
}
@replaceableComponent("views.settings.SetIntegrationManager")
export default class SetIntegrationManager extends React.Component {
constructor() {
super();
export default class SetIntegrationManager extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
const currentManager = IntegrationManagers.sharedInstance().getPrimaryManager();
@ -35,7 +45,7 @@ export default class SetIntegrationManager extends React.Component {
};
}
onProvisioningToggled = () => {
private onProvisioningToggled = (): void => {
const current = this.state.provisioningEnabled;
SettingsStore.setValue("integrationProvisioning", null, SettingLevel.ACCOUNT, !current).catch(err => {
console.error("Error changing integration manager provisioning");
@ -46,7 +56,7 @@ export default class SetIntegrationManager extends React.Component {
this.setState({ provisioningEnabled: !current });
};
render() {
public render(): React.ReactNode {
const ToggleSwitch = sdk.getComponent("views.elements.ToggleSwitch");
const currentManager = this.state.currentManager;

View file

@ -24,6 +24,8 @@ import PlatformPeg from "../../../../../PlatformPeg";
import { SettingLevel } from "../../../../../settings/SettingLevel";
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
import SettingsFlag from '../../../elements/SettingsFlag';
import * as KeyboardShortcuts from "../../../../../accessibility/KeyboardShortcuts";
import AccessibleButton from "../../../elements/AccessibleButton";
interface IState {
autoLaunch: boolean;
@ -45,6 +47,10 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
'breadcrumbs',
];
static KEYBINDINGS_SETTINGS = [
'ctrlFForSearch',
];
static COMPOSER_SETTINGS = [
'MessageComposerInput.autoReplaceEmoji',
'MessageComposerInput.suggestEmoji',
@ -53,28 +59,32 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
'MessageComposerInput.showStickersButton',
];
static TIMELINE_SETTINGS = [
'showTypingNotifications',
'autoplayGifsAndVideos',
'urlPreviewsEnabled',
'TextualBody.enableBigEmoji',
'showReadReceipts',
static TIME_SETTINGS = [
'showTwelveHourTimestamps',
'alwaysShowTimestamps',
'showRedactions',
];
static CODE_BLOCKS_SETTINGS = [
'enableSyntaxHighlightLanguageDetection',
'expandCodeByDefault',
'scrollToBottomOnMessageSent',
'showCodeLineNumbers',
'showJoinLeaves',
'showAvatarChanges',
'showDisplaynameChanges',
'showImages',
'showChatEffects',
'Pill.shouldShowPillAvatar',
'ctrlFForSearch',
];
static IMAGES_AND_VIDEOS_SETTINGS = [
'urlPreviewsEnabled',
'autoplayGifsAndVideos',
'showImages',
];
static TIMELINE_SETTINGS = [
'showTypingNotifications',
'showRedactions',
'showReadReceipts',
'showJoinLeaves',
'showDisplaynameChanges',
'showChatEffects',
'showAvatarChanges',
'Pill.shouldShowPillAvatar',
'TextualBody.enableBigEmoji',
'scrollToBottomOnMessageSent',
];
static GENERAL_SETTINGS = [
'TagPanel.enableTagPanel',
'promptBeforeInviteUnknownUsers',
@ -221,11 +231,34 @@ export default class PreferencesUserSettingsTab extends React.Component<{}, ISta
{this.renderGroup(PreferencesUserSettingsTab.ROOM_LIST_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Keyboard shortcuts")}</span>
<AccessibleButton className="mx_SettingsFlag" onClick={KeyboardShortcuts.toggleDialog}>
{ _t("To view all keyboard shortcuts, click here.") }
</AccessibleButton>
{this.renderGroup(PreferencesUserSettingsTab.KEYBINDINGS_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Displaying time")}</span>
{this.renderGroup(PreferencesUserSettingsTab.TIME_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Composer")}</span>
{this.renderGroup(PreferencesUserSettingsTab.COMPOSER_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Code blocks")}</span>
{this.renderGroup(PreferencesUserSettingsTab.CODE_BLOCKS_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Images, GIFs and videos")}</span>
{this.renderGroup(PreferencesUserSettingsTab.IMAGES_AND_VIDEOS_SETTINGS)}
</div>
<div className="mx_SettingsTab_section">
<span className="mx_SettingsTab_subheading">{_t("Timeline")}</span>
{this.renderGroup(PreferencesUserSettingsTab.TIMELINE_SETTINGS)}

View file

@ -15,39 +15,48 @@ limitations under the License.
*/
import React from "react";
import PropTypes from "prop-types";
import { _t, pickBestLanguage } from "../../../languageHandler";
import * as sdk from "../../..";
import { objectClone } from "../../../utils/objects";
import StyledCheckbox from "../elements/StyledCheckbox";
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
policiesAndServicePairs: any[];
onFinished: (string) => void;
agreedUrls: string[]; // array of URLs the user has accepted
introElement: Node;
}
interface IState {
policies: Policy[];
busy: boolean;
}
interface Policy {
checked: boolean;
url: string;
name: string;
}
@replaceableComponent("views.terms.InlineTermsAgreement")
export default class InlineTermsAgreement extends React.Component {
static propTypes = {
policiesAndServicePairs: PropTypes.array.isRequired, // array of service/policy pairs
agreedUrls: PropTypes.array.isRequired, // array of URLs the user has accepted
onFinished: PropTypes.func.isRequired, // takes an argument of accepted URLs
introElement: PropTypes.node,
};
constructor() {
super();
export default class InlineTermsAgreement extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
policies: [],
busy: false,
};
}
componentDidMount() {
public componentDidMount(): void {
// Build all the terms the user needs to accept
const policies = []; // { checked, url, name }
for (const servicePolicies of this.props.policiesAndServicePairs) {
const availablePolicies = Object.values(servicePolicies.policies);
for (const policy of availablePolicies) {
const language = pickBestLanguage(Object.keys(policy).filter(p => p !== 'version'));
const renderablePolicy = {
const renderablePolicy: Policy = {
checked: false,
url: policy[language].url,
name: policy[language].name,
@ -59,13 +68,13 @@ export default class InlineTermsAgreement extends React.Component {
this.setState({ policies });
}
_togglePolicy = (index) => {
private togglePolicy = (index: number): void => {
const policies = objectClone(this.state.policies);
policies[index].checked = !policies[index].checked;
this.setState({ policies });
};
_onContinue = () => {
private onContinue = (): void => {
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
if (hasUnchecked) return;
@ -73,7 +82,7 @@ export default class InlineTermsAgreement extends React.Component {
this.props.onFinished(this.state.policies.map(p => p.url));
};
_renderCheckboxes() {
private renderCheckboxes(): React.ReactNode[] {
const rendered = [];
for (let i = 0; i < this.state.policies.length; i++) {
const policy = this.state.policies[i];
@ -93,7 +102,7 @@ export default class InlineTermsAgreement extends React.Component {
<div key={i} className='mx_InlineTermsAgreement_cbContainer'>
<div>{introText}</div>
<div className='mx_InlineTermsAgreement_checkbox'>
<StyledCheckbox onChange={() => this._togglePolicy(i)} checked={policy.checked}>
<StyledCheckbox onChange={() => this.togglePolicy(i)} checked={policy.checked}>
{_t("Accept")}
</StyledCheckbox>
</div>
@ -103,16 +112,16 @@ export default class InlineTermsAgreement extends React.Component {
return rendered;
}
render() {
public render(): React.ReactNode {
const AccessibleButton = sdk.getComponent("views.elements.AccessibleButton");
const hasUnchecked = !!this.state.policies.some(p => !p.checked);
return (
<div>
{this.props.introElement}
{this._renderCheckboxes()}
{this.renderCheckboxes()}
<AccessibleButton
onClick={this._onContinue}
onClick={this.onContinue}
disabled={hasUnchecked || this.state.busy}
kind="primary_sm"
>

View file

@ -15,18 +15,17 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.verification.VerificationCancelled")
export default class VerificationCancelled extends React.Component {
static propTypes = {
onDone: PropTypes.func.isRequired,
}
interface IProps {
onDone: () => void;
}
render() {
@replaceableComponent("views.verification.VerificationCancelled")
export default class VerificationCancelled extends React.Component<IProps> {
public render(): React.ReactNode {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<p>{_t(

View file

@ -15,18 +15,17 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import * as sdk from '../../../index';
import { _t } from '../../../languageHandler';
import { replaceableComponent } from "../../../utils/replaceableComponent";
@replaceableComponent("views.verification.VerificationComplete")
export default class VerificationComplete extends React.Component {
static propTypes = {
onDone: PropTypes.func.isRequired,
}
interface IProps {
onDone: () => void;
}
render() {
@replaceableComponent("views.verification.VerificationComplete")
export default class VerificationComplete extends React.Component<IProps> {
public render(): React.ReactNode {
const DialogButtons = sdk.getComponent('views.elements.DialogButtons');
return <div>
<h2>{_t("Verified!")}</h2>

View file

@ -1,68 +0,0 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { _t } from '../../../languageHandler';
import AccessibleButton from "../elements/AccessibleButton";
import { replaceableComponent } from "../../../utils/replaceableComponent";
import VerificationQRCode from "../elements/crypto/VerificationQRCode";
import Spinner from "../elements/Spinner";
import { SCAN_QR_CODE_METHOD } from "matrix-js-sdk/src/crypto/verification/QRCode";
@replaceableComponent("views.verification.VerificationQREmojiOptions")
export default class VerificationQREmojiOptions extends React.Component {
static propTypes = {
request: PropTypes.object.isRequired,
onCancel: PropTypes.func.isRequired,
onStartEmoji: PropTypes.func.isRequired,
};
render() {
const { request } = this.props;
const showQR = request.otherPartySupportsMethod(SCAN_QR_CODE_METHOD);
let qrCode;
if (showQR) {
qrCode = <VerificationQRCode qrCodeData={request.qrCodeData} />;
} else {
qrCode = <div className='mx_VerificationQREmojiOptions_noQR'><Spinner /></div>;
}
return (
<div>
{_t("Verify this session by completing one of the following:")}
<div className='mx_IncomingSasDialog_startOptions'>
<div className='mx_IncomingSasDialog_startOption'>
<p>{_t("Scan this unique code")}</p>
{qrCode}
</div>
<div className='mx_IncomingSasDialog_betweenText'>{_t("or")}</div>
<div className='mx_IncomingSasDialog_startOption'>
<p>{_t("Compare unique emoji")}</p>
<span className='mx_IncomingSasDialog_helpText'>{_t("Compare a unique set of emoji if you don't have a camera on either device")}</span>
<AccessibleButton onClick={this.props.onStartEmoji} kind='primary'>
{_t("Start")}
</AccessibleButton>
</div>
</div>
<AccessibleButton onClick={this.props.onCancel} kind='danger'>
{_t("Cancel")}
</AccessibleButton>
</div>
);
}
}

View file

@ -15,7 +15,8 @@ limitations under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import { SAS } from "matrix-js-sdk/src/crypto/verification/SAS";
import { DeviceInfo } from "matrix-js-sdk/src//crypto/deviceinfo";
import { _t, _td } from '../../../languageHandler';
import { PendingActionSpinner } from "../right_panel/EncryptionInfo";
import AccessibleButton from "../elements/AccessibleButton";
@ -23,24 +24,29 @@ import DialogButtons from "../elements/DialogButtons";
import { fixupColorFonts } from '../../../utils/FontManager';
import { replaceableComponent } from "../../../utils/replaceableComponent";
interface IProps {
pending?: boolean;
displayName?: string; // required if pending is true
device?: DeviceInfo;
onDone: () => void;
onCancel: () => void;
sas: SAS.sas;
isSelf?: boolean;
inDialog?: boolean; // whether this component is being shown in a dialog and to use DialogButtons
}
interface IState {
pending: boolean;
cancelling?: boolean;
}
function capFirst(s) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
@replaceableComponent("views.verification.VerificationShowSas")
export default class VerificationShowSas extends React.Component {
static propTypes = {
pending: PropTypes.bool,
displayName: PropTypes.string, // required if pending is true
device: PropTypes.object,
onDone: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
sas: PropTypes.object.isRequired,
isSelf: PropTypes.bool,
inDialog: PropTypes.bool, // whether this component is being shown in a dialog and to use DialogButtons
};
constructor(props) {
export default class VerificationShowSas extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
@ -48,19 +54,19 @@ export default class VerificationShowSas extends React.Component {
};
}
componentWillMount() {
public componentWillMount(): void {
// As this component is also used before login (during complete security),
// also make sure we have a working emoji font to display the SAS emojis here.
// This is also done from LoggedInView.
fixupColorFonts();
}
onMatchClick = () => {
private onMatchClick = (): void => {
this.setState({ pending: true });
this.props.onDone();
};
onDontMatchClick = () => {
private onDontMatchClick = (): void => {
this.setState({ cancelling: true });
this.props.onCancel();
};