Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/18092
This commit is contained in:
commit
db951b43a3
40 changed files with 1169 additions and 677 deletions
133
src/components/views/settings/LayoutSwitcher.tsx
Normal file
133
src/components/views/settings/LayoutSwitcher.tsx
Normal file
|
@ -0,0 +1,133 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2021 Šimon Brandner <simon.bra.ag@gmail.com>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import classNames from "classnames";
|
||||
import SettingsStore from "../../../settings/SettingsStore";
|
||||
import EventTilePreview from "../elements/EventTilePreview";
|
||||
import StyledRadioButton from "../elements/StyledRadioButton";
|
||||
import { _t } from "../../../languageHandler";
|
||||
import { Layout } from "../../../settings/Layout";
|
||||
import { SettingLevel } from "../../../settings/SettingLevel";
|
||||
|
||||
interface IProps {
|
||||
userId: string;
|
||||
displayName: string;
|
||||
avatarUrl: string;
|
||||
messagePreviewText: string;
|
||||
onLayoutChanged?: (layout: Layout) => void;
|
||||
}
|
||||
|
||||
interface IState {
|
||||
layout: Layout;
|
||||
}
|
||||
|
||||
export default class LayoutSwitcher extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
layout: SettingsStore.getValue("layout"),
|
||||
};
|
||||
}
|
||||
|
||||
private onLayoutChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
const layout = e.target.value as Layout;
|
||||
|
||||
this.setState({ layout: layout });
|
||||
SettingsStore.setValue("layout", null, SettingLevel.DEVICE, layout);
|
||||
this.props.onLayoutChanged(layout);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
const ircClasses = classNames("mx_LayoutSwitcher_RadioButton", {
|
||||
mx_LayoutSwitcher_RadioButton_selected: this.state.layout == Layout.IRC,
|
||||
});
|
||||
const groupClasses = classNames("mx_LayoutSwitcher_RadioButton", {
|
||||
mx_LayoutSwitcher_RadioButton_selected: this.state.layout == Layout.Group,
|
||||
});
|
||||
const bubbleClasses = classNames("mx_LayoutSwitcher_RadioButton", {
|
||||
mx_LayoutSwitcher_RadioButton_selected: this.state.layout === Layout.Bubble,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab_section mx_LayoutSwitcher">
|
||||
<span className="mx_SettingsTab_subheading">
|
||||
{ _t("Message layout") }
|
||||
</span>
|
||||
|
||||
<div className="mx_LayoutSwitcher_RadioButtons">
|
||||
<label className={ircClasses}>
|
||||
<EventTilePreview
|
||||
className="mx_LayoutSwitcher_RadioButton_preview"
|
||||
message={this.props.messagePreviewText}
|
||||
layout={Layout.IRC}
|
||||
userId={this.props.userId}
|
||||
displayName={this.props.displayName}
|
||||
avatarUrl={this.props.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value={Layout.IRC}
|
||||
checked={this.state.layout === Layout.IRC}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{ _t("IRC") }
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
<label className={groupClasses}>
|
||||
<EventTilePreview
|
||||
className="mx_LayoutSwitcher_RadioButton_preview"
|
||||
message={this.props.messagePreviewText}
|
||||
layout={Layout.Group}
|
||||
userId={this.props.userId}
|
||||
displayName={this.props.displayName}
|
||||
avatarUrl={this.props.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value={Layout.Group}
|
||||
checked={this.state.layout == Layout.Group}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{ _t("Modern") }
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
<label className={bubbleClasses}>
|
||||
<EventTilePreview
|
||||
className="mx_LayoutSwitcher_RadioButton_preview"
|
||||
message={this.props.messagePreviewText}
|
||||
layout={Layout.Bubble}
|
||||
userId={this.props.userId}
|
||||
displayName={this.props.displayName}
|
||||
avatarUrl={this.props.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value={Layout.Bubble}
|
||||
checked={this.state.layout == Layout.Bubble}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{ _t("Message bubbles") }
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
|
||||
Copyright 2019 - 2021 The Matrix.org Foundation C.I.C.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
@ -37,10 +37,9 @@ import StyledRadioGroup from "../../../elements/StyledRadioGroup";
|
|||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
import { UIFeature } from "../../../../../settings/UIFeature";
|
||||
import { Layout } from "../../../../../settings/Layout";
|
||||
import classNames from 'classnames';
|
||||
import StyledRadioButton from '../../../elements/StyledRadioButton';
|
||||
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||
import { compare } from "../../../../../utils/strings";
|
||||
import LayoutSwitcher from "../../LayoutSwitcher";
|
||||
|
||||
interface IProps {
|
||||
}
|
||||
|
@ -243,17 +242,8 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
this.setState({ customThemeUrl: e.target.value });
|
||||
};
|
||||
|
||||
private onLayoutChange = (e: React.ChangeEvent<HTMLInputElement>): void => {
|
||||
let layout;
|
||||
switch (e.target.value) {
|
||||
case "irc": layout = Layout.IRC; break;
|
||||
case "group": layout = Layout.Group; break;
|
||||
case "bubble": layout = Layout.Bubble; break;
|
||||
}
|
||||
|
||||
private onLayoutChanged = (layout: Layout): void => {
|
||||
this.setState({ layout: layout });
|
||||
|
||||
SettingsStore.setValue("layout", null, SettingLevel.DEVICE, layout);
|
||||
};
|
||||
|
||||
private onIRCLayoutChange = (enabled: boolean) => {
|
||||
|
@ -391,75 +381,6 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
</div>;
|
||||
}
|
||||
|
||||
private renderLayoutSection = () => {
|
||||
return <div className="mx_SettingsTab_section mx_AppearanceUserSettingsTab_Layout">
|
||||
<span className="mx_SettingsTab_subheading">{ _t("Message layout") }</span>
|
||||
|
||||
<div className="mx_AppearanceUserSettingsTab_Layout_RadioButtons">
|
||||
<label className={classNames("mx_AppearanceUserSettingsTab_Layout_RadioButton", {
|
||||
mx_AppearanceUserSettingsTab_Layout_RadioButton_selected: this.state.layout == Layout.IRC,
|
||||
})}>
|
||||
<EventTilePreview
|
||||
className="mx_AppearanceUserSettingsTab_Layout_RadioButton_preview"
|
||||
message={this.MESSAGE_PREVIEW_TEXT}
|
||||
layout={Layout.IRC}
|
||||
userId={this.state.userId}
|
||||
displayName={this.state.displayName}
|
||||
avatarUrl={this.state.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value="irc"
|
||||
checked={this.state.layout === Layout.IRC}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{ _t("IRC") }
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
<label className={classNames("mx_AppearanceUserSettingsTab_Layout_RadioButton", {
|
||||
mx_AppearanceUserSettingsTab_Layout_RadioButton_selected: this.state.layout == Layout.Group,
|
||||
})}>
|
||||
<EventTilePreview
|
||||
className="mx_AppearanceUserSettingsTab_Layout_RadioButton_preview"
|
||||
message={this.MESSAGE_PREVIEW_TEXT}
|
||||
layout={Layout.Group}
|
||||
userId={this.state.userId}
|
||||
displayName={this.state.displayName}
|
||||
avatarUrl={this.state.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value="group"
|
||||
checked={this.state.layout == Layout.Group}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{ _t("Modern") }
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
<label className={classNames("mx_AppearanceUserSettingsTab_Layout_RadioButton", {
|
||||
mx_AppearanceUserSettingsTab_Layout_RadioButton_selected: this.state.layout === Layout.Bubble,
|
||||
})}>
|
||||
<EventTilePreview
|
||||
className="mx_AppearanceUserSettingsTab_Layout_RadioButton_preview"
|
||||
message={this.MESSAGE_PREVIEW_TEXT}
|
||||
layout={Layout.Bubble}
|
||||
userId={this.state.userId}
|
||||
displayName={this.state.displayName}
|
||||
avatarUrl={this.state.avatarUrl}
|
||||
/>
|
||||
<StyledRadioButton
|
||||
name="layout"
|
||||
value="bubble"
|
||||
checked={this.state.layout == Layout.Bubble}
|
||||
onChange={this.onLayoutChange}
|
||||
>
|
||||
{ _t("Message bubbles") }
|
||||
</StyledRadioButton>
|
||||
</label>
|
||||
</div>
|
||||
</div>;
|
||||
};
|
||||
|
||||
private renderAdvancedSection() {
|
||||
if (!SettingsStore.getValue(UIFeature.AdvancedSettings)) return null;
|
||||
|
||||
|
@ -527,6 +448,19 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
render() {
|
||||
const brand = SdkConfig.get().brand;
|
||||
|
||||
let layoutSection;
|
||||
if (SettingsStore.getValue("feature_new_layout_switcher")) {
|
||||
layoutSection = (
|
||||
<LayoutSwitcher
|
||||
userId={this.state.userId}
|
||||
displayName={this.state.displayName}
|
||||
avatarUrl={this.state.avatarUrl}
|
||||
messagePreviewText={this.MESSAGE_PREVIEW_TEXT}
|
||||
onLayoutChanged={this.onLayoutChanged}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab mx_AppearanceUserSettingsTab">
|
||||
<div className="mx_SettingsTab_heading">{ _t("Customise your appearance") }</div>
|
||||
|
@ -534,7 +468,7 @@ export default class AppearanceUserSettingsTab extends React.Component<IProps, I
|
|||
{ _t("Appearance Settings only affect this %(brand)s session.", { brand }) }
|
||||
</div>
|
||||
{ this.renderThemeSection() }
|
||||
{ SettingsStore.getValue("feature_new_layout_switcher") ? this.renderLayoutSection() : null }
|
||||
{ layoutSection }
|
||||
{ this.renderFontSection() }
|
||||
{ this.renderAdvancedSection() }
|
||||
</div>
|
||||
|
|
|
@ -15,9 +15,9 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import React from 'react';
|
||||
import AccessibleButton, { ButtonEvent } from "../../../elements/AccessibleButton";
|
||||
import { _t, getCurrentLanguage } from "../../../../../languageHandler";
|
||||
import { MatrixClientPeg } from "../../../../../MatrixClientPeg";
|
||||
import AccessibleButton from "../../../elements/AccessibleButton";
|
||||
import AccessibleTooltipButton from '../../../elements/AccessibleTooltipButton';
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
import createRoom from "../../../../../createRoom";
|
||||
|
@ -69,6 +69,18 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
if (this.closeCopiedTooltip) this.closeCopiedTooltip();
|
||||
}
|
||||
|
||||
private getVersionInfo(): { appVersion: string, olmVersion: string } {
|
||||
const brand = SdkConfig.get().brand;
|
||||
const appVersion = this.state.appVersion || 'unknown';
|
||||
let olmVersion = MatrixClientPeg.get().olmVersion;
|
||||
olmVersion = olmVersion ? `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}` : '<not-enabled>';
|
||||
|
||||
return {
|
||||
appVersion: `${_t("%(brand)s version:", { brand })} ${appVersion}`,
|
||||
olmVersion: `${_t("Olm version:")} ${olmVersion}`,
|
||||
};
|
||||
}
|
||||
|
||||
private onClearCacheAndReload = (e) => {
|
||||
if (!PlatformPeg.get()) return;
|
||||
|
||||
|
@ -173,17 +185,26 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
|
||||
onAccessTokenCopyClick = async (e) => {
|
||||
private async copy(text: string, e: ButtonEvent) {
|
||||
e.preventDefault();
|
||||
const target = e.target; // copy target before we go async and React throws it away
|
||||
const target = e.target as HTMLDivElement; // copy target before we go async and React throws it away
|
||||
|
||||
const successful = await copyPlaintext(MatrixClientPeg.get().getAccessToken());
|
||||
const successful = await copyPlaintext(text);
|
||||
const buttonRect = target.getBoundingClientRect();
|
||||
const { close } = ContextMenu.createMenu(GenericTextContextMenu, {
|
||||
...toRightOf(buttonRect, 2),
|
||||
message: successful ? _t('Copied!') : _t('Failed to copy'),
|
||||
});
|
||||
this.closeCopiedTooltip = target.onmouseleave = close;
|
||||
}
|
||||
|
||||
private onAccessTokenCopyClick = (e: ButtonEvent) => {
|
||||
this.copy(MatrixClientPeg.get().getAccessToken(), e);
|
||||
};
|
||||
|
||||
private onCopyVersionClicked = (e: ButtonEvent) => {
|
||||
const { appVersion, olmVersion } = this.getVersionInfo();
|
||||
this.copy(`${appVersion}\n${olmVersion}`, e);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
@ -232,11 +253,6 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
|
||||
const appVersion = this.state.appVersion || 'unknown';
|
||||
|
||||
let olmVersion = MatrixClientPeg.get().olmVersion;
|
||||
olmVersion = olmVersion ? `${olmVersion[0]}.${olmVersion[1]}.${olmVersion[2]}` : '<not-enabled>';
|
||||
|
||||
let updateButton = null;
|
||||
if (this.state.canUpdate) {
|
||||
updateButton = <UpdateCheckButton />;
|
||||
|
@ -275,6 +291,8 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
);
|
||||
}
|
||||
|
||||
const { appVersion, olmVersion } = this.getVersionInfo();
|
||||
|
||||
return (
|
||||
<div className="mx_SettingsTab mx_HelpUserSettingsTab">
|
||||
<div className="mx_SettingsTab_heading">{ _t("Help & About") }</div>
|
||||
|
@ -291,8 +309,15 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
<div className='mx_SettingsTab_section mx_HelpUserSettingsTab_versions'>
|
||||
<span className='mx_SettingsTab_subheading'>{ _t("Versions") }</span>
|
||||
<div className='mx_SettingsTab_subsectionText'>
|
||||
{ _t("%(brand)s version:", { brand }) } { appVersion }<br />
|
||||
{ _t("olm version:") } { olmVersion }<br />
|
||||
<div className="mx_HelpUserSettingsTab_copy">
|
||||
{ appVersion }<br />
|
||||
{ olmVersion }<br />
|
||||
<AccessibleTooltipButton
|
||||
title={_t("Copy")}
|
||||
onClick={this.onCopyVersionClicked}
|
||||
className="mx_HelpUserSettingsTab_copyButton"
|
||||
/>
|
||||
</div>
|
||||
{ updateButton }
|
||||
</div>
|
||||
</div>
|
||||
|
@ -308,12 +333,12 @@ export default class HelpUserSettingsTab extends React.Component<IProps, IState>
|
|||
<summary>{ _t("Access Token") }</summary><br />
|
||||
<b>{ _t("Your access token gives full access to your account."
|
||||
+ " Do not share it with anyone." ) }</b>
|
||||
<div className="mx_HelpUserSettingsTab_accessToken">
|
||||
<div className="mx_HelpUserSettingsTab_copy">
|
||||
<code>{ MatrixClientPeg.get().getAccessToken() }</code>
|
||||
<AccessibleTooltipButton
|
||||
title={_t("Copy")}
|
||||
onClick={this.onAccessTokenCopyClick}
|
||||
className="mx_HelpUserSettingsTab_accessToken_copy"
|
||||
className="mx_HelpUserSettingsTab_copyButton"
|
||||
/>
|
||||
</div>
|
||||
</details><br />
|
||||
|
|
|
@ -19,11 +19,12 @@ import { _t } from "../../../../../languageHandler";
|
|||
import PropTypes from "prop-types";
|
||||
import SettingsStore from "../../../../../settings/SettingsStore";
|
||||
import LabelledToggleSwitch from "../../../elements/LabelledToggleSwitch";
|
||||
import * as sdk from "../../../../../index";
|
||||
import { SettingLevel } from "../../../../../settings/SettingLevel";
|
||||
import { replaceableComponent } from "../../../../../utils/replaceableComponent";
|
||||
import SdkConfig from "../../../../../SdkConfig";
|
||||
import BetaCard from "../../../beta/BetaCard";
|
||||
import SettingsFlag from '../../../elements/SettingsFlag';
|
||||
import { MatrixClientPeg } from '../../../../../MatrixClientPeg';
|
||||
|
||||
export class LabsSettingToggle extends React.Component {
|
||||
static propTypes = {
|
||||
|
@ -47,6 +48,14 @@ export class LabsSettingToggle extends React.Component {
|
|||
export default class LabsUserSettingsTab extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
MatrixClientPeg.get().doesServerSupportUnstableFeature("org.matrix.msc2285").then((showHiddenReadReceipts) => {
|
||||
this.setState({ showHiddenReadReceipts });
|
||||
});
|
||||
|
||||
this.state = {
|
||||
showHiddenReadReceipts: false,
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@ -65,15 +74,22 @@ export default class LabsUserSettingsTab extends React.Component {
|
|||
|
||||
let labsSection;
|
||||
if (SdkConfig.get()['showLabsSettings']) {
|
||||
const SettingsFlag = sdk.getComponent("views.elements.SettingsFlag");
|
||||
const flags = labs.map(f => <LabsSettingToggle featureId={f} key={f} />);
|
||||
|
||||
let hiddenReadReceipts;
|
||||
if (this.state.showHiddenReadReceipts) {
|
||||
hiddenReadReceipts = (
|
||||
<SettingsFlag name="feature_hidden_read_receipts" level={SettingLevel.ACCOUNT} />
|
||||
);
|
||||
}
|
||||
|
||||
labsSection = <div className="mx_SettingsTab_section">
|
||||
{ flags }
|
||||
<SettingsFlag name="enableWidgetScreenshots" level={SettingLevel.ACCOUNT} />
|
||||
<SettingsFlag name="showHiddenEventsInTimeline" level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name="lowBandwidth" level={SettingLevel.DEVICE} />
|
||||
<SettingsFlag name="advancedRoomListLogging" level={SettingLevel.DEVICE} />
|
||||
{ hiddenReadReceipts }
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue