From 803d0910af6a7596379579dc20738da11ce1cadd Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Sun, 25 Jun 2023 08:22:50 +0200 Subject: [PATCH 01/21] feat: mobile and tablet styles --- package.json | 1 + src/element/emoji.css | 1 - src/element/live-chat.css | 56 +++++++--- src/element/live-chat.tsx | 12 ++- src/element/live-video-player.tsx | 25 +++-- src/element/profile.tsx | 10 +- src/element/textarea.css | 4 + src/hooks/event-feed.ts | 21 ++-- src/pages/layout.css | 105 ++++++++++++++++++- src/pages/layout.tsx | 15 +-- src/pages/root.css | 21 +++- src/pages/stream-page.css | 102 ++++++++++++------ src/pages/stream-page.tsx | 165 ++++++++++++++++++++---------- yarn.lock | 24 +++++ 14 files changed, 430 insertions(+), 132 deletions(-) diff --git a/package.json b/package.json index 82a5d39..54f00a2 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@react-hook/resize-observer": "^1.2.6", "@snort/system-react": "^1.0.8", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^13.0.0", diff --git a/src/element/emoji.css b/src/element/emoji.css index bf193f2..35e29bc 100644 --- a/src/element/emoji.css +++ b/src/element/emoji.css @@ -2,5 +2,4 @@ width: 21px; height: 21px; display: inline-block; - margin-bottom: -5px; } diff --git a/src/element/live-chat.css b/src/element/live-chat.css index 4f4a6a6..ddf9e19 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -1,13 +1,23 @@ .live-chat { - height: calc(100vh - 72px - 96px); + grid-area: chat; display: flex; flex-direction: column; - padding: 24px 16px 8px 24px; - border: 1px solid #171717; - border-radius: 24px; - gap: 16px; + padding: 8px 16px; + border: none; + height: unset; } +@media (min-width: 1020px) { + .live-chat { + height: calc(100vh - 72px - 96px); + padding: 24px 16px 8px 24px; + border: 1px solid #171717; + border-radius: 24px; + gap: 16px; + } +} + + .live-chat>.header { font-weight: 600; font-size: 24px; @@ -15,12 +25,19 @@ } .live-chat>.messages { - flex-grow: 1; display: flex; gap: 12px; flex-direction: column-reverse; overflow-y: auto; overflow-x: hidden; + padding-bottom: 8px; + border-bottom: 1px solid var(--border, #171717); +} + +@media (min-width: 1020px){ + .live-chat > .messages { + flex-grow: 1; + } } .live-chat>.write-message { @@ -42,6 +59,10 @@ flex-grow: 1; } +.live-chat .message { + word-wrap: break-word; +} + .live-chat .message .profile { gap: 8px; font-weight: 600; @@ -69,6 +90,10 @@ color: white; } +.live-chat .messages .pill:hover { + cursor: default; +} + .live-chat .zap { display: flex; align-items: center; @@ -84,14 +109,21 @@ .top-zappers-container { display: flex; - gap: 8px; - justify-content: space-between; - padding-top: 12px; - padding-bottom: 20px; - border-bottom: 1px solid var(--border, #171717); + padding-top: 8px; + padding-bottom: 8px; overflow-y: scroll; } +@media (min-width: 1020px) { + .top-zappers-container { + display: flex; + gap: 8px; + padding-top: 12px; + padding-bottom: 20px; + border-bottom: 1px solid var(--border, #171717); + } +} + .top-zapper { display: flex; padding: 4px 8px 4px 4px; @@ -114,6 +146,6 @@ margin: 0; } -.top-zapper-icon { +.zap-icon { color: #FFCB44; } diff --git a/src/element/live-chat.tsx b/src/element/live-chat.tsx index c563dbe..7ac1404 100644 --- a/src/element/live-chat.tsx +++ b/src/element/live-chat.tsx @@ -42,7 +42,8 @@ function totalZapped(pubkey: string, zaps: ParsedZap[]) { function TopZappers({ zaps }: { zaps: ParsedZap[] }) { const zappers = zaps .map((z) => (z.anonZap ? "anon" : z.sender)) - .map((p) => p as string); + .map((p) => p as string) + .slice(0, 3); const sortedZappers = useMemo(() => { const sorted = [...new Set([...zappers])]; @@ -63,7 +64,7 @@ function TopZappers({ zaps }: { zaps: ParsedZap[] }) { ) : ( )} - +

{formatSats(total)}

); @@ -76,9 +77,11 @@ function TopZappers({ zaps }: { zaps: ParsedZap[] }) { export function LiveChat({ link, options, + height, }: { link: NostrLink; options?: LiveChatOptions; + height?: number; }) { const messages = useLiveChatFeed(link); const login = useLogin(); @@ -88,7 +91,7 @@ export function LiveChat({ .map((ev) => parseZap(ev, System.ProfileLoader.Cache)) .filter((z) => z && z.valid); return ( -
+
{(options?.showHeader ?? true) && (
Stream Chat
)} @@ -157,7 +160,7 @@ function ChatZap({ ev }: { ev: TaggedRawEvent }) { return (
- + - zapped   {formatSats(parsed.amount)}   sats
diff --git a/src/element/live-video-player.tsx b/src/element/live-video-player.tsx index c3964d7..15da4b5 100644 --- a/src/element/live-video-player.tsx +++ b/src/element/live-video-player.tsx @@ -3,16 +3,23 @@ import { HTMLProps, useEffect, useMemo, useRef, useState } from "react"; export enum VideoStatus { Online = "online", - Offline = "offline" + Offline = "offline", } -export function LiveVideoPlayer(props: HTMLProps & { stream?: string }) { +export function LiveVideoPlayer( + props: HTMLProps & { stream?: string } +) { const video = useRef(null); const streamCached = useMemo(() => props.stream, [props.stream]); const [status, setStatus] = useState(); useEffect(() => { - if (streamCached && video.current && !video.current.src && Hls.isSupported()) { + if ( + streamCached && + video.current && + !video.current.src && + Hls.isSupported() + ) { try { const hls = new Hls(); hls.loadSource(streamCached); @@ -25,10 +32,10 @@ export function LiveVideoPlayer(props: HTMLProps & { stream?: hls.detachMedia(); setStatus(VideoStatus.Offline); } - }) + }); hls.on(Hls.Events.MANIFEST_PARSED, () => { setStatus(VideoStatus.Online); - }) + }); return () => hls.destroy(); } catch (e) { console.error(e); @@ -37,9 +44,11 @@ export function LiveVideoPlayer(props: HTMLProps & { stream?: } }, [video, streamCached]); return ( -
-
{status}
+ <> +
+
{status}
+
+ ); } diff --git a/src/element/profile.tsx b/src/element/profile.tsx index 2ccaf18..8fc9204 100644 --- a/src/element/profile.tsx +++ b/src/element/profile.tsx @@ -24,16 +24,24 @@ export function getName(pk: string, user?: UserMetadata) { export function Profile({ pubkey, + avatarClassname, options, }: { pubkey: string; + avatarClassname?: string; options?: ProfileOptions; }) { const profile = useUserProfile(System, pubkey); return (
- {(options?.showAvatar ?? true) && } + {(options?.showAvatar ?? true) && ( + {profile?.name + )} {(options?.showName ?? true) && (options?.overrideName ?? getName(pubkey, profile))}
diff --git a/src/element/textarea.css b/src/element/textarea.css index df253bf..7614ab5 100644 --- a/src/element/textarea.css +++ b/src/element/textarea.css @@ -1,3 +1,7 @@ +.rta__textarea { + resize: none; +} + .rta__list { border: none; } diff --git a/src/hooks/event-feed.ts b/src/hooks/event-feed.ts index 001b763..d9e05e8 100644 --- a/src/hooks/event-feed.ts +++ b/src/hooks/event-feed.ts @@ -1,5 +1,10 @@ import { useMemo } from "react"; -import { NostrPrefix, RequestBuilder, ReplaceableNoteStore, NostrLink } from "@snort/system"; +import { + NostrPrefix, + RequestBuilder, + ReplaceableNoteStore, + NostrLink, +} from "@snort/system"; import { useRequestBuilder } from "@snort/system-react"; import { System } from "index"; @@ -8,8 +13,8 @@ export default function useEventFeed(link: NostrLink, leaveOpen = false) { const sub = useMemo(() => { const b = new RequestBuilder(`event:${link.id.slice(0, 12)}`); b.withOptions({ - leaveOpen - }) + leaveOpen, + }); if (link.type === NostrPrefix.Address) { const f = b.withFilter().tag("d", [link.id]); if (link.author) { @@ -21,14 +26,18 @@ export default function useEventFeed(link: NostrLink, leaveOpen = false) { } else { const f = b.withFilter().ids([link.id]); if (link.relays) { - link.relays.slice(0, 2).forEach(r => f.relay(r)); + link.relays.slice(0, 2).forEach((r) => f.relay(r)); } if (link.author) { f.authors([link.author]); } } return b; - }, [link]); + }, [link, leaveOpen]); - return useRequestBuilder(System, ReplaceableNoteStore, sub); + return useRequestBuilder( + System, + ReplaceableNoteStore, + sub + ); } diff --git a/src/pages/layout.css b/src/pages/layout.css index d3b6546..1ca015f 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -1,4 +1,74 @@ +.page { + display: grid; + grid-template-areas: + "header" + "video-content" + "profile" + "chat"; + grid-template-rows: 64px 230px 56px min-content; + grid-template-columns: 1fr; + height: 100vh; + gap: 0; +} + + +.live-chat { + max-height: calc(100vh - 385px); +} + +@media (min-width: 768px) { + .info { + display: none; + } + + .video-content video { + height: calc(100vh - 64px); + } + + .live-chat { + width: fit-content; + max-height: calc(100vh - 82px); + } + + .page { + display: grid; + grid-template-areas: + "header header" + "video-content chat"; + grid-template-rows: 64px min-content; + grid-template-columns: calc(min(600px, 1fr)) 1fr; + gap: 0; + } +} + +@media (min-width: 1020px) { + .video-content video { + height: unset; + } + + .page { + width: unset; + display: grid; + height: calc(100vh - 72px - 32px - 32px); + padding: 0 40px; + grid-template-columns: auto 376px; + grid-template-rows: unset; + grid-template-areas: + "header header" + "video-content chat" + "profile chat"; + gap: 32px; + } +} + +@media (min-width: 2000px) { + .page { + grid-template-columns: auto 450px; + } +} + header { + grid-area: header; display: grid; grid-template-columns: min-content min-content auto; gap: 24px; @@ -6,7 +76,7 @@ header { padding: 24px 40px 0 40px; } -header>div:nth-child(1) { +header .logo { background: #171717; border-radius: 16px; width: 48px; @@ -19,12 +89,12 @@ header>div:nth-child(1) { cursor: pointer; } -header>div:nth-child(2) { +header .input { min-width: 300px; height: 32px; } -header>div:nth-child(3) { +header .header-right { justify-self: end; display: flex; gap: 24px; @@ -44,4 +114,31 @@ header button { header .profile img { width: 48px; height: 48px; -} \ No newline at end of file +} + +@media (max-width: 1020px) { + header { + padding: 8px 16px 8px 16px; + gap: 8px; + } + + header .header-right { + gap: 8px; + } + + header .input { + min-width: unset; + } + + header .input .search-input { + display: none; + } + + header .new-stream-button-text { + display: none; + } + + header .profile img { + border-radius: 12px; + } +} diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index 1fa21b0..c23d625 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -37,10 +37,11 @@ export function LayoutPage() { className="btn btn-primary" onClick={() => setNewStream(true)} > - New Stream + New Stream +
-
navigate("/")}>S
+
navigate("/")}> + S +
- +
-
+
{loggedIn()} {loggedOut()}
@@ -95,6 +98,6 @@ export function LayoutPage() { )} - +
); } diff --git a/src/pages/root.css b/src/pages/root.css index 2f1ab52..a8c5b2c 100644 --- a/src/pages/root.css +++ b/src/pages/root.css @@ -5,6 +5,24 @@ padding: 40px; } +@media (max-width: 1020px) { + .video-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 12px; + padding: 16px; + } +} + +@media (max-width: 720px) { + .video-grid { + display: grid; + grid-template-columns: 1fr; + gap: 12px; + padding: 16px; + } +} + @media(min-width: 1600px) { .video-grid { grid-template-columns: repeat(6, 1fr); @@ -18,7 +36,6 @@ } .homepage h2 { - background: #171717; padding: 40px; -} \ No newline at end of file +} diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index 6c09092..4d4a58e 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -1,27 +1,21 @@ -.live-page { - display: grid; - height: calc(100vh - 72px - 32px - 32px); - padding: 32px 40px; - grid-template-columns: auto 376px; - gap: 32px; +.video-content { + grid-area: video-content; } -@media (min-width: 2000px) { - .live-page { - grid-template-columns: auto 450px; + +.video-content video { + width: 100%; + aspect-ratio: 16/9; + max-height: 230px; +} + +@media (min-width: 768px){ + .video-content video { + max-height: unset; } } -.live-page>div:nth-child(1) { - overflow-y: auto; -} - -.live-page video { - width: 100%; - aspect-ratio: 16/9; -} - -.live-page .pill { +.pill { font-weight: 700; font-size: 14px; line-height: 18px; @@ -29,62 +23,106 @@ text-transform: uppercase; } -.live-page .pill.live { +.pill.live { color: inherit; } -.live-page .info { - margin-top: 32px; +.profile-info { + display: flex; + justify-content: space-between; + padding: 0 16px; + width: 100%; } -.live-page .info h1 { +@media (min-width: 1020px) { + .info { + display: flex; + align-items: flex-start; + justify-content: space-between; + } + + .profile-info { + width: unset; + } +} + +.live-chat .header { + display: none; +} + +.stream-info { + display: none; +} + +@media (min-width: 1020px) { + .live-chat .header { + display: block; + } + + .stream-info { + display: block; + } +} + +.info { + grid-area: profile; + margin-top: 8px +} + +@media (min-width: 1020px) { + .info { + margin-top: 32px; + } +} + +.info h1 { margin: 0 0 8px 0; font-weight: 600; font-size: 28px; line-height: 35px; } -.live-page .info p { +.info p { margin: 0 0 12px 0; } -.live-page .tags { +.tags { display: flex; gap: 8px; } -.live-page .actions { +.actions { margin: 8px 0 0 0; display: flex; gap: 12px; } -.live-page .btn.zap { +.info .btn.zap { padding: 12px 16px; display: flex; align-items: center; gap: 12px; } -.live-page .offline { +.offline { background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; } -.live-page .online>div { +.online>div { display: none; } -.live-page .offline>div { +.offline>div { position: fixed; text-transform: uppercase; font-size: 30px; font-weight: 700; } -.live-page .offline>video { +.offline>video { z-index: -1; position: relative; -} \ No newline at end of file +} diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx index 5c243c2..a04a913 100644 --- a/src/pages/stream-page.tsx +++ b/src/pages/stream-page.tsx @@ -1,7 +1,8 @@ import "./stream-page.css"; -import { useState } from "react"; +import { useRef, useState, useLayoutEffect } from "react"; import { parseNostrLink, EventPublisher } from "@snort/system"; import { useNavigate, useParams } from "react-router-dom"; +import useResizeObserver from "@react-hook/resize-observer"; import moment from "moment"; import useEventFeed from "hooks/event-feed"; @@ -15,26 +16,23 @@ import { useLogin } from "hooks/login"; import { StreamState, System } from "index"; import Modal from "element/modal"; import { SendZaps } from "element/send-zap"; +import type { NostrLink } from "@snort/system"; import { useUserProfile } from "@snort/system-react"; import { NewStream } from "element/new-stream"; -export function StreamPage() { - const params = useParams(); - const link = parseNostrLink(params.id!); +function ProfileInfo({ link }: { link: NostrLink }) { const thisEvent = useEventFeed(link, true); const login = useLogin(); const navigate = useNavigate(); const [zap, setZap] = useState(false); const [edit, setEdit] = useState(false); const profile = useUserProfile(System, thisEvent.data?.pubkey); + const zapTarget = profile?.lud16 ?? profile?.lud06; - const stream = findTag(thisEvent.data, "streaming"); const status = findTag(thisEvent.data, "status"); - const image = findTag(thisEvent.data, "image"); const start = findTag(thisEvent.data, "starts"); const isLive = status === "live"; const isMine = link.author === login?.pubkey; - const zapTarget = profile?.lud16 ?? profile?.lud06; async function deleteStream() { const pub = await EventPublisher.nip7(); @@ -47,59 +45,54 @@ export function StreamPage() { } return ( -
-
- -
-
-

{findTag(thisEvent.data, "title")}

-

{findTag(thisEvent.data, "summary")}

-
- {status} - {status === StreamState.Planned && Starts {moment(Number(start) * 1000).fromNow()}} - {thisEvent.data?.tags - .filter((a) => a[0] === "t") - .map((a) => a[1]) - .map((a) => ( - - {a} - - ))} -
- {isMine && ( -
- - - Delete - -
+ <> +
+
+

{findTag(thisEvent.data, "title")}

+

{findTag(thisEvent.data, "summary")}

+
+ {status} + {status === StreamState.Planned && ( + + Starts {moment(Number(start) * 1000).fromNow()} + )} + {thisEvent.data?.tags + .filter((a) => a[0] === "t") + .map((a) => a[1]) + .map((a) => ( + + {a} + + ))}
-
-
- + {isMine && ( +
+ + Delete +
-
+ )} +
+
+ +
- {zap && zapTarget && thisEvent.data && ( setZap(false)}> setEdit(false)}> - setEdit(false)} /> + + )} + + ); +} + +function VideoPlayer({ link }: { link: NostrLink }) { + const thisEvent = useEventFeed(link); + const [zap, setZap] = useState(false); + const [edit, setEdit] = useState(false); + const profile = useUserProfile(System, thisEvent.data?.pubkey); + const zapTarget = profile?.lud16 ?? profile?.lud06; + + const stream = findTag(thisEvent.data, "streaming"); + const image = findTag(thisEvent.data, "image"); + + return ( + <> + {zap && zapTarget && thisEvent.data && ( + setZap(false)}> + setEdit(false)} + targetName={getName(thisEvent.data.pubkey, profile)} + onFinish={() => setZap(false)} /> )} -
+ {edit && thisEvent.data && ( + setEdit(false)}> + setEdit(false)} /> + + )} +
+ +
+ + ); +} + +export function StreamPage() { + const ref = useRef(null); + const params = useParams(); + const link = parseNostrLink(params.id!); + const [height, setHeight] = useState(); + + function setChatHeight() { + const contentHeight = + document.querySelector(".live-page")?.clientHeight || 0; + const videoContentHeight = + document.querySelector(".video-content")?.clientHeight || 0; + if (window.innerWidth <= 480) { + setHeight(contentHeight - videoContentHeight); + } else { + setHeight(undefined); + } + } + + useLayoutEffect(setChatHeight, []); + useResizeObserver(ref, () => setChatHeight()); + + return ( + <> +
+ + + + ); } diff --git a/yarn.lock b/yarn.lock index bb6e718..4608140 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1634,6 +1634,11 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@juggle/resize-observer@^3.3.1": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60" + integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA== + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" @@ -1694,6 +1699,25 @@ schema-utils "^3.0.0" source-map "^0.7.3" +"@react-hook/latest@^1.0.2": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@react-hook/latest/-/latest-1.0.3.tgz#c2d1d0b0af8b69ec6e2b3a2412ba0768ac82db80" + integrity sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg== + +"@react-hook/passive-layout-effect@^1.2.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz#c06dac2d011f36d61259aa1c6df4f0d5e28bc55e" + integrity sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg== + +"@react-hook/resize-observer@^1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@react-hook/resize-observer/-/resize-observer-1.2.6.tgz#9a8cf4c5abb09becd60d1d65f6bf10eec211e291" + integrity sha512-DlBXtLSW0DqYYTW3Ft1/GQFZlTdKY5VAFIC4+km6IK5NiPPDFchGbEJm1j6pSgMqPRHbUQgHJX7RaR76ic1LWA== + dependencies: + "@juggle/resize-observer" "^3.3.1" + "@react-hook/latest" "^1.0.2" + "@react-hook/passive-layout-effect" "^1.2.0" + "@remix-run/router@1.6.3": version "1.6.3" resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.6.3.tgz#8205baf6e17ef93be35bf62c37d2d594e9be0dad" From 954a875c10059b401792d37bb459dc6c4809d096 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 11:14:57 +0200 Subject: [PATCH 02/21] fix: video content height for tablet --- src/pages/stream-page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx index a04a913..7cf7051 100644 --- a/src/pages/stream-page.tsx +++ b/src/pages/stream-page.tsx @@ -159,6 +159,8 @@ export function StreamPage() { document.querySelector(".video-content")?.clientHeight || 0; if (window.innerWidth <= 480) { setHeight(contentHeight - videoContentHeight); + } else if (window.innerWidth <= 768) { + setHeight(videoContentHeight); } else { setHeight(undefined); } From b0bf485f0abae1f3b92b65cad044e8438852efbc Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 11:27:04 +0200 Subject: [PATCH 03/21] fix: emoji margin --- src/element/emoji.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/element/emoji.css b/src/element/emoji.css index 35e29bc..bf193f2 100644 --- a/src/element/emoji.css +++ b/src/element/emoji.css @@ -2,4 +2,5 @@ width: 21px; height: 21px; display: inline-block; + margin-bottom: -5px; } From aa66e678d125d27233c7c0772a42a0c3b1c2710b Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 15:27:56 +0200 Subject: [PATCH 04/21] fix: hide scrollbars --- src/element/live-chat.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/element/live-chat.css b/src/element/live-chat.css index ddf9e19..156dcd8 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -112,6 +112,12 @@ padding-top: 8px; padding-bottom: 8px; overflow-y: scroll; + -ms-overflow-style: none; + scrollbar-width: none; +} + +.top-zappers-container::-webkit-scrollbar { + display: none; } @media (min-width: 1020px) { From 8bc4c1e2ce2f7fa5204f9104fea8b23d5f05f98b Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 15:28:06 +0200 Subject: [PATCH 05/21] fix: top zappers --- src/element/live-chat.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/element/live-chat.tsx b/src/element/live-chat.tsx index 7ac1404..2aa6691 100644 --- a/src/element/live-chat.tsx +++ b/src/element/live-chat.tsx @@ -42,13 +42,12 @@ function totalZapped(pubkey: string, zaps: ParsedZap[]) { function TopZappers({ zaps }: { zaps: ParsedZap[] }) { const zappers = zaps .map((z) => (z.anonZap ? "anon" : z.sender)) - .map((p) => p as string) - .slice(0, 3); + .map((p) => p as string); const sortedZappers = useMemo(() => { const sorted = [...new Set([...zappers])]; sorted.sort((a, b) => totalZapped(b, zaps) - totalZapped(a, zaps)); - return sorted; + return sorted.slice(0, 3); }, [zaps, zappers]); return ( From ddbe0e2a9263113c6d7465d2a311d4f4691a35b1 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 15:28:24 +0200 Subject: [PATCH 06/21] fix: chat height with css --- src/pages/layout.css | 21 +++++++++++---------- src/pages/layout.tsx | 7 +++++-- src/pages/stream-page.css | 14 ++++++++------ src/pages/stream-page.tsx | 23 ++--------------------- 4 files changed, 26 insertions(+), 39 deletions(-) diff --git a/src/pages/layout.css b/src/pages/layout.css index 1ca015f..be54589 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -11,16 +11,20 @@ gap: 0; } +.page.home { + display: grid; + grid-template-areas: + "header" + "video-content"; + grid-template-rows: 64px 1fr; + grid-template-columns: 1fr; +} .live-chat { max-height: calc(100vh - 385px); } @media (min-width: 768px) { - .info { - display: none; - } - .video-content video { height: calc(100vh - 64px); } @@ -34,18 +38,15 @@ display: grid; grid-template-areas: "header header" + "video-content profile" "video-content chat"; grid-template-rows: 64px min-content; - grid-template-columns: calc(min(600px, 1fr)) 1fr; + grid-template-columns: minmax(600px, 1fr) min-content; gap: 0; } } @media (min-width: 1020px) { - .video-content video { - height: unset; - } - .page { width: unset; display: grid; @@ -57,7 +58,7 @@ "header header" "video-content chat" "profile chat"; - gap: 32px; + gap: 0; } } diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index c23d625..5a34653 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -6,7 +6,7 @@ import { encodeTLV, NostrPrefix, } from "@snort/system"; -import { Outlet, useNavigate } from "react-router-dom"; +import { Outlet, useNavigate, useLocation } from "react-router-dom"; import AsyncButton from "element/async-button"; import { Login } from "index"; import { useLogin } from "hooks/login"; @@ -19,6 +19,7 @@ export function LayoutPage() { const navigate = useNavigate(); const login = useLogin(); const [newStream, setNewStream] = useState(false); + const location = useLocation(); async function doLogin() { const pub = await EventPublisher.nip7(); @@ -78,7 +79,9 @@ export function LayoutPage() { } return ( -
+
navigate("/")}> S diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index 4d4a58e..4ac6bff 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -62,6 +62,14 @@ .stream-info { display: block; } + + .video-content video { + height: 100%; + } + + .live-chat { + margin-left: 32px; + } } .info { @@ -69,12 +77,6 @@ margin-top: 8px } -@media (min-width: 1020px) { - .info { - margin-top: 32px; - } -} - .info h1 { margin: 0 0 8px 0; font-weight: 600; diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx index 7cf7051..2c1693c 100644 --- a/src/pages/stream-page.tsx +++ b/src/pages/stream-page.tsx @@ -1,8 +1,7 @@ import "./stream-page.css"; -import { useRef, useState, useLayoutEffect } from "react"; +import { useRef, useState } from "react"; import { parseNostrLink, EventPublisher } from "@snort/system"; import { useNavigate, useParams } from "react-router-dom"; -import useResizeObserver from "@react-hook/resize-observer"; import moment from "moment"; import useEventFeed from "hooks/event-feed"; @@ -150,31 +149,13 @@ export function StreamPage() { const ref = useRef(null); const params = useParams(); const link = parseNostrLink(params.id!); - const [height, setHeight] = useState(); - - function setChatHeight() { - const contentHeight = - document.querySelector(".live-page")?.clientHeight || 0; - const videoContentHeight = - document.querySelector(".video-content")?.clientHeight || 0; - if (window.innerWidth <= 480) { - setHeight(contentHeight - videoContentHeight); - } else if (window.innerWidth <= 768) { - setHeight(videoContentHeight); - } else { - setHeight(undefined); - } - } - - useLayoutEffect(setChatHeight, []); - useResizeObserver(ref, () => setChatHeight()); return ( <>
- + ); } From bda0d36f9a80d91dd170893149307d30273c924a Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 15:39:02 +0200 Subject: [PATCH 07/21] refactor: rename grid area --- src/pages/layout.css | 10 +++++----- src/pages/stream-page.css | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pages/layout.css b/src/pages/layout.css index be54589..f55f354 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -2,7 +2,7 @@ display: grid; grid-template-areas: "header" - "video-content" + "main-content" "profile" "chat"; grid-template-rows: 64px 230px 56px min-content; @@ -15,7 +15,7 @@ display: grid; grid-template-areas: "header" - "video-content"; + "main-content"; grid-template-rows: 64px 1fr; grid-template-columns: 1fr; } @@ -38,8 +38,8 @@ display: grid; grid-template-areas: "header header" - "video-content profile" - "video-content chat"; + "main-content profile" + "main-content chat"; grid-template-rows: 64px min-content; grid-template-columns: minmax(600px, 1fr) min-content; gap: 0; @@ -56,7 +56,7 @@ grid-template-rows: unset; grid-template-areas: "header header" - "video-content chat" + "main-content chat" "profile chat"; gap: 0; } diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index 4ac6bff..01e7fc1 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -1,5 +1,5 @@ .video-content { - grid-area: video-content; + grid-area: main-content; } From 9f5b17ac3cc37828794ed61ab248de76cc6faaea Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 15:46:48 +0200 Subject: [PATCH 08/21] fix: popout chat --- src/pages/chat-popout.css | 12 ++++++++++-- src/pages/chat-popout.tsx | 21 +++++++++++++-------- src/pages/layout.css | 14 ++++++++++++++ src/pages/layout.tsx | 8 +++++++- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/pages/chat-popout.css b/src/pages/chat-popout.css index c9d11fa..beadaca 100644 --- a/src/pages/chat-popout.css +++ b/src/pages/chat-popout.css @@ -1,5 +1,13 @@ +.popout-chat { + grid-area: main-content; +} + .popout-chat .live-chat { - height: calc(100vh - 20px); + margin-left: 0; +} + +.popout-chat .live-chat { + height: calc(100vh); padding: 10px; border: unset; border-radius: unset; @@ -7,4 +15,4 @@ .popout-chat .live-chat .messages { overflow: hidden; -} \ No newline at end of file +} diff --git a/src/pages/chat-popout.tsx b/src/pages/chat-popout.tsx index f9275d3..86f07d7 100644 --- a/src/pages/chat-popout.tsx +++ b/src/pages/chat-popout.tsx @@ -4,13 +4,18 @@ import { useParams } from "react-router-dom"; import { parseNostrLink } from "@snort/system"; export function ChatPopout() { - const params = useParams(); - const link = parseNostrLink(params.id!); + const params = useParams(); + const link = parseNostrLink(params.id!); - return
- + return ( +
+
-} \ No newline at end of file + ); +} diff --git a/src/pages/layout.css b/src/pages/layout.css index f55f354..ce914b3 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -20,10 +20,24 @@ grid-template-columns: 1fr; } +.popout-chat { + display: grid; + grid-template-areas: + "main-content"; + grid-template-rows: 1fr; + grid-template-columns: 1fr; + height: 100vh; + gap: 0; +} + .live-chat { max-height: calc(100vh - 385px); } +.popout-chat .live-chat { + max-height: unset; +} + @media (min-width: 768px) { .video-content video { height: calc(100vh - 64px); diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index 5a34653..0ee5247 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -80,7 +80,13 @@ export function LayoutPage() { return (
navigate("/")}> From 9d3f5fc23511ce4294ecbaaa89b69530d361c876 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 15:50:20 +0200 Subject: [PATCH 09/21] fix: offline placement --- src/pages/stream-page.css | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index 01e7fc1..39c81bc 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -10,6 +10,10 @@ } @media (min-width: 768px){ + .profile-info { + max-height: 42px; + } + .video-content video { max-height: unset; } @@ -119,11 +123,18 @@ .offline>div { position: fixed; + top: 5em; text-transform: uppercase; font-size: 30px; font-weight: 700; } +@media (min-width: 768px) { + .offline>div { + top: 10em; + } +} + .offline>video { z-index: -1; position: relative; From 4309df393c837317a0957a1322eafdf3b2bf1229 Mon Sep 17 00:00:00 2001 From: Kieran Date: Tue, 27 Jun 2023 11:09:38 +0100 Subject: [PATCH 10/21] Only set stream offline when fatal error --- src/element/live-video-player.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/element/live-video-player.tsx b/src/element/live-video-player.tsx index 15da4b5..1354f44 100644 --- a/src/element/live-video-player.tsx +++ b/src/element/live-video-player.tsx @@ -27,7 +27,7 @@ export function LiveVideoPlayer( hls.on(Hls.Events.ERROR, (event, data) => { console.debug(event, data); const errorType = data.type; - if (errorType === Hls.ErrorTypes.NETWORK_ERROR) { + if (errorType === Hls.ErrorTypes.NETWORK_ERROR && data.fatal) { hls.stopLoad(); hls.detachMedia(); setStatus(VideoStatus.Offline); From baa20a6fd0cb579280ec362fa06abac9889d2147 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Mon, 26 Jun 2023 19:54:50 +0200 Subject: [PATCH 11/21] fix: header adjustments --- src/pages/layout.css | 20 +++++++++----------- src/pages/stream-page.css | 1 - 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/pages/layout.css b/src/pages/layout.css index ce914b3..ab01905 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -84,11 +84,18 @@ header { grid-area: header; + align-items: center; display: grid; grid-template-columns: min-content min-content auto; + padding: 8px 16px; + gap: 8px; +} + +@media (min-width: 1020px) { + header { gap: 24px; - align-items: center; - padding: 24px 40px 0 40px; + padding: 24px 0 32px 0; + } } header .logo { @@ -132,11 +139,6 @@ header .profile img { } @media (max-width: 1020px) { - header { - padding: 8px 16px 8px 16px; - gap: 8px; - } - header .header-right { gap: 8px; } @@ -152,8 +154,4 @@ header .profile img { header .new-stream-button-text { display: none; } - - header .profile img { - border-radius: 12px; - } } diff --git a/src/pages/stream-page.css b/src/pages/stream-page.css index 39c81bc..2d6d046 100644 --- a/src/pages/stream-page.css +++ b/src/pages/stream-page.css @@ -24,7 +24,6 @@ font-size: 14px; line-height: 18px; color: #A7A7A7; - text-transform: uppercase; } .pill.live { From f17d97eca943177ab4c4a230b368b144294d951b Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Tue, 27 Jun 2023 16:35:37 +0200 Subject: [PATCH 12/21] fml --- src/element/live-chat.css | 3 +- src/pages/layout.css | 69 +++++++++++++++++++++------------------ 2 files changed, 39 insertions(+), 33 deletions(-) diff --git a/src/element/live-chat.css b/src/element/live-chat.css index 156dcd8..a3b9c9d 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -4,7 +4,8 @@ flex-direction: column; padding: 8px 16px; border: none; - height: unset; + max-width: calc(100vw - 32px); + height: calc(100vh - 72px - 230px - 64px); } @media (min-width: 1020px) { diff --git a/src/pages/layout.css b/src/pages/layout.css index ab01905..41150f1 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -1,18 +1,21 @@ .page { display: grid; + gap: 0; grid-template-areas: "header" "main-content" "profile" "chat"; - grid-template-rows: 64px 230px 56px min-content; + grid-template-rows: 64px 230px 56px 1fr; grid-template-columns: 1fr; height: 100vh; - gap: 0; + width: 100vw; } .page.home { display: grid; + height: 100vh; + width: 100vw; grid-template-areas: "header" "main-content"; @@ -20,51 +23,34 @@ grid-template-columns: 1fr; } -.popout-chat { - display: grid; - grid-template-areas: - "main-content"; - grid-template-rows: 1fr; - grid-template-columns: 1fr; - height: 100vh; - gap: 0; -} - -.live-chat { - max-height: calc(100vh - 385px); -} - -.popout-chat .live-chat { - max-height: unset; -} - @media (min-width: 768px) { - .video-content video { - height: calc(100vh - 64px); - } - - .live-chat { - width: fit-content; - max-height: calc(100vh - 82px); - } - .page { display: grid; + width: 100vw; + height: 100vh; grid-template-areas: "header header" "main-content profile" "main-content chat"; grid-template-rows: 64px min-content; - grid-template-columns: minmax(600px, 1fr) min-content; + grid-template-columns: minmax(600px, 1fr) 1fr; gap: 0; + } + + .video-content video { + height: calc(100vh - 64px); + } + + .live-chat { + height: calc(100vh - 56px); } } @media (min-width: 1020px) { .page { - width: unset; display: grid; - height: calc(100vh - 72px - 32px - 32px); + width: unset; + height: calc(100vh - 72px); padding: 0 40px; grid-template-columns: auto 376px; grid-template-rows: unset; @@ -74,6 +60,10 @@ "profile chat"; gap: 0; } + + .live-chat { + width: unset; + } } @media (min-width: 2000px) { @@ -155,3 +145,18 @@ header .profile img { display: none; } } + +.popout-chat { + display: grid; + grid-template-areas: + "main-content"; + grid-template-rows: 1fr; + grid-template-columns: 1fr; + height: 100vh; + gap: 0; +} + +.popout-chat .live-chat { + max-height: unset; +} + From f780f5212cec2169022c0d82f2e0f2ab7bb06d4f Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Tue, 27 Jun 2023 16:49:02 +0200 Subject: [PATCH 13/21] fix: tablet and mobile fixes --- src/element/live-chat.css | 2 -- src/pages/layout.css | 12 ++++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/element/live-chat.css b/src/element/live-chat.css index a3b9c9d..b99bced 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -4,8 +4,6 @@ flex-direction: column; padding: 8px 16px; border: none; - max-width: calc(100vw - 32px); - height: calc(100vh - 72px - 230px - 64px); } @media (min-width: 1020px) { diff --git a/src/pages/layout.css b/src/pages/layout.css index 41150f1..bd7c98b 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -12,6 +12,10 @@ width: 100vw; } +.live-chat { + height: calc(100vh - 16px - 64px - 56px - 230px); +} + .page.home { display: grid; height: 100vh; @@ -33,7 +37,7 @@ "main-content profile" "main-content chat"; grid-template-rows: 64px min-content; - grid-template-columns: minmax(600px, 1fr) 1fr; + grid-template-columns: minmax(600px, 3fr) 1fr; gap: 0; } @@ -42,7 +46,7 @@ } .live-chat { - height: calc(100vh - 56px); + height: calc(100vh - 56px - 64px - 16px); } } @@ -60,10 +64,6 @@ "profile chat"; gap: 0; } - - .live-chat { - width: unset; - } } @media (min-width: 2000px) { From 59f4fa1b815d6145840e6eed7eb30536b201bdbf Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Tue, 27 Jun 2023 17:59:24 +0200 Subject: [PATCH 14/21] fix: moar sizing fixes --- src/element/live-chat.css | 21 ++++++++++++++++++++- src/pages/layout.css | 26 +++++++++++++------------- src/pages/layout.tsx | 2 +- src/pages/stream-page.css | 36 ++++++++++++++++++++++++------------ src/pages/stream-page.tsx | 2 +- 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/element/live-chat.css b/src/element/live-chat.css index b99bced..dfda7fa 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -4,6 +4,22 @@ flex-direction: column; padding: 8px 16px; border: none; + height: calc(100vh - 56px - 64px - 56px - 230px); +} + +@media (min-width: 768px) { + .profile-info { + width: calc(100vw - 600px); + } + + .live-chat { + width: calc(100vw - 600px); + height: calc(100vh - 56px - 64px - 16px); + } + + .video-content video { + width: 100%; + } } @media (min-width: 1020px) { @@ -14,8 +30,11 @@ border-radius: 24px; gap: 16px; } -} + .live-chat { + width: 320px; + } +} .live-chat>.header { font-weight: 600; diff --git a/src/pages/layout.css b/src/pages/layout.css index bd7c98b..aa8973e 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -9,17 +9,12 @@ grid-template-rows: 64px 230px 56px 1fr; grid-template-columns: 1fr; height: 100vh; - width: 100vw; } -.live-chat { - height: calc(100vh - 16px - 64px - 56px - 230px); -} .page.home { display: grid; height: 100vh; - width: 100vw; grid-template-areas: "header" "main-content"; @@ -30,30 +25,25 @@ @media (min-width: 768px) { .page { display: grid; - width: 100vw; height: 100vh; grid-template-areas: "header header" "main-content profile" "main-content chat"; grid-template-rows: 64px min-content; - grid-template-columns: minmax(600px, 3fr) 1fr; + grid-template-columns: 600px 1fr; gap: 0; } .video-content video { - height: calc(100vh - 64px); + height: 100%; } - .live-chat { - height: calc(100vh - 56px - 64px - 16px); - } } @media (min-width: 1020px) { .page { display: grid; - width: unset; height: calc(100vh - 72px); padding: 0 40px; grid-template-columns: auto 376px; @@ -64,6 +54,7 @@ "profile chat"; gap: 0; } + } @media (min-width: 2000px) { @@ -146,6 +137,16 @@ header .profile img { } } +button span.hide-on-mobile { + display: none; +} + +@media (min-width: 1020px) { + button span.hide-on-mobile { + display: block; + } +} + .popout-chat { display: grid; grid-template-areas: @@ -159,4 +160,3 @@ header .profile img { .popout-chat .live-chat { max-height: unset; } - diff --git a/src/pages/layout.tsx b/src/pages/layout.tsx index 0ee5247..0856f33 100644 --- a/src/pages/layout.tsx +++ b/src/pages/layout.tsx @@ -38,7 +38,7 @@ export function LayoutPage() { className="btn btn-primary" onClick={() => setNewStream(true)} > - New Stream + New Stream
From 8ee39b137b4fedb84c4d752a9f516c0787de7693 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Tue, 27 Jun 2023 18:01:05 +0200 Subject: [PATCH 15/21] fix: popout chat --- src/pages/layout.css | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/layout.css b/src/pages/layout.css index aa8973e..0564fe7 100644 --- a/src/pages/layout.css +++ b/src/pages/layout.css @@ -158,5 +158,7 @@ button span.hide-on-mobile { } .popout-chat .live-chat { - max-height: unset; + padding: 8px 16px; + height: 100vh; + width: 100vw; } From 699f82527050a8da919c9a2dc159a099ba2acde0 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Tue, 27 Jun 2023 18:09:23 +0200 Subject: [PATCH 16/21] fix: profile info tweaks --- src/element/live-chat.css | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/element/live-chat.css b/src/element/live-chat.css index dfda7fa..31148c6 100644 --- a/src/element/live-chat.css +++ b/src/element/live-chat.css @@ -9,11 +9,11 @@ @media (min-width: 768px) { .profile-info { - width: calc(100vw - 600px); + width: calc(100vw - 600px - 16px); } .live-chat { - width: calc(100vw - 600px); + width: calc(100vw - 600px - 16px); height: calc(100vh - 56px - 64px - 16px); } @@ -23,6 +23,11 @@ } @media (min-width: 1020px) { + .profile-info { + width: unset; + padding: 0; + } + .live-chat { height: calc(100vh - 72px - 96px); padding: 24px 16px 8px 24px; @@ -48,8 +53,6 @@ flex-direction: column-reverse; overflow-y: auto; overflow-x: hidden; - padding-bottom: 8px; - border-bottom: 1px solid var(--border, #171717); } @media (min-width: 1020px){ @@ -61,6 +64,10 @@ .live-chat>.write-message { display: flex; gap: 8px; + margin-top: auto; + + padding-top: 8px; + border-top: 1px solid var(--border, #171717); } .live-chat>.write-message>div:nth-child(1) { From 391e54942173eadfc44ef6dc863948e2b701e834 Mon Sep 17 00:00:00 2001 From: Alejandro Gomez Date: Tue, 27 Jun 2023 23:59:37 +0200 Subject: [PATCH 17/21] feat: responsible new stream modal --- package.json | 1 + src/element/modal.css | 4 +- src/element/modal.tsx | 2 +- src/element/new-stream.css | 22 +++- src/element/new-stream.tsx | 75 +++++++++--- src/element/send-zap.tsx | 226 ++++++++++++++++++++++--------------- src/pages/layout.tsx | 20 +--- src/pages/stream-page.tsx | 52 +++------ yarn.lock | 218 ++++++++++++++++++++++++++++++++++- 9 files changed, 451 insertions(+), 169 deletions(-) diff --git a/package.json b/package.json index 54f00a2..7abebc3 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "@radix-ui/react-dialog": "^1.0.4", "@react-hook/resize-observer": "^1.2.6", "@snort/system-react": "^1.0.8", "@testing-library/jest-dom": "^5.14.1", diff --git a/src/element/modal.css b/src/element/modal.css index b8181bf..d49c6cb 100644 --- a/src/element/modal.css +++ b/src/element/modal.css @@ -13,10 +13,10 @@ .modal-body { display: flex; - width: 430px; + max-width: 430px; padding: 32px; margin-top: auto; margin-bottom: auto; border-radius: 32px; background: #171717; -} \ No newline at end of file +} diff --git a/src/element/modal.tsx b/src/element/modal.tsx index 8f7f7ca..f7345ad 100644 --- a/src/element/modal.tsx +++ b/src/element/modal.tsx @@ -18,7 +18,7 @@ export default function Modal(props: ModalProps) { return (
-
e.stopPropagation()}> +
e.stopPropagation()}> {props.children}
diff --git a/src/element/new-stream.css b/src/element/new-stream.css index e05814b..629fd25 100644 --- a/src/element/new-stream.css +++ b/src/element/new-stream.css @@ -2,7 +2,6 @@ display: flex; flex-direction: column; gap: 24px; - width: inherit; } .new-stream h3 { @@ -49,4 +48,23 @@ .new-stream .pill.active { color: inherit; background: #353535; -} \ No newline at end of file +} + +.dialog-overlay { + background-color: rgba(0, 0, 0, 0.8); + position: fixed; + inset: 0; +} + +.dialog-content { + background-color: #171717; + border-radius: 6px; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 90vw; + max-width: 450px; + max-height: 85vh; + padding: 25px; +} diff --git a/src/element/new-stream.tsx b/src/element/new-stream.tsx index 68f6fd8..6ef9bfc 100644 --- a/src/element/new-stream.tsx +++ b/src/element/new-stream.tsx @@ -1,4 +1,5 @@ import "./new-stream.css"; +import * as Dialog from "@radix-ui/react-dialog"; import { useEffect, useState } from "react"; import { EventPublisher, NostrEvent } from "@snort/system"; @@ -6,6 +7,7 @@ import { unixNow } from "@snort/shared"; import AsyncButton from "./async-button"; import { StreamState, System } from "index"; +import { Icon } from "element/icon"; import { findTag } from "utils"; export function NewStream({ @@ -19,7 +21,9 @@ export function NewStream({ const [summary, setSummary] = useState(findTag(ev, "summary") ?? ""); const [image, setImage] = useState(findTag(ev, "image") ?? ""); const [stream, setStream] = useState(findTag(ev, "streaming") ?? ""); - const [status, setStatus] = useState(findTag(ev, "status") ?? StreamState.Live); + const [status, setStatus] = useState( + findTag(ev, "status") ?? StreamState.Live + ); const [start, setStart] = useState(findTag(ev, "starts")); const [isValid, setIsValid] = useState(false); @@ -48,8 +52,7 @@ export function NewStream({ const dTag = findTag(ev, "d") ?? now.toString(); const starts = start ?? now.toString(); const ends = findTag(ev, "ends") ?? now.toString(); - eb - .kind(30_311) + eb.kind(30_311) .tag(["d", dTag]) .tag(["title", title]) .tag(["summary", summary]) @@ -69,11 +72,11 @@ export function NewStream({ } function toDateTimeString(n: number) { - return new Date(n * 1000).toISOString().substring(0, -1) + return new Date(n * 1000).toISOString().substring(0, -1); } function fromDateTimeString(s: string) { - return Math.floor(new Date(s).getTime() / 1000) + return Math.floor(new Date(s).getTime() / 1000); } return ( @@ -127,17 +130,32 @@ export function NewStream({

Status

- {[StreamState.Live, StreamState.Planned, StreamState.Ended].map(v => setStatus(v)}> - {v} - )} + {[StreamState.Live, StreamState.Planned, StreamState.Ended].map( + (v) => ( + setStatus(v)} + > + {v} + + ) + )}
- {status === StreamState.Planned &&
-

Start Time

-
- setStart(fromDateTimeString(e.target.value).toString())} /> + {status === StreamState.Planned && ( +
+

Start Time

+
+ + setStart(fromDateTimeString(e.target.value).toString()) + } + /> +
-
} + )}
); } + +interface NewStreamDialogProps { + text?: string; + btnClassName?: string; + ev?: NostrEvent; + onFinish: (e: NostrEvent) => void; +} + +export function NewStreamDialog({ + text = "New Stream", + ev, + onFinish, + btnClassName = "btn", +}: NewStreamDialogProps) { + return ( + + + + + + + + + + + + ); +} diff --git a/src/element/send-zap.tsx b/src/element/send-zap.tsx index 343f890..87b8de8 100644 --- a/src/element/send-zap.tsx +++ b/src/element/send-zap.tsx @@ -9,107 +9,149 @@ import { findTag } from "utils"; import { Relays } from "index"; import QrCode from "./qr-code"; -export function SendZaps({ lnurl, ev, targetName, onFinish }: { lnurl: string, ev?: NostrEvent, targetName?: string, onFinish: () => void }) { - const UsdRate = 30_000; +export function SendZaps({ + lnurl, + ev, + targetName, + onFinish, +}: { + lnurl: string; + ev?: NostrEvent; + targetName?: string; + onFinish: () => void; +}) { + const UsdRate = 30_000; - const satsAmounts = [ - 100, 1_000, 5_000, 10_000, 50_000, 100_000, 500_000, 1_000_000 - ]; - const usdAmounts = [ - 0.05, 0.50, 2, 5, 10, 50, 100, 200 - ] - const [isFiat, setIsFiat] = useState(false); - const [svc, setSvc] = useState(); - const [amount, setAmount] = useState(satsAmounts[0]); - const [comment, setComment] = useState(""); - const [invoice, setInvoice] = useState(""); + const satsAmounts = [ + 100, 1_000, 5_000, 10_000, 50_000, 100_000, 500_000, 1_000_000, + ]; + const usdAmounts = [0.05, 0.5, 2, 5, 10, 50, 100, 200]; + const [isFiat, setIsFiat] = useState(false); + const [svc, setSvc] = useState(); + const [amount, setAmount] = useState(satsAmounts[0]); + const [comment, setComment] = useState(""); + const [invoice, setInvoice] = useState(""); - const name = targetName ?? svc?.name; - async function loadService() { - const s = new LNURL(lnurl); - await s.load(); - setSvc(s); + const name = targetName ?? svc?.name; + async function loadService() { + const s = new LNURL(lnurl); + await s.load(); + setSvc(s); + } + + useEffect(() => { + if (!svc) { + loadService().catch(console.warn); } + }, [lnurl]); - useEffect(() => { - if (!svc) { - loadService().catch(console.warn); - } - }, [lnurl]); - - async function send() { - if (!svc) return; - const pub = await EventPublisher.nip7(); - if (!pub) return; - - const amountInSats = isFiat ? Math.floor((amount / UsdRate) * 1e8) : amount; - let zap: NostrEvent | undefined; - if (ev) { - zap = await pub.zap(amountInSats * 1000, ev.pubkey, Relays, undefined, comment, eb => { - return eb.tag(["a", `${ev.kind}:${ev.pubkey}:${findTag(ev, "d")}`]); - }); - } - const invoice = await svc.getInvoice(amountInSats, comment, zap); - if (!invoice.pr) return; - - if (window.webln) { - await window.webln.enable(); - await window.webln.sendPayment(invoice.pr); - onFinish(); - } else { - setInvoice(invoice.pr); + async function send() { + if (!svc) return; + const pub = await EventPublisher.nip7(); + if (!pub) return; + + const amountInSats = isFiat ? Math.floor((amount / UsdRate) * 1e8) : amount; + let zap: NostrEvent | undefined; + if (ev) { + zap = await pub.zap( + amountInSats * 1000, + ev.pubkey, + Relays, + undefined, + comment, + (eb) => { + return eb.tag(["a", `${ev.kind}:${ev.pubkey}:${findTag(ev, "d")}`]); } + ); } + const invoice = await svc.getInvoice(amountInSats, comment, zap); + if (!invoice.pr) return; - function input() { - if (invoice) return; - return <> -
- { setIsFiat(false); setAmount(satsAmounts[0]) }}> - SATS - - { setIsFiat(true); setAmount(usdAmounts[0]) }}> - USD - -
-
- Zap amount in {isFiat ? "USD" : "sats"} -
- {(isFiat ? usdAmounts : satsAmounts).map(a => - setAmount(a)}> - {isFiat ? `$${a.toLocaleString()}` : formatSats(a)} - )} -
-
-
- - Your comment for {name} - -
-