feat: Added virtual all servers view

This commit is contained in:
florian 2024-05-25 13:58:33 +02:00
parent f49fc74c4d
commit 03134c37d6
4 changed files with 70 additions and 22 deletions

View File

@ -9,13 +9,11 @@ import {
ShieldExclamationIcon,
XMarkIcon,
} from '@heroicons/react/24/outline';
import { Server as ServerType } from '../../utils/useUserServers';
import { ServerInfo } from '../../utils/useServerInfo';
import { formatDate, formatFileSize } from '../../utils/utils';
import { ServerInfo } from '../../utils/useServerInfo';
type ServerProps = {
server: ServerType;
serverInfo: ServerInfo;
server: ServerInfo;
selectedServer?: string | undefined;
setSelectedServer?: React.Dispatch<React.SetStateAction<string | undefined>>;
onTransfer?: (server: string) => void;
@ -28,13 +26,12 @@ const Server = ({
server,
selectedServer,
setSelectedServer,
serverInfo,
onTransfer,
onCancel,
onCheck,
blobsOnlyOnThisServer,
}: ServerProps) => {
const readyToUse = !serverInfo.isLoading && !serverInfo.isError;
const readyToUse = !server.isLoading && !server.isError;
return (
<div
className={
@ -50,24 +47,24 @@ const Server = ({
<div className="flex flex-col grow">
<div className="server-name">
{server.name}
{serverInfo.isLoading && <span className="ml-2 loading loading-spinner loading-sm"></span>}
{server.isLoading && <span className="ml-2 loading loading-spinner loading-sm"></span>}
</div>
{serverInfo.isError ? (
{server.isError ? (
<div className="badge badge-error">
<ExclamationTriangleIcon className="w-4 mr-2" /> Error connecting to server
</div>
) : (
<div className="server-stats">
<div className="server-stat tooltip text-left" data-tip="Number of blobs">
<DocumentDuplicateIcon /> {serverInfo.count}
<DocumentDuplicateIcon /> {server.count}
</div>
<div className="server-stat tooltip text-left" data-tip="Total size of blobs">
<CubeIcon /> {formatFileSize(serverInfo.size)}
<CubeIcon /> {formatFileSize(server.size)}
</div>
<div className="server-stat tooltip text-left" data-tip="Date of last change">
<ClockIcon /> {formatDate(serverInfo.lastChange)}
<ClockIcon /> {formatDate(server.lastChange)}
</div>
{serverInfo.count > 0 && (
{server.count > 0 && !server.virtual && (
<div className="server-stat">
{blobsOnlyOnThisServer > 0 ? (
<div>
@ -83,7 +80,7 @@ const Server = ({
</div>
)}
</div>
{((selectedServer == server.name && onTransfer) || onCancel) && (
{((selectedServer == server.name && !server.virtual && onTransfer) || onCancel) && (
<div className="server-actions ">
{selectedServer == server.name && (
<>

View File

@ -1,16 +1,16 @@
import { Cog8ToothIcon } from '@heroicons/react/24/outline';
import { useServerInfo } from '../../utils/useServerInfo';
import { ServerInfo, useServerInfo } from '../../utils/useServerInfo';
import { Server as ServerType } from '../../utils/useUserServers';
import Server from './Server';
import './ServerList.css';
import ServerListPopup from '../ServerListPopup';
import { useState } from 'react';
import { useMemo, useState } from 'react';
import { useNDK } from '../../utils/ndk';
import { NDKEvent } from '@nostr-dev-kit/ndk';
import dayjs from 'dayjs';
type ServerListProps = {
servers: ServerType[];
servers: ServerInfo[];
selectedServer?: string | undefined;
setSelectedServer?: React.Dispatch<React.SetStateAction<string | undefined>>;
onTransfer?: (server: string) => void;
@ -18,6 +18,7 @@ type ServerListProps = {
onCheck?: (server: string) => void;
title?: React.ReactElement;
manageServers?: boolean;
withVirtualServers?: boolean;
};
export const ServerList = ({
@ -28,9 +29,10 @@ export const ServerList = ({
onCancel,
title,
manageServers = false,
withVirtualServers = false,
}: ServerListProps) => {
const { ndk, user } = useNDK();
const { serverInfo, distribution } = useServerInfo();
const { distribution } = useServerInfo();
const blobsWithOnlyOneOccurance = Object.values(distribution)
.filter(d => d.servers.length == 1)
.map(d => ({ ...d.blob, server: d.servers[0] }));
@ -58,6 +60,8 @@ export const ServerList = ({
await ev.publish();
};
const serversToList = useMemo(() => withVirtualServers ? servers : servers.filter(s => !s.virtual), [servers, withVirtualServers])
return (
<>
<div className={`flex flex-row py-4 ${!title ? 'justify-end' : ''}`}>
@ -76,14 +80,13 @@ export const ServerList = ({
isOpen={isDialogOpen}
onClose={handleCloseDialog}
onSave={handleSaveServers}
initialServers={Object.values(serverInfo)}
initialServers={servers.filter(s => !s.virtual)}
/>
<div className="server-list">
{servers.map(server => (
{serversToList.map(server => (
<Server
key={server.name}
serverInfo={serverInfo[server.name]}
server={server}
selectedServer={selectedServer}
setSelectedServer={setSelectedServer}

View File

@ -4,7 +4,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
import { BlobDescriptor, BlossomClient } from 'blossom-client-sdk';
import { useNDK } from '../utils/ndk';
import BlobList from '../components/BlobList/BlobList';
import { useServerInfo } from '../utils/useServerInfo';
import { ServerInfo, useServerInfo } from '../utils/useServerInfo';
import { ServerList } from '../components/ServerList/ServerList';
import { useNavigate } from 'react-router-dom';
@ -62,6 +62,7 @@ function Home() {
onCheck={() => navigate('/check/' + selectedServer)}
title={<>Servers</>}
manageServers={true}
withVirtualServers={true}
></ServerList>
{selectedServer && serverInfo[selectedServer] && selectedServerBlobs && (

View File

@ -7,6 +7,7 @@ import { useUserServers } from './useUserServers';
import dayjs from 'dayjs';
export type ServerInfo = {
virtual: boolean;
count: number;
size: number;
lastChange: number;
@ -21,6 +22,21 @@ type BlobDictionary = {
[key: string]: { blob: BlobDescriptor; servers: string[] };
};
const mergeBlobs = (
baseBlobs: BlobDescriptor[],
newBlobs: BlobDescriptor[],
existingBlobs: { [key: string]: boolean }
): BlobDescriptor[] => {
const result = [...baseBlobs];
for (const blob of newBlobs) {
if (!existingBlobs[blob.sha256]) {
existingBlobs[blob.sha256] = true;
result.push(blob);
}
}
return result;
};
export const useServerInfo = () => {
const servers = useUserServers();
const { user, signEventTemplate } = useNDK();
@ -48,6 +64,7 @@ export const useServerInfo = () => {
servers.forEach((server, sx) => {
info[server.name] = {
...server,
virtual: false,
blobs: blobs[sx].data,
isLoading: blobs[sx].isLoading,
isError: blobs[sx].isError,
@ -59,6 +76,36 @@ export const useServerInfo = () => {
return info;
}, [servers, blobs]);
const allServersAggregation = useMemo(() => {
const serversInfos = Object.values(serverInfo);
const existingBlobs: { [key: string]: boolean } = {};
const initial: ServerInfo = {
virtual: true,
count: 0,
size: 0,
lastChange: 0,
isLoading: false,
isError: false,
name: 'All servers',
url: 'all',
blobs: [],
};
const allInfo = serversInfos.reduce(
(acc, server) => ({
...acc,
lastChange: Math.max(acc.lastChange, server.lastChange),
isLoading: acc.isLoading || server.isLoading,
isError: acc.isError || server.isError,
blobs: mergeBlobs(acc.blobs || [], server.blobs || [], existingBlobs),
}),
initial
);
allInfo.size = allInfo.blobs?.reduce((acc, blob) => acc + blob.size, 0) || 0;
allInfo.count = allInfo.blobs?.length || 0;
return { [allInfo.name]: allInfo, ...serverInfo };
}, [serverInfo]);
const distribution = useMemo(() => {
const dict: BlobDictionary = {};
@ -80,5 +127,5 @@ export const useServerInfo = () => {
return dict;
}, [servers, serverInfo]);
return { serverInfo, distribution };
return { serverInfo: allServersAggregation, distribution };
};