Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@open-core/framework",
"version": "1.0.10",
"version": "1.0.11",
"description": "Secure, event-driven TypeScript Framework & Runtime engine for CitizenFX (Cfx).",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
23 changes: 21 additions & 2 deletions src/runtime/client/decorators/onView.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
import { METADATA_KEYS } from '../system/metadata-client.keys'

export interface OnViewOptions {
viewId?: string
}

/**
* Registers a method as a WebView callback handler.
*
* @remarks
* This decorator only stores metadata. During bootstrap, the framework binds the decorated method
* to the active WebView runtime callback.
*
* If `options.viewId` is provided, the handler will only receive messages from the
* specified WebView viewId. Without it, the handler receives events from all views.
*
* @param eventName - Callback name.
* @param options - Optional configuration. Pass `{ viewId: 'my-view' }` to
* filter incoming messages by viewId.
*
* @example
* ```ts
Expand All @@ -17,11 +26,21 @@ import { METADATA_KEYS } from '../system/metadata-client.keys'
* saveSettings(payload: unknown) {
* // ...
* }
*
* @Client.onView('system-ui:ready', { viewId: 'system-ui' })
* onSystemUiReady() {
* // Only fires for the 'system-ui' WebView
* }
* }
* ```
*/
export function OnView(eventName: string) {
export function OnView(eventName: string, options?: OnViewOptions) {
return (target: any, propertyKey: string) => {
Reflect.defineMetadata(METADATA_KEYS.VIEW, { eventName }, target, propertyKey)
Reflect.defineMetadata(
METADATA_KEYS.VIEW,
{ eventName, viewId: options?.viewId },
target,
propertyKey,
)
}
}
7 changes: 5 additions & 2 deletions src/runtime/client/system/processors/view.processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ export class ViewProcessor implements DecoratorProcessor {

constructor(@inject(WebViewService as any) private readonly webviews: WebViewService) {}

process(target: any, methodName: string, metadata: { eventName: string }) {
process(target: any, methodName: string, metadata: { eventName: string; viewId?: string }) {
const handler = target[methodName].bind(target)
const handlerName = `${target.constructor.name}.${methodName}`

this.webviews.onMessage(async (message) => {
if (message.event !== metadata.eventName) return
if (metadata.viewId && message.viewId !== metadata.viewId) return
try {
await handler(message.payload)
} catch (error) {
Expand All @@ -30,6 +31,8 @@ export class ViewProcessor implements DecoratorProcessor {
}
})

loggers.webView.debug(`Registered WebView callback: ${metadata.eventName} -> ${handlerName}`)
loggers.webView.debug(
`Registered WebView callback: ${metadata.eventName}${metadata.viewId ? ` [viewId=${metadata.viewId}]` : ''} -> ${handlerName}`,
)
}
}
7 changes: 7 additions & 0 deletions src/runtime/client/webview-bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,3 +117,10 @@ function resolveWebViewService(): WebViewService {

export const WebView = new WebViewBridge(resolveWebViewService)
export const NUI = WebView

export function createWebView<
TSend extends Record<string, any> = Record<string, any>,
TReceive extends Record<string, any> = Record<string, any>,
>(viewId: string): WebViewBridge<TSend, TReceive> {
return new WebViewBridge(resolveWebViewService, viewId)
}
Loading