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:
CyferShepard
2024-10-15 00:03:49 +02:00
parent 939526015d
commit c88c23cbac
9 changed files with 28 additions and 285 deletions

View File

@@ -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" />} />}

View File

@@ -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">

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View File

@@ -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;

View File

@@ -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;

View File

@@ -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">