Skip to content

Commit

Permalink
Merge pull request #384 from getAlby/upgrade/react-router-v6
Browse files Browse the repository at this point in the history
React Router v6 + Auth Flow
  • Loading branch information
dylancom authored Nov 23, 2021
2 parents e1b29c1 + fca7b4b commit e19ae55
Show file tree
Hide file tree
Showing 29 changed files with 480 additions and 495 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"react-modal": "^3.14.4",
"react-palette": "^1.0.2",
"react-qr-code": "^2.0.2",
"react-router-dom": "^5.3.0",
"react-router-dom": "^6.0.2",
"sha.js": "^2.4.11",
"stream": "^0.0.2",
"webext-base-css": "^1.3.2",
Expand Down
6 changes: 3 additions & 3 deletions src/app/components/AccountMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import { useHistory } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import WalletIcon from "@bitcoin-design/bitcoin-icons/svg/outline/wallet.svg";
import { ChevronDownIcon, PlusIcon } from "@heroicons/react/solid";

Expand All @@ -13,7 +13,7 @@ type Props = {
};

function AccountMenu({ onAccountSwitch }: Props) {
const history = useHistory();
const navigate = useNavigate();
const [accounts, setAccounts] = useState<{
[key: string]: { name: "string"; connector: "string" };
}>({});
Expand All @@ -38,7 +38,7 @@ function AccountMenu({ onAccountSwitch }: Props) {
// close the popup
window.close();
} else {
history.push(`/${path}`);
navigate(`/${path}`);
}
}

Expand Down
12 changes: 7 additions & 5 deletions src/app/components/Navbar/NavbarLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import { NavLink } from "react-router-dom";

type Props = {
children: React.ReactNode;
exact?: boolean;
end?: boolean;
href: string;
};

