Compare commits

...

9 Commits

Author SHA1 Message Date
David Baker
63ecbab533 Merge remote-tracking branch 'origin/develop' into dbkr/module_api_additions_2 2025-10-16 10:46:06 +01:00
David Baker
b07362717b Add soacetreelevel change 2025-10-15 12:01:51 +01:00
David Baker
97f345651d Merge branch 'develop' into dbkr/module_api_additions_2 2025-10-15 11:49:40 +01:00
David Baker
898475ab5a CSS 2025-10-14 16:04:38 +01:00
David Baker
a79febf6d8 Add items from the module api 2025-10-14 15:48:54 +01:00
David Baker
494ba59aa1 and new files 2025-10-14 15:34:37 +01:00
David Baker
31410fe4ef Add actual module api bits 2025-10-14 15:31:42 +01:00
David Baker
d23223c836 right kind of bracket 2025-10-14 15:24:42 +01:00
David Baker
c02163bf55 Additions to module API 2025-10-14 15:16:06 +01:00
11 changed files with 90 additions and 21 deletions

View File

@@ -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");
}

View File

@@ -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",
@@ -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;

View File

@@ -69,6 +69,7 @@ import { type ConfigOptions } from "../../SdkConfig";
import { MatrixClientContextProvider } from "./MatrixClientContextProvider";
import { Landmark, LandmarkNavigation } from "../../accessibility/LandmarkNavigation";
import { SDKContext } from "../../contexts/SDKContext.ts";
import ModuleApi from "../../modules/Api.ts";
// We need to fetch each pinned message individually (if we don't already have it)
// so each pinned message may trigger a request. Limit the number per room for sanity.
@@ -679,6 +680,10 @@ class LoggedInView extends React.Component<IProps, IState> {
public render(): React.ReactNode {
let pageElement;
const moduleRenderer = this.props.page_type
? ModuleApi.navigation.locationRenderers.get(this.props.page_type)
: undefined;
switch (this.props.page_type) {
case PageTypes.RoomView:
pageElement = (
@@ -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({
@@ -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>

View File

@@ -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";
@@ -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.
@@ -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.navigation.locationRenderers.get(screen)) {
this.setState({ page_type: screen });
}
}
}

View File

@@ -44,6 +44,7 @@ import { type CallState, type MatrixCall } from "matrix-js-sdk/src/webrtc/call";
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";
import shouldHideEvent from "../../shouldHideEvent";
import { _t } from "../../languageHandler";
@@ -147,7 +148,7 @@ if (DEBUG) {
debuglog = logger.log.bind(console);
}
interface IRoomProps {
interface IRoomProps extends RoomViewProps {
threepidInvite?: IThreepidInvite;
oobData?: IOOBData;

View File

@@ -69,6 +69,7 @@ import AccessibleButton from "../elements/AccessibleButton";
import { Landmark, LandmarkNavigation } from "../../../accessibility/LandmarkNavigation";
import { KeyboardShortcut } from "../settings/KeyboardShortcut";
import { ReleaseAnnouncement } from "../../structures/ReleaseAnnouncement";
import ModuleApi from "../../../modules/Api.ts";
const useSpaces = (): [Room[], MetaSpace[], Room[], SpaceKey] => {
const invites = useEventEmitterState<Room[]>(SpaceStore.instance, UPDATE_INVITED_SPACES, () => {
@@ -341,6 +342,7 @@ const InnerSpacePanel = React.memo<IInnerSpacePanelProps>(
</Draggable>
))}
{children}
{ModuleApi.extras.spacePanelItems.map((renderer) => renderer({ isPanelCollapsed }))}
{shouldShowComponent(UIComponent.CreateSpaces) && (
<CreateSpaceButton isPanelCollapsed={isPanelCollapsed} setPanelCollapsed={setPanelCollapsed} />
)}

View File

@@ -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;
@@ -65,6 +66,7 @@ export const SpaceButton = <T extends keyof HTMLElementTagNameMap>({
space,
spaceKey: _spaceKey,
className,
icon,
selected,
label,
contextMenuTooltip,
@@ -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) {
@@ -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}

View File

@@ -26,6 +26,7 @@ import { WatchableProfile } from "./Profile.ts";
import { NavigationApi } from "./Navigation.ts";
import { openDialog } from "./Dialog.tsx";
import { overwriteAccountAuth } from "./Auth.ts";
import { ElementWebExtrasApi } from "./ExtrasApi.ts";
const legacyCustomisationsFactory = <T extends object>(baseCustomisations: T) => {
let used = false;
@@ -79,6 +80,7 @@ export class ModuleApi implements Api {
public readonly config = new ConfigApi();
public readonly i18n = new I18nApi();
public readonly customComponents = new CustomComponentsApi();
public readonly extras = new ElementWebExtrasApi();
public readonly rootNode = document.getElementById("matrixchat")!;
public createRoot(element: Element): Root {

View File

@@ -0,0 +1,16 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { type RoomViewProps, type BuiltinsApi } from "@element-hq/element-web-module-api";
import { RoomView } from "../components/structures/RoomView";
export class ElementWebBuiltinsApi implements BuiltinsApi {
public getRoomViewComponent(): React.ComponentType<RoomViewProps> {
return RoomView;
}
}

16
src/modules/ExtrasApi.ts Normal file
View File

@@ -0,0 +1,16 @@
/*
Copyright 2025 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
import { type SpacePanelItemRenderFunction, type ExtrasApi } from "@element-hq/element-web-module-api";
export class ElementWebExtrasApi implements ExtrasApi {
public spacePanelItems: SpacePanelItemRenderFunction[] = [];
public addSpacePanelItem(renderer: SpacePanelItemRenderFunction): void {
this.spacePanelItems.push(renderer);
}
}

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { type NavigationApi as INavigationApi } from "@element-hq/element-web-module-api";
import { type LocationRenderFunction, type NavigationApi as INavigationApi } from "@element-hq/element-web-module-api";
import { navigateToPermalink } from "../utils/permalinks/navigator.ts";
import { parsePermalink } from "../utils/permalinks/Permalinks.ts";
@@ -14,6 +14,8 @@ import { Action } from "../dispatcher/actions.ts";
import type { ViewRoomPayload } from "../dispatcher/payloads/ViewRoomPayload.ts";
export class NavigationApi implements INavigationApi {
public locationRenderers = new Map<string, LocationRenderFunction>();
public async toMatrixToLink(link: string, join = false): Promise<void> {
navigateToPermalink(link);
@@ -38,4 +40,8 @@ export class NavigationApi implements INavigationApi {
}
}
}
public registerLocationRenderer(path: string, renderer: LocationRenderFunction): void {
this.locationRenderers.set(path, renderer);
}
}