Redo page styles
This commit is contained in:
@ -1,6 +1,7 @@
|
|||||||
.goal {
|
.goal {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
margin-bottom: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.goal p {
|
.goal p {
|
||||||
|
@ -1,64 +1,19 @@
|
|||||||
.live-chat {
|
.live-chat {
|
||||||
grid-area: chat;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border: none;
|
border: none;
|
||||||
height: calc(100vh - 56px - 64px - 36px - 230px);
|
gap: var(--gap-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.live-chat ::-webkit-scrollbar {
|
.live-chat ::-webkit-scrollbar {
|
||||||
width: 8px;
|
width: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
.live-chat .header {
|
||||||
.profile-info {
|
display: flex;
|
||||||
width: calc(100vw - 600px - 16px);
|
align-items: center;
|
||||||
}
|
justify-content: space-between;
|
||||||
|
|
||||||
.live-chat {
|
|
||||||
width: calc(100vw - 600px - 16px);
|
|
||||||
height: calc(100vh - 56px - 64px - 16px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-content video {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1020px) {
|
|
||||||
.profile-info {
|
|
||||||
width: unset;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-chat {
|
|
||||||
height: calc(100vh - 72px - 96px);
|
|
||||||
padding: 24px 16px 8px 24px;
|
|
||||||
border: 1px solid #171717;
|
|
||||||
border-radius: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-chat {
|
|
||||||
width: auto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 2000px) {
|
|
||||||
.live-chat {
|
|
||||||
height: calc(100vh - 72px - 96px - 120px - 56px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-chat > .header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 768px){
|
|
||||||
.live-chat > .header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.live-chat .header .title {
|
.live-chat .header .title {
|
||||||
@ -158,9 +113,9 @@
|
|||||||
.top-zappers {
|
.top-zappers {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 16px;
|
gap: var(--gap-s);
|
||||||
border-bottom: 1px solid var(--border, #171717);
|
border-bottom: 1px solid var(--border, #171717);
|
||||||
padding-bottom: 18px;
|
padding-bottom: var(--gap-s);
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-zappers h3 {
|
.top-zappers h3 {
|
||||||
@ -170,12 +125,6 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.top-zappers h3 {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-zappers-container {
|
.top-zappers-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
|
@ -14,7 +14,7 @@ export function Tags({
|
|||||||
const status = findTag(ev, "status");
|
const status = findTag(ev, "status");
|
||||||
const start = findTag(ev, "starts");
|
const start = findTag(ev, "starts");
|
||||||
return (
|
return (
|
||||||
<div className="tags">
|
<>
|
||||||
{children}
|
{children}
|
||||||
{status === StreamState.Planned && (
|
{status === StreamState.Planned && (
|
||||||
<span className="pill">
|
<span className="pill">
|
||||||
@ -30,6 +30,6 @@ export function Tags({
|
|||||||
{a}
|
{a}
|
||||||
</span>
|
</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,21 @@ body {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--gap-l: 32px;
|
||||||
|
--gap-m: 24px;
|
||||||
|
--gap-s: 16px;
|
||||||
|
--header-height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media(max-width: 1020px) {
|
||||||
|
:root {
|
||||||
|
--gap-l: 24px;
|
||||||
|
--gap-m: 16px;
|
||||||
|
--gap-s: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
code {
|
code {
|
||||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||||
monospace;
|
monospace;
|
||||||
|
@ -48,10 +48,6 @@ const router = createBrowserRouter([
|
|||||||
path: "/p/:npub",
|
path: "/p/:npub",
|
||||||
element: <ProfilePage />,
|
element: <ProfilePage />,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: "/nsfw",
|
|
||||||
element: <RootPage nsfw={true} />,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: "/:id",
|
path: "/:id",
|
||||||
element: <StreamPage />,
|
element: <StreamPage />,
|
||||||
|
30
src/pages/ContentWarningOverlay.tsx
Normal file
30
src/pages/ContentWarningOverlay.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
export function ContentWarningOverlay() {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [is18Plus, setIs18Plus] = useState(
|
||||||
|
Boolean(window.localStorage.getItem("accepted-content-warning"))
|
||||||
|
);
|
||||||
|
if (is18Plus) return null;
|
||||||
|
|
||||||
|
function grownUp() {
|
||||||
|
window.localStorage.setItem("accepted-content-warning", "true");
|
||||||
|
setIs18Plus(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="fullscreen-exclusive age-check">
|
||||||
|
<h1>Sexually explicit material ahead!</h1>
|
||||||
|
<h2>Confirm your age</h2>
|
||||||
|
<div className="flex g24">
|
||||||
|
<button className="btn btn-warning" onClick={grownUp}>
|
||||||
|
Yes, I am over 18
|
||||||
|
</button>
|
||||||
|
<button className="btn" onClick={() => navigate("/")}>
|
||||||
|
No, I am under 18
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,22 +1,16 @@
|
|||||||
.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 {
|
.popout-chat .live-chat {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
width: 100vw;
|
width: calc(100vw - 32px);
|
||||||
height: calc(100vh - 32px);
|
height: calc(100vh - 16px);
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
border: unset;
|
border: unset;
|
||||||
border-radius: unset;
|
border-radius: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
.popout-chat .live-chat .messages {
|
.popout-chat .live-chat .messages {
|
||||||
overflow: hidden;
|
padding-right: unset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.popout-chat.embed .live-chat .messages::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
@ -9,14 +9,14 @@ export function ChatPopout() {
|
|||||||
const link = parseNostrLink(params.id!);
|
const link = parseNostrLink(params.id!);
|
||||||
const { data: ev } = useEventFeed(link, true);
|
const { data: ev } = useEventFeed(link, true);
|
||||||
|
|
||||||
const chat = new URL(window.location.href).searchParams.get("chat");
|
const chat = Boolean(new URL(window.location.href).searchParams.get("chat"));
|
||||||
return (
|
return (
|
||||||
<div className="popout-chat">
|
<div className={`popout-chat${chat ? "" : " embed"}`}>
|
||||||
<LiveChat
|
<LiveChat
|
||||||
ev={ev}
|
ev={ev}
|
||||||
link={link}
|
link={link}
|
||||||
options={{
|
options={{
|
||||||
canWrite: Boolean(chat),
|
canWrite: chat,
|
||||||
showHeader: false,
|
showHeader: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -1,95 +1,36 @@
|
|||||||
.page {
|
.page {
|
||||||
display: grid;
|
--page-pad-tb: 16px;
|
||||||
gap: 0;
|
--page-pad-lr: 40px;
|
||||||
grid-template-areas:
|
--header-page-padding: calc(var(--page-pad-tb) + var(--page-pad-tb));
|
||||||
"header"
|
|
||||||
"main-content"
|
padding: var(--page-pad-tb) var(--page-pad-lr);
|
||||||
"profile"
|
display: flex;
|
||||||
"chat";
|
flex-direction: column;
|
||||||
grid-template-rows: 64px 230px 56px 1fr;
|
gap: var(--gap-s);
|
||||||
grid-template-columns: 1fr;
|
|
||||||
height: 100vh;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.page.only-content {
|
.page.stream {
|
||||||
display: grid;
|
height: calc(100vh - var(--header-page-padding));
|
||||||
height: 100vh;
|
overflow: hidden;
|
||||||
grid-template-areas:
|
|
||||||
"header"
|
|
||||||
"main-content";
|
|
||||||
grid-template-rows: 64px 1fr;
|
|
||||||
grid-template-columns: 1fr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (max-width: 1020px) {
|
||||||
.page {
|
.page {
|
||||||
display: grid;
|
--page-pad-tb: 8px;
|
||||||
height: 100vh;
|
--page-pad-lr: 0;
|
||||||
grid-template-areas:
|
|
||||||
"header header"
|
|
||||||
"main-content profile"
|
|
||||||
"main-content chat";
|
|
||||||
grid-template-rows: 64px min-content;
|
|
||||||
grid-template-columns: 600px 1fr;
|
|
||||||
gap: 0;
|
|
||||||
}
|
}
|
||||||
|
.page.stream {
|
||||||
.video-content video {
|
display: flex;
|
||||||
height: 100%;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
header {
|
||||||
}
|
padding: 0 16px;
|
||||||
|
|
||||||
@media (min-width: 1020px) {
|
|
||||||
.page {
|
|
||||||
display: grid;
|
|
||||||
height: calc(100vh - 72px);
|
|
||||||
padding: 0 40px;
|
|
||||||
grid-template-columns: auto 376px;
|
|
||||||
grid-template-rows: unset;
|
|
||||||
grid-template-areas:
|
|
||||||
"header header"
|
|
||||||
"main-content chat"
|
|
||||||
"profile chat";
|
|
||||||
gap: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 2000px) {
|
|
||||||
.page {
|
|
||||||
padding: 0 40px;
|
|
||||||
grid-template-columns: auto 450px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-content {
|
|
||||||
max-height: calc(100vh - 320px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-content video {
|
|
||||||
height: 100%;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
header {
|
||||||
grid-area: header;
|
display: flex;
|
||||||
align-items: center;
|
gap: 24px;
|
||||||
display: grid;
|
|
||||||
grid-template-columns: min-content min-content min-content auto;
|
|
||||||
padding: 8px 16px;
|
|
||||||
gap: 8px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 1020px) {
|
|
||||||
header {
|
|
||||||
gap: 24px;
|
|
||||||
padding: 24px 0 32px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.page.only-content {
|
|
||||||
grid-template-rows: 88px 1fr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
header .logo {
|
header .logo {
|
||||||
@ -178,7 +119,7 @@ button span.hide-on-mobile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.zap-icon {
|
.zap-icon {
|
||||||
color: #FF8D2B;
|
color: #ff8d2b;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tags {
|
.tags {
|
||||||
@ -194,7 +135,7 @@ button span.hide-on-mobile {
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
background: #0A0A0A;
|
background: #0a0a0a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.age-check {
|
.age-check {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import "./layout.css";
|
import "./layout.css";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import * as Dialog from "@radix-ui/react-dialog";
|
import * as Dialog from "@radix-ui/react-dialog";
|
||||||
import { Outlet, useNavigate, useLocation, Link } from "react-router-dom";
|
import { Outlet, useNavigate } from "react-router-dom";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
|
|
||||||
import { Icon } from "element/icon";
|
import { Icon } from "element/icon";
|
||||||
@ -16,7 +16,6 @@ import { Login } from "index";
|
|||||||
export function LayoutPage() {
|
export function LayoutPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const login = useLogin();
|
const login = useLogin();
|
||||||
const location = useLocation();
|
|
||||||
const [showLogin, setShowLogin] = useState(false);
|
const [showLogin, setShowLogin] = useState(false);
|
||||||
|
|
||||||
function loggedIn() {
|
function loggedIn() {
|
||||||
@ -81,21 +80,9 @@ export function LayoutPage() {
|
|||||||
</Dialog.Root>
|
</Dialog.Root>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const isNsfw = window.location.pathname === "/nsfw";
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={`page${location.pathname.startsWith("/naddr1") ? " stream" : ""}`}>
|
||||||
className={
|
|
||||||
location.pathname === "/" ||
|
|
||||||
location.pathname.startsWith("/p/") ||
|
|
||||||
location.pathname.startsWith("/providers") ||
|
|
||||||
location.pathname === "/nsfw"
|
|
||||||
? "page only-content"
|
|
||||||
: location.pathname.startsWith("/chat/")
|
|
||||||
? "page chat"
|
|
||||||
: "page"
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>Home - zap.stream</title>
|
<title>Home - zap.stream</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
@ -105,47 +92,15 @@ export function LayoutPage() {
|
|||||||
<input className="search-input" type="text" placeholder="Search" />
|
<input className="search-input" type="text" placeholder="Search" />
|
||||||
<Icon name="search" size={15} />
|
<Icon name="search" size={15} />
|
||||||
</div>
|
</div>
|
||||||
<Link to={"/nsfw"}>
|
<div className="f-grow">
|
||||||
<div className={`btn-header${isNsfw ? " active" : ""}`}>
|
{/* Future menu items go here */}
|
||||||
Adult (18+)
|
</div>
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<div className="header-right">
|
<div className="header-right">
|
||||||
{loggedIn()}
|
{loggedIn()}
|
||||||
{loggedOut()}
|
{loggedOut()}
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
{isNsfw && <ContentWarningOverlay />}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ContentWarningOverlay() {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [is18Plus, setIs18Plus] = useState(
|
|
||||||
Boolean(window.localStorage.getItem("accepted-content-warning"))
|
|
||||||
);
|
|
||||||
if (is18Plus) return null;
|
|
||||||
|
|
||||||
function grownUp() {
|
|
||||||
window.localStorage.setItem("accepted-content-warning", "true");
|
|
||||||
setIs18Plus(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="fullscreen-exclusive age-check">
|
|
||||||
<h1>Sexually explicit material ahead!</h1>
|
|
||||||
<h2>Confirm your age</h2>
|
|
||||||
<div className="flex g24">
|
|
||||||
<button className="btn btn-warning" onClick={grownUp}>
|
|
||||||
Yes, I am over 18
|
|
||||||
</button>
|
|
||||||
<button className="btn" onClick={() => navigate("/")}>
|
|
||||||
No, I am under 18
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,7 +1,7 @@
|
|||||||
.video-grid {
|
.video-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(4, 1fr);
|
||||||
gap: 32px;
|
gap: var(--gap-l);
|
||||||
padding: 40px 0;
|
padding: 40px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -18,7 +18,6 @@
|
|||||||
.video-grid {
|
.video-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
gap: 32px;
|
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,11 +34,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.homepage {
|
|
||||||
width: 100%;
|
|
||||||
grid-area: main-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
.divider {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import { VideoTile } from "../element/video-tile";
|
|||||||
import { findTag } from "../utils";
|
import { findTag } from "../utils";
|
||||||
import { LIVE_STREAM } from "../const";
|
import { LIVE_STREAM } from "../const";
|
||||||
|
|
||||||
export function RootPage({ nsfw }: { nsfw?: boolean }) {
|
export function RootPage() {
|
||||||
const rb = useMemo(() => {
|
const rb = useMemo(() => {
|
||||||
const rb = new RequestBuilder("root");
|
const rb = new RequestBuilder("root");
|
||||||
rb.withOptions({
|
rb.withOptions({
|
||||||
@ -31,7 +31,7 @@ export function RootPage({ nsfw }: { nsfw?: boolean }) {
|
|||||||
);
|
);
|
||||||
const feedSorted = useMemo(() => {
|
const feedSorted = useMemo(() => {
|
||||||
if (feed.data) {
|
if (feed.data) {
|
||||||
return [...feed.data].filter(a => nsfw ? findTag(a, "content-warning") !== undefined : findTag(a, "content-warning") === undefined).sort((a, b) => {
|
return [...feed.data].sort((a, b) => {
|
||||||
const aStatus = findTag(a, "status")!;
|
const aStatus = findTag(a, "status")!;
|
||||||
const bStatus = findTag(b, "status")!;
|
const bStatus = findTag(b, "status")!;
|
||||||
if (aStatus === bStatus) {
|
if (aStatus === bStatus) {
|
||||||
@ -44,7 +44,7 @@ export function RootPage({ nsfw }: { nsfw?: boolean }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
}, [feed.data, nsfw]);
|
}, [feed.data]);
|
||||||
|
|
||||||
const live = feedSorted.filter(
|
const live = feedSorted.filter(
|
||||||
(a) => findTag(a, "status") === StreamState.Live
|
(a) => findTag(a, "status") === StreamState.Live
|
||||||
|
@ -1,43 +1,77 @@
|
|||||||
.video-content {
|
.stream-page {
|
||||||
grid-area: main-content;
|
display: grid;
|
||||||
|
grid-template-columns: auto 450px;
|
||||||
|
gap: var(--gap-m);
|
||||||
|
height: calc(100vh - var(--header-page-padding) - var(--header-height) - var(--gap-s));
|
||||||
}
|
}
|
||||||
|
|
||||||
.video-content video {
|
.stream-page .video-content {
|
||||||
max-height: 230px;
|
overflow-y: auto;
|
||||||
width: 100vw;
|
height: fit-content;
|
||||||
max-width: 100vw;
|
gap: var(--gap-s);
|
||||||
background: #000;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.live-chat {
|
.stream-page .video-content video {
|
||||||
max-width: calc(100vw - 32px);
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-page .live-chat {
|
||||||
|
padding: 24px 16px 8px 24px;
|
||||||
|
border: 1px solid #171717;
|
||||||
|
border-radius: 24px;
|
||||||
|
height: calc(100vh - var(--header-page-padding) - var(--header-height) - var(--gap-s) - 24px - 8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1020px) {
|
||||||
|
.stream-page {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: calc(100vh - var(--header-page-padding) - var(--header-height) - var(--gap-s));
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-page .video-content {
|
||||||
|
overflow-y: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-page .live-chat {
|
||||||
|
border-radius: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
height: unset;
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-page .live-chat .top-zappers h3,
|
||||||
|
.stream-page .live-chat .header {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-page .info {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-page .stream-info {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stream-page .profile-info {
|
||||||
|
width: calc(100% - 32px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-info {
|
.profile-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
gap: 24px;
|
gap: var(--gap-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-info .btn {
|
.profile-info .btn {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
|
||||||
.video-content {
|
|
||||||
height: calc(100vh - 64px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-content video {
|
|
||||||
max-height: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.profile-info {
|
|
||||||
max-height: 42px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.pill {
|
.pill {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
@ -55,53 +89,6 @@
|
|||||||
background: rgba(23, 23, 23, 0.7);
|
background: rgba(23, 23, 23, 0.7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 1020px) {
|
|
||||||
.info {
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-content video {
|
|
||||||
width: unset;
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-content {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.live-chat {
|
|
||||||
margin-left: 32px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.info {
|
|
||||||
grid-area: profile;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.info h1 {
|
.info h1 {
|
||||||
margin: 0 0 8px 0;
|
margin: 0 0 8px 0;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
@ -45,7 +45,7 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
|
|||||||
const viewers = Number(findTag(ev, "current_participants") ?? "0");
|
const viewers = Number(findTag(ev, "current_participants") ?? "0");
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex info">
|
<div className="flex f-center info">
|
||||||
<div className="f-grow stream-info">
|
<div className="f-grow stream-info">
|
||||||
<h1>{findTag(ev, "title")}</h1>
|
<h1>{findTag(ev, "title")}</h1>
|
||||||
<p>{findTag(ev, "summary")}</p>
|
<p>{findTag(ev, "summary")}</p>
|
||||||
@ -76,7 +76,7 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="profile-info w-max">
|
<div className="profile-info">
|
||||||
<Profile pubkey={host ?? ""} />
|
<Profile pubkey={host ?? ""} />
|
||||||
<div className="flex g12">
|
<div className="flex g12">
|
||||||
{ev && (
|
{ev && (
|
||||||
@ -100,18 +100,6 @@ function ProfileInfo({ ev, goal }: { ev?: NostrEvent; goal?: TaggedRawEvent }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function VideoPlayer({ ev }: { ev?: NostrEvent }) {
|
|
||||||
const stream = findTag(ev, "streaming");
|
|
||||||
const image = findTag(ev, "image");
|
|
||||||
const status = findTag(ev, "status");
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="video-content">
|
|
||||||
<LiveVideoPlayer stream={stream} poster={image} status={status} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function StreamPage() {
|
export function StreamPage() {
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
const link = parseNostrLink(params.id!);
|
const link = parseNostrLink(params.id!);
|
||||||
@ -122,6 +110,7 @@ export function StreamPage() {
|
|||||||
const title = findTag(ev, "title");
|
const title = findTag(ev, "title");
|
||||||
const summary = findTag(ev, "summary");
|
const summary = findTag(ev, "summary");
|
||||||
const image = findTag(ev, "image");
|
const image = findTag(ev, "image");
|
||||||
|
const stream = findTag(ev, "streaming");
|
||||||
const tags = ev?.tags.filter((a) => a[0] === "t").map((a) => a[1]) ?? [];
|
const tags = ev?.tags.filter((a) => a[0] === "t").map((a) => a[1]) ?? [];
|
||||||
|
|
||||||
const descriptionContent = [
|
const descriptionContent = [
|
||||||
@ -130,7 +119,7 @@ export function StreamPage() {
|
|||||||
...tags,
|
...tags,
|
||||||
].join(", ");
|
].join(", ");
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="stream-page">
|
||||||
<Helmet>
|
<Helmet>
|
||||||
<title>{`${title} - zap.stream`}</title>
|
<title>{`${title} - zap.stream`}</title>
|
||||||
<meta name="description" content={descriptionContent} />
|
<meta name="description" content={descriptionContent} />
|
||||||
@ -143,9 +132,11 @@ export function StreamPage() {
|
|||||||
<meta property="og:description" content={descriptionContent} />
|
<meta property="og:description" content={descriptionContent} />
|
||||||
<meta property="og:image" content={image ?? ""} />
|
<meta property="og:image" content={image ?? ""} />
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<VideoPlayer ev={ev} />
|
<div className="video-content">
|
||||||
<ProfileInfo ev={ev} goal={goal} />
|
<LiveVideoPlayer stream={stream} poster={image} status={status} />
|
||||||
|
<ProfileInfo ev={ev} goal={goal} />
|
||||||
|
</div>
|
||||||
<LiveChat link={link} ev={ev} goal={goal} />
|
<LiveChat link={link} ev={ev} goal={goal} />
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user