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:
@@ -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
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -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
|
||||
// }
|
||||
// }
|
||||
})
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user