From c452458812ee7c4cebbb669e4c70076bdf3145f1 Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Mon, 10 Jul 2023 20:12:35 +0100 Subject: [PATCH] experiments(dui3): extra cleanup and comments around --- packages/dui3/lib/bridge/sketchup.ts | 54 ++++++++++++++++++---------- packages/dui3/plugins/00.bindings.ts | 33 ++++++++--------- packages/dui3/types/index.ts | 3 +- yarn.lock | 8 +++++ 4 files changed, 60 insertions(+), 38 deletions(-) diff --git a/packages/dui3/lib/bridge/sketchup.ts b/packages/dui3/lib/bridge/sketchup.ts index 5d3fce19d..7342a127b 100644 --- a/packages/dui3/lib/bridge/sketchup.ts +++ b/packages/dui3/lib/bridge/sketchup.ts @@ -1,12 +1,15 @@ // TODO -import { rejects } from 'assert' import { uniqueId } from 'lodash-es' -import { resolve } from 'path' declare let sketchup: { exec: (data: Record) => void } +/** + * This class operates in different way than the others, because calls into Sketchup are one way only. + * E.g., we cannot return values from internal calls to it (e.g., const test = sketchup.rubyCall() does not work ). + * Values are passed back + */ export class SketchupBridge { private requests = {} as Record< string, @@ -19,38 +22,51 @@ export class SketchupBridge { private bindingsName: string private TIMEOUT_MS = 2000 // 2s public isInitalized: Promise - private isInitializedResolved!: () => unknown + private resolveIsInitializedPromise!: () => unknown constructor(bindingsName: string) { - // window.sketchup this.bindingsName = bindingsName || 'default_bindings' + // Initialization continues in the receiveCommandsAndInitializeBridge function, + // where we expect sketchup to return to us the command names. sketchup.exec({ name: 'get_commands' }) - // Initialization continues in the receiveCommandsAndInitializeBridge function this.isInitalized = new Promise((resolve, reject) => { - // TODO - this.isInitializedResolved = resolve + this.resolveIsInitializedPromise = resolve + setTimeout( + () => + reject( + `Failed to get command names from Sketchup; timed out after ${this.TIMEOUT_MS}ms.` + ), + this.TIMEOUT_MS + ) }) } - // executeScript(...) from skp + /** + * Will be called by `executeScript('bindings.receiveCommandsAndInitializeBridge()')` from sketchup. This is where the hoisting happens. + * NOTE: Oguhzan, we can defintively have commandNames be a string, and not a string[] + * And do JSON.parse() here to get them out properly. + * @param commandNames + */ private receiveCommandsAndInitializeBridge(commandNames: string[]) { const hoistTarget = this as unknown as Record - for (const commandName of commandNames) { hoistTarget[commandName] = (...args: unknown[]) => this.runMethod(commandName, args) } - // this.isInitalized = true - this.isInitializedResolved() + this.resolveIsInitializedPromise() } + /** + * Internal calls to Sketchup. + * @param methodName + * @param args + */ private async runMethod(methodName: string, args: unknown[]): Promise { const requestId = uniqueId(this.bindingsName) - // The single exec way sketchup.exec({ name: methodName, requestId, args }) return new Promise((resolve, reject) => { @@ -59,22 +75,22 @@ export class SketchupBridge { reject, rejectTimerId: window.setTimeout(() => { reject( - 'Sketchup response timed out - did not receive anything back in good time.' + `Sketchup response timed out - did not receive anything back in good time (${this.TIMEOUT_MS}ms).` ) - // TODO: clear request from requests object + delete this.requests[requestId] }, this.TIMEOUT_MS) } }) } private receiveResponse(requestId: string, data: string) { - // TODO - if (!this.requests[requestId]) return // throw new error? + if (!this.requests[requestId]) + throw new Error( + `Sketchup Bridge found no request to resolve with the id of ${requestId}. Something is weird!` + ) const request = this.requests[requestId] try { - // TODO: resolve also if data is null, it means it's a - // 'void' function call (does not return anything) - const parsedData = JSON.parse(data) as Record + const parsedData = JSON.parse(data) as Record // TODO: check if data is undefined request.resolve(parsedData) } catch (e) { request.reject(e as Error) diff --git a/packages/dui3/plugins/00.bindings.ts b/packages/dui3/plugins/00.bindings.ts index c7407e75c..1eea200be 100644 --- a/packages/dui3/plugins/00.bindings.ts +++ b/packages/dui3/plugins/00.bindings.ts @@ -30,7 +30,9 @@ export default defineNuxtPlugin(async () => { console.info('Bound WebUIBinding object for CefSharp.') bindings = new CefSharpBridge(WebUIBinding) as unknown as IWebUiBinding } catch (e) { - console.warn('Failed to bind CefSharp.') + console.warn( + 'Failed to bind CefSharp. This can be totally normal if the host is different.' + ) console.warn(e) } @@ -38,24 +40,26 @@ export default defineNuxtPlugin(async () => { if (!chrome.webview) throw new Error('No global Webview2 object found.') bindings = new WebView2Bridge('WebUIBinding') as unknown as IWebUiBinding console.info('Bound WebUIBinding object for Webview2.') - - const res = await bindings.sayHi('Test') - console.log(res) } catch (e) { - console.warn('Failed to bind Webview2.') + console.warn( + 'Failed to bind Webview2. This can be totally normal if the host is different.' + ) console.warn(e) } try { if (!sketchup) throw new Error('No global sketchup object found.') console.info('Found Sketchup. Hi SketchUp! We have yet... a lot of work to do :) ') - // TODO - const skpBindings = new SketchupBridge('default_bindings') - await skpBindings.isInitalized // resolve({...}) + const skpBindings = new SketchupBridge('default_bindings') + // Note, because of the way Sketchup bindings work, we need to wait here + // for them to be fully initialized. + await skpBindings.isInitalized bindings = skpBindings as unknown as IWebUiBinding } catch (e) { - console.warn('Failed to bind sketchup.') + console.warn( + 'Failed to bind sketchup. This can be totally normal if the host is different.' + ) console.warn(e) } @@ -64,17 +68,10 @@ export default defineNuxtPlugin(async () => { bindings = MockedBindings } + // We need the bindings object in global scope to allow + // host applications to send messages back to it. ;(globalThis as Record).bindings = bindings - // bindings.on('test', (args) => { - // console.log(args) - // }) - - // bindings.on('documentChanged', (args) => { - // // do somethings - // args.x - // }) - return { provide: { bindings diff --git a/packages/dui3/types/index.ts b/packages/dui3/types/index.ts index d52617777..a7ecd09ab 100644 --- a/packages/dui3/types/index.ts +++ b/packages/dui3/types/index.ts @@ -1,5 +1,6 @@ -import { createNanoEvents, Emitter } from 'nanoevents' /* eslint-disable @typescript-eslint/require-await */ +import { createNanoEvents } from 'nanoevents' + export type Account = { id: string isDefault: boolean diff --git a/yarn.lock b/yarn.lock index a5d18fe64..9671dae3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10933,6 +10933,7 @@ __metadata: graphql: ^16.6.0 graphql-tag: ^2.12.6 lodash-es: ^4.17.21 + nanoevents: ^8.0.0 nuxt: ^3.5.0 portal-vue: ^3.0.0 postcss: ^8.4.18 @@ -32762,6 +32763,13 @@ __metadata: languageName: node linkType: hard +"nanoevents@npm:^8.0.0": + version: 8.0.0 + resolution: "nanoevents@npm:8.0.0" + checksum: 46806fb1bca823de1dac0ec38352c9ebd25dd9f2186eada6357f2941983203c1c77fc383e460852a139b4ced569af2a2777f82cb574f705fd6f8402497f4bfc2 + languageName: node + linkType: hard + "nanoid@npm:3.3.3": version: 3.3.3 resolution: "nanoid@npm:3.3.3"