Merge branch 'develop' of github.com:matrix-org/matrix-react-sdk into t3chguy/fix/15174
Conflicts: src/settings/Settings.ts src/settings/UIFeature.ts
This commit is contained in:
commit
a784e29622
11 changed files with 79 additions and 46 deletions
|
@ -44,6 +44,8 @@ import { ViewUserPayload } from "./dispatcher/payloads/ViewUserPayload";
|
||||||
import { Action } from "./dispatcher/actions";
|
import { Action } from "./dispatcher/actions";
|
||||||
import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from "./utils/membership";
|
import { EffectiveMembership, getEffectiveMembership, leaveRoomBehaviour } from "./utils/membership";
|
||||||
import SdkConfig from "./SdkConfig";
|
import SdkConfig from "./SdkConfig";
|
||||||
|
import SettingsStore from "./settings/SettingsStore";
|
||||||
|
import {UIFeature} from "./settings/UIFeature";
|
||||||
|
|
||||||
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
|
// XXX: workaround for https://github.com/microsoft/TypeScript/issues/31816
|
||||||
interface HTMLInputEvent extends Event {
|
interface HTMLInputEvent extends Event {
|
||||||
|
@ -797,6 +799,7 @@ export const Commands = [
|
||||||
command: 'addwidget',
|
command: 'addwidget',
|
||||||
args: '<url | embed code | Jitsi url>',
|
args: '<url | embed code | Jitsi url>',
|
||||||
description: _td('Adds a custom widget by URL to the room'),
|
description: _td('Adds a custom widget by URL to the room'),
|
||||||
|
isEnabled: () => SettingsStore.getValue(UIFeature.Widgets),
|
||||||
runFn: function(roomId, widgetUrl) {
|
runFn: function(roomId, widgetUrl) {
|
||||||
if (!widgetUrl) {
|
if (!widgetUrl) {
|
||||||
return reject(_t("Please supply a widget URL or embed code"));
|
return reject(_t("Please supply a widget URL or embed code"));
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { _t } from '../../../languageHandler';
|
||||||
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
import {MatrixClientPeg} from '../../../MatrixClientPeg';
|
||||||
import PlatformPeg from '../../../PlatformPeg';
|
import PlatformPeg from '../../../PlatformPeg';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
import SdkConfig from "../../../SdkConfig";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This error boundary component can be used to wrap large content areas and
|
* This error boundary component can be used to wrap large content areas and
|
||||||
|
@ -73,9 +74,10 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||||
if (this.state.error) {
|
if (this.state.error) {
|
||||||
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
|
||||||
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
|
const newIssueUrl = "https://github.com/vector-im/element-web/issues/new";
|
||||||
return <div className="mx_ErrorBoundary">
|
|
||||||
<div className="mx_ErrorBoundary_body">
|
let bugReportSection;
|
||||||
<h1>{_t("Something went wrong!")}</h1>
|
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||||
|
bugReportSection = <React.Fragment>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
"Please <newIssueLink>create a new issue</newIssueLink> " +
|
"Please <newIssueLink>create a new issue</newIssueLink> " +
|
||||||
"on GitHub so that we can investigate this bug.", {}, {
|
"on GitHub so that we can investigate this bug.", {}, {
|
||||||
|
@ -94,6 +96,13 @@ export default class ErrorBoundary extends React.PureComponent {
|
||||||
<AccessibleButton onClick={this._onBugReport} kind='primary'>
|
<AccessibleButton onClick={this._onBugReport} kind='primary'>
|
||||||
{_t("Submit debug logs")}
|
{_t("Submit debug logs")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
</React.Fragment>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="mx_ErrorBoundary">
|
||||||
|
<div className="mx_ErrorBoundary_body">
|
||||||
|
<h1>{_t("Something went wrong!")}</h1>
|
||||||
|
{ bugReportSection }
|
||||||
<AccessibleButton onClick={this._onClearCacheAndReload} kind='danger'>
|
<AccessibleButton onClick={this._onClearCacheAndReload} kind='danger'>
|
||||||
{_t("Clear cache and reload")}
|
{_t("Clear cache and reload")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import classNames from 'classnames';
|
||||||
import { _t } from '../../../languageHandler';
|
import { _t } from '../../../languageHandler';
|
||||||
import * as sdk from '../../../index';
|
import * as sdk from '../../../index';
|
||||||
import Modal from '../../../Modal';
|
import Modal from '../../../Modal';
|
||||||
|
import SdkConfig from "../../../SdkConfig";
|
||||||
|
|
||||||
export default class TileErrorBoundary extends React.Component {
|
export default class TileErrorBoundary extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -54,14 +55,20 @@ export default class TileErrorBoundary extends React.Component {
|
||||||
mx_EventTile_content: true,
|
mx_EventTile_content: true,
|
||||||
mx_EventTile_tileError: true,
|
mx_EventTile_tileError: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let submitLogsButton;
|
||||||
|
if (SdkConfig.get().bug_report_endpoint_url) {
|
||||||
|
submitLogsButton = <a onClick={this._onBugReport} href="#">
|
||||||
|
{_t("Submit logs")}
|
||||||
|
</a>;
|
||||||
|
}
|
||||||
|
|
||||||
return (<div className={classNames(classes)}>
|
return (<div className={classNames(classes)}>
|
||||||
<div className="mx_EventTile_line">
|
<div className="mx_EventTile_line">
|
||||||
<span>
|
<span>
|
||||||
{_t("Can't load this message")}
|
{_t("Can't load this message")}
|
||||||
{ mxEvent && ` (${mxEvent.getType()})` }
|
{ mxEvent && ` (${mxEvent.getType()})` }
|
||||||
<a onClick={this._onBugReport} href="#">
|
{ submitLogsButton }
|
||||||
{_t("Submit logs")}
|
|
||||||
</a>
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</div>);
|
||||||
|
|
|
@ -42,6 +42,7 @@ import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import WidgetStore, {IApp} from "../../../stores/WidgetStore";
|
import WidgetStore, {IApp} from "../../../stores/WidgetStore";
|
||||||
import { E2EStatus } from "../../../utils/ShieldUtils";
|
import { E2EStatus } from "../../../utils/ShieldUtils";
|
||||||
import RoomContext from "../../../contexts/RoomContext";
|
import RoomContext from "../../../contexts/RoomContext";
|
||||||
|
import {UIFeature} from "../../../settings/UIFeature";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -242,7 +243,7 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<AppsSection room={room} />
|
{ SettingsStore.getValue(UIFeature.Widgets) && <AppsSection room={room} /> }
|
||||||
</BaseCard>;
|
</BaseCard>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -952,30 +952,26 @@ function useRoomPermissions(cli, room, user) {
|
||||||
|
|
||||||
const PowerLevelSection = ({user, room, roomPermissions, powerLevels}) => {
|
const PowerLevelSection = ({user, room, roomPermissions, powerLevels}) => {
|
||||||
const [isEditing, setEditing] = useState(false);
|
const [isEditing, setEditing] = useState(false);
|
||||||
if (room && user.roomId) { // is in room
|
if (isEditing) {
|
||||||
if (isEditing) {
|
return (<PowerLevelEditor
|
||||||
return (<PowerLevelEditor
|
user={user} room={room} roomPermissions={roomPermissions}
|
||||||
user={user} room={room} roomPermissions={roomPermissions}
|
onFinished={() => setEditing(false)} />);
|
||||||
onFinished={() => setEditing(false)} />);
|
|
||||||
} else {
|
|
||||||
const IconButton = sdk.getComponent('elements.IconButton');
|
|
||||||
const powerLevelUsersDefault = powerLevels.users_default || 0;
|
|
||||||
const powerLevel = parseInt(user.powerLevel, 10);
|
|
||||||
const modifyButton = roomPermissions.canEdit ?
|
|
||||||
(<IconButton icon="edit" onClick={() => setEditing(true)} />) : null;
|
|
||||||
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
|
|
||||||
const label = _t("<strong>%(role)s</strong> in %(roomName)s",
|
|
||||||
{role, roomName: room.name},
|
|
||||||
{strong: label => <strong>{label}</strong>},
|
|
||||||
);
|
|
||||||
return (
|
|
||||||
<div className="mx_UserInfo_profileField">
|
|
||||||
<div className="mx_UserInfo_roleDescription">{label}{modifyButton}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
const IconButton = sdk.getComponent('elements.IconButton');
|
||||||
|
const powerLevelUsersDefault = powerLevels.users_default || 0;
|
||||||
|
const powerLevel = parseInt(user.powerLevel, 10);
|
||||||
|
const modifyButton = roomPermissions.canEdit ?
|
||||||
|
(<IconButton icon="edit" onClick={() => setEditing(true)} />) : null;
|
||||||
|
const role = textualPowerLevel(powerLevel, powerLevelUsersDefault);
|
||||||
|
const label = _t("<strong>%(role)s</strong> in %(roomName)s",
|
||||||
|
{role, roomName: room.name},
|
||||||
|
{strong: label => <strong>{label}</strong>},
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<div className="mx_UserInfo_profileField">
|
||||||
|
<div className="mx_UserInfo_roleDescription">{label}{modifyButton}</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1268,14 +1264,15 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
|
||||||
spinner = <Loader imgClassName="mx_ContextualMenu_spinner" />;
|
spinner = <Loader imgClassName="mx_ContextualMenu_spinner" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const memberDetails = (
|
let memberDetails;
|
||||||
<PowerLevelSection
|
if (room && member.roomId) {
|
||||||
|
memberDetails = <PowerLevelSection
|
||||||
powerLevels={powerLevels}
|
powerLevels={powerLevels}
|
||||||
user={member}
|
user={member}
|
||||||
room={room}
|
room={room}
|
||||||
roomPermissions={roomPermissions}
|
roomPermissions={roomPermissions}
|
||||||
/>
|
/>;
|
||||||
);
|
}
|
||||||
|
|
||||||
// only display the devices list if our client supports E2E
|
// only display the devices list if our client supports E2E
|
||||||
const cryptoEnabled = cli.isCryptoEnabled();
|
const cryptoEnabled = cli.isCryptoEnabled();
|
||||||
|
|
|
@ -28,6 +28,7 @@ import RateLimitedFunc from '../../../ratelimitedfunc';
|
||||||
import SettingsStore from "../../../settings/SettingsStore";
|
import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
import AutoHideScrollbar from "../../structures/AutoHideScrollbar";
|
||||||
import CallView from "../voip/CallView";
|
import CallView from "../voip/CallView";
|
||||||
|
import {UIFeature} from "../../../settings/UIFeature";
|
||||||
|
|
||||||
|
|
||||||
export default class AuxPanel extends React.Component {
|
export default class AuxPanel extends React.Component {
|
||||||
|
@ -198,18 +199,21 @@ export default class AuxPanel extends React.Component {
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const appsDrawer = <AppsDrawer
|
let appsDrawer;
|
||||||
room={this.props.room}
|
if (SettingsStore.getValue(UIFeature.Widgets)) {
|
||||||
userId={this.props.userId}
|
appsDrawer = <AppsDrawer
|
||||||
maxHeight={this.props.maxHeight}
|
room={this.props.room}
|
||||||
showApps={this.props.showApps}
|
userId={this.props.userId}
|
||||||
hide={this.props.hideAppsDrawer}
|
maxHeight={this.props.maxHeight}
|
||||||
resizeNotifier={this.props.resizeNotifier}
|
showApps={this.props.showApps}
|
||||||
/>;
|
hide={this.props.hideAppsDrawer}
|
||||||
|
resizeNotifier={this.props.resizeNotifier}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
|
||||||
let stateViews = null;
|
let stateViews = null;
|
||||||
if (this.state.counters && SettingsStore.getValue("feature_state_counters")) {
|
if (this.state.counters && SettingsStore.getValue("feature_state_counters")) {
|
||||||
let counters = [];
|
const counters = [];
|
||||||
|
|
||||||
this.state.counters.forEach((counter, idx) => {
|
this.state.counters.forEach((counter, idx) => {
|
||||||
const title = counter.title;
|
const title = counter.title;
|
||||||
|
@ -218,7 +222,7 @@ export default class AuxPanel extends React.Component {
|
||||||
const severity = counter.severity;
|
const severity = counter.severity;
|
||||||
const stateKey = counter.stateKey;
|
const stateKey = counter.stateKey;
|
||||||
|
|
||||||
let span = <span>{ title }: { value }</span>
|
let span = <span>{ title }: { value }</span>;
|
||||||
|
|
||||||
if (link) {
|
if (link) {
|
||||||
span = (
|
span = (
|
||||||
|
|
|
@ -31,6 +31,7 @@ import SettingsStore from "../../../settings/SettingsStore";
|
||||||
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu";
|
import {aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu} from "../../structures/ContextMenu";
|
||||||
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
import AccessibleTooltipButton from "../elements/AccessibleTooltipButton";
|
||||||
import ReplyPreview from "./ReplyPreview";
|
import ReplyPreview from "./ReplyPreview";
|
||||||
|
import {UIFeature} from "../../../settings/UIFeature";
|
||||||
|
|
||||||
function ComposerAvatar(props) {
|
function ComposerAvatar(props) {
|
||||||
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
const MemberStatusMessageAvatar = sdk.getComponent('avatars.MemberStatusMessageAvatar');
|
||||||
|
@ -384,9 +385,12 @@ export default class MessageComposer extends React.Component {
|
||||||
permalinkCreator={this.props.permalinkCreator} />,
|
permalinkCreator={this.props.permalinkCreator} />,
|
||||||
<UploadButton key="controls_upload" roomId={this.props.room.roomId} />,
|
<UploadButton key="controls_upload" roomId={this.props.room.roomId} />,
|
||||||
<EmojiButton key="emoji_button" addEmoji={this.addEmoji} />,
|
<EmojiButton key="emoji_button" addEmoji={this.addEmoji} />,
|
||||||
<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (SettingsStore.getValue(UIFeature.Widgets)) {
|
||||||
|
controls.push(<Stickerpicker key="stickerpicker_controls_button" room={this.props.room} />);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.state.showCallButtons) {
|
if (this.state.showCallButtons) {
|
||||||
if (callInProgress) {
|
if (callInProgress) {
|
||||||
controls.push(
|
controls.push(
|
||||||
|
|
|
@ -37,6 +37,7 @@ import {abbreviateUrl} from "../../../../../utils/UrlUtils";
|
||||||
import { getThreepidsWithBindStatus } from '../../../../../boundThreepids';
|
import { getThreepidsWithBindStatus } from '../../../../../boundThreepids';
|
||||||
import Spinner from "../../../elements/Spinner";
|
import Spinner from "../../../elements/Spinner";
|
||||||
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
import {SettingLevel} from "../../../../../settings/SettingLevel";
|
||||||
|
import {UIFeature} from "../../../../../settings/UIFeature";
|
||||||
|
|
||||||
export default class GeneralUserSettingsTab extends React.Component {
|
export default class GeneralUserSettingsTab extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
|
@ -366,6 +367,8 @@ export default class GeneralUserSettingsTab extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderIntegrationManagerSection() {
|
_renderIntegrationManagerSection() {
|
||||||
|
if (!SettingsStore.getValue(UIFeature.Widgets)) return null;
|
||||||
|
|
||||||
const SetIntegrationManager = sdk.getComponent("views.settings.SetIntegrationManager");
|
const SetIntegrationManager = sdk.getComponent("views.settings.SetIntegrationManager");
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1440,8 +1440,8 @@
|
||||||
"Click to view edits": "Click to view edits",
|
"Click to view edits": "Click to view edits",
|
||||||
"Edited at %(date)s. Click to view edits.": "Edited at %(date)s. Click to view edits.",
|
"Edited at %(date)s. Click to view edits.": "Edited at %(date)s. Click to view edits.",
|
||||||
"edited": "edited",
|
"edited": "edited",
|
||||||
"Can't load this message": "Can't load this message",
|
|
||||||
"Submit logs": "Submit logs",
|
"Submit logs": "Submit logs",
|
||||||
|
"Can't load this message": "Can't load this message",
|
||||||
"Failed to load group members": "Failed to load group members",
|
"Failed to load group members": "Failed to load group members",
|
||||||
"Filter community members": "Filter community members",
|
"Filter community members": "Filter community members",
|
||||||
"Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Are you sure you want to remove '%(roomName)s' from %(groupId)s?",
|
"Are you sure you want to remove '%(roomName)s' from %(groupId)s?": "Are you sure you want to remove '%(roomName)s' from %(groupId)s?",
|
||||||
|
|
|
@ -618,6 +618,10 @@ export const SETTINGS: {[setting: string]: ISetting} = {
|
||||||
supportedLevels: LEVELS_UI_FEATURE,
|
supportedLevels: LEVELS_UI_FEATURE,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
[UIFeature.Widgets]: {
|
||||||
|
supportedLevels: LEVELS_UI_FEATURE,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
[UIFeature.Feedback]: {
|
[UIFeature.Feedback]: {
|
||||||
supportedLevels: LEVELS_UI_FEATURE,
|
supportedLevels: LEVELS_UI_FEATURE,
|
||||||
default: true,
|
default: true,
|
||||||
|
|
|
@ -17,5 +17,6 @@ limitations under the License.
|
||||||
// see settings.md for documentation on conventions
|
// see settings.md for documentation on conventions
|
||||||
export enum UIFeature {
|
export enum UIFeature {
|
||||||
URLPreviews = "UIFeature.urlPreviews",
|
URLPreviews = "UIFeature.urlPreviews",
|
||||||
|
Widgets = "UIFeature.widgets",
|
||||||
Feedback = "UIFeature.feedback",
|
Feedback = "UIFeature.feedback",
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue