Skip to content

Commit

Permalink
feat: client holdings.
Browse files Browse the repository at this point in the history
  • Loading branch information
jdrskr committed Dec 4, 2023
1 parent d91e277 commit aa3579f
Show file tree
Hide file tree
Showing 10 changed files with 436 additions and 172 deletions.
3 changes: 2 additions & 1 deletion backend/src/api/auth/strategies/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
async validate(payload: any) {
return {
userId: payload.userId,
isAdmin: payload.isAdmin
username: payload.username,
isAdmin: payload.isAdmin,
};
}
}
32 changes: 19 additions & 13 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { ForbiddenPage } from "./pages/errors/ForbiddenPage";
import { ClientPages } from "./pages/client/ClientPages";
import { NavigationBar } from "./components/NavigationBar";
import { DashboardLayout } from "./components/Layout";
import { Web3ContextProvider } from "./context/Web3Context";

function App() {
return (
Expand All @@ -32,21 +33,26 @@ function App() {
}}
>
<AuthContextProvider>
<ApiContextProvider>
<NavigationBar></NavigationBar>
<Routes>
<Route element={<DashboardLayout></DashboardLayout>}>
{AdminPages}
{ClientPages}
{AccountPages}
<Web3ContextProvider>
<ApiContextProvider>
<NavigationBar></NavigationBar>
<Routes>
<Route element={<DashboardLayout></DashboardLayout>}>
{AdminPages}
{ClientPages}
{AccountPages}

<Route path="errors">
<Route path="unauthorized" element={<ForbiddenPage />} />
<Route path="errors">
<Route path="unauthorized" element={<ForbiddenPage />} />
</Route>
<Route
path="*"
element={<Navigate to="/accounts/login" />}
/>
</Route>
<Route path="*" element={<Navigate to="/accounts/login" />} />
</Route>
</Routes>
</ApiContextProvider>
</Routes>
</ApiContextProvider>
</Web3ContextProvider>
</AuthContextProvider>
</SnackbarProvider>
</Router>
Expand Down
30 changes: 20 additions & 10 deletions frontend/src/components/NavigationBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,29 +72,39 @@ export const NavigationBar: React.FC = ({ ...props }) => {
</Box>
)}
{!authContext.isAdmin && (
<Box
sx={{
cursor: "pointer",
flexGrow: 1,
}}
onClick={() => navigate("/client/issue")}
>
Place order
</Box>
<React.Fragment>
<Box
sx={{
cursor: "pointer",
}}
onClick={() => navigate("/client/placeOrder")}
>
Place order
</Box>
<Box
sx={{
cursor: "pointer",
}}
onClick={() => navigate("/client/holdings")}
>
Holdings
</Box>
</React.Fragment>
)}
<Box
sx={{
display: "flex",
alignItems: "flex-end",
flexDirection: "column",
flexGrow: 1,
}}
>
<Typography
sx={{
fontSize: 14,
}}
>
{authContext.userId}
{authContext.username}
</Typography>
</Box>

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/constants/Assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ export const ASSETS = {
name: 'Notas do Tesouro Nacional ',
address: process.env.REACT_APP_TOKEN_NTN_ADDRESS
}
}
}

export const SUPPORTED_ASSETS = Object.keys(ASSETS);
10 changes: 9 additions & 1 deletion frontend/src/context/AuthContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface AuthContextPayloadType {
isAuthenticated: boolean;

userId: string;
username: string;
isAdmin: boolean;
}

Expand Down Expand Up @@ -48,7 +49,11 @@ const isTokenValid = () => {
return isFuture(new Date((decodedToken.exp || 0) * 1000));
};

const decodeToken = (): { userId?: string; isAdmin?: boolean } & JWTPayload => {
const decodeToken = (): {
userId?: string;
username?: string;
isAdmin?: boolean;
} & JWTPayload => {
const token = localStorage.getItem(LocalStorage.AuthToken);

if (!token) {
Expand All @@ -64,6 +69,7 @@ const defaultContextData: AuthContextPayloadType = {
isAuthenticated: isTokenValid(),
isAdmin: decodeToken().isAdmin || false,
userId: decodeToken().userId || "",
username: decodeToken().username || "",
};

const AuthContext = React.createContext<AuthContextType>(
Expand Down Expand Up @@ -93,12 +99,14 @@ export const AuthContextProvider: React.FC<React.PropsWithChildren<any>> = ({
...prevState,
isAuthenticated: true,
isAdmin: decoded.isAdmin!,
username: decoded.username!,
userId: decoded.userId!,
}));

return {
isAdmin: decoded.isAdmin!,
userId: decoded.userId!,
username: decoded.username!,
isAuthenticated: true,
};
};
Expand Down
137 changes: 137 additions & 0 deletions frontend/src/context/Web3Context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React from "react";
import { ethers } from "ethers";
import { useSnackbar } from "notistack";

// ---- User state types ---- //

interface Web3ContextValues {
connected: boolean;
provider?: ethers.providers.Web3Provider;
account?: string;
chainId?: number;
signer?: ethers.Signer;
}

interface Web3ContextFns {
connectWallet: () => Promise<any>;
}

const Web3Context = React.createContext<Web3ContextValues & Web3ContextFns>(
{} as any
);

export const Web3ContextProvider: React.FC<React.PropsWithChildren<any>> = ({
children,
}) => {
const snackbar = useSnackbar();

const defaultWeb3ContextData = {
connected: false,
};

const [web3ContextState, setWeb3ContextState] =
React.useState<Web3ContextValues>(defaultWeb3ContextData);
const connectWallet = async () => {
if (web3ContextState.provider) {
try {
await web3ContextState.provider.send("eth_requestAccounts", []);
const signer = web3ContextState.provider.getSigner();
const account = await signer.getAddress();
const network = await web3ContextState.provider.getNetwork();

snackbar.enqueueSnackbar("Successfully connected account.", {
variant: "success",
});

setWeb3ContextState({
...web3ContextState,
signer: signer,
account: account,
chainId: network.chainId,
connected: true,
});
} catch (e) {
snackbar.enqueueSnackbar("Failed to connect account.", {
variant: "error",
});
}
}
};
// endregion

// region Effects

React.useEffect(() => {
if ((window as any).ethereum) {
const provider = new ethers.providers.Web3Provider(
(window as any).ethereum,
"any"
);

const setAccountChanged = (accounts: any[]) => {
snackbar.enqueueSnackbar("Account change detected.", {
variant: "info",
});
if (accounts.length > 0) {
setWeb3ContextState((prev) => ({
...prev,
account: accounts[0],
}));
} else {
setWeb3ContextState((prev) => ({
...prev,
connected: false,
account: undefined,
}));
}
};
(window as any).ethereum.on("accountsChanged", setAccountChanged);

const setChainChanged = (chainId: string) => {
snackbar.enqueueSnackbar("Network change detected.", {
variant: "info",
});

setWeb3ContextState((prev) => ({
...prev,
chainId: parseInt(chainId),
}));
};
(window as any).ethereum.on("chainChanged", setChainChanged);

setWeb3ContextState({
...web3ContextState,
provider: provider,
});

return () => {
(window as any).ethereum?.removeListener(
"accountsChanged",
setAccountChanged
);
(window as any).ethereum?.removeListener(
"chainChanged",
setChainChanged
);
};
}

return;
}, []);

// Update the context on graphql data changes

// endregion

return (
<Web3Context.Provider
value={{
...web3ContextState,
connectWallet,
}}
children={children}
/>
);
};

export const useWeb3Context = () => React.useContext(Web3Context);
Loading

0 comments on commit aa3579f

Please sign in to comment.