Skip to content

Commit

Permalink
Introduce new methods on ActionManager for low-level handling
Browse files Browse the repository at this point in the history
  • Loading branch information
tassoevan committed Oct 6, 2023
1 parent 0cfe0d9 commit 5b60c51
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 184 deletions.
89 changes: 50 additions & 39 deletions apps/meteor/app/ui-message/client/ActionManager.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { UiKit, DistributiveOmit } from '@rocket.chat/core-typings';
import { Emitter } from '@rocket.chat/emitter';
import { Random } from '@rocket.chat/random';
import type { ActionManagerContext } from '@rocket.chat/ui-contexts';
Expand Down Expand Up @@ -30,7 +31,7 @@ class ActionManager implements ActionManagerType {
return appId;
}

protected instances = new Map<string, { payload?: any; close: () => void }>();
protected viewInstances = new Map<string, { payload?: any; close: () => void }>();

public on = this.events.on.bind(this.events);

Expand All @@ -43,7 +44,33 @@ class ActionManager implements ActionManagerType {
return triggerId;
}

public handlePayloadUserInteraction(type: any, { triggerId, ...data }: any) {
public async emitInteraction(appId: string, userInteraction: DistributiveOmit<UiKit.UserInteraction, 'triggerId'>) {
this.events.emit('busy', { busy: true });

const triggerId = this.generateTriggerId(appId);

let timeout: ReturnType<typeof setTimeout> | undefined;

try {
return new Promise((resolve, reject) => {
timeout = setTimeout(() => reject(new UiKitTriggerTimeoutError('Timeout', { triggerId, appId })), ActionManager.TRIGGER_TIMEOUT);

sdk.rest
.post(`/apps/ui.interaction/${appId}`, {
...userInteraction,
triggerId,
})
.then(({ type, ...data }) => {
resolve(this.handlePayloadUserInteraction(type, data));
}, reject);
});
} finally {
if (timeout) clearTimeout(timeout);
this.events.emit('busy', { busy: false });
}
}

public handlePayloadUserInteraction(this: this, type: any, { triggerId, ...data }: any) {
if (!this.triggersId.has(triggerId)) {
return;
}
Expand Down Expand Up @@ -98,10 +125,10 @@ class ActionManager implements ActionManagerType {
},
});

