mirror of
https://github.com/BreizhHardware/Jellystat.git
synced 2026-01-18 16:27:20 +01:00
centeralized global stat component to be reconfigured and used in multiple places instead of having multiple copies of the same component
This commit is contained in:
@@ -21,10 +21,10 @@ function GlobalStats(props) {
|
||||
function fetchStats(hours = 24, setMethod = setDayStats) {
|
||||
axios
|
||||
.post(
|
||||
`/stats/getGlobalUserStats`,
|
||||
`/stats/${props.endpoint ?? "getGlobalUserStats"}`,
|
||||
{
|
||||
hours: hours,
|
||||
userid: props.UserId,
|
||||
[props.param]: props.id,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -55,13 +55,11 @@ function GlobalStats(props) {
|
||||
fetchData();
|
||||
const intervalId = setInterval(fetchData, 60000 * 5);
|
||||
return () => clearInterval(intervalId);
|
||||
}, [props.UserId, token]);
|
||||
}, [props.id, token]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="py-3">
|
||||
<Trans i18nKey="USERS_PAGE.USER_STATS" />
|
||||
</h1>
|
||||
<h1 className="py-3">{props.title}</h1>
|
||||
<div className="global-stats-container">
|
||||
{!prefs.includes("1") && <WatchTimeStats data={dayStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_24_HRS" />} />}
|
||||
{!prefs.includes("7") && <WatchTimeStats data={weekStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_7_DAYS" />} />}
|
||||
@@ -10,7 +10,6 @@ import ExternalLinkFillIcon from "remixicon-react/ExternalLinkFillIcon";
|
||||
import ArchiveDrawerFillIcon from "remixicon-react/ArchiveDrawerFillIcon";
|
||||
import ArrowLeftSLineIcon from "remixicon-react/ArrowLeftSLineIcon";
|
||||
|
||||
import GlobalStats from "./item-info/globalStats";
|
||||
import "../css/items/item-details.css";
|
||||
|
||||
import MoreItems from "./item-info/more-items";
|
||||
@@ -27,6 +26,7 @@ import FilmLineIcon from "remixicon-react/FilmLineIcon";
|
||||
import FileMusicLineIcon from "remixicon-react/FileMusicLineIcon";
|
||||
import CheckboxMultipleBlankLineIcon from "remixicon-react/CheckboxMultipleBlankLineIcon";
|
||||
import baseUrl from "../../lib/baseurl";
|
||||
import GlobalStats from "./general/globalStats";
|
||||
|
||||
function ItemInfo() {
|
||||
const { Id } = useParams();
|
||||
@@ -208,7 +208,8 @@ function ItemInfo() {
|
||||
<img
|
||||
className="item-image"
|
||||
src={
|
||||
baseUrl+"/proxy/Items/Images/Primary?id=" +
|
||||
baseUrl +
|
||||
"/proxy/Items/Images/Primary?id=" +
|
||||
(["Episode", "Season"].includes(data.Type) ? data.SeriesId : data.Id) +
|
||||
"&fillWidth=200&quality=90"
|
||||
}
|
||||
@@ -347,7 +348,12 @@ function ItemInfo() {
|
||||
|
||||
<Tabs defaultActiveKey="tabOverview" activeKey={activeTab} variant="pills" className="hide-tab-titles">
|
||||
<Tab eventKey="tabOverview" title="Overview" className="bg-transparent">
|
||||
<GlobalStats ItemId={Id} />
|
||||
<GlobalStats
|
||||
id={Id}
|
||||
param={"itemid"}
|
||||
endpoint={"getGlobalItemStats"}
|
||||
title={<Trans i18nKey="GLOBAL_STATS.ITEM_STATS" />}
|
||||
/>
|
||||
{["Series", "Season"].includes(data && data.Type) ? <MoreItems data={data} /> : <></>}
|
||||
</Tab>
|
||||
<Tab eventKey="tabActivity" title="Activity" className="bg-transparent">
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import "../../css/globalstats.css";
|
||||
|
||||
import WatchTimeStats from "./globalstats/watchtimestats";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
function GlobalStats(props) {
|
||||
const [dayStats, setDayStats] = useState({});
|
||||
const [weekStats, setWeekStats] = useState({});
|
||||
const [monthStats, setMonthStats] = useState({});
|
||||
const [d180Stats, setd180Stats] = useState({});
|
||||
const [d365Stats, setd365Stats] = useState({});
|
||||
const [allStats, setAllStats] = useState({});
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
function fetchStats(hours = 24, setMethod = setDayStats) {
|
||||
axios
|
||||
.post(
|
||||
`/stats/getGlobalItemStats`,
|
||||
{
|
||||
hours: hours,
|
||||
itemid: props.ItemId,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((dayData) => {
|
||||
setMethod(dayData.data);
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
fetchStats(24, setDayStats);
|
||||
fetchStats(24 * 7, setWeekStats);
|
||||
fetchStats(24 * 30, setMonthStats);
|
||||
fetchStats(24 * 180, setd180Stats);
|
||||
fetchStats(24 * 365, setd365Stats);
|
||||
fetchStats(24 * 999, setAllStats);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
const intervalId = setInterval(fetchData, 60000 * 5);
|
||||
return () => clearInterval(intervalId);
|
||||
}, [props.ItemId, token]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="py-3">
|
||||
<Trans i18nKey="GLOBAL_STATS.ITEM_STATS" />
|
||||
</h1>
|
||||
<div className="global-stats-container">
|
||||
<WatchTimeStats data={dayStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_24_HRS" />} />
|
||||
<WatchTimeStats data={weekStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_7_DAYS" />} />
|
||||
<WatchTimeStats data={monthStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_30_DAYS" />} />
|
||||
<WatchTimeStats data={d180Stats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_180_DAYS" />} />
|
||||
<WatchTimeStats data={d365Stats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_365_DAYS" />} />
|
||||
<WatchTimeStats data={allStats} heading={<Trans i18nKey="GLOBAL_STATS.ALL_TIME" />} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default GlobalStats;
|
||||
@@ -1,61 +0,0 @@
|
||||
import "../../../css/globalstats.css";
|
||||
import i18next from "i18next";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
function WatchTimeStats(props) {
|
||||
function formatTime(totalSeconds, numberClassName, labelClassName) {
|
||||
const units = [
|
||||
{ label: "UNITS.DAY", seconds: 86400 },
|
||||
{ label: "UNITS.HOUR", seconds: 3600 },
|
||||
{ label: "UNITS.MINUTE", seconds: 60 },
|
||||
];
|
||||
|
||||
const parts = units.reduce((result, { label, seconds }) => {
|
||||
const value = Math.floor(totalSeconds / seconds);
|
||||
if (value) {
|
||||
const formattedValue = <p className={numberClassName}>{value}</p>;
|
||||
const labelPlural = label.toUpperCase() + "S";
|
||||
const formattedLabel = <span className={labelClassName}>{value === 1 ? i18next.t(label) : i18next.t(labelPlural)}</span>;
|
||||
result.push(
|
||||
<span key={label} className="time-part">
|
||||
{formattedValue} {formattedLabel}
|
||||
</span>
|
||||
);
|
||||
totalSeconds -= value * seconds;
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
if (parts.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<p className={numberClassName}>0</p>{" "}
|
||||
<p className={labelClassName}>
|
||||
<Trans i18nKey="UNITS.MINUTES" />
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="global-stats">
|
||||
<div className="stats-header">
|
||||
<div>{props.heading}</div>
|
||||
</div>
|
||||
|
||||
<div className="play-duration-stats" key={props.data.ItemId}>
|
||||
<p className="stat-value"> {props.data.Plays || 0}</p>
|
||||
<p className="stat-unit">
|
||||
<Trans i18nKey="UNITS.PLAYS" /> /
|
||||
</p>
|
||||
|
||||
<>{formatTime(props.data.total_playback_duration || 0, "stat-value", "stat-unit")}</>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WatchTimeStats;
|
||||
@@ -6,7 +6,6 @@ import FilmLineIcon from "remixicon-react/FilmLineIcon";
|
||||
|
||||
// import LibraryDetails from './library/library-details';
|
||||
import Loading from "./general/loading";
|
||||
import LibraryGlobalStats from "./library/library-stats";
|
||||
import LibraryLastWatched from "./library/last-watched";
|
||||
import RecentlyAdded from "./library/recently-added";
|
||||
import LibraryActivity from "./library/library-activity";
|
||||
@@ -16,6 +15,7 @@ import ErrorBoundary from "./general/ErrorBoundary";
|
||||
import { Tabs, Tab, Button, ButtonGroup } from "react-bootstrap";
|
||||
import { Trans } from "react-i18next";
|
||||
import LibraryOptions from "./library/library-options";
|
||||
import GlobalStats from "./general/globalStats";
|
||||
|
||||
function LibraryInfo() {
|
||||
const { LibraryId } = useParams();
|
||||
@@ -103,7 +103,12 @@ function LibraryInfo() {
|
||||
</div>
|
||||
<Tabs defaultActiveKey={activeTab} activeKey={activeTab} variant="pills" className="hide-tab-titles">
|
||||
<Tab eventKey="tabOverview" title="Overview" className="bg-transparent">
|
||||
<LibraryGlobalStats LibraryId={LibraryId} />
|
||||
<GlobalStats
|
||||
id={LibraryId}
|
||||
param={"libraryid"}
|
||||
endpoint={"getGlobalLibraryStats"}
|
||||
title={<Trans i18nKey="LIBRARY_INFO.LIBRARY_STATS" />}
|
||||
/>
|
||||
|
||||
{!data.archived && (
|
||||
<ErrorBoundary>
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
import "../../../css/globalstats.css";
|
||||
import i18next from "i18next";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
function WatchTimeStats(props) {
|
||||
|
||||
function formatTime(totalSeconds, numberClassName, labelClassName) {
|
||||
const units = [
|
||||
{ label: [i18next.t("UNITS.DAY"),i18next.t("UNITS.DAYS")], seconds: 86400 },
|
||||
{ label: [i18next.t("UNITS.HOUR"),i18next.t("UNITS.HOURS")], seconds: 3600 },
|
||||
{ label: [i18next.t("UNITS.MINUTE"),i18next.t("UNITS.MINUTES")], seconds: 60 },
|
||||
];
|
||||
|
||||
const parts = units.reduce((result, { label, seconds }) => {
|
||||
const value = Math.floor(totalSeconds / seconds);
|
||||
if (value) {
|
||||
const formattedValue = <p className={numberClassName}>{value}</p>;
|
||||
const formattedLabel = (
|
||||
<span className={labelClassName}>
|
||||
{value === 1 ? label[0] : label[1] }
|
||||
</span>
|
||||
);
|
||||
result.push(
|
||||
<span key={label} className="time-part">
|
||||
{formattedValue} {formattedLabel}
|
||||
</span>
|
||||
);
|
||||
totalSeconds -= value * seconds;
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
|
||||
if (parts.length === 0) {
|
||||
return (
|
||||
<>
|
||||
<p className={numberClassName}>0</p>{' '}
|
||||
<p className={labelClassName}><Trans i18nKey="UNITS.MINUTES"/></p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className="global-stats">
|
||||
<div className="stats-header">
|
||||
<div>{props.heading}</div>
|
||||
</div>
|
||||
|
||||
<div className="play-duration-stats" key={props.data.UserId}>
|
||||
<p className="stat-value"> {props.data.Plays || 0}</p>
|
||||
<p className="stat-unit" ><Trans i18nKey="UNITS.PLAYS"/> /</p>
|
||||
|
||||
<>{formatTime(props.data.total_playback_duration || 0,'stat-value','stat-unit')}</>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WatchTimeStats;
|
||||
@@ -1,73 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import axios from "../../../lib/axios_instance";
|
||||
import "../../css/globalstats.css";
|
||||
|
||||
import WatchTimeStats from "./globalstats/watchtimestats";
|
||||
import { Trans } from "react-i18next";
|
||||
|
||||
function LibraryGlobalStats(props) {
|
||||
const [dayStats, setDayStats] = useState({});
|
||||
const [weekStats, setWeekStats] = useState({});
|
||||
const [monthStats, setMonthStats] = useState({});
|
||||
const [d180Stats, setd180Stats] = useState({});
|
||||
const [d365Stats, setd365Stats] = useState({});
|
||||
const [allStats, setAllStats] = useState({});
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
function fetchStats(hours = 24, setMethod = setDayStats) {
|
||||
axios
|
||||
.post(
|
||||
`/stats/getGlobalLibraryStats`,
|
||||
{
|
||||
hours: hours,
|
||||
libraryid: props.LibraryId,
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
)
|
||||
.then((dayData) => {
|
||||
setMethod(dayData.data);
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
fetchStats(24, setDayStats);
|
||||
fetchStats(24 * 7, setWeekStats);
|
||||
fetchStats(24 * 30, setMonthStats);
|
||||
fetchStats(24 * 180, setd180Stats);
|
||||
fetchStats(24 * 365, setd365Stats);
|
||||
fetchStats(24 * 999, setAllStats);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
const intervalId = setInterval(fetchData, 60000 * 5);
|
||||
return () => clearInterval(intervalId);
|
||||
}, [props.LibraryId, token]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1 className="my-3">
|
||||
<Trans i18nKey="LIBRARY_INFO.LIBRARY_STATS" />
|
||||
</h1>
|
||||
<div className="global-stats-container">
|
||||
<WatchTimeStats data={dayStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_24_HRS" />} />
|
||||
<WatchTimeStats data={weekStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_7_DAYS" />} />
|
||||
<WatchTimeStats data={monthStats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_30_DAYS" />} />
|
||||
<WatchTimeStats data={d180Stats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_180_DAYS" />} />
|
||||
<WatchTimeStats data={d365Stats} heading={<Trans i18nKey="GLOBAL_STATS.LAST_365_DAYS" />} />
|
||||
<WatchTimeStats data={allStats} heading={<Trans i18nKey="GLOBAL_STATS.ALL_TIME" />} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LibraryGlobalStats;
|
||||
@@ -5,12 +5,12 @@ import AccountCircleFillIcon from "remixicon-react/AccountCircleFillIcon";
|
||||
import Config from "../../lib/config";
|
||||
import { Tabs, Tab, Button, ButtonGroup } from "react-bootstrap";
|
||||
|
||||
import GlobalStats from "./user-info/globalStats";
|
||||
import LastPlayed from "./user-info/lastplayed";
|
||||
import UserActivity from "./user-info/user-activity";
|
||||
import "../css/users/user-details.css";
|
||||
import { Trans } from "react-i18next";
|
||||
import baseUrl from "../../lib/baseurl";
|
||||
import GlobalStats from "./general/globalStats";
|
||||
|
||||
function UserInfo() {
|
||||
const { UserId } = useParams();
|
||||
@@ -77,7 +77,7 @@ function UserInfo() {
|
||||
) : (
|
||||
<img
|
||||
className="user-image"
|
||||
src={baseUrl+"/proxy/Users/Images/Primary?id=" + UserId + "&quality=100"}
|
||||
src={baseUrl + "/proxy/Users/Images/Primary?id=" + UserId + "&quality=100"}
|
||||
onError={handleImageError}
|
||||
alt=""
|
||||
></img>
|
||||
@@ -109,7 +109,12 @@ function UserInfo() {
|
||||
|
||||
<Tabs defaultActiveKey="tabOverview" activeKey={activeTab} variant="pills">
|
||||
<Tab eventKey="tabOverview" className="bg-transparent">
|
||||
<GlobalStats UserId={UserId} />
|
||||
<GlobalStats
|
||||
id={UserId}
|
||||
param={"UserId"}
|
||||
endpoint={"getGlobalUserStats"}
|
||||
title={<Trans i18nKey="USERS_PAGE.USER_STATS" />}
|
||||
/>
|
||||
<LastPlayed UserId={UserId} />
|
||||
</Tab>
|
||||
<Tab eventKey="tabActivity" className="bg-transparent">
|
||||
|
||||
Reference in New Issue
Block a user