163 lines
5.8 KiB
TypeScript
163 lines
5.8 KiB
TypeScript
import { useEffect, useMemo, useState } from "react";
|
||
import { useNavigate } from "react-router-dom";
|
||
import { ChevronRight, Plus } from "lucide-react";
|
||
import { useProfile } from "../context/ProfileContext";
|
||
import { getCachedProfile } from "../../services/profileService";
|
||
import { getProfileImageUrl } from "../../services/feedService";
|
||
import logoImage from "figma:asset/0a77244cc5b7dea0bea10275d45df2915d5170ca.png";
|
||
import coinImage from "../../assets/coin-star.png";
|
||
import profileFallbackImage from "../../assets/image 5.png";
|
||
|
||
interface AppHeaderProps {
|
||
showBack?: boolean;
|
||
onBack?: () => void;
|
||
centerTitle?: string;
|
||
centerSubtitle?: string;
|
||
}
|
||
|
||
const toPersianNumber = (num: number | null | undefined): string => {
|
||
if (num === null || num === undefined) return "۰";
|
||
const persianDigits = "۰۱۲۳۴۵۶۷۸۹";
|
||
return String(num).replace(/\d/g, (digit) => persianDigits[parseInt(digit, 10)]);
|
||
};
|
||
|
||
export function AppHeader({ showBack = false, onBack, centerTitle, centerSubtitle }: AppHeaderProps) {
|
||
const navigate = useNavigate();
|
||
const { profile } = useProfile();
|
||
|
||
const [displayCoins, setDisplayCoins] = useState<number>(() => {
|
||
const cachedProfile = getCachedProfile();
|
||
return cachedProfile?.coin_count ?? 0;
|
||
});
|
||
|
||
useEffect(() => {
|
||
if (profile?.coin_count !== null && profile?.coin_count !== undefined) {
|
||
setDisplayCoins(profile.coin_count);
|
||
}
|
||
}, [profile]);
|
||
|
||
const hasCustomAvatar = Boolean(profile?.image && profile?.user_stage_id);
|
||
const avatarUrl = useMemo(() => {
|
||
if (hasCustomAvatar) {
|
||
return getProfileImageUrl(profile.image, profile.user_stage_id);
|
||
}
|
||
return profileFallbackImage;
|
||
}, [hasCustomAvatar, profile?.image, profile?.user_stage_id]);
|
||
|
||
const navLikePanelStyle = {
|
||
backgroundImage: `
|
||
linear-gradient(180deg, #2E1B3D 0%, #23183E 100%),
|
||
linear-gradient(120deg, #7c3aed 0%, #f97316 58%, #facc15 100%)
|
||
`,
|
||
backgroundOrigin: "border-box",
|
||
backgroundClip: "padding-box, border-box",
|
||
boxShadow:
|
||
"0 -7px 20px rgba(7, 0, 18, 0.5), 0 6px 14px rgba(5, 2, 12, 0.26), inset 0 1px 0 rgba(255, 255, 255, 0.2), inset 0 2px 5px rgba(255, 222, 255, 0.09), inset 0 -2px 0 rgba(12, 7, 27, 0.72), inset 0 -8px 14px rgba(8, 4, 18, 0.34), inset 0 0 0 1px rgba(255, 255, 255, 0.045), inset 0 0 0 2px rgba(17, 10, 35, 0.32)",
|
||
backdropFilter: "blur(14px)",
|
||
WebkitBackdropFilter: "blur(14px)",
|
||
} as const;
|
||
|
||
return (
|
||
<div className="relative z-20 flex items-center justify-between px-4 pt-3 pb-0" dir="rtl">
|
||
{showBack ? (
|
||
<button
|
||
onClick={onBack}
|
||
className="w-11 h-11 rounded-full flex items-center justify-center border-[0.5px] border-transparent"
|
||
aria-label="بازگشت"
|
||
style={navLikePanelStyle}
|
||
>
|
||
<ChevronRight size={22} color="#ffffff" />
|
||
</button>
|
||
) : (
|
||
<button
|
||
onClick={() => navigate("/profile")}
|
||
className="relative w-12 h-12 rounded-full p-[2px] border-[0.5px] border-transparent overflow-hidden"
|
||
aria-label="پروفایل"
|
||
style={navLikePanelStyle}
|
||
>
|
||
{hasCustomAvatar ? (
|
||
<img
|
||
src={avatarUrl}
|
||
alt="پروفایل"
|
||
className="w-full h-full rounded-full object-cover"
|
||
onError={(event) => {
|
||
event.currentTarget.src = profileFallbackImage;
|
||
}}
|
||
/>
|
||
) : (
|
||
<div
|
||
className="w-full h-full rounded-full flex items-center justify-center"
|
||
style={navLikePanelStyle}
|
||
>
|
||
<img
|
||
src={profileFallbackImage}
|
||
alt="پروفایل پیشفرض"
|
||
className=" object-cover rounded-full"
|
||
/>
|
||
</div>
|
||
)}
|
||
</button>
|
||
)}
|
||
|
||
<div className="absolute inset-0 pointer-events-none flex items-center justify-center text-center translate-y-1">
|
||
{centerTitle ? (
|
||
<div className="leading-none">
|
||
<div
|
||
className="font-extrabold text-[22px]"
|
||
style={{
|
||
display: "inline-block",
|
||
background:
|
||
"linear-gradient(90deg, #F6D8A5 0%, #F3A599 20%, #DB7EB2 48%, #AA6798 72%, #CB75AB 100%)",
|
||
WebkitBackgroundClip: "text",
|
||
backgroundClip: "text",
|
||
WebkitTextFillColor: "transparent",
|
||
color: "transparent",
|
||
textShadow: "0 2px 10px rgba(255, 119, 202, 0.4)",
|
||
}}
|
||
>
|
||
{centerTitle}
|
||
</div>
|
||
{centerSubtitle && (
|
||
<div
|
||
className="mt-1 text-[11px] font-medium"
|
||
style={{
|
||
color: "#ffb7dd",
|
||
textShadow: "0 1px 6px rgba(255, 119, 202, 0.35)",
|
||
}}
|
||
>
|
||
{centerSubtitle}
|
||
</div>
|
||
)}
|
||
</div>
|
||
) : (
|
||
<img
|
||
src={logoImage}
|
||
alt="مدرسه"
|
||
className="h-12 object-contain"
|
||
style={{ filter: "drop-shadow(0 2px 6px rgba(192, 132, 252, 0.55))" }}
|
||
/>
|
||
)}
|
||
</div>
|
||
|
||
<div
|
||
className="relative h-10 w-fit rounded-full px-2.5 flex items-center gap-[6px] border-[0.5px] border-transparent"
|
||
style={navLikePanelStyle}
|
||
>
|
||
<div
|
||
className="w-5 h-5 rounded-full flex items-center justify-center border-[0.5px] border-transparent"
|
||
style={navLikePanelStyle}
|
||
>
|
||
<Plus size={11} color="#ffd6f0" strokeWidth={2.25} />
|
||
</div>
|
||
<span
|
||
className="font-semibold leading-none text-white tracking-tight"
|
||
style={{ fontSize: 16 }}
|
||
>
|
||
{toPersianNumber(displayCoins)}
|
||
</span>
|
||
<img src={coinImage} alt="سکه" className="w-7 h-7 object-contain -my-0.5" />
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|