topbar ws indicator and userPermissionsOnEntity

This commit is contained in:
mbecker20
2022-12-28 06:08:09 +00:00
parent de8c59bb61
commit 15662c951d
6 changed files with 130 additions and 48 deletions

View File

@@ -1,5 +1,5 @@
import { Component, JSX } from "solid-js";
import { combineClasses } from "../../../util/helpers";
import { combineClasses, filterOutFromObj } from "../../../util/helpers";
import s from "./Layout.module.css";
const Flex: Component<
@@ -28,6 +28,14 @@ const Flex: Component<
> = (p) => {
return (
<div
{...filterOutFromObj(p, [
"gap",
"alignItems",
"justifyContent",
"placeItems",
"style",
"class",
])}
class={combineClasses(s.Flex, p.class)}
style={{
gap: p.gap,
@@ -36,7 +44,6 @@ const Flex: Component<
"place-items": p.placeItems,
...(p.style as any),
}}
{...p}
>
{p.children}
</div>

View File

@@ -1,22 +1,24 @@
import { Component, JSX } from "solid-js";
import { combineClasses } from "../../../util/helpers";
import { combineClasses, filterOutFromObj } from "../../../util/helpers";
import s from "./Layout.module.css";
const Grid: Component<{
gap?: string | number;
placeItems?: string;
style?: JSX.CSSProperties;
class?: string
} & JSX.HTMLAttributes<HTMLDivElement>> = (p) => {
const Grid: Component<
{
gap?: string | number;
placeItems?: string;
style?: JSX.CSSProperties;
class?: string;
} & JSX.HTMLAttributes<HTMLDivElement>
> = (p) => {
return (
<div
{...filterOutFromObj(p, ["gap", "placeItems", "style", "class"])}
class={combineClasses(s.Grid, p.class)}
style={{
gap: p.gap,
"place-items": p.placeItems,
...(p.style as any),
}}
{...p}
>
{p.children}
</div>

View File

@@ -1,6 +1,8 @@
import { useNavigate } from "@solidjs/router";
import { Component, createSignal, JSX, Show } from "solid-js";
import { TOPBAR_HEIGHT } from "../..";
import { useAppDimensions } from "../../state/DimensionProvider";
import { useAppState } from "../../state/StateProvider";
import { useUser } from "../../state/UserProvider";
import { combineClasses, inPx } from "../../util/helpers";
import Circle from "../shared/Circle";
@@ -37,17 +39,17 @@ const Topbar: Component = () => {
};
const LeftSide: Component = () => {
const { ws } = useAppState();
const navigate = useNavigate();
return (
<Flex
alignItems="center"
style={{ padding: "0rem 0.5rem", "place-self": "center start" }}
>
<button class="grey" onClick={() => {
// selected.set("", "home");
}}>
<button class="grey" onClick={() => navigate("/")}>
<Icon type="home" width="1.15rem" />
</button>
{/* <HoverMenu
<HoverMenu
target={
<Circle
size={1}
@@ -57,7 +59,7 @@ const LeftSide: Component = () => {
}
content={ws.isOpen() ? "connected" : "disconnected"}
position="right center"
/> */}
/>
</Flex>
);
};
@@ -73,24 +75,6 @@ const RightSide: Component = () => {
alignItems="center"
style={{ padding: "0rem 0.5rem", "place-self": "center end" }}
>
{/* <Menu
show={menu() === "updates"}
close={close}
menuStyle={isMobile() ? mobileStyle : undefined}
target={
<Button
class="grey"
onClick={() =>
menu() === "updates" ? setMenu(undefined) : setMenu("updates")
}
>
<Icon type="notifications" alt="updates" width="1.15rem" />
</Button>
}
content={<Updates />}
position="bottom right"
backgroundColor={isMobile() ? "rgba(0,0,0,0.6)" : undefined}
/> */}
<Menu
show={menu() === "account"}
close={close}

View File

@@ -10,12 +10,16 @@ import {
} from "./hooks";
import connectToWs from "./ws";
import { useUser } from "./UserProvider";
import { PermissionLevel } from "../types";
export type State = {
servers: ReturnType<typeof useServers>;
getPermissionOnServer: (id: string) => PermissionLevel;
serverStats: ReturnType<typeof useServerStats>;
builds: ReturnType<typeof useBuilds>;
getPermissionOnBuild: (id: string) => PermissionLevel;
deployments: ReturnType<typeof useDeployments>;
getPermissionOnDeployment: (id: string) => PermissionLevel;
updates: ReturnType<typeof useUpdates>;
};
@@ -27,13 +31,50 @@ const context = createContext<
>();
export const AppStateProvider: ParentComponent = (p) => {
const { logout } = useUser();
const { user, logout } = useUser();
const navigate = useNavigate();
const userId = (user()._id as any).$oid as string;
const servers = useServers();
const builds = useBuilds();
const deployments = useDeployments();
const state: State = {
servers: useServers(),
servers,
getPermissionOnServer: (id: string) => {
const server = servers.get(id)!;
const permissions = server.server.permissions![userId] as
| PermissionLevel
| undefined;
if (permissions) {
return permissions;
} else {
return PermissionLevel.None;
}
},
builds,
getPermissionOnBuild: (id: string) => {
const build = builds.get(id)!;
const permissions = build.permissions![userId] as
| PermissionLevel
| undefined;
if (permissions) {
return permissions;
} else {
return PermissionLevel.None;
}
},
deployments,
getPermissionOnDeployment: (id: string) => {
const deployment = deployments.get(id)!;
const permissions = deployment.deployment.permissions![userId] as
| PermissionLevel
| undefined;
if (permissions) {
return permissions;
} else {
return PermissionLevel.None;
}
},
serverStats: useServerStats(),
builds: useBuilds(),
deployments: useDeployments(),
updates: useUpdates(),
};

View File

@@ -69,7 +69,7 @@ export function useDeployments() {
}
export function useUpdates(target?: UpdateTarget) {
const updates = useArray(() => client.list_updates(0, target));
const updates = useArrayWithId(() => client.list_updates(0, target), ["_id", "$oid"]);
const [noMore, setNoMore] = createSignal(false);
const loadMore = async () => {
const offset = updates.collection()?.length;
@@ -94,10 +94,10 @@ export function useArray<T>(query: () => Promise<T[]>) {
query().then(set);
});
const add = (item: T) => {
set((items: any) => (items ? [item, ...items] : [item]));
set((items: T[] | undefined) => (items ? [item, ...items] : [item]));
};
const addManyToEnd = (items: T[]) => {
set((curr: any) => (curr ? [...curr, ...items] : items));
set((curr: T[] | undefined) => (curr ? [...curr, ...items] : items));
};
const loaded = () => (collection() ? true : false);
return {
@@ -108,6 +108,44 @@ export function useArray<T>(query: () => Promise<T[]>) {
};
}
export function useArrayWithId<T>(query: () => Promise<T[]>, idPath: string[]) {
const [collection, set] = createSignal<T[]>();
createEffect(() => {
query().then(set);
});
const addOrUpdate = (item: T) => {
set((items: T[] | undefined) => {
if (items) {
const newId = getNestedEntry(item, idPath);
const existingIndex = items.findIndex(i => getNestedEntry(i, idPath) === newId);
if (existingIndex < 0) {
return [item, ...items]
} else {
return items.map((e, index) => {
if (index === existingIndex) {
return item
} else {
return e
}
});
}
} else {
return [item];
}
});
};
const addManyToEnd = (items: T[]) => {
set((curr: T[] | undefined) => (curr ? [...curr, ...items] : items));
};
const loaded = () => (collection() ? true : false);
return {
collection,
addOrUpdate,
addManyToEnd,
loaded,
};
}
export function useCollection<T>(query: () => Promise<Collection<T>>, idPath: string[]) {
const [collection, { mutate }] = createResource(query);
const add = (item: T) => {

View File

@@ -2,7 +2,7 @@ import { client, pushNotification, WS_URL } from "..";
import { State } from "./StateProvider";
import { createSignal } from "solid-js";
import ReconnectingWebSocket from "reconnecting-websocket";
import { Operation, Update, UpdateStatus } from "../types";
import { Operation, Update, UpdateStatus, UpdateTarget } from "../types";
function connectToWs(state: State) {
const ws = new ReconnectingWebSocket(WS_URL);
@@ -17,7 +17,7 @@ function connectToWs(state: State) {
ws.addEventListener("message", ({ data }) => {
if (data === "LOGGED_IN") {
// console.log("logged in to ws");
console.log("logged in to ws");
return;
}
const update = JSON.parse(data) as Update;
@@ -40,10 +40,7 @@ function connectToWs(state: State) {
});
return {
subscribe: (
operations: string[],
callback: (update: Update) => void
) => {
subscribe: (operations: string[], callback: (update: Update) => void) => {
const listener = ({ data }: { data: string }) => {
if (data === "PONG") {
return;
@@ -67,14 +64,27 @@ async function handleMessage(
{ deployments, builds, servers, updates }: State,
update: Update
) {
updates.add(update);
updates.addOrUpdate(update);
let name = "";
if (update.target.type === "Deployment") {
const deployment = deployments.get(update.target.id);
name = deployment ? deployment.deployment.name : "";
} else if (update.target.type === "Build") {
const build = builds.get(update.target.id);
name = build ? build.name : "";
} else if (update.target.type === "Server") {
const server = servers.get(update.target.id);
name = server ? server.server.name : "";
}
pushNotification(
update.status === UpdateStatus.InProgress
? "ok"
: update.success
? "good"
: "bad",
`${update.operation} (${update.status})`
`${update.operation.replaceAll("_", " ")} ${name ? `on ${name} ` : ""}(${
update.status
})`
);
// deployment