Skip to content

Commit

Permalink
save
Browse files Browse the repository at this point in the history
  • Loading branch information
dvovk committed Oct 18, 2024
1 parent dd2a317 commit eb148bb
Show file tree
Hide file tree
Showing 7 changed files with 2,645 additions and 2,028 deletions.
99 changes: 99 additions & 0 deletions src/app/components/SyncStages/TorrentPeersDetailsPopup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { useSelector } from "react-redux";
import { useEffect } from "react";
import { selectTorrrentPeersForNode } from "../../store/syncStagesSlice";
import CloseIcon from "@mui/icons-material/Close";
import { TorrentPeersTable } from "./TorrentPeersTable";

interface TorrentPeersDetailsPopupProps {
onClose: () => void;
}

export const TorrentPeersDetailsPopup = ({ onClose }: TorrentPeersDetailsPopupProps) => {
const peers = useSelector(selectTorrrentPeersForNode);

const handleKeyPress = (event: KeyboardEvent) => {
if (event.key === "Escape") {
onClose();
}
};

useEffect(() => {
window.addEventListener("keydown", handleKeyPress);

return () => {
window.removeEventListener("keydown", handleKeyPress);
};
}, []);

const renderHeader = () => {
return (
<div className="flex flex-row w-full pt-10 pr-10 pl-10">
<div className="flex-[1]"></div>
<div className="flex flex-[2] justify-center">
<h3 className="text-3xl font-semibold">Peers list</h3>
</div>
<div className="flex flex-[1] justify-end">
<CloseIcon
onClick={() => onClose()}
className="cursor-pointer"
/>
</div>
</div>
);
};

const bytesToString = (bts: number[]): string => {
//let s = bytes.map((byte) => String.fromCharCode(byte)).join("");
const bytes = new Uint8Array(bts);
let res = toString(bytes);
return res;
};

function toString(id: Uint8Array): string {
// Equivalent of the Go code checking `me[0] == '-' && me[7] == '-'`
if (id[0] === 45 && id[7] === 45) {
// 45 is the ASCII code for '-'
//return byteArrayToString(id.slice(0, 8)) + byteArrayToHex(id.slice(8));
return byteArrayToHex(id.slice(8));
}

// Hex encoding of the entire array if no condition is met
return byteArrayToHex(id);
}

// Helper function to convert byte array to string
function byteArrayToString(bytes: Uint8Array): string {
return new TextDecoder().decode(bytes);
}

// Helper function to convert byte array to hex string
function byteArrayToHex(bytes: Uint8Array): string {
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}

return (
<>
<div className="justify-center items-center flex overflow-x-hidden overflow-y-auto inset-0 z-50 outline-none focus:outline-none absolute bg-black/[.4]">
<div className="relative w-auto my-6 mx-auto max-w-[100vw]">
{/*content*/}
<div className="border-0 rounded-lg shadow-lg relative flex flex-col w-fit bg-white outline-none focus:outline-none items-center">
{/*header*/}
{renderHeader()}
{/*body*/}
<div className="flex flex-col relative p-6 flex-auto justify-start items-center h-[75vh] overflow-scroll">
<TorrentPeersTable
peers={peers}
peerSelected={false}
onPeerClicked={() => {}}
/>
</div>
{/*footer*/}
</div>
</div>
</div>
<div className="opacity-25 inset-0 z-40 bg-black"></div>
</>
);
};
238 changes: 238 additions & 0 deletions src/app/components/SyncStages/TorrentPeersTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
import { useState } from "react";
import { multipleBytes } from "../../../helpers/converters";
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
import SortIcon from "@mui/icons-material/Sort";
import { SegmentPeer } from "../../store/syncStagesSlice";

enum SortColumn {
Url = "Url",
DlRate = "DlRate",
Address = "Address",
ID = "ID",
PiecesCount = "PiecesCount",
TorrentName = "TorrentName"
}

interface SortState {
column: SortColumn;
descending: boolean;
}

