The UI looks good now
This commit is contained in:
parent
9704597804
commit
27e8c12cfe
1 changed files with 55 additions and 37 deletions
|
|
@ -93,41 +93,56 @@ interface TorrentStats {
|
||||||
};
|
};
|
||||||
time_remaining: {
|
time_remaining: {
|
||||||
human_readable: string;
|
human_readable: string;
|
||||||
|
duration: {
|
||||||
|
secs: number,
|
||||||
|
}
|
||||||
} | null;
|
} | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TorrentRow: React.FC<{
|
const TorrentRow: React.FC<{
|
||||||
detailsResponse: TorrentDetails, statsResponse: TorrentStats
|
id: number, detailsResponse: TorrentDetails, statsResponse: TorrentStats
|
||||||
}> = ({ detailsResponse, statsResponse }) => {
|
}> = ({ id, detailsResponse, statsResponse }) => {
|
||||||
const totalBytes = statsResponse.snapshot.total_bytes;
|
const totalBytes = statsResponse.snapshot.total_bytes;
|
||||||
const downloadedBytes = statsResponse.snapshot.have_bytes;
|
const downloadedBytes = statsResponse.snapshot.have_bytes;
|
||||||
const finished = totalBytes == downloadedBytes;
|
const finished = totalBytes == downloadedBytes;
|
||||||
const downloadPercentage = (downloadedBytes / totalBytes) * 100;
|
const downloadPercentage = (downloadedBytes / totalBytes) * 100;
|
||||||
|
|
||||||
|
let classes = [
|
||||||
|
];
|
||||||
|
if (id % 2 == 0) {
|
||||||
|
classes.push('bg-light');
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<tr className="torrent-row">
|
<Row className={classes.join(' ')}>
|
||||||
<Column label="Name">{getLargestFileName(detailsResponse)}</Column>
|
<Column size={4} label="Name">
|
||||||
<Column label="Size">{`${formatBytes(totalBytes)}`}</Column>
|
<div className='text-truncate'>
|
||||||
<Column label="Progress">
|
{getLargestFileName(detailsResponse)}
|
||||||
<ProgressBar now={downloadPercentage} label={`${downloadPercentage.toFixed(2)}%`} animated={!finished} style={{ 'minWidth': '200px' }} />
|
</div>
|
||||||
</Column>
|
</Column>
|
||||||
<Column label="Download Speed">{statsResponse.download_speed.human_readable}</Column>
|
<Column label="Size">{`${formatBytes(totalBytes)}`}</Column>
|
||||||
|
<Column size={2} label="Progress">
|
||||||
|
<ProgressBar now={downloadPercentage} label={`${downloadPercentage.toFixed(2)}%`} animated={!finished} />
|
||||||
|
</Column>
|
||||||
|
<Column size={2} label="Down Speed">{statsResponse.download_speed.human_readable}</Column>
|
||||||
<Column label="ETA">{getCompletionETA(statsResponse)}</Column>
|
<Column label="ETA">{getCompletionETA(statsResponse)}</Column>
|
||||||
<Column label="Peers">{`${statsResponse.snapshot.peer_stats.live} / ${statsResponse.snapshot.peer_stats.seen}`}</Column>
|
<Column size={2} label="Peers">{`${statsResponse.snapshot.peer_stats.live} / ${statsResponse.snapshot.peer_stats.seen}`}</Column>
|
||||||
</tr >
|
</Row>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Column: React.FC<{
|
const Column: React.FC<{
|
||||||
label: string,
|
label: string,
|
||||||
|
size?: number,
|
||||||
children?: any
|
children?: any
|
||||||
}> = ({ children }) => (
|
}> = ({ size, label, children }) => (
|
||||||
<td className='me-3 p-3'>
|
<Col md={size || 1} className='py-3'>
|
||||||
|
<div className='fw-bold'>{label}</div>
|
||||||
{children}
|
{children}
|
||||||
</td>
|
</Col>
|
||||||
);
|
);
|
||||||
|
|
||||||
const Torrent = ({ torrent }) => {
|
const Torrent = ({ id, torrent }) => {
|
||||||
const defaultDetails: TorrentDetails = {
|
const defaultDetails: TorrentDetails = {
|
||||||
info_hash: '',
|
info_hash: '',
|
||||||
files: []
|
files: []
|
||||||
|
|
@ -202,7 +217,7 @@ const Torrent = ({ torrent }) => {
|
||||||
return clear;
|
return clear;
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <TorrentRow detailsResponse={detailsResponse} statsResponse={statsResponse} />
|
return <TorrentRow id={id} detailsResponse={detailsResponse} statsResponse={statsResponse} />
|
||||||
}
|
}
|
||||||
|
|
||||||
const TorrentsList = (props: { torrents: Array<TorrentId>, loading: boolean }) => {
|
const TorrentsList = (props: { torrents: Array<TorrentId>, loading: boolean }) => {
|
||||||
|
|
@ -222,24 +237,11 @@ const TorrentsList = (props: { torrents: Array<TorrentId>, loading: boolean }) =
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Table className='text-center table-striped table-hover table-borderless'>
|
<>
|
||||||
<thead>
|
{props.torrents.map((t: TorrentId) =>
|
||||||
<tr>
|
<Torrent id={t.id} key={t.id} torrent={t} />
|
||||||
<th>Name</th>
|
)}
|
||||||
<th>Size</th>
|
</>
|
||||||
<th>Progress</th>
|
|
||||||
<th>Download Speed</th>
|
|
||||||
<th>ETA</th>
|
|
||||||
<th>Peers</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{props.torrents.map((t: TorrentId) =>
|
|
||||||
<Torrent key={t.id} torrent={t} />
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
</Table>
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -334,14 +336,14 @@ const Root = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return <AppContext.Provider value={context}>
|
return <AppContext.Provider value={context}>
|
||||||
<Container className='text-center'>
|
<div className='text-center'>
|
||||||
<h1 className="mt-3 mb-4">rqbit web 0.0.1-alpha</h1>
|
<h1 className="mt-3 mb-4">rqbit web 0.0.1-alpha</h1>
|
||||||
<RootContent
|
<RootContent
|
||||||
closeableError={closeableError}
|
closeableError={closeableError}
|
||||||
otherError={otherError}
|
otherError={otherError}
|
||||||
torrents={torrents}
|
torrents={torrents}
|
||||||
torrentsLoading={torrentsLoading} />
|
torrentsLoading={torrentsLoading} />
|
||||||
</Container>
|
</div>
|
||||||
</AppContext.Provider >
|
</AppContext.Provider >
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -608,13 +610,29 @@ function getLargestFileName(torrentDetails: TorrentDetails): string {
|
||||||
|
|
||||||
// Function to get the completion ETA of a torrent
|
// Function to get the completion ETA of a torrent
|
||||||
function getCompletionETA(stats: TorrentStats): string {
|
function getCompletionETA(stats: TorrentStats): string {
|
||||||
if (stats.time_remaining) {
|
if (stats.time_remaining && stats.time_remaining.duration) {
|
||||||
return stats.time_remaining.human_readable;
|
return formatSecondsToTime(stats.time_remaining.duration.secs);
|
||||||
} else {
|
} else {
|
||||||
return 'N/A';
|
return 'N/A';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatSecondsToTime(seconds: number) {
|
||||||
|
const hours = Math.floor(seconds / 3600);
|
||||||
|
const minutes = Math.floor((seconds % 3600) / 60);
|
||||||
|
const remainingSeconds = seconds % 60;
|
||||||
|
|
||||||
|
const formatUnit = (value, unit) => (value > 0 ? `${value}${unit}` : '');
|
||||||
|
|
||||||
|
if (hours > 0) {
|
||||||
|
return `${formatUnit(hours, 'h')} ${formatUnit(minutes, 'm')}`.trim();
|
||||||
|
} else if (minutes > 0) {
|
||||||
|
return `${formatUnit(minutes, 'm')} ${formatUnit(remainingSeconds, 's')}`.trim();
|
||||||
|
} else {
|
||||||
|
return `${formatUnit(remainingSeconds, 's')}`.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function customSetInterval(asyncCallback: any, interval: number) {
|
function customSetInterval(asyncCallback: any, interval: number) {
|
||||||
let timeoutId: number;
|
let timeoutId: number;
|
||||||
let currentInterval: number = interval;
|
let currentInterval: number = interval;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue