This commit is contained in:
parent
08d554aa8f
commit
45ee55a4c1
@ -39,6 +39,3 @@
|
||||
.collapsed-event-header svg {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.expanded-event {
|
||||
}
|
||||
|
@ -67,12 +67,7 @@ export function CollapsibleEvent({ link }: { link: NostrLink }) {
|
||||
</Collapsible.Trigger>
|
||||
</div>
|
||||
<Collapsible.Content>
|
||||
{open && event && (
|
||||
<div className="expanded-event">
|
||||
{" "}
|
||||
<NostrEvent ev={event} />
|
||||
</div>
|
||||
)}
|
||||
{open && event && <NostrEvent ev={event} />}
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
);
|
||||
|
@ -18,16 +18,3 @@
|
||||
font-weight: 400;
|
||||
line-height: 29px; /* 161.111% */
|
||||
}
|
||||
|
||||
.markdown img:not(.emoji):not(.note-avatar) {
|
||||
max-height: 720px;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.markdown video {
|
||||
width: 100%;
|
||||
aspect-ratio: 4/3;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
@ -4,17 +4,12 @@ import { useMemo } from "react";
|
||||
import ReactMarkdown from "react-markdown";
|
||||
|
||||
import { HyperText } from "element/hypertext";
|
||||
import {
|
||||
transformText,
|
||||
type Fragment,
|
||||
type NostrComponents,
|
||||
} from "element/text";
|
||||
import { transformText, type Fragment } from "element/text";
|
||||
import type { Tags } from "types";
|
||||
|
||||
interface MarkdownProps {
|
||||
content: string;
|
||||
tags?: Tags;
|
||||
customComponents?: NostrComponents;
|
||||
}
|
||||
|
||||
interface LinkProps {
|
||||
@ -26,36 +21,20 @@ interface ComponentProps {
|
||||
children?: Array<Fragment>;
|
||||
}
|
||||
|
||||
export function Markdown({
|
||||
content,
|
||||
tags = [],
|
||||
customComponents,
|
||||
}: MarkdownProps) {
|
||||
export function Markdown({ content, tags = [] }: MarkdownProps) {
|
||||
const components = useMemo(() => {
|
||||
return {
|
||||
li: ({ children, ...props }: ComponentProps) => {
|
||||
return (
|
||||
children && (
|
||||
<li {...props}>
|
||||
{transformText(children, tags, customComponents)}
|
||||
</li>
|
||||
)
|
||||
);
|
||||
return children && <li {...props}>{transformText(children, tags)}</li>;
|
||||
},
|
||||
td: ({ children }: ComponentProps) => {
|
||||
return (
|
||||
children && <td>{transformText(children, tags, customComponents)}</td>
|
||||
);
|
||||
return children && <td>{transformText(children, tags)}</td>;
|
||||
},
|
||||
th: ({ children }: ComponentProps) => {
|
||||
return (
|
||||
children && <th>{transformText(children, tags, customComponents)}</th>
|
||||
);
|
||||
return children && <th>{transformText(children, tags)}</th>;
|
||||
},
|
||||
p: ({ children }: ComponentProps) => {
|
||||
return (
|
||||
children && <p>{transformText(children, tags, customComponents)}</p>
|
||||
);
|
||||
return children && <p>{transformText(children, tags)}</p>;
|
||||
},
|
||||
a: ({ href, children }: LinkProps) => {
|
||||
return href && <HyperText link={href}>{children}</HyperText>;
|
||||
|
@ -174,73 +174,3 @@
|
||||
.stream-card {
|
||||
max-width: 343px;
|
||||
}
|
||||
|
||||
.top-zappers-card .top-zappers-leaderboard {
|
||||
border: 1px solid;
|
||||
padding: 4px 8px;
|
||||
border-radius: 12px;
|
||||
border-color: var(--border);
|
||||
}
|
||||
|
||||
.top-zappers-card .top-zapper-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-zapper-container .zap-amount {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.top-zapper-container .top-zapper-amount {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.top-zappers-card .top-zappers-leaderboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.top-zapper-container.first .profile {
|
||||
background: var(--gradient-purple);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.top-zapper-container.second .profile {
|
||||
background: var(--gradient-yellow);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.top-zapper-container.third .profile {
|
||||
background: var(--gradient-orange);
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.top-zapper-container.live .profile {
|
||||
background-size: 300% 300%;
|
||||
animation: animatedgradient 3s ease alternate infinite;
|
||||
}
|
||||
|
||||
@keyframes animatedgradient {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
@ -12,16 +12,12 @@ import { Icon } from "element/icon";
|
||||
import { ExternalLink } from "element/external-link";
|
||||
import { FileUploader } from "element/file-uploader";
|
||||
import { Markdown } from "element/markdown";
|
||||
import { Profile } from "element/profile";
|
||||
import { useLogin } from "hooks/login";
|
||||
import { useCards, useUserCards } from "hooks/cards";
|
||||
import { useZaps } from "hooks/zaps";
|
||||
import useTopZappers from "hooks/top-zappers";
|
||||
import { CARD, USER_CARDS } from "const";
|
||||
import { toTag, findTag } from "utils";
|
||||
import { Login, System } from "index";
|
||||
import type { Tags } from "types";
|
||||
import { formatSats } from "number";
|
||||
|
||||
interface CardType {
|
||||
identifier: string;
|
||||
@ -430,14 +426,12 @@ export function StreamCardEditor({ pubkey, tags }: StreamCardEditorProps) {
|
||||
|
||||
interface StreamCardsProps {
|
||||
host: string;
|
||||
isLive: boolean;
|
||||
}
|
||||
|
||||
export function ReadOnlyStreamCards({ host, isLive }: StreamCardsProps) {
|
||||
export function ReadOnlyStreamCards({ host }: StreamCardsProps) {
|
||||
const cards = useCards(host);
|
||||
return (
|
||||
<div className="stream-cards">
|
||||
{cards.length === 99 && <TopZappers host={host} isLive={isLive} />}
|
||||
{cards.map((ev) => (
|
||||
<Card cards={cards} key={ev!.id} ev={ev!} />
|
||||
))}
|
||||
@ -445,42 +439,7 @@ export function ReadOnlyStreamCards({ host, isLive }: StreamCardsProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface TopZappersProps {
|
||||
host: string;
|
||||
isLive: boolean;
|
||||
n?: number;
|
||||
}
|
||||
|
||||
function TopZappers({ host, isLive, n = 5 }: TopZappersProps) {
|
||||
const zaps = useZaps(host);
|
||||
const topZappers = useTopZappers(zaps);
|
||||
return topZappers.length > 0 ? (
|
||||
<div className="stream-card top-zappers-card">
|
||||
<h1 className="card-title">Top Zappers</h1>
|
||||
<div className="top-zappers-leaderboard">
|
||||
{topZappers
|
||||
.filter((z) => z.pubkey !== "anon")
|
||||
.slice(0, n)
|
||||
.map((z, idx) => (
|
||||
<div
|
||||
className={`top-zapper-container ${idx === 0 ? "first" : ""} ${
|
||||
idx === 1 ? "second" : ""
|
||||
} ${idx === 2 ? "third" : ""}
|
||||
${isLive && idx < 3 ? "live" : ""}`}
|
||||
>
|
||||
<Profile pubkey={z.pubkey} />
|
||||
<div className="zap-amount">
|
||||
<Icon name="zap-filled" className="zap-icon" />
|
||||
<p className="top-zapper-amount">{formatSats(z.total)}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : null;
|
||||
}
|
||||
|
||||
export function StreamCards({ host, isLive }: StreamCardsProps) {
|
||||
export function StreamCards({ host }: StreamCardsProps) {
|
||||
const login = useLogin();
|
||||
const canEdit = login?.pubkey === host;
|
||||
return (
|
||||
@ -488,7 +447,7 @@ export function StreamCards({ host, isLive }: StreamCardsProps) {
|
||||
{canEdit ? (
|
||||
<StreamCardEditor tags={login.cards.tags} pubkey={login.pubkey} />
|
||||
) : (
|
||||
<ReadOnlyStreamCards isLive={isLive} host={host} />
|
||||
<ReadOnlyStreamCards host={host} />
|
||||
)}
|
||||
</DndProvider>
|
||||
);
|
||||
|
@ -1,13 +0,0 @@
|
||||
.text img:not(.emoji):not(.note-avatar) {
|
||||
max-height: 720px;
|
||||
margin-top: 8px;
|
||||
width: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.text video {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
aspect-ratio: 4/3;
|
||||
border-radius: 6px;
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
import "./text.css";
|
||||
import { useMemo, type ReactNode, type FunctionComponent } from "react";
|
||||
|
||||
import {
|
||||
|
@ -260,13 +260,6 @@ div.paper {
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
.dialog-trigger {
|
||||
font-size: 15px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.ctx-menu {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
|
@ -3,9 +3,6 @@ import { parseNostrLink, TaggedRawEvent } from "@snort/system";
|
||||
import { useLocation, useNavigate, useParams } from "react-router-dom";
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import { NostrEvent } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
|
||||
import { LiveVideoPlayer } from "element/live-video-player";
|
||||
import {
|
||||
createNostrLink,
|
||||
@ -20,10 +17,13 @@ import { useLogin } from "hooks/login";
|
||||
import { useZapGoal } from "hooks/goals";
|
||||
import { StreamState, System } from "index";
|
||||
import { SendZapsDialog } from "element/send-zap";
|
||||
import { NostrEvent } from "@snort/system";
|
||||
import { useUserProfile } from "@snort/system-react";
|
||||
import { NewStreamDialog } from "element/new-stream";
|
||||
import { Tags } from "element/tags";
|
||||
import { StatePill } from "element/state-pill";
|
||||
import { StreamCards } from "element/stream-cards";
|
||||
import { formatSats } from "number";
|
||||
import { StreamTimer } from "element/stream-time";
|
||||
import { ShareMenu } from "element/share-menu";
|
||||
import {
|
||||
@ -31,7 +31,6 @@ import {
|
||||
isContentWarningAccepted,
|
||||
} from "element/content-warning";
|
||||
import { useCurrentStreamFeed } from "hooks/current-stream-feed";
|
||||
import { formatSats } from "number";
|
||||
|
||||
function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
|
||||
const login = useLogin();
|
||||
@ -157,7 +156,7 @@ export function StreamPage() {
|
||||
<div className="video-content">
|
||||
<LiveVideoPlayer stream={stream} poster={image} status={status} />
|
||||
<ProfileInfo ev={ev} goal={goal} />
|
||||
<StreamCards host={host} isLive={status === StreamState.Live} />
|
||||
<StreamCards host={host} />
|
||||
</div>
|
||||
<LiveChat link={createNostrLink(ev) ?? link} ev={ev} goal={goal} />
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user