function NavbarLink({ children, exact = false, href }: Props) {
function NavbarLink({ children, end = false, href }: Props) {
return (
<NavLink
exact={exact}
end={end}
to={href}
className="block text-gray-500 hover:text-gray-700 px-1 font-semibold transition-colors duration-200"
activeClassName="text-orange-bitcoin hover:text-orange-bitcoin"
className={({ isActive }) =>
"block text-gray-500 hover:text-gray-700 px-1 font-semibold transition-colors duration-200" +
(isActive ? " text-orange-bitcoin hover:text-orange-bitcoin" : "")
}
>
{children}
</NavLink>
Expand Down
17 changes: 10 additions & 7 deletions src/app/components/UserMenu/index.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import React from "react";
import { useHistory } from "react-router-dom";
import { useNavigate } from "react-router-dom";
import { CogIcon, LockClosedIcon, TableIcon } from "@heroicons/react/solid";
import { MenuIcon } from "@heroicons/react/outline";
import SendIcon from "@bitcoin-design/bitcoin-icons/svg/filled/send.svg";
import ReceiveIcon from "@bitcoin-design/bitcoin-icons/svg/filled/receive.svg";

import utils from "../../../common/lib/utils";
import { useAuth } from "../../context/AuthContext";
import Menu from "../Menu";

export default function UserMenu() {
const history = useHistory();
const navigate = useNavigate();
const auth = useAuth();

function openOptions(path: string) {
// if we are in the popup
Expand All @@ -18,14 +20,15 @@ export default function UserMenu() {
// close the popup
window.close();
} else {
history.push(`/${path}`);
navigate(`/${path}`);
}
}

async function lock() {
try {
await utils.call("lock");
window.close();
auth.lock(() => {
window.close();
});
} catch (e) {
console.error(e);
}
Expand All @@ -50,7 +53,7 @@ export default function UserMenu() {
</Menu.ItemButton>
<Menu.ItemButton
onClick={() => {
history.push("/send");
navigate("/send");
}}
>
<img
Expand All @@ -63,7 +66,7 @@ export default function UserMenu() {
</Menu.ItemButton>
<Menu.ItemButton
onClick={() => {
history.push("/receive");
navigate("/receive");
}}
>
<img
Expand Down
59 changes: 59 additions & 0 deletions src/app/context/AuthContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React, { useState, useEffect, createContext } from "react";
import utils from "../../common/lib/utils";

interface AuthContextType {
account: string | null;
loading: boolean;
unlock: (user: string, callback: VoidFunction) => void;
lock: (callback: VoidFunction) => void;
}

const AuthContext = createContext<AuthContextType>(null!);

export function AuthProvider({ children }: { children: React.ReactNode }) {
const [account, setAccount] = useState<string | null>(null);
const [loading, setLoading] = useState(true);

const unlock = (password: string, callback: VoidFunction) => {
return utils.call("unlock", { password }).then((response) => {
setAccount(response.currentAccountId);
callback();
});
};

const lock = (callback: VoidFunction) => {
return utils.call("lock").then(() => {
setAccount(null);
callback();
});
};

useEffect(() => {
utils
.call("status")
.then((response) => {
if (!response.configured) {
utils.openPage("welcome.html");
window.close();
} else if (response.unlocked) {
setAccount(response.currentAccountId);
} else {
setAccount(null);
}
})
.catch((e) => {
alert(`An unexpected error occurred (${e.message})`);
})
.finally(() => {
setLoading(false);
});
}, []);

const value = { account, loading, unlock, lock };

return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}

export function useAuth() {
return React.useContext(AuthContext);
}
114 changes: 60 additions & 54 deletions src/app/router/Options/Options.jsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,101 @@
import React, { useState, useEffect } from "react";
import { HashRouter, Switch, Redirect, Route } from "react-router-dom";
import { HashRouter, Navigate, Outlet, Routes, Route } from "react-router-dom";

import utils from "../../../common/lib/utils";
import { AuthProvider } from "../../context/AuthContext";
import RequireAuth from "../RequireAuth";
import Container from "../../components/Container";
import Navbar from "../../components/Navbar";
import UserMenu from "../../components/UserMenu";
import Publishers from "../../screens/Publishers";
import Publisher from "../../screens/Publisher";
import ChooseConnector from "../../screens/Options/ChooseConnector";
import TestConnection from "../../screens/Options/TestConnection";
import Send from "../../screens/Send";
import Receive from "../../screens/Receive";
import Settings from "../../screens/Settings";
import Unlock from "../../screens/Unlock";

function Options() {
return (
<AuthProvider>
<HashRouter>
<Routes>
<Route
path="/"
element={
<RequireAuth>
<Layout />
</RequireAuth>
}
>
<Route index element={<Navigate to="/publishers" replace />} />
<Route path="publishers">
<Route path=":id" element={<Publisher />} />
<Route index element={<Publishers />} />
</Route>
<Route path="send" element={<Send />} />
<Route path="receive" element={<Receive />} />
<Route path="settings" element={<Settings />} />
<Route
path="accounts/new/*"
element={
<Container>
<ChooseConnector />
</Container>
}
/>
<Route
path="test-connection"
element={
<Container>
<TestConnection />
</Container>
}
/>
</Route>
<Route path="unlock" element={<Unlock />} />
</Routes>
</HashRouter>
</AuthProvider>
);
}

const Layout = () => {
const [accountInfo, setAccountInfo] = useState({});

function loadAccountInfo() {
useEffect(() => {
getAccountInfo();
}, []);

function getAccountInfo() {
setAccountInfo({});
return utils.call("accountInfo").then((response) => {
utils.call("accountInfo").then((response) => {
const { alias } = response.info;
const balance = parseInt(response.balance.balance); // TODO: handle amounts
setAccountInfo({ alias, balance });
});
}

useEffect(() => {
utils
.call("status")
.then((response) => {
if (!response.configured) {
utils.openPage("welcome.html");
window.close();
}
})
.catch((e) => {
console.log(e);
});
loadAccountInfo();
}, []);

return (
<HashRouter>
<div>
<Navbar
title={accountInfo.alias}
subtitle={
typeof accountInfo.balance === "number"
? `${accountInfo.balance} sat`
: ""
}
onAccountSwitch={loadAccountInfo}
onAccountSwitch={getAccountInfo}
>
<Navbar.Link href="/publishers">Websites</Navbar.Link>
<Navbar.Link href="/send">Send</Navbar.Link>
<Navbar.Link href="/receive">Receive</Navbar.Link>
<Navbar.Link href="/settings">Settings</Navbar.Link>
</Navbar>

<Switch>
<Redirect from="/home" to="/publishers" />
<Route exact path="/">
<Redirect to="/publishers" />
</Route>
<Route exact path="/publishers">
<Publishers />
</Route>
<Route path="/publishers/:id">
<Publisher />
</Route>
<Route path="/send">
<Send />
</Route>
<Route path="/receive">
<Receive />
</Route>
<Route path="/settings">
<Settings />
</Route>
<Route path="/accounts/new">
<Container>
<ChooseConnector />
</Container>
</Route>
<Route path="/test-connection">
<Container>
<TestConnection />
</Container>
</Route>
</Switch>
</HashRouter>
<Outlet />
</div>
);
}
};

export default Options;
Loading

0 comments on commit e19ae55

Please sign in to comment.