added zapping functionality on event cards and sidebar

This commit is contained in:
zmeyer44 2023-10-19 13:37:20 -04:00
parent b0017440a0
commit c6ee922391
19 changed files with 272 additions and 114 deletions

View File

@ -1,7 +1,10 @@
import { Search } from "./components/Search";
import AuthActions from "./components/AuthActions";
import Logo from "@/assets/Logo";
import dynamic from "next/dynamic";
const Search = dynamic(() => import("./components/Search"), {
ssr: false,
});
export default function Header() {
return (
<header className="flex h-[var(--header-height)] shrink-0 grow-0 ">

View File

@ -43,6 +43,19 @@ type NavigationElement = {
current: boolean;
active: boolean;
} & (NavigationLink | NavigationButton);
const flockstrEvent = {
created_at: 1697736945,
content:
"Officially announcing Flockstr. Check it out at https://flockstr.com",
tags: [
["r", "https://flockstr.com"],
["client", "flockstr"],
],
kind: 1,
pubkey: "17717ad4d20e2a425cda0a2195624a0a4a73c4f6975f16b1593fc87fa46f2d58",
id: "a867ff28711eeab4767fb6bacbb33dfe17b2b5bbbff98f8e57f90a85ea684b0a",
sig: "37d8918e6da88d989467021a1f5809a3fbcab941ca1044d109ce261f29270d2d545aaa84297b7f224ae1ad7760263e50c317c24abc809034bcdb5c3260faf4b0",
};
export default function Sidebar() {
const modal = useModal();
@ -76,7 +89,7 @@ export default function Sidebar() {
active: false,
},
{
onClick: () => modal?.show(<ZapPickerModal />),
onClick: () => modal?.show(<ZapPickerModal event={flockstrEvent} />),
name: "zap",
label: "Zap Flockstr",
icon: HiOutlineLightningBolt,

View File

@ -0,0 +1,70 @@
"use client";
import { useKeyboardShortcut } from "@/lib/hooks/useKeyboardShortcut";
import {
CalendarIcon,
EnvelopeClosedIcon,
FaceIcon,
GearIcon,
PersonIcon,
RocketIcon,
} from "@radix-ui/react-icons";
import {
CommandDialog,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
CommandSeparator,
CommandShortcut,
} from "@/components/ui/command";
import { atom, useAtom } from "jotai";
export const commandDialogAtom = atom(false);
export default function CommandDialogComponent() {
const [open, setOpen] = useAtom(commandDialogAtom);
useKeyboardShortcut(["ctrl", "k"], () => setOpen((open) => !open));
return (
<CommandDialog open={open} onOpenChange={setOpen}>
<CommandInput placeholder="Type a command or search..." />
<CommandList>
<CommandEmpty>No results found.</CommandEmpty>
<CommandGroup heading="Suggestions">
<CommandItem>
<CalendarIcon className="mr-2 h-4 w-4" />
<span>Calendar</span>
</CommandItem>
<CommandItem>
<FaceIcon className="mr-2 h-4 w-4" />
<span>Search Emoji</span>
</CommandItem>
<CommandItem>
<RocketIcon className="mr-2 h-4 w-4" />
<span>Launch</span>
</CommandItem>
</CommandGroup>
<CommandSeparator />
<CommandGroup heading="Settings">
<CommandItem>
<PersonIcon className="mr-2 h-4 w-4" />
<span>Profile</span>
<CommandShortcut>P</CommandShortcut>
</CommandItem>
<CommandItem>
<EnvelopeClosedIcon className="mr-2 h-4 w-4" />
<span>Mail</span>
<CommandShortcut>B</CommandShortcut>
</CommandItem>
<CommandItem>
<GearIcon className="mr-2 h-4 w-4" />
<span>Settings</span>
<CommandShortcut>S</CommandShortcut>
</CommandItem>
</CommandGroup>
</CommandList>
</CommandDialog>
);
}

View File

@ -1,12 +1,17 @@
import { Input } from "@/components/ui/input";
"use client";
export function Search() {
import { Input } from "@/components/ui/input";
import { commandDialogAtom } from "./CommandDialog";
import { useAtom } from "jotai";
export default function Search() {
const [open, setOpen] = useAtom(commandDialogAtom);
return (
<div>
<Input
type="search"
placeholder="Search..."
className="sm:w-[200px] lg:w-[300px]"
onFocus={() => setOpen(true)}
/>
</div>
);

View File

@ -3,7 +3,11 @@ import Header from "./Header";
import Keystone from "./Keystone";
import MobileBanner from "./MobileBanner";
import Sidebar from "./Sidebar";
import dynamic from "next/dynamic";
const CommandDialog = dynamic(() => import("./components/CommandDialog"), {
ssr: false,
});
export default function AppLayout({ children }: { children: React.ReactNode }) {
return (
<main className="app-layout w-screen sm:absolute sm:inset-0">
@ -23,6 +27,7 @@ export default function AppLayout({ children }: { children: React.ReactNode }) {
{/* BottomNav */}
<BottomNav />
<CommandDialog />
</main>
);
}

BIN
bun.lockb

Binary file not shown.

View File

@ -15,8 +15,6 @@ export default function Kind1(props: Event) {
return (
<Container
pubkey={pubkey}
createdAt={createdAt}
actionOptions={[
{
label: "View profile",

View File

@ -19,9 +19,7 @@ export default function Kind30023(props: Event) {
return (
<Container
pubkey={pubkey}
contentTags={contentTags}
createdAt={createdAt}
event={props}
actionOptions={[
{
label: "View profile",

View File

@ -10,19 +10,16 @@ import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
import ReactPlayer from "react-player";
export default function Kind30311(props: Event) {
const { pubkey, created_at: createdAt, tags } = props;
const { pubkey, tags } = props;
const streamingUrl =
getTagValues("streaming", tags) ?? getTagValues("recording", tags);
const title = getTagValues("title", tags);
const summary = getTagValues("summary", tags);
const contentTags = getTagsValues("t", tags).filter(Boolean);
const npub = nip19.npubEncode(pubkey);
return (
<Container
pubkey={pubkey}
createdAt={createdAt}
contentTags={contentTags}
event={props}
actionOptions={[
{
label: "View profile",

View File

@ -65,7 +65,7 @@ export default function Kind3745(props: Event) {
return <KindCard {...decryptedEvent} />;
}
return (
<Container pubkey={pubkey}>
<Container event={props}>
<div className="relative ">
<div className=" blur">
<CardTitle className="mb-1.5 line-clamp-2 text-lg font-semibold">

View File

@ -1,11 +1,21 @@
"use client";
import { Button } from "@/components/ui/button";
import {
HiOutlineHandThumbUp,
HiOutlineChatBubbleLeftEllipsis,
} from "react-icons/hi2";
import { HiOutlineLightningBolt } from "react-icons/hi";
import ZapPicker from "@/components/Modals/ZapPicker";
import { useModal } from "@/app/_providers/modal/provider";
import { NostrEvent } from "@nostr-dev-kit/ndk";
import { stopPropagation } from "@/lib/utils";
export default function Actions() {
type ActionProps = {
event: NostrEvent;
};
export default function Actions({ event }: ActionProps) {
const modal = useModal();
return (
<div className="mt-3 flex items-center justify-between">
<div className="flex items-center gap-3">
@ -17,7 +27,15 @@ export default function Actions() {
<HiOutlineChatBubbleLeftEllipsis className="h-4 w-4" />
</Button>
</div>
<Button size={"sm"} className="gap-x-1.5">
<Button
onClick={(e) => {
stopPropagation(e);
console.log("captured");
modal?.show(<ZapPicker event={event} />);
}}
size={"sm"}
className="gap-x-1.5"
>
<HiOutlineLightningBolt className="h-4 w-4" />
<span>zap</span>
</Button>

View File

@ -7,10 +7,12 @@ import { formatDate } from "@/lib/utils/dates";
import { Button } from "@/components/ui/button";
import { ReactNode } from "react";
import ProfileHeader, { LoadingProfileHeader } from "./ProfileHeader";
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
import Actions from "./Actions";
import Tags from "./Tags";
import DropDownMenu from "@/components/DropDownMenu";
import { NostrEvent } from "@nostr-dev-kit/ndk";
import { removeDuplicates } from "@/lib/utils";
type OptionLink = {
href: string;
type: "link";
@ -24,25 +26,54 @@ type Option = {
} & (OptionLink | OptionButton);
type CreatorCardProps = {
pubkey?: string;
contentTags?: string[];
createdAt?: number;
children: ReactNode;
actionOptions?: Option[];
event?: NostrEvent;
};
export default function Container({
children,
createdAt,
contentTags,
pubkey,
actionOptions = [],
event,
}: CreatorCardProps) {
if (!event) {
return (
<Card className="relative flex h-full w-full flex-col overflow-hidden @container">
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4 pb-4">
<LoadingProfileHeader />
<div className="-mr-1 flex items-center gap-x-1.5 text-xs text-muted-foreground">
<DropDownMenu options={[]}>
<Button
size={"sm"}
variant={"ghost"}
className="center h-6 w-6 p-0"
>
<RiMoreFill className="h-4 w-4" />
</Button>
</DropDownMenu>
</div>
</CardHeader>
<CardContent className="flex grow flex-col px-4 pb-3">
{children}
<div className="mt-auto"></div>
</CardContent>
</Card>
);
}
const { pubkey, tags, created_at: createdAt } = event;
const contentTags = removeDuplicates(getTagsValues("t", tags)).filter(
Boolean,
);
return (
<Card className="relative flex h-full w-full flex-col overflow-hidden @container">
<Card
onClick={() => {
console.log("CLICK IN CONTAINER");
}}
className="relative flex h-full w-full flex-col overflow-hidden @container"
>
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4 pb-4">
{pubkey ? <ProfileHeader pubkey={pubkey} /> : <LoadingProfileHeader />}
<div className="-mr-1 flex items-center gap-x-1.5 text-xs text-muted-foreground">
{!!createdAt &&
formatDate(new Date(createdAt * 1000), "MMM Do, h:mm a")}
@ -68,7 +99,7 @@ export default function Container({
<div className="h-1.5" />
)}
<div className="border-t">
<Actions />
<Actions event={event} />
</div>
</div>
</CardContent>

View File

@ -16,8 +16,7 @@ export default function KindDefault(props: Event) {
return (
<Container
pubkey={pubkey}
createdAt={createdAt}
event={props}
actionOptions={[
{
label: "View profile",

View File

@ -3,23 +3,16 @@ import { useState, useRef, useEffect } from "react";
import Template from "./Template";
import { Button } from "@/components/ui/button";
import { useModal } from "@/app/_providers/modal/provider";
import { nip19 } from "nostr-tools";
// import { useKeys } from "@/app/_providers/keysProvider";
import { toast } from "sonner";
import { useNDK } from "@/app/_providers/ndk";
import useCurrentUser from "@/lib/hooks/useCurrentUser";
import { HiOutlineLightningBolt } from "react-icons/hi";
import { RiSubtractFill, RiAddFill } from "react-icons/ri";
import { formatCount } from "@/lib/utils";
import { Textarea } from "@/components/ui/textarea";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/components/ui/form";
import { Label } from "@/components/ui/label";
import { type NostrEvent } from "@nostr-dev-kit/ndk";
import { sendZap } from "@/lib/actions/zap";
const intervals = [
10, 25, 50, 75, 100, 150, 200, 250, 350, 500, 750, 1000, 1250, 1500, 2_000,
@ -27,13 +20,18 @@ const intervals = [
20_000, 25_000, 30_000, 40_000, 50_000, 75_000, 100_000, 150_000, 200_000,
300_000, 500_000, 750_000, 1_000_000, 1_250_000, 1_500_000, 2_000_000,
];
export default function ZapPicker() {
type ZapPickerProps = {
event: NostrEvent;
};
export default function ZapPicker({ event }: ZapPickerProps) {
const { loginWithNip07 } = useNDK();
const { loginWithPubkey, currentUser } = useCurrentUser();
const [isLoading, setIsLoading] = useState(false);
const [note, setNote] = useState("");
const modal = useModal();
const [sats, setSats] = useState(2000);
const { ndk } = useNDK();
function onClick(type: "+" | "-") {
setSats((prev) => {
@ -48,6 +46,18 @@ export default function ZapPicker() {
});
}
async function handleSendZap() {
try {
setIsLoading(true);
const result = await sendZap(ndk!, sats, event, note);
toast.success("Zap Sent!");
} catch (err) {
console.log("error sending zap", err);
} finally {
setIsLoading(false);
}
}
return (
<Template title="Send Zap!" className="md:max-w-[400px]">
<div className="flex flex-col gap-y-5">
@ -71,15 +81,7 @@ export default function ZapPicker() {
Satoshis
</div>
</div>
<div className="">
<FormLabel>Note</FormLabel>
<Textarea
placeholder="Add a note..."
onChange={(e) => setNote(e.target.value)}
value={note}
className="auto-sizing"
/>
</div>
<Button
variant="outline"
size="icon"
@ -91,9 +93,22 @@ export default function ZapPicker() {
<span className="sr-only">Increase</span>
</Button>
</div>
<div className="pt-3">
<Label>Note</Label>
<Textarea
placeholder="Add a note..."
onChange={(e) => setNote(e.target.value)}
value={note}
className="auto-sizing"
/>
</div>
</div>
<Button className="w-full gap-x-1">
<Button
onClick={handleSendZap}
loading={isLoading}
className="w-full gap-x-1"
>
<span>Send Zap</span>
<HiOutlineLightningBolt className="h-4 w-4" />
</Button>

View File

@ -1,12 +1,12 @@
"use client"
"use client";
import * as React from "react"
import { DialogProps } from "@radix-ui/react-dialog"
import { MagnifyingGlassIcon } from "@radix-ui/react-icons"
import { Command as CommandPrimitive } from "cmdk"
import * as React from "react";
import { DialogProps } from "@radix-ui/react-dialog";
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
import { Command as CommandPrimitive } from "cmdk";
import { cn } from "@/lib/utils"
import { Dialog, DialogContent } from "@/components/ui/dialog"
import { cn } from "@/lib/utils";
import { Dialog, DialogContent } from "@/components/ui/dialog";
const Command = React.forwardRef<
React.ElementRef<typeof CommandPrimitive>,
@ -16,12 +16,12 @@ const Command = React.forwardRef<
ref={ref}
className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
className
className,
)}
{...props}
/>
))
Command.displayName = CommandPrimitive.displayName
));
Command.displayName = CommandPrimitive.displayName;
interface CommandDialogProps extends DialogProps {}
@ -34,8 +34,8 @@ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
</Command>
</DialogContent>
</Dialog>
)
}
);
};
const CommandInput = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Input>,
@ -47,14 +47,14 @@ const CommandInput = React.forwardRef<
ref={ref}
className={cn(
"flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50",
className
className,
)}
{...props}
/>
</div>
))
));
CommandInput.displayName = CommandPrimitive.Input.displayName
CommandInput.displayName = CommandPrimitive.Input.displayName;
const CommandList = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.List>,
@ -65,9 +65,9 @@ const CommandList = React.forwardRef<
className={cn("max-h-[300px] overflow-y-auto overflow-x-hidden", className)}
{...props}
/>
))
));
CommandList.displayName = CommandPrimitive.List.displayName
CommandList.displayName = CommandPrimitive.List.displayName;
const CommandEmpty = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Empty>,
@ -78,9 +78,9 @@ const CommandEmpty = React.forwardRef<
className="py-6 text-center text-sm"
{...props}
/>
))
));
CommandEmpty.displayName = CommandPrimitive.Empty.displayName
CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
const CommandGroup = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Group>,
@ -90,13 +90,13 @@ const CommandGroup = React.forwardRef<
ref={ref}
className={cn(
"overflow-hidden p-1 text-foreground [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className
className,
)}
{...props}
/>
))
));
CommandGroup.displayName = CommandPrimitive.Group.displayName
CommandGroup.displayName = CommandPrimitive.Group.displayName;
const CommandSeparator = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Separator>,
@ -107,8 +107,8 @@ const CommandSeparator = React.forwardRef<
className={cn("-mx-1 h-px bg-border", className)}
{...props}
/>
))
CommandSeparator.displayName = CommandPrimitive.Separator.displayName
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
const CommandItem = React.forwardRef<
React.ElementRef<typeof CommandPrimitive.Item>,
@ -118,13 +118,13 @@ const CommandItem = React.forwardRef<
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
className
className,
)}
{...props}
/>
))
));
CommandItem.displayName = CommandPrimitive.Item.displayName
CommandItem.displayName = CommandPrimitive.Item.displayName;
const CommandShortcut = ({
className,
@ -134,13 +134,13 @@ const CommandShortcut = ({
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className
className,
)}
{...props}
/>
)
}
CommandShortcut.displayName = "CommandShortcut"
);
};
CommandShortcut.displayName = "CommandShortcut";
export {
Command,
@ -152,4 +152,4 @@ export {
CommandItem,
CommandShortcut,
CommandSeparator,
}
};

View File

@ -1,16 +1,16 @@
"use client"
"use client";
import * as React from "react"
import * as DialogPrimitive from "@radix-ui/react-dialog"
import { Cross2Icon } from "@radix-ui/react-icons"
import * as React from "react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { Cross2Icon } from "@radix-ui/react-icons";
import { cn } from "@/lib/utils"
import { cn } from "@/lib/utils";
const Dialog = DialogPrimitive.Root
const Dialog = DialogPrimitive.Root;
const DialogTrigger = DialogPrimitive.Trigger
const DialogTrigger = DialogPrimitive.Trigger;
const DialogPortal = DialogPrimitive.Portal
const DialogPortal = DialogPrimitive.Portal;
const DialogOverlay = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Overlay>,
@ -19,13 +19,13 @@ const DialogOverlay = React.forwardRef<
<DialogPrimitive.Overlay
ref={ref}
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
"z-overlay- fixed inset-0 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className,
)}
{...props}
/>
))
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
const DialogContent = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Content>,
@ -36,20 +36,20 @@ const DialogContent = React.forwardRef<
<DialogPrimitive.Content
ref={ref}
className={cn(
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
className
"z-overlay fixed left-[50%] top-[50%] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg md:w-full",
className,
)}
{...props}
>
{children}
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity data-[state=open]:bg-accent data-[state=open]:text-muted-foreground hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none">
<Cross2Icon className="h-4 w-4" />
<span className="sr-only">Close</span>
</DialogPrimitive.Close>
</DialogPrimitive.Content>
</DialogPortal>
))
DialogContent.displayName = DialogPrimitive.Content.displayName
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
const DialogHeader = ({
className,
@ -58,12 +58,12 @@ const DialogHeader = ({
<div
className={cn(
"flex flex-col space-y-1.5 text-center sm:text-left",
className
className,
)}
{...props}
/>
)
DialogHeader.displayName = "DialogHeader"
);
DialogHeader.displayName = "DialogHeader";
const DialogFooter = ({
className,
@ -72,12 +72,12 @@ const DialogFooter = ({
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
className,
)}
{...props}
/>
)
DialogFooter.displayName = "DialogFooter"
);
DialogFooter.displayName = "DialogFooter";
const DialogTitle = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Title>,
@ -87,12 +87,12 @@ const DialogTitle = React.forwardRef<
ref={ref}
className={cn(
"text-lg font-semibold leading-none tracking-tight",
className
className,
)}
{...props}
/>
))
DialogTitle.displayName = DialogPrimitive.Title.displayName
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
const DialogDescription = React.forwardRef<
React.ElementRef<typeof DialogPrimitive.Description>,
@ -103,8 +103,8 @@ const DialogDescription = React.forwardRef<
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
DialogDescription.displayName = DialogPrimitive.Description.displayName
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
export {
Dialog,
@ -116,4 +116,4 @@ export {
DialogFooter,
DialogTitle,
DialogDescription,
}
};

View File

@ -28,7 +28,7 @@ export async function sendZap(
) {
log("func", "sendZap");
const event = await new NDKEvent(ndk, _event);
log("info", event.toString());
log("info", JSON.stringify(event));
const pr = await event.zap(amount * 1000, comment);
if (!pr) {
log("info", "No PR");

View File

@ -88,7 +88,7 @@ export function log(
type: "info" | "error" | "warn" | "func",
...args: unknown[]
) {
const isOn = true;
const isOn = process.env.NODE_ENV === "development";
if (!isOn) return;
const consoleType = type === "func" ? "info" : type;
const items = [...args].map((a) => `%c${a}`);
@ -115,3 +115,8 @@ export function btcToSats(amount: number) {
export function satsToBtc(amount: number) {
return amount / 100_000_000;
}
export function stopPropagation(e: React.MouseEvent) {
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
e.preventDefault();
}

View File

@ -39,6 +39,7 @@
"dayjs": "^1.11.10",
"focus-trap-react": "^10.2.3",
"framer-motion": "^10.16.4",
"jotai": "^2.4.3",
"next": "13.5.4",
"next-themes": "^0.2.1",
"node-html-parser": "^6.1.10",