experiments(dui3): super wip work

This commit is contained in:
Dimitrie Stefanescu
2023-07-09 22:19:55 +01:00
parent 0923c3f1d2
commit ca63e2936a
9 changed files with 101 additions and 39 deletions
@@ -29,7 +29,7 @@ export async function useAccountsSetup() {
// Matches local accounts coming from the host app to app state.
const refreshAccounts = async () => {
const accs = JSON.parse(await $bindings.getAccounts()) as Account[]
const accs = await $bindings.getAccounts()
const newAccs = [] as DUIAccount[]
for (const acc of accs) {
const existing = accounts.value.find((a) => a.accountInfo.id === acc.id)
@@ -53,10 +53,16 @@ export async function useAccountsSetup() {
accounts.value = newAccs
}
// Call this one first to initialize the account state
await refreshAccounts()
const defaultAccount = computed(() =>
accounts.value.find((acc) => acc.accountInfo.isDefault)
)
const accState = {
accounts,
defaultAccount,
refreshAccounts
}
+28
View File
@@ -0,0 +1,28 @@
import { IWebUiBinding } from '~/types'
import { createNanoEvents, Emitter } from 'nanoevents'
import { HostAppEvents } from '~/types'
export class CefSharpBridge {
private emitter: Emitter
constructor(bindingObject: IWebUiBinding) {
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]
}
this.emitter = createNanoEvents<HostAppEvents>()
this.emitter.emit('start', 'polo pasta')
}
on<E extends keyof HostAppEvents>(event: E, callback: HostAppEvents[E]) {
return this.emitter.on(event, callback)
}
emit(eventName: string, payload: string) {
const parsedPayload = JSON.parse(payload) as unknown
this.emitter.emit(eventName, parsedPayload)
}
}
+1
View File
@@ -0,0 +1 @@
// TODO
+14
View File
@@ -1,4 +1,6 @@
// github.com/johot/WebView2-better-bridge/blob/master/web-ui/src/betterBridge.ts
import { createNanoEvents, Emitter } from 'nanoevents'
import { HostAppEvents } from '~/types'
type IWebView2 = {
webview: {
@@ -17,6 +19,7 @@ declare let chrome: IWebView2
export class WebView2Bridge {
private webViewBridge: IRawBridge
private emitter: Emitter
constructor(bridgeName: string) {
this.webViewBridge = chrome.webview.hostObjects[bridgeName]
@@ -33,6 +36,8 @@ export class WebView2Bridge {
hoistTarget[lowercasedMethodName] = (...args: unknown[]) =>
this.runMethod(methodName, args)
}
this.emitter = createNanoEvents<HostAppEvents>()
}
private async runMethod(methodName: string, args: unknown[]): Promise<unknown> {
@@ -45,6 +50,15 @@ export class WebView2Bridge {
return JSON.parse(result) as unknown
}
on<E extends keyof HostAppEvents>(event: E, callback: HostAppEvents[E]) {
return this.emitter.on(event, callback)
}
emit(eventName: string, payload: string) {
const parsedPayload = JSON.parse(payload) as unknown
this.emitter.emit(eventName, parsedPayload)
}
}
const lowercaseMethodName = (name: string) =>
-30
View File
@@ -1,30 +0,0 @@
import { IWebUiBinding } from '~/types'
export class SketchupBindings implements IWebUiBinding {
requestId = 0
requests = {} as Record<number, unknown>
SketchupBindings() {
// todo
}
getAccounts() {
this.requestId++
this.requests[this.requestId] = new Promise((resolve, reject) => resolve('[]'))
return this.requests[this.requestId] as Promise<string>
}
async sayHi(name: string) {
return `Hi ${name} from (sketchup mocked bindings)!`
}
getSourceAppName() {
return new Promise((resolve, reject) => resolve('sketchup')) as Promise<string>
}
async openDevTools() {
// eslint-disable-next-line no-alert
window.alert('Please right click and select show dev tools.')
}
}
+1
View File
@@ -34,6 +34,7 @@
"graphql": "^16.6.0",
"graphql-tag": "^2.12.6",
"lodash-es": "^4.17.21",
"nanoevents": "^8.0.0",
"portal-vue": "^3.0.0",
"subscriptions-transport-ws": "^0.11.0"
},
+2 -1
View File
@@ -18,6 +18,7 @@
{{ acc.accountInfo.serverInfo.name }}
</div>
</div>
<div>Your default account is {{ defaultAccount?.accountInfo }}</div>
<div>
<div v-for="(res, clientId) in queries" :key="clientId">
<strong>{{ clientId }}:</strong>
@@ -38,7 +39,7 @@ import { ServerInfoTestQuery } from '~/lib/common/generated/gql/graphql'
const { $bindings } = useNuxtApp()
const appName = await $bindings.getSourceAppName()
const { accounts, refreshAccounts } = await useAccountsSetup()
const { accounts, refreshAccounts, defaultAccount } = await useAccountsSetup()
const versionQuery = graphql(`
query ServerInfoTest {
+12 -4
View File
@@ -1,5 +1,6 @@
import { IWebUiBinding, MockedBindings } from '~/types'
import { WebView2Bridge } from '~/lib/bridge/webview'
import { CefSharpBridge } from '~/lib/bridge/cefSharp'
interface ICefSharp {
BindObjectAsync: (arg: string) => Promise<void>
@@ -11,10 +12,14 @@ interface IWebView2 {
declare let CefSharp: ICefSharp
declare let chrome: IWebView2
declare let sketchup: Record<string, unknown> //
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
export default defineNuxtPlugin(async () => {
let bindings: IWebUiBinding | undefined = undefined
@@ -22,7 +27,7 @@ export default defineNuxtPlugin(async () => {
if (!CefSharp) throw new Error('No global CefSharp object found.')
await CefSharp.BindObjectAsync('WebUIBinding')
console.info('Bound WebUIBinding object for CefSharp.')
bindings = WebUIBinding
bindings = new CefSharpBridge(WebUIBinding) as unknown as IWebUiBinding
} catch (e) {
console.warn('Failed to bind CefSharp.')
console.warn(e)
@@ -32,10 +37,9 @@ 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)
// TODO: Wrap the motherfucking wv2 bindings up
} catch (e) {
console.warn('Failed to bind Webview2.')
console.warn(e)
@@ -57,6 +61,10 @@ export default defineNuxtPlugin(async () => {
;(globalThis as Record<string, unknown>).bindings = bindings
bindings.on('test', (args) => {
console.log(args)
})
return {
provide: {
bindings
+36 -3
View File
@@ -1,3 +1,4 @@
import { createNanoEvents, Emitter } from 'nanoevents'
/* eslint-disable @typescript-eslint/require-await */
export type Account = {
id: string
@@ -30,17 +31,46 @@ type ModelCard = {
// settings: Record<string,unknown>???
// report: Record<string,unknown>???
// progress: Record<string,unknown>???
status: 'idle' | 'inprogress' | 'error' | 'warning' | 'disabled'
}
export type IWebUiBinding = {
type TestData = {
foo: number
bar: string
baz: boolean
}
export interface HostAppEvents {
test: (data: TestData) => void
documentChanged: () => void
selectionChanged: () => void
documentClosed: () => void
updateModelCardState: () => void
displayToastNotification: () => void // bla bla bla
}
export interface IWebUiBinding {
sayHi: (name: string) => Promise<string>
openDevTools: () => Promise<void>
getAccounts: () => Promise<Account[]>
getSourceAppName: () => Promise<string>
// etc.
getFileState: () => Promise<FileState>
// getFileState: () => Promise<FileState>
// addModelCard(string modelId, string projectId), removeModelCard(...) // etc. etc.
/**
* Subscribe to messages from the host application.
* @param event
* @param callback
*/
on: <E extends keyof HostAppEvents>(event: E, callback: HostAppEvents[E]) => void
/**
* Used by the host application to notify/send data to the web app. Do not use from the frontend.
* @param eventName
* @param args
*/
emit?: (eventName: string, args: Record<string, unknown>) => void
}
const mockedEmitter = createNanoEvents<HostAppEvents>()
export const MockedBindings: IWebUiBinding = {
async sayHi(name: string) {
return `Hi ${name} from (mocked bindings)!`
@@ -57,5 +87,8 @@ export const MockedBindings: IWebUiBinding = {
},
async getFileState() {
return { models: [] }
},
on<E extends keyof HostAppEvents>(event: E, callback: HostAppEvents[E]) {
return mockedEmitter.on(event, callback)
}
}