From 5f603917ac723cc6697ecfec15f0af9930da9876 Mon Sep 17 00:00:00 2001 From: Luluameh Date: Sun, 22 Feb 2026 00:27:18 +0100 Subject: [PATCH 1/2] Create Wallet Context for global wallet state management --- dongle/app/layout.tsx | 6 ++- dongle/app/page.tsx | 44 ++++++++++++++- dongle/context/wallet.context.tsx | 90 +++++++++++++++++++++++++++++++ dongle/package-lock.json | 1 + 4 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 dongle/context/wallet.context.tsx diff --git a/dongle/app/layout.tsx b/dongle/app/layout.tsx index f7fa87e..73fb573 100644 --- a/dongle/app/layout.tsx +++ b/dongle/app/layout.tsx @@ -1,7 +1,7 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import "./globals.css"; - +import { WalletProvider } from "@/context/wallet.context"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], @@ -27,7 +27,9 @@ export default function RootLayout({ - {children} + + {children} + ); diff --git a/dongle/app/page.tsx b/dongle/app/page.tsx index 295f8fd..dcce020 100644 --- a/dongle/app/page.tsx +++ b/dongle/app/page.tsx @@ -1,8 +1,50 @@ +"use client"; + import Image from "next/image"; +import { useWallet } from "@/context/wallet.context"; export default function Home() { + const { isConnected, isConnecting, publicKey, connectWallet, disconnectWallet } = + useWallet(); + return ( -
+
+ {/* Wallet Header */} +
+ {isConnected ? ( +
+
+ + {publicKey ? `${publicKey.substring(0, 6)}...${publicKey.substring(publicKey.length - 4)}` : "Connected"} + + +
+ ) : ( + + )} +
+
Promise; + disconnectWallet: () => void; +} + +const WalletContext = createContext(undefined); + +const WALLET_STORAGE_KEY = "dongle_wallet_state"; + +export function WalletProvider({ children }: { children: ReactNode }) { + const [publicKey, setPublicKey] = useState(null); + const [isConnected, setIsConnected] = useState(false); + const [isConnecting, setIsConnecting] = useState(false); + + // Initialize from local storage to persist connection state across sessions + useEffect(() => { + try { + const storedState = localStorage.getItem(WALLET_STORAGE_KEY); + if (storedState) { + const { publicKey: storedKey, isConnected: storedConnected } = JSON.parse(storedState); + if (storedConnected && storedKey) { + setPublicKey(storedKey); + setIsConnected(true); + } + } + } catch (error) { + console.error("Failed to restore wallet state:", error); + } + }, []); + + const connectWallet = useCallback(async () => { + if (isConnected) return; + + setIsConnecting(true); + try { + // Placeholder logic for connecting wallet. + // To be replaced with actual provider integration (e.g. Solana Wallet Adapter) in Task #3 + await new Promise((resolve) => setTimeout(resolve, 800)); // Simulate connection delay + + const mockPublicKey = "E9G8...ProfessionalPublicKey...2kP1"; // Mocked base58-like public key + setPublicKey(mockPublicKey); + setIsConnected(true); + + // Persist state + localStorage.setItem(WALLET_STORAGE_KEY, JSON.stringify({ + publicKey: mockPublicKey, + isConnected: true + })); + } catch (error) { + console.error("Wallet connection failed:", error); + } finally { + setIsConnecting(false); + } + }, [isConnected]); + + const disconnectWallet = useCallback(() => { + setPublicKey(null); + setIsConnected(false); + localStorage.removeItem(WALLET_STORAGE_KEY); + }, []); + + return ( + + {children} + + ); +} + +export function useWallet(): WalletContextType { + const context = useContext(WalletContext); + if (context === undefined) { + throw new Error("useWallet must be used within a WalletProvider"); + } + return context; +} diff --git a/dongle/package-lock.json b/dongle/package-lock.json index fd607db..c590dc4 100644 --- a/dongle/package-lock.json +++ b/dongle/package-lock.json @@ -3217,6 +3217,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", From bfe01f08e11c807a04804b966c93238525589bc5 Mon Sep 17 00:00:00 2001 From: Luluameh Date: Wed, 25 Feb 2026 13:53:20 +0100 Subject: [PATCH 2/2] feat: integrate Freighter wallet service with wallet context (#4) --- dongle/context/wallet.context.tsx | 43 +++++++++++++++++-------------- dongle/package-lock.json | 11 ++++++++ 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/dongle/context/wallet.context.tsx b/dongle/context/wallet.context.tsx index bfece7e..498b297 100644 --- a/dongle/context/wallet.context.tsx +++ b/dongle/context/wallet.context.tsx @@ -1,6 +1,7 @@ "use client"; import React, { createContext, useContext, useState, useEffect, ReactNode, useCallback } from "react"; +import { walletService } from "@/services/wallet/wallet.service"; interface WalletContextType { publicKey: string | null; @@ -19,20 +20,29 @@ export function WalletProvider({ children }: { children: ReactNode }) { const [isConnected, setIsConnected] = useState(false); const [isConnecting, setIsConnecting] = useState(false); - // Initialize from local storage to persist connection state across sessions useEffect(() => { - try { - const storedState = localStorage.getItem(WALLET_STORAGE_KEY); - if (storedState) { - const { publicKey: storedKey, isConnected: storedConnected } = JSON.parse(storedState); - if (storedConnected && storedKey) { - setPublicKey(storedKey); - setIsConnected(true); + const restoreWalletState = async () => { + try { + const storedState = localStorage.getItem(WALLET_STORAGE_KEY); + if (storedState) { + const { publicKey: storedKey, isConnected: storedConnected } = JSON.parse(storedState); + if (storedConnected && storedKey) { + const isStillConnected = await walletService.isConnected(); + if (isStillConnected) { + const currentKey = await walletService.getPublicKey(); + setPublicKey(currentKey); + setIsConnected(true); + } else { + localStorage.removeItem(WALLET_STORAGE_KEY); + } + } } + } catch (error) { + console.error("Failed to restore wallet state:", error); + localStorage.removeItem(WALLET_STORAGE_KEY); } - } catch (error) { - console.error("Failed to restore wallet state:", error); - } + }; + restoreWalletState(); }, []); const connectWallet = useCallback(async () => { @@ -40,17 +50,12 @@ export function WalletProvider({ children }: { children: ReactNode }) { setIsConnecting(true); try { - // Placeholder logic for connecting wallet. - // To be replaced with actual provider integration (e.g. Solana Wallet Adapter) in Task #3 - await new Promise((resolve) => setTimeout(resolve, 800)); // Simulate connection delay - - const mockPublicKey = "E9G8...ProfessionalPublicKey...2kP1"; // Mocked base58-like public key - setPublicKey(mockPublicKey); + const address = await walletService.connectWallet(); + setPublicKey(address); setIsConnected(true); - // Persist state localStorage.setItem(WALLET_STORAGE_KEY, JSON.stringify({ - publicKey: mockPublicKey, + publicKey: address, isConnected: true })); } catch (error) { diff --git a/dongle/package-lock.json b/dongle/package-lock.json index c95db11..bebbfd9 100644 --- a/dongle/package-lock.json +++ b/dongle/package-lock.json @@ -68,6 +68,7 @@ "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.28.6", "@babel/generator": "^7.28.6", @@ -1584,6 +1585,7 @@ "integrity": "sha512-3MbSL37jEchWZz2p2mjntRZtPt837ij10ApxKfgmXCTuHWagYg7iA5bqPw6C8BMPfwidlvfPI/fxOc42HLhcyg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1643,6 +1645,7 @@ "integrity": "sha512-npiaib8XzbjtzS2N4HlqPvlpxpmZ14FjSJrteZpPxGUaYPlvhzlzUZ4mZyABo0EFrOWnvyd0Xxroq//hKhtAWg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.53.0", "@typescript-eslint/types": "8.53.0", @@ -2142,6 +2145,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2502,6 +2506,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -3093,6 +3098,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -5471,6 +5477,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -5480,6 +5487,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -6168,6 +6176,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -6330,6 +6339,7 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6605,6 +6615,7 @@ "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" }