Add support for badge hue rotate

This commit is contained in:
Kalle 2022-07-05 11:30:01 +03:00
parent 3378320f06
commit 94d5e8c137
13 changed files with 88 additions and 48 deletions

41
app/components/Badge.tsx Normal file
View File

@ -0,0 +1,41 @@
import type { Badge as BadgeDBType } from "~/db/types";
import { badgeUrl } from "~/utils/urls";
import { Image } from "./Image";
export function Badge({
badge,
onClick,
isAnimated,
size,
}: {
badge: Pick<BadgeDBType, "displayName" | "code" | "hue">;
onClick?: () => void;
isAnimated: boolean;
size: number;
}) {
const commonProps = {
title: badge.displayName,
onClick,
width: size,
height: size,
style: badge.hue ? { filter: `hue-rotate(${badge.hue}deg)` } : undefined,
};
if (isAnimated) {
return (
<img
src={badgeUrl({ code: badge.code, extension: "gif" })}
alt={badge.displayName}
{...commonProps}
/>
);
}
return (
<Image
path={badgeUrl({ code: badge.code })}
alt={badge.displayName}
{...commonProps}
/>
);
}

View File

@ -5,6 +5,7 @@ export function Image({
className,
width,
height,
style,
}: {
path: string;
alt: string;
@ -12,6 +13,7 @@ export function Image({
className?: string;
width?: number;
height?: number;
style?: React.CSSProperties;
}) {
return (
<picture title={title}>
@ -21,6 +23,7 @@ export function Image({
className={className}
width={width}
height={height}
style={style}
/>
<img
alt={alt}
@ -28,6 +31,7 @@ export function Image({
className={className}
width={width}
height={height}
style={style}
/>
</picture>
);

View File

@ -2,7 +2,12 @@ import { sql } from "../sql";
import type { Badge, User } from "../types";
const countsByUserIdStm = sql.prepare(`
select "Badge"."code", "Badge"."displayName", "Badge"."id", count("BadgeOwner"."badgeId") as count
select
"Badge"."code",
"Badge"."displayName",
"Badge"."id",
"Badge"."hue",
count("BadgeOwner"."badgeId") as count
from "BadgeOwner"
join "Badge" on "Badge"."id" = "BadgeOwner"."badgeId"
where "BadgeOwner"."userId" = $userId
@ -10,7 +15,7 @@ const countsByUserIdStm = sql.prepare(`
`);
export type CountsByUserId = Array<
Pick<Badge, "code" | "displayName" | "id"> & {
Pick<Badge, "code" | "displayName" | "id" | "hue"> & {
count: number;
}
>;
@ -20,11 +25,15 @@ export function countsByUserId(userId: User["id"]) {
}
const allStm = sql.prepare(`
select "Badge"."id", "Badge"."code", "Badge"."displayName"
select
"Badge"."id",
"Badge"."code",
"Badge"."displayName",
"Badge"."hue"
from "Badge"
`);
export type All = Array<Pick<Badge, "id" | "displayName" | "code">>;
export type All = Array<Pick<Badge, "id" | "displayName" | "code" | "hue">>;
export function all() {
return allStm.all() as All;

View File

@ -56,6 +56,7 @@ export interface Badge {
id: number;
code: string;
displayName: string;
hue?: number;
}
export interface BadgeOwner {

View File

@ -1,12 +1,11 @@
import type { LinksFunction, LoaderFunction } from "@remix-run/node";
import { NavLink, Outlet, useLoaderData } from "@remix-run/react";
import { Image } from "~/components/Image";
import { Badge } from "~/components/Badge";
import { Main } from "~/components/Main";
import { db } from "~/db";
import type { All } from "~/db/models/badges.server";
import styles from "~/styles/badges.css";
import { jsonCached } from "~/utils/remix";
import { badgeUrl } from "~/utils/urls";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
@ -35,13 +34,7 @@ export default function BadgesPageLayout() {
key={badge.id}
to={String(badge.id)}
>
<Image
path={badgeUrl({ code: badge.code })}
title={badge.displayName}
alt={badge.displayName}
width={64}
height={64}
/>
<Badge badge={badge} size={64} isAnimated={false} />
</NavLink>
))}
</div>

View File

@ -1,13 +1,14 @@
import type { LoaderFunction } from "@remix-run/node";
import { useLoaderData, useMatches, useParams } from "@remix-run/react";
import clsx from "clsx";
import { Badge } from "~/components/Badge";
import { Redirect } from "~/components/Redirect";
import { db } from "~/db";
import type { OwnersByBadge } from "~/db/models/badges.server";
import type { Badge } from "~/db/types";
import type { Badge as BadgeDBType } from "~/db/types";
import { jsonCached } from "~/utils/remix";
import { discordFullName } from "~/utils/strings";
import { BADGES_PAGE, badgeUrl } from "~/utils/urls";
import { BADGES_PAGE } from "~/utils/urls";
import type { BadgesLoaderData } from "../badges";
export interface BadgeDetailsLoaderData {
@ -37,13 +38,7 @@ export default function BadgeDetailsPage() {
return (
<div className="stack md items-center">
<img
src={badgeUrl({ code: badge.code, extension: "gif" })}
alt={badge.displayName}
title={badge.displayName}
width="200"
height="200"
/>
<Badge badge={badge} isAnimated size={200} />
<div className="badges__explanation">{badgeExplanationText(badge)}</div>
<div className="badges__owners-container">
<ul className="badges__owners">
@ -66,7 +61,7 @@ export default function BadgeDetailsPage() {
}
export function badgeExplanationText(
badge: Pick<Badge, "displayName" | "code"> & { count?: number }
badge: Pick<BadgeDBType, "displayName" | "code"> & { count?: number }
) {
const countString =
badge.count && badge.count > 1 ? ` (x${badge.count})` : "";

View File

@ -56,8 +56,6 @@ export const loader: LoaderFunction = async ({ request }) => {
export default function PlusVotingResultsPage() {
const data = useLoaderData<PlusVotingResultsLoaderData>();
console.log({ data });
const { month, year } = lastCompletedVoting(new Date());
return (

View File

@ -12,7 +12,7 @@ import { TwitchIcon } from "~/components/icons/Twitch";
import { TwitterIcon } from "~/components/icons/Twitter";
import { YouTubeIcon } from "~/components/icons/YouTube";
import { badgeExplanationText } from "../badges/$id";
import { badgeUrl } from "~/utils/urls";
import { Badge } from "~/components/Badge";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
@ -135,25 +135,16 @@ function BadgeContainer(props: { badges: UserPageLoaderData["badges"] }) {
"justify-center": smallBadges.length === 0,
})}
>
<img
key={bigBadge.code}
src={badgeUrl({ code: bigBadge.code, extension: "gif" })}
alt={bigBadge.displayName}
title={bigBadge.displayName}
width="125"
height="125"
/>
<Badge badge={bigBadge} size={125} isAnimated />
{smallBadges.length > 0 ? (
<div className="u__small-badges">
{smallBadges.map((badge) => (
<div key={badge.id} className="u__small-badge-container">
<img
src={badgeUrl({ code: badge.code, extension: "gif" })}
alt={badge.displayName}
title={badge.displayName}
<Badge
badge={badge}
onClick={() => setBadgeFirst(badge)}
width="48"
height="48"
size={48}
isAnimated
/>
{badge.count > 1 ? (
<div className="u__small-badge-count">×{badge.count}</div>

View File

@ -2,11 +2,11 @@
display: flex;
flex-direction: column;
align-items: center;
padding-block: var(--s-2);
padding-inline: var(--s-3);
background-color: var(--bg-badge);
border-radius: var(--rounded);
gap: var(--s-6);
padding-block: var(--s-2);
padding-inline: var(--s-3);
}
.badges__small-badges {
@ -27,8 +27,8 @@
}
.badges__owners-container {
overflow-y: auto;
height: 8rem;
overflow-y: auto;
}
.badges__owners {

View File

@ -5,8 +5,8 @@
.button-text-paragraph > button {
font-size: var(--fonts-md);
font-weight: var(--semi-bold);
font-size: var(--fonts-sm);
font-weight: var(--semi-bold);
margin-block-end: 0.125rem;
}

View File

@ -1,12 +1,12 @@
.faq__summary {
background-color: var(--bg-lighter);
padding: var(--s-3);
background-color: var(--bg-lighter);
border-radius: var(--rounded);
font-size: var(--fonts-lg);
font-weight: var(--bold);
}
.faq__details > p {
padding-block: var(--s-3);
margin-inline: var(--s-4);
padding-block: var(--s-3);
}

View File

@ -0,0 +1,8 @@
module.exports.up = function (db) {
db.prepare(`alter table "Badge" add "hue" integer`).run();
db.prepare(`update "Badge" set "hue" = -72 where "code" = 'ebtv'`).run();
};
module.exports.down = function (db) {
db.prepare(`alter table "Badge" drop column "hue"`).run();
};

View File

@ -21,8 +21,8 @@
"test:unit": "uvu -r tsm -r tsconfig-paths/register -i cypress",
"cy:open": "cypress open",
"cy:run": "cypress run",
"checks": "npm run typecheck && npm run test:unit && npm run lint:styles && npm run lint:ts && npm run prettier:check",
"cf": "npm run typecheck && npm run test:unit && npm run lint:styles -- --fix && npm run lint:ts -- --fix && npm run prettier:write"
"checks": "npm run test:unit && npm run lint:styles && npm run lint:ts && npm run prettier:check && npm run typecheck",
"cf": "npm run test:unit && npm run lint:styles -- --fix && npm run lint:ts -- --fix && npm run prettier:write && npm run typecheck"
},
"dependencies": {
"@faker-js/faker": "^7.3.0",