From 9061cc4628ac86c45722995854c32fa10fe154e0 Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Wed, 15 Mar 2023 11:14:54 +0300 Subject: [PATCH 1/8] Add support to customize UI components rendering. Signed-off-by: Mikhail Aheichyk --- src/lifecycles/UiComponentLifecycle.ts | 65 ++++++++++++++++++++ src/lifecycles/types.ts | 2 + test/lifecycles/UiComponentLifecycle.test.ts | 53 ++++++++++++++++ 3 files changed, 120 insertions(+) create mode 100644 src/lifecycles/UiComponentLifecycle.ts create mode 100644 test/lifecycles/UiComponentLifecycle.test.ts diff --git a/src/lifecycles/UiComponentLifecycle.ts b/src/lifecycles/UiComponentLifecycle.ts new file mode 100644 index 0000000..afc0ee0 --- /dev/null +++ b/src/lifecycles/UiComponentLifecycle.ts @@ -0,0 +1,65 @@ +/* +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. +*/ + +export enum UiComponentLifecycle { + ShouldShowComponent = "should_show_component" +} + +export type ShouldShowUiComponentOps = { + shouldShowComponent: boolean | undefined; +}; + +/** + * Listener for "should_show_component" request + */ +export type ShouldShowUiComponentListener = ( + shouldShowUiComponentOps: ShouldShowUiComponentOps, + component: UiComponent, + matrixUserId: string +) => 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", +} diff --git a/src/lifecycles/types.ts b/src/lifecycles/types.ts index 72c3a34..5184b1f 100644 --- a/src/lifecycles/types.ts +++ b/src/lifecycles/types.ts @@ -16,8 +16,10 @@ limitations under the License. import { RoomViewLifecycle } from "./RoomViewLifecycle"; import { WidgetLifecycle } from "./WidgetLifecycle"; +import { UiComponentLifecycle } from "./UiComponentLifecycle"; export type AnyLifecycle = | RoomViewLifecycle | WidgetLifecycle + | UiComponentLifecycle ; diff --git a/test/lifecycles/UiComponentLifecycle.test.ts b/test/lifecycles/UiComponentLifecycle.test.ts new file mode 100644 index 0000000..25232ec --- /dev/null +++ b/test/lifecycles/UiComponentLifecycle.test.ts @@ -0,0 +1,53 @@ +/* +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 { + constructor() { + super(undefined); + + this.on(UiComponentLifecycle.ShouldShowComponent, this.shouldShowComponent); + } + + protected shouldShowComponent: ShouldShowUiComponentListener = ( + shouldShowUiComponentOps: ShouldShowUiComponentOps, + component: UiComponent, + matrixUserId: string, + ) => { + 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); + }); +}); From 8b8ff2cced3e16378b0b96d93a85b347a6b4a59f Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Thu, 23 Mar 2023 14:58:38 +0300 Subject: [PATCH 2/8] Comments are added, readme updated. Signed-off-by: Mikhail Aheichyk --- README.md | 4 ++++ src/lifecycles/UiComponentLifecycle.ts | 13 +++++++++++++ 2 files changed, 17 insertions(+) diff --git a/README.md b/README.md index cfc32bb..9ff3285 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,10 @@ being raised for additional handling. The module can also change what room/user/entity the user is looking at, and join it (if it's a room), with `navigatePermalink` on a `ModuleApi` instance. +### 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/lifecycles/UiComponentLifecycle.ts b/src/lifecycles/UiComponentLifecycle.ts index afc0ee0..6fcda37 100644 --- a/src/lifecycles/UiComponentLifecycle.ts +++ b/src/lifecycles/UiComponentLifecycle.ts @@ -15,11 +15,24 @@ 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; }; From 67e56d76c9ff206615dbf9ae52c55e6c9008490e Mon Sep 17 00:00:00 2001 From: maheichyk Date: Thu, 15 Jun 2023 17:48:18 +0300 Subject: [PATCH 3/8] Readme update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Šimon Brandner --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9ff3285..fc78497 100644 --- a/README.md +++ b/README.md @@ -99,6 +99,7 @@ The module can also change what room/user/entity the user is looking at, and joi `navigatePermalink` on a `ModuleApi` instance. ### 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. From 0da55bfce3982c27e984f2cfb41ca7fbe7e11313 Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Thu, 15 Jun 2023 17:49:02 +0300 Subject: [PATCH 4/8] Conform to Typescript `strict` mode Signed-off-by: Mikhail Aheichyk --- test/lifecycles/UiComponentLifecycle.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/lifecycles/UiComponentLifecycle.test.ts b/test/lifecycles/UiComponentLifecycle.test.ts index 25232ec..7950446 100644 --- a/test/lifecycles/UiComponentLifecycle.test.ts +++ b/test/lifecycles/UiComponentLifecycle.test.ts @@ -26,7 +26,7 @@ describe("UiComponentLifecycle", () => { beforeAll(() => { module = new class extends RuntimeModule { constructor() { - super(undefined); + super(undefined as any); this.on(UiComponentLifecycle.ShouldShowComponent, this.shouldShowComponent); } From c9fe8635aba7b5b6558afda1b35b7dec4491e647 Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Thu, 15 Jun 2023 17:55:54 +0300 Subject: [PATCH 5/8] UiComponentLifecycle renamed Signed-off-by: Mikhail Aheichyk --- ...ntLifecycle.ts => UIComponentLifecycle.ts} | 12 +++++------ src/lifecycles/types.ts | 4 ++-- test/lifecycles/UiComponentLifecycle.test.ts | 20 +++++++++---------- 3 files changed, 18 insertions(+), 18 deletions(-) rename src/lifecycles/{UiComponentLifecycle.ts => UIComponentLifecycle.ts} (90%) diff --git a/src/lifecycles/UiComponentLifecycle.ts b/src/lifecycles/UIComponentLifecycle.ts similarity index 90% rename from src/lifecycles/UiComponentLifecycle.ts rename to src/lifecycles/UIComponentLifecycle.ts index 6fcda37..6a3de37 100644 --- a/src/lifecycles/UiComponentLifecycle.ts +++ b/src/lifecycles/UIComponentLifecycle.ts @@ -18,7 +18,7 @@ limitations under the License. /** * UI Component lifecycle events. */ -export enum UiComponentLifecycle { +export enum UIComponentLifecycle { /** * An event to check if component should be shown by the Element. */ @@ -28,7 +28,7 @@ export enum UiComponentLifecycle { /** * Opts object containing the result of whether the component should be shown or not. */ -export type ShouldShowUiComponentOps = { +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. @@ -39,13 +39,13 @@ export type ShouldShowUiComponentOps = { /** * Listener for "should_show_component" request */ -export type ShouldShowUiComponentListener = ( - shouldShowUiComponentOps: ShouldShowUiComponentOps, - component: UiComponent, +export type ShouldShowUIComponentListener = ( + shouldShowUIComponentOps: ShouldShowUIComponentOps, + component: UIComponent, matrixUserId: string ) => void; -export enum UiComponent { +export enum UIComponent { /** * Components that lead to a user being invited. */ diff --git a/src/lifecycles/types.ts b/src/lifecycles/types.ts index 5184b1f..1b3de72 100644 --- a/src/lifecycles/types.ts +++ b/src/lifecycles/types.ts @@ -16,10 +16,10 @@ limitations under the License. import { RoomViewLifecycle } from "./RoomViewLifecycle"; import { WidgetLifecycle } from "./WidgetLifecycle"; -import { UiComponentLifecycle } from "./UiComponentLifecycle"; +import { UIComponentLifecycle } from "./UIComponentLifecycle"; export type AnyLifecycle = | RoomViewLifecycle | WidgetLifecycle - | UiComponentLifecycle + | UIComponentLifecycle ; diff --git a/test/lifecycles/UiComponentLifecycle.test.ts b/test/lifecycles/UiComponentLifecycle.test.ts index 7950446..ac0d375 100644 --- a/test/lifecycles/UiComponentLifecycle.test.ts +++ b/test/lifecycles/UiComponentLifecycle.test.ts @@ -16,8 +16,8 @@ limitations under the License. */ import { - UiComponentLifecycle, ShouldShowUiComponentOps, ShouldShowUiComponentListener, UiComponent, -} from "../../src/lifecycles/UiComponentLifecycle"; + UIComponentLifecycle, ShouldShowUIComponentOps, ShouldShowUIComponentListener, UIComponent, +} from "../../src/lifecycles/UIComponentLifecycle"; import { RuntimeModule } from "../../src/RuntimeModule"; describe("UiComponentLifecycle", () => { @@ -28,26 +28,26 @@ describe("UiComponentLifecycle", () => { constructor() { super(undefined as any); - this.on(UiComponentLifecycle.ShouldShowComponent, this.shouldShowComponent); + this.on(UIComponentLifecycle.ShouldShowComponent, this.shouldShowComponent); } - protected shouldShowComponent: ShouldShowUiComponentListener = ( - shouldShowUiComponentOps: ShouldShowUiComponentOps, - component: UiComponent, + protected shouldShowComponent: ShouldShowUIComponentListener = ( + shouldShowUiComponentOps: ShouldShowUIComponentOps, + component: UIComponent, matrixUserId: string, ) => { - shouldShowUiComponentOps.shouldShowComponent = component !== UiComponent.CreateRooms; + shouldShowUiComponentOps.shouldShowComponent = component !== UIComponent.CreateRooms; }; }; }); it("should handle should_show_component request", () => { - const opts: ShouldShowUiComponentOps = {shouldShowComponent: undefined}; + const opts: ShouldShowUIComponentOps = {shouldShowComponent: undefined}; - module.emit(UiComponentLifecycle.ShouldShowComponent, opts, UiComponent.CreateRooms, "@user-id"); + module.emit(UIComponentLifecycle.ShouldShowComponent, opts, UIComponent.CreateRooms, "@user-id"); expect(opts.shouldShowComponent).toBe(false); - module.emit(UiComponentLifecycle.ShouldShowComponent, opts, UiComponent.ExploreRooms, "@user-id"); + module.emit(UIComponentLifecycle.ShouldShowComponent, opts, UIComponent.ExploreRooms, "@user-id"); expect(opts.shouldShowComponent).toBe(true); }); }); From c645bb1d867c08f440db05c1b6220db42bdc3771 Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Thu, 15 Jun 2023 17:59:52 +0300 Subject: [PATCH 6/8] UIComponent enum updated Signed-off-by: Mikhail Aheichyk --- src/lifecycles/UIComponentLifecycle.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/lifecycles/UIComponentLifecycle.ts b/src/lifecycles/UIComponentLifecycle.ts index 6a3de37..4d0e814 100644 --- a/src/lifecycles/UIComponentLifecycle.ts +++ b/src/lifecycles/UIComponentLifecycle.ts @@ -75,4 +75,14 @@ export enum UIComponent { * 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", } From 5f83dda8abe529a6ec66aaf4ca49175cb5b2bd40 Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Fri, 21 Jul 2023 10:03:53 +0300 Subject: [PATCH 7/8] Update ModuleApi to provide userId Signed-off-by: Mikhail Aheichyk --- src/ModuleApi.ts | 7 +++++++ src/lifecycles/UIComponentLifecycle.ts | 3 +-- test/lifecycles/UiComponentLifecycle.test.ts | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/ModuleApi.ts b/src/ModuleApi.ts index 65ea344..15a0fbc 100644 --- a/src/ModuleApi.ts +++ b/src/ModuleApi.ts @@ -117,4 +117,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 index 4d0e814..1ca706d 100644 --- a/src/lifecycles/UIComponentLifecycle.ts +++ b/src/lifecycles/UIComponentLifecycle.ts @@ -41,8 +41,7 @@ export type ShouldShowUIComponentOps = { */ export type ShouldShowUIComponentListener = ( shouldShowUIComponentOps: ShouldShowUIComponentOps, - component: UIComponent, - matrixUserId: string + component: UIComponent ) => void; export enum UIComponent { diff --git a/test/lifecycles/UiComponentLifecycle.test.ts b/test/lifecycles/UiComponentLifecycle.test.ts index ac0d375..9fc986f 100644 --- a/test/lifecycles/UiComponentLifecycle.test.ts +++ b/test/lifecycles/UiComponentLifecycle.test.ts @@ -34,7 +34,6 @@ describe("UiComponentLifecycle", () => { protected shouldShowComponent: ShouldShowUIComponentListener = ( shouldShowUiComponentOps: ShouldShowUIComponentOps, component: UIComponent, - matrixUserId: string, ) => { shouldShowUiComponentOps.shouldShowComponent = component !== UIComponent.CreateRooms; }; From 0d39f5d108d5b05148db3744a35252d2cba1efc0 Mon Sep 17 00:00:00 2001 From: Mikhail Aheichyk Date: Tue, 5 Sep 2023 17:16:02 +0300 Subject: [PATCH 8/8] Fix lint issues. Signed-off-by: Mikhail Aheichyk --- src/lifecycles/UIComponentLifecycle.ts | 4 ++-- test/lifecycles/UiComponentLifecycle.test.ts | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lifecycles/UIComponentLifecycle.ts b/src/lifecycles/UIComponentLifecycle.ts index 1ca706d..0766fe8 100644 --- a/src/lifecycles/UIComponentLifecycle.ts +++ b/src/lifecycles/UIComponentLifecycle.ts @@ -22,7 +22,7 @@ export enum UIComponentLifecycle { /** * An event to check if component should be shown by the Element. */ - ShouldShowComponent = "should_show_component" + ShouldShowComponent = "should_show_component", } /** @@ -41,7 +41,7 @@ export type ShouldShowUIComponentOps = { */ export type ShouldShowUIComponentListener = ( shouldShowUIComponentOps: ShouldShowUIComponentOps, - component: UIComponent + component: UIComponent, ) => void; export enum UIComponent { diff --git a/test/lifecycles/UiComponentLifecycle.test.ts b/test/lifecycles/UiComponentLifecycle.test.ts index 9fc986f..0c74713 100644 --- a/test/lifecycles/UiComponentLifecycle.test.ts +++ b/test/lifecycles/UiComponentLifecycle.test.ts @@ -16,7 +16,10 @@ limitations under the License. */ import { - UIComponentLifecycle, ShouldShowUIComponentOps, ShouldShowUIComponentListener, UIComponent, + UIComponentLifecycle, + ShouldShowUIComponentOps, + ShouldShowUIComponentListener, + UIComponent, } from "../../src/lifecycles/UIComponentLifecycle"; import { RuntimeModule } from "../../src/RuntimeModule"; @@ -24,8 +27,8 @@ describe("UiComponentLifecycle", () => { let module: RuntimeModule; beforeAll(() => { - module = new class extends RuntimeModule { - constructor() { + module = new (class extends RuntimeModule { + public constructor() { super(undefined as any); this.on(UIComponentLifecycle.ShouldShowComponent, this.shouldShowComponent); @@ -37,11 +40,11 @@ describe("UiComponentLifecycle", () => { ) => { shouldShowUiComponentOps.shouldShowComponent = component !== UIComponent.CreateRooms; }; - }; + })(); }); it("should handle should_show_component request", () => { - const opts: ShouldShowUIComponentOps = {shouldShowComponent: undefined}; + const opts: ShouldShowUIComponentOps = { shouldShowComponent: undefined }; module.emit(UIComponentLifecycle.ShouldShowComponent, opts, UIComponent.CreateRooms, "@user-id"); expect(opts.shouldShowComponent).toBe(false);