Worker cards design
This commit is contained in:
parent
ea55a8fdf4
commit
9d50b7694b
|
|
@ -10,10 +10,13 @@
|
||||||
--text: #f8f8f2;
|
--text: #f8f8f2;
|
||||||
--text-muted: #75715e;
|
--text-muted: #75715e;
|
||||||
--text-dim: #cfcfc2;
|
--text-dim: #cfcfc2;
|
||||||
|
--text-dark: #1a1a1a;
|
||||||
--yellow: #e6db74;
|
--yellow: #e6db74;
|
||||||
--orange: #fd971f;
|
--orange: #fd971f;
|
||||||
--blue: #66d9ef;
|
--blue: #66d9ef;
|
||||||
--purple: #ae81ff;
|
--purple: #ae81ff;
|
||||||
|
--green: #a3be8c;
|
||||||
|
--red: #e06c75;
|
||||||
|
|
||||||
--radius: 4px;
|
--radius: 4px;
|
||||||
--transition: 0.15s ease;
|
--transition: 0.15s ease;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
const API_KEY = 'awoorwa32';
|
|
||||||
|
|
||||||
const saveThrottleDelay = 2000;
|
const saveThrottleDelay = 2000;
|
||||||
const pendingSaves = new Map<string, ReturnType<typeof setTimeout>>();
|
const pendingSaves = new Map<string, ReturnType<typeof setTimeout>>();
|
||||||
|
|
||||||
|
|
@ -15,12 +13,10 @@ export const loadObject = async <T>(key: string, defaultObject: T): Promise<T> =
|
||||||
|
|
||||||
let remoteObject: Partial<T> = {};
|
let remoteObject: Partial<T> = {};
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`https://demo.pabloader.ru/storage/${key}?_=${Math.random()}`);
|
// TODO loading from a remote source
|
||||||
if (response.ok) {
|
const compressedData = new Blob([]);
|
||||||
const compressedData = await response.blob();
|
const decompressedData = await decompressBlob(compressedData);
|
||||||
const decompressedData = await decompressBlob(compressedData);
|
remoteObject = JSON.parse(await decompressedData.text());
|
||||||
remoteObject = JSON.parse(await decompressedData.text());
|
|
||||||
}
|
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
return { ...defaultObject, ...localObject, ...remoteObject };
|
return { ...defaultObject, ...localObject, ...remoteObject };
|
||||||
|
|
@ -43,22 +39,9 @@ const doSaveObject = async <T>(key: string, obj: T) => {
|
||||||
} catch { }
|
} catch { }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const url = new URL('https://demo.pabloader.ru/storage/index.php');
|
|
||||||
url.searchParams.set('filename', key);
|
|
||||||
|
|
||||||
const compressedData = await compressBlob(saveData);
|
const compressedData = await compressBlob(saveData);
|
||||||
|
// TODO saving to remote storage
|
||||||
const response = await fetch(url, {
|
void compressedData;
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/gzip',
|
|
||||||
'Authorization': `Bearer ${API_KEY}`,
|
|
||||||
},
|
|
||||||
body: compressedData,
|
|
||||||
});
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error('Failed to save context');
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -68,14 +51,12 @@ export const compressBlob = async (blob: Blob | string): Promise<Blob> => {
|
||||||
blob = new Blob([blob]);
|
blob = new Blob([blob]);
|
||||||
}
|
}
|
||||||
const cs = new CompressionStream("gzip");
|
const cs = new CompressionStream("gzip");
|
||||||
// @ts-ignore
|
|
||||||
const compressedStream = blob.stream().pipeThrough(cs);
|
const compressedStream = blob.stream().pipeThrough(cs);
|
||||||
return await new Response(compressedStream).blob();
|
return await new Response(compressedStream).blob();
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decompressBlob = async (blob: Blob): Promise<Blob> => {
|
export const decompressBlob = async (blob: Blob): Promise<Blob> => {
|
||||||
const ds = new DecompressionStream("gzip");
|
const ds = new DecompressionStream("gzip");
|
||||||
// @ts-ignore
|
|
||||||
const decompressedStream = blob.stream().pipeThrough(ds);
|
const decompressedStream = blob.stream().pipeThrough(ds);
|
||||||
return await new Response(decompressedStream).blob();
|
return await new Response(decompressedStream).blob();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,15 @@
|
||||||
border-color: var(--accent-alt) !important;
|
border-color: var(--accent-alt) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maintenanceBorder {
|
||||||
|
border-color: var(--yellow) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ownBadge {
|
||||||
|
background: var(--accent-alt);
|
||||||
|
color: var(--text-dark);
|
||||||
|
}
|
||||||
|
|
||||||
.offline {
|
.offline {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
@ -65,24 +74,14 @@
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.online {
|
|
||||||
background: var(--accent-alt);
|
|
||||||
color: #1a1a1a;
|
|
||||||
}
|
|
||||||
|
|
||||||
.offlineBadge {
|
|
||||||
background: var(--bg-active);
|
|
||||||
color: var(--text-muted);
|
|
||||||
}
|
|
||||||
|
|
||||||
.maintenance {
|
.maintenance {
|
||||||
background: var(--yellow);
|
background: var(--yellow);
|
||||||
color: #1a1a1a;
|
color: var(--text-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.trusted {
|
.trusted {
|
||||||
background: var(--blue);
|
background: var(--accent);
|
||||||
color: #1a1a1a;
|
color: var(--text-dark);
|
||||||
}
|
}
|
||||||
|
|
||||||
.info {
|
.info {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ export const WorkerCard = ({ worker, isOwn }: Props) => {
|
||||||
return (
|
return (
|
||||||
<div class={clsx(styles.card, {
|
<div class={clsx(styles.card, {
|
||||||
[styles.own]: isOwn,
|
[styles.own]: isOwn,
|
||||||
|
[styles.maintenanceBorder]: worker.maintenance_mode,
|
||||||
[styles.offline]: !worker.online,
|
[styles.offline]: !worker.online,
|
||||||
})}>
|
})}>
|
||||||
<div class={styles.header}>
|
<div class={styles.header}>
|
||||||
|
|
@ -29,14 +30,9 @@ export const WorkerCard = ({ worker, isOwn }: Props) => {
|
||||||
{worker.info ? <span class={styles.infoToggle}>{expanded.value ? ' ▲' : ' ▼'}</span> : null}
|
{worker.info ? <span class={styles.infoToggle}>{expanded.value ? ' ▲' : ' ▼'}</span> : null}
|
||||||
</span>
|
</span>
|
||||||
<div class={styles.badges}>
|
<div class={styles.badges}>
|
||||||
<span class={clsx(styles.badge, {
|
{isOwn && <span class={clsx(styles.badge, styles.ownBadge)}>own</span>}
|
||||||
[styles.online]: worker.online,
|
|
||||||
[styles.offlineBadge]: !worker.online,
|
|
||||||
})}>
|
|
||||||
{worker.online ? 'online' : 'offline'}
|
|
||||||
</span>
|
|
||||||
{worker.maintenance_mode && <span class={clsx(styles.badge, styles.maintenance)}>maintenance</span>}
|
{worker.maintenance_mode && <span class={clsx(styles.badge, styles.maintenance)}>maintenance</span>}
|
||||||
{worker.trusted && <span class={clsx(styles.badge, styles.trusted)}>trusted</span>}
|
{!worker.trusted && <span class={clsx(styles.badge, styles.trusted)}>not trusted</span>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -77,6 +73,10 @@ export const WorkerCard = ({ worker, isOwn }: Props) => {
|
||||||
<dt>Kudos earned</dt>
|
<dt>Kudos earned</dt>
|
||||||
<dd>{formatNumber(worker.kudos_rewards)}</dd>
|
<dd>{formatNumber(worker.kudos_rewards)}</dd>
|
||||||
</div>
|
</div>
|
||||||
|
<div class={styles.detail}>
|
||||||
|
<dt>Kudos/hour</dt>
|
||||||
|
<dd>{worker.uptime > 0 ? formatNumber(Math.round((worker.kudos_details.generated / worker.uptime) * 3600)) : '0'}</dd>
|
||||||
|
</div>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue