diff --git a/package.json b/package.json
index 2afdea6..1788583 100644
--- a/package.json
+++ b/package.json
@@ -5,18 +5,12 @@
"@emoji-mart/data": "^1.1.2",
"@emoji-mart/react": "^1.1.1",
"@noble/curves": "^1.2.0",
- "@radix-ui/react-collapsible": "^1.0.3",
- "@radix-ui/react-dialog": "^1.0.4",
- "@radix-ui/react-progress": "^1.0.3",
- "@radix-ui/react-tabs": "^1.0.4",
- "@radix-ui/react-toggle": "^1.0.3",
- "@react-hook/resize-observer": "^1.2.6",
"@scure/base": "^1.1.3",
- "@snort/shared": "^1.0.12",
- "@snort/system": "^1.2.1",
- "@snort/system-react": "^1.2.1",
+ "@snort/shared": "^1.0.14",
+ "@snort/system": "^1.2.12",
+ "@snort/system-react": "^1.2.12",
"@snort/system-wasm": "^1.0.2",
- "@snort/system-web": "^1.0.4",
+ "@snort/system-web": "^1.2.11",
"@szhsin/react-menu": "^4.0.2",
"@types/webscopeio__react-textarea-autocomplete": "^4.7.2",
"@void-cat/api": "^1.0.7",
diff --git a/public/zap-stream.svg b/public/zap-stream.svg
index b0fb3a1..e7f3870 100644
--- a/public/zap-stream.svg
+++ b/public/zap-stream.svg
@@ -1,3 +1,3 @@
-
{sortEndpoints(info.endpoints).map(a => (
- setEndpoint(a)}>
{a.name}
-
+
))}
@@ -167,23 +170,18 @@ export function NostrProviderDialog({
-
-
-
+
-
-
-
-
+
window.navigator.clipboard.writeText(ep?.key ?? "")}>
-
+
@@ -191,16 +189,16 @@ export function NostrProviderDialog({
-
+
-
setTopup(true)}>
+ setTopup(true)}>
-
+
@@ -212,7 +210,7 @@ export function NostrProviderDialog({
{ep?.capabilities?.map(a => (
-
{parseCapability(a)}
+
{parseCapability(a)}
))}
@@ -264,18 +262,18 @@ export function NostrProviderDialog({
{info.forwards?.map(a => (
<>
-
{a.name}
-
{a.name}
+
{
await provider.removeForward(a.id);
+ await loadInfo();
}}>
-
+
>
))}
- {}} />
+
);
}
@@ -419,40 +417,35 @@ function AddForwardInputs({
}
return (
-
+
-
-
-
-
- setName(e.target.value)}
- />
-
-
-
+
setTarget(e.target.value)}
+ className="flex-1"
+ placeholder={formatMessage({ defaultMessage: "Display name", id: "dOQCL8" })}
+ value={name}
+ onChange={e => setName(e.target.value)}
/>
-
+ setTarget(e.target.value)}
+ />
+
-
+
{error &&
{error}}
);
diff --git a/src/element/notifications-button.tsx b/src/element/notifications-button.tsx
index 02f5cc6..36d552c 100644
--- a/src/element/notifications-button.tsx
+++ b/src/element/notifications-button.tsx
@@ -1,10 +1,10 @@
-import AsyncButton from "./async-button";
import { useLogin } from "@/hooks/login";
import { NostrStreamProvider } from "@/providers";
import { base64 } from "@scure/base";
import { unwrap } from "@snort/shared";
import { useEffect, useState } from "react";
import { Icon } from "./icon";
+import { DefaultButton } from "./buttons";
export function NotificationsButton({ host, service }: { host: string; service: string }) {
const login = useLogin();
@@ -80,8 +80,8 @@ export function NotificationsButton({ host, service }: { host: string; service:
}, []);
return (
-
+
-
+
);
}
diff --git a/src/element/pill.tsx b/src/element/pill.tsx
new file mode 100644
index 0000000..66a27a5
--- /dev/null
+++ b/src/element/pill.tsx
@@ -0,0 +1,6 @@
+import classNames from "classnames";
+import { HTMLProps } from "react";
+
+export default function Pill({ children, selected, className, ...props }: HTMLProps
) {
+ return {children}
+}
\ No newline at end of file
diff --git a/src/element/raid-menu.tsx b/src/element/raid-menu.tsx
index df24891..a40cfa7 100644
--- a/src/element/raid-menu.tsx
+++ b/src/element/raid-menu.tsx
@@ -6,9 +6,9 @@ import { Profile } from "./profile";
import { useLogin } from "@/hooks/login";
import { useContext, useState } from "react";
import { NostrLink, parseNostrLink } from "@snort/system";
-import AsyncButton from "./async-button";
import { SnortContext } from "@snort/system-react";
import { LIVE_STREAM_RAID } from "@/const";
+import { DefaultButton } from "./buttons";
export function DashboardRaidMenu({ link, onClose }: { link: NostrLink; onClose: () => void }) {
const system = useContext(SnortContext);
@@ -41,13 +41,13 @@ export function DashboardRaidMenu({ link, onClose }: { link: NostrLink; onClose:
-
+
{livePubkeys.map(a => (
{
const liveEvent = live.find(b => getHost(b) === a);
if (liveEvent) {
@@ -60,7 +60,7 @@ export function DashboardRaidMenu({ link, onClose }: { link: NostrLink; onClose:
-
+
@@ -68,16 +68,16 @@ export function DashboardRaidMenu({ link, onClose }: { link: NostrLink; onClose:
-
+
setMsg(e.target.value)} />
-
+
-
+
);
}
diff --git a/src/element/send-zap.css b/src/element/send-zap.css
index de83ada..5a11927 100644
--- a/src/element/send-zap.css
+++ b/src/element/send-zap.css
@@ -19,11 +19,6 @@
text-align: center;
}
-.send-zap .pill.active {
- color: inherit;
- background: #353535;
-}
-
.send-zap p {
margin: 0 0 8px 0;
font-weight: 500;
diff --git a/src/element/send-zap.tsx b/src/element/send-zap.tsx
index 80fa086..4604e82 100644
--- a/src/element/send-zap.tsx
+++ b/src/element/send-zap.tsx
@@ -1,5 +1,4 @@
import "./send-zap.css";
-import * as Dialog from "@radix-ui/react-dialog";
import { type ReactNode, useEffect, useState } from "react";
import { LNURL } from "@snort/shared";
import { EventPublisher, NostrEvent } from "@snort/system";
@@ -9,12 +8,14 @@ import { FormattedMessage, FormattedNumber } from "react-intl";
import { formatSats } from "../number";
import { Icon } from "./icon";
-import AsyncButton from "./async-button";
import QrCode from "./qr-code";
import { useLogin } from "@/hooks/login";
import Copy from "./copy";
import { defaultRelays } from "@/const";
import { useRates } from "@/hooks/rates";
+import { DefaultButton } from "./buttons";
+import Modal from "./modal";
+import Pill from "./pill";
export interface LNURLLike {
get name(): string;
@@ -110,25 +111,25 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
return (
<>
-
{
setIsFiat(false);
setAmount(satsAmounts[0]);
}}>
SATS
-
-
+ {
setIsFiat(true);
setAmount(usdAmounts[0]);
}}>
USD
-
+
-
+
)}
-
+
{(isFiat ? usdAmounts : satsAmounts).map(a => (
-
setAmount(a)}>
+ setAmount(a)}>
{isFiat ? `$${a.toLocaleString()}` : formatSats(a)}
-
+
))}
@@ -167,9 +168,9 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
)}
>
);
@@ -185,18 +186,18 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
- onFinish()}>
+ onFinish()}>
-
+
>
);
}
return (
-
+
-
+
{input()}
{payInvoice()}
@@ -205,29 +206,21 @@ export function SendZaps({ lnurl, pubkey, aTag, eTag, targetName, onFinish }: Se
}
export function SendZapsDialog(props: Omit
) {
- const [isOpen, setIsOpen] = useState(false);
- return (
-
-
- {props.button ? (
- props.button
- ) : (
-
-
-
-
-
-
- )}
-
-
-
-
-
- setIsOpen(false)} />
-
-
-
-
+ const [open, setOpen] = useState(false);
+ return (<>
+ {props.button ? (
+ props.button
+ ) : (
+ setOpen(true)}>
+
+
+
+
+
+ )}
+ {open && setOpen(false)}>
+ setOpen(false)} />
+ }
+ >
);
}
diff --git a/src/element/share-menu.tsx b/src/element/share-menu.tsx
index 3162470..4f20a58 100644
--- a/src/element/share-menu.tsx
+++ b/src/element/share-menu.tsx
@@ -1,27 +1,37 @@
import { Menu, MenuItem } from "@szhsin/react-menu";
-import * as Dialog from "@radix-ui/react-dialog";
-import { unwrap } from "@snort/shared";
-import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
-import { FormattedMessage } from "react-intl";
+import { NostrEvent, NostrLink, NostrPrefix } from "@snort/system";
+import { FormattedMessage, useIntl } from "react-intl";
import { useContext, useState } from "react";
import { SnortContext } from "@snort/system-react";
import { Icon } from "./icon";
import { Textarea } from "./textarea";
-import { findTag } from "@/utils";
-import AsyncButton from "./async-button";
+import { getHost } from "@/utils";
import { useLogin } from "@/hooks/login";
+import { DefaultButton } from "./buttons";
+import Modal from "./modal";
type ShareOn = "nostr" | "twitter";
export function ShareMenu({ ev }: { ev: NostrEvent }) {
const system = useContext(SnortContext);
const [share, setShare] = useState();
- const [message, setMessage] = useState("");
const login = useLogin();
+ const { formatMessage } = useIntl();
+ const host = getHost(ev);
- const naddr = encodeTLV(NostrPrefix.Address, unwrap(findTag(ev, "d")), undefined, ev.kind, ev.pubkey);
- const link = `https://zap.stream/${naddr}`;
+ const defaultMyMsg = formatMessage({
+ defaultMessage: "Come check out my stream on zap.stream!\n\n{link}\n\n", id: 'HsgeUk'
+ }, {
+ link: `https://${window.location.host}/${NostrLink.fromEvent(ev).encode()}`
+ });
+ const defaultHostMsg = formatMessage({
+ defaultMessage: "Come check out {name} stream on zap.stream!\n\n{link}", id: 'PUymyQ'
+ }, {
+ name: `nostr:${new NostrLink(NostrPrefix.PublicKey, host ?? ev.pubkey).encode()}`,
+ link: `https://${window.location.host}/${NostrLink.fromEvent(ev).encode()}`
+ });
+ const [message, setMessage] = useState(login?.pubkey === host ? defaultMyMsg : defaultHostMsg);
async function sendMessage() {
const pub = login?.publisher();
@@ -40,45 +50,37 @@ export function ShareMenu({ ev }: { ev: NostrEvent }) {
gap={5}
menuClassName="ctx-menu"
menuButton={
-
+
-
+
}>
- setShare(undefined)}>
-
-
-
-
-
-
-
+ {share && setShare(undefined)}>
+
+
+
+
+
+ }
>
);
}
diff --git a/src/element/state-pill.tsx b/src/element/state-pill.tsx
index 861d328..342776f 100644
--- a/src/element/state-pill.tsx
+++ b/src/element/state-pill.tsx
@@ -2,19 +2,20 @@ import { HTMLProps } from "react";
import "./state-pill.css";
import classNames from "classnames";
import { StreamState } from "@/const";
+import Pill from "./pill";
type StatePillProps = { state: StreamState } & HTMLProps;
export function StatePill({ state, ...props }: StatePillProps) {
return (
-
{state}
-
+
);
}
diff --git a/src/element/stream-cards.css b/src/element/stream-cards.css
deleted file mode 100644
index 2ef5e63..0000000
--- a/src/element/stream-cards.css
+++ /dev/null
@@ -1,177 +0,0 @@
-.stream-cards,
-.edit-container {
- display: none;
-}
-
-@media (min-width: 1020px) {
- .edit-container {
- display: block;
- }
- .stream-cards {
- display: grid;
- align-items: flex-start;
- grid-template-columns: repeat(2, 1fr);
- gap: 32px;
- margin-top: 12px;
- flex-wrap: wrap;
- }
-}
-
-@media (min-width: 1600px) {
- .stream-cards {
- grid-template-columns: repeat(3, 1fr);
- }
-}
-
-@media (min-width: 2100px) {
- .stream-cards {
- grid-template-columns: repeat(4, 1fr);
- }
-}
-
-.card-container {
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-
-.editor-buttons {
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-
-.stream-card {
- display: flex;
- align-self: flex-start;
- flex-direction: column;
- gap: 16px;
- flex: 1;
- width: 100%;
- overflow-wrap: break-word;
-}
-
-.stream-card.image-card {
- padding: 0;
- background: transparent;
-}
-
-.stream-card .card-title {
- margin: 0;
- font-size: 22px;
- font-style: normal;
- font-weight: 600;
- line-height: normal;
-}
-
-@media (min-width: 1900px) {
- .stream-card {
- width: 342px;
- }
-}
-
-.add-card {
- align-items: center;
- justify-content: center;
-}
-
-.add-card .add-icon {
- color: var(--text-muted);
- cursor: pointer;
- width: 24px;
- height: 24px;
-}
-
-.new-card {
- display: flex;
- flex-direction: column;
- gap: 12px;
-}
-
-.new-card h3 {
- margin: 0;
- margin-bottom: 12px;
- font-weight: 500;
-}
-
-.new-card input[type="text"] {
- background: #262626;
- padding: 8px 16px;
- border-radius: 16px;
- width: unset;
- margin-bottom: 8px;
- font-size: 16px;
- font-weight: 500;
- line-height: 20px;
-}
-
-.new-card textarea {
- width: unset;
- background: #262626;
- padding: 8px 16px;
- border-radius: 16px;
- margin-bottom: 8px;
- resize: vertical;
- min-height: 210px;
-}
-
-.form-control {
- display: flex;
- flex-direction: column;
-}
-
-.form-control label {
- margin-bottom: 8px;
-}
-
-.new-card-buttons {
- display: flex;
- flex-direction: column;
- gap: 12px;
- margin-top: 12px;
-}
-
-.help-text {
- color: var(--text-muted);
- font-size: 14px;
- margin-left: 6px;
-}
-
-.help-text a {
- color: var(--primary);
-}
-
-.add-button {
- height: 50px;
-}
-
-.delete-button {
- background: transparent;
- color: var(--text-danger);
-}
-
-@keyframes shake {
- 0% {
- transform: rotate(0deg);
- }
- 25% {
- transform: rotate(5deg);
- }
- 50% {
- transform: rotate(0eg);
- }
- 75% {
- transform: rotate(-5deg);
- }
- 100% {
- transform: rotate(0deg);
- }
-}
-
-.stream-card .card-image {
- max-width: 343px;
-}
-
-.stream-card {
- max-width: 343px;
-}
diff --git a/src/element/stream-cards.tsx b/src/element/stream-cards.tsx
deleted file mode 100644
index 7082241..0000000
--- a/src/element/stream-cards.tsx
+++ /dev/null
@@ -1,455 +0,0 @@
-import "./stream-cards.css";
-
-import { Suspense, forwardRef, lazy, useContext, useState } from "react";
-import { FormattedMessage, useIntl } from "react-intl";
-import * as Dialog from "@radix-ui/react-dialog";
-import { DndProvider, useDrag, useDrop } from "react-dnd";
-import { HTML5Backend } from "react-dnd-html5-backend";
-
-import { removeUndefined, unwrap } from "@snort/shared";
-import { NostrLink, TaggedNostrEvent } from "@snort/system";
-import { SnortContext } from "@snort/system-react";
-
-const Markdown = lazy(() => import("./markdown"));
-import { Toggle } from "./toggle";
-import { Icon } from "./icon";
-import { ExternalLink } from "./external-link";
-import { FileUploader } from "./file-uploader";
-import { useLogin } from "@/hooks/login";
-import { useCards, useUserCards } from "@/hooks/cards";
-import { CARD, USER_CARDS } from "@/const";
-import { findTag } from "@/utils";
-import { Login } from "@/index";
-import type { Tags } from "@/types";
-import AsyncButton from "./async-button";
-
-interface CardType {
- identifier: string;
- content: string;
- title?: string;
- image?: string;
- link?: string;
-}
-
-type NewCard = Omit;
-
-function isEmpty(s?: string) {
- return !s || s.trim().length === 0;
-}
-
-interface CardPreviewProps extends NewCard {
- style: object;
-}
-
-const CardPreview = forwardRef(({ style, title, link, image, content }: CardPreviewProps, ref) => {
- const isImageOnly = !isEmpty(image) && isEmpty(content) && isEmpty(title);
- return (
- '
- ref={ref}
- style={style}>
- {title &&
{title}
}
- {image &&
- (link && link?.length > 0 ? (
-
-
-
- ) : (
-
![{title}]({image})
- ))}
-
-
-
-
- );
-});
-
-interface CardProps {
- canEdit?: boolean;
- ev: TaggedNostrEvent;
- cards: TaggedNostrEvent[];
-}
-
-interface CardItem {
- identifier: string;
-}
-
-function Card({ canEdit, ev, cards }: CardProps) {
- const system = useContext(SnortContext);
- const login = useLogin();
- const identifier = findTag(ev, "d") ?? "";
- const title = findTag(ev, "title") || findTag(ev, "subject");
- const image = findTag(ev, "image");
- const link = findTag(ev, "r");
- const content = ev.content;
- const evCard = { title, image, link, content, identifier };
- const tags = removeUndefined(cards.map(a => NostrLink.fromEvent(a).toEventTag()));
- const [style, dragRef] = useDrag(
- () => ({
- type: "card",
- item: { identifier } as CardItem,
- canDrag: () => {
- return Boolean(canEdit);
- },
- collect: monitor => {
- const isDragging = monitor.isDragging();
- return {
- opacity: isDragging ? 0.1 : 1,
- cursor: !canEdit ? "auto" : isDragging ? "grabbing" : "grab",
- };
- },
- }),
- [canEdit, identifier]
- );
-
- function findTagByIdentifier(d: string) {
- return tags.find(t => t[1].endsWith(`:${d}`));
- }
-
- const [dropStyle, dropRef] = useDrop(
- () => ({
- accept: ["card"],
- canDrop: () => {
- return Boolean(canEdit);
- },
- collect: monitor => {
- const isOvering = monitor.isOver({ shallow: true });
- return {
- opacity: isOvering ? 0.3 : 1,
- animation: isOvering ? "shake 0.1s 3" : "",
- };
- },
- async drop(item) {
- const typed = item as CardItem;
- if (identifier === typed.identifier) {
- return;
- }
- const newItem = findTagByIdentifier(typed.identifier);
- const oldItem = findTagByIdentifier(identifier);
- const newTags = tags.map(t => {
- if (t === oldItem) {
- return newItem;
- }
- if (t === newItem) {
- return oldItem;
- }
- return t;
- }) as Tags;
- const pub = login?.publisher();
- if (pub) {
- const userCardsEv = await pub.generic(eb => {
- eb.kind(USER_CARDS).content("");
- for (const tag of newTags) {
- eb.tag(tag);
- }
- return eb;
- });
- console.debug(userCardsEv);
- await system.BroadcastEvent(userCardsEv);
- Login.setCards(newTags, userCardsEv.created_at);
- }
- },
- }),
- [canEdit, tags, identifier]
- );
-
- const card = (
-
- );
- const editor = canEdit && (
-
-
-
- );
- return canEdit ? (
-
- {card}
- {editor}
-
- ) : (
- {card}
- );
-}
-
-interface CardDialogProps {
- header?: string;
- cta?: string;
- cancelCta?: string;
- card?: CardType;
- onSave(ev: NewCard): void;
- onCancel(): void;
-}
-
-function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDialogProps) {
- const [title, setTitle] = useState(card?.title ?? "");
- const [image, setImage] = useState(card?.image ?? "");
- const [content, setContent] = useState(card?.content ?? "");
- const [link, setLink] = useState(card?.link ?? "");
- const { formatMessage } = useIntl();
-
- return (
-
-
{header || }
-
-
- setTitle(e.target.value)}
- placeholder={formatMessage({ defaultMessage: "e.g. about me", id: "k21gTS" })}
- />
-
-
-
- setImage("")} />
-
-
-
- setLink(e.target.value)}
- />
-
-
-
-
-
-
onSave({ title, image, content, link })}>
- {cta || }
-
-
- {cancelCta || }
-
-
-
- );
-}
-
-interface EditCardProps {
- card: CardType;
- cards: TaggedNostrEvent[];
-}
-
-function EditCard({ card, cards }: EditCardProps) {
- const system = useContext(SnortContext);
- const login = useLogin();
- const [isOpen, setIsOpen] = useState(false);
- const identifier = card.identifier;
- const tags = removeUndefined(cards.map(a => NostrLink.fromEvent(a).toEventTag()));
- const { formatMessage } = useIntl();
-
- async function editCard({ title, image, link, content }: CardType) {
- const pub = login?.publisher();
- if (pub) {
- const ev = await pub.generic(eb => {
- eb.kind(CARD).content(content).tag(["d", card.identifier]);
- if (title && title?.length > 0) {
- eb.tag(["title", title]);
- }
- if (image && image?.length > 0) {
- eb.tag(["image", image]);
- }
- if (link && link?.length > 0) {
- eb.tag(["r", link]);
- }
- return eb;
- });
- console.debug(ev);
- await system.BroadcastEvent(ev);
- setIsOpen(false);
- }
- }
-
- async function onCancel() {
- const pub = login?.publisher();
- if (pub) {
- const newTags = tags.filter(t => !t[1].endsWith(`:${identifier}`));
- const userCardsEv = await pub.generic(eb => {
- eb.kind(USER_CARDS).content("");
- for (const tag of newTags) {
- eb.tag(tag);
- }
- return eb;
- });
-
- console.debug(userCardsEv);
- await system.BroadcastEvent(userCardsEv);
- Login.setCards(newTags, userCardsEv.created_at);
- setIsOpen(false);
- }
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-interface AddCardProps {
- cards: TaggedNostrEvent[];
-}
-
-function AddCard({ cards }: AddCardProps) {
- const system = useContext(SnortContext);
- const login = useLogin();
- const tags = removeUndefined(cards.map(a => NostrLink.fromEvent(a).toEventTag()));
- const [isOpen, setIsOpen] = useState(false);
-
- async function createCard({ title, image, link, content }: NewCard) {
- const pub = login?.publisher();
- if (pub) {
- const ev = await pub.generic(eb => {
- const d = String(Date.now());
- eb.kind(CARD).content(content).tag(["d", d]);
- if (title && title?.length > 0) {
- eb.tag(["title", title]);
- }
- if (image && image?.length > 0) {
- eb.tag(["image", image]);
- }
- if (link && link?.length > 0) {
- eb.tag(["r", link]);
- }
- return eb;
- });
- const userCardsEv = await pub.generic(eb => {
- eb.kind(USER_CARDS).content("");
- tags.forEach(a => eb.tag(a));
- eb.tag(unwrap(NostrLink.fromEvent(ev).toEventTag()));
- return eb;
- });
-
- console.debug(ev);
- console.debug(userCardsEv);
-
- await system.BroadcastEvent(ev);
- await system.BroadcastEvent(userCardsEv);
- setIsOpen(false);
- }
- }
-
- function onCancel() {
- setIsOpen(false);
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
-
-interface StreamCardEditorProps {
- pubkey: string;
- tags: Tags;
-}
-
-export function StreamCardEditor({ pubkey, tags }: StreamCardEditorProps) {
- const cards = useUserCards(pubkey, tags, true);
- const [isEditing, setIsEditing] = useState(false);
- return (
- <>
-
- {cards.map(ev => (
-
- ))}
- {isEditing &&
}
-
-
-
-
- >
- );
-}
-
-interface StreamCardsProps {
- host: string;
-}
-
-export function ReadOnlyStreamCards({ host }: StreamCardsProps) {
- const cards = useCards(host);
- return (
-
- {cards.map(ev => (
-
- ))}
-
- );
-}
-
-export function StreamCards({ host }: StreamCardsProps) {
- const login = useLogin();
- const canEdit = login?.pubkey === host;
- return (
-
- {canEdit ? (
-
- ) : (
-
- )}
-
- );
-}
diff --git a/src/element/stream-cards/add-card.tsx b/src/element/stream-cards/add-card.tsx
new file mode 100644
index 0000000..c95b53d
--- /dev/null
+++ b/src/element/stream-cards/add-card.tsx
@@ -0,0 +1,64 @@
+import { useContext, useState } from "react";
+import { removeUndefined, unwrap } from "@snort/shared";
+import { NostrLink, TaggedNostrEvent } from "@snort/system";
+import { SnortContext } from "@snort/system-react";
+import { Icon } from "../icon";
+import { useLogin } from "@/hooks/login";
+import { CARD, USER_CARDS } from "@/const";
+import Modal from "../modal";
+import { NewCard } from ".";
+import { CardDialog } from "./new-card";
+
+
+interface AddCardProps {
+ cards: TaggedNostrEvent[];
+}
+
+export function AddCard({ cards }: AddCardProps) {
+ const system = useContext(SnortContext);
+ const login = useLogin();
+ const tags = removeUndefined(cards.map(a => NostrLink.fromEvent(a).toEventTag()));
+ const [open, setOpen] = useState(false);
+
+ async function createCard({ title, image, link, content }: NewCard) {
+ const pub = login?.publisher();
+ if (pub) {
+ const ev = await pub.generic(eb => {
+ const d = String(Date.now());
+ eb.kind(CARD).content(content).tag(["d", d]);
+ if (title && title?.length > 0) {
+ eb.tag(["title", title]);
+ }
+ if (image && image?.length > 0) {
+ eb.tag(["image", image]);
+ }
+ if (link && link?.length > 0) {
+ eb.tag(["r", link]);
+ }
+ return eb;
+ });
+ const userCardsEv = await pub.generic(eb => {
+ eb.kind(USER_CARDS).content("");
+ tags.forEach(a => eb.tag(a));
+ eb.tag(unwrap(NostrLink.fromEvent(ev).toEventTag()));
+ return eb;
+ });
+
+ console.debug(ev);
+ console.debug(userCardsEv);
+
+ await system.BroadcastEvent(ev);
+ await system.BroadcastEvent(userCardsEv);
+ setOpen(false);
+ }
+ }
+
+ return (
+ setOpen(true)}>
+
+ {open && setOpen(false)}>
+ setOpen(false)} />
+ }
+
+ );
+}
diff --git a/src/element/stream-cards/card-item.tsx b/src/element/stream-cards/card-item.tsx
new file mode 100644
index 0000000..1d68a1e
--- /dev/null
+++ b/src/element/stream-cards/card-item.tsx
@@ -0,0 +1,116 @@
+import { useContext } from "react";
+import { useDrag, useDrop } from "react-dnd";
+import { removeUndefined } from "@snort/shared";
+import { NostrLink, TaggedNostrEvent } from "@snort/system";
+import { SnortContext } from "@snort/system-react";
+import { CardItem } from ".";
+import { USER_CARDS } from "@/const";
+import { useLogin } from "@/hooks/login";
+import { Login } from "@/index";
+import { Tags } from "@/types";
+import { findTag } from "@/utils";
+import { EditCard } from "./edit-card";
+import { CardPreview } from "./preview";
+
+interface CardProps {
+ canEdit?: boolean;
+ ev: TaggedNostrEvent;
+ cards: TaggedNostrEvent[];
+}
+export function Card({ canEdit, ev, cards }: CardProps) {
+ const system = useContext(SnortContext);
+ const login = useLogin();
+ const identifier = findTag(ev, "d") ?? "";
+ const title = findTag(ev, "title") || findTag(ev, "subject");
+ const image = findTag(ev, "image");
+ const link = findTag(ev, "r");
+ const content = ev.content;
+ const evCard = { title, image, link, content, identifier };
+ const tags = removeUndefined(cards.map(a => NostrLink.fromEvent(a).toEventTag()));
+ const [style, dragRef] = useDrag(
+ () => ({
+ type: "card",
+ item: { identifier } as CardItem,
+ canDrag: () => {
+ return Boolean(canEdit);
+ },
+ collect: monitor => {
+ const isDragging = monitor.isDragging();
+ return {
+ opacity: isDragging ? 0.1 : 1,
+ cursor: !canEdit ? "auto" : isDragging ? "grabbing" : "grab",
+ };
+ },
+ }),
+ [canEdit, identifier]
+ );
+
+ function findTagByIdentifier(d: string) {
+ return tags.find(t => t[1].endsWith(`:${d}`));
+ }
+
+ const [dropStyle, dropRef] = useDrop(
+ () => ({
+ accept: ["card"],
+ canDrop: () => {
+ return Boolean(canEdit);
+ },
+ collect: monitor => {
+ const isOvering = monitor.isOver({ shallow: true });
+ return {
+ opacity: isOvering ? 0.3 : 1,
+ animation: isOvering ? "shake 0.1s 3" : "",
+ };
+ },
+ async drop(item) {
+ const typed = item as CardItem;
+ if (identifier === typed.identifier) {
+ return;
+ }
+ const newItem = findTagByIdentifier(typed.identifier);
+ const oldItem = findTagByIdentifier(identifier);
+ const newTags = tags.map(t => {
+ if (t === oldItem) {
+ return newItem;
+ }
+ if (t === newItem) {
+ return oldItem;
+ }
+ return t;
+ }) as Tags;
+ const pub = login?.publisher();
+ if (pub) {
+ const userCardsEv = await pub.generic(eb => {
+ eb.kind(USER_CARDS).content("");
+ for (const tag of newTags) {
+ eb.tag(tag);
+ }
+ return eb;
+ });
+ console.debug(userCardsEv);
+ await system.BroadcastEvent(userCardsEv);
+ Login.setCards(newTags, userCardsEv.created_at);
+ }
+ },
+ }),
+ [canEdit, tags, identifier]
+ );
+
+ const card = (
+
+ );
+ if (canEdit) {
+ return
+ {card}
+
+
+ }
+ return card;
+}
diff --git a/src/element/stream-cards/edit-card.tsx b/src/element/stream-cards/edit-card.tsx
new file mode 100644
index 0000000..cd985b6
--- /dev/null
+++ b/src/element/stream-cards/edit-card.tsx
@@ -0,0 +1,84 @@
+import { CARD, USER_CARDS } from "@/const";
+import { useLogin } from "@/hooks/login";
+import { Login } from "@/index";
+import { removeUndefined } from "@snort/shared";
+import { TaggedNostrEvent, NostrLink } from "@snort/system";
+import { SnortContext } from "@snort/system-react";
+import { useContext, useState } from "react";
+import { useIntl, FormattedMessage } from "react-intl";
+import { DefaultButton } from "../buttons";
+import Modal from "../modal";
+import { CardDialog } from "./new-card";
+import { CardType } from ".";
+
+interface EditCardProps {
+ card: CardType;
+ cards: TaggedNostrEvent[];
+}
+
+export function EditCard({ card, cards }: EditCardProps) {
+ const system = useContext(SnortContext);
+ const login = useLogin();
+ const [open, setOpen] = useState(false);
+ const identifier = card.identifier;
+ const tags = removeUndefined(cards.map(a => NostrLink.fromEvent(a).toEventTag()));
+ const { formatMessage } = useIntl();
+
+ async function editCard({ title, image, link, content }: CardType) {
+ const pub = login?.publisher();
+ if (pub) {
+ const ev = await pub.generic(eb => {
+ eb.kind(CARD).content(content).tag(["d", card.identifier]);
+ if (title && title?.length > 0) {
+ eb.tag(["title", title]);
+ }
+ if (image && image?.length > 0) {
+ eb.tag(["image", image]);
+ }
+ if (link && link?.length > 0) {
+ eb.tag(["r", link]);
+ }
+ return eb;
+ });
+ console.debug(ev);
+ await system.BroadcastEvent(ev);
+ setOpen(false);
+ }
+ }
+
+ async function onCancel() {
+ const pub = login?.publisher();
+ if (pub) {
+ const newTags = tags.filter(t => !t[1].endsWith(`:${identifier}`));
+ const userCardsEv = await pub.generic(eb => {
+ eb.kind(USER_CARDS).content("");
+ for (const tag of newTags) {
+ eb.tag(tag);
+ }
+ return eb;
+ });
+
+ console.debug(userCardsEv);
+ await system.BroadcastEvent(userCardsEv);
+ Login.setCards(newTags, userCardsEv.created_at);
+ setOpen(false);
+ }
+ }
+
+ return (<>
+ setOpen(true)}>
+
+
+ {open && setOpen(false)}>
+
+ }
+ >
+ );
+}
diff --git a/src/element/stream-cards/index.tsx b/src/element/stream-cards/index.tsx
new file mode 100644
index 0000000..49f3a69
--- /dev/null
+++ b/src/element/stream-cards/index.tsx
@@ -0,0 +1,50 @@
+import { DndProvider } from "react-dnd";
+import { HTML5Backend } from "react-dnd-html5-backend";
+
+import { useLogin } from "@/hooks/login";
+import { useCards } from "@/hooks/cards";
+import { StreamCardEditor } from "./stream-card-editor";
+import { Card } from "./card-item";
+
+export interface CardType {
+ identifier: string;
+ content: string;
+ title?: string;
+ image?: string;
+ link?: string;
+}
+
+export type NewCard = Omit;
+
+export interface CardItem {
+ identifier: string;
+}
+
+interface StreamCardsProps {
+ host: string;
+}
+
+export function ReadOnlyStreamCards({ host }: StreamCardsProps) {
+ const cards = useCards(host);
+ return (
+
+ {cards.map(ev => (
+
+ ))}
+
+ );
+}
+
+export function StreamCards({ host }: StreamCardsProps) {
+ const login = useLogin();
+ const canEdit = login?.pubkey === host;
+ return (
+
+ {canEdit ? (
+
+ ) : (
+
+ )}
+
+ );
+}
diff --git a/src/element/stream-cards/new-card.tsx b/src/element/stream-cards/new-card.tsx
new file mode 100644
index 0000000..dd49507
--- /dev/null
+++ b/src/element/stream-cards/new-card.tsx
@@ -0,0 +1,87 @@
+import { useState } from "react";
+import { FormattedMessage, useIntl } from "react-intl";
+import { ExternalLink } from "../external-link";
+import { FileUploader } from "../file-uploader";
+import { DefaultButton, WarningButton } from "../buttons";
+import { CardType, NewCard } from ".";
+
+
+interface CardDialogProps {
+ header?: string;
+ cta?: string;
+ cancelCta?: string;
+ card?: CardType;
+ onSave(ev: NewCard): void;
+ onCancel(): void;
+}
+
+export function CardDialog({ header, cta, cancelCta, card, onSave, onCancel }: CardDialogProps) {
+ const [title, setTitle] = useState(card?.title ?? "");
+ const [image, setImage] = useState(card?.image ?? "");
+ const [content, setContent] = useState(card?.content ?? "");
+ const [link, setLink] = useState(card?.link ?? "");
+ const { formatMessage } = useIntl();
+
+ return (
+
+ );
+}
diff --git a/src/element/stream-cards/preview.tsx b/src/element/stream-cards/preview.tsx
new file mode 100644
index 0000000..5bb1947
--- /dev/null
+++ b/src/element/stream-cards/preview.tsx
@@ -0,0 +1,35 @@
+import { isEmpty } from "lodash";
+import { forwardRef, lazy, Suspense } from "react";
+import { ExternalLink } from "../external-link";
+import { NewCard } from ".";
+import classNames from "classnames";
+const Markdown = lazy(() => import("../markdown"));
+
+interface CardPreviewProps extends NewCard {
+ style: object;
+}
+
+export const CardPreview = forwardRef(({ style, title, link, image, content }: CardPreviewProps, ref) => {
+ const isImageOnly = !isEmpty(image) && isEmpty(content) && isEmpty(title);
+ return (
+
+ {title &&
{title}
}
+ {image &&
+ (link && link?.length > 0 ? (
+
+
+
+ ) : (
+
![{title}]({image})
+ ))}
+ {content &&
+
+
+
+ }
+
+ );
+});
diff --git a/src/element/stream-cards/stream-card-editor.tsx b/src/element/stream-cards/stream-card-editor.tsx
new file mode 100644
index 0000000..f09f8b7
--- /dev/null
+++ b/src/element/stream-cards/stream-card-editor.tsx
@@ -0,0 +1,32 @@
+import { useState } from "react";
+import { FormattedMessage } from "react-intl";
+import { Toggle } from "../toggle";
+import { useUserCards } from "@/hooks/cards";
+import { AddCard } from "./add-card";
+import { Tags } from "@/types";
+import { Card } from "./card-item";
+
+interface StreamCardEditorProps {
+ pubkey: string;
+ tags: Tags;
+}
+
+export function StreamCardEditor({ pubkey, tags }: StreamCardEditorProps) {
+ const cards = useUserCards(pubkey, tags, true);
+ const [isEditing, setIsEditing] = useState(false);
+ return (
+ <>
+
+
+ setIsEditing(s => !s)} checked={isEditing} size={40} />
+
+
+
+ {cards.map(ev => (
+
+ ))}
+ {isEditing &&
}
+
+ >
+ );
+}
diff --git a/src/element/stream-editor.css b/src/element/stream-editor.css
index 0aa6aa7..f692921 100644
--- a/src/element/stream-editor.css
+++ b/src/element/stream-editor.css
@@ -11,10 +11,4 @@
padding: 4px 10px !important;
border-radius: 12px !important;
display: unset !important;
-}
-
-.content-warning {
- padding: 16px;
- border-radius: 16px;
- border: 1px solid #ff563f;
-}
+}
\ No newline at end of file
diff --git a/src/element/stream-editor.tsx b/src/element/stream-editor.tsx
index 499f163..61955da 100644
--- a/src/element/stream-editor.tsx
+++ b/src/element/stream-editor.tsx
@@ -5,12 +5,13 @@ import { unixNow } from "@snort/shared";
import { TagsInput } from "react-tag-input-component";
import { FormattedMessage, useIntl } from "react-intl";
-import AsyncButton from "./async-button";
import { extractStreamInfo, findTag } from "@/utils";
import { useLogin } from "@/hooks/login";
import { NewGoalDialog } from "./new-goal";
import { useGoals } from "@/hooks/goals";
import { StreamState } from "@/const";
+import { DefaultButton } from "./buttons";
+import Pill from "./pill";
export interface StreamEditorProps {
ev?: NostrEvent;
@@ -206,9 +207,9 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
{[StreamState.Live, StreamState.Planned, StreamState.Ended].map(v => (
-
setStatus(v)} key={v}>
+ setStatus(v)} key={v}>
{v}
-
+
))}
@@ -262,12 +263,12 @@ export function StreamEditor({ ev, onFinish, options }: StreamEditorProps) {
>
)}
{(options?.canSetContentWarning ?? true) && (
-
+
setContentWarning(e.target.checked)} />
-
+
)}
>
);
diff --git a/src/element/tags.tsx b/src/element/tags.tsx
index 5e43f06..73833f9 100644
--- a/src/element/tags.tsx
+++ b/src/element/tags.tsx
@@ -4,6 +4,7 @@ import { FormattedMessage } from "react-intl";
import { NostrEvent } from "@snort/system";
import { findTag, getTagValues } from "@/utils";
import { StreamState } from "@/const";
+import Pill from "./pill";
export function Tags({ children, max, ev }: { children?: ReactNode; max?: number; ev: NostrEvent }) {
const status = findTag(ev, "status");
@@ -14,13 +15,13 @@ export function Tags({ children, max, ev }: { children?: ReactNode; max?: number
<>
{children}
{status === StreamState.Planned && (
-
+
{status === StreamState.Planned ? : ""}
-
+
)}
{tags.map(a => (
-
- {a}
+
+ {a}
))}
>
diff --git a/src/element/textarea.css b/src/element/textarea.css
index 844557e..2790c95 100644
--- a/src/element/textarea.css
+++ b/src/element/textarea.css
@@ -15,7 +15,7 @@
.emoji-item,
.user-item {
color: white;
- background: #171717;
+ @apply bg-layer-2;
display: flex;
flex-direction: row;
gap: 8px;
@@ -26,7 +26,7 @@
.emoji-item:hover,
.user-item:hover {
- color: #171717;
+ @apply text-layer-2;
background: white;
}
diff --git a/src/element/toggle.css b/src/element/toggle.css
deleted file mode 100644
index 2c69e03..0000000
--- a/src/element/toggle.css
+++ /dev/null
@@ -1,27 +0,0 @@
-.toggle-container {
- display: flex;
- align-items: center;
- gap: 6px;
-}
-
-.toggle {
- display: flex;
- align-items: center;
- justify-content: center;
- background: transparent;
- border: none;
-}
-.toggle svg {
- color: var(--text-muted);
- height: 32px;
- width: 32px;
-}
-.toggle:hover {
- cursor: pointer;
-}
-.toggle:hover svg {
- color: white;
-}
-.toggle[data-state="on"] svg {
- color: var(--primary);
-}
diff --git a/src/element/toggle.tsx b/src/element/toggle.tsx
index 9a52a14..1528cb1 100644
--- a/src/element/toggle.tsx
+++ b/src/element/toggle.tsx
@@ -1,22 +1,32 @@
-import * as BaseToggle from "@radix-ui/react-toggle";
-import "./toggle.css";
-import { Icon } from "./icon";
+import { HTMLProps } from "react";
-interface ToggleProps {
- label: string;
- text: string;
- pressed?: boolean;
- onPressedChange?: (b: boolean) => void;
-}
-
-export function Toggle({ label, text, ...rest }: ToggleProps) {
- const { pressed } = rest;
+export function Toggle({ size, className, checked, ...props }: HTMLProps) {
return (
-
-
- {pressed ? : }
-
- {text}
-
+
+
+
+
);
}
diff --git a/src/element/top-zappers.tsx b/src/element/top-zappers.tsx
index 946fb7f..bd3a305 100644
--- a/src/element/top-zappers.tsx
+++ b/src/element/top-zappers.tsx
@@ -2,11 +2,9 @@ import { ParsedZap } from "@snort/system";
import useTopZappers from "@/hooks/top-zappers";
import { ZapperRow } from "./zapper-row";
-export function TopZappers({ zaps, limit }: { zaps: ParsedZap[]; limit?: number }) {
+export function TopZappers({ zaps, limit, avatarSize, showName, className }: { zaps: ParsedZap[]; limit?: number, avatarSize?: number, showName?: boolean, className?: string }) {
const zappers = useTopZappers(zaps);
return zappers.slice(0, limit ?? 10).map(({ pubkey, total }) => (
-
-
-
+
));
}
diff --git a/src/element/video-tile.css b/src/element/video-tile.css
deleted file mode 100644
index 6ac3108..0000000
--- a/src/element/video-tile.css
+++ /dev/null
@@ -1,67 +0,0 @@
-.video-tile {
- position: relative;
-}
-
-.video-tile-container {
- display: flex;
- flex-direction: column;
-}
-
-.video-tile-info {
- display: flex;
- flex-direction: column;
- gap: 6px;
-}
-
-.video-tile.nsfw > div:nth-child(1) {
- filter: blur(3px);
-}
-
-.video-tile > div:nth-child(1) {
- border-radius: 16px;
- width: 100%;
- aspect-ratio: 16 / 10;
- background-size: cover;
- background-position: center;
- background-repeat: no-repeat;
-}
-
-.video-tile h3 {
- font-size: 20px;
- line-height: 25px;
- margin: 16px 0 6px 0;
- word-break: break-all;
- word-wrap: break-word;
-}
-
-.video-tile .pill-box {
- margin: 16px 20px;
- text-transform: uppercase;
- display: flex;
- flex-direction: column;
- justify-content: space-between;
- align-items: flex-end;
- position: absolute;
- top: 0;
- right: 0;
- gap: 8px;
-}
-
-.video-tile .pill-box .pill {
- width: fit-content;
-}
-
-.video-tile .pill-box .pill.viewers {
- text-transform: lowercase;
-}
-
-.video-tags {
- display: flex;
- align-items: flex-start;
- flex-wrap: wrap;
- gap: 4px;
-}
-
-.video-tags .pill {
- font-size: 12px;
-}
diff --git a/src/element/video-tile.tsx b/src/element/video-tile.tsx
index 58ab4f2..ef5a368 100644
--- a/src/element/video-tile.tsx
+++ b/src/element/video-tile.tsx
@@ -1,16 +1,16 @@
-import "./video-tile.css";
import { Link } from "react-router-dom";
import { Profile } from "./profile";
-import { NostrEvent, NostrPrefix, encodeTLV } from "@snort/system";
-import { useInView } from "react-intersection-observer";
+import { NostrEvent, NostrLink } from "@snort/system";
import { FormattedMessage } from "react-intl";
import { StatePill } from "./state-pill";
-import { extractStreamInfo, findTag, getHost } from "@/utils";
+import { extractStreamInfo, getHost } from "@/utils";
import { formatSats } from "@/number";
-import { isContentWarningAccepted } from "./content-warning";
import { Tags } from "./tags";
import { StreamState } from "@/const";
+import Pill from "./pill";
+import classNames from "classnames";
+import Logo from "./logo";
export function VideoTile({
ev,
@@ -21,43 +21,39 @@ export function VideoTile({
showAuthor?: boolean;
showStatus?: boolean;
}) {
- const { inView, ref } = useInView({ triggerOnce: true });
- const id = findTag(ev, "d") ?? "";
const { title, image, status, participants, contentWarning } = extractStreamInfo(ev);
const host = getHost(ev);
- const link = encodeTLV(NostrPrefix.Address, id, undefined, ev.kind, ev.pubkey);
+ const link = NostrLink.fromEvent(ev);
+ const hasImg = (image?.length ?? 0) > 0;
return (
-
+
-
0 ? image : "/zap-stream.svg") : ""})`,
- }}>
-
- {showStatus && }
- {participants && (
-
-
-
- )}
-
+
+ {hasImg ?
![]({image})
:
+
}
+
+ {showStatus && }
+ {participants && (
+
+
+
+ )}
+
+
{title}
-
-
-
-
- {showAuthor &&
}
+
+
+ {showAuthor &&
}
);
}
diff --git a/src/element/write-message.tsx b/src/element/write-message.tsx
index bd9b131..394c1e7 100644
--- a/src/element/write-message.tsx
+++ b/src/element/write-message.tsx
@@ -6,12 +6,12 @@ import { unixNowMs } from "@snort/shared";
const EmojiPicker = lazy(() => import("./emoji-picker"));
import { useLogin } from "@/hooks/login";
-import AsyncButton from "./async-button";
import { Icon } from "./icon";
import { Textarea } from "./textarea";
import type { Emoji, EmojiPack } from "@/types";
import { LIVE_STREAM_CHAT } from "@/const";
import { TimeSync } from "@/index";
+import { BorderButton } from "./buttons";
export function WriteMessage({ link, emojiPacks }: { link: NostrLink; emojiPacks: EmojiPack[] }) {
const system = useContext(SnortContext);
@@ -100,9 +100,9 @@ export function WriteMessage({ link, emojiPacks }: { link: NostrLink; emojiPacks
)}
-
+
-
+
>
);
}
diff --git a/src/element/zapper-row.tsx b/src/element/zapper-row.tsx
index d6579de..df05cd1 100644
--- a/src/element/zapper-row.tsx
+++ b/src/element/zapper-row.tsx
@@ -2,16 +2,17 @@ import { formatSats } from "@/number";
import { Icon } from "./icon";
import { Profile } from "./profile";
import { FormattedMessage } from "react-intl";
+import classNames from "classnames";
-export function ZapperRow({ pubkey, total, showName }: { pubkey: string; total: number; showName?: boolean }) {
+export function ZapperRow({ pubkey, total, showName, avatarSize, className }: { pubkey: string; total: number; showName?: boolean, avatarSize?: number, className?: string }) {
return (
-
+
{pubkey === "anon" ? (
-
+
) : (
-
+
)}
diff --git a/src/hooks/clips.ts b/src/hooks/clips.ts
new file mode 100644
index 0000000..dda10b6
--- /dev/null
+++ b/src/hooks/clips.ts
@@ -0,0 +1,15 @@
+import { LIVE_STREAM_CLIP } from "@/const";
+import { NostrLink, RequestBuilder } from "@snort/system";
+import { useRequestBuilder } from "@snort/system-react";
+import { useMemo } from "react";
+
+export function useClips(link?: NostrLink, limit?: number) {
+ const sub = useMemo(() => {
+ if (!link) return;
+ const rb = new RequestBuilder(`clips:${link.id.slice(0, 12)}`);
+ rb.withFilter().kinds([LIVE_STREAM_CLIP]).tag("p", [link.id]).limit(limit);
+ return rb;
+ }, [link]);
+
+ return useRequestBuilder(sub);
+}
\ No newline at end of file
diff --git a/src/hooks/goals.ts b/src/hooks/goals.ts
index 51d8ee8..0404f85 100644
--- a/src/hooks/goals.ts
+++ b/src/hooks/goals.ts
@@ -15,15 +15,14 @@ export function useZapGoal(id?: string) {
return data.at(0);
}
-export function useGoals(pubkey?: string, leaveOpen = false) {
+export function useGoals(pubkey?: string, leaveOpen?: boolean, limit?: number) {
const sub = useMemo(() => {
if (!pubkey) return null;
const b = new RequestBuilder(`goals:${pubkey.slice(0, 12)}`);
b.withOptions({ leaveOpen });
- b.withFilter().kinds([GOAL]).authors([pubkey]);
+ b.withFilter().kinds([GOAL]).authors([pubkey]).limit(limit);
return b;
- }, [pubkey, leaveOpen]);
+ }, [pubkey, leaveOpen, limit]);
- const data = useRequestBuilder(sub);
- return data;
+ return useRequestBuilder(sub);
}
diff --git a/src/index.css b/src/index.css
index faf9e30..4b2e61f 100644
--- a/src/index.css
+++ b/src/index.css
@@ -20,7 +20,7 @@ body {
--primary: #f838d9;
--secondary: #34d2fe;
--zap: #ff8d2b;
- --text-danger: #ff563f;
+ --success: green;
--surface: #222;
--border: #171717;
--border-2: #393939;
@@ -29,7 +29,18 @@ body {
--gradient-orange: linear-gradient(270deg, #ff5b27 0%, rgba(255, 182, 39, 0.99) 100%);
}
-@media (max-width: 1020px) {
+.btn-border {
+ border: 1px solid transparent;
+ color: inherit;
+ background: linear-gradient(black, black) padding-box, linear-gradient(94.73deg, #2bd9ff 0%, #f838d9 100%) border-box;
+ transition: 0.3s;
+}
+
+.btn-border:hover {
+ background: linear-gradient(black, black) padding-box, linear-gradient(94.73deg, #14b4d8 0%, #ba179f 100%) border-box;
+}
+
+@media screen(xl) {
:root {
--gap-l: 24px;
--gap-m: 16px;
@@ -66,225 +77,18 @@ a {
outline: none;
}
-.pill {
- padding: 4px 8px;
- border-radius: 9px;
- font-weight: 700;
- font-size: 14px;
- line-height: 18px;
- cursor: pointer;
- user-select: none;
-}
-
-.w-max {
- width: stretch;
- width: -webkit-fill-available;
- width: -moz-available;
-}
-
-.pointer {
- cursor: pointer;
-}
-
-.btn {
- border: none;
- outline: none;
- cursor: pointer;
- font-weight: 700;
- font-size: 16px;
- line-height: 20px;
- padding: 8px 16px;
- border-radius: 16px;
- background: white;
- color: black;
-}
-
-.btn-block {
- width: 100%;
-}
-
-.btn-small {
- font-size: 14px;
- line-height: 18px;
- padding: 4px 8px;
-}
-
-.btn-border {
- border: 1px solid transparent;
- color: inherit;
- background: linear-gradient(black, black) padding-box, linear-gradient(94.73deg, #2bd9ff 0%, #f838d9 100%) border-box;
- transition: 0.3s;
-}
-
-.btn-border:hover {
- background: linear-gradient(black, black) padding-box, linear-gradient(94.73deg, #14b4d8 0%, #ba179f 100%) border-box;
-}
-
-.btn-primary {
- background: #fff;
- color: #0a0a0a;
-}
-
-.btn-primary:hover {
- opacity: 0.9;
-}
-
-.btn-secondary {
- color: white;
- background: #222;
-}
-
-.btn-warning {
- background: #ff563f;
- color: white;
-}
-
input[type="text"],
textarea,
input[type="datetime-local"],
input[type="password"],
-input[type="number"] {
- font-family: inherit;
- border: unset;
- background-color: unset;
- color: inherit;
- width: 100%;
- font-size: 16px;
- font-weight: 500;
-}
-
+input[type="number"],
select {
- font-family: inherit;
- border: unset;
- background-color: #262626;
- color: inherit;
- width: 100%;
- font-size: 16px;
- font-weight: 500;
-}
-
-input[type="checkbox"] {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- width: 20px;
- height: 20px;
- border-radius: 4px;
- border: 2px solid #333;
- background-color: transparent;
-}
-
-input[type="checkbox"]:after {
- content: " ";
- position: relative;
- left: 40%;
- top: 20%;
- width: 15%;
- height: 40%;
- border: solid #fff;
- border-width: 0 2px 2px 0;
- transform: rotate(50deg);
- display: none;
-}
-
-input[type="checkbox"]:checked:after {
- display: block;
-}
-
-.plain-paper {
- background: #171717;
- border-radius: 16px;
- padding: 8px 16px;
-}
-
-div.paper {
- background: #171717;
- border-radius: 16px;
- padding: 8px 16px;
- display: flex;
- gap: 10px;
- align-items: center;
+ @apply bg-layer-2 w-full font-medium px-4 py-2 rounded-xl;
}
.scroll-lock {
overflow: hidden;
- height: 100vh;
-}
-
-.warning {
- color: #ff563f;
-}
-
-.border-warning {
- border: 1px solid #ff563f;
-}
-
-.dialog-overlay {
- background-color: rgba(0, 0, 0, 0.8);
- position: fixed;
- inset: 0;
- z-index: 99;
-}
-
-.dialog-content {
- display: flex;
- flex-direction: column;
- gap: 12px;
- z-index: 2;
- background-color: #171717;
- border-radius: 24px;
- position: fixed;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- width: 500px;
- max-width: 90vw;
- max-height: 85vh;
- overflow-y: auto;
- z-index: 100;
-}
-
-.dialog-content .header-image {
- width: 100%;
- height: auto;
-}
-
-.dialog-content .content-inner {
- width: 100%;
- display: flex;
- flex-direction: column;
- padding: 25px;
- box-sizing: border-box;
- gap: 16px;
-}
-
-.dialog-content .username,
-.dialog-content .username input {
- width: 100%;
-}
-
-.dialog-content div.paper {
- background: #262626;
- width: 100%;
- box-sizing: border-box;
-}
-
-.dialog-content h2 {
- font-size: 24px;
- font-weight: 500;
- margin: 0;
-}
-.dialog-content h3 {
- font-size: 16px;
- font-weight: 500;
- margin: 0;
- margin-bottom: 24px;
-}
-
-.dialog-content small {
- display: block;
- color: #868686;
- margin: 6px;
+ height: 100dvh;
}
.ctx-menu {
@@ -354,7 +158,8 @@ div.paper {
.full-page-height .live-chat {
padding: 24px 16px 8px 24px;
- border: 1px solid #171717;
+ border: 1px solid;
+ @apply border-layer-2;
border-radius: 24px;
height: inherit;
}
diff --git a/src/index.tsx b/src/index.tsx
index d869378..3f76a1c 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -33,12 +33,14 @@ import { WasmOptimizer, WasmPath, wasmInit } from "./wasm";
const DashboardPage = lazy(() => import("./pages/dashboard"));
import Faq from "@/faq.md";
+import MockPage from "./pages/mock";
const hasWasm = "WebAssembly" in globalThis;
const db = new SnortSystemDb();
const System = new NostrSystem({
db,
optimizer: hasWasm ? WasmOptimizer : undefined,
+ automaticOutboxModel: false,
});
export const Login = new LoginStore();
@@ -57,7 +59,9 @@ async function doInit() {
db.ready = await db.isAvailable();
await System.Init();
try {
- const req = await fetch("https://api.zap.stream/api/time");
+ const req = await fetch("https://api.zap.stream/api/time", {
+ signal: AbortSignal.timeout(1000)
+ });
const nowAtServer = (await req.json()).time as number;
const now = unixNowMs();
TimeSync = now - nowAtServer;
@@ -75,6 +79,10 @@ const router = createBrowserRouter([
return null;
},
children: [
+ {
+ path: "/mock",
+ element:
+ },
{
path: "/",
element:
,
diff --git a/src/pages/dashboard.tsx b/src/pages/dashboard.tsx
index 86e3ee9..c469d17 100644
--- a/src/pages/dashboard.tsx
+++ b/src/pages/dashboard.tsx
@@ -1,4 +1,3 @@
-import AsyncButton from "@/element/async-button";
import { ChatZap, LiveChat } from "@/element/live-chat";
import LiveVideoPlayer from "@/element/live-video-player";
import { MuteButton } from "@/element/mute-button";
@@ -15,8 +14,9 @@ import { HTMLProps, ReactNode, useEffect, useMemo, useState } from "react";
import { FormattedMessage, FormattedNumber } from "react-intl";
import { Text } from "@/element/text";
import { StreamTimer } from "@/element/stream-time";
-import * as Dialog from "@radix-ui/react-dialog";
import { DashboardRaidMenu } from "@/element/raid-menu";
+import { DefaultButton } from "@/element/buttons";
+import Modal from "@/element/modal";
export default function DashboardPage() {
const login = useLogin();
@@ -77,7 +77,7 @@ function DashboardForLink({ link }: { link: NostrLink }) {
function DashboardCard(props: HTMLProps
) {
return (
-
+
{props.children}
);
@@ -91,8 +91,8 @@ function DashboardStatsCard({
return (
-
{name}
+ className={classNames("flex-1 bg-layer-1 flex flex-col gap-1 px-4 py-2 rounded-xl", props.className)}>
+
{name}
{value}
);
@@ -106,13 +106,13 @@ function DashboardChatList({ link }: { link: NostrLink }) {
}, [feed]);
return pubkeys.map(a => (
-
+
-
{}} className="font-bold">
+ { }} className="font-bold">
-
+
));
@@ -144,7 +144,7 @@ function DashboardZapColumn({ link }: { link: NostrLink }) {
function DashboardHighlightZap({ zap }: { zap: ParsedZap }) {
return (
-
+
- setShow(true)}>
-
-
-
-
-
- setShow(false)} />
-
-
-
+ return (<>
+ setShow(true)}>
+
+
+ {show && }
+ >
);
}
diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx
index 3d643a1..7c74811 100644
--- a/src/pages/layout.tsx
+++ b/src/pages/layout.tsx
@@ -1,7 +1,6 @@
import "./layout.css";
import { CSSProperties, useEffect, useState, useSyncExternalStore } from "react";
-import * as Dialog from "@radix-ui/react-dialog";
import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
import { Helmet } from "react-helmet";
import { FormattedMessage } from "react-intl";
@@ -17,8 +16,10 @@ import { Login } from "@/index";
import { useLang } from "@/hooks/lang";
import { AllLocales } from "@/intl";
import { NewVersion } from "@/serviceWorker";
-import AsyncButton from "@/element/async-button";
import { trackEvent } from "@/utils";
+import { BorderButton, DefaultButton } from "@/element/buttons";
+import Modal from "@/element/modal";
+import Logo from "@/element/logo";
export function LayoutPage() {
const navigate = useNavigate();
@@ -109,24 +110,15 @@ export function LayoutPage() {
function loggedOut() {
if (login) return;
-
- function handleLogin() {
- setShowLogin(true);
- }
-
- return (
-
-
-
-
-
-
-
-
- setShowLogin(false)} />
-
-
-
+ return (<>
+ setShowLogin(true)}>
+
+
+
+ {showLogin &&
+ setShowLogin(false)} />
+ }
+ >
);
}
@@ -141,16 +133,16 @@ export function LayoutPage() {
navigate("/")}>
-
![](/zap-stream.svg)
+
+ className="flex items-center max-md:hidden gap-1 bg-layer-1 hover:bg-layer-2 font-bold p-2 rounded-xl">
Discord
@@ -182,9 +174,9 @@ function NewVersionBanner() {
- window.location.reload()} className="btn">
+ window.location.reload()} className="btn">
-
+
);
}
diff --git a/src/pages/mock.tsx b/src/pages/mock.tsx
new file mode 100644
index 0000000..cd68279
--- /dev/null
+++ b/src/pages/mock.tsx
@@ -0,0 +1,25 @@
+import { LIVE_STREAM } from "@/const";
+import { LiveChat } from "@/element/live-chat";
+import { SendZapsDialog } from "@/element/send-zap";
+import { EventBuilder, NostrLink } from "@snort/system";
+
+export default function MockPage() {
+ const pubkey = "cf45a6ba1363ad7ed213a078e710d24115ae721c9b47bd1ebf4458eaefb4c2a5";
+ const fakeStream = new EventBuilder()
+ .kind(LIVE_STREAM)
+ .pubKey(pubkey)
+ .tag(["d", "mock"])
+ .tag(["title", "Example Stream"])
+ .tag(["summary", "An example mock stream for debugging"])
+ .tag(["streaming", "https://example.com/live.m3u8"])
+ .tag(["t", "nostr"])
+ .tag(["t", "mock"])
+ .processContent()
+ .build();
+ const fakeStreamLink = NostrLink.fromEvent(fakeStream);
+
+ return
+
+
+
+}
\ No newline at end of file
diff --git a/src/pages/profile-page.tsx b/src/pages/profile-page.tsx
index 3f43184..e5d6566 100644
--- a/src/pages/profile-page.tsx
+++ b/src/pages/profile-page.tsx
@@ -1,8 +1,7 @@
import "./profile-page.css";
import { useMemo } from "react";
import { useNavigate, useParams } from "react-router-dom";
-import * as Tabs from "@radix-ui/react-tabs";
-import { NostrPrefix, ParsedZap, TaggedNostrEvent, encodeTLV, parseNostrLink } from "@snort/system";
+import { CachedMetadata, NostrEvent, NostrLink, TaggedNostrEvent, parseNostrLink } from "@snort/system";
import { useUserProfile } from "@snort/system-react";
import { unwrap } from "@snort/shared";
import { FormattedMessage } from "react-intl";
@@ -13,128 +12,127 @@ import { VideoTile } from "@/element/video-tile";
import { FollowButton } from "@/element/follow-button";
import { MuteButton } from "@/element/mute-button";
import { useProfile } from "@/hooks/profile";
-import useTopZappers from "@/hooks/top-zappers";
import { Text } from "@/element/text";
import { findTag } from "@/utils";
import { StatePill } from "@/element/state-pill";
import { Avatar } from "@/element/avatar";
-import { ZapperRow } from "@/element/zapper-row";
import { StreamState } from "@/const";
-import AsyncButton from "@/element/async-button";
-
-function TopZappers({ zaps }: { zaps: ParsedZap[] }) {
- const zappers = useTopZappers(zaps);
- return (
-
- {zappers.map(z => (
-
- ))}
-
- );
-}
+import { DefaultButton } from "@/element/buttons";
+import { useGoals } from "@/hooks/goals";
+import { Goal } from "@/element/goal";
+import { TopZappers } from "@/element/top-zappers";
+import { useClips } from "@/hooks/clips";
const defaultBanner = "https://void.cat/d/Hn1AdN5UKmceuDkgDW847q.webp";
export function ProfilePage() {
- const navigate = useNavigate();
const params = useParams();
const link = parseNostrLink(unwrap(params.npub));
- const profile = useUserProfile(link.id);
- const zapTarget = profile?.lud16 ?? profile?.lud06;
const { streams, zaps } = useProfile(link, true);
- const liveEvent = useMemo(() => {
- return streams.find(ev => findTag(ev, "status") === StreamState.Live);
- }, [streams]);
+ const profile = useUserProfile(link.id);
+
const pastStreams = useMemo(() => {
return streams.filter(ev => findTag(ev, "status") === StreamState.Ended);
}, [streams]);
- const futureStreams = useMemo(() => {
- return streams.filter(ev => findTag(ev, "status") === StreamState.Planned);
- }, [streams]);
- const isLive = Boolean(liveEvent);
-
- function goToLive() {
- if (liveEvent) {
- const d = findTag(liveEvent, "d") || "";
- const naddr = encodeTLV(NostrPrefix.Address, d, undefined, liveEvent.kind, liveEvent.pubkey);
- navigate(`/${naddr}`);
- }
- }
return (
-
+
![{profile?.name]({profile?.banner)
-
-
-
-
- {profile?.name &&
{profile.name}
}
- {profile?.about && (
-
-
-
- )}
+
+
+
-
- {zapTarget && (
-
-
-
-
- }
- targetName={profile?.name || link.id}
- />
- )}
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
);
}
+function ProfileHeader({ profile, link, streams }: { profile?: CachedMetadata, link: NostrLink, streams: Array
}) {
+ const navigate = useNavigate();
+ const liveEvent = useMemo(() => {
+ return streams.find(ev => findTag(ev, "status") === StreamState.Live);
+ }, [streams]);
+ const zapTarget = profile?.lud16 ?? profile?.lud06;
+ const isLive = Boolean(liveEvent);
+
+ function goToLive() {
+ if (liveEvent) {
+ const evLink = NostrLink.fromEvent(liveEvent);
+ navigate(`/${evLink.encode()}`);
+ }
+ }
+
+ return
+
+
+
+ {profile?.name &&
{profile.name}
}
+ {profile?.about && (
+
+
+
+ )}
+
+
+
+ {zapTarget && (
+
+
+
+
+ }
+ targetName={profile?.name || link.id}
+ />
+ )}
+
+
+
+
+}
+
function ProfileStreamList({ streams }: { streams: Array }) {
+ if (streams.length === 0) {
+ return
+ }
return (
-
+
{streams.map(ev => (
-
+
}) {
);
}
+
+function ProfileZapGoals({ link }: { link: NostrLink }) {
+ const limit = 5;
+ const goals = useGoals(link.id, false, limit);
+ if (goals.length === 0) {
+ return
+ }
+ return goals
+ .sort((a, b) => a.created_at > b.created_at ? -1 : 1)
+ .slice(0, limit)
+ .map(a =>
+
+
);
+}
+
+function ProfileClips({ link }: { link: NostrLink }) {
+ const clips = useClips(link, 10);
+ if (clips.length === 0) {
+ return
+ }
+ return clips.map(a => {
+ const r = findTag(a, "r");
+ return
+ })
+}
\ No newline at end of file
diff --git a/src/pages/providers/index.tsx b/src/pages/providers/index.tsx
index b2a5f7b..505db25 100644
--- a/src/pages/providers/index.tsx
+++ b/src/pages/providers/index.tsx
@@ -6,7 +6,7 @@ import Owncast from "@/owncast.png";
import Cloudflare from "@/cloudflare.png";
import { ConfigureOwncast } from "./owncast";
import { ConfigureNostrType } from "./nostr";
-import AsyncButton from "@/element/async-button";
+import { DefaultButton } from "@/element/buttons";
export function StreamProvidersPage() {
const navigate = useNavigate();
@@ -38,9 +38,9 @@ export function StreamProvidersPage() {
{mapName(p)}
{mapLogo(p)}
-
navigate(p)}>
+ navigate(p)}>
+ Configure
-
+
);
}
diff --git a/src/pages/providers/nostr.tsx b/src/pages/providers/nostr.tsx
index 1a416db..80bcd73 100644
--- a/src/pages/providers/nostr.tsx
+++ b/src/pages/providers/nostr.tsx
@@ -2,11 +2,11 @@ import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { FormattedMessage } from "react-intl";
-import AsyncButton from "@/element/async-button";
import { StatePill } from "@/element/state-pill";
import { StreamProviderInfo, StreamProviderStore } from "@/providers";
import { NostrStreamProvider } from "@/providers/zsz";
import { StreamState } from "@/const";
+import { DefaultButton } from "@/element/buttons";
export function ConfigureNostrType() {
const [url, setUrl] = useState("");
@@ -55,14 +55,13 @@ export function ConfigureNostrType() {
)}
-
{
StreamProviderStore.add(new NostrStreamProvider(new URL(url).host, url));
navigate("/");
}}>
-
+
>
);
@@ -77,9 +76,9 @@ export function ConfigureNostrType() {
setUrl(e.target.value)} />
-
+
-
+
{status()}
diff --git a/src/pages/providers/owncast.tsx b/src/pages/providers/owncast.tsx
index f0db76e..a182613 100644
--- a/src/pages/providers/owncast.tsx
+++ b/src/pages/providers/owncast.tsx
@@ -1,11 +1,11 @@
import { useState } from "react";
import { useNavigate } from "react-router-dom";
-import AsyncButton from "@/element/async-button";
import { StatePill } from "@/element/state-pill";
import { StreamProviderInfo, StreamProviderStore } from "@/providers";
import { OwncastProvider } from "@/providers/owncast";
import { StreamState } from "@/const";
+import { DefaultButton } from "@/element/buttons";
export function ConfigureOwncast() {
const [url, setUrl] = useState("");
@@ -55,14 +55,13 @@ export function ConfigureOwncast() {
)}
-
{
StreamProviderStore.add(new OwncastProvider(url, token));
navigate("/");
}}>
Save
-
+
>
);
@@ -83,9 +82,9 @@ export function ConfigureOwncast() {
setToken(e.target.value)} />
-
+
Connect
-
+
{status()}
diff --git a/src/pages/root.css b/src/pages/root.css
index 6b0725b..2c63b95 100644
--- a/src/pages/root.css
+++ b/src/pages/root.css
@@ -61,7 +61,7 @@
.one-line:before,
.one-line:after {
- background-color: #171717;
+ @apply bg-layer-2;
}
::-webkit-scrollbar {
diff --git a/src/pages/settings-page.tsx b/src/pages/settings-page.tsx
index af5f81f..7154ea1 100644
--- a/src/pages/settings-page.tsx
+++ b/src/pages/settings-page.tsx
@@ -11,11 +11,11 @@ import { Login } from "..";
import { StatePill } from "@/element/state-pill";
import { NostrStreamProvider } from "@/providers";
import { StreamState } from "@/const";
-import AsyncButton from "@/element/async-button";
+import { Layer1Button } from "@/element/buttons";
const enum Tab {
Account,
- Notifications,
+ Stream,
}
export function SettingsPage() {
@@ -51,7 +51,9 @@ export function SettingsPage() {
-
+
+
+
)}
@@ -69,20 +71,24 @@ export function SettingsPage() {
onClick={() => Login.setColor(a)}>
))}
-
-
-
-
- a.name === "zap.stream")) as NostrStreamProvider}
- showEndpoints={true}
- showEditor={false}
- showForwards={true}
- />
-
>
);
}
+ case Tab.Stream: {
+ return <>
+
+
+
+
+ a.name === "zap.stream")) as NostrStreamProvider}
+ showEndpoints={true}
+ showEditor={false}
+ showForwards={true}
+ />
+
+ >
+ }
}
}
@@ -90,6 +96,8 @@ export function SettingsPage() {
switch (t) {
case Tab.Account:
return ;
+ case Tab.Stream:
+ return ;
}
}
@@ -101,13 +109,13 @@ export function SettingsPage() {
- {[Tab.Account].map(t => (
-
setTab(t)} className="rounded-xl px-3 py-2 bg-gray-2 hover:bg-gray-1">
+ {[Tab.Account, Tab.Stream].map(t => (
+ setTab(t)} className={t === tab ? "active" : ""}>
{tabName(t)}
-
+
))}
-
{tabContent()}
+
{tabContent()}
diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx
index b4d6597..146b796 100644
--- a/src/pages/stream-page.tsx
+++ b/src/pages/stream-page.tsx
@@ -11,7 +11,6 @@ const LiveVideoPlayer = lazy(() => import("@/element/live-video-player"));
import { extractStreamInfo, findTag, getEventFromLocationState, getHost } from "@/utils";
import { Profile, getName } from "@/element/profile";
import { LiveChat } from "@/element/live-chat";
-import AsyncButton from "@/element/async-button";
import { useLogin } from "@/hooks/login";
import { useZapGoal } from "@/hooks/goals";
import { SendZapsDialog } from "@/element/send-zap";
@@ -29,6 +28,8 @@ import { FollowButton } from "@/element/follow-button";
import { ClipButton } from "@/element/clip-button";
import { StreamState } from "@/const";
import { NotificationsButton } from "@/element/notifications-button";
+import { WarningButton } from "@/element/buttons";
+import Pill from "@/element/pill";
function ProfileInfo({ ev, goal }: { ev?: TaggedNostrEvent; goal?: TaggedNostrEvent }) {
const system = useContext(SnortContext);
@@ -60,22 +61,22 @@ function ProfileInfo({ ev, goal }: { ev?: TaggedNostrEvent; goal?: TaggedNostrEv
{summary}
-
+
-
+
{status === StreamState.Live && (
-
+
-
+
)}
{ev &&
}
{isMine && (
)}
diff --git a/src/pages/widgets.tsx b/src/pages/widgets.tsx
index 7570626..2eded5f 100644
--- a/src/pages/widgets.tsx
+++ b/src/pages/widgets.tsx
@@ -14,7 +14,7 @@ import { Views } from "./widgets/views";
import { Music } from "./widgets/music";
import groupBy from "lodash/groupBy";
import { hexToBech32 } from "@snort/shared";
-import AsyncButton from "@/element/async-button";
+import { DefaultButton } from "@/element/buttons";
interface ZapAlertConfigurationProps {
npub: string;
@@ -153,9 +153,9 @@ function ZapAlertConfiguration({ npub, baseUrl }: ZapAlertConfigurationProps) {
onChange={ev => setTestText(ev.target.value)}
/>
-
+
-
+
>
)}
>
diff --git a/src/pages/widgets/top-zappers.tsx b/src/pages/widgets/top-zappers.tsx
index 133d2d9..3a6ac3a 100644
--- a/src/pages/widgets/top-zappers.tsx
+++ b/src/pages/widgets/top-zappers.tsx
@@ -14,7 +14,7 @@ export function TopZappersWidget({ link }: { link: NostrLink }) {
-
+
);
diff --git a/src/utils.ts b/src/utils.ts
index beec27f..1a47aec 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -98,10 +98,13 @@ interface StreamInfo {
starts?: string;
ends?: string;
service?: string;
+ host?: string;
}
export function extractStreamInfo(ev?: NostrEvent) {
- const ret = {} as StreamInfo;
+ const ret = {
+ host: getHost(ev)
+ } as StreamInfo;
const matchTag = (tag: Array
, k: string, into: (v: string) => void) => {
if (tag[0] === k) {
into(tag[1]);
diff --git a/tailwind.config.js b/tailwind.config.js
index 208c8e7..b94d539 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -4,12 +4,14 @@ module.exports = {
theme: {
extend: {
colors: {
- "gray-1": "#171717",
- "gray-2": "#222",
- "gray-3": "#797979",
+ "layer-1": "rgb(23 23 23 / )",
+ "layer-2": "rgb(34 34 34 / )",
+ "layer-3": "rgb(50 50 50 / )",
primary: "var(--primary)",
secondary: "var(--secondary)",
zap: "var(--zap)",
+ success: "rgb(0 127 0 / )",
+ warning: "rgb(255 86 63 / )"
},
animation: {
"ping-once": "ping 1s cubic-bezier(0, 0, 0.2, 1);",
diff --git a/yarn.lock b/yarn.lock
index c171e20..b123d8e 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1467,7 +1467,7 @@ __metadata:
languageName: node
linkType: hard
-"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
+"@babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.9.2":
version: 7.22.11
resolution: "@babel/runtime@npm:7.22.11"
dependencies:
@@ -2052,13 +2052,6 @@ __metadata:
languageName: node
linkType: hard
-"@juggle/resize-observer@npm:^3.3.1":
- version: 3.4.0
- resolution: "@juggle/resize-observer@npm:3.4.0"
- checksum: 2505028c05cc2e17639fcad06218b1c4b60f932a4ebb4b41ab546ef8c157031ae377e3f560903801f6d01706dbefd4943b6c4704bf19ed86dfa1c62f1473a570
- languageName: node
- linkType: hard
-
"@noble/curves@npm:^1.2.0":
version: 1.2.0
resolution: "@noble/curves@npm:1.2.0"
@@ -2118,457 +2111,6 @@ __metadata:
languageName: node
linkType: hard
-"@radix-ui/primitive@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/primitive@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- checksum: 2b93e161d3fdabe9a64919def7fa3ceaecf2848341e9211520c401181c9eaebb8451c630b066fad2256e5c639c95edc41de0ba59c40eff37e799918d019822d1
- languageName: node
- linkType: hard
-
-"@radix-ui/react-collapsible@npm:^1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-collapsible@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/primitive": 1.0.1
- "@radix-ui/react-compose-refs": 1.0.1
- "@radix-ui/react-context": 1.0.1
- "@radix-ui/react-id": 1.0.1
- "@radix-ui/react-presence": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-use-controllable-state": 1.0.1
- "@radix-ui/react-use-layout-effect": 1.0.1
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: 26976e4a72a3e0f4b2c62af2898b3e205c3652af46a3b41cda9a43567fe8381d9ef6afb0b29e3214c450b847f4f2099a533cffc5045844ecab290e9fa6114ca9
- languageName: node
- linkType: hard
-
-"@radix-ui/react-collection@npm:1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-collection@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-compose-refs": 1.0.1
- "@radix-ui/react-context": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-slot": 1.0.2
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: acfbc9b0b2c553d343c22f02c9f098bc5cfa99e6e48df91c0d671855013f8b877ade9c657b7420a7aa523b5aceadea32a60dd72c23b1291f415684fb45d00cff
- languageName: node
- linkType: hard
-
-"@radix-ui/react-compose-refs@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-compose-refs@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 2b9a613b6db5bff8865588b6bf4065f73021b3d16c0a90b2d4c23deceeb63612f1f15de188227ebdc5f88222cab031be617a9dd025874c0487b303be3e5cc2a8
- languageName: node
- linkType: hard
-
-"@radix-ui/react-context@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-context@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 60e9b81d364f40c91a6213ec953f7c64fcd9d75721205a494a5815b3e5ae0719193429b62ee6c7002cd6aaf70f8c0e2f08bdbaba9ffcc233044d32b56d2127d1
- languageName: node
- linkType: hard
-
-"@radix-ui/react-dialog@npm:^1.0.4":
- version: 1.0.4
- resolution: "@radix-ui/react-dialog@npm:1.0.4"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/primitive": 1.0.1
- "@radix-ui/react-compose-refs": 1.0.1
- "@radix-ui/react-context": 1.0.1
- "@radix-ui/react-dismissable-layer": 1.0.4
- "@radix-ui/react-focus-guards": 1.0.1
- "@radix-ui/react-focus-scope": 1.0.3
- "@radix-ui/react-id": 1.0.1
- "@radix-ui/react-portal": 1.0.3
- "@radix-ui/react-presence": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-slot": 1.0.2
- "@radix-ui/react-use-controllable-state": 1.0.1
- aria-hidden: ^1.1.1
- react-remove-scroll: 2.5.5
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: 01ad549a3685e221628950e6fbec306494170aa3b92cbe00732b1531c16e1cf681138cd4a79d658f4f97d4096676a40d08642090fdea1675d0b7dc78df66d962
- languageName: node
- linkType: hard
-
-"@radix-ui/react-direction@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-direction@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 5336a8b0d4f1cde585d5c2b4448af7b3d948bb63a1aadb37c77771b0e5902dc6266e409cf35fd0edaca7f33e26424be19e64fb8f9d7f7be2d6f1714ea2764210
- languageName: node
- linkType: hard
-
-"@radix-ui/react-dismissable-layer@npm:1.0.4":
- version: 1.0.4
- resolution: "@radix-ui/react-dismissable-layer@npm:1.0.4"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/primitive": 1.0.1
- "@radix-ui/react-compose-refs": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-use-callback-ref": 1.0.1
- "@radix-ui/react-use-escape-keydown": 1.0.3
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: ea86004ed56a10609dd84eef39dc1e57b400d687a35be41bb4aaa06dc7ad6dbd0a8da281e08c8c077fdbd523122e4d860cb7438a60c664f024f77c8b41299ec6
- languageName: node
- linkType: hard
-
-"@radix-ui/react-focus-guards@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-focus-guards@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 1f8ca8f83b884b3612788d0742f3f054e327856d90a39841a47897dbed95e114ee512362ae314177de226d05310047cabbf66b686ae86ad1b65b6b295be24ef7
- languageName: node
- linkType: hard
-
-"@radix-ui/react-focus-scope@npm:1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-focus-scope@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-compose-refs": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-use-callback-ref": 1.0.1
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: e5b1a089071fbe77aca11124a4ad9623fc2bcaf4c019759b0cd044bf0878ecc924131ee09c6a22d38a3f094684ef68ed18fa65c8d891918412e0afc685a464e0
- languageName: node
- linkType: hard
-
-"@radix-ui/react-id@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-id@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-use-layout-effect": 1.0.1
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 446a453d799cc790dd2a1583ff8328da88271bff64530b5a17c102fa7fb35eece3cf8985359d416f65e330cd81aa7b8fe984ea125fc4f4eaf4b3801d698e49fe
- languageName: node
- linkType: hard
-
-"@radix-ui/react-portal@npm:1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-portal@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-primitive": 1.0.3
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: d352bcd6ad65eb43c9e0d72d0755c2aae85e03fb287770866262be3a2d5302b2885aee3cd99f2bbf62ecd14fcb1460703f1dcdc40351f77ad887b931c6f0012a
- languageName: node
- linkType: hard
-
-"@radix-ui/react-presence@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-presence@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-compose-refs": 1.0.1
- "@radix-ui/react-use-layout-effect": 1.0.1
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: ed2ff9faf9e4257a4065034d3771459e5a91c2d840b2fcec94661761704dbcb65bcdd927d28177a2a129b3dab5664eb90a9b88309afe0257a9f8ba99338c0d95
- languageName: node
- linkType: hard
-
-"@radix-ui/react-primitive@npm:1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-primitive@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-slot": 1.0.2
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: 9402bc22923c8e5c479051974a721c301535c36521c0237b83e5fa213d013174e77f3ad7905e6d60ef07e14f88ec7f4ea69891dc7a2b39047f8d3640e8f8d713
- languageName: node
- linkType: hard
-
-"@radix-ui/react-progress@npm:^1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-progress@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-context": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: a4398812315ae5b25f8637a6553daf85bd36e6e08d15f7486248538a994a99268752e2c21e4af15277c17e861c1825fdd7292938c515c112dc2cdce63ec1a892
- languageName: node
- linkType: hard
-
-"@radix-ui/react-roving-focus@npm:1.0.4":
- version: 1.0.4
- resolution: "@radix-ui/react-roving-focus@npm:1.0.4"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/primitive": 1.0.1
- "@radix-ui/react-collection": 1.0.3
- "@radix-ui/react-compose-refs": 1.0.1
- "@radix-ui/react-context": 1.0.1
- "@radix-ui/react-direction": 1.0.1
- "@radix-ui/react-id": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-use-callback-ref": 1.0.1
- "@radix-ui/react-use-controllable-state": 1.0.1
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: 69b1c82c2d9db3ba71549a848f2704200dab1b2cd22d050c1e081a78b9a567dbfdc7fd0403ee010c19b79652de69924d8ca2076cd031d6552901e4213493ffc7
- languageName: node
- linkType: hard
-
-"@radix-ui/react-slot@npm:1.0.2":
- version: 1.0.2
- resolution: "@radix-ui/react-slot@npm:1.0.2"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-compose-refs": 1.0.1
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: edf5edf435ff594bea7e198bf16d46caf81b6fb559493acad4fa8c308218896136acb16f9b7238c788fd13e94a904f2fd0b6d834e530e4cae94522cdb8f77ce9
- languageName: node
- linkType: hard
-
-"@radix-ui/react-tabs@npm:^1.0.4":
- version: 1.0.4
- resolution: "@radix-ui/react-tabs@npm:1.0.4"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/primitive": 1.0.1
- "@radix-ui/react-context": 1.0.1
- "@radix-ui/react-direction": 1.0.1
- "@radix-ui/react-id": 1.0.1
- "@radix-ui/react-presence": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-roving-focus": 1.0.4
- "@radix-ui/react-use-controllable-state": 1.0.1
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: 1daf0550da3ba527c1c2d8d7efd3a6618628f1f101a40f16c62eafb28df64a6bc7ee17ccb970b883907f99d601864c8f3c229c05e7bc7faf7f8c95b087141353
- languageName: node
- linkType: hard
-
-"@radix-ui/react-toggle@npm:^1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-toggle@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/primitive": 1.0.1
- "@radix-ui/react-primitive": 1.0.3
- "@radix-ui/react-use-controllable-state": 1.0.1
- peerDependencies:
- "@types/react": "*"
- "@types/react-dom": "*"
- react: ^16.8 || ^17.0 || ^18.0
- react-dom: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- "@types/react-dom":
- optional: true
- checksum: ed5407f48254f20cda542017774f259d0b2c0007ea4bd7287d10d751016dbf269cb13d1142591432c269c3ab768cde2f1ba0344743027d36bbec10af909f19de
- languageName: node
- linkType: hard
-
-"@radix-ui/react-use-callback-ref@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: b9fd39911c3644bbda14a84e4fca080682bef84212b8d8931fcaa2d2814465de242c4cfd8d7afb3020646bead9c5e539d478cea0a7031bee8a8a3bb164f3bc4c
- languageName: node
- linkType: hard
-
-"@radix-ui/react-use-controllable-state@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-use-controllable-state@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-use-callback-ref": 1.0.1
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: dee2be1937d293c3a492cb6d279fc11495a8f19dc595cdbfe24b434e917302f9ac91db24e8cc5af9a065f3f209c3423115b5442e65a5be9fd1e9091338972be9
- languageName: node
- linkType: hard
-
-"@radix-ui/react-use-escape-keydown@npm:1.0.3":
- version: 1.0.3
- resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.3"
- dependencies:
- "@babel/runtime": ^7.13.10
- "@radix-ui/react-use-callback-ref": 1.0.1
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: c6ed0d9ce780f67f924980eb305af1f6cce2a8acbaf043a58abe0aa3cc551d9aa76ccee14531df89bbee302ead7ecc7fce330886f82d4672c5eda52f357ef9b8
- languageName: node
- linkType: hard
-
-"@radix-ui/react-use-layout-effect@npm:1.0.1":
- version: 1.0.1
- resolution: "@radix-ui/react-use-layout-effect@npm:1.0.1"
- dependencies:
- "@babel/runtime": ^7.13.10
- peerDependencies:
- "@types/react": "*"
- react: ^16.8 || ^17.0 || ^18.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: bed9c7e8de243a5ec3b93bb6a5860950b0dba359b6680c84d57c7a655e123dec9b5891c5dfe81ab970652e7779fe2ad102a23177c7896dde95f7340817d47ae5
- languageName: node
- linkType: hard
-
"@react-dnd/asap@npm:^5.0.1":
version: 5.0.2
resolution: "@react-dnd/asap@npm:5.0.2"
@@ -2590,37 +2132,6 @@ __metadata:
languageName: node
linkType: hard
-"@react-hook/latest@npm:^1.0.2":
- version: 1.0.3
- resolution: "@react-hook/latest@npm:1.0.3"
- peerDependencies:
- react: ">=16.8"
- checksum: 2408c9cd35c5cfa7697b6da3bc5eebef254a932ade70955074c474f23be7dd3e2f81bbba12edcc9208bd0f89c6ed366d6b11d4f6d7b1052877a0bac8f74afad4
- languageName: node
- linkType: hard
-
-"@react-hook/passive-layout-effect@npm:^1.2.0":
- version: 1.2.1
- resolution: "@react-hook/passive-layout-effect@npm:1.2.1"
- peerDependencies:
- react: ">=16.8"
- checksum: 217cb8aa90fb8e677672319a9a466d7752890cf4357c76df000b207696e9cc717cf2ee88080671cc9dae238a82f92093ab4f61ab2f6032d2a8db958fc7d99b5d
- languageName: node
- linkType: hard
-
-"@react-hook/resize-observer@npm:^1.2.6":
- version: 1.2.6
- resolution: "@react-hook/resize-observer@npm:1.2.6"
- dependencies:
- "@juggle/resize-observer": ^3.3.1
- "@react-hook/latest": ^1.0.2
- "@react-hook/passive-layout-effect": ^1.2.0
- peerDependencies:
- react: ">=16.8"
- checksum: d2ff6c50e847514acad774f2a4010fb1e6782a231ae00c9507c1a98028b3a26399e35f094170918f11b1eeafc581d60da7641bc178d496abf00c56eee8a6b36b
- languageName: node
- linkType: hard
-
"@remix-run/router@npm:1.8.0":
version: 1.8.0
resolution: "@remix-run/router@npm:1.8.0"
@@ -2791,9 +2302,9 @@ __metadata:
languageName: node
linkType: hard
-"@snort/shared@npm:^1.0.11":
- version: 1.0.11
- resolution: "@snort/shared@npm:1.0.11"
+"@snort/shared@npm:^1.0.14":
+ version: 1.0.14
+ resolution: "@snort/shared@npm:1.0.14"
dependencies:
"@noble/curves": ^1.2.0
"@noble/hashes": ^1.3.2
@@ -2801,32 +2312,18 @@ __metadata:
debug: ^4.3.4
eventemitter3: ^5.0.1
light-bolt11-decoder: ^3.0.0
- checksum: 4a5441c9a1b2636283a9d39533821fcf4606dbbfbb39329aa814e6512282adf48768e761553e906f0c9c62c3862a0cb48b05a5a84cc871562a2d1120c7a902ac
+ checksum: 9f260aecc0f35232471259b68071183fda74cd4f0db93ad8b50d3af07f106a434896452c196db8d954caf16795fa182770f2f3c12ee7e34b5e36b83694453bb8
languageName: node
linkType: hard
-"@snort/shared@npm:^1.0.12":
- version: 1.0.12
- resolution: "@snort/shared@npm:1.0.12"
+"@snort/system-react@npm:^1.2.12":
+ version: 1.2.12
+ resolution: "@snort/system-react@npm:1.2.12"
dependencies:
- "@noble/curves": ^1.2.0
- "@noble/hashes": ^1.3.2
- "@scure/base": ^1.1.2
- debug: ^4.3.4
- eventemitter3: ^5.0.1
- light-bolt11-decoder: ^3.0.0
- checksum: b31ee1a1977db5c7fa2e5d29f364a956e6de38fab3d8b0e8f2b233a362e7a1095ee615b839c0c2a7d8d48b1c3445ec7cecc0f8470cf45863d0ddeb673053fcbb
- languageName: node
- linkType: hard
-
-"@snort/system-react@npm:^1.2.1":
- version: 1.2.1
- resolution: "@snort/system-react@npm:1.2.1"
- dependencies:
- "@snort/shared": ^1.0.12
- "@snort/system": ^1.2.1
+ "@snort/shared": ^1.0.14
+ "@snort/system": ^1.2.12
react: ^18.2.0
- checksum: 7f354f2251fdd4ed8e2dace0dd54c8d0b06de2d5c5eac5468850ac6416bf24d97a16fb0d6fff211a34b2fb22899f4b01c1ecc7de210066709d5b85fb436dc906
+ checksum: e0fa13c7d9efbd4cb5e226386db329f5953f9868dd1be3cf306f7fe249bf08ff32dbbaf624a6b67fa2c61309af29d783a098f3f7cfc394a18d04d33a558a668d
languageName: node
linkType: hard
@@ -2837,44 +2334,25 @@ __metadata:
languageName: node
linkType: hard
-"@snort/system-web@npm:^1.0.4":
- version: 1.0.4
- resolution: "@snort/system-web@npm:1.0.4"
+"@snort/system-web@npm:^1.2.11":
+ version: 1.2.11
+ resolution: "@snort/system-web@npm:1.2.11"
dependencies:
- "@snort/shared": ^1.0.11
- "@snort/system": ^1.2.0
+ "@snort/shared": ^1.0.14
+ "@snort/system": ^1.2.11
dexie: ^3.2.4
- checksum: ee705a586009cdc35c167aaeb4e505afac4d4c5f8eb80ae3b8519e631a7e9cfae828a7e00cc589eeff509bb5bd5e9398888763e03e26e1ce888393d3f5daf11b
+ checksum: 851db0c1c830df3507297a54f0c944da42e8bc35a97fcb6fd8de1e7891b97d50e2b919ccea7c5da62ccdba3cb2c6c788207b317d404fd92a003771a6ebe070f3
languageName: node
linkType: hard
-"@snort/system@npm:^1.2.0":
- version: 1.2.0
- resolution: "@snort/system@npm:1.2.0"
+"@snort/system@npm:^1.2.11":
+ version: 1.2.11
+ resolution: "@snort/system@npm:1.2.11"
dependencies:
"@noble/curves": ^1.2.0
"@noble/hashes": ^1.3.2
"@scure/base": ^1.1.2
- "@snort/shared": ^1.0.11
- "@stablelib/xchacha20": ^1.0.1
- debug: ^4.3.4
- eventemitter3: ^5.0.1
- isomorphic-ws: ^5.0.0
- lokijs: ^1.5.12
- uuid: ^9.0.0
- ws: ^8.14.0
- checksum: f5fb8a89f3d35db87c0c9a9e6cc49081d3c93c5092c51a6105ca020ff6679ae24b688d59c66482124e81eb839313ad5d723087eac948cad76f460a7f34876213
- languageName: node
- linkType: hard
-
-"@snort/system@npm:^1.2.1":
- version: 1.2.1
- resolution: "@snort/system@npm:1.2.1"
- dependencies:
- "@noble/curves": ^1.2.0
- "@noble/hashes": ^1.3.2
- "@scure/base": ^1.1.2
- "@snort/shared": ^1.0.12
+ "@snort/shared": ^1.0.14
"@stablelib/xchacha20": ^1.0.1
debug: ^4.3.4
eventemitter3: ^5.0.1
@@ -2883,7 +2361,27 @@ __metadata:
lru-cache: ^10.2.0
uuid: ^9.0.0
ws: ^8.14.0
- checksum: 78557fddba7f8ebba4ee1df0b0896f475c16d622f833aa71a849e40e9a1a2fc693bcfdcddf0ea9ec413f37297854881597eb3ae9cdebb8bd4e18b251eff1ee1c
+ checksum: 6aa810f8be82f9d8825cb93097ad4da4f8da546294058627fb6968818f8264886ad73b1d42822a7e92c2f4840496be7f21a489abe962262db344ca0d221239ee
+ languageName: node
+ linkType: hard
+
+"@snort/system@npm:^1.2.12":
+ version: 1.2.12
+ resolution: "@snort/system@npm:1.2.12"
+ dependencies:
+ "@noble/curves": ^1.2.0
+ "@noble/hashes": ^1.3.2
+ "@scure/base": ^1.1.2
+ "@snort/shared": ^1.0.14
+ "@stablelib/xchacha20": ^1.0.1
+ debug: ^4.3.4
+ eventemitter3: ^5.0.1
+ isomorphic-ws: ^5.0.0
+ lokijs: ^1.5.12
+ lru-cache: ^10.2.0
+ uuid: ^9.0.0
+ ws: ^8.14.0
+ checksum: d9546267bd6d95114528542a265f266320657b41f579b9c84deabc048a4295e73306ff04605168ef824aa51e74dccf1e5866f14cee6b4ae2a5a3247a605564f7
languageName: node
linkType: hard
@@ -3672,15 +3170,6 @@ __metadata:
languageName: node
linkType: hard
-"aria-hidden@npm:^1.1.1":
- version: 1.2.3
- resolution: "aria-hidden@npm:1.2.3"
- dependencies:
- tslib: ^2.0.0
- checksum: 7d7d211629eef315e94ed3b064c6823d13617e609d3f9afab1c2ed86399bb8e90405f9bdd358a85506802766f3ecb468af985c67c846045a34b973bcc0289db9
- languageName: node
- linkType: hard
-
"aria-query@npm:5.1.3":
version: 5.1.3
resolution: "aria-query@npm:5.1.3"
@@ -3859,20 +3348,6 @@ __metadata:
languageName: node
linkType: hard
-"browserslist@npm:^4.23.0":
- version: 4.23.0
- resolution: "browserslist@npm:4.23.0"
- dependencies:
- caniuse-lite: ^1.0.30001587
- electron-to-chromium: ^1.4.668
- node-releases: ^2.0.14
- update-browserslist-db: ^1.0.13
- bin:
- browserslist: cli.js
- checksum: 436f49e796782ca751ebab7edc010cfc9c29f68536f387666cd70ea22f7105563f04dd62c6ff89cb24cc3254d17cba385f979eeeb3484d43e012412ff7e75def
- languageName: node
- linkType: hard
-
"buffer-from@npm:^1.0.0":
version: 1.1.2
resolution: "buffer-from@npm:1.1.2"
@@ -3955,13 +3430,6 @@ __metadata:
languageName: node
linkType: hard
-"caniuse-lite@npm:^1.0.30001587":
- version: 1.0.30001588
- resolution: "caniuse-lite@npm:1.0.30001588"
- checksum: 2ab5fcec8fd3ee5d817a44bf1fb69804a6924d190e476863fb519692cd3e85a3a775bf4a2b6ba793f8db592ca61255b7f77f3d773ff7d42b452216f180bcdd2f
- languageName: node
- linkType: hard
-
"chalk@npm:^2.4.2":
version: 2.4.2
resolution: "chalk@npm:2.4.2"
@@ -4350,13 +3818,6 @@ __metadata:
languageName: node
linkType: hard
-"detect-node-es@npm:^1.1.0":
- version: 1.1.0
- resolution: "detect-node-es@npm:1.1.0"
- checksum: e46307d7264644975b71c104b9f028ed1d3d34b83a15b8a22373640ce5ea630e5640b1078b8ea15f202b54641da71e4aa7597093bd4b91f113db520a26a37449
- languageName: node
- linkType: hard
-
"dexie@npm:^3.2.4":
version: 3.2.4
resolution: "dexie@npm:3.2.4"
@@ -4448,13 +3909,6 @@ __metadata:
languageName: node
linkType: hard
-"electron-to-chromium@npm:^1.4.668":
- version: 1.4.679
- resolution: "electron-to-chromium@npm:1.4.679"
- checksum: 1884239565cec13308298d08a746a2721ed73f9c0128b7660ef5251404f00b0e1c0fb44afbd73be7d3e9c6bb15c24e7f6374e7148416c127140be9ec86cd9f6f
- languageName: node
- linkType: hard
-
"emoji-mart@npm:^5.5.2":
version: 5.5.2
resolution: "emoji-mart@npm:5.5.2"
@@ -5128,13 +4582,6 @@ __metadata:
languageName: node
linkType: hard
-"get-nonce@npm:^1.0.0":
- version: 1.0.1
- resolution: "get-nonce@npm:1.0.1"
- checksum: e2614e43b4694c78277bb61b0f04583d45786881289285c73770b07ded246a98be7e1f78b940c80cbe6f2b07f55f0b724e6db6fd6f1bcbd1e8bdac16521074ed
- languageName: node
- linkType: hard
-
"get-own-enumerable-property-symbols@npm:^3.0.0":
version: 3.0.2
resolution: "get-own-enumerable-property-symbols@npm:3.0.2"
@@ -5498,15 +4945,6 @@ __metadata:
languageName: node
linkType: hard
-"invariant@npm:^2.2.4":
- version: 2.2.4
- resolution: "invariant@npm:2.2.4"
- dependencies:
- loose-envify: ^1.0.0
- checksum: cc3182d793aad82a8d1f0af697b462939cb46066ec48bbf1707c150ad5fad6406137e91a262022c269702e01621f35ef60269f6c0d7fd178487959809acdfb14
- languageName: node
- linkType: hard
-
"ip@npm:^2.0.0":
version: 2.0.0
resolution: "ip@npm:2.0.0"
@@ -6066,7 +5504,7 @@ __metadata:
languageName: node
linkType: hard
-"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
+"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
@@ -6395,13 +5833,6 @@ __metadata:
languageName: node
linkType: hard
-"node-releases@npm:^2.0.14":
- version: 2.0.14
- resolution: "node-releases@npm:2.0.14"
- checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41
- languageName: node
- linkType: hard
-
"nopt@npm:^6.0.0":
version: 6.0.0
resolution: "nopt@npm:6.0.0"
@@ -6953,41 +6384,6 @@ __metadata:
languageName: node
linkType: hard
-"react-remove-scroll-bar@npm:^2.3.3":
- version: 2.3.4
- resolution: "react-remove-scroll-bar@npm:2.3.4"
- dependencies:
- react-style-singleton: ^2.2.1
- tslib: ^2.0.0
- peerDependencies:
- "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: b5ce5f2f98d65c97a3e975823ae4043a4ba2a3b63b5ba284b887e7853f051b5cd6afb74abde6d57b421931c52f2e1fdbb625dc858b1cb5a32c27c14ab85649d4
- languageName: node
- linkType: hard
-
-"react-remove-scroll@npm:2.5.5":
- version: 2.5.5
- resolution: "react-remove-scroll@npm:2.5.5"
- dependencies:
- react-remove-scroll-bar: ^2.3.3
- react-style-singleton: ^2.2.1
- tslib: ^2.1.0
- use-callback-ref: ^1.3.0
- use-sidecar: ^1.1.2
- peerDependencies:
- "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 2c7fe9cbd766f5e54beb4bec2e2efb2de3583037b23fef8fa511ab426ed7f1ae992382db5acd8ab5bfb030a4b93a06a2ebca41377d6eeaf0e6791bb0a59616a4
- languageName: node
- linkType: hard
-
"react-resize-detector@npm:^8.0.4":
version: 8.1.0
resolution: "react-resize-detector@npm:8.1.0"
@@ -7047,23 +6443,6 @@ __metadata:
languageName: node
linkType: hard
-"react-style-singleton@npm:^2.2.1":
- version: 2.2.1
- resolution: "react-style-singleton@npm:2.2.1"
- dependencies:
- get-nonce: ^1.0.0
- invariant: ^2.2.4
- tslib: ^2.0.0
- peerDependencies:
- "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 7ee8ef3aab74c7ae1d70ff34a27643d11ba1a8d62d072c767827d9ff9a520905223e567002e0bf6c772929d8ea1c781a3ba0cc4a563e92b1e3dc2eaa817ecbe8
- languageName: node
- linkType: hard
-
"react-tag-input-component@npm:^2.0.2":
version: 2.0.2
resolution: "react-tag-input-component@npm:2.0.2"
@@ -7715,18 +7094,12 @@ __metadata:
"@formatjs/cli": ^6.1.3
"@formatjs/ts-transformer": ^3.13.3
"@noble/curves": ^1.2.0
- "@radix-ui/react-collapsible": ^1.0.3
- "@radix-ui/react-dialog": ^1.0.4
- "@radix-ui/react-progress": ^1.0.3
- "@radix-ui/react-tabs": ^1.0.4
- "@radix-ui/react-toggle": ^1.0.3
- "@react-hook/resize-observer": ^1.2.6
"@scure/base": ^1.1.3
- "@snort/shared": ^1.0.12
- "@snort/system": ^1.2.1
- "@snort/system-react": ^1.2.1
+ "@snort/shared": ^1.0.14
+ "@snort/system": ^1.2.12
+ "@snort/system-react": ^1.2.12
"@snort/system-wasm": ^1.0.2
- "@snort/system-web": ^1.0.4
+ "@snort/system-web": ^1.2.11
"@szhsin/react-menu": ^4.0.2
"@testing-library/dom": ^9.3.1
"@types/lodash": ^4.14.195
@@ -7743,7 +7116,6 @@ __metadata:
"@webbtc/webln-types": ^1.0.12
"@webscopeio/react-textarea-autocomplete": ^4.9.2
autoprefixer: ^10.4.16
- browserslist: ^4.23.0
buffer: ^6.0.3
classnames: ^2.3.2
emoji-mart: ^5.5.2
@@ -8111,7 +7483,7 @@ __metadata:
languageName: node
linkType: hard
-"tslib@npm:2.6.2, tslib@npm:^2.0.0, tslib@npm:^2.1.0, tslib@npm:^2.4.0":
+"tslib@npm:2.6.2, tslib@npm:^2.4.0":
version: 2.6.2
resolution: "tslib@npm:2.6.2"
checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad
@@ -8349,20 +7721,6 @@ __metadata:
languageName: node
linkType: hard
-"update-browserslist-db@npm:^1.0.13":
- version: 1.0.13
- resolution: "update-browserslist-db@npm:1.0.13"
- dependencies:
- escalade: ^3.1.1
- picocolors: ^1.0.0
- peerDependencies:
- browserslist: ">= 4.21.0"
- bin:
- update-browserslist-db: cli.js
- checksum: 1e47d80182ab6e4ad35396ad8b61008ae2a1330221175d0abd37689658bdb61af9b705bfc41057fd16682474d79944fb2d86767c5ed5ae34b6276b9bed353322
- languageName: node
- linkType: hard
-
"uri-js@npm:^4.2.2":
version: 4.4.1
resolution: "uri-js@npm:4.4.1"
@@ -8372,37 +7730,6 @@ __metadata:
languageName: node
linkType: hard
-"use-callback-ref@npm:^1.3.0":
- version: 1.3.0
- resolution: "use-callback-ref@npm:1.3.0"
- dependencies:
- tslib: ^2.0.0
- peerDependencies:
- "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 7913df383a5a6fcb399212eedefaac2e0c6f843555202d4e3010bac3848afe38ecaa3d0d6500ad1d936fbeffd637e6c517e68edb024af5e6beca7f27f3ce7b21
- languageName: node
- linkType: hard
-
-"use-sidecar@npm:^1.1.2":
- version: 1.1.2
- resolution: "use-sidecar@npm:1.1.2"
- dependencies:
- detect-node-es: ^1.1.0
- tslib: ^2.0.0
- peerDependencies:
- "@types/react": ^16.9.0 || ^17.0.0 || ^18.0.0
- react: ^16.8.0 || ^17.0.0 || ^18.0.0
- peerDependenciesMeta:
- "@types/react":
- optional: true
- checksum: 925d1922f9853e516eaad526b6fed1be38008073067274f0ecc3f56b17bb8ab63480140dd7c271f94150027c996cea4efe83d3e3525e8f3eda22055f6a39220b
- languageName: node
- linkType: hard
-
"usehooks-ts@npm:^2.9.1":
version: 2.9.1
resolution: "usehooks-ts@npm:2.9.1"