Skip to content
Draft
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: 4 additions & 0 deletions res/css/structures/_SpacePanel.pcss
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,10 @@ Please see LICENSE files in the repository root for full details.
}
}

&.mx_SpaceButton_withIcon .mx_SpaceButton_icon {
background-color: $panel-actions;
}

&.mx_SpaceButton_home .mx_SpaceButton_icon::before {
mask-image: url("@vector-im/compound-design-tokens/icons/home-solid.svg");
}
Expand Down
6 changes: 3 additions & 3 deletions src/PosthogTrackers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const notLoggedInMap: Record<Exclude<Views, Views.LOGGED_IN>, ScreenName> = {
[Views.LOCK_STOLEN]: "SessionLockStolen",
};

const loggedInPageTypeMap: Record<PageType, ScreenName> = {
const loggedInPageTypeMap: Record<PageType | string, ScreenName> = {
[PageType.HomePage]: "Home",
[PageType.RoomView]: "Room",
[PageType.UserView]: "User",
Expand All @@ -48,10 +48,10 @@ export default class PosthogTrackers {
}

private view: Views = Views.LOADING;
private pageType?: PageType;
private pageType?: PageType | string;
private override?: ScreenName;

public trackPageChange(view: Views, pageType: PageType | undefined, durationMs: number): void {
public trackPageChange(view: Views, pageType: PageType | string | undefined, durationMs: number): void {
this.view = view;
this.pageType = pageType;
if (this.override) return;
Expand Down
38 changes: 26 additions & 12 deletions src/components/structures/LoggedInView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { monitorSyncedPushRules } from "../../utils/pushRules/monitorSyncedPushR
import { type ConfigOptions } from "../../SdkConfig";
import { MatrixClientContextProvider } from "./MatrixClientContextProvider";
import { Landmark, LandmarkNavigation } from "../../accessibility/LandmarkNavigation";
import { ModuleApi } from "../../modules/Api.ts";
import { SDKContext } from "../../contexts/SDKContext.ts";

// We need to fetch each pinned message individually (if we don't already have it)
Expand Down Expand Up @@ -679,6 +680,10 @@ class LoggedInView extends React.Component<IProps, IState> {
public render(): React.ReactNode {
let pageElement;

const moduleRenderer = this.props.page_type
? ModuleApi.instance.navigation.locationRenderers.get(this.props.page_type)
: undefined;

switch (this.props.page_type) {
case PageTypes.RoomView:
pageElement = (
Expand All @@ -705,6 +710,13 @@ class LoggedInView extends React.Component<IProps, IState> {
);
}
break;
default: {
if (moduleRenderer) {
pageElement = moduleRenderer();
} else {
console.warn(`Couldn't render page type "${this.props.page_type}"`);
}
}
}

const wrapperClasses = classNames({
Expand Down Expand Up @@ -746,20 +758,22 @@ class LoggedInView extends React.Component<IProps, IState> {
)}
<SpacePanel />
{!useNewRoomList && <BackdropPanel backgroundImage={this.state.backgroundImage} />}
<div
className="mx_LeftPanel_wrapper--user"
ref={this._resizeContainer}
data-collapsed={shouldUseMinimizedUI ? true : undefined}
>
<LeftPanel
pageType={this.props.page_type as PageTypes}
isMinimized={shouldUseMinimizedUI || false}
resizeNotifier={this.context.resizeNotifier}
/>
</div>
{!moduleRenderer && (
<div
className="mx_LeftPanel_wrapper--user"
ref={this._resizeContainer}
data-collapsed={shouldUseMinimizedUI ? true : undefined}
>
<LeftPanel
pageType={this.props.page_type as PageTypes}
isMinimized={shouldUseMinimizedUI || false}
resizeNotifier={this.context.resizeNotifier}
/>
</div>
)}
</div>
</div>
<ResizeHandle passRef={this.resizeHandler} id="lp-resizer" />
{!moduleRenderer && <ResizeHandle passRef={this.resizeHandler} id="lp-resizer" />}
<div className="mx_RoomView_wrapper">{pageElement}</div>
</div>
</div>
Expand Down
11 changes: 8 additions & 3 deletions src/components/structures/MatrixChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ import { ShareFormat, type SharePayload } from "../../dispatcher/payloads/ShareP
import Markdown from "../../Markdown";
import { sanitizeHtmlParams } from "../../Linkify";
import { isOnlyAdmin } from "../../utils/membership";
import { ModuleApi } from "../../modules/Api.ts";

// legacy export
export { default as Views } from "../../Views";
Expand Down Expand Up @@ -175,9 +176,11 @@ interface IProps {
interface IState {
// the master view we are showing.
view: Views;
// What the LoggedInView would be showing if visible
// What the LoggedInView would be showing if visible.
// A member of the enum for standard pages or a string for those provided by
// a module.
// eslint-disable-next-line camelcase
page_type?: PageType;
page_type?: PageType | string;
// The ID of the room we're viewing. This is either populated directly
// in the case where we view a room by ID or by RoomView when it resolves
// what ID an alias points at.
Expand Down Expand Up @@ -1922,7 +1925,9 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
subAction: params?.action,
});
} else {
logger.info(`Ignoring showScreen for '${screen}'`);
if (ModuleApi.instance.navigation.locationRenderers.get(screen)) {
this.setState({ page_type: screen });
}
}
}

Expand Down
52 changes: 32 additions & 20 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import { debounce, throttle } from "lodash";
import { CryptoEvent } from "matrix-js-sdk/src/crypto-api";
import { type ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle";
import { type RoomViewProps } from "@element-hq/element-web-module-api";

Check failure on line 47 in src/components/structures/RoomView.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Module '"@element-hq/element-web-module-api"' has no exported member 'RoomViewProps'.

import shouldHideEvent from "../../shouldHideEvent";
import { _t } from "../../languageHandler";
Expand Down Expand Up @@ -134,6 +135,7 @@
import { DeclineAndBlockInviteDialog } from "../views/dialogs/DeclineAndBlockInviteDialog";
import { type FocusMessageSearchPayload } from "../../dispatcher/payloads/FocusMessageSearchPayload.ts";
import { isRoomEncrypted } from "../../hooks/useIsEncrypted";
import { type RoomViewStore } from "../../stores/RoomViewStore.tsx";

const DEBUG = false;
const PREVENT_MULTIPLE_JITSI_WITHIN = 30_000;
Expand All @@ -147,7 +149,7 @@
debuglog = logger.log.bind(console);
}

interface IRoomProps {
interface IRoomProps extends RoomViewProps {
threepidInvite?: IThreepidInvite;
oobData?: IOOBData;

Expand Down Expand Up @@ -380,6 +382,8 @@
private messagePanel: TimelinePanel | null = null;
private roomViewBody = createRef<HTMLDivElement>();

private roomViewStore: RoomViewStore;

public static contextType = SDKContext;
declare public context: React.ContextType<typeof SDKContext>;

Expand All @@ -392,6 +396,12 @@
throw new Error("Unable to create RoomView without MatrixClient");
}

if (props.roomId) {

Check failure on line 399 in src/components/structures/RoomView.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Property 'roomId' does not exist on type 'IRoomProps'.
this.roomViewStore = this.context.multiRoomViewStore.getRoomViewStoreForRoom(props.roomId);

Check failure on line 400 in src/components/structures/RoomView.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Property 'roomId' does not exist on type 'IRoomProps'.
} else {
this.roomViewStore = context.roomViewStore;
}

const llMembers = context.client.hasLazyLoadMembersEnabled();
this.state = {
roomId: undefined,
Expand Down Expand Up @@ -525,7 +535,7 @@
};

private getMainSplitContentType = (room: Room): MainSplitContentType => {
if (this.context.roomViewStore.isViewingCall() || isVideoRoom(room)) {
if (this.roomViewStore.isViewingCall() || isVideoRoom(room)) {
return MainSplitContentType.Call;
}
if (this.context.widgetLayoutStore.hasMaximisedWidget(room)) {
Expand All @@ -539,8 +549,8 @@
return;
}

const roomLoadError = this.context.roomViewStore.getRoomLoadError() ?? undefined;
if (!initial && !roomLoadError && this.state.roomId !== this.context.roomViewStore.getRoomId()) {
const roomLoadError = this.roomViewStore.getRoomLoadError() ?? undefined;
if (!initial && !roomLoadError && this.state.roomId !== this.roomViewStore.getRoomId()) {
// RoomView explicitly does not support changing what room
// is being viewed: instead it should just be re-mounted when
// switching rooms. Therefore, if the room ID changes, we
Expand All @@ -555,29 +565,29 @@
return;
}

const roomId = this.context.roomViewStore.getRoomId() ?? null;
const roomId = this.roomViewStore.getRoomId() ?? null;
const room = this.context.client?.getRoom(roomId ?? undefined) ?? undefined;

const newState: Partial<IRoomState> = {
roomId: roomId ?? undefined,
roomAlias: this.context.roomViewStore.getRoomAlias() ?? undefined,
roomLoading: this.context.roomViewStore.isRoomLoading(),
roomAlias: this.roomViewStore.getRoomAlias() ?? undefined,
roomLoading: this.roomViewStore.isRoomLoading(),
roomLoadError,
joining: this.context.roomViewStore.isJoining(),
replyToEvent: this.context.roomViewStore.getQuotingEvent() ?? undefined,
joining: this.roomViewStore.isJoining(),
replyToEvent: this.roomViewStore.getQuotingEvent() ?? undefined,
// we should only peek once we have a ready client
shouldPeek: this.state.matrixClientIsReady && this.context.roomViewStore.shouldPeek(),
shouldPeek: this.state.matrixClientIsReady && this.roomViewStore.shouldPeek(),
showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId),
showRedactions: SettingsStore.getValue("showRedactions", roomId),
showJoinLeaves: SettingsStore.getValue("showJoinLeaves", roomId),
showAvatarChanges: SettingsStore.getValue("showAvatarChanges", roomId),
showDisplaynameChanges: SettingsStore.getValue("showDisplaynameChanges", roomId),
wasContextSwitch: this.context.roomViewStore.getWasContextSwitch(),
wasContextSwitch: this.roomViewStore.getWasContextSwitch(),
mainSplitContentType: room ? this.getMainSplitContentType(room) : undefined,
initialEventId: undefined, // default to clearing this, will get set later in the method if needed
showRightPanel: roomId ? this.context.rightPanelStore.isOpenForRoom(roomId) : false,
promptAskToJoin: this.context.roomViewStore.promptAskToJoin(),
viewRoomOpts: this.context.roomViewStore.getViewRoomOpts(),
promptAskToJoin: this.roomViewStore.promptAskToJoin(),
viewRoomOpts: this.roomViewStore.getViewRoomOpts(),
};

if (
Expand All @@ -593,7 +603,7 @@
newState.showRightPanel = false;
}

const initialEventId = this.context.roomViewStore.getInitialEventId() ?? this.state.initialEventId;
const initialEventId = this.roomViewStore.getInitialEventId() ?? this.state.initialEventId;
if (initialEventId) {
let initialEvent = room?.findEventById(initialEventId);
// The event does not exist in the current sync data
Expand All @@ -619,13 +629,13 @@
action: Action.ShowThread,
rootEvent: thread.rootEvent,
initialEvent,
highlighted: this.context.roomViewStore.isInitialEventHighlighted(),
scroll_into_view: this.context.roomViewStore.initialEventScrollIntoView(),
highlighted: this.roomViewStore.isInitialEventHighlighted(),
scroll_into_view: this.roomViewStore.initialEventScrollIntoView(),
});
} else {
newState.initialEventId = initialEventId;
newState.isInitialEventHighlighted = this.context.roomViewStore.isInitialEventHighlighted();
newState.initialEventScrollIntoView = this.context.roomViewStore.initialEventScrollIntoView();
newState.isInitialEventHighlighted = this.roomViewStore.isInitialEventHighlighted();
newState.initialEventScrollIntoView = this.roomViewStore.initialEventScrollIntoView();
}
}

Expand Down Expand Up @@ -885,7 +895,7 @@
this.context.client.on(MatrixEventEvent.Decrypted, this.onEventDecrypted);
}
// Start listening for RoomViewStore updates
this.context.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);
this.roomViewStore.on(UPDATE_EVENT, this.onRoomViewStoreUpdate);

this.context.rightPanelStore.on(UPDATE_EVENT, this.onRightPanelStoreUpdate);

Expand Down Expand Up @@ -1002,7 +1012,7 @@

window.removeEventListener("beforeunload", this.onPageUnload);

this.context.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);
this.roomViewStore.off(UPDATE_EVENT, this.onRoomViewStoreUpdate);

this.context.rightPanelStore.off(UPDATE_EVENT, this.onRightPanelStoreUpdate);
WidgetEchoStore.removeListener(UPDATE_EVENT, this.onWidgetEchoStoreUpdate);
Expand Down Expand Up @@ -1030,6 +1040,8 @@
// clean up if this was a local room
this.context.client?.store.removeRoom(this.state.room.roomId);
}

if (this.props.roomId) this.context.multiRoomViewStore.removeRoomViewStore(this.props.roomId);

Check failure on line 1044 in src/components/structures/RoomView.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Property 'roomId' does not exist on type 'Readonly<IRoomProps>'.

Check failure on line 1044 in src/components/structures/RoomView.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Property 'roomId' does not exist on type 'Readonly<IRoomProps>'.
}

