diff --git a/README.md b/README.md index 1fe76be..d5d83e5 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,11 @@ The module can also change what room/user/entity the user is looking at, and joi From the `RuntimeModule` instance, modules can listen for `WrapperLifecycle.Wrapper` to provide a wrapper react component. It would wrap the `MatrixChat` component and let any consumer add a header, a footer. +### UI Component management + +From the `RuntimeModule` instance, modules can listen to `UiComponentLifecycle.ShouldShowComponent` that allows to +control if some components should be shown or not. + ## Contributing / developing Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for the mechanics of the contribution process. diff --git a/src/ModuleApi.ts b/src/ModuleApi.ts index 4e1b7ed..b9d01ca 100644 --- a/src/ModuleApi.ts +++ b/src/ModuleApi.ts @@ -120,4 +120,11 @@ export interface ModuleApi { * @returns The config value verbatim. */ getConfigValue(namespace: string, key: string): T | undefined; + + /** + * Get the user-id of the logged-in user + * + * @returns MXID for the logged-in user, or undefined if not logged in + */ + getUserId(): string | undefined; } diff --git a/src/lifecycles/UIComponentLifecycle.ts b/src/lifecycles/UIComponentLifecycle.ts new file mode 100644 index 0000000..0766fe8 --- /dev/null +++ b/src/lifecycles/UIComponentLifecycle.ts @@ -0,0 +1,87 @@ +/* +Copyright 2023 Mikhail Aheichyk +Copyright 2023 Nordeck IT + Consulting GmbH. + +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. +*/ + +/** + * UI Component lifecycle events. + */ +export enum UIComponentLifecycle { + /** + * An event to check if component should be shown by the Element. + */ + ShouldShowComponent = "should_show_component", +} + +/** + * Opts object containing the result of whether the component should be shown or not. + */ +export type ShouldShowUIComponentOps = { + /** + * Should be true to show the component, false to hide it. If not defined, then the default value from + * the Element should be applied. + */ + shouldShowComponent: boolean | undefined; +}; + +/** + * Listener for "should_show_component" request + */ +export type ShouldShowUIComponentListener = ( + shouldShowUIComponentOps: ShouldShowUIComponentOps, + component: UIComponent, +) => void; + +export enum UIComponent { + /** + * Components that lead to a user being invited. + */ + InviteUsers = "UIComponent.sendInvites", + + /** + * Components that lead to a room being created that aren't already + * guarded by some other condition (ie: "only if you can edit this + * space" is *not* guarded by this component, but "start DM" is). + */ + CreateRooms = "UIComponent.roomCreation", + + /** + * Components that lead to a Space being created that aren't already + * guarded by some other condition (ie: "only if you can add subspaces" + * is *not* guarded by this component, but "create new space" is). + */ + CreateSpaces = "UIComponent.spaceCreation", + + /** + * Components that lead to the public room directory. + */ + ExploreRooms = "UIComponent.exploreRooms", + + /** + * Components that lead to the user being able to easily add widgets + * and integrations to the room, such as from the room information card. + */ + AddIntegrations = "UIComponent.addIntegrations", + + /** + * Component that lead to the user being able to search, dial, explore rooms + */ + FilterContainer = "UIComponent.filterContainer", + + /** + * Components that lead the user to room options menu. + */ + RoomOptionsMenu = "UIComponent.roomOptionsMenu", +} diff --git a/src/lifecycles/types.ts b/src/lifecycles/types.ts index 8bcded1..2481ff3 100644 --- a/src/lifecycles/types.ts +++ b/src/lifecycles/types.ts @@ -17,5 +17,6 @@ limitations under the License. import { RoomViewLifecycle } from "./RoomViewLifecycle"; import { WidgetLifecycle } from "./WidgetLifecycle"; import { WrapperLifecycle } from "./WrapperLifecycle"; +import { UIComponentLifecycle } from "./UIComponentLifecycle"; -export type AnyLifecycle = RoomViewLifecycle | WidgetLifecycle | WrapperLifecycle; +export type AnyLifecycle = RoomViewLifecycle | WidgetLifecycle | WrapperLifecycle | UIComponentLifecycle; diff --git a/test/lifecycles/UiComponentLifecycle.test.ts b/test/lifecycles/UiComponentLifecycle.test.ts new file mode 100644 index 0000000..0c74713 --- /dev/null +++ b/test/lifecycles/UiComponentLifecycle.test.ts @@ -0,0 +1,55 @@ +/* +Copyright 2023 Mikhail Aheichyk +Copyright 2023 Nordeck IT + Consulting GmbH. + +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 { + UIComponentLifecycle, + ShouldShowUIComponentOps, + ShouldShowUIComponentListener, + UIComponent, +} from "../../src/lifecycles/UIComponentLifecycle"; +import { RuntimeModule } from "../../src/RuntimeModule"; + +describe("UiComponentLifecycle", () => { + let module: RuntimeModule; + + beforeAll(() => { + module = new (class extends RuntimeModule { + public constructor() { + super(undefined as any); + + this.on(UIComponentLifecycle.ShouldShowComponent, this.shouldShowComponent); + } + + protected shouldShowComponent: ShouldShowUIComponentListener = ( + shouldShowUiComponentOps: ShouldShowUIComponentOps, + component: UIComponent, + ) => { + shouldShowUiComponentOps.shouldShowComponent = component !== UIComponent.CreateRooms; + }; + })(); + }); + + it("should handle should_show_component request", () => { + const opts: ShouldShowUIComponentOps = { shouldShowComponent: undefined }; + + module.emit(UIComponentLifecycle.ShouldShowComponent, opts, UIComponent.CreateRooms, "@user-id"); + expect(opts.shouldShowComponent).toBe(false); + + module.emit(UIComponentLifecycle.ShouldShowComponent, opts, UIComponent.ExploreRooms, "@user-id"); + expect(opts.shouldShowComponent).toBe(true); + }); +});