Skip to content

Commit

Permalink
feat: Store wallet connected state in localStorage
Browse files Browse the repository at this point in the history
  • Loading branch information
kovipu committed Oct 27, 2024
1 parent 7de71e1 commit f52bcf3
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 41 deletions.
33 changes: 24 additions & 9 deletions src/components/Nav.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { type PropsWithChildren, useEffect, useRef } from 'react';
import type { PropsWithChildren } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useWallet } from 'src/stellar-wallet';
import logo from '/public/laina_v3_shrinked.png';
import { Button } from './Button';
import Identicon from './Identicon';

export default function Nav() {
const { pathname } = useLocation();
const { createConnectWalletButton } = useWallet();

const buttonWrapperRef = useRef<HTMLDivElement>(null);

useEffect(() => {
buttonWrapperRef.current && createConnectWalletButton(buttonWrapperRef.current);
}, [createConnectWalletButton]);
const { wallet, openConnectWalletModal, disconnectWallet } = useWallet();

const isIndex = pathname === '/';

Expand All @@ -30,7 +26,26 @@ export default function Nav() {
<LinkItem to="/lend">Assets</LinkItem>
</div>

<div ref={buttonWrapperRef} />
{!wallet ? (
<Button onClick={openConnectWalletModal}>Connect wallet</Button>
) : (
<div className="dropdown dropdown-end">
<div tabIndex={0} role="button">
<Identicon address={wallet.address} />
</div>
<ul className="dropdown-content rounded-box bg-white mt-1 mr-1 w-64 z-[1] p-4 shadow">
<li className="px-8 py-4">
<p className="font-semibold">{wallet.displayName}</p>
<p className="text-grey leading-tight">{wallet.name}</p>
</li>
<li>
<Button variant="outline" onClick={disconnectWallet}>
Disconnect Wallet
</Button>
</li>
</ul>
</div>
)}
</nav>
);
}
Expand Down
12 changes: 7 additions & 5 deletions src/components/WalletCard/WalletCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ const ASSET_MODAL_ID = 'assets-modal';
const LOANS_MODAL_ID = 'loans-modal';