export interface TorrentPeersTableProps {
peers: SegmentPeer[];
peerSelected: boolean;
onPeerClicked: (segment: SegmentPeer) => void;
}

export const TorrentPeersTable = ({ peers, peerSelected, onPeerClicked }: TorrentPeersTableProps) => {
const [visibleSegments, setVisibleSegments] = useState<SegmentPeer[]>(peers);

const [currentSortState, setCurrentSortState] = useState<SortState>({
column: SortColumn.Url,
descending: true
});

const sortSegments = (seg: SegmentPeer[], sotOpt: SortState): void => {
console.log("sortSegments", seg[0]);
let tosort = [...seg];
let sortedSegments = tosort.sort((a, b) => {
if (sotOpt.column === SortColumn.Url) {
return compareStrings(a.url, b.url, sotOpt.descending);
} else if (sotOpt.column === SortColumn.DlRate) {
return compareNumbers(a.downloadRate, b.downloadRate, sotOpt.descending);
} else if (sotOpt.column === SortColumn.PiecesCount) {
return compareNumbers(a.piecesCount, b.piecesCount, sotOpt.descending);
} else if (sotOpt.column === SortColumn.Address) {
return compareStrings(a.remoteAddr, b.remoteAddr, sotOpt.descending);
} else if (sotOpt.column === SortColumn.ID) {
return compareStrings(bytesToString(a.peerId), bytesToString(b.peerId), sotOpt.descending);
} else {
return compareStrings(a.torrentName, b.torrentName, sotOpt.descending);
}
});

console.log("sortedSegments", sortedSegments[0]);

setCurrentSortState(sotOpt);
setVisibleSegments(sortedSegments);
};

const compareStrings = (a: string, b: string, desc: boolean) => {
if (desc) {
return b.localeCompare(a);
} else {
return a.localeCompare(b);
}
};

const compareNumbers = (a: number, b: number, desc: boolean) => {
if (desc) {
return b - a;
} else {
return a - b;
}
};

const getArrowIcon = (column: SortColumn) => {
if (currentSortState.column !== column) {
return <SortIcon className="ml-2" />;
} else {
if (currentSortState.descending) {
return <ArrowDropDownIcon />;
} else {
return <ArrowDropUpIcon />;
}
}
};

const bytesToString = (bts: number[]): string => {
//let s = bytes.map((byte) => String.fromCharCode(byte)).join("");
const bytes = new Uint8Array(bts);
let res = toString(bytes);
return res;
};

function toString(id: Uint8Array): string {
// Equivalent of the Go code checking `me[0] == '-' && me[7] == '-'`
if (id[0] === 45 && id[7] === 45) {
// 45 is the ASCII code for '-'
//return byteArrayToString(id.slice(0, 8)) + byteArrayToHex(id.slice(8));
return byteArrayToHex(id.slice(8));
}

// Hex encoding of the entire array if no condition is met
return byteArrayToHex(id);
}

// Helper function to convert byte array to string
function byteArrayToString(bytes: Uint8Array): string {
return new TextDecoder().decode(bytes);
}

// Helper function to convert byte array to hex string
function byteArrayToHex(bytes: Uint8Array): string {
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}

return (
<div
className="w-full h-[95%]"
style={{ overflowY: !peerSelected ? "scroll" : "hidden" }}
>
<table className="table-fixed text-left">
<thead>
<tr className="border-b">
<th
className="px-4 py-2 cursor-pointer"
onClick={() => {
sortSegments(visibleSegments, {
column: SortColumn.Url,
descending: !currentSortState.descending
});
}}
>
<div className="flex flex-row">
URL
{getArrowIcon(SortColumn.Url)}
</div>
</th>
<th
className="px-4 py-2 cursor-pointer"
onClick={() => {
sortSegments(visibleSegments, {
column: SortColumn.DlRate,
descending: !currentSortState.descending
});
}}
>
<div className="flex flex-row">
Download Rate
{getArrowIcon(SortColumn.DlRate)}
</div>
</th>
<th
className="px-4 py-2 cursor-pointer"
onClick={() => {
sortSegments(visibleSegments, {
column: SortColumn.Address,
descending: !currentSortState.descending
});
}}
>
<div className="flex flex-row">
Remote Address
{getArrowIcon(SortColumn.Address)}
</div>
</th>
<th
className="px-4 py-2 cursor-pointer"
onClick={() => {
sortSegments(visibleSegments, {
column: SortColumn.ID,
descending: !currentSortState.descending
});
}}
>
<div className="flex flex-row">
ID
{getArrowIcon(SortColumn.ID)}
</div>
</th>
<th
className="px-4 py-2 cursor-pointer"
onClick={() => {
sortSegments(visibleSegments, {
column: SortColumn.PiecesCount,
descending: !currentSortState.descending
});
}}
>
<div className="flex flex-row">
Pieces Count
{getArrowIcon(SortColumn.PiecesCount)}
</div>
</th>
<th
className="px-4 py-2 cursor-pointer"
onClick={() => {
sortSegments(visibleSegments, {
column: SortColumn.TorrentName,
descending: !currentSortState.descending
});
}}
>
<div className="flex flex-row">
Torrent Name
{getArrowIcon(SortColumn.TorrentName)}
</div>
</th>
</tr>
</thead>
<tbody>
{visibleSegments.map((peer) => {
return (
<tr
key={uniqueId()}
className="border-b"
>
<td className="px-4 py-2">{peer.url}</td>
<td className="px-4 py-2">{multipleBytes(peer.downloadRate)}</td>
<td className="px-4 py-2">{peer.remoteAddr}</td>
<td className="px-4 py-2">{bytesToString(peer.peerId)}</td>
<td className="px-4 py-2">{peer.piecesCount}</td>
<td className="px-4 py-2">{peer.torrentName}</td>
</tr>
);
})}
</tbody>
</table>
</div>
);

function uniqueId() {
return Math.random().toString(36).substr(2, 9);
}
};
15 changes: 15 additions & 0 deletions src/app/pages/NetworkDownloaderPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import { FlagsDetailsTable } from "../components/Flags/FlagsDetailsTable";
import { downloaderFlags } from "../components/Flags/flagConstants";
import { selectNetworkSpeedIssues } from "../store/issuesSlice";
import { useNavigate } from "react-router-dom";
import { TorrentPeersDetailsPopup } from "../components/SyncStages/TorrentPeersDetailsPopup";

