Merge pull request #8521 from matrix-org/travis/remove-unused-labs-1

Remove some labs features which don't get used or create maintenance burden: custom status, multiple integration managers, and do not disturb
This commit is contained in:
Travis Ralston 2022-05-06 13:21:07 -06:00 committed by GitHub
commit d39d332f54
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 17 additions and 624 deletions

View file

@ -14,9 +14,8 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import React, { createRef, useContext, useRef, useState } from "react";
import React, { createRef } from "react";
import { Room } from "matrix-js-sdk/src/models/room";
import classNames from "classnames";
import { MatrixClientPeg } from "../../MatrixClientPeg";
import defaultDispatcher from "../../dispatcher/dispatcher";
@ -32,9 +31,7 @@ import LogoutDialog from "../views/dialogs/LogoutDialog";
import SettingsStore from "../../settings/SettingsStore";
import { findHighContrastTheme, getCustomTheme, isHighContrastTheme } from "../../theme";
import {
RovingAccessibleButton,
RovingAccessibleTooltipButton,
useRovingTabIndex,
} from "../../accessibility/RovingTabIndex";
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
import SdkConfig from "../../SdkConfig";
@ -44,7 +41,6 @@ import { UPDATE_EVENT } from "../../stores/AsyncStore";
import BaseAvatar from '../views/avatars/BaseAvatar';
import { SettingLevel } from "../../settings/SettingLevel";
import IconizedContextMenu, {
IconizedContextMenuCheckbox,
IconizedContextMenuOption,
IconizedContextMenuOptionList,
} from "../views/context_menus/IconizedContextMenu";
@ -52,65 +48,10 @@ import { UIFeature } from "../../settings/UIFeature";
import HostSignupAction from "./HostSignupAction";
import SpaceStore from "../../stores/spaces/SpaceStore";
import { UPDATE_SELECTED_SPACE } from "../../stores/spaces";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload";
import UserIdentifierCustomisations from "../../customisations/UserIdentifier";
import PosthogTrackers from "../../PosthogTrackers";
import { ViewHomePagePayload } from "../../dispatcher/payloads/ViewHomePagePayload";
const CustomStatusSection = () => {
const cli = useContext(MatrixClientContext);
const setStatus = cli.getUser(cli.getUserId()).unstable_statusMessage || "";
const [value, setValue] = useState(setStatus);
const ref = useRef<HTMLInputElement>(null);
const [onFocus, isActive] = useRovingTabIndex(ref);
const classes = classNames({
'mx_UserMenu_CustomStatusSection_field': true,
'mx_UserMenu_CustomStatusSection_field_hasQuery': value,
});
let details: JSX.Element;
if (value !== setStatus) {
details = <>
<p>{ _t("Your status will be shown to people you have a DM with.") }</p>
<RovingAccessibleButton
onClick={() => cli._unstable_setStatusMessage(value)}
kind="primary_outline"
>
{ value ? _t("Set status") : _t("Clear status") }
</RovingAccessibleButton>
</>;
}
return <form className="mx_UserMenu_CustomStatusSection">
<div className={classes}>
<input
type="text"
value={value}
className="mx_UserMenu_CustomStatusSection_input"
onChange={e => setValue(e.target.value)}
placeholder={_t("Set a new status")}
autoComplete="off"
onFocus={onFocus}
ref={ref}
tabIndex={isActive ? 0 : -1}
/>
<AccessibleButton
// The clear button is only for mouse users
tabIndex={-1}
title={_t("Clear")}
className="mx_UserMenu_CustomStatusSection_clear"
onClick={() => setValue("")}
/>
</div>
{ details }
</form>;
};
interface IProps {
isPanelCollapsed: boolean;
}
@ -122,7 +63,6 @@ interface IState {
isDarkTheme: boolean;
isHighContrast: boolean;
selectedSpace?: Room;
dndEnabled: boolean;
}
const toRightOf = (rect: PartialDOMRect) => {
@ -154,19 +94,11 @@ export default class UserMenu extends React.Component<IProps, IState> {
contextMenuPosition: null,
isDarkTheme: this.isUserOnDarkTheme(),
isHighContrast: this.isUserOnHighContrastTheme(),
dndEnabled: this.doNotDisturb,
selectedSpace: SpaceStore.instance.activeSpaceRoom,
};
OwnProfileStore.instance.on(UPDATE_EVENT, this.onProfileUpdate);
SpaceStore.instance.on(UPDATE_SELECTED_SPACE, this.onSelectedSpaceUpdate);
SettingsStore.monitorSetting("feature_dnd", null);
SettingsStore.monitorSetting("doNotDisturb", null);
}
private get doNotDisturb(): boolean {
return SettingsStore.getValue("doNotDisturb");
}
private get hasHomePage(): boolean {
@ -239,20 +171,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
if (this.buttonRef.current) this.buttonRef.current.click();
}
break;
case Action.SettingUpdated: {
const settingUpdatedPayload = payload as SettingUpdatedPayload;
switch (settingUpdatedPayload.settingName) {
case "feature_dnd":
case "doNotDisturb": {
const dndEnabled = this.doNotDisturb;
if (this.state.dndEnabled !== dndEnabled) {
this.setState({ dndEnabled });
}
break;
}
}
}
}
};
@ -348,12 +266,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
this.setState({ contextMenuPosition: null }); // also close the menu
};
private onDndToggle = (ev: ButtonEvent) => {
ev.stopPropagation();
const current = SettingsStore.getValue("doNotDisturb");
SettingsStore.setValue("doNotDisturb", null, SettingLevel.DEVICE, !current);
};
private renderContextMenu = (): React.ReactNode => {
if (!this.state.contextMenuPosition) return null;
@ -400,24 +312,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
);
}
let customStatusSection: JSX.Element;
if (SettingsStore.getValue("feature_custom_status")) {
customStatusSection = <CustomStatusSection />;
}
let dndButton: JSX.Element;
if (SettingsStore.getValue("feature_dnd")) {
dndButton = (
<IconizedContextMenuCheckbox
iconClassName={this.state.dndEnabled ? "mx_UserMenu_iconDnd" : "mx_UserMenu_iconDndOff"}
label={_t("Do not disturb")}
onClick={this.onDndToggle}
active={this.state.dndEnabled}
words
/>
);
}
let feedbackButton;
if (SettingsStore.getValue(UIFeature.Feedback)) {
feedbackButton = <IconizedContextMenuOption
@ -430,7 +324,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
let primaryOptionList = (
<IconizedContextMenuOptionList>
{ homeButton }
{ dndButton }
<IconizedContextMenuOption
iconClassName="mx_UserMenu_iconBell"
label={_t("Notifications")}
@ -502,7 +395,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
/>
</RovingAccessibleTooltipButton>
</div>
{ customStatusSection }
{ topSection }
{ primaryOptionList }
</IconizedContextMenu>;
@ -515,11 +407,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
const displayName = OwnProfileStore.instance.displayName || userId;
const avatarUrl = OwnProfileStore.instance.getHttpAvatarUrl(avatarSize);
let badge: JSX.Element;
if (this.state.dndEnabled) {
badge = <div className="mx_UserMenu_dndBadge" />;
}
let name: JSX.Element;
if (!this.props.isPanelCollapsed) {
name = <div className="mx_UserMenu_name">
@ -534,9 +421,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
label={_t("User menu")}
isExpanded={!!this.state.contextMenuPosition}
onContextMenu={this.onContextMenu}
className={classNames({
mx_UserMenu_cutout: badge,
})}
>
<div className="mx_UserMenu_userAvatar">
<BaseAvatar
@ -548,7 +432,6 @@ export default class UserMenu extends React.Component<IProps, IState> {
resizeMethod="crop"
className="mx_UserMenu_userAvatar_BaseAvatar"
/>
{ badge }
</div>
{ name }

View file

@ -1,176 +0,0 @@
/*
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 { Room } from "matrix-js-sdk/src/models/room";
import classNames from 'classnames';
import { logger } from "matrix-js-sdk/src/logger";
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import { dialogTermsInteractionCallback, TermsNotSignedError } from "../../../Terms";
import * as ScalarMessaging from "../../../ScalarMessaging";
import { IntegrationManagerInstance } from "../../../integrations/IntegrationManagerInstance";
import ScalarAuthClient from "../../../ScalarAuthClient";
import AccessibleButton from "../elements/AccessibleButton";
import IntegrationManager from "../settings/IntegrationManager";
import { IDialogProps } from "./IDialogProps";
interface IProps extends IDialogProps {
/**
* Optional room where the integration manager should be open to
*/
room?: Room;
/**
* Optional screen to open on the integration manager
*/
screen?: string;
/**
* Optional integration ID to open in the integration manager
*/
integrationId?: string;
}
interface IState {
managers: IntegrationManagerInstance[];
busy: boolean;
currentIndex: number;
currentConnected: boolean;
currentLoading: boolean;
currentScalarClient: ScalarAuthClient;
}
export default class TabbedIntegrationManagerDialog extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
managers: IntegrationManagers.sharedInstance().getOrderedManagers(),
busy: true,
currentIndex: 0,
currentConnected: false,
currentLoading: true,
currentScalarClient: null,
};
}
public componentDidMount(): void {
this.openManager(0, true);
}
private openManager = async (i: number, force = false): Promise<void> => {
if (i === this.state.currentIndex && !force) return;
const manager = this.state.managers[i];
const client = manager.getScalarClient();
this.setState({
busy: true,
currentIndex: i,
currentLoading: true,
currentConnected: false,
currentScalarClient: client,
});
ScalarMessaging.setOpenManagerUrl(manager.uiUrl);
client.setTermsInteractionCallback((policyInfo, agreedUrls) => {
// To avoid visual glitching of two modals stacking briefly, we customise the
// terms dialog sizing when it will appear for the integration manager so that
// it gets the same basic size as the IM's own modal.
return dialogTermsInteractionCallback(
policyInfo, agreedUrls, 'mx_TermsDialog_forIntegrationManager',
);
});
try {
await client.connect();
if (!client.hasCredentials()) {
this.setState({
busy: false,
currentLoading: false,
currentConnected: false,
});
} else {
this.setState({
busy: false,
currentLoading: false,
currentConnected: true,
});
}
} catch (e) {
if (e instanceof TermsNotSignedError) {
return;
}
logger.error(e);
this.setState({
busy: false,
currentLoading: false,
currentConnected: false,
});
}
};
private renderTabs(): JSX.Element[] {
return this.state.managers.map((m, i) => {
const classes = classNames({
'mx_TabbedIntegrationManagerDialog_tab': true,
'mx_TabbedIntegrationManagerDialog_currentTab': this.state.currentIndex === i,
});
return (
<AccessibleButton
className={classes}
onClick={() => this.openManager(i)}
key={`tab_${i}`}
disabled={this.state.busy}
>
{ m.name }
</AccessibleButton>
);
});
}
public renderTab(): JSX.Element {
let uiUrl = null;
if (this.state.currentScalarClient) {
uiUrl = this.state.currentScalarClient.getScalarInterfaceUrlForRoom(
this.props.room,
this.props.screen,
this.props.integrationId,
);
}
return <IntegrationManager
loading={this.state.currentLoading}
connected={this.state.currentConnected}
url={uiUrl}
onFinished={() => {/* no-op */}}
/>;
}
public render(): JSX.Element {
return (
<div className='mx_TabbedIntegrationManagerDialog_container'>
<div className='mx_TabbedIntegrationManagerDialog_tabs'>
{ this.renderTabs() }
</div>
<div className='mx_TabbedIntegrationManagerDialog_currentManager'>
{ this.renderTab() }
</div>
</div>
);
}
}

