From 2bb7fb8c24d61022d9404d448bcdbb1cd6eb0147 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 24 Apr 2025 11:59:41 +0200 Subject: [PATCH 01/18] feat: Add telemetry logger --- .env.example | 7 +- src/features/telemetry/logger.ts | 133 +++++++++++++++++++++++++++++++ src/features/telemetry/types.ts | 1 + 3 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 src/features/telemetry/logger.ts create mode 100644 src/features/telemetry/types.ts diff --git a/.env.example b/.env.example index 8bf22aa..e6bde15 100644 --- a/.env.example +++ b/.env.example @@ -8,6 +8,11 @@ NEXT_PUBLIC_BASE_URL= # Optional: Set the dev mode ("true" or "false"). Default to "false". NEXT_PUBLIC_DEV_MODE=false +# ------ Telemetry ------ + +# Optional: Set the log level ("error", "warn", "info", "debug"). Default to "info". +NEXT_PUBLIC_LOG_LEVEL=info + # ------ Verida Network ------ # Optional: Set the Verida Network ("myrtle", "banksia", "devnet", "local"). Default to "banksia". @@ -20,4 +25,4 @@ NEXT_PUBLIC_VERIDA_RPC_URL= NEXT_PUBLIC_DCS_URL= # Endpoint to initiate auth consent for API token with Verida Vault -NEXT_PUBLIC_VAULT_AUTH_ENDPOINT= \ No newline at end of file +NEXT_PUBLIC_VAULT_AUTH_ENDPOINT= diff --git a/src/features/telemetry/logger.ts b/src/features/telemetry/logger.ts new file mode 100644 index 0000000..263b2e5 --- /dev/null +++ b/src/features/telemetry/logger.ts @@ -0,0 +1,133 @@ +/* eslint-disable no-console */ +import { commonConfig } from "@/config/common" +import type { LogLevel } from "@/features/telemetry/types" + +const levelOrder: LogLevel[] = ["error", "warn", "info", "debug"] + +/** + * Custom logger to use the console. + * + * The log level can be configured globally with the environment variable. + * + * The console will be used if in "dev" mode which is also defined by an environment variable. + */ +export class Logger { + private static instances = new Map() + private static currentLevelIndex: number = levelOrder.indexOf( + commonConfig.LOG_LEVEL + ) + + private readonly category: string + + private constructor(category: string) { + this.category = category + } + + /** + * Creates a new instance of the logger. + * + * @param category Used to prefix the message in the console. + */ + static create(category: string) { + if (Logger.instances.has(category)) { + return Logger.instances.get(category)! + } + const logger = new Logger(category) + Logger.instances.set(category, logger) + return logger + } + + /** + * Sets the log level for all instances at runtime. + * + * @param level The log level to set. + */ + static setLogLevel(level: LogLevel) { + Logger.currentLevelIndex = levelOrder.indexOf(level) + } + + private shouldSkipPrint(level: LogLevel) { + return ( + levelOrder.indexOf(level) > Logger.currentLevelIndex || + (commonConfig.isClient && !commonConfig.DEV_MODE) + ) + } + + private formatMessage(message: string) { + return `${new Date().toISOString()} - [${this.category}] ${message}` + } + + private log( + level: LogLevel, + message: string, + extra?: Record + ) { + if (this.shouldSkipPrint(level)) { + return + } + + const formattedMessage = this.formatMessage(message) + + const formattedExtra: Record[] = [] + if (extra) { + formattedExtra.push(extra) + } + + console[level](formattedMessage, ...formattedExtra) + } + + public error(error: Error | unknown) { + if (this.shouldSkipPrint("error")) { + return + } + + // Log the error message with the formatting (timestamp, category) + this.log("error", error instanceof Error ? error.message : "") + + // Use the standard error logging for the rest of the Error object + console.error(error) + } + + public warn(message: string, data?: Record) { + this.log("warn", message, data) + } + + public info(message: string, data?: Record) { + this.log("info", message, data) + } + + public debug(message: string, data?: Record) { + this.log("debug", message, data) + } + + public startTimer(label: string) { + if (this.shouldSkipPrint("debug")) { + return () => this.endTimer(label) + } + this.debug(`Starting timer: ${label}`) + console.time(label) + return () => this.endTimer(label) + } + + public logTimer(label: string, ...extra: string[]) { + if (this.shouldSkipPrint("debug")) { + return + } + console.timeLog(label, extra) + } + + public endTimer(label: string) { + if (this.shouldSkipPrint("debug")) { + return + } + console.timeEnd(label) + this.debug(`Timer ended: ${label}`) + } + + public table(data: unknown[], properties?: string[]) { + if (this.shouldSkipPrint("debug")) { + return + } + console.table(data, properties) + } +} diff --git a/src/features/telemetry/types.ts b/src/features/telemetry/types.ts new file mode 100644 index 0000000..8d73c09 --- /dev/null +++ b/src/features/telemetry/types.ts @@ -0,0 +1 @@ +export type LogLevel = "error" | "warn" | "info" | "debug" From 8d4d3a0040ede01e0922b084479e4475c03612c7 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 24 Apr 2025 12:00:35 +0200 Subject: [PATCH 02/18] feat: Add env var for log level --- src/config/common.ts | 1 + src/config/schemas.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/config/common.ts b/src/config/common.ts index 200c012..58c0816 100644 --- a/src/config/common.ts +++ b/src/config/common.ts @@ -7,6 +7,7 @@ const commonConfigCheckResult = CommonConfigSchema.safeParse({ // the code(like here). It also allows us to have shorter names. BASE_URL: process.env.NEXT_PUBLIC_BASE_URL, DEV_MODE: process.env.NEXT_PUBLIC_DEV_MODE, + LOG_LEVEL: process.env.NEXT_PUBLIC_LOG_LEVEL, VERIDA_NETWORK: process.env.NEXT_PUBLIC_VERIDA_NETWORK, VERIDA_RPC_URL: process.env.NEXT_PUBLIC_VERIDA_RPC_URL, VAULT_AUTH_ENDPOINT: process.env.NEXT_PUBLIC_VAULT_AUTH_ENDPOINT, diff --git a/src/config/schemas.ts b/src/config/schemas.ts index 4854c57..9749d67 100644 --- a/src/config/schemas.ts +++ b/src/config/schemas.ts @@ -9,6 +9,7 @@ export const CommonConfigSchema = z.object({ .string() .optional() .transform((value) => value === "true"), + LOG_LEVEL: z.enum(["error", "warn", "info", "debug"]).default("info"), VERIDA_NETWORK: z .enum(["myrtle", "banksia", "devnet", "local"]) .default("banksia") From 11d9193a90a08623104b186d9760cf39959c9c6f Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 24 Apr 2025 12:01:42 +0200 Subject: [PATCH 03/18] feat: Integrate TanStack Query --- package.json | 4 + src/components/root-providers.tsx | 5 +- src/features/queries/queries-provider.tsx | 105 ++++++++++++++++++++++ src/features/queries/react-query.d.ts | 23 +++++ src/features/queries/types.ts | 6 ++ src/features/queries/utils.ts | 22 +++++ yarn.lock | 46 ++++++++++ 7 files changed, 210 insertions(+), 1 deletion(-) create mode 100644 src/features/queries/queries-provider.tsx create mode 100644 src/features/queries/react-query.d.ts create mode 100644 src/features/queries/types.ts create mode 100644 src/features/queries/utils.ts diff --git a/package.json b/package.json index 2a06120..7c7611b 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,9 @@ "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.7", + "@tanstack/query-sync-storage-persister": "^5.74.6", + "@tanstack/react-query": "^5.74.4", + "@tanstack/react-query-persist-client": "^5.74.6", "@verida/types": "^4.4.0", "@verida/web-helpers": "^4.4.1", "class-variance-authority": "^0.7.1", @@ -55,6 +58,7 @@ "@eslint/eslintrc": "^3.2.0", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/forms": "^0.5.10", + "@tanstack/react-query-devtools": "^5.74.6", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/node": "^22.12.0", "@types/react": "^19.0.8", diff --git a/src/components/root-providers.tsx b/src/components/root-providers.tsx index 4c7e1fc..e4e0def 100644 --- a/src/components/root-providers.tsx +++ b/src/components/root-providers.tsx @@ -4,6 +4,7 @@ import { NuqsAdapter } from "nuqs/adapters/next/app" import { Suspense } from "react" import { TooltipProvider } from "@/components/ui/tooltip" +import { QueriesProvider } from "@/features/queries/queries-provider" import { ThemesProvider } from "@/features/themes/themes-provider" import { VeridaProvider } from "@/features/verida/components/verida-provider" @@ -21,7 +22,9 @@ export function RootProviders(props: RootProvidersProps) { - {children} + + {children} + diff --git a/src/features/queries/queries-provider.tsx b/src/features/queries/queries-provider.tsx new file mode 100644 index 0000000..d337587 --- /dev/null +++ b/src/features/queries/queries-provider.tsx @@ -0,0 +1,105 @@ +"use client" + +import { createSyncStoragePersister } from "@tanstack/query-sync-storage-persister" +import { MutationCache, QueryCache, QueryClient } from "@tanstack/react-query" +import { ReactQueryDevtools } from "@tanstack/react-query-devtools" +import { + PersistQueryClientProvider, + removeOldestQuery, +} from "@tanstack/react-query-persist-client" +import type { ReactNode } from "react" + +import { + getLogger, + invalidateQueries, + logError, +} from "@/features/queries/utils" + +const PERSISTENCE_MAX_AGE = 1000 * 60 * 60 * 24 * 5 // 5 days +const GC_TIME = Infinity + +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + refetchOnWindowFocus: true, + gcTime: GC_TIME, + retry: 2, + }, + }, + queryCache: new QueryCache({ + onError: (cause, query) => { + const logger = getLogger(query.meta?.logCategory) + logError(cause, logger, query.meta?.errorMessage) + }, + }), + mutationCache: new MutationCache({ + onSettled: (_data, _error, _variables, _context, mutation) => { + const logger = getLogger(mutation.meta?.logCategory) + + const keys = mutation.meta?.onSettledInvalidationQueryKeys + if (keys) { + invalidateQueries(queryClient, keys, logger) + } + }, + onSuccess: (_data, _variables, _context, mutation) => { + const logger = getLogger(mutation.meta?.logCategory) + + const keys = mutation.meta?.onSuccessInvalidationQueryKeys + if (keys) { + invalidateQueries(queryClient, keys, logger) + } + }, + onError: (cause, _variables, _context, mutation) => { + const logger = getLogger(mutation.meta?.logCategory) + + const keys = mutation.meta?.onErrorInvalidationQueryKeys + if (keys) { + invalidateQueries(queryClient, keys, logger) + } + + logError(cause, logger, mutation.meta?.errorMessage) + }, + }), +}) + +// For security reasons, do not persist user's private data +const localStoragePersister = createSyncStoragePersister({ + storage: typeof window !== "undefined" ? window.localStorage : undefined, + retry: removeOldestQuery, +}) + +export interface QueriesProviderProps { + children: ReactNode +} + +export function QueriesProvider(props: QueriesProviderProps) { + const { children } = props + + return ( + + {children} + + + ) +} +QueriesProvider.displayName = "QueriesProvider" diff --git a/src/features/queries/react-query.d.ts b/src/features/queries/react-query.d.ts new file mode 100644 index 0000000..1ac2004 --- /dev/null +++ b/src/features/queries/react-query.d.ts @@ -0,0 +1,23 @@ +import "@tanstack/react-query" +import { QueryKey } from "@tanstack/react-query" + +interface CustomQueryMeta extends Record { + logCategory: string + errorMessage: string + persist?: boolean +} + +interface CustomMutationMeta extends Record { + logCategory: string + errorMessage: string + onSettledInvalidationQueryKeys?: QueryKey + onSuccessInvalidationQueryKeys?: QueryKey + onErrorInvalidationQueryKeys?: QueryKey +} + +declare module "@tanstack/react-query" { + interface Register { + queryMeta: CustomQueryMeta + mutationMeta: CustomMutationMeta + } +} diff --git a/src/features/queries/types.ts b/src/features/queries/types.ts new file mode 100644 index 0000000..39fa87a --- /dev/null +++ b/src/features/queries/types.ts @@ -0,0 +1,6 @@ +// TODO: Could try to use UseQueryOptions from the tanstack package +export type UseQueryOptions = { + enabled?: boolean + staleTime?: number + gcTime?: number +} diff --git a/src/features/queries/utils.ts b/src/features/queries/utils.ts new file mode 100644 index 0000000..2ce3c03 --- /dev/null +++ b/src/features/queries/utils.ts @@ -0,0 +1,22 @@ +import { QueryClient, type QueryKey } from "@tanstack/react-query" + +import { Logger } from "@/features/telemetry/logger" + +export function getLogger(logCategory?: string) { + return Logger.create(logCategory || "queries") +} + +export function invalidateQueries( + queryClient: QueryClient, + queryKeys: QueryKey, + logger: Logger +) { + logger.debug("Invalidating queries", { keys: queryKeys }) + queryClient.invalidateQueries({ queryKey: queryKeys }).then(() => { + logger.debug("Successfully invalidated queries", { keys: queryKeys }) + }) +} + +export function logError(error: Error, logger: Logger, errorMessage?: string) { + logger.error(errorMessage ? new Error(errorMessage, { cause: error }) : error) +} diff --git a/yarn.lock b/yarn.lock index bbcbf19..b43888e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1427,6 +1427,52 @@ dependencies: mini-svg-data-uri "^1.2.3" +"@tanstack/query-core@5.74.4": + version "5.74.4" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.74.4.tgz#08c4f88f336738d822d9242c5e7d2be50f5c25b3" + integrity sha512-YuG0A0+3i9b2Gfo9fkmNnkUWh5+5cFhWBN0pJAHkHilTx6A0nv8kepkk4T4GRt4e5ahbtFj2eTtkiPcVU1xO4A== + +"@tanstack/query-devtools@5.74.6": + version "5.74.6" + resolved "https://registry.yarnpkg.com/@tanstack/query-devtools/-/query-devtools-5.74.6.tgz#88a29fac2ba3db6c9bda8f40808f808d7a88df0b" + integrity sha512-djaFT11mVCOW3e0Ezfyiq7T6OoHy2LRI1fUFQvj+G6+/4A1FkuRMNUhQkdP1GXlx8id0f1/zd5fgDpIy5SU/Iw== + +"@tanstack/query-persist-client-core@5.74.6": + version "5.74.6" + resolved "https://registry.yarnpkg.com/@tanstack/query-persist-client-core/-/query-persist-client-core-5.74.6.tgz#9e52619afd3d8648c190f7d1e68e960c25a59e9b" + integrity sha512-9OJ4tz4evC53tS4NfxaohctFjE9/sqvK4U5HlUBsBcQRI/fn+75lIpD9RcLIbUPcx78hPC35NSUKD7OX6YNw4Q== + dependencies: + "@tanstack/query-core" "5.74.4" + +"@tanstack/query-sync-storage-persister@^5.74.6": + version "5.74.6" + resolved "https://registry.yarnpkg.com/@tanstack/query-sync-storage-persister/-/query-sync-storage-persister-5.74.6.tgz#95c51baf9045f797094bc8ae51f86b36782e200b" + integrity sha512-zMB14UUlEeACKu9GTSYLr2tgFIXZj+x0JbMlVHmmw8OnalrRTWc3TKSgHcPW3f6kPbilI0hU0fo8AnFdvAJUFw== + dependencies: + "@tanstack/query-core" "5.74.4" + "@tanstack/query-persist-client-core" "5.74.6" + +"@tanstack/react-query-devtools@^5.74.6": + version "5.74.6" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-devtools/-/react-query-devtools-5.74.6.tgz#169a3e2beab0c87ce3ebbda909bbd2c6590239f8" + integrity sha512-vlsDwz4/FsblK0h7VAlXUdJ+9OV+i1n8OLb8CLLAZqu0M9GCnbajytZwsRmns33PXBZ6wQBJ859kg6aajx+e9Q== + dependencies: + "@tanstack/query-devtools" "5.74.6" + +"@tanstack/react-query-persist-client@^5.74.6": + version "5.74.6" + resolved "https://registry.yarnpkg.com/@tanstack/react-query-persist-client/-/react-query-persist-client-5.74.6.tgz#65791015fd20ed3b38ed6fd41ebd5f1faaec7866" + integrity sha512-gGJ+4IKIRWC9JeChBOvGgZj1lQcP3bAT8Kofv1Csx4CbqkyhbALlUkpiUXYyVpeS60acXS6NRAajlfzRtfsE+Q== + dependencies: + "@tanstack/query-persist-client-core" "5.74.6" + +"@tanstack/react-query@^5.74.4": + version "5.74.4" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.74.4.tgz#d73ee1899c08a227519cbf53b9a0e0b1e67cd3fe" + integrity sha512-mAbxw60d4ffQ4qmRYfkO1xzRBPUEf/72Dgo3qqea0J66nIKuDTLEqQt0ku++SDFlMGMnB6uKDnEG1xD/TDse4Q== + dependencies: + "@tanstack/query-core" "5.74.4" + "@tanstack/react-virtual@^3.8.1": version "3.12.0" resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.12.0.tgz#85da7a99f3d07e569849a375b018bdaaac6f5233" From 2751ea2e8fbb1ae6be02158f25a0ac78e3ebce3c Mon Sep 17 00:00:00 2001 From: aurelticot Date: Thu, 24 Apr 2025 12:02:27 +0200 Subject: [PATCH 04/18] chore: Remove unused code on Verida feature --- .../verida/components/verida-provider.tsx | 35 +++---------------- .../verida/contexts/verida-context.ts | 7 ---- 2 files changed, 4 insertions(+), 38 deletions(-) diff --git a/src/features/verida/components/verida-provider.tsx b/src/features/verida/components/verida-provider.tsx index 146ce4c..2fb430b 100644 --- a/src/features/verida/components/verida-provider.tsx +++ b/src/features/verida/components/verida-provider.tsx @@ -1,6 +1,5 @@ "use client" -import { type DatastoreOpenConfig } from "@verida/types" import { WebUser, type WebUserProfile } from "@verida/web-helpers" import React, { useCallback, useEffect, useMemo, useRef, useState } from "react" @@ -166,52 +165,26 @@ export function VeridaProvider(props: VeridaProviderProps) { return sessionToken }, [webUserInstanceRef]) - const openDatastore = useCallback( - async (schemaUrl: string, config?: DatastoreOpenConfig) => { - console.info("Opening Verida datastore", { - schemaUrl, - config, - }) - - const datastore = await webUserInstanceRef.current.openDatastore( - schemaUrl, - config - ) - - console.info("Verida datastore succesfully opened", { - schemaUrl, - config, - }) - return datastore - }, - [webUserInstanceRef] - ) - const contextValue: VeridaContextType = useMemo( () => ({ - isReady: isConnected && !!did, + webUserInstanceRef, isConnected, isConnecting, - isDisconnecting, did, + profile, connect, disconnect, getAccountSessionToken, - openDatastore, - profile, - webUserInstanceRef, }), [ + webUserInstanceRef, isConnected, isConnecting, - isDisconnecting, did, + profile, connect, disconnect, getAccountSessionToken, - openDatastore, - profile, - webUserInstanceRef, ] ) diff --git a/src/features/verida/contexts/verida-context.ts b/src/features/verida/contexts/verida-context.ts index 91171b1..41ef2d5 100644 --- a/src/features/verida/contexts/verida-context.ts +++ b/src/features/verida/contexts/verida-context.ts @@ -1,22 +1,15 @@ -import { type DatastoreOpenConfig, type IDatastore } from "@verida/types" import { type WebUser, type WebUserProfile } from "@verida/web-helpers" import React, { createContext } from "react" export type VeridaContextType = { webUserInstanceRef: React.RefObject - isReady: boolean isConnected: boolean isConnecting: boolean - isDisconnecting: boolean did: string | null profile: WebUserProfile | undefined connect: () => Promise disconnect: () => Promise getAccountSessionToken: () => Promise - openDatastore: ( - schemaUrl: string, - config?: DatastoreOpenConfig - ) => Promise } export const VeridaContext = createContext(null) From 8fb3dfc4e1586d4a149b3aac1acd6108452462c6 Mon Sep 17 00:00:00 2001 From: aurelticot Date: Mon, 28 Apr 2025 15:29:51 +0200 Subject: [PATCH 05/18] feat: Add Verida app DID env var --- .env.example | 3 +++ eslint.config.mjs | 1 + src/config/common.ts | 1 + src/config/schemas.ts | 1 + 4 files changed, 6 insertions(+) diff --git a/.env.example b/.env.example index e6bde15..a729c04 100644 --- a/.env.example +++ b/.env.example @@ -18,6 +18,9 @@ NEXT_PUBLIC_LOG_LEVEL=info # Optional: Set the Verida Network ("myrtle", "banksia", "devnet", "local"). Default to "banksia". NEXT_PUBLIC_VERIDA_NETWORK=banksia +# Required: Set the Verida DID for this application. Required for Verida auth. +NEXT_PUBLIC_VERIDA_APP_DID= + # Optional: Set the Verida RPC URL. Default to the SDK default public RPC URL. Strongly suggested to set a custom paid one for reliability. Production deployments should set this. NEXT_PUBLIC_VERIDA_RPC_URL= diff --git a/eslint.config.mjs b/eslint.config.mjs index 13ecd15..86cfff3 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -25,6 +25,7 @@ const config = [ rules: { "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-unused-vars": "warn", + "@typescript-eslint/no-empty-object-type": "off", "no-console": "warn", "prettier/prettier": "warn", }, diff --git a/src/config/common.ts b/src/config/common.ts index 58c0816..b4b8b11 100644 --- a/src/config/common.ts +++ b/src/config/common.ts @@ -11,6 +11,7 @@ const commonConfigCheckResult = CommonConfigSchema.safeParse({ VERIDA_NETWORK: process.env.NEXT_PUBLIC_VERIDA_NETWORK, VERIDA_RPC_URL: process.env.NEXT_PUBLIC_VERIDA_RPC_URL, VAULT_AUTH_ENDPOINT: process.env.NEXT_PUBLIC_VAULT_AUTH_ENDPOINT, + VERIDA_APP_DID: process.env.NEXT_PUBLIC_VERIDA_APP_DID, DCS_URL: process.env.NEXT_PUBLIC_DCS_URL, isClient: !(typeof window === "undefined"), appVersion: version, diff --git a/src/config/schemas.ts b/src/config/schemas.ts index 9749d67..c3a1ccb 100644 --- a/src/config/schemas.ts +++ b/src/config/schemas.ts @@ -5,6 +5,7 @@ export const CommonConfigSchema = z.object({ BASE_URL: z.string().url(), DCS_URL: z.string().url(), VAULT_AUTH_ENDPOINT: z.string().url(), + VERIDA_APP_DID: z.string(), DEV_MODE: z .string() .optional() From 2665d290351df421b4faa36b0f97f28c9d1bf67e Mon Sep 17 00:00:00 2001 From: aurelticot Date: Mon, 28 Apr 2025 22:32:40 +0200 Subject: [PATCH 06/18] feat: replace Verida Connect with Verida Auth --- .env.example | 3 - next.config.ts | 14 +- package.json | 11 +- public/fonts/Sora-Regular.ttf | Bin 57644 -> 0 bytes public/images/verida-network-logo.svg | 12 + .../images/verida_vault_logo_for_connect.png | Bin 16667 -> 0 bytes src/app/(connected)/credits/page.tsx | 65 +- src/app/(connected)/dashboard/page.tsx | 52 +- src/app/(connected)/layout.tsx | 12 +- .../sandbox/generate-token/page.tsx | 193 +- src/app/(connected)/sandbox/page.tsx | 2 - .../sandbox/token-generated/page.tsx | 2 +- src/app/auth/page.tsx | 45 + src/app/page.tsx | 10 +- src/components/app-connection-handler.tsx | 28 - src/components/root-connection-handler.tsx | 37 - src/components/root-providers.tsx | 4 +- src/components/sidebar-nav.tsx | 4 +- src/components/ui/accordion.tsx | 58 + src/config/common.ts | 2 - src/config/schemas.ts | 19 - src/constants/app.ts | 6 +- .../components/app-authentication-handler.tsx | 30 + .../components/authentication-loading.tsx | 24 + .../auth/components/connect-button.tsx | 32 + .../root-authentication-handler.tsx | 38 + .../hooks/use-auth-redirect-path-state.ts | 22 + .../auth/hooks/use-auth-redirection.ts | 32 + src/features/auth/utils.ts | 15 + src/features/dcs/api.ts | 41 +- src/features/dcs/utils.ts | 4 +- src/features/routes/utils.ts | 43 + .../components/verida-auth-provider.tsx | 70 + src/features/verida-auth/constants.ts | 34 + .../contexts/verida-auth-context.ts | 7 + .../use-get-verida-auth-token-details.ts | 29 + .../hooks/use-verida-auth-response.ts | 35 + .../verida-auth/hooks/use-verida-auth.ts | 11 + src/features/verida-auth/schemas.ts | 13 + src/features/verida-auth/type.ts | 52 + src/features/verida-auth/utils.ts | 122 + .../components/verida-connect-button.tsx | 47 - .../verida/components/verida-provider.tsx | 197 -- .../verida/contexts/verida-context.ts | 15 - src/features/verida/hooks/use-verida.ts | 11 - tailwind.config.ts | 22 + yarn.lock | 2421 +---------------- 47 files changed, 1068 insertions(+), 2878 deletions(-) delete mode 100644 public/fonts/Sora-Regular.ttf create mode 100644 public/images/verida-network-logo.svg delete mode 100644 public/images/verida_vault_logo_for_connect.png create mode 100644 src/app/auth/page.tsx delete mode 100644 src/components/app-connection-handler.tsx delete mode 100644 src/components/root-connection-handler.tsx create mode 100644 src/components/ui/accordion.tsx create mode 100644 src/features/auth/components/app-authentication-handler.tsx create mode 100644 src/features/auth/components/authentication-loading.tsx create mode 100644 src/features/auth/components/connect-button.tsx create mode 100644 src/features/auth/components/root-authentication-handler.tsx create mode 100644 src/features/auth/hooks/use-auth-redirect-path-state.ts create mode 100644 src/features/auth/hooks/use-auth-redirection.ts create mode 100644 src/features/auth/utils.ts create mode 100644 src/features/routes/utils.ts create mode 100644 src/features/verida-auth/components/verida-auth-provider.tsx create mode 100644 src/features/verida-auth/constants.ts create mode 100644 src/features/verida-auth/contexts/verida-auth-context.ts create mode 100644 src/features/verida-auth/hooks/use-get-verida-auth-token-details.ts create mode 100644 src/features/verida-auth/hooks/use-verida-auth-response.ts create mode 100644 src/features/verida-auth/hooks/use-verida-auth.ts create mode 100644 src/features/verida-auth/schemas.ts create mode 100644 src/features/verida-auth/type.ts create mode 100644 src/features/verida-auth/utils.ts delete mode 100644 src/features/verida/components/verida-connect-button.tsx delete mode 100644 src/features/verida/components/verida-provider.tsx delete mode 100644 src/features/verida/contexts/verida-context.ts delete mode 100644 src/features/verida/hooks/use-verida.ts diff --git a/.env.example b/.env.example index a729c04..014ad6c 100644 --- a/.env.example +++ b/.env.example @@ -21,9 +21,6 @@ NEXT_PUBLIC_VERIDA_NETWORK=banksia # Required: Set the Verida DID for this application. Required for Verida auth. NEXT_PUBLIC_VERIDA_APP_DID= -# Optional: Set the Verida RPC URL. Default to the SDK default public RPC URL. Strongly suggested to set a custom paid one for reliability. Production deployments should set this. -NEXT_PUBLIC_VERIDA_RPC_URL= - # Data connector server URL NEXT_PUBLIC_DCS_URL= diff --git a/next.config.ts b/next.config.ts index bc2a70e..0a1f715 100644 --- a/next.config.ts +++ b/next.config.ts @@ -5,23 +5,11 @@ const nextConfig: NextConfig = { // TODO: Enable the check again. This is now to disable next.js checking node_modules type errors on building the app ignoreBuildErrors: true, }, - webpack: (config, { isServer }) => { + webpack: (config) => { config.node = { __dirname: true, } - config.module.rules.push({ - test: /\.(woff|woff2|eot|ttf|otf)$/, - use: { - loader: "file-loader", - options: { - name: "[name].[ext]", - publicPath: "/_next/static/fonts/", - outputPath: `${isServer ? "../" : ""}static/fonts/`, - }, - }, - }) - return config }, images: { diff --git a/package.json b/package.json index 7c7611b..c39a7ae 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "fix": "yarn run fix:format && yarn run fix:lint" }, "dependencies": { - "@headlessui/react": "^2.2.0", + "@radix-ui/react-accordion": "^1.2.8", "@radix-ui/react-alert-dialog": "^1.1.5", "@radix-ui/react-avatar": "^1.1.2", "@radix-ui/react-checkbox": "^1.1.3", @@ -37,8 +37,6 @@ "@tanstack/query-sync-storage-persister": "^5.74.6", "@tanstack/react-query": "^5.74.4", "@tanstack/react-query-persist-client": "^5.74.6", - "@verida/types": "^4.4.0", - "@verida/web-helpers": "^4.4.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "genversion": "^3.2.0", @@ -70,12 +68,9 @@ "eslint-plugin-prettier": "^5.2.3", "file-loader": "^6.2.0", "postcss": "^8.5.1", - "prettier": "^3.4.2", + "prettier": "^3.5.3", "prettier-plugin-tailwindcss": "^0.6.11", "tailwindcss": "^3.4.17", - "typescript": "^5.7.3" - }, - "resolutions": { - "qrcode-with-logos": "1.0.3" + "typescript": "^5.8.3" } } diff --git a/public/fonts/Sora-Regular.ttf b/public/fonts/Sora-Regular.ttf deleted file mode 100644 index b1f11eafe7b2ecf600562ede6c8c10719e2b7f19..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57644 zcmb?^31C~r(f{s~EID@K`;HGCw&X*WW!PhaC+jE8+c!HqJ3BKw zJNry1AtVaFq=eK|R#lHZwCD^%F1eTxVQWoGb9=b*`gaJ~9ZE?3;hOeN_lpDBR}=Eu zF+xJk&Fuxo{v$P`gsgi4So+&L$~(@yd?JI8zv3&&9a=Rod1BYP8A7HMXuvZzux=9Y zDB$0UYv|a@Ge@J=?g2hJpO8`W_{hL;@@ZWf8F)?o;kj1YE#>p zBWL11zK4>PYla3wl0Vo>$XGw{3#$e;O$ssOAild$U$J^%)yQzgB}WO_G==*ylWW#Z zJ^6alBZO>5|02GaTsty3F57tl?yp3BIe5@J^NNH@LdimsLDWP`$cV*cv|7v>wMrJR zHe0PGV?siFe4I=sx5?!eQ+!CYGCE5ct)%DW@5f=2U2D%F2s9p4}4Tch6acBVag5#jn?d<76ILfId?b zRZ9}pHi^xqOiZQneKd093&RG}N5x87tQ3w6BjYgKH^=`}*;&|Gwnrz;nk4ei;4Q+vEp)<^VsjISCp71TD&I*QTl8gP~mmMfJN z!xc-nMhu6w{ye67^8Atn_x6?U%Cp)_TeI_9CrXPa+6tB~YS>)Qc*I-dV;t!l<1x;= zx$uMJZ$5Y^qYgY1>pxBxowojA@_rDUjlEd^1noyV0_X7u;Bg8`7dc!iSj^}k<8itG zUPjAR`4cNCJrnXVk5mGi4?QfPOlBlL?TQs;%O-y}2>#6BIYwYH%)!xe#aeX(_ zb&9y9hW^^Ox=QS}WZ}%1>6Kv1mKfoRX+`ZoA?LjyX!r~j+Zv^IPJ^HK8p6gzMjy0= z1Sn?nPpo~6T#LJyf9Q!27#Th;5=$0WWo%3w-lu3i8xLC5Lwl$b`cPnUEJ?$x(UQEM zm^Vy~%VZL!7x#DW7_t^Fxv*pL?k@ST+&y4-^jB$e>PMWN=P5J$3iocI7sc2|FKJ7t zK7V;h)!D5jtvNZK33tO84Vo(3#y{WVs->#7Hs3swr14SLaA~-Ye9e2#=?a1$_Rk~l zyNq7+U95kUt_iBoMqPxTpi6*{_3u;ZN#O%hNQPMdG#ZO3E+GMWfLSZ87J9;Ot<+`> zGhed!xP-(+OeGAX^1rN4+q=)L8c?-WyIQjw>piYz=CZX7p0%z4b(O_ZsdScj8dern zZV`;#n=1ws&DCYin#TGTRCBHKG_q>d7E86tS<+I!ve>;j%06C`=GJS=GV`2rxvZeQ zxN^v59;#K8WM?_m+L92t+}KfEF;*0)rz>XlI*R7jlNmdlsOOiFhi7GE0P-rTWM#b2 zGdR5;a`FI^5iTcX5E0DyTrl#Rz%!Wf?GOS<(382s3%B^6)wQ%tzjP`FN|@j99LK<= za^)ba0Eq>9Iv&7GHUg*IXL#=&e27MQKc!JFp?7+ptD{4>u%m-X491bRammmFtBhPT z#Gnk*l^j|CC~Pj-$K+z>E5Iv)YTx%2s|{Ar3j?)fD6XYgN@$0Dl z+$tQT0nFpGRM1ofpuXNQ9mT1jjX}L*P)-GHWW6J*8G*oS3e+9_fKxFeun`E< z9knyOpQE)!+`@}^kA2SSS^>SDLq)vD01$`QDU9>kiO5ciL-SME*e=jXdf)M zTFY^XGmd!HoaZPlbqHzGUwB50ppjF<`Q!#4<>WBAj_U{D6{a}6!Uy-_3VVuQ`_Ue= zWn_S=;Z15On-PN5`|1kXKIuI+k6t|e@F4A^7kF>3@|JVaMN1RjciEHRTs6mdl<90A zMww5h1GbUr-zN4XdWmNTP*KLIsl_vp;Q3Y(j1%%eMA^4_{4gEdWaI?nDm(oxz0qD3m0C4)lHgVIQD!(&ac zcq%$kqL$dAC$_y7_3HV9^5-|b5Gow^Ceyz|C9Dy=x%3rb`cm;(ZUvbK?l*kg3{HsV z?iTMZMI4&qR!$>qgU$2x@bA3eO}vNK(DV4{u$~9Q5BfY&#$(u8^jxfe)aNyc^_d0a zaC(eJ1D|Nka$(KlNzN(O%;yzD58@87jlpWnB8&^1=8(bSH) zeAk%jwcDHt<=a=e-K(~jCpfpQt?o6nFE1*YXw~amCrXNzw;KovftC4C@($XKgZ0V9 zTr~35C4L_?5sa0|v~WJ<(TRR5lP-k#R2^Sy9BeyhaZ##%>7_$C)k$4lN!2-;?hK_e zqf6VfrBYKrPQz!{9&!5S!EpoJ(^c;9dDEJC;XP4NT48BcbY$y=ZAB|O43(#>N0=?m z@LT6 zO;q)AhgW!GTIJIf8~hmCPmhr@j3@GJsj2qd6VJ4)ve{O)dfy+U&aQNOmd2LeW#}!7 zD(f|P4|AlPkNFZd3+59Q)7jkX5%s7kUi*^_73|g)U9VQL`Lmzk>7Oz@?w&EFru+B* z312x~4|BSF<6DLC&H8C_Zt;H6Ps6?U{scAEbfxzS^M6PQ8_CbC4ZzD< zxWd;0;uWW~z~UsqYhvJEeC6_My_s*joTa62Ki|62YF*jNvA25frH<|jhohp)uo%O< zn6DNX^?W|V7eWuNp-FsR@isY}#f2CgYZRZ>4X%HXTor&{5mf&mK@5r2ht>smApau% zqtq0DqfdZ~_>WTfJFo@N7s5u!8&u)0 z*y=N-{`H{u2_ap$YI;K02E#8or51Fa-;ZW>`{-UTuTEVpEqKiT5NP4Jd7lt-gtu^T z6{9d^CX}@Z${AEn9w%>c%2PmLF#ITak5kB`qZSnYr1sita%E(=OnY~BP8AnVbsitw z*_~OQmsg(Emo-I!l(AO-oAYVQ+2UC5`9_$HV&vyTs+TmG@*W{660ra4!5X`D0Lh=97zf zUiNWhPJP&?XM^gqI2x}{bEzZ<&g_#2Zw$06u>J<%qf`@ApT*I{`q$IAAULxlBK#N+ z_)?4j5%z&^hu62#Sd)atPLI4}*kpQf@SkAsXi}1}7fU2a7UWm*1+NWBTNkL$ z^p*JJJAWAYRl!E{C4D#1sC$>)VBBQ7cJMbo9Gj;rg-55i`>=u692@8L`95C1$Z4Gm zKTO+#;EbnY{g+uB>ic+TDs2Eh%A(f1Z_KBp@t_g^Bw9ph)siS0_4S&^qn@0Yp)_vI z@u)x13uEZ0_kEh=y(-4Lg_^wo!}`lGoW=W#nb;oaCG%;&et-0^W<&PD{^Q&G(cx09E{1-n*aqdIuh#2JVj)iS=3U#QOeN(HuNtObERz1#SJHjrGn# z<=R9gbK$g!T4j7GeUhd`ds~J1-V&-vrq65=B9k)1+cr)AD>Xlg$2$1aoVwjU>YxKa z-CX!VMoj?Ds1xg-pi(?15dF+&^q)d1yiGkM$f6eG?lQSW40y&z%URrlXY9XQz6L(b(itpTuq>7z6Y0){aa24J+zKNi7-qa zljKTe%<_Q0zVFbKq}67U_l&F&hL0TKcO-G-6UjmUopUFzmc-q5TL820jgJl)d1*bX zuZPe3b3sF(Nmw*ki>R;qv|O3XJhhLB@fce9Y)FW$XIbc2xZ2w0)P|>oSh|bmGS3dy zmT=a#4(Z%sj?sqBGwj|LI+2i7Yq2+EWi>iWn?rlU-9s^{g|4ikM(wGby*$8oO^>Iz z-%-DzBDXf%(WWylDt7dmELpbZ+(3_KIZqdoowJ-5-t>7B z2WTMbl^D-r<2>X=Ll}D?HMT?^0~2FpCd89tA>%@__fL9LeBnfgvP}`^Dvhgg~2bUB#wa{;bKf+p&cIX(U zU)=jv-_~?q()qY^gLjB_dw&Tzh$A1S0Y=md`0v2UjbUBF$icq&y z>qr;BDCO+S$ElgAX&RberoxBxEM1w3annE-3UWy}T8?B9k8-WWmMD);l*r{;TcS-X zu_Y#IZCW`!e(kmS=bwKxy5p|=by-ory{jX-H!5o#-MF%UWs@VQ&XK5ZAEsjTa#C=&UAfVbN+8vPaL?|<5+X+ik=-U*){sonx*=Z zad(t^w8+p~{+RbzYG$-;xN7mmz4@+n*DgBip)t96DNZ*^cvEiS=W=b+~j=mz(gQFh1cYjx;t zO=u2wZ00cHd-IvT>4pvZh zf!;xn&2o>~Aq$n+E%#e&9^S$Zw4nKqTQIhcuogyIlalfZSPKPtNgMimf5}_8y|>?E zHvhuc!tG|WI1}rsOA;!rA$QVEggil9qR!a?s0Pqq`l0CmYV_BQ{)&4+oEs3wcnQkt zKEs`N-g!XsyYJi{k7TXK!{*NFnH{LlBELM|#<+KC{SA`qzZ*vV7SxAiSb;x`({i`J zRSw+&D5K@qekeFYgr(Fd$z^-O?J5Y1I0Z~H+1>_^#4XLs$;p?NO6rBBD+Vg+>M90S zuxBI@)B~#mdyZ#jABqt6Icn6BNImwz2u^aN zM8~K{bVA;27A}d;vo}8vB%87DRSA7;DbxA|wqoQJ`mSq*_@|=t}Goe^h{5YoCSZzdP zcC<>Bo|f&>MHScAeJzt#N@ej;kujT=Wf}5yW#F3`d@h2nQ1Qqhw?R+`qK}cNK4q-g zP_s=oHhZWGL#|M|M=Hz5D?Q`;8xMfEa&Z)%E-dmXSnMn9Bk=w^J9v)^64#cUH0fUf#N)Axq=g(A=`#qseO6 z(As!LNy!)SCuF9|H{*SH8v8QL)v$c+lbO#jx!2hDlU4x);u&itlG!D9~0Esj{ zmF3FLb*a^^+-z6Yg#j7`uCY63J)dCfpcO4T;}oQzK2C$$ed>)u@}f=TZpIPIq_Mh z)eQV$9}bwxXFQ(i)$hG`*yGvz-g|T@aQUpp=Mo*XJ;DLd6%PuX0qbxbkG^m?JxIG@ z{gDku@1R|vHxk)s+=}}>V@x}?=fVsCU*bHZugQj+T2P&xU0sl)*XN+nJ94XYbF29O z>RgvK6EjwwX=QW}VLPo9zJYaliMQ|j4!Zur>G}&fD#9f?+QoN8PH^}MRv&zHlOAEd zz@mPfk1W;~0v<3vxal+AzmJcPQ=`Xod~%ZRnVe)#4B?6L(?2mp-|)UZ_X%dF4%53N zd3cs|BAQNnTfF<6lCkfu1;jpcuFxha=DSgB)yL+cFZTf~(Q17m0yZm8NqxX~FfL$- zs=79n^(vbxd%K(J-TiAz)-*J%a`q}JY=u=>r7ri1^W2-ZboF*Qme;7N3p|Aems~b) zaB~~s$-`#Z#K}rC@S{ji7q+XCQ{ z(`1J)g~&@~TYE)CdwYz1*sZHEM}#fVIW=WH1qD528mDeSScIh}&pm8!QOiSx@ z}iph$~H5K@v zajL7%YDQ!#7ktIrI9Bp&>0SnBS{QIziyPt- z^y8o>=!w7+fX_ZbPl!*jsDxe6A+8vXvG(WxA>Bo$&Wc8Og2!CpIwFrkv^vqWusTQA zKQQS1+%rF~r$Rb1&|h6r)^xNhKf6#p*nLz;_*?TT$Ci!W=V?`GLsPl;_hJwAz7>+# z!x8MXD`4x~b)Zeeo{UL38e8!yi&THjUHV+skQN?l@U zVY+qULQBSqZN-)igT>9-+~y)jQ?|Bwnd&`NTz*-6mPvizhn?;92UOdZIh2TKU%x8#dzK?8c1^U1uMBMY{ z8NZx#G49P5(A)<>r2fiF_g=B5YtkXUP|8(~i2b@0njt{joqDAux zuYun0?WgxgJbEzV;G?|7OK!YjXI+_V(C%+hAie0Rm^X*<7PI3_@k4_vrj855OOBs) z7WmUU<3ybVL_@y9yh(5&V#A=~lRoHi28CoOAoOdnEA7kM4TPnTlC6xNSi0EVmX*Da zHVcJ|I$Clvryr+BYmtg1KDQZJl-#ph7cJUDcn!?45})${uoq$I>O9%5bL5H%CXnGTrN*aQc@BXJZ`s}6FvPMHoS)%wY3gT^$n~CHATTa zVDpbi_Mk1X2duq9*@dxV|Vd#V)hD^P?Iv7^74 z?Qz2N2lTnxTB*l#iz+2W#R^+%F>0;}^qghy+J3tKKS&kl$->0MWGdl2NlQ*%$cVdG z*kirF!cW|Yqq^FWn4X?EeVwqPsH&=nf4)*U9f_cD#xA)Wc8BpRlVsyW0JO{xjXU@R z8wjRB%cy^xah9sA>`RL@Iel5J71Pzi<<`4?n0rUNa5#Jd7a6e+w8 z6h`6{iw@ZfW`sI_Qigt-QXexDQozjeatK&*a!P>UQsKHFf|onoZby7-YW(!o!ioTQ zulzBxvD#u|zcVnGW+neL^T|KrHpt=|5;y~zIWt`K%x@0=C2d{OugvF&{}^N+xq zNHLDzNM7|x8spMWawvqYG}ut`5I399vr|q|UtW&cmzh=)VUX! zhgZvzZF*sFdT(re8+`_f1bv77b#kdn;+kJ~s!aX#=qz0vEI{5Hs559h{Ceic24I$k zNT2OF4NrcWj_--a98&&6ap2M*IJh)0?VK3UgFb0snmrby!PcE~G!aul`>17m|2?w` z$j&49w)M+-0NzwbV`U$q45+aG9I%ripz zEo%|hKCqhgyCiaTtTJ9{lW2oH7V7MnaxTrOHyImpa~e!pp2VIkdan1Oq|}tOc1y?F zV(0pps>w2aZEjATUSF?EPNdu0l-|G1OH);?Dvl^#RRvp`HS;AMrk7#sDvbO26w$da z=&*1p^E=?Nmny5~ z3w5a`U6Y@o$cv78r#ALyLSmY-SR>Q8H1JS6;k^%`&)GlGXaBA>^e4rlxTG(y(PV1O z!|$x-#6?;3Lht?PbBxiI)js7c*}ywpmy=t2N~hyeBYnMQ^Bxx4jFy?Nz;nBUb$9Rx zL$5G}vX9!%>A&L+!LLP_2Js>h35mnI#oZrh5@C~1a)6!V3$oT6XRz*=2q=qv)b*ZE zT?or()dO=byw+JA0Z#>UQ5c7Q;4znt7Me&C#Y24tT2O&;rLR5G`|Az#X^Xcb#`9|- zWcpj?Ev3OPI+O0j2xFyzm7D;`3_Vl0)*rjU7H^`79}Y~ry?l>+W{Zb5w|$-cN|?Ka zD!gx@QJ%%I>2xiZQeB1j{dx5G!>4PR-bT-ql!`5r+XH#w4%@M-{nB;+Qu>*f@6gU;V^Uhy&YhRNU$oiSzXtyRY*#Q zoc1LZS(?gaj*8J@Nvfy#ye30)L1B~Lj7`(#7i1x=A#!bd`KYILg(c53WQ(y6ASYo} zGkw>a)4$P9GqK{+TkAE3?&9K}0`QVk$kz8P3kv+(PagKI+CX^_{1CY}2<{Bjf0|{~ z`SGy0f)CGMC{};qK<3Ozp&DMeZ`I=G04AT9RT;EIVaqV=ys{7%^DAt=4=gN67bg|Q z#aUx(Dz$YcZK9?iA+aD!YfA24IU;rS78rU;WnCMb_T}Dld*_Ep+oT~Lo31*?qq3x? znN>KMIz2-_Uwgi#+iY&mQ&{`UN|ssZF`mhQT@UgE+x0+XgRL=r-`RMweYVxBmv{4| z7TiHj%g16)3%hg06>B&1q!tPBkWI+INx?HdInIi=WLZL3P|iaSchw)5dm0P( zG+jQ5rK}WBWa5cT-xJu+WGmn-oc@~gocL^}$8%NV`dY*KhO>t7IJgaoqVLgnu;zvz zjkUHK=TSuXF8YDc3^;Th;F&(S9reFN{YaXD49Bo1_=u5R^h?x@qzb@JJi&U;@P^TM z&=0Z^nGz;sO5C;W;@*od)*i_{_HxY2Vx3(yLTC=IbNBWgeLHvVI;wl_)wov~Zq{B3 zZ%<9#LG7jR_A&tz+v@|rb~>`|FBajYtLXG_NZ?cLQ61 zm)CYyd5@tr?Bp;kkUiKM^!gwvpc^537)x4-9V~JEjov`l%WtV!-18v{8`UF6SS{>f z4e@bBuiJgK?&=&q<1-XC!nfT9&bY}(^uTQF!A+;c*n^wa0)9Q(;46HzE^~e{-$+hW`8S54gk3FF(b2qi4WDWB4pYd-S<&9ljV>2>OKpAWX3yVCVz!Ko{y|R1j1^9 zR~WH#oFTz3t|#X0_cQMpTRD_WzsOe(9?$Hufwebdrg~0s5$E9{v?uu5OW^H&zqQ#G z=3s`rFqtie%<{QDpj9Jd(#Fpq(PK~dd@=HdoG$Px`~OYTyfY^6L1t0WYtrZakUfQ7 z7lR5pd5VVODzM#e@+7cv8TIpgu>Y>R0;ZS}&&)S;KI+WpJLR+9`f;C;4f4Cu5-?I7 z%Uh%+yua6!u=d@+QOKdlUxXxG<7QA+x1a6a@VeN&Ay0BW!dk57a8{ea@8<9%?92X{ z_d>+|R5!!o@qWnY0~QhYQa==~={%@Lw?wMA%Ccgg0T!JQy`*%lLQ57Iw@LPfHN?R#h>$ z#Em?fivn;Y+;DgcR%;uCMCQG+TpuwJHLy1132inLGtKOymhJz<$G7xX7G;;H+0xLM z<5bR{pWE(|D$4cJ)Oquc3U7KJEW^f)j+S|cuqs^WELev4X_BoVqqHpJY(}G-8WAJi z!f0U;QtY*{Z8mmi2zSb7?;s-TzvHCG$TNJ5SvH}>>1+dpMtDENe5xcHgfO94oKGV* zDvzaFN?&w%*EuZoQj2%Z1_#~g@J`V^7VjkLFDH|jm*rypji?)fXKti7Sdf!=t;0L! zn55TRyu%g;y~e?)VzZw`ltqgb7|);>GDa~dbbR`VF~`Dn5=;eT?P>}eh7p_#KL8(- z!=XLUDrT5i|0!R-r5QeYgYYrt*xkbA!tp0qUB%2aUCZ`4=wdv}ykiCVj;_T%M;epI zcIH;G5O^TxCODHKk?*~Vt0T=3d!u%IMO;p7qAtqfNQkAiWfe_*jd^-~p4J$z6|T?k zbjFO2ON1&xqAt&KMLQ}RoaJqm`3gl|jw)Uq4{Dq<-wMr~!g~?46Dj1qaz6O9ld6xY}tGHSz+EDAID|Baa8j0nhW1g9y%@j5Zem@mqfamJr?o21V0?iU~p*^ z`s0M=oYNn*FD>cxn~|8LtoJM-cc0*c5YNldga$0hD z`e!z?zA7s*J2B0WoRmDTQX0bMpdDwUKSsU^Rv6#IVr6K4YpX6YC=^PCIz1yiEj&Ek zTWKn2&4|xWOt7XTW`;cNb4Cd(fTw0hV|G+=4)V{laPH;sQG%95eb9qhMVS{1 ztF$f}8NSNsW4h<4Al#*Do7SM6bkzC-k4UTp9*QTBNH1{Lv9pZg@8~KC83XQ>@za8zX=_0yRcn#yEL(cT~&n(2_%mUl)opWRX z(Tt>s40U9_J~m359aUPB@rbQXTdnWxQm8XCVpWMWL0V3SR-LU-WBJn{2oxzl?}e4x zwdQ2Aa^XTnWTqmixQt1@hIY}Lg}0EMgc-%MJ=u2AY*x}-p?)VIp}J0|GCwg1Iw(IW zF`sr>y9*bXx-FJ&(}KcoYlG6JRNE8^n_6WBVz#e#3>u>zpI8pm51wKTTBgG6)ze4Y zyp;!jM0uEnEz{dne7FBU&>&nFiZb;q&zcl>F3-Wt=lRsI#_?J)4<4$UMGjzp6Qi+- z?vJL4`P6-$n2@qjXVJ3COr#b$9OIQCx_gmMt!uIJbn2tR8-EwNR+)94VLLW_Jv!s$ z;ub_Q!%T~doxLW1LbZfRoe+hcC-%;!XwH8zPmbr!n&2B8UE1HjbZO2|^8mfHr!Ks{ z+xr_YwyLI8;;B|jYLN>(E(v8%cZ(?jY-^8YyCr^OY1QSonpc;0=j*!54wU@-*Ao+W z?{rk`xqWEpj?1grjH*ZG@i;P%Py2K;A|2+{C0+TtuF?&~7wsD#zx_go`;r?6`fuJ@ z$+T${+BUE}WV+Fxam=ARuns^TGTr2dB0`-x^QA=3S84H(G!udFv3FESPA74*84Evp z_gm(Uez?t1qg2(}>@_N7b&)<_pO63a%hLdv5uh|7Xl{{}2-etm`L`_f_rl{~+1S*YLJM#Xd7ZPk1wr z`u6C%kn7be)*A8EqB(6i4);cvAf5j1gcvjRka8hGIFHvuG6aw6MB6w{53q*d26frS*&SH z({AMJZvR25Q{D}SAUTU@JzG^%r7F?pmuL1_CYq~9Y}OS`nZm@>@rOOOoZ^hEvYg^k zmt&b@zI~}pe+@Rh*^D&IWDD5shM&4!$QBZw5MdtzcH9ROJ;Pr3@g0C^=x%Poc9T1~ zg)+=E(4FLbNH8qp^e=!hm)=D$6yC=~T0N@42`_`AVQa3J?pZJ4LfL?fj*JT*(59;-^obm!B%%n1dV4f#^3IwMUJ zqtA=4YsnD%(?Fi#G@|wCU!V>2hTQF2r$AR07H{b0{>dl^lSD6|%=5UNv@!wke*8Z}gH#^cZGSWLdj;bohfkK?Nh%ybd6cn@!n4$^? zTMNx)%U$l}B_+$>%s<^&`fVg{jF#YoP%f^Aa8{c@5IR-i?QN4ZKXz-H8{M zNBqTMYy(7#If=|0{I_xEBr#r0!;vN6om7czp*3ynG~1RI7gaiQESYU5XwP#IQStid z?69b)3bUd)OH*x3hzpTQ!{()_W6~02iVCB?R8f2$e;}g~u3kSQPmJtOHH6^z!{S<}UsB^wLJ#Vyk1gyrQnW(3;uy z0iAp;G6sW@6CNF1VN2_YdN;bG~Su|Gm^cz9}fTHP$c3*tlNQL*!*&Du;R zD|j~tc}r4+SjBaa6~X_V=O3Js#RSdd9$X{nKw6on!MMd)=I3@%p8fkXj@3kLCH z$!kB3YopEOvayn`RHmay2yzBpte6!^NeWgV`XqnmQ~D3A_u(6Yt6K4#5*u}lxzEzg zX7VSpA!mDf;mg{u1cf5$kT7`bM{UOz0_g17v&Tbb&WfQqk5Y%Y^E$_mP{7f zP-&{uwlpj9_3HG@5ZS!OYGY#_c*NW0bMFT5h|OSe9darhb8?JvG=C<7QHt3o*8Uy2 zl+WwAwGWN*+L)`swV(2x%4d<3wOj+YvNmZe)4<@Vedbp}lq4QISm#4V)^bur_S=pS4BqCeSET%AgMmH#+j*g=PpuK=Ig4p>I%DZBBjt> zmQ$%!*A%G>yKIYANP4N+Vkyk2)G$!4r*l!O%Bss=s7jt6o}$4aSGO)qmuAdRWT(sz zOIDe(T863I99gDSXi|8Myn>pX);dKtmIs=o@UWz;B3*tJ+XqX>X_RN^hf)nWB4z#F zjCl20^xJ^Wr$C4EXzx0z<*P6jN8Cd#GVdAm1Ml&}EyB6;+NP%W3UNn{Fy3Olhxbph z9cX5~SOf;^J=%%&p1400tHl~IUc#{SUn)OFZ8u(9F+;27RcgIg($F0+aqJ{45#(r> zSqN~~#v6tM9sm2Y*GRpq=;v+Yh6`@>w#2lZ>)d_k@C9s)dhx66bhI9!mJshS{o4E5 z3!K(<)7J_S$BwafFq#5hGx+^>yjv%w-Vyq3`wHU)w@){to#I`0j`DVDk&CztYcrPn z>yJvXrI}CrVJ*T>eTs8ou4bo8T2_=Ty!%$IRiU(JFP_jR(c-a+rj@p$b)B|)2!Ywu zs?YSq*$1{%8GikU6uWuS%5!`37d4NY%T`y{Ou9O9>)26LO`SzouVehqLuT(e^a}8s zMN=5R^N`tlj&M2no(G@C=dE)J`JrM^DDL+|PJ{JJ!YejJye6V_ar4vD=9`NWkc-Mf zUQM}%g4}FleD=xw&f=KmV*<7`1*&%Cl}9_O8l0~7N}UoRFLj(6AqfFl03Tvk`^_KW zkN?;@bjA1hv%DGnx#uR0EkohZ_DE*l%A`uaSKa57FEl=;raB9@kXtoK;pY^)QzT2r&TT25+b zyINA>LO1ZHT-b2&y}M)`d)1oS7l;#&iTq-Knp>!8;2ZDl!U69&E#7m41N1d-Hr?7& zRb|+_cNb#HQrLeY-1rliMh9pb-VibHPwxZ5HQt|fd4DEc!!hly55j~_i*pvo#FRMW@~hFt+ulrimFh>^pfhh$7v7<@hZ(5~ zr~xHi&E6q{xrm*yf6_|I&xefRP7>|=r^^L-iomYK7U7_{SH*2R400e}i|X)ZgT8{i zrQSVsQl@IJ5rpZ_@(Q}>OJXi(6y86vA5Z)8Lh!sVqz}W2Xg*8dW2c2^L~U=MPNC2@ z^>_~=0p*pxi0&5J>AgdjlcTF&L{HS#Wv*|5 zD9lCk+SV2??XEAZ&F-$t$d5MA#4yBb9{YQBbWUo^WO4BsF|LWy%9hNE{K}5V@UCMq zZ5P}rk%c;kizAB0T;NeUvZ1=UEjq$=Ba1zLLAzm#R9rK%Lq@a7PA`)t#MR)D@*>YE#p)!a_?I$!jamhz<#H7s>3k z%&ydX$66d<0zAl*G@*?{B zYbp;5?&}AVi+G$9edTe`RZO~QqyQ_*ctQ8lZXV}k_iqP;2(gxjY@9* zVQe!CwF)I-l@T!!Ry97W64>WRi$n)Vd~9i4s{aaYk%afCrh}utd0ZZiospn-K(gFX z3C0c?aT`N7wv{ee-?n~%_fM{esFAkOi@l%FXzwN*Zufpg=TYx|+ULEEwpnr1jAGA> z<&J&K_FtHavM41S2DwSE|e6bJcx%p1A@)8j)SLJN1H39&=!_yRbXOqYrFH zAh>bZY$k=ircLpBZDDOnnntP6G`Cb$wHWNRu`zKU&NOBg#-)cVQc^QB)1CD-c3FN& zV!Av!A}+KB{acFub^I6o8(mmty~tYBT3ge))Uv9hW0keH?IMFJh2Dw&8I#LW>Kv8+ z{uM0`5b{(-+;&5Q*@RxjWSnbo)-%s|Qw*G$KyT9k+ zKSU56I1T>WgY{5&^Bq~|5yibd=x+YJ-xd5K&xTYr{*?AM6VAzpu$e!zcq+WI-Z z+_t&(b9`^`7e|U^v2c2@zY~O?@B46i00(}I2$|U|xMmJBOWui@C%Cr5;_`k)2nr!F zfMxf~b2I})Y1Ch>WeP{bbzOl?2 zvHr7ouQFQ6U=Mo~;e~F5cIj25dh-2q=ii{I2EkfAzmyB|bjra6l zVo%ANyho?PW#rJxKyBVnRvTxSS2J%k4!!Av#us;CK+~!3SM%?Re|lo{q3qSUzwUo- zbFcR~Mt~4T&2*pliqKHJX0z9O3@YH@_2RiGDLZ*0tO~|jj*f}%rekkK@nfZ7e;VBn zw72#D0ciioMiXnEA42>69%B|>z{iA{m?wQ>VxjTAx3F2*TiA-II%@t}%M>+h+r|fmM#qK*5kWt%);e;_vSqi7 zSZmK4boA`H_U0WsZrr<@wVMwg>l$v;yr7P)Kl9;ZT?3mIji`EH{poM<)N}6dl(uSf zTAj|;Y;6mITV_L{$@qn-*S@fLkqH~>3+=ro4|apxZtMmDa|70vi_jOoFUswJFABx& zg@D2$_omy2F*LUlLe$B0m7Tvl=KXGecSK0Q_6&IA0 zY@Zmrpp5G_n1*4@|5g-ex}YBks33^qmSM-}=Q!PW2l)-&X8A06mApefLHEOHJw1=^ zqSw;f>3#Gu`T;Uey+S%XU#sATPuPn$I;<782zLq(2#*WT3V)MiNeU$;k~&GJYU?UeRQS4cNV&y((yUM;;vdXMxW>0#;f(&N&_FJ-VgCsGBAkXV2-k;u!h6Dp!zaTxhhGl4KXblrzGHs%{MPx8&wqCQs|#`#SQeBo=v=U6!QBzs2xG(<5$8tijCd^K zI4#HYvS#-AU*Gyau?f`qb!hJ@~f;e^SAa}xF^Jeu%)!rO_~ zM0euZi4P*;$w`$-XC?hUIVO21`SIj0Q%X|KO*xX{U0A$uyspF|Fw;I5?^(@N8Jr(KnHYuf&_r_=tC_C-2P4^NLz*QPtt zE7BX&C(}2l|2+Mc^n21DNq;^4{q)b%y&3Z|;xaTDg&EF_nvAxLb2D~i+>!BQ#&a2O zWqhR&6blr|iY$deQLLy^j3_oJu2ejvIH~wf8LEs^HYpb?$CVqDyOsNtPb*)>zrQL! zQhuc(s!~<0szbG2^=s8bsz0b+RK2dQQje>5s;^Psrrxi9LjAP*&+1PzNoG`La%OMl z?#$;hk7p%k6=f~T+LU#E*6yqiv%bQgteB_St@(xKx0>g*k=k-?gSJyUpq>Df8i=IpZUYqS5AQ=ik8vnl7soD(_k=X{v!p|)IX|!ry#1Jx}dpWYr%nn#|z#uNDWbjR71AG zY;YMoh91MPVbZYKaIxVE!_9`f4F?T}4KEl@7~VIWEF^`=g;j;Eg-Z&T7p^b7yzt?| zBZV&(o-oRdCS#d#iE+7c%DC0I-}sR6JrnFN*9y=C8=rgR#Ii`|7epd?pJ|D|NXItv zt32_QnO)v@r6;A=l^TV8F0@jA$q_vaW515jYVW7eS-AhIwW3azQVPL7R|his}|SKaJArm zpYQ(bxGDhm)gvk&{BvBb!d9XY>VvNPKoctpUIu(u_B~y44QLQ)ChU4j86bA45_IDF zQw42K2s`n-udeVYz8^vPq3eCbE}X8kkrZKWSq;4Lr0+j1!eNpijV0-G3nEofpz-@< zHz^f@OBvA%L4_hd=>K7G$sj=ZBe;a8JrWg5Ibtr6B z1%B{O1*5Q+&asM`wNVBo$?Js9T_BVj`Gpn z*(KZx8s?VOBy$dLJm8Ni#>PAbB;<;d% zz8>%Q^N+ENIPtj>d|2+IxsSYyaTn?D!_J2pyMPK-P{+yP58%wvZRq2iB6V`PEJcr* z%mnTuComT>na&E}J0!i(Lss!Sy&4&UN1(s_^)Y{hvq%``M=eS{N)#)SHtQoWys^4`ZBj6i%V>Zh*gf1fZ)bl7c7X9YOwB{ztl zgXstFlY|LUi8?u~pdQ{^iR%<(VkOG|_UZ!dKlE$?Ns_dXl(|JPz+S5{--F8}=a-Ga z==;PPT!g=ae}5qPtbpzY!s4ROc;CQ_$n@|Jc1TY$NbnD|jZ|pD4hh>k+QEDU(jZ>x zQs^mHaNU~;Sp${Ju`+m5b5j+0f{}LUJxS49On0C-|td_nJ zk^-2CL?#IXW{t!G%v*u^(e%$0v5F)`ssek~z-&}FPQg43%oLcLXHIhLfnVMj$f9KW zJMRrrnjO(Mq6Zp-_QQ;W@Tg$>R0TEtYUw{P&`Vo$@IzapY z`HEhI5&bhgo7_NmBUkfGynS^mIYRU3X1bq#MQ_q6XO zcaweO9&rC1nnEr^eD(pnx9)ZF4UMB(nosStn3iBfI_NUGo}N#CPH&(O(LZ4g`%n5Q z{TFP(omlVQMSelM$k*fv`djiW{es*NUHKsU$8A`ea+kHpr1tb*l9T_BnMc5E~LS!Nt(W^941Z`)Am!XC>=_Or|$E9Qu z-tNDIY$ltK>v=ZzzBb@5gbjgIq)dr3J)Pt{Q_*@ zi;&U};FG*b(y_8vkx%jVfVW^T6X5H{Lq8`$7bRj|XA>8-5ht}^ZWh2UTcJ-)&{Yl` zh<3uSDI*QE0yd!>_OF&;OOdoehqlsgMEpO2E?jAQujzhN&lM#9;Sx&DcWAt+P z+c%Om^cV2IZYHbfO=OBbj^y0O$XfacIS)E!3;ioOlfFu}()Y+Y^i6UueV1H7KO&dV z&&f{mbNX*`Dg726-8W>C-a^*X-yLu z@C>)p56Ol2o2!|yJiF*uSL^==Rej|$bnPRNj8-*F1N$ft~X9?u>0pDjSWcY30XBK_^#`jqUzkzEgJR1tT zz~1f4@XP~$Sxd8vhfSJ`;PX^KJA=F$2=sF+9d`LFK9Sj)-WHp}23AEdW_Qrr=C1_X+ z`t+cX{VT?7Z$_!YCqIq({?rg^4x=#s2lLL)iFKfD@O#+w zz%atw6Q2_Ky%9QZkwL5jrt>?Q32{#b49b%P%NEP|mE4d2Z5 zkgU`Eu-n*!%Orcq(^LXIG>CVkOR>i>1dsF{Zq=r7QV2P0G!$BQ9u33X8bO40J~;|| zvjEm7f=1FP@(&t~nUYJsrZG7E69@gA2R*HWem+K%XfkZw|3LRWhh36yXc~DQnk$1U zV2xDJWSP+6S@1@*SnFovVPRa7#*PDY4Nq>Vltx6ZCj5z)Q4(ybRvHf=rhx z=+GKk3oTPb>tJ;os0VY{34Pm09-&R7gf^4+XbY)?MQFuL+l7tnc8t``WP)~(d9)K! zSdLL%0BgAl^S=x>e<>_;KkbGcf+vSlOuaY-`9A&zaW(PMKG?*i6doh2dp~)D4$whp z`yuig9i}6&y^(YjHglX@0&BgT)WBbqpw~>-t>3Oh;+u&Plrx(x* z;Q?LDt;;3IR)2!-g1+AkuVWASJ?vm2y^LN?uYfgt6dL2-^eT83*N~r)3dGj;!g^kb zxBFj5?!wy#euve`UEM^q!od@>vR?L-+@J?QZ^}C(!!+EbpdMEuQX~N2+ znf?lX)vxJ2^j>&d_rU|X0{+B1^nU2~52n_doDR{s5c!TiE0W$zk{=o$vskr!UYK;YM|fl8C)}s|4Bo=WB!Ql!U(hefm$3MMreDK%*h%8)H}qThq0?Al zdf{#0*r7nlRzV==2@-OyAjN*63|X3?SaHt7spD{*Bv~LtkVRNIM&kTbv=D>84AMtF z6k_2STqVTejhIV>1oC%e1H{7TPex{GMVqXAaP7#35qbHlfuXf)R?ExRjICKcvLdW} z?ef)Q14HYlM#3wGmaiRJziM>l$fmH0;WbkOLqj90r(~5w19$+JwQB~ZWK|rEtcnA} zssh#IRU%?Z)#@>Mm55xve)aOgDtASA%`9dHGZY$)VKuXl@@kGMY3<;^T3MZb-N4GJ z@H$Qt|17KHZH9{%4vMOq#mE5>b;HX?){d-OzK(y5tP5c0-^1(1)(&hK;h*JoBBk?v zS07L$d48RE~8)bf?XBjMsj1l99Pc>QeqQ9++YT)c}WtIWguAoFlui5)k&rA?h4 zkE}_&)f9BgY>_qdjxJ~(Uym_ayMEQmf%Q}JW|3T3i%3ALcxm%p#Gz{o;#M2S87^Le z2(nn`bHXfE@zHki(e~M%a1xD$c4-G2h>jrItVZ9xa#^QHW9KO}cKW2MQ{-MqCzC2! zCug6mTWqL%wxN05{?U?k^MMIpG<#FFh=XLk{IRg!z(~plc+>MZd}z(8!HB>o{m{U= z5&3|RiUE;|h{3f3LnA9kMyL4gNd9&0^09I8dldV=Vq_{%A=(F+t-?EPGKh@BRcwZF z(PXFy_g(c6SI$|$c!ltx+0KLw1;`hX8Es-+qm9>{$EKKmYW4b6zP5}$RK}pTj6MR4 z!2}c<7YwamyLM#t(3$*s+3@UmnGALx8oNl*@bV4IherbK7>$LN@ZmKpR}QQVz|bWh zX$Fxr*$8L1Y=q+t8wqqIY=rah|J8OT@KqIe-#_Q(=H_l$AZ$V^NG&3uvM7rcl(p1{ zS|3G4sv@!m14NR@Qlu6UkWIv;R;^WDr4)T}`5+ZY5vii(L0&|}h#|(1#t;$1A-$&Y zn)`hJ|1C7n3qTebTPs^vS`lifEenXIc46Yyooa>&z?I=WAaT^ z`AwNRVX7bKNh*#Bxj{9P9y8QqM$#isJ@VrB&)|OdYTDFX^$bkSg(c+8T$j9<>(cj{ z>fRcdI=g6&2_-H!l(^yr*|+zAOGDF=VxWcFr*FUDv_vijRLWJ16E1zf*3*h|^YW(5 zoSl@+?1WcRHZ@oQYDQQi;TlZ6lEJ*w(PblB!;DZ2b4z6U{QT)tOrGYts+D_j-t0+v zW3T&uDZLH=Bh8xr6kuy_qeRea}o9*V2pNdqaK^+pC zI^wd>yZ5j`8Tk{@)I>pL>S?l&vh%0R$(=n}xPVG*vc78|KhL~ktGkM(6cpypo|ROA zIc~l}U{Z0rmhp14dX8Gjoa}R%tK8TL&prc2s6>dJkX&eBsLAu8nhsxq`;dZ`CEsU| zuVBg~QWY$qVKGZ{z&Irjj8n?eI3+_e-LT@+bodK$=ec<&4$V7pB=c_Q2yemcITHh7 zsCRk+d6b9TCj<5C*WWF!ewX{i)?jgW~ua`cX&GjyrUFrYYjNkUa>_xA| zW-R9#%xmct*o+h0hB=WD2b*60uQBhVH)qoedKB|F%y3L$4D`3SpJYVSrmyjP%;z|4 zdaSQ-Rr(V)J&LWkU*||+gtHi*oy=NFVWy>wm%YR945cs<{2u-X=;PX~NB9t*PpneR zqx5BM#zJc_zhWHJre`BxEuwZB9fC0Za1VX%4wkL+5K|aQ?v9&&w3R~tc>wM~_6W?; z_D?a#(7R1xg!u;CH_=PA>6PA!If0(2%{b&#u1=%>X)|V9i20!X80M4o@=_R$UXS}v z^v6=@kL||&U-X|+=o?kwuC$M1e!&=~%~&-lw7+Kj(RO!$@iRg^ znK+{aJ@UJb65q|n{!e2|NsGUHyAgY|v44&&10!KTo^nk}>K#TrY(}M}#HC!fQj$`3 zf7OzroFwmc?1KE2+|rz(vroFa^VXgbqsEN2GV&%C<Iw_{+>a`2r+QOSyB(oLN?z znG@&SX?2`==S&&ZlPmIvEMrO@rEp(x;K0${M?c1y9|Sw+^M-j6AG@oSrEN+nJHt`u z%}5@>VmZ<_efJPwZ%*2%_z?=HjXT3JBP~Boa5u-}@k8!)V{U4u6{qdzd~dKM=rlzr z+~nvQiXS7w6=yuEvsNUIr?R#tjssclQI=(n>~V2dR$0zs=AY@2Rhc|;Ms=#paTs*l#8J_@w|fx!nw)X1`)7MO z2*=S&{LC>ddobJxz@{~2oSgB%<&z-9MnM^7c-*vV?qt)=&iFh`!HrU z2E82%gARc?pMnd7HJ8Ad&ohp)(fT9Y@S^p<`O+lZxk~wR4I?vq_+nPV@rW|wqt-OgY%GuuALdPznbt*y3bNL75k{Fq9<{PT z8_*400J?(tXN+ zcoh5=JOQ2rPl2bw@4<6m6Rm+4z-I6g*b<9auYqk~JJpkoP;C*lulz~q{ zIrt1zfMcK%94CAY_7|}R>r3zz?^VnBX;4SGwE=BGJJ23<0Ox_OKz0)E26|GO7g72b zgG)ef&fw!HS$_>ToQfSbTLa4VQV{I`RN;0`be zOa{}ybdU=QKp~jV2=xN6Ft*i5V}pGk{*;Bij5IFCe>GTx`$6Jf3myXNz{B7X@F;i; z{Ek^mPlMlsXTY;yJ$N}*XTQSz9}wmc_z)ZhAAu6^F*pJ~0j1z5C`LsvfhzDhs0POY`ES=_{{u9DZzvNldQ;CxxhJ7XUi8c1Jw=;Dzv^it9E;Hkz^p{# z2ydN07f&)Dt5<9vAi(*mD!(*`( zd#>qOtqX*nMd1J~Vb88oLjT-G|2RLt~k_3erJOvw9!Eu9kg)~ z+UTH-wP<54+89L}9kkIw8y&RKK^q;k(Loy>w9!Eu8_~u_w6PIwY(yJt(Z(p+7)2YS zXk!#@jG~QEv@wb{M$yJ7+89L}qiCZaZS47r6m5*6jZw7G zK^q;k(Loy>w9!Eu9kkIw8y&RKK^q;k(Xr?~ya{%LW3h33-N>@P04Kng;3W78)G{~k6t)9SgF5gv^FE}usuxY*?mdY!b_z!Z z;&q(Ve5|tuOGwc%W{1d?D7u8cf<>g!y&|jq7o%CdVyEzJF!7!gT?Hpt-+mI^baizf zAr5Mu9Yfcqp><_wTp1cyhQ^hlab;*6JJ5k32!Sw22bZ8deLz2O85jTtfggjx;7TwQ z3};5vNN`uI)S3edKp`jsi>Y(&qrP4O?gvZ3GO#>$#CjlBZmj?-!78vCtl`>&U@dqE zY=K{11KYrMumco>JuuXJ*ayJ-;3(kDm__4d_KcS={$9SVdYMt9xKf zhrR^0xKD#Rq=`9ce986lCD+TBTrWLLFJET;_5|$P!9;Kem;@$+X<$0Y1qGlGEQAs7 z!(KuUWjXHE;1%N7h8AoGJ3uk`GuR2<0K34OU^h4x+pCOwn6IcF+7~In3sQj(qyaw& zfFKBgFh~cPNKqDO1zLk_&<1n^7l7{ILeK;Bq@0Gq2*YCsVaHm^?I3NLdfGDev}NjP z%hc1Bsi!SdPg|y*woE;3nR<&l)Vd8!1b2Wti6;-t1hc?wkPq%6Z|8slPzZ{^a`O2B z`k5=hO0Wv72J2!qFl7x)Sp!qnz?3yGWerSO15?((lr=DAjrAPZL>w=G&EO@lB~}7c zmcW!HFl7l$Sprj*SZ~KpBO7~R%M#eK1hy=JElXg_64hlWjSnF4qG~~WeIFq0$Y|?-^3cNZ$S)Jv;n)Q+o>Q8q=PoG64c7Bf>9k9)qznR7}bGM9T?StQ5_i7fl(b8)qznRZQl%m0i=y{ z6}GfIcf z=kl0wy@)vOkMjJ3Q%KVQTd)$M+=pCv73FAp3G93?H8|_Z;DB4fkwjG^tnY2;rqQRgLa*2Q|Y6 z?sMx2Y6ja6?`KdFGpI*4FcmKgmjh8S7;8^Scqdg^z5ZQKS?61mv zjm(YQL|gF%uo=8W%{Ct_&>HR}_c)YX10~l$$u&@N4U}91CD%ZyHBf2|lv)F&) z5I=P!eRU5tkQbx^A4mg!5CB0C0%4F2dXkH1CbgbJt>;kdIn;U%wVp$*=TPf8)OrrJ zp7aJIuxA7&tcD4zVZv&duo@<;h6$_5$9?4EKJsxN`M8gK+((M4s12*h$7=GintWu= z3|J29Jb=9dtOTpTYOn_Oc@V4x4*_zKT(}1bT0%QPTHoZhwhyG`AZ=~h6i8hvV+bC` z4LrYTHsjc(_m66Vj7*WlqXJs-;2|W@j_@K@Rf7E673m4ImZx zKpH?Y$-#PZu$~;OCkN}v!FqD9k=$z}_ZrE)Mslx_+-oHF8p*vza<7rxYqa1nW)k_n zCzmS8r4!`R337>9qF@J}M|f-Q1ebvUU=a8*7!0liL&0$5 zawPcP+LPLN4k!SHpa?8R)@b*V8xFbQkQ)xU;gB0uXVkwnuU1&+ZQ0ob4pHmIWpj8X$esjZ~FT?aGN!3@$RK0%zY_tf6G2w^&2z?6*d7?@z#J@KbO# z_zy6KXa5ZQ8gMQ6PjDT$9*pJLH-H<#O<)|j8H@+N0Jnf!!EImy;co{MaZkdY45omo zU>cYXa={F6C-0L7W`bE@HpmBe#Wo@-8l!pA-D%D z0>1+Hf?tEh)I#@xCE$Lr6f6UjI+C>!J==($ZA8yDqGubw+cqb^UIW{}cCZ5!gZIDz z@IE*S%D|_f9DD{Uz%ft>YQR_UMlJSfPzM^&ly68w6#HB3M(h|;XrZ4rwg)=}+l!rw z?ZZyP4r8Zdw}Btqf_9)i=m7qmaixLGtQ!O_2aGD%Lx`7=1=?T{IHjI8SOi|FrwtbQ zC*w^uw83gq)rvB;3k;KTr!}oWgd%X;25g z1}(>Mv1^E5f~)`qeNhoi0sk@qp&&>F!~6iL}ZU{`7J}(qzX2vf=#Mm zlPcJxO2?dfagCiPV3S%nz7~$Jh2v}C_*yu=7LKol<7?sgS~y;MR?@4gg+(H;NCXy% zz#>V2}t55`jS?Fh~RjiNGKc7^DgYse(bOV2~;pqzVS9fV2}ea$N?DS01Q$KgA~Ic#V|-Q3{nh( z6vH6R$I3jkFtY6X87GjohI|oSto`8oVYn39IWpSA@1)_E!S{`@eKGbWpf~6P`htG+ zvMj<`{>Cm0r!KYU>R6W`{4oVgcV>VSOr#t=P9i%95ZY8{13GY_IyOr>6CA?b+?^eRQ zmGEvQ{neB3>PdL@B)obOUOj2A2is!X;Z62&6mBhrTT9{AQnV z9EFUGS*@>b*M`+`-RT9Gzu0lt0aM3)s`$2F6RYEw-%4Zjq0uh>V4J4b8<0&x#r^(Y zY>7V^dpsufZAhG%r^mh}owN(${@hvH#I50D)J3#X|LI@T=fM!>&&`9_*K!*BHsQ+{ zL-Y9P=d}21mLl@c{i8L{A7v+IGXFjxS1ZLY{uh7O*m0f{qe^1)3dDT%!i+i$LYCtX}6w&s75R`(5JjG(l*h7`E(DtXcTt(%8Tp0$i4 zdF9`9`P}fyyi^%$jQvCWlX)&umy}k$MrkMS!tLgD(8L#w=RLZkIcx4oKF=$<(fm(y zsXlRMvh*a!lV?Ud+j`hA2=-URaKhxqNR%{oH zSra=5b2i4x7;ho9yodBh{j_fV%#rZNwvfU-v{Ff9Y#*Fjg3k$IR{Zx9BWui6y36rl zl$oPm&zt0iN{d5mKH@vX^=jT9nGw0-c@;cWQtXd0YE7F|#@Y0bbk!2JT$r1B7R@1@ z^pK@ypnc}oI9^8MXlbbXFjuQ$FG^jc$6n;xEAbNEXrR>BoFPZTbmlp#lf2iV*voOh zv$2!tSd%Lru4oQ0NxYOLW)t1jv}ovLj<|Ot#noE7HL2}+ zb}*@Znp2u1#3?dEtAto+U6_YAvq;qmkRvy4_!e!_E5d)O%kX5QNVn=6V@7bd_)Cc~ zPOV`N5ynl$S?34R+^nV6GUeYr7hQ|{;u86jIfwF>kX1R?w-ssg#`PFY2(y~j{o@Mt ziSU{$&+3a??x20DY$v*+fAGHBpOKl1$ri1L9WCVf@<&*ngI2g@<7ypgRh}V#kjmz4 zq->Jt%SaTlV*mzd{>L240K9#I-h|XM)J1X~cL`=GEk@-y;e_O0>@8O2H~EXlDmeig z`G_aN=n(how*~p0;MVLFynk5V!j(SNa>DOpep8b_Hx1k)vMBX2f2#A7%sbd}6nT_y97&I5m#@3~*F z`sPcHr*(bI?{$65v+OmI#;Sf)#0Dsk%8Q@!V2KZKHSodT$_(jb3-p1Y!S6lDv zeD8z0CgzaN^ggUJy+6{`Fh_Ncce&2-KE@pHh3p5lj5*#_I>$T09Pj6u*Dd=Pe5+V6WJ_|g2i%#JXsY%D3!O@#ma2uq)12gPBIrl*=gP@i{P|F~wWf0Ud2x=JwwG4t<20_YTD0dCx=*BnZ;noH0YB2)$ zNRA8Go#H2qX^m!Hep~AX4i9BDfoI&o(bt;9k;AH;$?Q=vg(HXkIHuyB#*t%9=lB8T zn2UP`#}C*kBcF8MMM<~QlJ@DEsrHPSE+@qgFvqC_J7GNp$FD;ILP)@GF`wW_(N$C- zm4*<~@Ercnb9hxITB%I5QkiJ2GSM2Dc$*andpOQf$;d!54zP2{`y3f6A1TPkajwex zsT3sT3#3_Or8Bbf6--ylk)jfl!uo**a{3#No>r8j2kQyG#myWi#sTF&)ArcxqNHmK zI_h32ne4*E9@WTM82@ynt)og?7nQayDs5qvw$5fX)<8yTGj;7jFLo#y%w4hqp(Fd0 zT#5TC)**D%{Yrjh4`)tOhOSBIs4^N>84at9hE+zxx^9d4cg(;!TiJAtgj{V=;?oyU$KkKlgPeiZj(=s;K1fo#=*3(*^@=qIdn!7wlO=0 z5o2~IDQ2EuC-(06Gd??+>(|MCgV}yRw0AMzuM@j>yoq}^bN)J^H-91hAMh&(Y4#zT zwF>OI@geTR?7iV#(l&-g8LKu6Woj_<34I1#a+hlPy5-A<5S$_ zb~*0P*qI{@jjX_3X;)lCZA_CS#&r>H8!P*#s*Yl1FEqB)!2Y)Y(OobU7~1LwrZEH+SOLItF3C6N43k=H9EFx zS4g!htlAY)?Fyq^jp%udBdm+vLN`fF#-+Z{!&^mMutqZBv zh0Pks^X#t7q+&-QOyT&ps&yXr2D%)*5`Am0`qn}9t-b172lQk|cvSa1s(T*QJ&)E8cC*!zMd;p-?D@Q!4^dl}SKmtN%fINBnT>Q_m6cdGYV_A~ws>K{Lk+dKVl5qhuxbz__JV7dRWzm!f~m9bBprR})k{0B^U z6OTXs9OLh=@YiX2PPVWs{MF6;8)Uo0fX{F2K<~h?z-WB>2gU}*V~5RoYjfVgoS*xD zd49gRzlXX13UhvqIdA#=me+3&`y~B=seyTF=b7_4=6p#&`+$KZ0px>lPX&<6z{bE@ zW5>^*3OuI%@$>Bgk$@^n5{Z z7EfCmjN8)_=Y_`qUbUa&_cn@gtupqy@39{@{!bhGWn;(h-_*i?ckqa&<6UF#Z($!Y z=jB1|w*{+=eWHc!nDfR^mSu(7g}lZNwy-be>Qx}e*j}+t_=@l_?9t&4=KNguyybPy&o|+F zNcfi5>-HQ`pPh2m%f2XFeU+Um4oEA-(@w)Yt6?@Y9^&e= z>a(6F+V(tk&(o{R#D~2@XW~CxVxWDFkEf%=W6SUQ+V&QW;c30=RgGbt`mZx?_HVIU z>(v{?w0@^%xtcj@PH_F@E~{M4-^sJsTR@)9id*qlpK^`Uefn5^%AeF6D>2xAQnR&& z`K6i@^y)pD!YsXdxwx$-)qkLbv7VH-^YqrMJ6uz*KCO4{(6@d{LVCLC)mPNi@E(ux zk(_x&?|MZ;_S5`%Mf2?)4fC#=*QrU1kMP&4TiOuptD^VL(VLh4>?6;z-sV?FZ0i$^VVPXDmP$x|8Aak;sy@`2xLN&( zxk~+4X_#j;wZrx5Doy84HO>>7SEtl`Tzy_qQ}xAKXO|PtLJ7}kfW-V8@v(oWsT!{4 zXie?a8vkfb^9X&H>(zaohT-=cx$Ay4uha0?xmPvh+nTr6*&)JQu3@ef6D<=nq9&Y+ z`)~R#->A7xpEyCor>dE*K0(*5F$eWo+4@F|;1j=3-M zeg2@~qZ;xLYTlscMh(Mn9&pzb^~n;`+NmbL=ZgPGb${Xds3|i^*c*ym^)ZTU^8`7U zFGV&ZuEQ{sdl`ATCVJEXoD0t9>OyRr(Rnd{tfoj0dkzs>J4Sa2p*i$3HHWG>5;OiZ z8N6|yHiR9Y|NY&P+ViyAz+^B3>;(Bh_)5~Y7%T^Cz$4%Z@CTWWbzi0K^=iJLK10+!RNcGOoUcC1)xA>P8`Pbz z?z!sbS50~1Cfw~A-S5wuv0;p|%dbO>w{B+)J&!fP^XS_zp)bGIdW

