add usernames cache to state

This commit is contained in:
mbecker20
2023-01-02 06:33:18 +00:00
parent f5fa676db6
commit a6d655b42e
10 changed files with 121 additions and 113 deletions

View File

@@ -18,7 +18,7 @@ const AddServer: Component<{}> = () => {
target="add server"
targetClass="green shadow"
targetStyle={{ width: "100%" }}
content={<Content close={toggleShow} />}
content={() => <Content close={toggleShow} />}
position="center"
/>
);

View File

@@ -12,7 +12,7 @@ import UpdateMenu from "../../update/UpdateMenu";
import s from "./update.module.scss";
const Update: Component<{ update: UpdateType }> = (p) => {
const { deployments, servers, builds } = useAppState();
const { deployments, servers, builds, usernames } = useAppState();
const name = () => {
if (p.update.target.type === "Deployment" && deployments.loaded()) {
return deployments.get(p.update.target.id!)?.deployment.name || "deleted";
@@ -53,7 +53,7 @@ const Update: Component<{ update: UpdateType }> = (p) => {
</div>
<Flex gap="0.5rem">
<Icon type="user" />
<div>{p.update.operator}</div>
<div>{usernames.get(p.update.operator)}</div>
</Flex>
</Grid>
<UpdateMenu update={p.update} />

View File

@@ -16,7 +16,7 @@ const StatGraphs: Component<{ id: string }> = (p) => {
toggleShow={toggleShow}
target={<Icon type="timeline-line-chart" width="0.85rem" />}
targetClass="blue"
content={<Graphs id={p.id} />}
content={() => <Graphs id={p.id} />}
title={`${name()} stats`}
/>
);

View File

@@ -17,7 +17,7 @@ import s from "./menu.module.scss";
const CenterMenu: Component<{
show: Accessor<boolean>;
toggleShow: () => void;
content: JSXElement;
content: () => JSXElement;
target: JSXElement;
targetStyle?: JSX.CSSProperties;
targetClass?: string;
@@ -58,7 +58,7 @@ const CenterMenu: Component<{
const Child: Component<{
title?: string;
content: JSXElement;
content: () => JSXElement;
show: Accessor<boolean>;
toggleShow: () => void;
padding?: string | number;
@@ -95,7 +95,7 @@ const Child: Component<{
</button>
</Flex>
</Flex>
{p.content}
{p.content()}
</Grid>
</Grid>
);

View File

@@ -1,68 +1,38 @@
import { Component, For, Show } from "solid-js";
import { Component, Show } from "solid-js";
import { useAppState } from "../../state/StateProvider";
import { Update as UpdateType } from "../../types";
import {
combineClasses,
readableDuration,
readableMonitorTimestamp,
} from "../../util/helpers";
import { useToggle } from "../../util/hooks";
import Icon from "../shared/Icon";
import Flex from "../shared/layout/Flex";
import Grid from "../shared/layout/Grid";
import CenterMenu from "../shared/menu/CenterMenu";
import s from "./update.module.scss";
import UpdateMenu from "./UpdateMenu";
const Update: Component<{ update: UpdateType; showName: boolean }> = (p) => {
const { deployments, servers, builds } = useAppState();
const name = () => {
if (p.update.target.type === "Deployment" && deployments.loaded()) {
return deployments.get(p.update.target.id!)?.deployment.name || "deleted";
} else if (p.update.target.type === "Server" && servers.loaded()) {
return servers.get(p.update.target.id)?.server.name || "deleted";
} else if (p.update.target.type === "Build" && builds.loaded()) {
return builds.get(p.update.target.id)?.name || "deleted";
} else {
return "monitor";
}
};
const Update: Component<{ update: UpdateType }> = (p) => {
const { usernames } = useAppState();
const operation = () => {
return p.update.operation.replaceAll("_", " ")
};
const [showLog, toggleShowLog] = useToggle();
return (
<Grid
gap="0.25rem"
class={combineClasses(s.Update, !p.showName && s.NoName, "shadow")}
>
<Show when={p.showName}>
<h2 style={{ "place-self": "center" }}>{name()}</h2>
</Show>
<Grid
gap="0.25rem"
<Grid gap="0.25rem" class={combineClasses(s.Update, "shadow")}>
<div
style={{
"grid-template-columns": "1fr 1fr",
"grid-template-rows": "1fr 1fr",
color: !p.update.success ? "rgb(182, 47, 52)" : "inherit",
}}
placeItems="center start"
>
<div
style={{
color: !p.update.success ? "rgb(182, 47, 52)" : "inherit",
}}
>
{operation()}
</div>
<div style={{ "place-self": "center end" }}>
{readableMonitorTimestamp(p.update.start_ts)}
</div>
<Flex alignItems="center">
<Icon type="user" />
<div>{p.update.operator}</div>
</Flex>
{operation()}
</div>
<div style={{ "place-self": "start end" }}>{readableMonitorTimestamp(p.update.start_ts)}</div>
<Flex gap="0.5rem" alignItems="center">
<Icon type="user" />
<div>{usernames.get(p.update.operator)}</div>
</Flex>
<Flex style={{ "place-self": "center end" }} alignItems="center">
<UpdateMenu update={p.update} />
</Grid>
</Flex>
</Grid>
);
};

View File

@@ -1,4 +1,5 @@
import { Component, For, Show } from "solid-js";
import { Component, createResource, For, Show } from "solid-js";
import { client } from "../..";
import { useAppState } from "../../state/StateProvider";
import { Update as UpdateType } from "../../types";
import { combineClasses, readableDuration, readableMonitorTimestamp } from "../../util/helpers";
@@ -36,63 +37,70 @@ const UpdateMenu: Component<{ update: UpdateType }> = (p) => {
targetClass="blue"
padding="1rem 2rem"
content={
<Grid class={s.LogContainer} gap="1rem">
<Grid gap="0.5rem" class="card light shadow">
<div>started at: {readableMonitorTimestamp(p.update.start_ts)}</div>
<Show when={p.update.end_ts}>
<div>
duration:{" "}
{readableDuration(p.update.start_ts, p.update.end_ts!)}
</div>
</Show>
</Grid>
<For each={p.update.logs}>
{(log, index) => {
return (
<Grid gap="0.5rem" class="card light shadow">
<Flex alignItems="center" class="wrap">
<h1>{log.stage}</h1>
<div style={{ opacity: 0.7 }}>
(stage {index() + 1} of {p.update.logs.length})
</div>
<div style={{ opacity: 0.7 }}>
{readableDuration(log.start_ts, log.end_ts)}
</div>
</Flex>
<Show when={log.command}>
<div>command</div>
<pre class={combineClasses(s.Log)}>{log.command}</pre>
</Show>
<Show when={log.stdout}>
<div>stdout</div>
<pre
class={combineClasses(s.Log)}
// style={{
// "max-height": log.stderr ? "30vh" : "60vh",
// }}
>
{log.stdout}
</pre>
</Show>
<Show when={log.stderr}>
<div>stderr</div>
<pre
class={combineClasses(s.Log)}
// style={{
// "max-height": log.stdout ? "30vh" : "60vh",
// }}
>
{log.stderr}
</pre>
</Show>
</Grid>
);
}}
</For>
</Grid>
() => <UpdateMenuContent update={p.update} />
}
/>
);
};
export default UpdateMenu;
export default UpdateMenu;
const UpdateMenuContent: Component<{ update: UpdateType }> = (p) => {
const { usernames } = useAppState();
return (
<Grid class={s.LogContainer} gap="1rem">
<Grid gap="0.5rem" class="card light shadow">
<div>operator: {usernames.get(p.update.operator)}</div>
<div>started at: {readableMonitorTimestamp(p.update.start_ts)}</div>
<Show when={p.update.end_ts}>
<div>
duration: {readableDuration(p.update.start_ts, p.update.end_ts!)}
</div>
</Show>
</Grid>
<For each={p.update.logs}>
{(log, index) => {
return (
<Grid gap="0.5rem" class="card light shadow">
<Flex alignItems="center" class="wrap">
<h1>{log.stage}</h1>
<div style={{ opacity: 0.7 }}>
(stage {index() + 1} of {p.update.logs.length})
</div>
<div style={{ opacity: 0.7 }}>
{readableDuration(log.start_ts, log.end_ts)}
</div>
</Flex>
<Show when={log.command}>
<div>command</div>
<pre class={combineClasses(s.Log)}>{log.command}</pre>
</Show>
<Show when={log.stdout}>
<div>stdout</div>
<pre
class={combineClasses(s.Log)}
// style={{
// "max-height": log.stderr ? "30vh" : "60vh",
// }}
>
{log.stdout}
</pre>
</Show>
<Show when={log.stderr}>
<div>stderr</div>
<pre
class={combineClasses(s.Log)}
// style={{
// "max-height": log.stdout ? "30vh" : "60vh",
// }}
>
{log.stderr}
</pre>
</Show>
</Grid>
);
}}
</For>
</Grid>
);
}

View File

@@ -14,7 +14,9 @@
.Update {
background-color: c.$lightgrey;
padding: 0.75rem;
height: 90px;
height: 70px;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr;
transform-origin: top;
animation-name: Enter;
animation-duration: 750ms;
@@ -33,7 +35,7 @@
}
.Update.NoName {
height: 70px;
// height: 70px;
animation-name: EnterNoName;
}

View File

@@ -7,12 +7,14 @@ import {
useServers,
useServerStats,
useUpdates,
useUsernames,
} from "./hooks";
import connectToWs from "./ws";
import { useUser } from "./UserProvider";
import { PermissionLevel } from "../types";
export type State = {
usernames: ReturnType<typeof useUsernames>
servers: ReturnType<typeof useServers>;
getPermissionOnServer: (id: string) => PermissionLevel;
serverStats: ReturnType<typeof useServerStats>;
@@ -37,7 +39,9 @@ export const AppStateProvider: ParentComponent = (p) => {
const servers = useServers();
const builds = useBuilds();
const deployments = useDeployments();
const usernames = useUsernames();
const state: State = {
usernames,
servers,
getPermissionOnServer: (id: string) => {
const server = servers.get(id)!;

View File

@@ -45,6 +45,31 @@ export function useServerStats() {
};
}
export function useUsernames() {
const [usernames, set] = createSignal<Record<string, string | undefined>>(
{}
);
const load = async (userID: string) => {
const username = await client.get_username(userID);
set((s) => ({ ...s, [userID]: username }));
};
const loading: Record<string, boolean> = {};
return {
get: (userID: string) => {
const username = usernames()[userID];
if (
username === undefined &&
!loading[userID]
) {
loading[userID] = true;
load(userID);
}
return username;
},
load,
};
}
const buildIdPath = ["_id", "$oid"];
export function useBuilds() {

View File

@@ -42,9 +42,8 @@ function connectToWs(state: State) {
return {
subscribe: (operations: Operation[], callback: (update: Update) => void) => {
const listener = ({ data }: { data: string }) => {
if (data === "PONG") {
return;
}
if (data === "PONG") return;
if (data = "LOGGED_IN") return;
const update = JSON.parse(data) as Update;
if (operations.length === 0 || operations.includes(update.operation)) {
callback(update);