View file

@ -207,11 +207,8 @@ const AppsSection: React.FC<IAppsSectionProps> = ({ room }) => {
if (!managers.hasManager()) {
managers.openNoManagerDialog();
} else {
if (SettingsStore.getValue("feature_many_integration_managers")) {
managers.openAll(room);
} else {
managers.getPrimaryManager().open(room);
}
// noinspection JSIgnoredPromiseFromCall
managers.getPrimaryManager().open(room);
}
};

View file

@ -75,7 +75,6 @@ import { UIComponent } from "../../../settings/UIFeature";
import { TimelineRenderingType } from "../../../contexts/RoomContext";
import RightPanelStore from '../../../stores/right-panel/RightPanelStore';
import { IRightPanelCardState } from '../../../stores/right-panel/RightPanelStoreIPanelState';
import { useUserStatusMessage } from "../../../hooks/useUserStatusMessage";
import UserIdentifierCustomisations from '../../../customisations/UserIdentifier';
import PosthogTrackers from "../../../PosthogTrackers";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
@ -1411,7 +1410,6 @@ const UserInfoHeader: React.FC<{
roomId?: string;
}> = ({ member, e2eStatus, roomId }) => {
const cli = useContext(MatrixClientContext);
const statusMessage = useUserStatusMessage(member);
const onMemberAvatarClick = useCallback(() => {
const avatarUrl = (member as RoomMember).getMxcAvatarUrl
@ -1472,11 +1470,6 @@ const UserInfoHeader: React.FC<{
);
}
let statusLabel = null;
if (statusMessage) {
statusLabel = <span className="mx_UserInfo_statusMessage">{ statusMessage }</span>;
}
let e2eIcon;
if (e2eStatus) {
e2eIcon = <E2EIcon size={18} status={e2eStatus} isUser={true} />;
@ -1499,7 +1492,6 @@ const UserInfoHeader: React.FC<{
<div>{ UserIdentifierCustomisations.getDisplayUserIdentifier(member.userId, { roomId, withDisplayName: true }) }</div>
<div className="mx_UserInfo_profileStatus">
{ presenceLabel }
{ statusLabel }
</div>
</div>
</div>

View file

@ -20,12 +20,10 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member";
import { MatrixEvent } from "matrix-js-sdk/src/models/event";
import { EventType } from "matrix-js-sdk/src/@types/event";
import { DeviceInfo } from "matrix-js-sdk/src/crypto/deviceinfo";
import { UserEvent } from "matrix-js-sdk/src/models/user";
import { CryptoEvent } from "matrix-js-sdk/src/crypto";
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
import { UserTrustLevel } from 'matrix-js-sdk/src/crypto/CrossSigning';
import SettingsStore from "../../../settings/SettingsStore";
import dis from "../../../dispatcher/dispatcher";
import { _t } from '../../../languageHandler';
import { MatrixClientPeg } from "../../../MatrixClientPeg";
@ -41,7 +39,6 @@ interface IProps {
}
interface IState {
statusMessage: string;
isRoomEncrypted: boolean;
e2eStatus: string;
}
@ -58,7 +55,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
super(props);
this.state = {
statusMessage: this.getStatusMessage(),
isRoomEncrypted: false,
e2eStatus: null,
};
@ -67,13 +63,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
componentDidMount() {
const cli = MatrixClientPeg.get();
if (SettingsStore.getValue("feature_custom_status")) {
const { user } = this.props.member;
if (user) {
user.on(UserEvent._UnstableStatusMessage, this.onStatusMessageCommitted);
}
}
const { roomId } = this.props.member;
if (roomId) {
const isRoomEncrypted = cli.isRoomEncrypted(roomId);
@ -94,11 +83,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
componentWillUnmount() {
const cli = MatrixClientPeg.get();
const { user } = this.props.member;
if (user) {
user.removeListener(UserEvent._UnstableStatusMessage, this.onStatusMessageCommitted);
}
if (cli) {
cli.removeListener(RoomStateEvent.Events, this.onRoomStateEvents);
cli.removeListener(CryptoEvent.UserTrustStatusChanged, this.onUserTrustStatusChanged);
@ -158,21 +142,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
});
}
private getStatusMessage(): string {
const { user } = this.props.member;
if (!user) {
return "";
}
return user.unstable_statusMessage;
}
private onStatusMessageCommitted = (): void => {
// The `User` object has observed a status message change.
this.setState({
statusMessage: this.getStatusMessage(),
});
};
shouldComponentUpdate(nextProps: IProps, nextState: IState): boolean {
if (
this.memberLastModifiedTime === undefined ||
@ -222,11 +191,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
const name = this.getDisplayName();
const presenceState = member.user ? member.user.presence : null;
let statusMessage = null;
if (member.user && SettingsStore.getValue("feature_custom_status")) {
statusMessage = this.state.statusMessage;
}
const av = (
<MemberAvatar member={member} width={36} height={36} aria-hidden="true" />
);
@ -277,7 +241,6 @@ export default class MemberTile extends React.Component<IProps, IState> {
nameJSX={nameJSX}
powerStatus={powerStatus}
showPresence={this.props.showPresence}
subtextLabel={statusMessage}
e2eStatus={e2eStatus}
onClick={this.onClick}
/>

View file

@ -26,7 +26,6 @@ import AccessibleButton from '../elements/AccessibleButton';
import WidgetUtils, { IWidgetEvent } from '../../../utils/WidgetUtils';
import PersistedElement from "../elements/PersistedElement";
import { IntegrationManagers } from "../../../integrations/IntegrationManagers";
import SettingsStore from "../../../settings/SettingsStore";
import ContextMenu, { ChevronFace } from "../../structures/ContextMenu";
import { WidgetType } from "../../../widgets/WidgetType";
import { WidgetMessagingStore } from "../../../stores/widgets/WidgetMessagingStore";
@ -339,20 +338,12 @@ export default class Stickerpicker extends React.PureComponent<IProps, IState> {
* Launch the integration manager on the stickers integration page
*/
private launchManageIntegrations = (): void => {
// TODO: Open the right integration manager for the widget
if (SettingsStore.getValue("feature_many_integration_managers")) {
IntegrationManagers.sharedInstance().openAll(
this.props.room,
`type_${WidgetType.STICKERPICKER.preferred}`,
this.state.widgetId,
);
} else {
IntegrationManagers.sharedInstance().getPrimaryManager().open(
this.props.room,
`type_${WidgetType.STICKERPICKER.preferred}`,
this.state.widgetId,
);
}
// noinspection JSIgnoredPromiseFromCall
IntegrationManagers.sharedInstance().getPrimaryManager().open(
this.props.room,
`type_${WidgetType.STICKERPICKER.preferred}`,
this.state.widgetId,
);
};
public render(): JSX.Element {