experiments(dui3): cleanup

This commit is contained in:
Dimitrie Stefanescu
2023-07-14 08:13:22 +01:00
parent 2467b4749f
commit 047a7644a2
10 changed files with 105 additions and 204 deletions
@@ -28,7 +28,6 @@ export interface IBaseBinding {
export interface IBaseBindingHostEvents {
displayToastNotification: (args: ToastInfo) => void
documentChanged: () => void
selectionChanged: (args: SelectionChangedInfo) => void
}
export type Account = {
+1 -1
View File
@@ -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)
}
}
-14
View File
@@ -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<string, unknown>
// const hoistSource = bindingObject as unknown as Record<string, unknown>
// for (const key in bindingObject) {
// hoistTarget[key] = hoistSource[key]
// }
// }
// }
@@ -0,0 +1,8 @@
/**
* Defines the expected contract of the host application bound object.
*/
export type IRawBridge = {
GetBindingsMethodNames: () => Promise<string[]>
RunMethod: (methodName: string, args: string) => Promise<string>
ShowDevTools: () => Promise<void>
}
+4 -8
View File
@@ -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<string[]>
RunMethod: (methodName: string, args: string) => Promise<string>
ShowDevTools: () => Promise<void>
}
import { IRawBridge } from '~/lib/bridge/definitions'
/**
* A generic bridge class for Webivew2 or CefSharp.
*/
export class GenericBridge extends BaseBridge {
private bridge: IRawBridge
-53
View File
@@ -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<string, IRawBridge> & {
sync: Record<string, IRawBridge>
}
}
}
type IRawBridge = {
GetBindingsMethodNames: () => string[]
RunMethod: (methodName: string, args: string) => Promise<string>
}
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<string, object>
hoistTarget[lowercasedMethodName] = (...args: unknown[]) =>
this.runMethod(methodName, args)
}
}
private async runMethod(methodName: string, args: unknown[]): Promise<unknown> {
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)
+25
View File
@@ -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<DocumentInfo>()
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<Ref<DocumentInfo>>(DocumentInfoInjectionKey)
return documentInfo
}
+8 -6
View File
@@ -4,7 +4,6 @@
<div class="mx-2 py-1 px-2 rounded text-xs bg-primary text-white font-bold">
for {{ appName }}
</div>
<!-- <HeaderNavLink :to="'/'" :name="'Home'"></HeaderNavLink> -->
</Portal>
<div class="space-y-2">
<div>
@@ -18,13 +17,17 @@
{{ acc.accountInfo.serverInfo.name }}
</div>
</div>
<div>Your default account is {{ defaultAccount?.accountInfo }}</div>
<div>
Your default account is at {{ defaultAccount?.accountInfo.serverInfo.url }}
</div>
<div>
<div v-for="(res, clientId) in queries" :key="clientId">
<strong>{{ clientId }}:</strong>
{{ res.result.value?.serverInfo.version || res.error }}
</div>
</div>
<div>Doc info:</div>
<div>{{ documentInfo }}</div>
<div>
<FormButton @click="refreshAccounts()">Refresh Accounts</FormButton>
</div>
@@ -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<
+59 -70
View File
@@ -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<void>
// }
import {
IBaseBinding,
IRhinoRandomBinding
} from '~/lib/bindings/definitions/baseBindings'
// interface IWebView2 {
// webview: unknown
// }
// Makes TS happy
declare let globalThis: Record<string, unknown> & {
CefSharp?: { BindObjectAsync: (name: string) => Promise<void> }
chrome?: { webview: { hostObjects: Record<string, IRawBridge> } }
sketchup?: Record<string, unknown>
}
// declare let CefSharp: ICefSharp
// declare let chrome: IWebView2
// declare let sketchup: Record<string, unknown>
// 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<string, unknown>).bindings = bindings
// return {
// provide: {
// bindings
// }
// }
const baseBinding = await tryHoistBinding<IBaseBinding>('baseBinding')
const rhinoRandomBinding = await tryHoistBinding<IRhinoRandomBinding>(
'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 <T>(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
}
-51
View File
@@ -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<string, unknown> & {
CefSharp?: { BindObjectAsync: (name: string) => Promise<void> }
chrome?: { webview: { hostObjects: Record<string, IRawBridge> } }
sketchup?: Record<string, unknown>
}
// eslint-disable-next-line @typescript-eslint/require-await
const tryHoistBinding = async <T>(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<IBaseBinding>('baseBinding')
const rhinoRandomBinding = await tryHoistBinding<IRhinoRandomBinding>(
'rhinoRandomBinding'
)
return {
provide: {
baseBinding,
rhinoRandomBinding
}
}
})