feat: make all activity grids configurable

pull/23/head
Gabe Farrell 6 months ago
parent 7ed60b3785
commit 5a2cb437a1

@ -3,5 +3,12 @@
## Features ## Features
- Allow loading environment variables from files using the _FILE suffix (#20) - Allow loading environment variables from files using the _FILE suffix (#20)
## Enhancements
## Fixes ## Fixes
- Sub-second precision is stripped from incoming listens to ensure they can be deleted reliably - Sub-second precision is stripped from incoming listens to ensure they can be deleted reliably
## Updates
- Adjusted colors for the "Yuu" theme
- Themes now have a single source of truth in themes.css.ts
- Basline support for custom themes added

@ -45,13 +45,17 @@ export default function ActivityGrid({
albumId = 0, albumId = 0,
trackId = 0, trackId = 0,
configurable = false, configurable = false,
autoAdjust = false,
}: Props) { }: Props) {
const [color, setColor] = useState(getPrimaryColor()) const [color, setColor] = useState(getPrimaryColor())
const [stepState, setStep] = useState(step) const [stepState, setStep] = useState(step)
const [rangeState, setRange] = useState(range) const [rangeState, setRange] = useState(range)
// sometimes, a little bit of a lie for the sake of better design is necessary
if (rangeState === 365) {
setRange(rangeState - 1)
}
const { isPending, isError, data, error } = useQuery({ const { isPending, isError, data, error } = useQuery({
queryKey: [ queryKey: [
'listen-activity', 'listen-activity',
@ -111,25 +115,27 @@ export default function ActivityGrid({
const getDarkenAmount = (v: number, t: number): number => { const getDarkenAmount = (v: number, t: number): number => {
if (autoAdjust) { // really ugly way to just check if this is for all items and not a specific item.
// is it jsut better to just pass the target in as a var? probably.
const adjustment = artistId == albumId && albumId == trackId && trackId == 0 ? 10 : 1
// automatically adjust the target value based on step // automatically adjust the target value based on step
// the smartest way to do this would be to have the api return the // the smartest way to do this would be to have the api return the
// highest value in the range. too bad im not smart // highest value in the range. too bad im not smart
switch (stepState) { switch (stepState) {
case 'day': case 'day':
t = 10 t = 10 * adjustment
break; break;
case 'week': case 'week':
t = 20 t = 20 * adjustment
break; break;
case 'month': case 'month':
t = 50 t = 50 * adjustment
break; break;
case 'year': case 'year':
t = 100 t = 100 * adjustment
break; break;
} }
}
v = Math.min(v, t) v = Math.min(v, t)
if (theme === "pearl") { if (theme === "pearl") {
@ -142,7 +148,15 @@ export default function ActivityGrid({
} }
} }
return (<div className="flex flex-col items-start"> const CHUNK_SIZE = 26 * 7;
const chunks = [];
for (let i = 0; i < data.length; i += CHUNK_SIZE) {
chunks.push(data.slice(i, i + CHUNK_SIZE));
}
return (
<div className="flex flex-col items-start">
<h2>Activity</h2> <h2>Activity</h2>
{configurable ? ( {configurable ? (
<ActivityOptsSelector <ActivityOptsSelector
@ -151,11 +165,14 @@ export default function ActivityGrid({
stepSetter={setStep} stepSetter={setStep}
currentStep={stepState} currentStep={stepState}
/> />
) : ( ) : null}
''
)} {chunks.map((chunk, index) => (
<div className="w-auto grid grid-flow-col grid-rows-7 gap-[3px] md:gap-[5px]"> <div
{data.map((item) => ( key={index}
className="w-auto grid grid-flow-col grid-rows-7 gap-[3px] md:gap-[5px] mb-4"
>
{chunk.map((item) => (
<div <div
key={new Date(item.start_time).toString()} key={new Date(item.start_time).toString()}
className="w-[10px] sm:w-[12px] h-[10px] sm:h-[12px]" className="w-[10px] sm:w-[12px] h-[10px] sm:h-[12px]"
@ -174,13 +191,15 @@ export default function ActivityGrid({
? LightenDarkenColor(color, getDarkenAmount(item.listens, 100)) ? LightenDarkenColor(color, getDarkenAmount(item.listens, 100))
: 'var(--color-bg-secondary)', : 'var(--color-bg-secondary)',
}} }}
className={`w-[10px] sm:w-[12px] h-[10px] sm:h-[12px] rounded-[2px] md:rounded-[3px] ${item.listens > 0 ? '' : 'border-[0.5px] border-(--color-bg-tertiary)'}`} className={`w-[10px] sm:w-[12px] h-[10px] sm:h-[12px] rounded-[2px] md:rounded-[3px] ${
item.listens > 0 ? '' : 'border-[0.5px] border-(--color-bg-tertiary)'
}`}
></div> ></div>
</Popup> </Popup>
</div> </div>
))} ))}
</div> </div>
))}
</div> </div>
); );
} }

@ -1,4 +1,5 @@
import { useEffect } from "react"; import { ChevronDown, ChevronUp } from "lucide-react";
import { useEffect, useState } from "react";
interface Props { interface Props {
stepSetter: (value: string) => void; stepSetter: (value: string) => void;
@ -18,16 +19,15 @@ export default function ActivityOptsSelector({
const stepPeriods = ['day', 'week', 'month', 'year']; const stepPeriods = ['day', 'week', 'month', 'year'];
const rangePeriods = [105, 182, 365]; const rangePeriods = [105, 182, 365];
const stepDisplay = (str: string): string => { const [collapsed, setCollapsed] = useState(true);
return str.split('_').map(w =>
const stepDisplay = (str: string): string =>
str.split('_').map(w =>
w.split('').map((char, index) => w.split('').map((char, index) =>
index === 0 ? char.toUpperCase() : char).join('') index === 0 ? char.toUpperCase() : char).join('')
).join(' '); ).join(' ');
};
const rangeDisplay = (r: number): string => { const rangeDisplay = (r: number): string => `${r}`;
return `${r}`
}
const setStep = (val: string) => { const setStep = (val: string) => {
stepSetter(val); stepSetter(val);
@ -46,18 +46,23 @@ export default function ActivityOptsSelector({
useEffect(() => { useEffect(() => {
if (!disableCache) { if (!disableCache) {
const cachedRange = parseInt(localStorage.getItem('activity_range_' + window.location.pathname.split('/')[1]) ?? '35'); const cachedRange = parseInt(localStorage.getItem('activity_range_' + window.location.pathname.split('/')[1]) ?? '35');
if (cachedRange) { if (cachedRange) rangeSetter(cachedRange);
rangeSetter(cachedRange);
}
const cachedStep = localStorage.getItem('activity_step_' + window.location.pathname.split('/')[1]); const cachedStep = localStorage.getItem('activity_step_' + window.location.pathname.split('/')[1]);
if (cachedStep) { if (cachedStep) stepSetter(cachedStep);
stepSetter(cachedStep);
}
} }
}, []); }, []);
return ( return (
<div className="flex flex-col"> <div className="flex flex-col gap-2 relative">
<button
onClick={() => setCollapsed(!collapsed)}
className="text-sm underline self-start color-fg-secondary hover:color-fg transition absolute -top-9 left-20"
>
{collapsed ? <ChevronDown size={18} /> : <ChevronUp size={18} />}
</button>
{!collapsed && (
<>
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<p>Step:</p> <p>Step:</p>
{stepPeriods.map((p, i) => ( {stepPeriods.map((p, i) => (
@ -93,6 +98,8 @@ export default function ActivityOptsSelector({
</div> </div>
))} ))}
</div> </div>
</>
)}
</div> </div>
); );
} }

@ -26,7 +26,7 @@ export default function Home() {
<div className="flex-1 flex flex-col items-center gap-16 min-h-0 mt-20"> <div className="flex-1 flex flex-col items-center gap-16 min-h-0 mt-20">
<div className="flex flex-col md:flex-row gap-10 md:gap-20"> <div className="flex flex-col md:flex-row gap-10 md:gap-20">
<AllTimeStats /> <AllTimeStats />
<ActivityGrid /> <ActivityGrid configurable />
</div> </div>
<PeriodSelector setter={setPeriod} current={period} /> <PeriodSelector setter={setPeriod} current={period} />
<div className="flex flex-wrap gap-10 2xl:gap-20 xl:gap-10 justify-between mx-5 md:gap-5"> <div className="flex flex-wrap gap-10 2xl:gap-20 xl:gap-10 justify-between mx-5 md:gap-5">

@ -52,7 +52,7 @@ export default function Album() {
<div className="flex flex-wrap gap-20 mt-10"> <div className="flex flex-wrap gap-20 mt-10">
<LastPlays limit={30} albumId={album.id} /> <LastPlays limit={30} albumId={album.id} />
<TopTracks limit={12} period={period} albumId={album.id} /> <TopTracks limit={12} period={period} albumId={album.id} />
<ActivityGrid autoAdjust configurable albumId={album.id} /> <ActivityGrid configurable albumId={album.id} />
</div> </div>
</MediaLayout> </MediaLayout>
); );

@ -59,7 +59,7 @@ export default function Artist() {
<div className="flex gap-15 mt-10 flex-wrap"> <div className="flex gap-15 mt-10 flex-wrap">
<LastPlays limit={20} artistId={artist.id} /> <LastPlays limit={20} artistId={artist.id} />
<TopTracks limit={8} period={period} artistId={artist.id} /> <TopTracks limit={8} period={period} artistId={artist.id} />
<ActivityGrid configurable autoAdjust artistId={artist.id} /> <ActivityGrid configurable artistId={artist.id} />
</div> </div>
<ArtistAlbums period={period} artistId={artist.id} name={artist.name} /> <ArtistAlbums period={period} artistId={artist.id} name={artist.name} />
</div> </div>

@ -54,7 +54,7 @@ export default function Track() {
</div> </div>
<div className="flex flex-wrap gap-20 mt-10"> <div className="flex flex-wrap gap-20 mt-10">
<LastPlays limit={20} trackId={track.id}/> <LastPlays limit={20} trackId={track.id}/>
<ActivityGrid trackId={track.id} configurable autoAdjust /> <ActivityGrid trackId={track.id} configurable />
</div> </div>
</MediaLayout> </MediaLayout>
) )

Loading…
Cancel
Save