This commit is contained in:
Francisco Coelho 2022-09-12 09:37:42 +01:00
commit 0c9c1c599f
27 changed files with 456 additions and 244 deletions

View file

@ -0,0 +1,64 @@
import useSWR from "swr";
import { useTranslation } from "react-i18next";
import getSymbolFromCurrency from "currency-symbol-map";
import Widget from "../widget";
import Block from "../block";
import { formatApiUrl } from "utils/api-helpers";
export default function CoinMarketCap({ service }) {
const { t } = useTranslation();
const config = service.widget;
const symbols = [...service.symbols];
const currencyCode = service.currency ?? "USD";
const { data: statsData, error: statsError } = useSWR(
formatApiUrl(config, `v1/cryptocurrency/quotes/latest?symbol=${symbols.join(",")}&convert=${currencyCode}`)
);
if (!symbols || symbols.length === 0) {
return (
<Widget>
<Block value={t("coinmarketcap.configure")} />
</Widget>
);
}
if (statsError) {
return <Widget error={t("widget.api_error")} />;
}
if (!statsData) {
return (
<Widget>
<Block value={t("coinmarketcap.configure")} />
</Widget>
);
}
const { data } = statsData;
const currencySymbol = getSymbolFromCurrency(currencyCode);
return symbols.map((key) => (
<Widget key={data[key].symbol}>
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-row items-center justify-between p-1">
<div className="font-thin text-sm">{data[key].name}</div>
<div className="flex flex-col text-right">
<div className="font-bold text-xs">
{currencySymbol}
{data[key].quote[currencyCode].price.toFixed(2)}
</div>
<div
className={`font-bold text-xs ${
data[key].quote[currencyCode].percent_change_1h > 0 ? "text-emerald-300" : "text-rose-300"
}`}
>
{data[key].quote[currencyCode].percent_change_1h.toFixed(2)}%
</div>
</div>
</div>
</Widget>
));
}

View file

@ -27,6 +27,7 @@ function ticksToString(ticks) {
}
function SingleSessionEntry({ playCommand, session }) {
console.log(session);
const {
NowPlayingItem: { Name, SeriesName, RunTimeTicks },
PlayState: { PositionTicks, IsPaused, IsMuted },
@ -175,7 +176,7 @@ export default function Emby({ service }) {
if (playing.length === 0) {
return (
<div className="flex flex-col pb-1">
<div className="flex flex-col pb-1 mx-1">
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
<span className="absolute left-2 text-xs mt-[2px]">{t("emby.no_active")}</span>
</div>
@ -189,7 +190,7 @@ export default function Emby({ service }) {
if (playing.length === 1) {
const session = playing[0];
return (
<div className="flex flex-col pb-1">
<div className="flex flex-col pb-1 mx-1">
<SingleSessionEntry
playCommand={(currentSession, command) => handlePlayCommand(currentSession, command)}
session={session}
@ -199,7 +200,7 @@ export default function Emby({ service }) {
}
return (
<div className="flex flex-col pb-1">
<div className="flex flex-col pb-1 mx-1">
{playing.map((session) => (
<SessionEntry
key={session.Id}

View file

@ -0,0 +1,41 @@
import useSWR from "swr";
import { useTranslation } from "react-i18next";
import Widget from "../widget";
import Block from "../block";
import { formatApiUrl } from "utils/api-helpers";
export default function Readarr({ service }) {
const { t } = useTranslation();
const config = service.widget;
const { data: booksData, error: booksError } = useSWR(formatApiUrl(config, "book"));
const { data: wantedData, error: wantedError } = useSWR(formatApiUrl(config, "wanted/missing"));
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue/status"));
if (booksError || wantedError || queueError) {
return <Widget error={t("widget.api_error")} />;
}
if (!booksData || !wantedData || !queueData) {
return (
<Widget>
<Block label={t("readarr.wanted")} />
<Block label={t("readarr.queued")} />
<Block label={t("readarr.books")} />
</Widget>
);
}
const have = booksData.filter((book) => book.statistics.bookFileCount > 0);
return (
<Widget>
<Block label={t("readarr.wanted")} value={wantedData.totalRecords} />
<Block label={t("readarr.queued")} value={queueData.totalCount} />
<Block label={t("readarr.books")} value={have.length} />
</Widget>
);
}

View file

@ -6,22 +6,22 @@ import Block from "../block";
import { formatApiUrl } from "utils/api-helpers";
export default function Sabnzbd({ service }) {
const { t } = useTranslation("common");
export default function SABnzbd({ service }) {
const { t } = useTranslation();
const config = service.widget;
const { data: statusData, error: statusError } = useSWR(formatApiUrl(config, "mode=queue"));
if (statusError) {
const { data: queueData, error: queueError } = useSWR(formatApiUrl(config, "queue"));
if (queueError) {
return <Widget error={t("widget.api_error")} />;
}
if (!statusData) {
if (!queueData) {
return (
<Widget>
<Block label={t("sabnzbd.status")} />
<Block label={t("sabnzbd.speed")} />
<Block label={t("sabnzbd.remaining")} />
<Block label={t("sabnzbd.rate")} />
<Block label={t("sabnzbd.queue")} />
<Block label={t("sabnzbd.timeleft")} />
</Widget>
);
@ -29,13 +29,9 @@ export default function Sabnzbd({ service }) {
return (
<Widget>
<Block label={t("sabnzbd.status")} value={statusData?.queue?.status } />
<Block label={t("sabnzbd.speed")} value={statusData?.queue?.speed } />
<Block
label={t("sabnzbd.remaining")}
value={t("common.bytes", { value: statusData?.queue?.mbleft * 1024 * 1024 })}
/>
<Block label={t("sabnzbd.timeleft")} value={statusData?.queue?.timeleft} />
<Block label={t("sabnzbd.rate")} value={`${queueData.queue.speed}bps`} />
<Block label={t("sabnzbd.queue")} value={queueData.queue.noofslots} />
<Block label={t("sabnzbd.timeleft")} value={queueData.queue.timeleft} />
</Widget>
);
}

View file

@ -104,7 +104,7 @@ export default function Tautulli({ service }) {
if (!activityData) {
return (
<div className="flex flex-col pb-1">
<div className="flex flex-col pb-1 mx-1">
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
<span className="absolute left-2 text-xs mt-[2px]">-</span>
</div>
@ -127,7 +127,7 @@ export default function Tautulli({ service }) {
if (playing.length === 0) {
return (
<div className="flex flex-col pb-1">
<div className="flex flex-col pb-1 mx-1">
<div className="text-theme-700 dark:text-theme-200 text-xs relative h-5 w-full rounded-md bg-theme-200/50 dark:bg-theme-900/20 mt-1">
<span className="absolute left-2 text-xs mt-[2px]">{t("tautulli.no_active")}</span>
</div>
@ -141,14 +141,14 @@ export default function Tautulli({ service }) {
if (playing.length === 1) {
const session = playing[0];
return (
<div className="flex flex-col pb-1">
<div className="flex flex-col pb-1 mx-1">
<SingleSessionEntry session={session} />
</div>
);
}
return (
<div className="flex flex-col pb-1">
<div className="flex flex-col pb-1 mx-1">
{playing.map((session) => (
<SessionEntry key={session.Id} session={session} />
))}