diff --git a/packages/core/package.json b/packages/core/package.json index 5e14d467..a73aa059 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -35,7 +35,7 @@ "@ucanto/interface": "^9.0.0", "@ucanto/principal": "^9.0.0", "@ucanto/transport": "^9.0.0", - "@web3-storage/access": "^18.0.3", + "@web3-storage/access": "^18.0.5", "@web3-storage/did-mailto": "^2.0.2", "@web3-storage/w3up-client": "^11.2.0" }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4dc3aa1e..d7e304af 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -6,9 +6,11 @@ import { Client, create as createW3UPClient } from '@web3-storage/w3up-client' import { Account } from '@web3-storage/w3up-client/account' import { Space } from '@web3-storage/w3up-client/space' import { createServiceConf } from './service' +import { Driver } from '@web3-storage/access/drivers/types' export * from '@web3-storage/w3up-client/types' export { Client, Account, Space, ServiceConfig } +export type Store = Driver const DB_NAME = '@w3ui' const DB_STORE_NAME = 'core' @@ -29,7 +31,12 @@ export interface ContextState { } // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ContextActions {} +export interface ContextActions { + /** + * Reset local store (deleting existing agent), logging the user out. + */ + logout: () => Promise +} export interface CreateClientOptions extends ServiceConfig { events?: EventTarget @@ -59,11 +66,11 @@ class IndexedDBEventDispatcherStore extends StoreIndexedDB { */ export async function createClient ( options?: CreateClientOptions -): Promise<{ client: Client, events: EventTarget }> { +): Promise<{ client: Client, events: EventTarget, store: Store }> { const dbName = `${DB_NAME}${options?.servicePrincipal != null ? '@' + options?.servicePrincipal.did() : ''}` const events = options?.events ?? new EventTarget() const store = new IndexedDBEventDispatcherStore(dbName, events) const serviceConf = createServiceConf(options) const client = await createW3UPClient({ store, serviceConf }) - return { client, events } + return { client, events, store } } diff --git a/packages/react/src/Authenticator.tsx b/packages/react/src/Authenticator.tsx index ff2c0674..f61f5af7 100644 --- a/packages/react/src/Authenticator.tsx +++ b/packages/react/src/Authenticator.tsx @@ -60,6 +60,9 @@ export const AuthenticatorContextDefaultValue: AuthenticatorContextValue = [ }, cancelLogin: () => { throw new Error('missing cancel login function') + }, + logout: () => { + throw new Error('missing logout function') } } ] @@ -112,6 +115,7 @@ export const AuthenticatorRoot: Component = () => [ { ...state, email, submitted, handleRegisterSubmit }, { + ...actions, setEmail, cancelLogin: () => { loginAbortController?.abort() diff --git a/packages/react/src/providers/Provider.tsx b/packages/react/src/providers/Provider.tsx index bc2b409d..a2e210bf 100644 --- a/packages/react/src/providers/Provider.tsx +++ b/packages/react/src/providers/Provider.tsx @@ -7,7 +7,7 @@ import type { Account } from '@w3ui/core' -import React, { createContext, useState, useContext, useEffect } from 'react' +import React, { createContext, useState, useContext, useEffect, ReactNode } from 'react' import { createClient } from '@w3ui/core' export { ContextState, ContextActions } @@ -23,7 +23,11 @@ export const ContextDefaultValue: ContextValue = [ accounts: [], spaces: [] }, - {} + { + logout: async () => { + throw new Error('missing logout function') + } + } ] export const Context = createContext( @@ -31,7 +35,7 @@ export const Context = createContext( ) export interface ProviderProps extends ServiceConfig { - children?: JSX.Element + children?: ReactNode } /** @@ -41,7 +45,7 @@ export function Provider ({ children, servicePrincipal, connection -}: ProviderProps): JSX.Element { +}: ProviderProps): ReactNode { const [client, setClient] = useState() const [events, setEvents] = useState() const [accounts, setAccounts] = useState([]) @@ -61,22 +65,32 @@ export function Provider ({ } }, [client, events]) - const getClient = async (): Promise => { - if (client == null) { - const { client, events } = await createClient({ servicePrincipal, connection }) - setClient(client) - setEvents(events) - setAccounts(Object.values(client.accounts())) - setSpaces(client.spaces()) - return client - } - return client + const setupClient = async (): Promise => { + const { client, events } = await createClient({ servicePrincipal, connection }) + setClient(client) + setEvents(events) + setAccounts(Object.values(client.accounts())) + setSpaces(client.spaces()) + } + + const logout = async (): Promise => { + // it's possible that setupClient hasn't been run yet - run createClient here + // to get a reliable handle on the latest store + const { store } = await createClient({ servicePrincipal, connection }) + await store.reset() + // set state back to defaults + setClient(undefined) + setEvents(undefined) + setAccounts([]) + setSpaces([]) + // set state up again + await setupClient() } - useEffect(() => { void getClient() }, []) // load client - once. + useEffect(() => { void setupClient() }, []) // load client - once. return ( - + {children} ) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6292cd8f..0c317bb3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -550,8 +550,8 @@ importers: specifier: ^9.0.0 version: 9.0.0 '@web3-storage/access': - specifier: ^18.0.3 - version: 18.0.3 + specifier: ^18.0.5 + version: 18.0.5 '@web3-storage/did-mailto': specifier: ^2.0.2 version: 2.1.0 @@ -3459,29 +3459,6 @@ packages: uint8arrays: 4.0.9 dev: false - /@web3-storage/access@18.0.3: - resolution: {integrity: sha512-cKxOimmUtKjkwsw+naa7sB9U+e67+bHJb+/KC4Q/d8b+rAmYY0UwlcPCC207w9c7+Ne/cdOuuJd7BH6LfcdX1w==} - dependencies: - '@ipld/car': 5.2.4 - '@ipld/dag-ucan': 3.4.0 - '@scure/bip39': 1.2.1 - '@ucanto/client': 9.0.0 - '@ucanto/core': 9.0.1 - '@ucanto/interface': 9.0.0 - '@ucanto/principal': 9.0.0 - '@ucanto/transport': 9.0.0 - '@ucanto/validator': 9.0.1 - '@web3-storage/capabilities': 12.0.3 - '@web3-storage/did-mailto': 2.1.0 - bigint-mod-arith: 3.3.1 - conf: 11.0.2 - multiformats: 12.1.3 - one-webcrypto: github.com/web3-storage/one-webcrypto/5148cd14d5489a8ac4cd38223870e02db15a2382 - p-defer: 4.0.0 - type-fest: 3.13.1 - uint8arrays: 4.0.9 - dev: false - /@web3-storage/access@18.0.5: resolution: {integrity: sha512-dTojMu7UWb7esbnX3F18eTC+hwlYkDhxBZAogmHX7legRGRK5MwwoRMpk8qz6zI6ImUmkApWFYrF2U8OaGC0bQ==} dependencies: