-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor useExecuteCode to leverage atomCallback (#814)
Co-authored-by: anserwaseem <[email protected]>
- Loading branch information
1 parent
f466659
commit b42978c
Showing
17 changed files
with
687 additions
and
202 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
"@ensembleui/react-framework": patch | ||
"@ensembleui/react-kitchen-sink": patch | ||
"@ensembleui/react-runtime": patch | ||
--- | ||
|
||
Optimize dependencies for useExecuteCode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { has, set } from "lodash-es"; | ||
import type { Setter } from "jotai"; | ||
import { DataFetcher, type WebSocketConnection, type Response } from "../data"; | ||
import { error } from "../shared"; | ||
import type { | ||
EnsembleActionHookResult, | ||
EnsembleMockResponse, | ||
EnsembleSocketModel, | ||
} from "../shared"; | ||
import { screenDataAtom, type ScreenContextDefinition } from "../state"; | ||
import { isUsingMockResponse } from "../appConfig"; | ||
import { mockResponse } from "../evaluate/mock"; | ||
|
||
export const invokeAPI = async ( | ||
apiName: string, | ||
screenContext: ScreenContextDefinition, | ||
apiInputs?: { [key: string]: unknown }, | ||
context?: { [key: string]: unknown }, | ||
evaluatedMockResponse?: string | EnsembleMockResponse, | ||
setter?: Setter, | ||
): Promise<Response | undefined> => { | ||
const api = screenContext.model?.apis?.find( | ||
(model) => model.name === apiName, | ||
); | ||
|
||
if (!api) { | ||
error(`Unable to find API with name ${apiName}`); | ||
return; | ||
} | ||
|
||
const update = {}; | ||
if (setter) { | ||
// Now, because the API exists, set its state to loading | ||
set(update, api.name, { | ||
isLoading: true, | ||
isError: false, | ||
isSuccess: false, | ||
}); | ||
setter(screenDataAtom, update); | ||
} | ||
|
||
// If mock resposne does not exist, fetch the data directly from the API | ||
const useMockResponse = | ||
has(api, "mockResponse") && isUsingMockResponse(screenContext.app?.id); | ||
const res = await DataFetcher.fetch( | ||
api, | ||
{ ...apiInputs, ...context }, | ||
{ | ||
mockResponse: mockResponse( | ||
evaluatedMockResponse ?? api.mockResponse, | ||
useMockResponse, | ||
), | ||
useMockResponse, | ||
}, | ||
); | ||
|
||
if (setter) { | ||
set(update, api.name, res); | ||
setter(screenDataAtom, { ...update }); | ||
} | ||
return res; | ||
}; | ||
|
||
export const handleConnectSocket = ( | ||
socketName: string, | ||
screenContext: ScreenContextDefinition, | ||
onOpen?: EnsembleActionHookResult, | ||
onMessage?: EnsembleActionHookResult, | ||
onClose?: EnsembleActionHookResult, | ||
setter?: Setter, | ||
): WebSocketConnection | undefined => { | ||
const socket = findSocket(socketName, screenContext); | ||
if (!socket) { | ||
error(`Unable to find socket ${socketName}`); | ||
return; | ||
} | ||
|
||
// check the socket is already connected | ||
const prevSocketConnection = screenContext.data[socket.name] as | ||
| WebSocketConnection | ||
| undefined; | ||
|
||
if (prevSocketConnection?.isConnected) { | ||
return prevSocketConnection; | ||
} | ||
|
||
const ws = new WebSocket(socket.uri); | ||
|
||
if (onOpen?.callback) { | ||
ws.onopen = (): unknown => onOpen.callback(); | ||
} | ||
|
||
if (onMessage?.callback) { | ||
ws.onmessage = (e: MessageEvent): unknown => | ||
onMessage.callback({ data: e.data as unknown }); | ||
} | ||
|
||
if (onClose?.callback) { | ||
ws.onclose = (): unknown => onClose.callback(); | ||
} | ||
|
||
if (setter) { | ||
const update = {}; | ||
set(update, socket.name, { socket: ws, isConnected: true }); | ||
setter(screenDataAtom, update); | ||
} | ||
|
||
return { socket: ws, isConnected: true }; | ||
}; | ||
|
||
export const handleMessageSocket = ( | ||
socketName: string, | ||
message: { [key: string]: unknown }, | ||
screenContext: ScreenContextDefinition, | ||
): void => { | ||
const socket = findSocket(socketName, screenContext); | ||
if (!socket) { | ||
error(`Unable to find socket ${socketName}`); | ||
return; | ||
} | ||
|
||
const socketInstance = screenContext.data[socket.name] as WebSocketConnection; | ||
if (socketInstance.isConnected) { | ||
socketInstance.socket?.send(JSON.stringify(message)); | ||
} | ||
}; | ||
|
||
export const handleDisconnectSocket = ( | ||
socketName: string, | ||
screenContext: ScreenContextDefinition, | ||
setter?: Setter, | ||
): void => { | ||
const socket = findSocket(socketName, screenContext); | ||
if (!socket) { | ||
error(`Unable to find socket ${socketName}`); | ||
return; | ||
} | ||
|
||
const socketInstance = screenContext.data[socket.name] as WebSocketConnection; | ||
if (socketInstance.isConnected) { | ||
socketInstance.socket?.close(); | ||
if (setter) { | ||
const update = {}; | ||
set(update, socket.name, { isConnected: false }); | ||
setter(screenDataAtom, update); | ||
} | ||
} | ||
}; | ||
|
||
const findSocket = ( | ||
socketName: string, | ||
screenContext: ScreenContextDefinition, | ||
): EnsembleSocketModel | undefined => | ||
screenContext.model?.sockets?.find((model) => model.name === socketName); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from "./data"; | ||
export * from "./navigate"; | ||
export * from "./modal"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import { cloneDeep, isObject } from "lodash-es"; | ||
import type { ReactNode } from "react"; | ||
import { unwrapWidget } from "../parser"; | ||
import type { | ||
ShowDialogAction, | ||
EnsembleWidget, | ||
ShowDialogOptions, | ||
} from "../shared"; | ||
|
||
export interface ModalProps { | ||
title?: string | React.ReactNode; | ||
maskClosable?: boolean; | ||
mask?: boolean; | ||
hideCloseIcon?: boolean; | ||
hideFullScreenIcon?: boolean; | ||
onClose?: () => void; | ||
position?: "top" | "right" | "bottom" | "left" | "center"; | ||
height?: string; | ||
width?: string | number; | ||
margin?: string; | ||
padding?: string; | ||
backgroundColor?: string; | ||
horizontalOffset?: number; | ||
verticalOffset?: number; | ||
showShadow?: boolean; | ||
} | ||
|
||
export interface ModalContext { | ||
openModal: ( | ||
content: React.ReactNode, | ||
options: ModalProps, | ||
isDialog?: boolean, | ||
context?: { [key: string]: unknown }, | ||
) => void; | ||
closeAllModals: () => void; | ||
navigateBack: () => void; | ||
} | ||
|
||
export interface ShowDialogApiProps { | ||
action?: ShowDialogAction; | ||
openModal?: (...args: any[]) => void; | ||
render?: (widgets: EnsembleWidget[]) => ReactNode[]; | ||
} | ||
|
||
export const showDialog = (props?: ShowDialogApiProps): void => { | ||
const { action, openModal, render } = props ?? {}; | ||
if (!action || !openModal || (!action.widget && !action.body)) { | ||
return; | ||
} | ||
|
||
const widget = action.widget ?? action.body; | ||
|
||
const content = widget?.name | ||
? (cloneDeep(widget) as unknown as EnsembleWidget) | ||
: unwrapWidget(cloneDeep(widget!)); | ||
|
||
openModal( | ||
render?.([content]), | ||
getShowDialogOptions(action.options), | ||
true, | ||
screen || undefined, | ||
); | ||
}; | ||
|
||
export const getShowDialogOptions = ( | ||
options?: ShowDialogOptions, | ||
onClose?: () => void, | ||
): ShowDialogOptions => { | ||
const noneStyleOption = { | ||
backgroundColor: "transparent", | ||
showShadow: false, | ||
}; | ||
|
||
const dialogOptions = { | ||
maskClosable: true, | ||
hideCloseIcon: true, | ||
hideFullScreenIcon: true, | ||
verticalOffset: options?.verticalOffset, | ||
horizontalOffset: options?.horizontalOffset, | ||
padding: "12px", | ||
onClose, | ||
...(options?.style === "none" ? noneStyleOption : {}), | ||
...(isObject(options) ? options : {}), | ||
}; | ||
|
||
return dialogOptions; | ||
}; |
Oops, something went wrong.