export const NetworkDownloaderPage = () => {
const syncStatus = useSelector(selectSnapshotDownloadStatusesForNode);
const [showDetails, setShowDetails] = useState(false);
const [showPeerDetail, setShowPeerDetails] = useState(false);
const allFlags = useSelector(selectFlagsForNode);
const issues = useSelector(selectNetworkSpeedIssues);
const navigate = useNavigate();
Expand Down Expand Up @@ -101,6 +103,12 @@ export const NetworkDownloaderPage = () => {
}
</tbody>
</table>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={() => setShowPeerDetails(true)}
>
{"Show Peers"}
</button>
<div className="flex flex-col">
<p className="font-bold mt-5">Flags related to downloader</p>
<FlagsDetailsTable flags={flags} />
Expand All @@ -112,6 +120,13 @@ export const NetworkDownloaderPage = () => {
}}
/>
)}
{showPeerDetail && (
<TorrentPeersDetailsPopup
onClose={() => {
setShowPeerDetails(false);
}}
/>
)}
</div>
);
};
4 changes: 2 additions & 2 deletions src/app/store/connectionSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ const initialState: ConnectionState = {
isConnectedToInternet: true,
isConnectedToNode: true,
nodeConnectionType: NodeConnectionType.Unknown,
backendAddress: window.location.origin
//backendAddress: "http://localhost:8080"
//backendAddress: window.location.origin
backendAddress: "http://localhost:8080"
};

export const connectionSlice = createSlice({
Expand Down
Loading

0 comments on commit eb148bb

Please sign in to comment.