mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-07-04 12:01:52 +02:00
Add commercial use upsell banners
This commit is contained in:
@@ -1,57 +1,84 @@
|
||||
import type { Color } from "@yaakapp-internal/plugins";
|
||||
import type { BannerProps } from "@yaakapp-internal/ui";
|
||||
import { Banner, HStack } from "@yaakapp-internal/ui";
|
||||
import { Banner } from "@yaakapp-internal/ui";
|
||||
import classNames from "classnames";
|
||||
import { useKeyValue } from "../../hooks/useKeyValue";
|
||||
import type { ButtonProps } from "./Button";
|
||||
import { Button } from "./Button";
|
||||
|
||||
export function DismissibleBanner({
|
||||
children,
|
||||
className,
|
||||
dismissForDays,
|
||||
id,
|
||||
actions,
|
||||
...props
|
||||
}: BannerProps & {
|
||||
id: string;
|
||||
actions?: { label: string; onClick: () => void; color?: Color }[];
|
||||
dismissForDays?: number;
|
||||
actions?: {
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
color?: Color;
|
||||
variant?: ButtonProps["variant"];
|
||||
}[];
|
||||
}) {
|
||||
const { set: setDismissed, value: dismissed } = useKeyValue<boolean>({
|
||||
const {
|
||||
isLoading,
|
||||
set: setDismissed,
|
||||
value: dismissed,
|
||||
} = useKeyValue<boolean | string>({
|
||||
namespace: "global",
|
||||
key: ["dismiss-banner", id],
|
||||
fallback: false,
|
||||
});
|
||||
|
||||
if (dismissed) return null;
|
||||
if (isLoading || isDismissed(dismissed, dismissForDays)) return null;
|
||||
|
||||
return (
|
||||
<Banner
|
||||
className={classNames(className, "relative grid grid-cols-[1fr_auto] gap-3")}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<HStack space={1.5}>
|
||||
{actions?.map((a) => (
|
||||
<Button
|
||||
key={a.label}
|
||||
variant="border"
|
||||
color={a.color ?? props.color}
|
||||
size="xs"
|
||||
onClick={a.onClick}
|
||||
title={a.label}
|
||||
>
|
||||
{a.label}
|
||||
</Button>
|
||||
))}
|
||||
<Button
|
||||
variant="border"
|
||||
color={props.color}
|
||||
size="xs"
|
||||
onClick={() => setDismissed((d) => !d)}
|
||||
title="Dismiss message"
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
</HStack>
|
||||
<Banner className={classNames(className, "relative")} {...props}>
|
||||
<div className="@container">
|
||||
<div className="grid gap-2 @[34rem]:grid-cols-[minmax(0,1fr)_auto] @[34rem]:items-center @[34rem]:gap-3">
|
||||
{children}
|
||||
<div className="flex flex-wrap gap-1.5 @[34rem]:justify-end">
|
||||
<Button
|
||||
variant="border"
|
||||
color={props.color}
|
||||
size="xs"
|
||||
onClick={() => setDismissed(dismissForDays == null ? true : new Date().toISOString())}
|
||||
title="Dismiss message"
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
{actions?.map((a) => (
|
||||
<Button
|
||||
key={a.label}
|
||||
variant={a.variant ?? "border"}
|
||||
color={a.color ?? props.color}
|
||||
size="xs"
|
||||
onClick={a.onClick}
|
||||
title={a.label}
|
||||
>
|
||||
{a.label}
|
||||
</Button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Banner>
|
||||
);
|
||||
}
|
||||
|
||||
function isDismissed(
|
||||
dismissed: boolean | string | null,
|
||||
dismissForDays: number | undefined,
|
||||
): boolean {
|
||||
if (dismissed === false || dismissed == null) return false;
|
||||
if (dismissed === true) return true;
|
||||
if (dismissForDays == null) return dismissed.length > 0;
|
||||
|
||||
const dismissedAt = new Date(dismissed).getTime();
|
||||
if (Number.isNaN(dismissedAt)) return false;
|
||||
|
||||
return Date.now() - dismissedAt < dismissForDays * 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user