experiments(dui3): coupled with some other rhino experiments that i need to push still, investigating some patterns re composables & co. in nuxt and possibly having a strategy around multiple host app bindings

This commit is contained in:
Dimitrie Stefanescu
2023-07-01 20:41:57 +01:00
parent 033516e669
commit 76d2f475f5
7 changed files with 201 additions and 40 deletions
+2 -2
View File
@@ -7,12 +7,12 @@
<div class="flex items-center">
<HeaderLogoBlock :active="false" class="mr-0" />
<div class="flex flex-shrink-0 items-center -ml-2 md:ml-0">
<HeaderNavLink
<!-- <HeaderNavLink
to="/"
name="Dashboard"
:separator="true"
class="hidden md:inline-block"
/>
/> -->
<PortalTarget name="navigation"></PortalTarget>
</div>
</div>
@@ -0,0 +1,69 @@
import { ApolloClient } from '@apollo/client/core'
import { ApolloClients } from '@vue/apollo-composable'
import { ShallowRef } from 'vue'
import { resolveClientConfig } from '~/lib/core/configs/apollo'
import { Account } from '~/types'
export type DUIAccount = {
accountInfo: Account
client: ApolloClient<unknown>
}
export type DUIAccountsState = {
accounts: ShallowRef<DUIAccount[]>
refreshAccounts: () => Promise<void>
}
const AccountsInjectionKey = 'DUI_ACCOUNTS_STATE'
export async function useAccountsSetup() {
const app = useNuxtApp()
const $bindings = app.$bindings
// Using a shallow ref as we don't need inner values reactive
const accounts = shallowRef([] as DUIAccount[])
const apolloClients = {} as Record<string, ApolloClient<unknown>>
// Matches local accounts coming from the host app to app state.
const refreshAccounts = async () => {
const accs = JSON.parse(await $bindings.getAccounts()) as Account[]
const newAccs = [] as DUIAccount[]
for (const acc of accs) {
const existing = accounts.value.find((a) => a.accountInfo.id === acc.id)
if (existing) {
newAccs.push(existing)
continue
}
const client = new ApolloClient(
resolveClientConfig({
httpEndpoint: new URL('/graphql', acc.serverInfo.url).href,
authToken: () => acc.token
})
)
apolloClients[acc.id] = client
newAccs.push({
accountInfo: acc,
client
})
}
accounts.value = newAccs
}
await refreshAccounts()
const accState = {
accounts,
refreshAccounts
}
app.vueApp.provide(ApolloClients, apolloClients)
provide(AccountsInjectionKey, accState)
return accState
}
export function useInjectedAccounts() {
const state = inject(AccountsInjectionKey) as DUIAccountsState
return state
}
+1 -1
View File
@@ -6,7 +6,7 @@ export default defineNuxtConfig({
shim: false,
strict: true
},
modules: ['@nuxtjs/tailwindcss'],
modules: ['@nuxtjs/tailwindcss', '@speckle/ui-components-nuxt'],
alias: {
// Rewriting all lodash calls to lodash-es for proper tree-shaking & chunk splitting
lodash: 'lodash-es'
+37 -10
View File
@@ -1,22 +1,45 @@
<template>
<div>
Hello world! Query results:
<div>
<div v-for="(res, clientId) in queries" :key="clientId">
<strong>{{ clientId }}:</strong>
{{ res.result.value?.serverInfo.version || '' }}
<Portal to="navigation">
<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>
Hello world! You have
{{ accounts.length }} accounts.
</div>
<div v-for="acc in accounts" :key="acc.accountInfo.id">
<div class="truncate text-xs">
{{ acc.accountInfo.userInfo.email }} @
<b>{{ acc.accountInfo.serverInfo.url }}</b>
{{ acc.accountInfo.serverInfo.name }}
</div>
</div>
<div>
<div v-for="(res, clientId) in queries" :key="clientId">
<strong>{{ clientId }}:</strong>
{{ res.result.value?.serverInfo.version || res.error }}
</div>
</div>
<div>
<FormButton @click="refreshAccounts()">Refresh Accounts</FormButton>
</div>
</div>
<Portal to="navigation">
<HeaderNavLink :to="'/'" :name="'Home'"></HeaderNavLink>
</Portal>
</div>
</template>
<script setup lang="ts">
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'
const { $bindings } = useNuxtApp()
const appName = await $bindings.getSourceAppName()
const { accounts, refreshAccounts } = await useAccountsSetup()
const versionQuery = graphql(`
query ServerInfoTest {
serverInfo {
@@ -25,16 +48,20 @@ const versionQuery = graphql(`
}
`)
watch(accounts, () => {
console.log('accs ref')
})
/**
* Imagine these come from window or something
*/
const clients = ['latest', 'xyz']
const clientIds = accounts.value.map((a) => a.accountInfo.id)
const queries: Record<
string,
UseQueryReturn<ServerInfoTestQuery, Record<string, never>>
> = {}
for (const clientId of clients) {
for (const clientId of clientIds) {
queries[clientId] = useQuery(versionQuery, undefined, { clientId })
}
</script>
+26
View File
@@ -0,0 +1,26 @@
import { ICefSharp, WebUiBindingType, MockedBindings } from '~/types'
declare let CefSharp: ICefSharp
declare let WebUIBinding: WebUiBindingType
export default defineNuxtPlugin(async () => {
let bindings: WebUiBindingType
try {
if (!CefSharp) throw new Error('No global CefSharp object found.')
await CefSharp.BindObjectAsync('WebUIBinding')
console.info('Bound WebUIBinding object for CefSharp.')
bindings = WebUIBinding
} catch (e) {
console.error('Failed to bind CefSharp, will use mocked bindings.')
console.error(e)
bindings = MockedBindings
}
return {
provide: {
bindings
}
}
})
+26 -27
View File
@@ -1,34 +1,33 @@
import { ApolloClient } from '@apollo/client/core'
import { ApolloClients } from '@vue/apollo-composable'
import { resolveClientConfig } from '~/lib/core/configs/apollo'
// import { ApolloClient } from '@apollo/client/core'
// import { ApolloClients } from '@vue/apollo-composable'
// import { resolveClientConfig } from '~/lib/core/configs/apollo'
export default defineNuxtPlugin((nuxtApp) => {
/**
* TODO: You can use `window` here to get credentials for all of the clients
* we need from the parent connectors. The following is just an example
*/
const apolloClients = {
latest: new ApolloClient(
// Imagine endpoint & token is resolved from window or something
resolveClientConfig({
httpEndpoint: 'https://latest.speckle.systems/graphql',
authToken: () => null
})
),
xyz: new ApolloClient(
// Imagine endpoint & token is resolved from window or something
resolveClientConfig({
httpEndpoint: 'https://speckle.xyz/graphql',
authToken: () => null
})
)
}
nuxtApp.vueApp.provide(ApolloClients, apolloClients)
return {
provide: {
apolloClients
}
}
// const { $bindings } = useNuxtApp()
// const apolloClients = {
// latest: new ApolloClient(
// // Imagine endpoint & token is resolved from window or something
// resolveClientConfig({
// httpEndpoint: 'https://latest.speckle.systems/graphql',
// authToken: () => null
// })
// ),
// xyz: new ApolloClient(
// // Imagine endpoint & token is resolved from window or something
// resolveClientConfig({
// httpEndpoint: 'https://speckle.xyz/graphql',
// authToken: () => null
// })
// )
// }
// nuxtApp.vueApp.provide(ApolloClients, apolloClients)
// return {
// provide: {
// apolloClients
// }
// }
})
+40
View File
@@ -0,0 +1,40 @@
/* eslint-disable @typescript-eslint/require-await */
export type Account = {
id: string
isDefault: boolean
token: string
serverInfo: {
name: string
url: string
}
userInfo: {
id: string
avatar: string
email: string
name: string
commits: { totalCount: number }
streams: { totalCount: number }
}
}
export interface ICefSharp {
BindObjectAsync: (arg: string) => Promise<void>
}
export type WebUiBindingType = {
getAccounts: () => Promise<string>
sayHi: (name: string) => Promise<string>
getSourceAppName: () => Promise<string>
}
export const MockedBindings: WebUiBindingType = {
async getAccounts() {
return '[]'
},
async sayHi(name: string) {
return `Hi ${name} from (mocked bindings)!`
},
async getSourceAppName() {
return 'Mocked App'
}
}