Fix race condition with using getComputedStyle primary color for dynamic activity grid darkening

Instead just use the color from the current theme directly. Tested works on initial load and theme changes.
Fixes https://github.com/gabehf/Koito/issues/75
dev
Michael Landry 2 months ago committed by Gabe Farrell
parent 56ffe0a041
commit 517cc8ac28

@ -1,41 +1,41 @@
import { useQuery } from "@tanstack/react-query"
import { getActivity, type getActivityArgs, type ListenActivityItem } from "api/api"
import Popup from "./Popup"
import { useState } from "react"
import { useTheme } from "~/hooks/useTheme"
import ActivityOptsSelector from "./ActivityOptsSelector"
import type { Theme } from "~/styles/themes.css"
import { useQuery } from "@tanstack/react-query";
import {
getActivity,
type getActivityArgs,
type ListenActivityItem,
} from "api/api";
import Popup from "./Popup";
import { useState } from "react";
import { useTheme } from "~/hooks/useTheme";
import ActivityOptsSelector from "./ActivityOptsSelector";
import type { Theme } from "~/styles/themes.css";
function getPrimaryColor(theme: Theme): string {
const value = theme.primary;
const rgbMatch = value.match(/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/);
const rgbMatch = value.match(
/^rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)$/
);
if (rgbMatch) {
const [, r, g, b] = rgbMatch.map(Number);
return (
'#' +
[r, g, b]
.map((n) => n.toString(16).padStart(2, '0'))
.join('')
);
return "#" + [r, g, b].map((n) => n.toString(16).padStart(2, "0")).join("");
}
return value;
}
interface Props {
step?: string
range?: number
month?: number
year?: number
artistId?: number
albumId?: number
trackId?: number
configurable?: boolean
autoAdjust?: boolean
step?: string;
range?: number;
month?: number;
year?: number;
artistId?: number;
albumId?: number;
trackId?: number;
configurable?: boolean;
autoAdjust?: boolean;
}
export default function ActivityGrid({
step = 'day',
step = "day",
range = 182,
month = 0,
year = 0,
@ -43,14 +43,13 @@ export default function ActivityGrid({
albumId = 0,
trackId = 0,
configurable = false,
}: Props) {
const [stepState, setStep] = useState(step)
const [rangeState, setRange] = useState(range)
}: Props) {
const [stepState, setStep] = useState(step);
const [rangeState, setRange] = useState(range);
const { isPending, isError, data, error } = useQuery({
queryKey: [
'listen-activity',
"listen-activity",
{
step: stepState,
range: rangeState,
@ -58,81 +57,81 @@ export default function ActivityGrid({
year: year,
artist_id: artistId,
album_id: albumId,
track_id: trackId
track_id: trackId,
},
],
queryFn: ({ queryKey }) => getActivity(queryKey[1] as getActivityArgs),
});
const { theme, themeName } = useTheme();
const color = getPrimaryColor(theme);
if (isPending) {
return (
<div className="w-[500px]">
<h2>Activity</h2>
<p>Loading...</p>
</div>
)
);
}
if (isError) return <p className="error">Error:{error.message}</p>
if (isError) return <p className="error">Error:{error.message}</p>;
// from https://css-tricks.com/snippets/javascript/lighten-darken-color/
function LightenDarkenColor(hex: string, lum: number) {
// validate hex string
hex = String(hex).replace(/[^0-9a-f]/gi, '');
hex = String(hex).replace(/[^0-9a-f]/gi, "");
if (hex.length < 6) {
hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
lum = lum || 0;
// convert to decimal and change luminosity
var rgb = "#", c, i;
var rgb = "#",
c,
i;
for (i = 0; i < 3; i++) {
c = parseInt(hex.substring(i*2,(i*2)+2), 16);
c = Math.round(Math.min(Math.max(0, c + (c * lum)), 255)).toString(16);
rgb += ("00"+c).substring(c.length);
c = parseInt(hex.substring(i * 2, i * 2 + 2), 16);
c = Math.round(Math.min(Math.max(0, c + c * lum), 255)).toString(16);
rgb += ("00" + c).substring(c.length);
}
return rgb;
}
const getDarkenAmount = (v: number, t: number): number => {
// 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
const adjustment =
artistId == albumId && albumId == trackId && trackId == 0 ? 10 : 1;
// automatically adjust the target value based on step
// the smartest way to do this would be to have the api return the
// highest value in the range. too bad im not smart
switch (stepState) {
case 'day':
t = 10 * adjustment
case "day":
t = 10 * adjustment;
break;
case 'week':
t = 20 * adjustment
case "week":
t = 20 * adjustment;
break;
case 'month':
t = 50 * adjustment
case "month":
t = 50 * adjustment;
break;
case 'year':
t = 100 * adjustment
case "year":
t = 100 * adjustment;
break;
}
v = Math.min(v, t)
v = Math.min(v, t);
if (themeName === "pearl") {
// special case for the only light theme lol
// could be generalized by pragmatically comparing the
// lightness of the bg vs the primary but eh
return ((t-v) / t)
return (t - v) / t;
} else {
return ((v-t) / t) * .8
}
return ((v - t) / t) * 0.8;
}
};
const CHUNK_SIZE = 26 * 7;
const chunks = [];
@ -167,18 +166,25 @@ export default function ActivityGrid({
position="top"
space={12}
extraClasses="left-2"
inner={`${new Date(item.start_time).toLocaleDateString()} ${item.listens} plays`}
inner={`${new Date(item.start_time).toLocaleDateString()} ${
item.listens
} plays`}
>
<div
style={{
display: 'inline-block',
display: "inline-block",
background:
item.listens > 0
? LightenDarkenColor(color, getDarkenAmount(item.listens, 100))
: 'var(--color-bg-secondary)',
? LightenDarkenColor(
color,
getDarkenAmount(item.listens, 100)
)
: "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)'
item.listens > 0
? ""
: "border-[0.5px] border-(--color-bg-tertiary)"
}`}
></div>
</Popup>

Loading…
Cancel
Save