- Remove rounded clipping on navbar user avatar button (was showing dark circular background) - Fix "My Avatar" preview in team settings showing circular instead of square shape - Pass owner avatar data through to TeamAvatar in standings, draft room, and draft board so teams with avatarType="owner" correctly show the user's avatar instead of the generated flag - Consolidate user lookups in draft board loader to avoid duplicate DB queries - Fix DraftSlotWithTeam interface to use RawFlagConfig type and include logoUrl/flagConfig/avatarType fields Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
136 lines
4.2 KiB
TypeScript
136 lines
4.2 KiB
TypeScript
import { useMemo, useState } from "react";
|
|
import { Save, User } from "lucide-react";
|
|
import { getDisplayFlagConfig } from "~/lib/avatar-data";
|
|
import type { AvatarData, FlagConfig, RawFlagConfig } from "~/lib/flag-types";
|
|
import { Button } from "~/components/ui/button";
|
|
import { FlagBuilder } from "~/components/ui/FlagBuilder";
|
|
import { FlagSvg } from "~/components/ui/FlagSvg";
|
|
import { AvatarUploader } from "~/components/ui/AvatarUploader";
|
|
import { cloudinaryAvatarUrl } from "~/lib/cloudinary-url";
|
|
import { cn } from "~/lib/utils";
|
|
|
|
interface AvatarEditorProps {
|
|
id: string;
|
|
flagConfig?: RawFlagConfig | null;
|
|
uploadedPhotoUrl?: string | null;
|
|
uploadUrl: string;
|
|
isTeam?: boolean;
|
|
currentAvatarType?: string | null;
|
|
ownerAvatarData?: AvatarData | null;
|
|
flagIntent?: string;
|
|
removePhotoIntent?: string;
|
|
useOwnerAvatarIntent?: string;
|
|
}
|
|
|
|
type Mode = "flag" | "upload" | "owner";
|
|
|
|
export function AvatarEditor({
|
|
id,
|
|
flagConfig,
|
|
uploadedPhotoUrl,
|
|
uploadUrl,
|
|
isTeam = false,
|
|
currentAvatarType,
|
|
ownerAvatarData,
|
|
flagIntent = "update-avatar-flag",
|
|
removePhotoIntent = "remove-avatar-photo",
|
|
useOwnerAvatarIntent = "use-owner-avatar",
|
|
}: AvatarEditorProps) {
|
|
const initialFlag = useMemo(() => getDisplayFlagConfig(id, flagConfig), [flagConfig, id]);
|
|
const [config, setConfig] = useState<FlagConfig>(initialFlag);
|
|
|
|
const showOwnerTab = isTeam && ownerAvatarData !== null && ownerAvatarData !== undefined;
|
|
const initialMode: Mode =
|
|
currentAvatarType === "owner" && showOwnerTab ? "owner" :
|
|
currentAvatarType === "uploaded" ? "upload" :
|
|
"flag";
|
|
const [mode, setMode] = useState<Mode>(initialMode);
|
|
|
|
const tabs = [
|
|
{ key: "flag" as const, label: "Flag" },
|
|
{ key: "upload" as const, label: "Upload Photo" },
|
|
...(showOwnerTab ? [{ key: "owner" as const, label: "My Avatar" }] : []),
|
|
];
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex overflow-hidden rounded-lg border border-border">
|
|
{tabs.map((tab) => (
|
|
<button
|
|
key={tab.key}
|
|
type="button"
|
|
onClick={() => setMode(tab.key)}
|
|
className={cn(
|
|
"flex-1 py-2 text-sm font-medium transition-colors",
|
|
mode === tab.key
|
|
? "bg-primary text-primary-foreground"
|
|
: "text-muted-foreground hover:bg-accent"
|
|
)}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{mode === "flag" && (
|
|
<form method="post" className="space-y-4">
|
|
<input type="hidden" name="intent" value={flagIntent} />
|
|
<input type="hidden" name="flagConfig" value={JSON.stringify(config)} />
|
|
<FlagBuilder value={config} seed={id} onChange={setConfig} />
|
|
<Button type="submit" className="w-full">
|
|
<Save className="mr-2 h-4 w-4" />
|
|
Save flag
|
|
</Button>
|
|
</form>
|
|
)}
|
|
|
|
{mode === "upload" && (
|
|
<AvatarUploader
|
|
uploadUrl={uploadUrl}
|
|
teamId={isTeam ? id : undefined}
|
|
savedPhotoUrl={uploadedPhotoUrl}
|
|
removePhotoIntent={removePhotoIntent}
|
|
/>
|
|
)}
|
|
|
|
{mode === "owner" && ownerAvatarData && (
|
|
<form method="post" className="space-y-4">
|
|
<input type="hidden" name="intent" value={useOwnerAvatarIntent} />
|
|
<div className="flex flex-col items-center gap-3 py-2">
|
|
<OwnerAvatarPreview avatarData={ownerAvatarData} />
|
|
<p className="text-sm text-muted-foreground">Your team will use your current profile avatar.</p>
|
|
</div>
|
|
<Button type="submit" className="w-full">
|
|
<User className="mr-2 h-4 w-4" />
|
|
Use my avatar
|
|
</Button>
|
|
</form>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function OwnerAvatarPreview({ avatarData }: { avatarData: AvatarData }) {
|
|
if (avatarData.type === "image") {
|
|
return (
|
|
<img
|
|
src={cloudinaryAvatarUrl(avatarData.url, 64)}
|
|
alt="Your avatar"
|
|
className="h-16 w-16 object-cover ring-2 ring-border"
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (avatarData.type === "flag") {
|
|
return (
|
|
<FlagSvg
|
|
config={avatarData.config}
|
|
size={64}
|
|
title="Your avatar"
|
|
className="h-16 w-16 ring-2 ring-border"
|
|
/>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|