Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 106x 31x 106x 106x 20x 106x 26x 26x 26x 26x 26x 3x 26x 8x 8x 18x 8x 8x 8x 26x 18x 11x 26x 19x 1x 1x 19x 26x 11x | import { Check, X } from "lucide-react";
import { useEffect, useRef } from "react";
import { useLocation } from "react-router";
import { useLocalStorage } from "@/hooks/useLocalStorage";
import { Observer } from "@/utils/basic-observable";
import { Alert, AlertVariant } from "../Alert";
export type Banner = {
header: string;
body: string;
variant?: AlertVariant;
pathnameToDisplayOn: string;
};
class BannerObserver extends Observer<Banner> {
create = (data: Banner) => {
this.publish(data);
};
dismiss = () => {
this.publish(null);
};
}
const bannerState = new BannerObserver();
export const banner = (newBanner: Banner) => {
return bannerState.create(newBanner);
};
export const Banner = () => {
const bannerObserverRef = useRef<(() => void) | null>(null);
const { pathname } = useLocation();
const previousPathRef = useRef(pathname);
const [activeBanner, setActiveBanner] = useLocalStorage<Banner | null>("banner", null);
const onClose = () => {
setActiveBanner(null);
};
useEffect(() => {
Eif (bannerObserverRef.current === null) {
bannerObserverRef.current = bannerState.subscribe((banner) => {
setActiveBanner(banner);
});
return () => {
bannerObserverRef.current?.();
bannerObserverRef.current = null;
};
}
}, [setActiveBanner]);
useEffect(() => {
if (activeBanner) {
bannerState.create(activeBanner);
}
}, [activeBanner]);
useEffect(() => {
// only run cleanup if:
// 1. we've actually navigated (pathname changed from previous render)
// 2. there's an active banner to clean up
// 3. the banner's target pathname doesn't match where we navigated to
if (pathname !== previousPathRef.current) {
Eif (activeBanner && activeBanner.pathnameToDisplayOn !== pathname) {
onClose();
}
}
// store current pathname for next render's comparison
previousPathRef.current = pathname;
}, [pathname, activeBanner]); // eslint-disable-line react-hooks/exhaustive-deps
if (activeBanner && activeBanner.pathnameToDisplayOn === pathname) {
return (
<Alert variant={activeBanner.variant} className="mt-4 mb-8 flex-row text-sm">
<div className="flex items-start justify-between">
<Check />
<div className="ml-2 w-full">
<h3 className="text-lg font-bold" data-testid="banner-header">
{activeBanner.header}
</h3>
<p data-testid="banner-body">{activeBanner.body}</p>
</div>
<button onClick={onClose} data-testid="banner-close">
<X size={20} />
</button>
</div>
</Alert>
);
}
return null;
};
|