lx|Z%y@@r zx0Luxs;5t=>}^o@)9PQW?&s9KP2E>%nDy%Og8B?m_fU1aEOe#1UsV5()xFZhEcIQ! z`p*?Mww_l1OG>9N+bA9m9{tXeb5$L&!xsql$7Jv)T2ue;LBxEJWr+HV(9Ik$qjQ$&)6{%(rGt&dZ%<&5;e^ta($} z^Uzv^$3$znY*}foQS%YmB+hzbHleI%Wa~j|LqS3BKGur`MfnBRtGaQV$dN}=Cn*#S z@$lrZ`kbR?D@^&djBI^-8BvpvDQdcRW~o~w+2*T`m@fZhsymG@(f#db*zxCx9rawy zQI|3`<*;`a$F9^2j&Z3^r7lQaj=3o{k{aQcBcceHcf+jPc_^ z=;>g_i-$5oJQ9hz1KpX*_m~;zO%eMjy}&L?FIjKWhWW@k!V3Bm>@@9I5$hl9L=>`H z*lpQajD5e?vupQD z?9RQFQT<}Z@ps$rusil4#^y_SC+2Z8(q6%+`sa+GM{K^DkY4#sgTDH!A_LhE{|a_} zx(YoQ!51d^g%tT!g9F9{%RuO#IOl-7hfolYj!bz zBT*hwx~ZJm_C*Ol`Hc;)YJoAB8e)6ae4bS z^0g@;Um{no1a)X45lDkdLcGlHrHnTu%2>))N>OxI$GcPVwG^-5cW$y$2J-YSR=1Q< zj7v9C((J0l7Y4Bh^F6|Y&96k_7sebcckl~T-BK=2$&kDFI+23bT*Q|Nu?O-cLcS@; z-it$2$K|(7x}bk|b6uX^4P378&iVrNX~Q#e_|7WYOv^`~H4vLxnSB>!S4F8mf5ZNQ zTN#O$G5Al|Rq#S~Y<$xGFXFICwLoGQP8Ofm;DqrB5x*SQaDFY{SAVAAT;Ax$+dR&` zg|9Qp{}vFIc;Jzu^$FET2+kdqW;^T!i zW{A#f8dWA}4~X`;;nCpOBsu4+Wzugm?-+m6u8H^39t8Kb)$rK~|AFRxrLGS$BOt(U zVvOe(8~(tr8@$c$2YlNkW*;@6g!m&d+vo2d^Du)}}+iRWv~y$B~QG)Ywl zOexuQiD&j`awW)I1Yt+vzzwjSXo0L)_gMUnl-{4rnY;MS2!OB5SQ$%upWR&a%aD8> z8irpGCXhB?Yo2;84;TFy$hS7p4DK;9FZ%%c_{Jr37~mYf + + + + + + + + + + + diff --git a/public/images/verida_vault_logo_for_connect.png b/public/images/verida_vault_logo_for_connect.png deleted file mode 100644 index de7a1fb2cf74b297b53ca6f148b3e49491d84ed5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16667 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 Iy(dBcA5}EE{r~^~ diff --git a/src/app/(connected)/credits/page.tsx b/src/app/(connected)/credits/page.tsx index c12357e..b1acfb4 100644 --- a/src/app/(connected)/credits/page.tsx +++ b/src/app/(connected)/credits/page.tsx @@ -1,24 +1,21 @@ "use client" -import { useEffect, useState } from "react" +import { useCallback, useEffect, useState } from "react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -// shadcn/ui components (adjust imports to match your actual setup) import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" +// eslint-disable-next-line @typescript-eslint/no-unused-vars import { getAccount, getVdaPrice, submitDeposit } from "@/features/dcs/api" import type { BillingAccount } from "@/features/dcs/interfaces" import { accountBalance, accountCredits } from "@/features/dcs/utils" -import { useVerida } from "@/features/verida/hooks/use-verida" // Regex for validating Ethereum/Polygon addresses const ethAddressRegex = /^0x[a-fA-F0-9]{40}$/ export default function CreditsPage() { - const { getAccountSessionToken, webUserInstanceRef } = useVerida() - // Track current step (1 or 2) const [step, setStep] = useState(1) @@ -83,54 +80,56 @@ export default function CreditsPage() { } // If we get here, we have transactionHash - const sessionToken = await getAccountSessionToken() - const veridaAccount = webUserInstanceRef.current.getAccount() + // const sessionToken = await getAccountSessionToken() try { - await submitDeposit( - veridaAccount, - sessionToken, - walletAddress, - tokenAmount, - transactionHash - ) + // await submitDeposit( + // veridaAccount, + // sessionToken, + // walletAddress, + // tokenAmount, + // transactionHash + // ) // Reload account so new credits are displayed - await loadAccount() + // await loadAccount() // Reset the form setWalletAddress("") setTokenAmount("") setTransactionHash("") handleBack() - } catch (err: any) { - setSubmitError(`${err.message}`) + } catch (error) { + setSubmitError( + error instanceof Error ? error.message : "An unknown error occurred" + ) } } - async function loadAccount() { - const sessionToken = await getAccountSessionToken() - const account = await getAccount(sessionToken) + // const loadAccount = useCallback(async () => { + // // const sessionToken = await getAccountSessionToken() + // const account = await getAccount(sessionToken) - if (account) { - setAccount(account) - const credits = await accountCredits(sessionToken, account) - console.log(credits) - if (credits) { - setCredits(credits) - } - } - } + // if (account) { + // setAccount(account) + // const credits = await accountCredits(sessionToken, account) + // // eslint-disable-next-line no-console -- TODO: Replace with Logger + // console.log(credits) + // if (credits) { + // setCredits(credits) + // } + // } + // }, []) // A function that does something on mount - async function onLoad() { - await loadAccount() - } + const onLoad = useCallback(async () => { + // await loadAccount() + }, []) // Run the function once, when the component mounts useEffect(() => { onLoad() - }, []) + }, [onLoad]) return (
diff --git a/src/app/(connected)/dashboard/page.tsx b/src/app/(connected)/dashboard/page.tsx index 5421d97..0d98658 100644 --- a/src/app/(connected)/dashboard/page.tsx +++ b/src/app/(connected)/dashboard/page.tsx @@ -2,57 +2,61 @@ import { Info } from "lucide-react" import Link from "next/link" -import { useEffect, useState } from "react" +import { useCallback, useEffect, useState } from "react" import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" import { Button } from "@/components/ui/button" import { accountUsage, getAccount, registerAccount } from "@/features/dcs/api" import type { BillingAccount, UsageStats } from "@/features/dcs/interfaces" import { accountCredits } from "@/features/dcs/utils" -import { useVerida } from "@/features/verida/hooks/use-verida" +import { useVeridaAuth } from "@/features/verida-auth/hooks/use-verida-auth" // webUserInstanceRef export default function DashboardPage() { - const { did, profile, getAccountSessionToken } = useVerida() + const { authDetails } = useVeridaAuth() const [account, setAccount] = useState(null) const [credits, setCredits] = useState(null) const [usageStats, setUsageStats] = useState(null) - // const imgSrc = `data:image/png;base64,${profile?.avatarUri}` - - async function handleRegisterClick() { - // Place your registration logic here - const sessionToken = await getAccountSessionToken() - const newAccount = await registerAccount(sessionToken) - - if (newAccount) { - loadAccount() + const loadAccount = useCallback(async () => { + if (!authDetails?.token) { + return } - } - // A function that does something on mount - async function loadAccount() { - const sessionToken = await getAccountSessionToken() - const account = await getAccount(sessionToken) + const account = await getAccount(authDetails.token) if (account) { setAccount(account) - setCredits(await accountCredits(sessionToken, account)) - setUsageStats(await accountUsage(sessionToken)) + setCredits(await accountCredits(authDetails.token, account)) + setUsageStats(await accountUsage(authDetails.token)) + } + }, [authDetails]) + + const handleRegisterClick = useCallback(async () => { + if (!authDetails?.token) { + return + } + + const newAccount = await registerAccount(authDetails.token) + + if (newAccount) { + loadAccount() } - } + }, [authDetails, loadAccount]) // Run the function once, when the component mounts useEffect(() => { loadAccount() - }, []) + }, [loadAccount]) return (

