feat: Added distribution info
This commit is contained in:
parent
e66b41560d
commit
a88f48751f
@ -3,7 +3,7 @@
|
||||
}
|
||||
|
||||
.content {
|
||||
@apply flex flex-col self-center sm:w-4/6 w-full;
|
||||
@apply flex flex-col self-center sm:w-10/12 lg:w-4/6 w-full;
|
||||
}
|
||||
|
||||
.title {
|
||||
|
94
src/components/ServerList/Server.tsx
Normal file
94
src/components/ServerList/Server.tsx
Normal file
@ -0,0 +1,94 @@
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
ArrowUpOnSquareStackIcon,
|
||||
CheckBadgeIcon,
|
||||
ClockIcon,
|
||||
CubeIcon,
|
||||
DocumentDuplicateIcon,
|
||||
ExclamationCircleIcon,
|
||||
ExclamationTriangleIcon,
|
||||
ServerIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { Server } from '../../utils/useServers';
|
||||
import { ServerInfo } from '../../utils/useServerInfo';
|
||||
import { formatDate, formatFileSize } from '../../utils';
|
||||
|
||||
type ServerProps = {
|
||||
server: Server;
|
||||
serverInfo: ServerInfo;
|
||||
selectedServer?: string | undefined;
|
||||
setSelectedServer?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
onTransfer?: (server: string) => void;
|
||||
onCancel?: () => void;
|
||||
blobsOnlyOnThisServer: number;
|
||||
};
|
||||
|
||||
const Server = ({
|
||||
server,
|
||||
selectedServer,
|
||||
setSelectedServer,
|
||||
serverInfo,
|
||||
onTransfer,
|
||||
onCancel,
|
||||
blobsOnlyOnThisServer,
|
||||
}: ServerProps) => {
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
`server ${selectedServer == server.name ? 'selected' : ''} ` +
|
||||
`${setSelectedServer ? ' hover:bg-neutral-700 cursor-pointer' : ''} `
|
||||
}
|
||||
key={server.name}
|
||||
onClick={() => setSelectedServer && setSelectedServer(server.name)}
|
||||
>
|
||||
<div className="server-icon">
|
||||
<ServerIcon />
|
||||
</div>
|
||||
<div className="flex flex-col grow">
|
||||
<div className="server-name">
|
||||
{server.name}
|
||||
{serverInfo.isLoading && <ArrowPathIcon className="loading" />}
|
||||
</div>
|
||||
<div className="server-stats">
|
||||
<div className="server-stat">
|
||||
<DocumentDuplicateIcon /> {serverInfo.count}
|
||||
</div>
|
||||
<div className="server-stat">
|
||||
<CubeIcon /> {formatFileSize(serverInfo.size)}
|
||||
</div>
|
||||
<div className="server-stat">
|
||||
<ClockIcon /> {formatDate(serverInfo.lastChange)}
|
||||
</div>
|
||||
<div className="server-stat">
|
||||
{blobsOnlyOnThisServer > 0 ? (
|
||||
<div>
|
||||
<ExclamationTriangleIcon /> {blobsOnlyOnThisServer} objects only available here
|
||||
</div>
|
||||
) : (
|
||||
<div>
|
||||
<CheckBadgeIcon /> all objects distributed.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{((selectedServer == server.name && onTransfer) || onCancel) && (
|
||||
<div className="server-actions">
|
||||
{selectedServer == server.name && onTransfer && (
|
||||
<a onClick={() => onTransfer(server.name)}>
|
||||
<ArrowUpOnSquareStackIcon /> Transfer
|
||||
</a>
|
||||
)}
|
||||
{onCancel && (
|
||||
<a onClick={() => onCancel()}>
|
||||
<XMarkIcon />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Server;
|
@ -15,7 +15,8 @@
|
||||
}
|
||||
|
||||
.server-stats {
|
||||
@apply flex flex-row gap-8;
|
||||
@apply grid grid-cols-4 gap-8;
|
||||
grid-template-columns: 4em 6em 8em auto;
|
||||
}
|
||||
|
||||
.server-stat svg {
|
||||
|
@ -1,19 +1,10 @@
|
||||
import {
|
||||
ArrowPathIcon,
|
||||
ArrowUpOnSquareStackIcon,
|
||||
ClockIcon,
|
||||
CubeIcon,
|
||||
DocumentDuplicateIcon,
|
||||
ServerIcon,
|
||||
XMarkIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { formatDate, formatFileSize } from '../../utils';
|
||||
import { useServerInfo } from '../../utils/useServerInfo';
|
||||
import { Server as ServerType } from '../../utils/useServers';
|
||||
import Server from './Server';
|
||||
import './ServerList.css';
|
||||
import { Server } from '../../utils/useServers';
|
||||
|
||||
type ServerListProps = {
|
||||
servers: Server[];
|
||||
servers: ServerType[];
|
||||
selectedServer?: string | undefined;
|
||||
setSelectedServer?: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||
onTransfer?: (server: string) => void;
|
||||
@ -21,54 +12,24 @@ type ServerListProps = {
|
||||
};
|
||||
|
||||
export const ServerList = ({ servers, selectedServer, setSelectedServer, onTransfer, onCancel }: ServerListProps) => {
|
||||
const serverInfo = useServerInfo();
|
||||
//
|
||||
const { serverInfo, distribution } = useServerInfo();
|
||||
const blobsWithOnlyOneOccurance = Object.values(distribution)
|
||||
.filter(d => d.servers.length == 1)
|
||||
.map(d => ({ ...d.blob, server: d.servers[0] }));
|
||||
|
||||
return (
|
||||
<div className="server-list">
|
||||
{servers.map((server, sx) => (
|
||||
<div
|
||||
className={
|
||||
`server ${selectedServer == server.name ? 'selected' : ''} ` +
|
||||
`${setSelectedServer ? ' hover:bg-neutral-700 cursor-pointer' : ''} `
|
||||
}
|
||||
key={sx}
|
||||
onClick={() => setSelectedServer && setSelectedServer(server.name)}
|
||||
>
|
||||
<div className="server-icon">
|
||||
<ServerIcon />
|
||||
</div>
|
||||
<div className="flex flex-col grow">
|
||||
<div className="server-name">
|
||||
{server.name}
|
||||
{serverInfo[server.name].isLoading && <ArrowPathIcon className="loading" />}
|
||||
</div>
|
||||
<div className="server-stats">
|
||||
<div className="server-stat">
|
||||
<DocumentDuplicateIcon /> {serverInfo[server.name].count}
|
||||
</div>
|
||||
<div className="server-stat">
|
||||
<CubeIcon /> {formatFileSize(serverInfo[server.name].size)}
|
||||
</div>
|
||||
<div className="server-stat">
|
||||
<ClockIcon /> {formatDate(serverInfo[server.name].lastChange)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{((selectedServer == server.name && onTransfer) || onCancel) && (
|
||||
<div className="server-actions">
|
||||
{selectedServer == server.name && onTransfer && (
|
||||
<a onClick={() => onTransfer(server.name)}>
|
||||
<ArrowUpOnSquareStackIcon /> Transfer
|
||||
</a>
|
||||
)}
|
||||
{onCancel && (
|
||||
<a onClick={() => onCancel()}>
|
||||
<XMarkIcon />
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{servers.map(server => (
|
||||
<Server
|
||||
key={server.name}
|
||||
serverInfo={serverInfo[server.name]}
|
||||
server={server}
|
||||
selectedServer={selectedServer}
|
||||
setSelectedServer={setSelectedServer}
|
||||
onTransfer={onTransfer}
|
||||
onCancel={onCancel}
|
||||
blobsOnlyOnThisServer={blobsWithOnlyOneOccurance.filter(b => b.server == server.name).length}
|
||||
></Server>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
|
@ -24,35 +24,13 @@ import { useNavigate } from 'react-router-dom';
|
||||
function Home() {
|
||||
const { loginWithExtension, signEventTemplate } = useNDK();
|
||||
const [selectedServer, setSelectedServer] = useState<string | undefined>();
|
||||
const serverInfo = useServerInfo();
|
||||
const { serverInfo } = useServerInfo();
|
||||
const navigate = useNavigate();
|
||||
|
||||
useEffect(() => {
|
||||
loginWithExtension();
|
||||
}, []);
|
||||
|
||||
/*,
|
||||
combine: (results) => {
|
||||
const dict: BlobDictionary = {};
|
||||
|
||||
results.forEach((server) =>
|
||||
server.data && server.data.forEach((blob: BlobDescriptor) => {
|
||||
if (dict[blob.sha256]) {
|
||||
dict[blob.sha256].urls.push(blob.url);
|
||||
} else {
|
||||
dict[blob.sha256] = {
|
||||
...blob,
|
||||
urls: [blob.url],
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
data: dict,
|
||||
};
|
||||
},*/
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const deleteBlob = useMutation({
|
||||
@ -64,7 +42,7 @@ function Home() {
|
||||
queryClient.setQueryData(['blobs', variables.serverName], (oldData: BlobDescriptor[]) =>
|
||||
oldData ? oldData.filter(b => b.sha256 !== variables.hash) : oldData
|
||||
);
|
||||
console.log({ key: ['blobs', variables.serverName] });
|
||||
// console.log({ key: ['blobs', variables.serverName] });
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -28,7 +28,7 @@ type TransferStatus = {
|
||||
export const Transfer = () => {
|
||||
const { source: transferSource } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const serverInfo = useServerInfo();
|
||||
const {serverInfo} = useServerInfo();
|
||||
const [transferTarget, setTransferTarget] = useState<string | undefined>();
|
||||
const { signEventTemplate } = useNDK();
|
||||
const queryClient = useQueryClient();
|
||||
|
@ -15,6 +15,10 @@ export type ServerInfo = {
|
||||
blobs?: BlobDescriptor[];
|
||||
};
|
||||
|
||||
type BlobDictionary = {
|
||||
[key: string]: { blob: BlobDescriptor; servers: string[] };
|
||||
};
|
||||
|
||||
export const useServerInfo = () => {
|
||||
const servers = useServers();
|
||||
const { user, signEventTemplate } = useNDK();
|
||||
@ -58,5 +62,26 @@ export const useServerInfo = () => {
|
||||
return info;
|
||||
}, [servers, blobs]);
|
||||
|
||||
return serverInfo;
|
||||
const distribution = useMemo(() => {
|
||||
const dict: BlobDictionary = {};
|
||||
|
||||
servers.forEach(server => {
|
||||
const si = serverInfo[server.name];
|
||||
|
||||
si.blobs &&
|
||||
si.blobs.forEach((blob: BlobDescriptor) => {
|
||||
if (dict[blob.sha256]) {
|
||||
dict[blob.sha256].servers.push(server.name);
|
||||
} else {
|
||||
dict[blob.sha256] = {
|
||||
blob,
|
||||
servers: [server.name],
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
return dict;
|
||||
}, [servers, serverInfo]);
|
||||
|
||||
return {serverInfo, distribution};
|
||||
};
|
||||
|
@ -22,7 +22,7 @@ export const useServers = (): Server[] => {
|
||||
const pubkey = user?.npub && (nip19.decode(user?.npub).data as string); // TODO validate type
|
||||
|
||||
const serverListEvent = useEvent({ kinds: [10063 as NDKKind], authors: [pubkey!] }, { disable: !pubkey });
|
||||
console.log(serverListEvent);
|
||||
|
||||
const servers = useMemo(() => {
|
||||
const serverUrls = uniqAndSort(
|
||||
[...(serverListEvent?.getMatchingTags('r').map(t => t[1]) || []), ...additionalServers].map(s =>
|
||||
|
Loading…
Reference in New Issue
Block a user