feat: showing limits error on frontend

This commit is contained in:
2025-08-30 11:15:17 +01:00
parent e45688d57e
commit 8b54d502f2
4 changed files with 123 additions and 51 deletions

View File

@ -15,6 +15,8 @@ import { ProtectedRoute } from "@components/protected-route";
import { AppWrapper } from "@components/app-wrapper"; import { AppWrapper } from "@components/app-wrapper";
import { WithTopbarAndDock } from "@components/app-wrapper/with-topbar-and-dock"; import { WithTopbarAndDock } from "@components/app-wrapper/with-topbar-and-dock";
import { onSendImage } from "@contexts/send-image"; import { onSendImage } from "@contexts/send-image";
import { Toast } from "@kobalte/core/toast";
import { Portal } from "solid-js/web";
export const App = () => { export const App = () => {
onAndroidMount(); onAndroidMount();
@ -31,8 +33,14 @@ export const App = () => {
<Route path="/" component={WithTopbarAndDock}> <Route path="/" component={WithTopbarAndDock}>
<Route path="/" component={FrontPage} /> <Route path="/" component={FrontPage} />
<Route path="/search" component={SearchPage} /> <Route path="/search" component={SearchPage} />
<Route path="/all-images" component={AllImages} /> <Route
<Route path="/image/:imageId" component={ImagePage} /> path="/all-images"
component={AllImages}
/>
<Route
path="/image/:imageId"
component={ImagePage}
/>
<Route path="/list/:listId" component={List} /> <Route path="/list/:listId" component={List} />
<Route path="/settings" component={Settings} /> <Route path="/settings" component={Settings} />
</Route> </Route>
@ -41,6 +49,12 @@ export const App = () => {
</Route> </Route>
<Route path="*" component={() => <Navigate href="/" />} /> <Route path="*" component={() => <Navigate href="/" />} />
</Router> </Router>
<Portal>
<Toast.Region class="fixed w-72 top-4 right-4 z-50 flex flex-col space-y-2">
<Toast.List />
</Toast.Region>
</Portal>
</SearchImageContextProvider> </SearchImageContextProvider>
); );
}; };

View File

@ -1,7 +1,8 @@
import { createEffect } from "solid-js"; import { createEffect } from "solid-js";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { getCurrentWindow } from "@tauri-apps/api/window"; import { getCurrentWindow } from "@tauri-apps/api/window";
import { sendImage } from "@network/index"; import { ImageLimitReached, sendImage } from "@network/index";
import { createToast } from "../utils/show-toast";
export const onSendImage = () => { export const onSendImage = () => {
let sentImage = ""; let sentImage = "";
@ -22,7 +23,16 @@ export const onSendImage = () => {
appWindow.show(); appWindow.show();
appWindow.setFocus(); appWindow.setFocus();
try {
await sendImage("test-image.png", base64Data); await sendImage("test-image.png", base64Data);
} catch (e) {
if (e instanceof ImageLimitReached) {
createToast("Limits reached!", "You've reached your image limit")
console.log("Reached image limits!");
} else {
throw e
}
}
}); });
return () => { return () => {

View File

@ -68,6 +68,12 @@ export const sendImageFile = async (
return parse(sendImageResponseValidator, res); return parse(sendImageResponseValidator, res);
}; };
export class ImageLimitReached extends Error {
constructor() {
super();
}
}
export const sendImage = async ( export const sendImage = async (
imageName: string, imageName: string,
base64Image: string, base64Image: string,
@ -80,7 +86,12 @@ export const sendImage = async (
request.headers.set("Content-Type", "application/base64"); request.headers.set("Content-Type", "application/base64");
const res = await fetch(request).then((res) => res.json()); const rawRes = await fetch(request);
if (!rawRes.ok && rawRes.status == 429) {
throw new ImageLimitReached()
}
const res = await rawRes.json();
return parse(sendImageResponseValidator, res); return parse(sendImageResponseValidator, res);
}; };

View File

@ -0,0 +1,37 @@
import { Toast, toaster } from "@kobalte/core/toast";
import { IconCircleDashedX } from "@tabler/icons-solidjs";
export const createToast = (title: string, text: string) => {
console.log("creating toast")
toaster.show((props) => (
<Toast
toastId={props.toastId}
class="max-w-lg w-full bg-white shadow-lg rounded-lg pointer-events-auto flex ring-1 ring-black ring-opacity-5"
>
<div class="flex-1 w-0 p-4">
<div class="flex items-start">
<div class="flex-shrink-0 pt-0.5">
<IconCircleDashedX class="h-6 w-6 text-red-600" />
</div>
<div class="ml-3 flex-1">
<Toast.Title class="text-sm font-medium text-gray-900">
{title}
</Toast.Title>
<Toast.Description class="mt-1 text-sm text-gray-500">
{text}
</Toast.Description>
</div>
</div>
</div>
<div class="flex border-l border-gray-200">
<Toast.CloseButton class="w-full border border-transparent rounded-none rounded-r-lg p-4 flex items-center justify-center text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500">
<span class="sr-only">Close</span>
<IconCircleDashedX class="h-5 w-5" aria-hidden="true" />
</Toast.CloseButton>
</div>
<Toast.ProgressTrack class="absolute bottom-0 left-0 right-0 h-1 bg-gray-200 rounded-b-lg overflow-hidden">
<Toast.ProgressFill class="h-full bg-indigo-600 transition-all duration-300" />
</Toast.ProgressTrack>
</Toast>
));
};