forked from github-starred/komodo
add basic socket toast and invalidate
This commit is contained in:
@@ -10,6 +10,7 @@ import { DeploymentName } from "@resources/deployment/util";
|
||||
import { DesktopUpdates } from "@components/updates/desktop";
|
||||
import { BuildName } from "@resources/build/util";
|
||||
import { Omnibar } from "./omnibar";
|
||||
import { WsStatusIndicator } from "@util/socket";
|
||||
|
||||
export const Paths = () => {
|
||||
const path = useLocation().pathname.split("/")[1];
|
||||
@@ -73,9 +74,7 @@ export const Header = () => {
|
||||
<div className="flex">
|
||||
{user && (
|
||||
<>
|
||||
<Button disabled variant="ghost">
|
||||
<Circle className="w-4 h-4 fill-green-500 stroke-none" />
|
||||
</Button>
|
||||
<WsStatusIndicator />
|
||||
<Omnibar />
|
||||
<DesktopUpdates />
|
||||
</>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
useMutation,
|
||||
UseQueryOptions,
|
||||
UseMutationOptions,
|
||||
useQueryClient,
|
||||
} from "@tanstack/react-query";
|
||||
import { useAtomValue, useSetAtom } from "jotai";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
WriteResponses,
|
||||
} from "@monitor/client/dist/responses";
|
||||
import { useEffect, useState } from "react";
|
||||
import { ReadRequest } from "@monitor/client/dist/types";
|
||||
|
||||
export const useRead = <
|
||||
T extends Types.ReadRequest["type"],
|
||||
@@ -61,6 +63,17 @@ export const useExecute = <
|
||||
config
|
||||
);
|
||||
|
||||
export const useInvalidate = () => {
|
||||
const qc = useQueryClient();
|
||||
|
||||
return <
|
||||
T extends ReadRequest["type"],
|
||||
P = Extract<Types.ReadRequest, { type: T }>["params"]
|
||||
>(
|
||||
...keys: Array<[T] | [T, P]>
|
||||
) => keys.forEach((k) => qc.invalidateQueries([...k]));
|
||||
};
|
||||
|
||||
export const useUser = () => useRead("GetUser", {});
|
||||
|
||||
export const useLogin = () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Header } from "@components/header";
|
||||
import { useUser } from "@hooks";
|
||||
import { Toaster } from "@ui/toast";
|
||||
import { WebsocketProvider } from "@util/socket";
|
||||
import { Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
export const Layout = () => {
|
||||
@@ -10,7 +11,7 @@ export const Layout = () => {
|
||||
if (isError && !path.includes("login")) nav("/login");
|
||||
|
||||
return (
|
||||
<>
|
||||
<WebsocketProvider>
|
||||
<div className="relative flex min-h-screen flex-col">
|
||||
<Header />
|
||||
<div className="container pt-12 pb-16">
|
||||
@@ -18,6 +19,6 @@ export const Layout = () => {
|
||||
</div>
|
||||
</div>
|
||||
<Toaster />
|
||||
</>
|
||||
</WebsocketProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -13,7 +13,7 @@ const ToastViewport = React.forwardRef<
|
||||
<ToastPrimitives.Viewport
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed top-0 sm:right-0 z-[100] gap-2 flex max-h-screen w-full flex-col-reverse p-4 sm:flex-col md:max-w-[420px]",
|
||||
"fixed bottom-0 sm:right-0 z-[100] gap-2 flex max-h-screen w-full flex-col-reverse p-4 sm:flex-col md:max-w-[420px]",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -1,24 +1,81 @@
|
||||
import { useInvalidate } from "@hooks";
|
||||
import { Types } from "@monitor/client";
|
||||
import { Button } from "@ui/button";
|
||||
import { toast } from "@ui/toast/use-toast";
|
||||
import { useState } from "react";
|
||||
import { atom, useAtom } from "jotai";
|
||||
import { Circle } from "lucide-react";
|
||||
import { ReactNode } from "react";
|
||||
import rws from "reconnecting-websocket";
|
||||
import { cn } from "./helpers";
|
||||
import { UPDATE_WS_URL } from "@main";
|
||||
|
||||
export const WebsocketProvider = () => {
|
||||
const ws = new rws("ws-url");
|
||||
const [_, set] = useState(false);
|
||||
const rws_atom = atom(new rws(UPDATE_WS_URL));
|
||||
const useWebsocket = () => useAtom(rws_atom);
|
||||
|
||||
export const WsStatusIndicator = () => {
|
||||
const [ws] = useWebsocket();
|
||||
const onclick = () =>
|
||||
toast({ title: "surprise", description: "motherfucker" });
|
||||
return (
|
||||
<Button variant="ghost" onClick={onclick}>
|
||||
<Circle
|
||||
className={cn(
|
||||
"w-4 h-4 stroke-none",
|
||||
!!ws ? "fill-green-500" : "fill-red-500"
|
||||
)}
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export const WebsocketProvider = ({ children }: { children: ReactNode }) => {
|
||||
const invalidate = useInvalidate();
|
||||
const [ws] = useWebsocket();
|
||||
|
||||
ws.addEventListener("open", () => {
|
||||
const token = localStorage.getItem("token");
|
||||
const token = localStorage.getItem("monitor-auth-token");
|
||||
if (token) ws.send(token);
|
||||
set(true);
|
||||
});
|
||||
|
||||
ws.addEventListener("message", ({ data }) => {
|
||||
if (data == "LOGGED_IN") return console.log("logged in to ws");
|
||||
const update = JSON.parse(data) as Types.Update;
|
||||
|
||||
toast({
|
||||
title: `${update.target} ${update.operation}`,
|
||||
description: update.operator,
|
||||
});
|
||||
|
||||
invalidate(["ListUpdates"]);
|
||||
|
||||
if (update.target.type === "Deployment") {
|
||||
invalidate(
|
||||
["ListDeployments"],
|
||||
["GetDeployment", { id: update.target.id }],
|
||||
["GetLog", { id: update.target.id }],
|
||||
["GetDeploymentActionState", { id: update.target.id }],
|
||||
["GetDeploymentStatus", { id: update.target.id }]
|
||||
);
|
||||
}
|
||||
|
||||
if (update.target.type === "Server") {
|
||||
invalidate(
|
||||
["ListServers"],
|
||||
["GetServer", { id: update.target.id }],
|
||||
["GetServerActionState", { id: update.target.id }],
|
||||
["GetServerStatus", { id: update.target.id }],
|
||||
["GetHistoricalServerStats", { id: update.target.id }]
|
||||
);
|
||||
}
|
||||
|
||||
if (update.target.type === "Build") {
|
||||
invalidate(
|
||||
["ListBuilds"],
|
||||
["GetBuild", { id: update.target.id }],
|
||||
["GetBuildActionState", { id: update.target.id }]
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return <>{children}</>;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user