Skip to content

Commit

Permalink
feat: add toasts and new app error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
rolznz committed Dec 27, 2023
1 parent c803e63 commit 1935b0e
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 6 deletions.
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hot-toast": "^2.4.1",
"react-qr-code": "^2.0.12",
"react-router-dom": "^6.21.0",
"swr": "^2.2.4"
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ import Navbar from "./components/Navbar";
import Footer from "./components/Footer";
import NewApp from "./screens/apps/NewApp";
import AppCreated from "./screens/apps/AppCreated";
import Toaster from "./components/Toast/Toaster";

function App() {
return (
<div className="dark:bg-black min-h-full">
<Toaster />
<BrowserRouter>
<Routes>
<Route path="/" element={<Navbar />}>
Expand Down
12 changes: 12 additions & 0 deletions frontend/src/components/Toast/Toaster.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Toaster as HotToaster } from "react-hot-toast";

export default function Toaster() {
return (
<HotToaster
position="bottom-center"
toastOptions={{
duration: 4_000,
}}
/>
);
}
45 changes: 45 additions & 0 deletions frontend/src/components/Toast/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { ReactNode } from "react";
import {
CheckmarkIcon,
ErrorIcon,
ToastOptions,
toast as hotToast,
} from "react-hot-toast";

type ToastType = "error" | "success";

interface ToastMethods {
success: (message: string | ReactNode, options?: ToastOptions) => void;
error: (message: string | ReactNode, options?: ToastOptions) => void;
custom: (
children: ReactNode,
type: ToastType,
options?: ToastOptions
) => void;
}

const toast: ToastMethods = {
success: (message: string | ReactNode, options?: ToastOptions) => {
toast.custom(message, "success", options);
},
error: (message: string | ReactNode, options?: ToastOptions) => {
toast.custom(message, "error", { duration: options?.duration ?? 8_000 });
},
custom: (children: ReactNode, type: ToastType, options?: ToastOptions) => {
hotToast.custom(
(t: { visible: boolean; id: string }) =>
t.visible ? (
<div className="bg-white dark:bg-surface-02dp px-4 py-3 drop-shadow-lg rounded-lg overflow-hidden flex flex-row items-center gap-3 text-gray-800 dark:text-neutral-200">
<div className="shrink-0">
{type == "success" && <CheckmarkIcon />}
{type == "error" && <ErrorIcon />}
</div>
<div>{children}</div>
</div>
) : null,
options
);
},
};

export default toast;
10 changes: 6 additions & 4 deletions frontend/src/screens/apps/NewApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {
nip47MethodIcons,
validBudgetRenewals,
} from "../../types";
import toast from "react-hot-toast";
import { handleFetchError, validateFetchResponse } from "../../utils/fetch";

const NewApp = () => {
const { data: info } = useInfo();
Expand Down Expand Up @@ -123,15 +125,15 @@ const NewApp = () => {
returnTo: returnTo,
}),*/
});
console.log(response);
await validateFetchResponse(response);

const createAppResponse: CreateAppResponse = await response.json();
navigate("/apps/created", {
state: createAppResponse,
});
toast.success("App created!");
} catch (error) {
// TODO: Deal with invalid pubkey format error
// Invalid expiresAt error
console.error("Error deleting app:", error);
handleFetchError("Failed to create app", error);
}
};

Expand Down
8 changes: 6 additions & 2 deletions frontend/src/screens/apps/ShowApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import Loading from "../../components/Loading";
import { useNavigate, useParams } from "react-router-dom";
import { useInfo } from "../../hooks/useInfo";
import { useApp } from "../../hooks/useApp";
import { handleFetchError, validateFetchResponse } from "../../utils/fetch";
import toast from "../../components/Toast";

function ShowApp() {
const { data: info } = useInfo();
Expand All @@ -15,16 +17,18 @@ function ShowApp() {
const handleDelete = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
try {
await fetch(`/api/apps/${app.nostrPubkey}`, {
const response = await fetch(`/api/apps/${app.nostrPubkey}`, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
"X-CSRF-Token": info.csrf,
},
});
await validateFetchResponse(response);
navigate("/apps");
toast.success("App disconnected");
} catch (error) {
console.error("Error deleting app:", error);
await handleFetchError("Failed to delete app", error);
}
};

Expand Down
18 changes: 18 additions & 0 deletions frontend/src/utils/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import toast from "../components/Toast";

export async function validateFetchResponse(response: Response) {
if (!response.ok) {
let reason = "unknown";
try {
reason = await response.text();
} catch (error) {
console.error("Failed to read response text", error);
}
throw new Error("Unexpected response: " + response.status + " " + reason);
}
}

export function handleFetchError(message: string, error: unknown) {
console.error(message, error);
toast.error(message + ": " + error);
}
12 changes: 12 additions & 0 deletions frontend/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,11 @@ globby@^11.1.0:
merge2 "^1.4.1"
slash "^3.0.0"

goober@^2.1.10:
version "2.1.13"
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.13.tgz#e3c06d5578486212a76c9eba860cbc3232ff6d7c"
integrity sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==

graphemer@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
Expand Down Expand Up @@ -1419,6 +1424,13 @@ react-dom@^18.2.0:
loose-envify "^1.1.0"
scheduler "^0.23.0"

react-hot-toast@^2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/react-hot-toast/-/react-hot-toast-2.4.1.tgz#df04295eda8a7b12c4f968e54a61c8d36f4c0994"
integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==
dependencies:
goober "^2.1.10"

react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
Expand Down

0 comments on commit 1935b0e

Please sign in to comment.