this.instances.set(viewId, {
this.viewInstances.set(viewId, {
close: () => {
instance.close();
this.instances.delete(viewId);
this.viewInstances.delete(viewId);
},
});

Expand All @@ -125,7 +152,7 @@ class ActionManager implements ActionManagerType {
}

if (type === 'contextual_bar.open') {
this.instances.set(viewId, {
this.viewInstances.set(viewId, {
payload: {
type,
triggerId,
Expand All @@ -134,7 +161,7 @@ class ActionManager implements ActionManagerType {
...data,
},
close: () => {
this.instances.delete(viewId);
this.viewInstances.delete(viewId);
},
});

Expand All @@ -152,7 +179,7 @@ class ActionManager implements ActionManagerType {

if (type === 'banner.open') {
banners.open(data);
this.instances.set(viewId, {
this.viewInstances.set(viewId, {
close() {
banners.closeById(viewId);
},
Expand All @@ -162,24 +189,21 @@ class ActionManager implements ActionManagerType {
}

if (type === 'banner.close') {
const instance = this.instances.get(viewId);

if (instance) {
instance.close();
}
this.disposeView(viewId);
return 'banner.close';
}

if (type === 'contextual_bar.close') {
const instance = this.instances.get(viewId);

if (instance) {
instance.close();
}
this.disposeView(viewId);
return 'contextual_bar.close';
}

return 'modal.close';
if (type === 'modal.close') {
this.disposeView(viewId);
return 'modal.close';
}

throw new Error(`Unknown type: ${type}`);
}

public async triggerAction({ type, appId, ...rest }: Parameters<ActionManagerType['triggerAction']>[0]) {
Expand Down Expand Up @@ -210,17 +234,9 @@ class ActionManager implements ActionManagerType {
}
}

public async triggerBlockAction(options: Parameters<ActionManagerType['triggerBlockAction']>[0]): Promise<void> {
await this.triggerAction({ type: 'blockAction', ...options });
}

public async triggerActionButtonAction(options: Parameters<ActionManagerType['triggerActionButtonAction']>[0]): Promise<void> {
await this.triggerAction({ type: 'actionButton', ...options });
}

public async triggerSubmitView({ viewId, ...options }: any): Promise<void> {
const close = () => {
const instance = this.instances.get(viewId);
const instance = this.viewInstances.get(viewId);

if (instance) {
instance.close();
Expand All @@ -241,30 +257,25 @@ class ActionManager implements ActionManagerType {
}
}

public async triggerCancel({ view, ...options }: any): Promise<void> {
const instance = this.instances.get(view.id);
try {
await this.triggerAction({ type: 'viewClosed', view, ...options });
} finally {
if (instance) {
instance.close();
}
}
}

public getUserInteractionPayloadByViewId(viewId: string) {
if (!viewId) {
throw new Error('No viewId provided when checking for `user interaction payload`');
}

const instance = this.instances.get(viewId);
const instance = this.viewInstances.get(viewId);

if (!instance) {
return undefined;
}

return instance.payload;
}

public disposeView(viewId: UiKit.View['viewId']) {
const instance = this.viewInstances.get(viewId);
instance?.close?.();
this.viewInstances.delete(viewId);
}
}

/** @deprecated consumer should use the context instead */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@ const UiKitMessageBlock = ({ rid, mid, blocks }: UiKitMessageBlockProps): ReactE
}
}

actionManager?.triggerBlockAction({
actionManager.emitInteraction(appId, {
type: 'blockAction',
actionId,
appId,
container: {
type: 'message',
id: mid,
Expand Down
12 changes: 6 additions & 6 deletions apps/meteor/client/hooks/useAppActionButtons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,11 @@ export const useMessageboxAppsActionButtons = () => {
label: Utilities.getI18nKeyForApp(action.labelI18n, action.appId),
action: (params) => {
void actionManager
.triggerActionButtonAction({
.emitInteraction(action.appId, {
type: 'actionButton',
rid: params.rid,
tmid: params.tmid,
actionId: action.actionId,
appId: action.appId,
payload: { context: action.context, message: params.chat.composer?.text },
})
.catch(async (reason) => {
Expand Down Expand Up @@ -133,9 +133,9 @@ export const useUserDropdownAppsActionButtons = () => {
content: action.labelI18n,
onClick: () => {
void actionManager
.triggerActionButtonAction({
.emitInteraction(action.appId, {
type: 'actionButton',
actionId: action.actionId,
appId: action.appId,
payload: { context: action.context },
})
.catch(async (reason) => {
Expand Down Expand Up @@ -188,12 +188,12 @@ export const useMessageActionAppsActionButtons = (context?: MessageActionContext
variant: action.variant,
action: (_, params) => {
void actionManager
.triggerActionButtonAction({
.emitInteraction(action.appId, {
type: 'actionButton',
rid: params.message.rid,
tmid: params.message.tmid,
mid: params.message._id,
actionId: action.actionId,
appId: action.appId,
payload: { context: action.context },
})
.catch(async (reason) => {
Expand Down
43 changes: 23 additions & 20 deletions apps/meteor/client/views/banners/UiKitBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import React, { useMemo } from 'react';
import { useUiKitActionManager } from '../../UIKit/hooks/useUiKitActionManager';
import { useUiKitView } from '../../UIKit/hooks/useUiKitView';
import MarkdownText from '../../components/MarkdownText';
import * as banners from '../../lib/banners';

// TODO: move this to fuselage-ui-kit itself
bannerParser.mrkdwn = ({ text }): ReactElement => <MarkdownText variant='inline' content={text} />;
Expand All @@ -31,49 +30,53 @@ const UiKitBanner = ({ initialView }: UiKitBannerProps) => {
}, [view.icon]);

const dispatchToastMessage = useToastMessageDispatch();
const handleClose = useMutableCallback(() =>
actionManager
.triggerCancel({
appId: view.appId,
viewId: view.viewId,
view: {
...view,
id: view.viewId,
const handleClose = useMutableCallback(() => {
void actionManager
.emitInteraction(view.appId, {
type: 'viewClosed',
payload: {
view: {
...view,
id: view.viewId,
},
isCleared: true,
},
isCleared: true,
})
.then(() => banners.close())
.catch((error) => {
dispatchToastMessage({ type: 'error', message: error });
banners.close();
return Promise.reject(error);
}),
);
})
.finally(() => {
actionManager.disposeView(view.viewId);
});
});

const actionManager = useUiKitActionManager();

const contextValue = useMemo(
(): ContextType<typeof UiKitContext> => ({
action: async ({ appId, viewId, actionId }): Promise<void> => {
action: async ({ appId, viewId, actionId }) => {
if (!appId || !viewId) {
return;
}

await actionManager.triggerBlockAction({
await actionManager.emitInteraction(appId, {
type: 'blockAction',
actionId,
container: {
type: 'view',
id: viewId,
},
actionId,
appId,
payload: view,
});
banners.closeById(view.viewId);

actionManager.disposeView(view.viewId);
},
state: (): void => undefined,
appId: view.appId,
values,
}),
[actionManager, view.appId, view.viewId, values],
[view, values, actionManager],
);

return (
Expand Down
Loading

0 comments on commit 5b60c51

Please sign in to comment.