From 047a7644a21ed0b6bbabfb801d5edec335a36d9a Mon Sep 17 00:00:00 2001 From: Dimitrie Stefanescu Date: Fri, 14 Jul 2023 08:13:22 +0100 Subject: [PATCH] experiments(dui3): cleanup --- .../lib/bindings/definitions/baseBindings.ts | 1 - packages/dui3/lib/bridge/base.ts | 2 +- packages/dui3/lib/bridge/cefSharp.ts | 14 -- packages/dui3/lib/bridge/definitions/index.ts | 8 ++ packages/dui3/lib/bridge/generic.ts | 12 +- packages/dui3/lib/bridge/webview.ts | 53 ------- packages/dui3/lib/document-info/index.ts | 25 ++++ packages/dui3/pages/index.vue | 14 +- packages/dui3/plugins/00.bindings.ts | 129 ++++++++---------- packages/dui3/plugins/00.bindingsV2.ts | 51 ------- 10 files changed, 105 insertions(+), 204 deletions(-) delete mode 100644 packages/dui3/lib/bridge/cefSharp.ts create mode 100644 packages/dui3/lib/bridge/definitions/index.ts delete mode 100644 packages/dui3/lib/bridge/webview.ts create mode 100644 packages/dui3/lib/document-info/index.ts delete mode 100644 packages/dui3/plugins/00.bindingsV2.ts diff --git a/packages/dui3/lib/bindings/definitions/baseBindings.ts b/packages/dui3/lib/bindings/definitions/baseBindings.ts index 2b5c0407d..d6170caa0 100644 --- a/packages/dui3/lib/bindings/definitions/baseBindings.ts +++ b/packages/dui3/lib/bindings/definitions/baseBindings.ts @@ -28,7 +28,6 @@ export interface IBaseBinding { export interface IBaseBindingHostEvents { displayToastNotification: (args: ToastInfo) => void documentChanged: () => void - selectionChanged: (args: SelectionChangedInfo) => void } export type Account = { diff --git a/packages/dui3/lib/bridge/base.ts b/packages/dui3/lib/bridge/base.ts index ae0c6ddf3..ade5aa42c 100644 --- a/packages/dui3/lib/bridge/base.ts +++ b/packages/dui3/lib/bridge/base.ts @@ -18,7 +18,7 @@ export class BaseBridge { // NOTE: this could be private - as it should be only used by the host application. emit(eventName: string, payload: string) { - const parsedPayload = JSON.parse(payload) as unknown + const parsedPayload = payload ? (JSON.parse(payload) as unknown) : null this.emitter.emit(eventName, parsedPayload) } } diff --git a/packages/dui3/lib/bridge/cefSharp.ts b/packages/dui3/lib/bridge/cefSharp.ts deleted file mode 100644 index 08ed31e0e..000000000 --- a/packages/dui3/lib/bridge/cefSharp.ts +++ /dev/null @@ -1,14 +0,0 @@ -// import { IWebUiBinding } from '~/types' -// import { BaseBridge } from '~/lib/bridge/base' - -// export class CefSharpBridge extends BaseBridge { -// constructor(bindingObject: IWebUiBinding) { -// super() -// const hoistTarget = this as unknown as Record -// const hoistSource = bindingObject as unknown as Record - -// for (const key in bindingObject) { -// hoistTarget[key] = hoistSource[key] -// } -// } -// } diff --git a/packages/dui3/lib/bridge/definitions/index.ts b/packages/dui3/lib/bridge/definitions/index.ts new file mode 100644 index 000000000..3eb9dd242 --- /dev/null +++ b/packages/dui3/lib/bridge/definitions/index.ts @@ -0,0 +1,8 @@ +/** + * Defines the expected contract of the host application bound object. + */ +export type IRawBridge = { + GetBindingsMethodNames: () => Promise + RunMethod: (methodName: string, args: string) => Promise + ShowDevTools: () => Promise +} diff --git a/packages/dui3/lib/bridge/generic.ts b/packages/dui3/lib/bridge/generic.ts index 50170669a..a3afa7b1a 100644 --- a/packages/dui3/lib/bridge/generic.ts +++ b/packages/dui3/lib/bridge/generic.ts @@ -1,12 +1,8 @@ -// github.com/johot/WebView2-better-bridge/blob/master/web-ui/src/betterBridge.ts import { BaseBridge } from '~/lib/bridge/base' - -export type IRawBridge = { - GetBindingsMethodNames: () => Promise - RunMethod: (methodName: string, args: string) => Promise - ShowDevTools: () => Promise -} - +import { IRawBridge } from '~/lib/bridge/definitions' +/** + * A generic bridge class for Webivew2 or CefSharp. + */ export class GenericBridge extends BaseBridge { private bridge: IRawBridge diff --git a/packages/dui3/lib/bridge/webview.ts b/packages/dui3/lib/bridge/webview.ts deleted file mode 100644 index 7878a5b0b..000000000 --- a/packages/dui3/lib/bridge/webview.ts +++ /dev/null @@ -1,53 +0,0 @@ -// github.com/johot/WebView2-better-bridge/blob/master/web-ui/src/betterBridge.ts -import { BaseBridge } from '~/lib/bridge/base' - -type IWebView2 = { - webview: { - hostObjects: Record & { - sync: Record - } - } -} - -type IRawBridge = { - GetBindingsMethodNames: () => string[] - RunMethod: (methodName: string, args: string) => Promise -} - -declare let chrome: IWebView2 - -export class WebView2Bridge extends BaseBridge { - private webViewBridge: IRawBridge - - constructor(bridgeName: string) { - super() - this.webViewBridge = chrome.webview.hostObjects[bridgeName] - - // NOTE: GetMethods is a call to the .NET side. - const availableMethodNames = - chrome.webview.hostObjects.sync[bridgeName].GetBindingsMethodNames() - - // NOTE: hoisting original calls as lowerCasedMethodNames, but using the UpperCasedName for the .NET call - // This allows us to follow js convetions and keep .NET ones too (eg. bindings.sayHi('') => public string SayHi(string name) {} - for (const methodName of availableMethodNames) { - const lowercasedMethodName = lowercaseMethodName(methodName) - const hoistTarget = this as unknown as Record - hoistTarget[lowercasedMethodName] = (...args: unknown[]) => - this.runMethod(methodName, args) - } - } - - private async runMethod(methodName: string, args: unknown[]): Promise { - const preserializedArgs = args.map((a) => JSON.stringify(a)) - // NOTE: RunMethod is a call to the .NET side. - const result = await this.webViewBridge.RunMethod( - methodName, - JSON.stringify(preserializedArgs) - ) - - return JSON.parse(result) as unknown - } -} - -const lowercaseMethodName = (name: string) => - name.charAt(0).toLowerCase() + name.slice(1) diff --git a/packages/dui3/lib/document-info/index.ts b/packages/dui3/lib/document-info/index.ts new file mode 100644 index 000000000..ff7f00118 --- /dev/null +++ b/packages/dui3/lib/document-info/index.ts @@ -0,0 +1,25 @@ +import { DocumentInfo } from '~/lib/bindings/definitions/baseBindings' +import { ref } from 'vue' + +const DocumentInfoInjectionKey = 'DUI_ACCOUNTS_STATE' + +export async function useDocumentInfoSetup() { + const app = useNuxtApp() + const documentInfo = ref() + + app.$baseBinding.on('documentChanged', () => { + setTimeout(async () => { + const docInfo = await app.$baseBinding.getDocumentInfo() + documentInfo.value = docInfo + }, 500) // Don't ask + }) + + documentInfo.value = await app.$baseBinding.getDocumentInfo() + provide(DocumentInfoInjectionKey, documentInfo) + return documentInfo +} + +export function useInjectedDocumentInfo() { + const documentInfo = inject>(DocumentInfoInjectionKey) + return documentInfo +} diff --git a/packages/dui3/pages/index.vue b/packages/dui3/pages/index.vue index 6f03802db..6242797a3 100644 --- a/packages/dui3/pages/index.vue +++ b/packages/dui3/pages/index.vue @@ -4,7 +4,6 @@
for {{ appName }}
-
@@ -18,13 +17,17 @@ {{ acc.accountInfo.serverInfo.name }}
-
Your default account is {{ defaultAccount?.accountInfo }}
+
+ Your default account is at {{ defaultAccount?.accountInfo.serverInfo.url }} +
{{ clientId }}: {{ res.result.value?.serverInfo.version || res.error }}
+
Doc info:
+
{{ documentInfo }}
Refresh Accounts
@@ -36,11 +39,14 @@ import { UseQueryReturn, useQuery } from '@vue/apollo-composable' import { useAccountsSetup } from '~/lib/accounts/composables/setup' import { graphql } from '~/lib/common/generated/gql' import { ServerInfoTestQuery } from '~/lib/common/generated/gql/graphql' +import { useDocumentInfoSetup } from '~/lib/document-info' const { $baseBinding } = useNuxtApp() const appName = await $baseBinding.getSourceApplicationName() const { accounts, refreshAccounts, defaultAccount } = await useAccountsSetup() +const documentInfo = await useDocumentInfoSetup() + const versionQuery = graphql(` query ServerInfoTest { serverInfo { @@ -49,10 +55,6 @@ const versionQuery = graphql(` } `) -watch(accounts, () => { - console.log('accounts were refreshed, shallow ref does its job') -}) - const clientIds = accounts.value.map((a) => a.accountInfo.id) const queries: Record< diff --git a/packages/dui3/plugins/00.bindings.ts b/packages/dui3/plugins/00.bindings.ts index a21617061..61eb51f5a 100644 --- a/packages/dui3/plugins/00.bindings.ts +++ b/packages/dui3/plugins/00.bindings.ts @@ -1,75 +1,64 @@ -// import { IWebUiBinding, MockedBindings } from '~/types' -// import { WebView2Bridge } from '~/lib/bridge/webview' -// import { CefSharpBridge } from '~/lib/bridge/cefSharp' -// import { SketchupBridge } from '~/lib/bridge/sketchup' +import { GenericBridge } from '~/lib/bridge/generic' +import { IRawBridge } from '~/lib/bridge/definitions' -// interface ICefSharp { -// BindObjectAsync: (arg: string) => Promise -// } +import { + IBaseBinding, + IRhinoRandomBinding +} from '~/lib/bindings/definitions/baseBindings' -// interface IWebView2 { -// webview: unknown -// } +// Makes TS happy +declare let globalThis: Record & { + CefSharp?: { BindObjectAsync: (name: string) => Promise } + chrome?: { webview: { hostObjects: Record } } + sketchup?: Record +} -// declare let CefSharp: ICefSharp -// declare let chrome: IWebView2 -// declare let sketchup: Record - -// declare let WebUIBinding: IWebUiBinding - -// Tries to find the correct host application binding. The sequence is: -// - CEFSharp (.NET) -// - WebView2 (.NET) -// - Sketchup (Ruby) - NOT IMPLEMENTED +/** + * Here we are loading any bindings that we expect to have from all + * connectors. If some are not present, that's okay - we're going to + * strip or customize functionality from the ui itself. + */ export default defineNuxtPlugin(async () => { - // let bindings: IWebUiBinding | undefined = undefined - // try { - // if (!CefSharp) throw new Error('No global CefSharp object found.') - // await CefSharp.BindObjectAsync('WebUIBinding') - // console.info('Bound WebUIBinding object for CefSharp.') - // bindings = new CefSharpBridge(WebUIBinding) as unknown as IWebUiBinding - // } catch (e) { - // console.warn( - // 'Failed to bind CefSharp. This can be totally normal if the host is different.' - // ) - // console.warn(e) - // } - // try { - // 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.') - // } catch (e) { - // console.warn( - // 'Failed to bind Webview2. This can be totally normal if the host is different.' - // ) - // console.warn(e) - // } - // // The sketchup ruby side is in flux. We know though that - // // this part will work! Nevertheless, it will currently throw if loaded in sketchup. - // 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 :) ') - // 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. This can be totally normal if the host is different.' - // ) - // console.warn(e) - // } - // if (!bindings) { - // console.warn('No bindings found - falling back to mocked bindings.') - // 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 - // return { - // provide: { - // bindings - // } - // } + const baseBinding = await tryHoistBinding('baseBinding') + + const rhinoRandomBinding = await tryHoistBinding( + 'rhinoRandomBinding' + ) + + return { + provide: { + baseBinding, + rhinoRandomBinding + } + } }) + +/** + * Checks possible browser window targets for a given binding, and, if it finds it, + * creates a bridge for it and registers it in the global scope. + * @param name binding name + * @returns null if the binding was not found, or the binding. + */ +const tryHoistBinding = async (name: string) => { + let bridge: GenericBridge | null = null + + if (globalThis.CefSharp) { + await globalThis.CefSharp.BindObjectAsync(name) + bridge = new GenericBridge(globalThis[name] as unknown as IRawBridge) + await bridge.create() + } + + if (globalThis.chrome && !bridge) { + bridge = new GenericBridge(globalThis.chrome.webview.hostObjects[name]) + await bridge.create() + } + + if (globalThis.sketchup && !bridge) { + // TODO + } + + if (!bridge) console.warn(`Failed to bind ${name} binding.`) + + globalThis[name] = bridge + return bridge as unknown as T +} diff --git a/packages/dui3/plugins/00.bindingsV2.ts b/packages/dui3/plugins/00.bindingsV2.ts deleted file mode 100644 index c768cc9ae..000000000 --- a/packages/dui3/plugins/00.bindingsV2.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { GenericBridge, IRawBridge } from '~/lib/bridge/generic' -import { - IBaseBinding, - IRhinoRandomBinding -} from '~/lib/bindings/definitions/baseBindings' - -// Makes TS happy -declare let globalThis: Record & { - CefSharp?: { BindObjectAsync: (name: string) => Promise } - chrome?: { webview: { hostObjects: Record } } - sketchup?: Record -} - -// eslint-disable-next-line @typescript-eslint/require-await -const tryHoistBinding = async (name: string) => { - let bridge: GenericBridge | null = null - - if (globalThis.CefSharp) { - await globalThis.CefSharp.BindObjectAsync(name) - bridge = new GenericBridge(globalThis[name] as unknown as IRawBridge) - await bridge.create() - } - - if (globalThis.chrome && !bridge) { - bridge = new GenericBridge(globalThis.chrome.webview.hostObjects[name]) - await bridge.create() - } - - if (globalThis.sketchup && !bridge) { - // TODO - } - - if (!bridge) console.warn(`Failed to bind ${name} binding.`) - - globalThis[name] = bridge - return bridge as unknown as T -} - -export default defineNuxtPlugin(async () => { - const baseBinding = await tryHoistBinding('baseBinding') - const rhinoRandomBinding = await tryHoistBinding( - 'rhinoRandomBinding' - ) - - return { - provide: { - baseBinding, - rhinoRandomBinding - } - } -})