From 5dcf8267bda69d91c63dd01847a55d1f80959ab9 Mon Sep 17 00:00:00 2001 From: Bob <160986752+bob2402@users.noreply.github.com> Date: Sat, 29 Jun 2024 14:58:41 +0800 Subject: [PATCH] Modal Component (#465) --- app/UI/app.tsx | 13 +++++++ app/UI/app_update.tsx | 13 +++++-- app/UI/components/modal.test.tsx | 59 ++++++++++++++++++++++++++++++++ app/UI/components/modal.tsx | 59 ++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 app/UI/components/modal.test.tsx create mode 100644 app/UI/components/modal.tsx diff --git a/app/UI/app.tsx b/app/UI/app.tsx index 0aea7f3..441220d 100644 --- a/app/UI/app.tsx +++ b/app/UI/app.tsx @@ -33,6 +33,7 @@ import { getTags, Parsed_Event } from "../nostr.ts"; import { Toast } from "./components/toast.tsx"; import { ToastChannel } from "./components/toast.tsx"; import { RightPanelChannel } from "./components/right-panel.tsx"; +import { Modal, ModalInputChannel } from "./components/modal.tsx"; import { func_IsAdmin } from "./message-list.tsx"; import { getRelayInformation } from "../../libs/nostr.ts/nip11.ts"; @@ -52,6 +53,7 @@ export async function Start(database: DexieDatabase) { const eventBus = new EventBus(); const popOverInputChan: PopOverInputChannel = new Channel(); const rightPanelInputChan: RightPanelChannel = new Channel(); + const modalInputChan: ModalInputChannel = new Channel(); const toastInputChan: ToastChannel = new Channel(); const dbView = await Database_View.New(database, database, database); @@ -74,6 +76,7 @@ export async function Start(database: DexieDatabase) { pool: new ConnectionPool({ signer: ctx }), popOverInputChan, rightPanelInputChan, + modalInputChan, otherConfig, lamport, installPrompt, @@ -91,6 +94,7 @@ export async function Start(database: DexieDatabase) { model={model} popOverInputChan={popOverInputChan} rightPanelInputChan={rightPanelInputChan} + modalInputChan={modalInputChan} installPrompt={installPrompt} toastInputChan={toastInputChan} />, @@ -104,6 +108,7 @@ export async function Start(database: DexieDatabase) { dbView: dbView, popOver: popOverInputChan, rightPanel: rightPanelInputChan, + modal: modalInputChan, lamport, installPrompt, toastInputChan: toastInputChan, @@ -117,6 +122,7 @@ export async function Start(database: DexieDatabase) { model={model} popOverInputChan={popOverInputChan} rightPanelInputChan={rightPanelInputChan} + modalInputChan={modalInputChan} installPrompt={installPrompt} toastInputChan={toastInputChan} />, @@ -136,6 +142,7 @@ export class App { public readonly pool: ConnectionPool, public readonly popOverInputChan: PopOverInputChannel, public readonly rightPanelInputChan: RightPanelChannel, + public readonly modalInputChan: ModalInputChannel, public readonly otherConfig: OtherConfig, public readonly conversationLists: DM_List, public readonly relayConfig: RelayConfig, @@ -152,6 +159,7 @@ export class App { pool: ConnectionPool; popOverInputChan: PopOverInputChannel; rightPanelInputChan: RightPanelChannel; + modalInputChan: ModalInputChannel; otherConfig: OtherConfig; lamport: LamportTime; installPrompt: InstallPrompt; @@ -247,6 +255,7 @@ export class App { args.pool, args.popOverInputChan, args.rightPanelInputChan, + args.modalInputChan, args.otherConfig, conversationLists, relayConfig, @@ -274,6 +283,7 @@ export class App { model={this.model} popOverInputChan={this.popOverInputChan} rightPanelInputChan={this.rightPanelInputChan} + modalInputChan={this.modalInputChan} installPrompt={installPrompt} toastInputChan={this.toastInputChan} />, @@ -305,6 +315,7 @@ export class App { model={this.model} popOverInputChan={this.popOverInputChan} rightPanelInputChan={this.rightPanelInputChan} + modalInputChan={this.modalInputChan} installPrompt={installPrompt} toastInputChan={this.toastInputChan} />, @@ -326,6 +337,7 @@ type AppProps = { eventBus: AppEventBus; popOverInputChan: PopOverInputChannel; rightPanelInputChan: RightPanelChannel; + modalInputChan: ModalInputChannel; toastInputChan: ToastChannel; installPrompt: InstallPrompt; }; @@ -495,6 +507,7 @@ export class AppComponent extends Component + ); diff --git a/app/UI/app_update.tsx b/app/UI/app_update.tsx index 29423a6..fb7bf25 100644 --- a/app/UI/app_update.tsx +++ b/app/UI/app_update.tsx @@ -62,7 +62,7 @@ import { SyncEvent } from "./message-panel.tsx"; import { SendingEventRejection, ToastChannel } from "./components/toast.tsx"; import { SingleRelayConnection } from "../../libs/nostr.ts/relay-single.ts"; import { default_blowater_relay } from "./relay-config.ts"; -import { forever, setState } from "./_helper.ts"; +import { forever } from "./_helper.ts"; import { DeleteEvent, func_GetEventByID } from "./message-list.tsx"; import { FilterContent } from "./filter.tsx"; import { CloseRightPanel } from "./components/right-panel.tsx"; @@ -70,7 +70,7 @@ import { RightPanelChannel } from "./components/right-panel.tsx"; import { ReplyToMessage } from "./message-list.tsx"; import { EditorSelectProfile } from "./editor.tsx"; import { uploadFile } from "../../libs/nostr.ts/nip96.ts"; -import * as csp from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts"; +import { HideModal, ModalInputChannel } from "./components/modal.tsx"; export type UI_Interaction_Event = | SearchUpdate @@ -92,6 +92,7 @@ export type UI_Interaction_Event = | UnblockUser | SelectSpace | HidePopOver + | HideModal | SyncEvent | FilterContent | CloseRightPanel @@ -121,6 +122,7 @@ export function UI_Interaction_Update(args: { dbView: Database_View; popOver: PopOverInputChannel; rightPanel: RightPanelChannel; + modal: ModalInputChannel; lamport: LamportTime; installPrompt: InstallPrompt; toastInputChan: ToastChannel; @@ -136,6 +138,7 @@ const handle_update_event = async (chan: PutChannel, args: { dbView: Database_View; popOver: PopOverInputChannel; rightPanel: RightPanelChannel; + modal: ModalInputChannel; lamport: LamportTime; installPrompt: InstallPrompt; toastInputChan: ToastChannel; @@ -161,6 +164,7 @@ const handle_update_event = async (chan: PutChannel, args: { pool, popOverInputChan: args.popOver, rightPanelInputChan: args.rightPanel, + modalInputChan: args.modal, otherConfig, lamport: args.lamport, installPrompt, @@ -210,6 +214,11 @@ const handle_update_event = async (chan: PutChannel, args: { /> ); args.popOver.put({ children: search }); + } else if (event.type == "HideModal") { + await app.modalInputChan.put({ + children: undefined, + onClose: event.onClose, + }); } // // // Setting diff --git a/app/UI/components/modal.test.tsx b/app/UI/components/modal.test.tsx new file mode 100644 index 0000000..5a986f3 --- /dev/null +++ b/app/UI/components/modal.test.tsx @@ -0,0 +1,59 @@ +/** @jsx h */ +import { h, render } from "https://esm.sh/preact@10.17.1"; +import { HideModal, Modal, ModalInputChannel } from "./modal.tsx"; +import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts"; +import { CenterClass } from "./tw.ts"; +import { testEventBus } from "../_setup.test.ts"; +import { emitFunc } from "../../event-bus.ts"; + +const modalChan: ModalInputChannel = new Channel(); + +function ModalTest(props: { + emit: emitFunc; +}) { + return ( +
+ +
+ ), + onClose() { + console.log("close the modal by click"); + }, + }); + }} + > + Show + + + + ); +} + +render(, document.body); + +for await (const event of testEventBus.onChange()) { + if (event.type === "HideModal") { + await modalChan.put({ children: undefined, onClose: event.onClose }); + } +} diff --git a/app/UI/components/modal.tsx b/app/UI/components/modal.tsx new file mode 100644 index 0000000..4d66597 --- /dev/null +++ b/app/UI/components/modal.tsx @@ -0,0 +1,59 @@ +/** @jsx h */ +import { Component, ComponentChildren, h } from "https://esm.sh/preact@10.17.1"; +import { Channel } from "https://raw.githubusercontent.com/BlowaterNostr/csp/master/csp.ts"; +import { setState } from "../_helper.ts"; + +export type HideModal = { + type: "HideModal"; + onClose?: () => void; +}; + +type State = { + show: boolean; +}; + +export type ModalInputChannel = Channel<{ children: ComponentChildren; onClose?: () => void }>; +export class Modal extends Component<{ + inputChan: ModalInputChannel; +}, State> { + state: State = { show: false }; + children: ComponentChildren = undefined; + onClose?: () => void; + + async componentDidMount() { + for await (const { children, onClose } of this.props.inputChan) { + this.onClose = onClose; + if (children) { + await this.show(children); + } else { + await this.hide(); + } + } + } + + show = async (children: ComponentChildren) => { + this.children = children; + await setState(this, { show: true }); + }; + + hide = async () => { + await setState(this, { show: false }); + if (this.onClose) this.onClose(); + }; + + render() { + if (!this.state.show) return; + return ( +
+
+
+
+ {this.children} +
+
+ ); + } +}