Skip to content

Commit 6ddbcb2

Browse files
authored
Persist wallet in localstorage (#1447)
1 parent 6a7493a commit 6ddbcb2

File tree

5 files changed

+124
-26
lines changed

5 files changed

+124
-26
lines changed

src/components/WalletKit/ConnectWallet.tsx

Lines changed: 78 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useContext, useState } from "react";
1+
import { useContext, useEffect, useState } from "react";
22
import { Button, Modal, Text } from "@stellar/design-system";
33
import { ISupportedWallet } from "@creit.tech/stellar-wallets-kit";
44
import { useStore } from "@/store/useStore";
@@ -7,6 +7,7 @@ import { useAccountInfo } from "@/query/useAccountInfo";
77

88
import { shortenStellarAddress } from "@/helpers/shortenStellarAddress";
99
import { getNetworkHeaders } from "@/helpers/getNetworkHeaders";
10+
import { localStorageSavedWallet } from "@/helpers/localStorageSavedWallet";
1011

1112
import { ConnectedModal } from "@/components/WalletKit/ConnectedModal";
1213
import { WalletKitContext } from "@/components/WalletKit/WalletKitContextProvider";
@@ -15,56 +16,109 @@ import { trackEvent, TrackingEvent } from "@/metrics/tracking";
1516