Dashboard

-

- Welcome {profile?.name} ( - {did}) +

+ Welcome ({authDetails?.did})

{!account && ( diff --git a/src/app/(connected)/layout.tsx b/src/app/(connected)/layout.tsx index 01c8de9..10fe05c 100644 --- a/src/app/(connected)/layout.tsx +++ b/src/app/(connected)/layout.tsx @@ -1,11 +1,6 @@ -import { AppConnectionHandler } from "@/components/app-connection-handler" import { AppProviders } from "@/components/app-providers" import { SidebarNav } from "@/components/sidebar-nav" - -export const metadata = { - title: "My App", - description: "Generated by Next.js", -} +import { AppAuthenticationHandler } from "@/features/auth/components/app-authentication-handler" export interface AppLayoutProps { children: React.ReactNode @@ -15,10 +10,9 @@ export default function AppLayout(props: AppLayoutProps) { const { children } = props return ( - +
- {/* Sidebar */} @@ -32,7 +26,7 @@ export default function AppLayout(props: AppLayoutProps) {
- + ) } AppLayout.displayName = "AppLayout" diff --git a/src/app/(connected)/sandbox/generate-token/page.tsx b/src/app/(connected)/sandbox/generate-token/page.tsx index d9906f3..1207182 100644 --- a/src/app/(connected)/sandbox/generate-token/page.tsx +++ b/src/app/(connected)/sandbox/generate-token/page.tsx @@ -1,10 +1,14 @@ "use client" -import { Disclosure } from "@headlessui/react" import Image from "next/image" import React, { useEffect, useState } from "react" -import { Button } from "@/components/ui/button" +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Checkbox } from "@/components/ui/checkbox" import { Label } from "@/components/ui/label" @@ -12,7 +16,7 @@ import { Separator } from "@/components/ui/separator" import { commonConfig } from "@/config/common" import { fetchScopes } from "@/features/dcs/api" import type { Scope } from "@/features/dcs/interfaces" -import { useVerida } from "@/features/verida/hooks/use-verida" +import { useVeridaAuth } from "@/features/verida-auth/hooks/use-verida-auth" const DEFAULT_SCOPES = [ "api:ds-query", @@ -27,7 +31,7 @@ const RETURN_URL = `${commonConfig.BASE_URL}/sandbox/token-generated` export default function GenerateApiKeyPage() { const [scopes, setScopes] = useState>({}) - const { did } = useVerida() + const { authDetails } = useVeridaAuth() const [selectedScopes, setSelectedScopes] = useState([ ...DEFAULT_SCOPES, ]) @@ -41,12 +45,16 @@ export default function GenerateApiKeyPage() { }, []) function buildConnectUrl(): string { + if (!authDetails?.did) { + return "#" + } + const redirectUrl = new URL(AUTH_ENDPOINT) for (const scope of selectedScopes) { redirectUrl.searchParams.append("scopes", scope) } redirectUrl.searchParams.append("redirectUrl", RETURN_URL) - redirectUrl.searchParams.append("appDID", did!) + redirectUrl.searchParams.append("appDID", authDetails?.did) return redirectUrl.toString() } @@ -89,105 +97,86 @@ export default function GenerateApiKeyPage() { )} {/* Collapsible sections for different scope groups */} -
- {/* API Scopes Section */} - - {({ open }) => ( -
- - API Scopes {open ? "-" : "+"} - - -
- {Object.entries(scopes) - .filter(([key, scope]) => scope.type === "api") - .map(([scopeKey, scopeDef], idx) => { - const isChecked = selectedScopes.includes(scopeKey) - return ( -
- - handleScopeToggle(scopeKey, checked) - } - /> - -
- ) - })} -
-
+ + + + API Scopes + +
+ {Object.entries(scopes) + .filter(([key, scope]) => scope.type === "api") + .map(([scopeKey, scopeDef], idx) => { + const isChecked = selectedScopes.includes(scopeKey) + return ( +
+ + handleScopeToggle(scopeKey, checked) + } + /> + +
+ ) + })}
- )} - - - {/* Datastore Scopes Section */} - - {({ open }) => ( -
- - Datastore Scopes {open ? "-" : "+"} - - -
- {Object.entries(scopes) - .filter(([key, scope]) => scope.type === "ds") - .map(([scopeKey, scopeDef], idx) => { - const isChecked = selectedScopes.includes(scopeKey) - return ( -
- - handleScopeToggle(scopeKey, checked) - } - /> - -
- ) - })} -
-
+ + + + Datastore Scopes + +
+ {Object.entries(scopes) + .filter(([key, scope]) => scope.type === "ds") + .map(([scopeKey, scopeDef], idx) => { + const isChecked = selectedScopes.includes(scopeKey) + return ( +
+ + handleScopeToggle(scopeKey, checked) + } + /> + +
+ ) + })}
- )} - -
+
+
+
diff --git a/src/app/(connected)/sandbox/page.tsx b/src/app/(connected)/sandbox/page.tsx index 8863c0e..b83fd24 100644 --- a/src/app/(connected)/sandbox/page.tsx +++ b/src/app/(connected)/sandbox/page.tsx @@ -1,5 +1,3 @@ -"use client" - export default function SandboxIndexPage() { return (
diff --git a/src/app/(connected)/sandbox/token-generated/page.tsx b/src/app/(connected)/sandbox/token-generated/page.tsx index 32af375..f68e4ce 100644 --- a/src/app/(connected)/sandbox/token-generated/page.tsx +++ b/src/app/(connected)/sandbox/token-generated/page.tsx @@ -20,7 +20,7 @@ export default function ApiKeyGeneratedPage() { const searchParams = useSearchParams() const [apiKey, setApiKey] = useState("") const [apiKeySaved, setApiKeySaved] = useState(false) - const [tokenData, setTokenData] = useState({}) + const [tokenData, setTokenData] = useState>({}) function saveApiKey(key?: string) { localStorage.setItem("authToken", key || apiKey) diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx new file mode 100644 index 0000000..5dd7809 --- /dev/null +++ b/src/app/auth/page.tsx @@ -0,0 +1,45 @@ +"use client" + +import { useRouter } from "next/navigation" +import { useEffect } from "react" + +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" +import { ConnectButton } from "@/features/auth/components/connect-button" +import { getRootPageRoute } from "@/features/routes/utils" +import { VERIDA_AUTH_ERROR_MESSAGES } from "@/features/verida-auth/constants" +import { useVeridaAuth } from "@/features/verida-auth/hooks/use-verida-auth" +import { useVeridaAuthResponse } from "@/features/verida-auth/hooks/use-verida-auth-response" + +export default function AuthPage() { + const router = useRouter() + const { setToken } = useVeridaAuth() + const authResponse = useVeridaAuthResponse() + + useEffect(() => { + if (authResponse.status === "success") { + setToken(authResponse.token) + // TODO: get the redirectPath from the state + router.replace(getRootPageRoute()) + } + }, [authResponse, router, setToken]) + + if (authResponse.status === "error") { + const errorInfo = VERIDA_AUTH_ERROR_MESSAGES[authResponse.error] + + return ( +
+ + {errorInfo.title} + + {authResponse.errorDescription || errorInfo.description} + + + +
+ ) + } + + // TODO: Display something while processing the response or if there is no response at all + return null +} +AuthPage.displayName = "AuthPage" diff --git a/src/app/page.tsx b/src/app/page.tsx index c5a6763..455f2c1 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,14 +1,14 @@ -import { RootConnectionHandler } from "@/components/root-connection-handler" -import { VeridaConnectButton } from "@/features/verida/components/verida-connect-button" +import { ConnectButton } from "@/features/auth/components/connect-button" +import { RootAuthenticationHandler } from "@/features/auth/components/root-authentication-handler" export default function RootPage() { return ( - +

Verida: AI Developer Admin

- +
-
+ ) } RootPage.displayName = "RootPage" diff --git a/src/components/app-connection-handler.tsx b/src/components/app-connection-handler.tsx deleted file mode 100644 index 0d8069e..0000000 --- a/src/components/app-connection-handler.tsx +++ /dev/null @@ -1,28 +0,0 @@ -"use client" - -import { redirect, usePathname, useSearchParams } from "next/navigation" - -import { useVerida } from "@/features/verida/hooks/use-verida" - -export interface AppConnectionHandlerProps { - children: React.ReactNode -} - -export function AppConnectionHandler(props: AppConnectionHandlerProps) { - const { children } = props - - const pathName = usePathname() - const searchParams = useSearchParams() - const { isConnected } = useVerida() - - if (!isConnected) { - const fullPath = `${pathName}${searchParams.toString() ? `?${searchParams.toString()}` : ""}` - const encodedRedirectPath = encodeURIComponent(fullPath) - // Ensure to use the same `redirectPath` query parameter as in `RootConnectionHandler`. - // TODO: Use nuqs to factorise the redirect path search param. - redirect(`/?redirectPath=${encodedRedirectPath}`) - } - - return <>{children} -} -AppConnectionHandler.displayName = "AppConnectionHandler" diff --git a/src/components/root-connection-handler.tsx b/src/components/root-connection-handler.tsx deleted file mode 100644 index c465b69..0000000 --- a/src/components/root-connection-handler.tsx +++ /dev/null @@ -1,37 +0,0 @@ -"use client" - -import { redirect, useSearchParams } from "next/navigation" - -import { useVerida } from "@/features/verida/hooks/use-verida" - -const DEFAULT_REDIRECT_PATH = "/dashboard" - -export interface RootConnectionHandlerProps { - children: React.ReactNode -} - -export function RootConnectionHandler(props: RootConnectionHandlerProps) { - const { children } = props - - const { isConnected, isConnecting } = useVerida() - - const searchParams = useSearchParams() - // Ensure to use the same `redirectPath` search parameter as in `AppConnectionHandler`. - // TODO: Use nuqs to factorise the redirect path search param. - const redirectPath = searchParams.get("redirectPath") || DEFAULT_REDIRECT_PATH - - if (isConnected) { - redirect(decodeURIComponent(redirectPath)) - } - - if (isConnecting) { - return ( -
-

Connecting...

-
- ) - } - - return <>{children} -} -RootConnectionHandler.displayName = "RootConnectionHandler" diff --git a/src/components/root-providers.tsx b/src/components/root-providers.tsx index e4e0def..68b9473 100644 --- a/src/components/root-providers.tsx +++ b/src/components/root-providers.tsx @@ -6,7 +6,7 @@ import { Suspense } from "react" import { TooltipProvider } from "@/components/ui/tooltip" import { QueriesProvider } from "@/features/queries/queries-provider" import { ThemesProvider } from "@/features/themes/themes-provider" -import { VeridaProvider } from "@/features/verida/components/verida-provider" +import { VeridaAuthProvider } from "@/features/verida-auth/components/verida-auth-provider" export interface RootProvidersProps { children: React.ReactNode @@ -23,7 +23,7 @@ export function RootProviders(props: RootProvidersProps) { - {children} + {children} diff --git a/src/components/sidebar-nav.tsx b/src/components/sidebar-nav.tsx index e140501..5f400d5 100644 --- a/src/components/sidebar-nav.tsx +++ b/src/components/sidebar-nav.tsx @@ -6,7 +6,7 @@ import { usePathname } from "next/navigation" import { Button } from "@/components/ui/button" import { navItems } from "@/config/nav" import type { NavItem } from "@/config/nav" -import { useVerida } from "@/features/verida/hooks/use-verida" +import { useVeridaAuth } from "@/features/verida-auth/hooks/use-verida-auth" import { cn } from "@/styles/utils" // If you have a classNames utility @@ -32,7 +32,7 @@ function SidebarLink({ item }: { item: NavItem }) { } export function SidebarNav() { - const { disconnect } = useVerida() + const { disconnect } = useVeridaAuth() return (