const WalletCard = () => {
const { wallet, positions, prices } = useWallet();
const { wallet, openConnectWalletModal, positions, prices } = useWallet();

if (!wallet) {
return (
<Card bgColor="black" className="text-white p-12 mb-12 min-h-36 flex flex-col justify-center">
<Card bgColor="black" className="text-white p-12 mb-12 min-h-36 flex flex-col justify-center items-start">
<h2 className="text-xl font-semibold">My Account</h2>
<p className="mt-2">To view your assets, connect a wallet first.</p>
<p className="mt-2 mb-6">To view your assets, connect a wallet first.</p>
<Button variant="white" onClick={openConnectWalletModal}>
Connect Wallet
</Button>
</Card>
);
}
Expand All @@ -35,8 +38,7 @@ const WalletCard = () => {
<Identicon address={wallet.address} />
<div className="ml-9">
<p className="text-xl">{wallet.displayName}</p>
{/* TODO: Get wallet type from the kit. */}
<p className="text-grey leading-tight">Freighter</p>
<p className="text-grey leading-tight">{wallet.name}</p>
</div>
</div>
</div>
Expand Down
75 changes: 48 additions & 27 deletions src/stellar-wallet.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
import { FREIGHTER_ID, StellarWalletsKit, WalletNetwork, allowAllModules } from '@creit.tech/stellar-wallets-kit';
import {
FREIGHTER_ID,
type ISupportedWallet,
StellarWalletsKit,
WalletNetwork,
allowAllModules,
} from '@creit.tech/stellar-wallets-kit';
import * as StellarSdk from '@stellar/stellar-sdk';
import { type PropsWithChildren, createContext, useContext, useEffect, useState } from 'react';

import { contractClient as loanManagerClient } from '@contracts/loan_manager';
import type { SupportedCurrency } from 'currencies';
import { isNil } from 'ramda';
import { CURRENCY_BINDINGS_ARR } from './currency-bindings';

const HorizonServer = new StellarSdk.Horizon.Server('https://horizon-testnet.stellar.org/');

export type Wallet = {
name: string;
address: string;
displayName: string;
};
Expand Down Expand Up @@ -38,7 +46,8 @@ export type WalletContext = {
walletBalances: BalanceRecord;
positions: PositionsRecord;
prices: PriceRecord | null;
createConnectWalletButton: (container: HTMLElement) => void;
openConnectWalletModal: () => void;
disconnectWallet: () => void;
refetchBalances: () => void;
signTransaction: SignTransaction;
};
Expand All @@ -59,7 +68,8 @@ const Context = createContext<WalletContext>({
walletBalances: {},
positions: {},
prices: null,
createConnectWalletButton: () => {},
openConnectWalletModal: () => {},
disconnectWallet: () => {},
refetchBalances: () => {},
signTransaction: () => Promise.reject(),
});
Expand All @@ -70,7 +80,8 @@ const kit: StellarWalletsKit = new StellarWalletsKit({
modules: allowAllModules(),
});

const createWalletObj = (address: string): Wallet => ({
const createWalletObj = (name: string, address: string): Wallet => ({
name,
address,
displayName: `${address.slice(0, 4)}...${address.slice(-4)}`,
});
Expand Down Expand Up @@ -119,25 +130,32 @@ const fetchPriceData = async (token: string): Promise<bigint> => {
};

export const WalletProvider = ({ children }: PropsWithChildren) => {
const [address, setAddress] = useState<string | null>(null);
const [wallet, setWallet] = useState<Wallet | null>(null);
const [walletBalances, setWalletBalances] = useState<BalanceRecord>({});
const [positions, setPositions] = useState<PositionsRecord>({});
const [prices, setPrices] = useState<PriceRecord | null>(null);

const setWallet = async (address: string) => {
setAddress(address);
const { balances } = await HorizonServer.loadAccount(address);
setWalletBalances(createBalanceRecord(balances));
setPositions(await fetchAllPositions(address));
const loadWallet = async (name: string) => {
try {
const { address } = await kit.getAddress();
setWallet(createWalletObj(name, address));
const { balances } = await HorizonServer.loadAccount(address);
setWalletBalances(createBalanceRecord(balances));
setPositions(await fetchAllPositions(address));
localStorage.setItem('wallet-connected', name);
} catch (err) {
console.error('Loading wallet failed', err);
localStorage.removeItem('wallet-connected');
}
};

// Set initial wallet on load.
// biome-ignore lint: useEffect is ass
useEffect(() => {
kit
.getAddress()
.then(({ address }) => setWallet(address))
.catch((err) => console.log('No initial wallet.', err));
const walletConnected = localStorage.getItem('wallet-connected');
if (!isNil(walletConnected)) {
loadWallet(walletConnected);
}
fetchAllPrices()
.then((res) => setPrices(res))
.catch((err) => console.error('Error fetching prices', err));
Expand All @@ -148,40 +166,43 @@ export const WalletProvider = ({ children }: PropsWithChildren) => {
return signedTxXdr;
};

const createConnectWalletButton = (container: HTMLElement) => {
kit.createButton({
container,
onConnect: ({ address }) => setWallet(address),
onDisconnect: () => {
setAddress(null);
setWalletBalances({});
const openConnectWalletModal = () => {
kit.openModal({
onWalletSelected: ({ name }) => {
loadWallet(name);
},
});
};

const disconnectWallet = () => {
setWallet(null);
setWalletBalances({});
setPositions({});
localStorage.removeItem('wallet-connected');
};

const refetchBalances = async () => {
if (!address) return;
if (!wallet) return;

try {
const { balances } = await HorizonServer.loadAccount(address);
const { balances } = await HorizonServer.loadAccount(wallet.address);
setWalletBalances(createBalanceRecord(balances));
const positions = await fetchAllPositions(address);
const positions = await fetchAllPositions(wallet.address);
setPositions(positions);
} catch (err) {
console.error('Error fetching balances', err);
}
};

const wallet: Wallet | null = address ? createWalletObj(address) : null;

return (
<Context.Provider
value={{
wallet,
walletBalances,
positions,
prices,
createConnectWalletButton,
openConnectWalletModal,
disconnectWallet,
refetchBalances,
signTransaction,
}}
Expand Down

0 comments on commit f52bcf3

Please sign in to comment.