UNPKG

reblock

Version:

Build interactive Slack surfaces with React

166 lines (165 loc) 4.07 kB
import { createContainer, render, Root } from '../renderer' import { jsxToBlocks } from '../jsx/blocks' import { activeModals, activeRoots, ensureEventRegistered } from '../events' import { blocks } from './blocks' export class ModalRoot extends Root { client args modalArgs onSubmit onClose resolve reject viewID existingViewID get isOpen() { return !!this.viewID } constructor(client, args, modalArgs, onSubmit, onClose, resolve, reject) { super() this.client = client this.args = args this.modalArgs = modalArgs this.onSubmit = onSubmit this.onClose = onClose this.resolve = resolve this.reject = reject } async publish() { try { activeRoots.add(this) const children = this.getChildren() const view = { ...this.modalArgs, type: 'modal', blocks: jsxToBlocks(children)[0], notify_on_close: true, callback_id: 'reblock', } if (!this.viewID) { this.viewID = (async () => { const result = await this.client.views.open({ ...this.args, view, }) if (!result.ok || !result.view?.id) { const error = new Error('Failed to open modal') if (this.reject) { this.reject(error) } throw error } if (this.resolve) { this.resolve(result.view.id) } this.existingViewID = result.view.id activeModals.set(result.view.id, this) return result.view.id })() return } const viewID = await this.viewID await this.client.views.update({ view_id: viewID, view, }) } catch (error) { if (this.reject) { this.reject(error) } console.error(error) } } async submit(event) { this.stopRendering() activeRoots.delete(this) if (this.existingViewID) activeModals.delete(this.existingViewID) if (this.onSubmit) this.onSubmit(event) } async close(event) { this.stopRendering() activeRoots.delete(this) if (this.existingViewID) activeModals.delete(this.existingViewID) if (this.onClose) this.onClose(event) } handle = new ModalHandle(this) } export class ModalHandle { root constructor(root) { this.root = root } get viewID() { return this.root.existingViewID } get isOpen() { return this.root.isOpen } get rendering() { return this.root.rendering } async stop(behavior = 'clear') { this.root.stopRendering() activeRoots.delete(this.root) const viewID = await this.root.viewID if (viewID) activeModals.delete(viewID) if (!viewID) return if (behavior === 'keep') return if (behavior === 'clear') { await this.root.client.views.update({ view_id: viewID, view: { ...this.root.modalArgs, type: 'modal', blocks: [], }, }) return } const finalBlocks = behavior === 'clear' ? [] : blocks(behavior) await this.root.client.views.update({ view_id: viewID, view: { ...this.root.modalArgs, type: 'modal', blocks: finalBlocks, }, }) } } export async function modal( app, argsOrId, titleOrModalArgs, element, // eslint-disable-next-line @typescript-eslint/no-explicit-any onSubmit, onClose ) { ensureEventRegistered(app) let resolve = undefined let reject = undefined const promise = new Promise((res, rej) => { resolve = res reject = rej }) const args = typeof argsOrId === 'string' ? { trigger_id: argsOrId } : argsOrId const modalArgs = typeof titleOrModalArgs === 'string' ? { title: { type: 'plain_text', text: titleOrModalArgs } } : titleOrModalArgs const root = new ModalRoot( app.client, args, modalArgs, onSubmit, onClose ?? onSubmit, resolve, reject ) const container = createContainer(root) render(element, container) await promise return root.handle }