Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/TextForEvent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { WIDGET_LAYOUT_EVENT_TYPE } from "./stores/widgets/WidgetLayoutStore";
import { RightPanelPhases } from "./stores/right-panel/RightPanelStorePhases";
import defaultDispatcher from "./dispatcher/dispatcher";
import { MatrixClientPeg } from "./MatrixClientPeg";
import { ROOM_SECURITY_TAB } from "./components/views/dialogs/RoomSettingsDialog";
import { RoomSettingsTab } from "./components/views/dialogs/RoomSettingsDialog";
import AccessibleButton, { ButtonEvent } from "./components/views/elements/AccessibleButton";
import RightPanelStore from "./stores/right-panel/RightPanelStore";
import { highlightEvent, isLocationEvent } from "./utils/EventUtils";
Expand Down Expand Up @@ -236,7 +236,7 @@ function textForTombstoneEvent(ev: MatrixEvent): (() => string) | null {
const onViewJoinRuleSettingsClick = (): void => {
defaultDispatcher.dispatch({
action: "open_room_settings",
initial_tab_id: ROOM_SECURITY_TAB,
initial_tab_id: RoomSettingsTab.Security,
});
};

Expand Down
30 changes: 15 additions & 15 deletions src/components/structures/TabbedView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { RovingAccessibleButton, RovingTabIndexProvider } from "../../accessibil
/**
* Represents a tab for the TabbedView.
*/
export class Tab {
export class Tab<T extends string> {
/**
* Creates a new tab.
* @param {string} id The tab's ID.
Expand All @@ -39,7 +39,7 @@ export class Tab {
* @param {string} screenName The screen name to report to Posthog.
*/
public constructor(
public readonly id: string,
public readonly id: T,
public readonly label: string,
public readonly icon: string | null,
public readonly body: React.ReactNode,
Expand All @@ -52,20 +52,20 @@ export enum TabLocation {
TOP = "top",
}

interface IProps {
tabs: NonEmptyArray<Tab>;
initialTabId?: string;
interface IProps<T extends string> {
tabs: NonEmptyArray<Tab<T>>;
initialTabId?: T;
tabLocation: TabLocation;
onChange?: (tabId: string) => void;
onChange?: (tabId: T) => void;
screenName?: ScreenName;
}

interface IState {
activeTabId: string;
interface IState<T extends string> {
activeTabId: T;
}

export default class TabbedView extends React.Component<IProps, IState> {
public constructor(props: IProps) {
export default class TabbedView<T extends string> extends React.Component<IProps<T>, IState<T>> {
public constructor(props: IProps<T>) {
super(props);

const initialTabIdIsValid = props.tabs.find((tab) => tab.id === props.initialTabId);
Expand All @@ -78,7 +78,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
tabLocation: TabLocation.LEFT,
};

private getTabById(id: string): Tab | undefined {
private getTabById(id: T): Tab<T> | undefined {
return this.props.tabs.find((tab) => tab.id === id);
}

Expand All @@ -87,7 +87,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
* @param {Tab} tab the tab to show
* @private
*/
private setActiveTab(tab: Tab): void {
private setActiveTab(tab: Tab<T>): void {
// make sure this tab is still in available tabs
if (!!this.getTabById(tab.id)) {
if (this.props.onChange) this.props.onChange(tab.id);
Expand All @@ -97,7 +97,7 @@ export default class TabbedView extends React.Component<IProps, IState> {
}
}

private renderTabLabel(tab: Tab): JSX.Element {
private renderTabLabel(tab: Tab<T>): JSX.Element {
const isActive = this.state.activeTabId === tab.id;
const classes = classNames("mx_TabbedView_tabLabel", {
mx_TabbedView_tabLabel_active: isActive,
Expand Down Expand Up @@ -130,11 +130,11 @@ export default class TabbedView extends React.Component<IProps, IState> {
);
}

private getTabId(tab: Tab): string {
private getTabId(tab: Tab<T>): string {
return `mx_tabpanel_${tab.id}`;
}

private renderTabPanel(tab: Tab): React.ReactNode {
private renderTabPanel(tab: Tab<T>): React.ReactNode {
const id = this.getTabId(tab);
return (
<div className="mx_TabbedView_tabPanel" key={id} id={id} aria-labelledby={`${id}_label`}>
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/context_menus/RoomContextMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import ExportDialog from "../dialogs/ExportDialog";
import { useFeatureEnabled } from "../../../hooks/useSettings";
import { usePinnedEvents } from "../right_panel/PinnedMessagesCard";
import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases";
import { ROOM_NOTIFICATIONS_TAB } from "../dialogs/RoomSettingsDialog";
import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog";
import { useEventEmitterState } from "../../../hooks/useEventEmitter";
import RightPanelStore from "../../../stores/right-panel/RightPanelStore";
import DMRoomMap from "../../../utils/DMRoomMap";
Expand Down Expand Up @@ -199,7 +199,7 @@ const RoomContextMenu: React.FC<IProps> = ({ room, onFinished, ...props }) => {
dis.dispatch({
action: "open_room_settings",
room_id: room.roomId,
initial_tab_id: ROOM_NOTIFICATIONS_TAB,
initial_tab_id: RoomSettingsTab.Notifications,
});
onFinished();

Expand Down
2 changes: 1 addition & 1 deletion src/components/views/dialogs/InviteDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1494,7 +1494,7 @@ export default class InviteDialog extends React.PureComponent<Props, IInviteDial

let dialogContent;
if (this.props.kind === InviteKind.CallTransfer) {
const tabs: NonEmptyArray<Tab> = [
const tabs: NonEmptyArray<Tab<TabId>> = [
new Tab(TabId.UserDirectory, _td("User Directory"), "mx_InviteDialog_userDirectoryIcon", usersSection),
];

Expand Down
40 changes: 21 additions & 19 deletions src/components/views/dialogs/RoomSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,16 @@ import { NonEmptyArray } from "../../../@types/common";
import { PollHistoryTab } from "../settings/tabs/room/PollHistoryTab";
import ErrorBoundary from "../elements/ErrorBoundary";

export const ROOM_GENERAL_TAB = "ROOM_GENERAL_TAB";
export const ROOM_VOIP_TAB = "ROOM_VOIP_TAB";
export const ROOM_SECURITY_TAB = "ROOM_SECURITY_TAB";
export const ROOM_ROLES_TAB = "ROOM_ROLES_TAB";
export const ROOM_NOTIFICATIONS_TAB = "ROOM_NOTIFICATIONS_TAB";
export const ROOM_BRIDGES_TAB = "ROOM_BRIDGES_TAB";
export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB";
export const ROOM_POLL_HISTORY_TAB = "ROOM_POLL_HISTORY_TAB";
export const enum RoomSettingsTab {
General = "ROOM_GENERAL_TAB",
Voip = "ROOM_VOIP_TAB",
Security = "ROOM_SECURITY_TAB",
Roles = "ROOM_ROLES_TAB",
Notifications = "ROOM_NOTIFICATIONS_TAB",
Bridges = "ROOM_BRIDGES_TAB",
Advanced = "ROOM_ADVANCED_TAB",
PollHistory = "ROOM_POLL_HISTORY_TAB",
}

interface IProps {
roomId: string;
Expand Down Expand Up @@ -118,12 +120,12 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
this.forceUpdate();
};

private getTabs(): NonEmptyArray<Tab> {
const tabs: Tab[] = [];
private getTabs(): NonEmptyArray<Tab<RoomSettingsTab>> {
const tabs: Tab<RoomSettingsTab>[] = [];

tabs.push(
new Tab(
ROOM_GENERAL_TAB,
RoomSettingsTab.General,
_td("General"),
"mx_RoomSettingsDialog_settingsIcon",
<GeneralRoomSettingsTab room={this.state.room} />,
Expand All @@ -133,7 +135,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
if (SettingsStore.getValue("feature_group_calls")) {
tabs.push(
new Tab(
ROOM_VOIP_TAB,
RoomSettingsTab.Voip,
_td("Voice & Video"),
"mx_RoomSettingsDialog_voiceIcon",
<VoipRoomSettingsTab room={this.state.room} />,
Expand All @@ -142,7 +144,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
}
tabs.push(
new Tab(
ROOM_SECURITY_TAB,
RoomSettingsTab.Security,
_td("Security & Privacy"),
"mx_RoomSettingsDialog_securityIcon",
<SecurityRoomSettingsTab room={this.state.room} closeSettingsFn={() => this.props.onFinished(true)} />,
Expand All @@ -151,7 +153,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
);
tabs.push(
new Tab(
ROOM_ROLES_TAB,
RoomSettingsTab.Roles,
_td("Roles & Permissions"),
"mx_RoomSettingsDialog_rolesIcon",
<RolesRoomSettingsTab room={this.state.room} />,
Expand All @@ -160,7 +162,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
);
tabs.push(
new Tab(
ROOM_NOTIFICATIONS_TAB,
RoomSettingsTab.Notifications,
_td("Notifications"),
"mx_RoomSettingsDialog_notificationsIcon",
(
Expand All @@ -176,7 +178,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
if (SettingsStore.getValue("feature_bridge_state")) {
tabs.push(
new Tab(
ROOM_BRIDGES_TAB,
RoomSettingsTab.Bridges,
_td("Bridges"),
"mx_RoomSettingsDialog_bridgesIcon",
<BridgeSettingsTab room={this.state.room} />,
Expand All @@ -187,7 +189,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {

tabs.push(
new Tab(
ROOM_POLL_HISTORY_TAB,
RoomSettingsTab.PollHistory,
_td("Poll history"),
"mx_RoomSettingsDialog_pollsIcon",
<PollHistoryTab room={this.state.room} onFinished={() => this.props.onFinished(true)} />,
Expand All @@ -197,7 +199,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
if (SettingsStore.getValue(UIFeature.AdvancedSettings)) {
tabs.push(
new Tab(
ROOM_ADVANCED_TAB,
RoomSettingsTab.Advanced,
_td("Advanced"),
"mx_RoomSettingsDialog_warningIcon",
(
Expand All @@ -211,7 +213,7 @@ class RoomSettingsDialog extends React.Component<IProps, IState> {
);
}

return tabs as NonEmptyArray<Tab>;
return tabs as NonEmptyArray<Tab<RoomSettingsTab>>;
}

public render(): React.ReactNode {
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/dialogs/SpacePreferencesDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ const SpacePreferencesAppearanceTab: React.FC<Pick<IProps, "space">> = ({ space
};

const SpacePreferencesDialog: React.FC<IProps> = ({ space, initialTabId, onFinished }) => {
const tabs: NonEmptyArray<Tab> = [
const tabs: NonEmptyArray<Tab<SpacePreferenceTab>> = [
new Tab(
SpacePreferenceTab.Appearance,
_td("Appearance"),
Expand Down
2 changes: 1 addition & 1 deletion src/components/views/dialogs/SpaceSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ const SpaceSettingsDialog: React.FC<IProps> = ({ matrixClient: cli, space, onFin
<AdvancedRoomSettingsTab room={space} closeSettingsFn={onFinished} />,
)
: null,
].filter(Boolean) as NonEmptyArray<Tab>;
].filter(Boolean) as NonEmptyArray<Tab<SpaceSettingsTab>>;
}, [cli, space, onFinished]);

return (
Expand Down
6 changes: 3 additions & 3 deletions src/components/views/dialogs/UserSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
this.setState({ newSessionManagerEnabled: newValue });
};

private getTabs(): NonEmptyArray<Tab> {
const tabs: Tab[] = [];
private getTabs(): NonEmptyArray<Tab<UserTab>> {
const tabs: Tab<UserTab>[] = [];

tabs.push(
new Tab(
Expand Down Expand Up @@ -208,7 +208,7 @@ export default class UserSettingsDialog extends React.Component<IProps, IState>
),
);

return tabs as NonEmptyArray<Tab>;
return tabs as NonEmptyArray<Tab<UserTab>>;
}

public render(): React.ReactNode {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ export interface PickerIProps {
onFinished(sourceId?: string): void;
}

type TabId = "screen" | "window";

export default class DesktopCapturerSourcePicker extends React.Component<PickerIProps, PickerIState> {
public interval: number;

Expand Down Expand Up @@ -134,7 +136,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
this.props.onFinished();
};

private getTab(type: "screen" | "window", label: string): Tab {
private getTab(type: TabId, label: string): Tab<TabId> {
const sources = this.state.sources
.filter((source) => source.id.startsWith(type))
.map((source) => {
Expand All @@ -152,7 +154,7 @@ export default class DesktopCapturerSourcePicker extends React.Component<PickerI
}

public render(): React.ReactNode {
const tabs: NonEmptyArray<Tab> = [
const tabs: NonEmptyArray<Tab<TabId>> = [
this.getTab("screen", _t("Share entire screen")),
this.getTab("window", _t("Application window")),
];
Expand Down
4 changes: 2 additions & 2 deletions src/components/views/rooms/NewRoomIntro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { Action } from "../../../dispatcher/actions";
import SpaceStore from "../../../stores/spaces/SpaceStore";
import { showSpaceInvite } from "../../../utils/space";
import EventTileBubble from "../messages/EventTileBubble";
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog";
import { MatrixClientPeg } from "../../../MatrixClientPeg";
import { shouldShowComponent } from "../../../customisations/helpers/UIComponents";
import { UIComponent } from "../../../settings/UIFeature";
Expand Down Expand Up @@ -268,7 +268,7 @@ const NewRoomIntro: React.FC = () => {
event.preventDefault();
defaultDispatcher.dispatch({
action: "open_room_settings",
initial_tab_id: ROOM_SECURITY_TAB,
initial_tab_id: RoomSettingsTab.Security,
});
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/views/settings/JoinRuleSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { upgradeRoom } from "../../../utils/RoomUpgrade";
import { arrayHasDiff } from "../../../utils/arrays";
import { useLocalEcho } from "../../../hooks/useLocalEcho";
import dis from "../../../dispatcher/dispatcher";
import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog";
import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog";
import { Action } from "../../../dispatcher/actions";
import { ViewRoomPayload } from "../../../dispatcher/payloads/ViewRoomPayload";
import { doesRoomVersionSupport, PreferredRoomVersions } from "../../../utils/PreferredRoomVersions";
Expand Down Expand Up @@ -320,7 +320,7 @@ const JoinRuleSettings: React.FC<IProps> = ({
// open new settings on this tab
dis.dispatch({
action: "open_room_settings",
initial_tab_id: ROOM_SECURITY_TAB,
initial_tab_id: RoomSettingsTab.Security,
});
},
});
Expand Down
4 changes: 2 additions & 2 deletions test/components/structures/TabbedView-test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ describe("<TabbedView />", () => {
const securityTab = new Tab("SECURITY", "Security", "security", <div>security</div>);
const defaultProps = {
tabLocation: TabLocation.LEFT,
tabs: [generalTab, labsTab, securityTab] as NonEmptyArray<Tab>,
tabs: [generalTab, labsTab, securityTab] as NonEmptyArray<Tab<any>>,
};
const getComponent = (props = {}): React.ReactElement => <TabbedView {...defaultProps} {...props} />;

const getTabTestId = (tab: Tab): string => `settings-tab-${tab.id}`;
const getTabTestId = (tab: Tab<string>): string => `settings-tab-${tab.id}`;
const getActiveTab = (container: HTMLElement): Element | undefined =>
container.getElementsByClassName("mx_TabbedView_tabLabel_active")[0];
const getActiveTabBody = (container: HTMLElement): Element | undefined =>
Expand Down