private onRightPanelStoreUpdate = (): void => {
Expand Down
25 changes: 25 additions & 0 deletions src/components/views/spaces/SpacePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@
import AccessibleButton from "../elements/AccessibleButton";
import { Landmark, LandmarkNavigation } from "../../../accessibility/LandmarkNavigation";
import { KeyboardShortcut } from "../settings/KeyboardShortcut";
import { ModuleApi } from "../../../modules/Api.ts";
import { useModuleSpacePanelItems } from "../../../modules/ExtrasApi.ts";
import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement";

const useSpaces = (): [Room[], MetaSpace[], Room[], SpaceKey] => {
Expand Down Expand Up @@ -290,6 +292,8 @@
const [invites, metaSpaces, actualSpaces, activeSpace] = useSpaces();
const activeSpaces = activeSpace ? [activeSpace] : [];

const moduleSpaceItems = useModuleSpacePanelItems(ModuleApi.instance.extras);

const metaSpacesSection = metaSpaces
.filter((key) => !(key === MetaSpace.VideoRooms && !SettingsStore.getValue("feature_video_rooms")))
.map((key) => {
Expand Down Expand Up @@ -341,6 +345,27 @@
</Draggable>
))}
{children}
{moduleSpaceItems.map((item) => (
<li
key={item.spaceKey}
className={classNames("mx_SpaceItem", {
collapsed: isPanelCollapsed,
})}
role="treeitem"
aria-selected={false} // TODO
>
<SpaceButton

Check failure on line 357 in src/components/views/spaces/SpacePanel.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Property 'label' is missing in type '{ isNarrow: boolean; size: string; selected: boolean; onClick: () => void; spaceKey: string; }' but required in type '{ space?: Room | undefined; spaceKey?: string | undefined; className?: string | undefined; selected?: boolean | undefined; label: string; icon?: Element | undefined; ... 6 more ...; onClick?(ev?: ButtonEvent | undefined): void; }'.
{...item}
isNarrow={isPanelCollapsed}
size="32px"
selected={activeSpace === item.spaceKey}
onClick={() => {
SpaceStore.instance.setActiveSpace(item.spaceKey);
item.onSelected?.();

Check failure on line 364 in src/components/views/spaces/SpacePanel.tsx

View workflow job for this annotation

GitHub Actions / Typescript Syntax Check

Property 'onSelected' does not exist on type 'ModuleSpacePanelItem'.
}}
/>
</li>
))}
{shouldShowComponent(UIComponent.CreateSpaces) && (
<CreateSpaceButton isPanelCollapsed={isPanelCollapsed} setPanelCollapsed={setPanelCollapsed} />
)}
Expand Down
5 changes: 4 additions & 1 deletion src/components/views/spaces/SpaceTreeLevel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type ButtonProps<T extends keyof HTMLElementTagNameMap> = Omit<
className?: string;
selected?: boolean;
label: string;
icon?: JSX.Element;
contextMenuTooltip?: string;
notificationState?: NotificationState;
isNarrow?: boolean;
Expand All @@ -65,6 +66,7 @@ export const SpaceButton = <T extends keyof HTMLElementTagNameMap>({
space,
spaceKey: _spaceKey,
className,
icon,
selected,
label,
contextMenuTooltip,
Expand All @@ -84,7 +86,7 @@ export const SpaceButton = <T extends keyof HTMLElementTagNameMap>({

let avatar = (
<div className="mx_SpaceButton_avatarPlaceholder">
<div className="mx_SpaceButton_icon" />
<div className="mx_SpaceButton_icon">{icon}</div>
</div>
);
if (space) {
Expand Down Expand Up @@ -143,6 +145,7 @@ export const SpaceButton = <T extends keyof HTMLElementTagNameMap>({
mx_SpaceButton_active: selected,
mx_SpaceButton_hasMenuOpen: menuDisplayed,
mx_SpaceButton_narrow: isNarrow,
mx_SpaceButton_withIcon: Boolean(icon),
})}
aria-label={label}
title={!isNarrow || menuDisplayed ? undefined : label}
Expand Down
Loading
Loading