1617
export const ConnectWallet = () => {
1718
const { network, walletKit, updateWalletKit } = useStore();
19+
const [connected, setConnected] = useState<boolean>(false);
1820
const [isModalVisible, setShowModal] = useState(false);
1921
const [errorMessageOnConnect, setErrorMessageOnConnect] = useState("");
2022
const walletKitInstance = useContext(WalletKitContext);
23+
const savedWallet = localStorageSavedWallet.get();
2124

2225
const { data: accountInfo, refetch: fetchAccountInfo } = useAccountInfo({
2326
publicKey: walletKit?.publicKey || "",
2427
horizonUrl: network?.horizonUrl || "",
2528
headers: network ? getNetworkHeaders(network, "horizon") : {},
2629
});
2730

28-
const resetWalletKit = () => {
31+
const disconnect = () => {
2932
updateWalletKit({
3033
publicKey: undefined,
3134
walletType: undefined,
3235
});
3336

3437
setShowModal(false);
38+
setConnected(false);
39+
localStorageSavedWallet.remove();
3540
};
3641

42+
useEffect(() => {
43+
let t: NodeJS.Timeout;
44+
45+
if (
46+
!connected &&
47+
!!savedWallet?.id &&
48+
![undefined, "false", "wallet_connect"].includes(savedWallet?.id) &&
49+
savedWallet.network.id === network.id
50+
) {
51+
t = setTimeout(() => {
52+
walletKitInstance.walletKit?.setWallet(savedWallet.id);
53+
handleSetWalletAddress();
54+
clearTimeout(t);
55+
}, 750);
56+
}
57+
58+
return () => {
59+
clearTimeout(t);
60+
};
61+
}, [savedWallet?.id, connected, walletKitInstance]);
62+
63+
async function handleSetWalletAddress(): Promise<boolean> {
64+
try {
65+
const addressResult = await walletKitInstance.walletKit?.getAddress();
66+
67+
if (!addressResult?.address) {
68+
return false;
69+
}
70+
const publicKey = addressResult.address;
71+
72+
if (!publicKey) {
73+
return false;
74+
}
75+
76+
updateWalletKit({
77+
publicKey,
78+
walletType: savedWallet?.id,
79+
});
80+
setConnected(true);
81+
82+
return true;
83+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
84+
} catch (e) {
85+
return false;
86+
}
87+
}
88+
3789
const connectWallet = async () => {
3890
try {
3991
await walletKitInstance.walletKit?.openModal({
4092
onWalletSelected: async (option: ISupportedWallet) => {
41-
try {
42-
walletKitInstance.walletKit?.setWallet(option.id);
43-
const addressResult =
44-
await walletKitInstance.walletKit?.getAddress();
45-
46-
if (addressResult?.address) {
47-
updateWalletKit({
48-
publicKey: addressResult.address,
49-
walletType: option.id,
50-
});
51-
52-
trackEvent(TrackingEvent.WALLET_KIT_SELECTED, {
53-
walletType: option.id,
54-
});
55-
}
56-
} catch (e: unknown) {
57-
// Ledger sends a message with the error code, so we need to check for that
58-
const errorMessage =
59-
(e as { message?: string })?.message || "Unknown error occurred";
93+
walletKitInstance.walletKit?.setWallet(option.id);
94+
const isWalletConnected = await handleSetWalletAddress();
95+
96+
if (!isWalletConnected) {
97+
const errorMessage = "Unable to load wallet information";
6098
setErrorMessageOnConnect(errorMessage);
61-
resetWalletKit();
99+
disconnect();
100+
return;
62101
}
102+
103+
localStorageSavedWallet.set({
104+
id: option.id,
105+
network: {
106+
id: network.id,
107+
label: network.label,
108+
},
109+
});
110+
111+
trackEvent(TrackingEvent.WALLET_KIT_SELECTED, {
112+
walletType: option.id,
113+
});
63114
},
64115
});
65116
// eslint-disable-next-line @typescript-eslint/no-unused-vars
66117
} catch (e) {
67-
resetWalletKit();
118+
const errorMessage =
119+
(e as { message?: string })?.message || "Unknown error occurred";
120+
setErrorMessageOnConnect(errorMessage);
121+
disconnect();
68122
}
69123
};
70124

@@ -82,7 +136,7 @@ export const ConnectWallet = () => {
82136
isVisible={isModalVisible}
83137
showModal={setShowModal}
84138
publicKey={walletKit?.publicKey || ""}
85-
onDisconnect={resetWalletKit}
139+
onDisconnect={disconnect}
86140
balance={
87141
xlmBalance?.balance
88142
? `${xlmBalance.balance} XLM`

src/components/WalletKit/WalletKitContextProvider.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import { createContext, useEffect, useMemo } from "react";
3+
import { createContext, useEffect, useMemo, useState } from "react";
44
import { useStore } from "@/store/useStore";
55

66
import {
@@ -17,9 +17,12 @@ import { LedgerModule } from "@creit.tech/stellar-wallets-kit/modules/ledger.mod
1717

1818
import { getWalletKitNetwork } from "@/helpers/getWalletKitNetwork";
1919
import { localStorageSavedTheme } from "@/helpers/localStorageSavedTheme";
20+
import { localStorageSavedWallet } from "@/helpers/localStorageSavedWallet";
21+
import { SavedWallet } from "@/types/types";
2022

2123
type WalletKitProps = {
2224
walletKit?: StellarWalletsKit;
25+
walletId?: string;
2326
};
2427

2528
export const WalletKitContext = createContext<WalletKitProps>({
@@ -32,6 +35,7 @@ export const WalletKitContextProvider = ({
3235
children: React.ReactNode;
3336
}) => {
3437
const { network, theme, setTheme } = useStore();
38+
const [savedWallet, setSavedWallet] = useState<SavedWallet | null>(null);
3539
const networkType = getWalletKitNetwork(network.id);
3640

3741
useEffect(() => {
@@ -44,6 +48,15 @@ export const WalletKitContextProvider = ({
4448
// eslint-disable-next-line react-hooks/exhaustive-deps
4549
}, []);
4650

51+
useEffect(() => {
52+
const savedWallet = localStorageSavedWallet.get();
53+
54+
if (savedWallet && savedWallet.network.id === network.id) {
55+
setSavedWallet(savedWallet);
56+
}
57+
// eslint-disable-next-line react-hooks/exhaustive-deps
58+
}, []);
59+
4760
const walletKitInstance = useMemo(() => {
4861
const isDarkTheme = theme === "sds-theme-dark";
4962

@@ -111,7 +124,12 @@ export const WalletKitContextProvider = ({
111124
}, [networkType, theme]);
112125

113126
return (
114-
<WalletKitContext.Provider value={{ walletKit: walletKitInstance }}>
127+
<WalletKitContext.Provider
128+
value={{
129+
walletKit: walletKitInstance,
130+
walletId: savedWallet?.id,
131+
}}
132+
>
115133
{children}
116134
</WalletKitContext.Provider>
117135
);

src/constants/settings.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export const LOCAL_STORAGE_SAVED_CONTRACTS = "stellar_lab_saved_contract_ids";
1616
export const LOCAL_STORAGE_SAVED_THEME = "stellarTheme:Laboratory";
1717
export const LOCAL_STORAGE_SAVED_EXPLORER_TRANSACTIONS =
1818
"stellar_lab_saved_explorer_transactions";
19+
export const LOCAL_STORAGE_SAVED_WALLET = "stellar_lab_wallet";
1920

2021
export const XDR_TYPE_TRANSACTION_ENVELOPE = "TransactionEnvelope";
2122

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { LOCAL_STORAGE_SAVED_WALLET } from "@/constants/settings";
2+
import { SavedWallet } from "@/types/types";
3+
4+
export const localStorageSavedWallet = {
5+
get: () => {
6+
const savedWalletString = localStorage.getItem(LOCAL_STORAGE_SAVED_WALLET);
7+
return savedWalletString
8+
? (JSON.parse(savedWalletString) as SavedWallet)
9+
: null;
10+
},
11+
set: (savedWallet: SavedWallet) => {
12+
return localStorage.setItem(
13+
LOCAL_STORAGE_SAVED_WALLET,
14+
JSON.stringify(savedWallet),
15+
);
16+
},
17+
remove: () => {
18+
return localStorage.removeItem(LOCAL_STORAGE_SAVED_WALLET);
19+
},
20+
};

src/types/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,11 @@ export interface SavedContract extends LocalStorageSavedItem {
396396
shareableUrl: string;
397397
}
398398

399+
export interface SavedWallet {
400+
id: string;
401+
network: LocalStorageSavedNetwork;
402+
}
403+
399404
export type SavedTransactionPage = "build" | "sign" | "simulate" | "submit";
400405

401406
export interface SavedEndpointHorizon extends LocalStorageSavedItem {

0 commit comments

Comments
 (0)