🔀 Merge pull request #195 from LarveyOfficial/patch-3

More Information in Torrents Module
This commit is contained in:
Thomas Camlong
2022-06-12 15:39:32 +02:00
committed by GitHub
6 changed files with 95 additions and 46 deletions

View File

@@ -155,8 +155,9 @@ const AppShelf = (props: any) => {
</Accordion.Item> </Accordion.Item>
) : null} ) : null}
<Accordion.Item key="Downloads" label="Your downloads"> <Accordion.Item key="Downloads" label="Your downloads">
<ModuleMenu module={DownloadsModule} />
<Paper <Paper
p="lg"
radius="lg"
style={{ style={{
background: `rgba(${colorScheme === 'dark' ? '37, 38, 43,' : '255, 255, 255,'} \ background: `rgba(${colorScheme === 'dark' ? '37, 38, 43,' : '255, 255, 255,'} \
${(config.settings.appOpacity || 100) / 100}`, ${(config.settings.appOpacity || 100) / 100}`,
@@ -164,6 +165,7 @@ const AppShelf = (props: any) => {
${(config.settings.appOpacity || 100) / 100}`, ${(config.settings.appOpacity || 100) / 100}`,
}} }}
> >
<ModuleMenu module={DownloadsModule} />
<DownloadComponent /> <DownloadComponent />
</Paper> </Paper>
</Accordion.Item> </Accordion.Item>

View File

@@ -11,7 +11,7 @@ function SettingsMenu(props: any) {
<Tabs.Tab data-autofocus label="Common"> <Tabs.Tab data-autofocus label="Common">
<CommonSettings /> <CommonSettings />
</Tabs.Tab> </Tabs.Tab>
<Tabs.Tab label="Customisations"> <Tabs.Tab label="Customizations">
<AdvancedSettings /> <AdvancedSettings />
</Tabs.Tab> </Tabs.Tab>
</Tabs> </Tabs>

View File

@@ -1,5 +1,13 @@
/* eslint-disable react/no-children-prop */ /* eslint-disable react/no-children-prop */
import { Box, Divider, Indicator, Popover, ScrollArea, createStyles, useMantineTheme } from '@mantine/core'; import {
Box,
Divider,
Indicator,
Popover,
ScrollArea,
createStyles,
useMantineTheme,
} from '@mantine/core';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { Calendar } from '@mantine/dates'; import { Calendar } from '@mantine/dates';
import { IconCalendar as CalendarIcon } from '@tabler/icons'; import { IconCalendar as CalendarIcon } from '@tabler/icons';
@@ -101,12 +109,13 @@ export default function CalendarComponent(props: any) {
onChange={(day: any) => {}} onChange={(day: any) => {}}
dayStyle={(date) => dayStyle={(date) =>
date.getDay() === today.getDay() && date.getDate() === today.getDate() date.getDay() === today.getDay() && date.getDate() === today.getDate()
? { backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[0] } ? {
backgroundColor:
theme.colorScheme === 'dark' ? theme.colors.dark[5] : theme.colors.gray[0],
}
: {} : {}
} }
dayClassName={(date, modifiers) => dayClassName={(date, modifiers) => cx({ [classes.weekend]: modifiers.weekend })}
cx({ [classes.weekend]: modifiers.weekend })
}
renderDay={(renderdate) => ( renderDay={(renderdate) => (
<DayComponent <DayComponent
renderdate={renderdate} renderdate={renderdate}

View File

@@ -14,10 +14,12 @@ import { IconDownload as Download } from '@tabler/icons';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import axios from 'axios'; import axios from 'axios';
import { NormalizedTorrent } from '@ctrl/shared-torrent'; import { NormalizedTorrent } from '@ctrl/shared-torrent';
import { useViewportSize } from '@mantine/hooks';
import { IModule } from '../modules'; import { IModule } from '../modules';
import { useConfig } from '../../../tools/state'; import { useConfig } from '../../../tools/state';
import { AddItemShelfButton } from '../../AppShelf/AddAppShelfItem'; import { AddItemShelfButton } from '../../AppShelf/AddAppShelfItem';
import { useSetSafeInterval } from '../../../tools/hooks/useSetSafeInterval'; import { useSetSafeInterval } from '../../../tools/hooks/useSetSafeInterval';
import { humanFileSize } from '../../../tools/humanFileSize';
export const DownloadsModule: IModule = { export const DownloadsModule: IModule = {
title: 'Torrent', title: 'Torrent',
@@ -34,6 +36,7 @@ export const DownloadsModule: IModule = {
export default function DownloadComponent() { export default function DownloadComponent() {
const { config } = useConfig(); const { config } = useConfig();
const { height, width } = useViewportSize();
const downloadServices = const downloadServices =
config.services.filter( config.services.filter(
(service) => (service) =>
@@ -81,21 +84,40 @@ export default function DownloadComponent() {
</> </>
); );
} }
const DEVICE_WIDTH = 576;
const ths = ( const ths = (
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>Download</th> <th>Size</th>
<th>Upload</th> {width > 576 ? <th>Down</th> : ''}
{width > 576 ? <th>Up</th> : ''}
<th>ETA</th>
<th>Progress</th> <th>Progress</th>
</tr> </tr>
); );
// Convert Seconds to readable format.
function calculateETA(givenSeconds: number) {
// If its superior than one day return > 1 day
if (givenSeconds > 86400) {
return '> 1 day';
}
// Transform the givenSeconds into a readable format. e.g. 1h 2m 3s
const hours = Math.floor(givenSeconds / 3600);
const minutes = Math.floor((givenSeconds % 3600) / 60);
const seconds = Math.floor(givenSeconds % 60);
// Only show hours if it's greater than 0.
const hoursString = hours > 0 ? `${hours}h ` : '';
const minutesString = minutes > 0 ? `${minutes}m ` : '';
const secondsString = seconds > 0 ? `${seconds}s` : '';
return `${hoursString}${minutesString}${secondsString}`;
}
// Loop over qBittorrent torrents merging with deluge torrents // Loop over qBittorrent torrents merging with deluge torrents
const rows = torrents const rows = torrents
.filter((torrent) => !(torrent.progress === 1 && hideComplete)) .filter((torrent) => !(torrent.progress === 1 && hideComplete))
.map((torrent) => { .map((torrent) => {
const downloadSpeed = torrent.downloadSpeed / 1024 / 1024; const downloadSpeed = torrent.downloadSpeed / 1024 / 1024;
const uploadSpeed = torrent.uploadSpeed / 1024 / 1024; const uploadSpeed = torrent.uploadSpeed / 1024 / 1024;
const size = torrent.totalSelected;
return ( return (
<tr key={torrent.id}> <tr key={torrent.id}>
<td> <td>
@@ -111,17 +133,33 @@ export default function DownloadComponent() {
</Text> </Text>
</Tooltip> </Tooltip>
</td> </td>
<td>
<Text size="xs">{humanFileSize(size)}</Text>
</td>
{width > 576 ? (
<td> <td>
<Text size="xs">{downloadSpeed > 0 ? `${downloadSpeed.toFixed(1)} Mb/s` : '-'}</Text> <Text size="xs">{downloadSpeed > 0 ? `${downloadSpeed.toFixed(1)} Mb/s` : '-'}</Text>
</td> </td>
) : (
''
)}
{width > 576 ? (
<td> <td>
<Text size="xs">{uploadSpeed > 0 ? `${uploadSpeed.toFixed(1)} Mb/s` : '-'}</Text> <Text size="xs">{uploadSpeed > 0 ? `${uploadSpeed.toFixed(1)} Mb/s` : '-'}</Text>
</td> </td>
) : (
''
)}
<td>
<Text size="xs">{torrent.eta <= 0 ? '∞' : calculateETA(torrent.eta)}</Text>
</td>
<td> <td>
<Text>{(torrent.progress * 100).toFixed(1)}%</Text> <Text>{(torrent.progress * 100).toFixed(1)}%</Text>
<Progress <Progress
radius="lg" radius="lg"
color={torrent.progress === 1 ? 'green' : 'blue'} color={
torrent.state === 'paused' ? 'yellow' : torrent.progress === 1 ? 'green' : 'blue'
}
value={torrent.progress * 100} value={torrent.progress * 100}
size="lg" size="lg"
/> />

View File

@@ -8,41 +8,10 @@ import { Datum, ResponsiveLine } from '@nivo/line';
import { useListState } from '@mantine/hooks'; import { useListState } from '@mantine/hooks';
import { AddItemShelfButton } from '../../AppShelf/AddAppShelfItem'; import { AddItemShelfButton } from '../../AppShelf/AddAppShelfItem';
import { useConfig } from '../../../tools/state'; import { useConfig } from '../../../tools/state';
import { humanFileSize } from '../../../tools/humanFileSize';
import { IModule } from '../modules'; import { IModule } from '../modules';
import { useSetSafeInterval } from '../../../tools/hooks/useSetSafeInterval'; import { useSetSafeInterval } from '../../../tools/hooks/useSetSafeInterval';
/**
* Format bytes as human-readable text.
*
* @param bytes Number of bytes.
* @param si True to use metric (SI) units, aka powers of 1000. False to use
* binary (IEC), aka powers of 1024.
* @param dp Number of decimal places to display.
*
* @return Formatted string.
*/
function humanFileSize(initialBytes: number, si = true, dp = 1) {
const thresh = si ? 1000 : 1024;
let bytes = initialBytes;
if (Math.abs(bytes) < thresh) {
return `${bytes} B`;
}
const units = si
? ['kb', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
let u = -1;
const r = 10 ** dp;
do {
bytes /= thresh;
u += 1;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return `${bytes.toFixed(dp)} ${units[u]}`;
}
export const TotalDownloadsModule: IModule = { export const TotalDownloadsModule: IModule = {
title: 'Download Speed', title: 'Download Speed',
description: 'Show the current download speed of supported services', description: 'Show the current download speed of supported services',

View File

@@ -0,0 +1,31 @@
/**
* Format bytes as human-readable text.
*
* @param bytes Number of bytes.
* @param si True to use metric (SI) units, aka powers of 1000. False to use
* binary (IEC), aka powers of 1024.
* @param dp Number of decimal places to display.
*
* @return Formatted string.
*/
export function humanFileSize(initialBytes: number, si = true, dp = 1) {
const thresh = si ? 1000 : 1024;
let bytes = initialBytes;
if (Math.abs(bytes) < thresh) {
return `${bytes} B`;
}
const units = si
? ['kb', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
let u = -1;
const r = 10 ** dp;
do {
bytes /= thresh;
u += 1;
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
return `${bytes.toFixed(dp)} ${units[u]}`;
}