Skip to content

Commit

Permalink
fix: persist login state across refreshes
Browse files Browse the repository at this point in the history
  • Loading branch information
moldy530 committed Oct 8, 2024
1 parent 52eda43 commit cca2633
Show file tree
Hide file tree
Showing 11 changed files with 298 additions and 129 deletions.
152 changes: 151 additions & 1 deletion account-kit/core/src/store/store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,157 @@ describe("createConfig tests", () => {
"421614": {},
},
},
"version": 8,
"version": 9,
}
`);
});

it("should serialize/deserialize state correctly when using single chain config", async () => {
const config = createConfig({
chain: sepolia,
transport: alchemy({ rpcUrl: "/api/sepolia" }),
signerConnection: { rpcUrl: "/api/signer" },
sessionConfig: {
expirationTimeMs: 1000,
},
policyId: "test-policy-id",
storage: () => localStorage,
});

await config.store.persist.rehydrate();

config.store.setState({
accounts: createDefaultAccountState([sepolia]),
});

expect(JSON.parse(localStorage.getItem(DEFAULT_STORAGE_KEY) ?? "{}"))
.toMatchInlineSnapshot(`
{
"state": {
"accountConfigs": {
"11155111": {},
},
"chain": {
"blockExplorers": {
"default": {
"apiUrl": "https://api-sepolia.etherscan.io/api",
"name": "Etherscan",
"url": "https://sepolia.etherscan.io",
},
},
"contracts": {
"ensRegistry": {
"address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
},
"ensUniversalResolver": {
"address": "0xc8Af999e38273D658BE1b921b88A9Ddf005769cC",
"blockCreated": 5317080,
},
"multicall3": {
"address": "0xca11bde05977b3631167028862be2a173976ca11",
"blockCreated": 751532,
},
},
"id": 11155111,
"name": "Sepolia",
"nativeCurrency": {
"decimals": 18,
"name": "Sepolia Ether",
"symbol": "ETH",
},
"rpcUrls": {
"alchemy": {
"http": [
"https://eth-sepolia.g.alchemy.com/v2",
],
},
"default": {
"http": [
"https://rpc.sepolia.org",
],
},
},
"testnet": true,
},
"config": {
"client": {
"connection": {
"rpcUrl": "/api/signer",
},
},
"sessionConfig": {
"expirationTimeMs": 1000,
},
},
"connections": {
"__type": "Map",
"value": [
[
11155111,
{
"chain": {
"blockExplorers": {
"default": {
"apiUrl": "https://api-sepolia.etherscan.io/api",
"name": "Etherscan",
"url": "https://sepolia.etherscan.io",
},
},
"contracts": {
"ensRegistry": {
"address": "0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e",
},
"ensUniversalResolver": {
"address": "0xc8Af999e38273D658BE1b921b88A9Ddf005769cC",
"blockCreated": 5317080,
},
"multicall3": {
"address": "0xca11bde05977b3631167028862be2a173976ca11",
"blockCreated": 751532,
},
},
"id": 11155111,
"name": "Sepolia",
"nativeCurrency": {
"decimals": 18,
"name": "Sepolia Ether",
"symbol": "ETH",
},
"rpcUrls": {
"alchemy": {
"http": [
"https://eth-sepolia.g.alchemy.com/v2",
],
},
"default": {
"http": [
"https://rpc.sepolia.org",
],
},
},
"testnet": true,
},
"policyId": "test-policy-id",
"transport": {
"__type": "Transport",
"rpcUrl": "/api/sepolia",
},
},
],
],
},
"signerStatus": {
"isAuthenticating": false,
"isConnected": false,
"isDisconnected": false,
"isInitializing": true,
"status": "INITIALIZING",
},
"smartAccountClients": {
"11155111": {},
},
},
"version": 9,
}
`);
});
Expand Down
2 changes: 1 addition & 1 deletion account-kit/core/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const createAccountKitStore = (
skipHydration: ssr,
partialize: ({ signer, accounts, ...writeableState }) =>
writeableState,
version: 8,
version: 9,
})
: () => createInitialStoreState(params)
)
Expand Down
82 changes: 82 additions & 0 deletions examples/ui-demo/src/app/config.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { AuthCardHeader } from "@/components/shared/AuthCardHeader";
import { alchemy, arbitrumSepolia } from "@account-kit/infra";
import { cookieStorage, createConfig } from "@account-kit/react";
import { AccountKitTheme } from "@account-kit/react/tailwind";
import { QueryClient } from "@tanstack/react-query";

export type Config = {
auth: {
showEmail: boolean;
showExternalWallets: boolean;
showPasskey: boolean;
addPasskey: boolean;
};
ui: {
theme: "light" | "dark";
primaryColor: {
dark: string;
light: string;
};
borderRadius: AccountKitTheme["borderRadius"];
illustrationStyle: "outline" | "linear" | "filled" | "flat";
logoLight:
| {
fileName: string;
fileSrc: string;
}
| undefined;
logoDark:
| {
fileName: string;
fileSrc: string;
}
| undefined;
};
supportUrl?: string;
};

export const DEFAULT_CONFIG: Config = {
auth: {
showEmail: true,
showExternalWallets: false,
showPasskey: true,
addPasskey: true,
},
ui: {
theme: "light",
primaryColor: {
light: "#363FF9",
dark: "#9AB7FF",
},
borderRadius: "sm",
illustrationStyle: "outline",
logoLight: undefined,
logoDark: undefined,
},
};

export const queryClient = new QueryClient();

export const alchemyConfig = createConfig(
{
transport: alchemy({ rpcUrl: "/api/rpc" }),
chain: arbitrumSepolia,
ssr: true,
policyId: process.env.NEXT_PUBLIC_PAYMASTER_POLICY_ID,
storage: cookieStorage,
},
{
illustrationStyle: DEFAULT_CONFIG.ui.illustrationStyle,
auth: {
sections: [[{ type: "email" as const }], [{ type: "passkey" as const }]],
addPasskeyOnSignup: DEFAULT_CONFIG.auth.addPasskey,
header: (
<AuthCardHeader
theme={DEFAULT_CONFIG.ui.theme}
logoDark={DEFAULT_CONFIG.ui.logoDark}
logoLight={DEFAULT_CONFIG.ui.logoLight}
/>
),
},
}
);
10 changes: 9 additions & 1 deletion examples/ui-demo/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { cookieToInitialState } from "@account-kit/core";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { headers } from "next/headers";
import { alchemyConfig } from "./config";
import "./globals.css";
import { Providers } from "./providers";

Expand All @@ -15,10 +18,15 @@ export default function RootLayout({
}: Readonly<{
children: React.ReactNode;
}>) {
const initialState = cookieToInitialState(
alchemyConfig,
headers().get("cookie") ?? undefined
);

return (
<html lang="en" className="light">
<body className={inter.className}>
<Providers>{children}</Providers>
<Providers initialState={initialState}>{children}</Providers>
</body>
</html>
);
Expand Down
18 changes: 9 additions & 9 deletions examples/ui-demo/src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
"use client";

import { useConfig } from "@/app/state";
import { Authentication } from "@/components/configuration/Authentication";
import { Styling } from "@/components/configuration/Styling";
import { MobileSplashPage } from "@/components/preview/MobileSplashPage";
import {
EOAPostLoginActions,
EOAPostLoginContents,
} from "@/components/shared/eoa-post-login/EOAPostLoginContents";
import { RenderUserConnectionAvatar } from "@/components/shared/user-connection-avatar/RenderUserConnectionAvatar";
import { cn } from "@/lib/utils";
import { useUser } from "@account-kit/react";
import { Inter, Public_Sans } from "next/font/google";
import { useState } from "react";
import { AuthCardWrapper } from "../components/preview/AuthCardWrapper";
import { CodePreview } from "../components/preview/CodePreview";
import { CodePreviewSwitch } from "../components/shared/CodePreviewSwitch";
import { TopNav } from "../components/topnav/TopNav";
import { RenderUserConnectionAvatar } from "@/components/shared/user-connection-avatar/RenderUserConnectionAvatar";
import { useUser } from "@account-kit/react";
import { MobileSplashPage } from "@/components/preview/MobileSplashPage";
import { cn } from "@/lib/utils";
import { useConfig } from "@/app/state";
import {
EOAPostLoginContents,
EOAPostLoginActions,
} from "@/components/shared/eoa-post-login/EOAPostLoginContents";

const publicSans = Public_Sans({
subsets: ["latin"],
Expand Down
42 changes: 10 additions & 32 deletions examples/ui-demo/src/app/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,23 @@
"use client";

import { AuthCardHeader } from "@/components/shared/AuthCardHeader";
import { alchemy, arbitrumSepolia } from "@account-kit/infra";
import { AlchemyAccountProvider, createConfig } from "@account-kit/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { PropsWithChildren, Suspense } from "react";
import { ConfigContextProvider, DEFAULT_CONFIG } from "./state";
import { ToastProvider } from "@/contexts/ToastProvider";
import { AlchemyClientState } from "@account-kit/core";
import { AlchemyAccountProvider } from "@account-kit/react";
import { QueryClientProvider } from "@tanstack/react-query";
import { PropsWithChildren, Suspense } from "react";
import { alchemyConfig, queryClient } from "./config";
import { ConfigContextProvider } from "./state";

const queryClient = new QueryClient();

const alchemyConfig = createConfig(
{
transport: alchemy({ rpcUrl: "/api/rpc" }),
chain: arbitrumSepolia,
ssr: true,
policyId: process.env.NEXT_PUBLIC_PAYMASTER_POLICY_ID,
},
{
illustrationStyle: DEFAULT_CONFIG.ui.illustrationStyle,
auth: {
sections: [[{ type: "email" as const }], [{ type: "passkey" as const }]],
addPasskeyOnSignup: DEFAULT_CONFIG.auth.addPasskey,
header: (
<AuthCardHeader
theme={DEFAULT_CONFIG.ui.theme}
logoDark={DEFAULT_CONFIG.ui.logoDark}
logoLight={DEFAULT_CONFIG.ui.logoLight}
/>
),
},
}
);

export const Providers = (props: PropsWithChildren<{}>) => {
export const Providers = (
props: PropsWithChildren<{ initialState?: AlchemyClientState }>
) => {
return (
<Suspense>
<QueryClientProvider client={queryClient}>
<AlchemyAccountProvider
config={alchemyConfig}
queryClient={queryClient}
initialState={props.initialState}
>
<ToastProvider>
<ConfigContextProvider>{props.children}</ConfigContextProvider>
Expand Down
Loading

0 comments on commit cca2633

Please sign in to comment.