mirror of
https://github.com/owncast/owncast.git
synced 2024-10-10 19:16:02 +00:00
Update to the page content header
This commit is contained in:
parent
fa1c680f6e
commit
6ffe720d90
@ -0,0 +1,57 @@
|
||||
.root {
|
||||
position: relative;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.buttonsLogoTitleSection {
|
||||
// margin-left: 1.5vw;
|
||||
// margin-right: 1.5vw;
|
||||
}
|
||||
|
||||
.row {
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
|
||||
.logoTitleSection {
|
||||
display: flex;
|
||||
@media (max-width: 768px) {
|
||||
flex-direction: column;
|
||||
.logo {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.titleSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
text-transform: uppercase;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 24px;
|
||||
font-weight: 400;
|
||||
line-height: 22px;
|
||||
color: var(--theme-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.tagList {
|
||||
font-family: var(--theme-text-display-font-family);
|
||||
color: var(--theme-text-primary);
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 300;
|
||||
}
|
||||
}
|
36
web/components/common/ContentHeader/ContentHeader.tsx
Normal file
36
web/components/common/ContentHeader/ContentHeader.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import cn from 'classnames';
|
||||
|
||||
import { ServerLogo } from '../../ui';
|
||||
import SocialLinks from '../../ui/SocialLinks/SocialLinks';
|
||||
import { SocialLink } from '../../../interfaces/social-link.model';
|
||||
import s from './ContentHeader.module.scss';
|
||||
|
||||
interface Props {
|
||||
name: string;
|
||||
title: string;
|
||||
summary: string;
|
||||
tags: string[];
|
||||
links: SocialLink[];
|
||||
logo: string;
|
||||
}
|
||||
export default function ContentHeader({ name, title, summary, logo, tags, links }: Props) {
|
||||
return (
|
||||
<div className={s.root}>
|
||||
<div className={s.logoTitleSection}>
|
||||
<div className={s.logo}>
|
||||
<ServerLogo src={logo} />
|
||||
</div>
|
||||
<div className={s.titleSection}>
|
||||
<div className={cn(s.title, s.row)}>{name}</div>
|
||||
<div className={cn(s.subtitle, s.row)}>{title || summary}</div>
|
||||
<div className={cn(s.tagList, s.row)}>
|
||||
{tags.length > 0 && tags.map(tag => <span key={tag}>#{tag} </span>)}
|
||||
</div>
|
||||
<div className={cn(s.socialLinks, s.row)}>
|
||||
<SocialLinks links={links} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
1
web/components/common/ContentHeader/index.ts
Normal file
1
web/components/common/ContentHeader/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default } from './ContentHeader';
|
@ -1,89 +0,0 @@
|
||||
.root {
|
||||
position: relative;
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.buttonsLogoTitleSection {
|
||||
margin-left: 1.5vw;
|
||||
margin-right: 1.5vw;
|
||||
}
|
||||
|
||||
.logoTitleSection {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.titleSection {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.title {
|
||||
font-size: 1.5rem;
|
||||
font-weight: bold;
|
||||
color: var(--theme-text-primary);
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 0.35em;
|
||||
font-size: 1.5em;
|
||||
font-weight: 300;
|
||||
color: var(--theme-text-secondary);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile {
|
||||
&.root {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 0 0.3rem;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.mobileInfo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.title {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
.mobileStatus {
|
||||
display: flex;
|
||||
font-weight: 600;
|
||||
.viewerCount {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
.liveStatus {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 0.5rem;
|
||||
font-size: 0.8rem;
|
||||
gap: 4px;
|
||||
.liveCircle {
|
||||
border-radius: 50%;
|
||||
background-color: red;
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tagList {
|
||||
font-family: var(--theme-text-display-font-family);
|
||||
color: var(--theme-text-secondary);
|
||||
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 0.7em 0 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import cn from 'classnames';
|
||||
import { EyeFilled } from '@ant-design/icons';
|
||||
import { useEffect } from 'react';
|
||||
import { ClientConfig } from '../../../interfaces/client-config.model';
|
||||
import {
|
||||
clientConfigStateAtom,
|
||||
isOnlineSelector,
|
||||
serverStatusState,
|
||||
} from '../../stores/ClientConfigStore';
|
||||
import { ServerLogo } from '../../ui';
|
||||
import SocialLinks from '../../ui/SocialLinks/SocialLinks';
|
||||
import s from './StreamInfo.module.scss';
|
||||
import { ServerStatus } from '../../../interfaces/server-status.model';
|
||||
|
||||
interface Props {
|
||||
isMobile: boolean;
|
||||
}
|
||||
export default function StreamInfo({ isMobile }: Props) {
|
||||
const { socialHandles, name, title, tags, summary } =
|
||||
useRecoilValue<ClientConfig>(clientConfigStateAtom);
|
||||
const { viewerCount } = useRecoilValue<ServerStatus>(serverStatusState);
|
||||
const online = useRecoilValue<boolean>(isOnlineSelector);
|
||||
|
||||
useEffect(() => {
|
||||
console.log({ online });
|
||||
}, [online]);
|
||||
|
||||
return isMobile ? (
|
||||
<div className={cn(s.root, s.mobile)}>
|
||||
<div className={s.mobileInfo}>
|
||||
<ServerLogo src="/logo" />
|
||||
<div className={s.title}>{name}</div>
|
||||
</div>
|
||||
<div className={s.mobileStatus}>
|
||||
<div className={s.viewerCount}>
|
||||
{online && (
|
||||
<>
|
||||
<span>{viewerCount}</span>
|
||||
<EyeFilled />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<div className={s.liveStatus}>
|
||||
{online && <div className={s.liveCircle} />}
|
||||
<span>{online ? 'LIVE' : 'OFFLINE'}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className={s.root}>
|
||||
<div className={s.logoTitleSection}>
|
||||
<ServerLogo src="/logo" />
|
||||
<div className={s.titleSection}>
|
||||
<div className={s.title}>{name}</div>
|
||||
<div className={s.subtitle}>{title || summary}</div>
|
||||
<div className={s.tagList}>
|
||||
{tags.length > 0 && tags.map(tag => <span key={tag}>#{tag} </span>)}
|
||||
</div>
|
||||
<SocialLinks links={socialHandles} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1 +0,0 @@
|
||||
export { default } from './StreamInfo';
|
@ -35,7 +35,7 @@ import FollowButton from '../../action-buttons/FollowButton';
|
||||
import NotifyButton from '../../action-buttons/NotifyButton';
|
||||
import Modal from '../Modal/Modal';
|
||||
import BrowserNotifyModal from '../../modals/BrowserNotify/BrowserNotifyModal';
|
||||
import StreamInfo from '../../common/StreamInfo';
|
||||
import ContentHeader from '../../common/ContentHeader';
|
||||
import { ServerStatus } from '../../../interfaces/server-status.model';
|
||||
import { StatusBar } from '..';
|
||||
|
||||
@ -51,9 +51,18 @@ export default function ContentComponent() {
|
||||
const online = useRecoilValue<boolean>(isOnlineSelector);
|
||||
const chatDisplayName = useRecoilValue<string>(chatDisplayNameAtom);
|
||||
const chatUserId = useRecoilValue<string>(chatUserIdAtom);
|
||||
const { viewerCount, lastConnectTime, lastDisconnectTime } =
|
||||
const { viewerCount, lastConnectTime, lastDisconnectTime, streamTitle } =
|
||||
useRecoilValue<ServerStatus>(serverStatusState);
|
||||
const { extraPageContent, version, name, externalActions, offlineMessage } = clientConfig;
|
||||
const {
|
||||
extraPageContent,
|
||||
version,
|
||||
name,
|
||||
summary,
|
||||
socialHandles,
|
||||
tags,
|
||||
externalActions,
|
||||
offlineMessage,
|
||||
} = clientConfig;
|
||||
const [showNotifyReminder, setShowNotifyReminder] = useState(false);
|
||||
const [showNotifyPopup, setShowNotifyPopup] = useState(false);
|
||||
|
||||
@ -143,7 +152,14 @@ export default function ContentComponent() {
|
||||
<BrowserNotifyModal />
|
||||
</Modal>
|
||||
</div>
|
||||
<StreamInfo isMobile={isMobile} />
|
||||
<ContentHeader
|
||||
name={name}
|
||||
title={streamTitle}
|
||||
summary={summary}
|
||||
tags={tags}
|
||||
links={socialHandles}
|
||||
logo="/logo"
|
||||
/>
|
||||
</div>
|
||||
<div className={s.lowerHalf}>
|
||||
<Tabs defaultActiveKey="0" style={{ height: '100%' }}>
|
||||
|
@ -4,17 +4,17 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
margin-right: .5rem;
|
||||
width: clamp(2.5vh, 9vw, 120px);
|
||||
height: clamp(2.5vh, 9vw, 120px);
|
||||
margin-right: 0.5rem;
|
||||
width: 96px;
|
||||
height: 96px;
|
||||
border-radius: 50%;
|
||||
border-width: 3px;
|
||||
border-width: 5px;
|
||||
border-style: solid;
|
||||
border-color: var(--theme-primary-color);
|
||||
background-color: var(--theme-background-secondary);
|
||||
}
|
||||
|
||||
.container {
|
||||
.container {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
border-radius: 50%;
|
||||
@ -28,4 +28,3 @@
|
||||
background-position: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
.link {
|
||||
width: 2em;
|
||||
margin-right: 4px;
|
||||
width: 1.8em;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.links {
|
||||
|
76
web/stories/ContentHeader.stories.tsx
Normal file
76
web/stories/ContentHeader.stories.tsx
Normal file
@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import ContentHeader from '../components/common/ContentHeader/ContentHeader';
|
||||
|
||||
export default {
|
||||
title: 'owncast/Components/Content Header',
|
||||
component: ContentHeader,
|
||||
parameters: {},
|
||||
} as ComponentMeta<typeof ContentHeader>;
|
||||
|
||||
const Template: ComponentStory<typeof ContentHeader> = args => <ContentHeader {...args} />;
|
||||
|
||||
export const Example = Template.bind({});
|
||||
Example.args = {
|
||||
name: 'My Awesome Owncast Stream',
|
||||
summary: 'A calvacade of glorious sights and sounds',
|
||||
tags: ['word', 'tag with spaces', 'music'],
|
||||
logo: 'https://watch.owncast.online/logo',
|
||||
links: [
|
||||
{
|
||||
platform: 'github',
|
||||
url: 'https://github.com/owncast/owncast',
|
||||
icon: 'https://watch.owncast.online/img/platformlogos/github.svg',
|
||||
},
|
||||
{
|
||||
platform: 'Documentation',
|
||||
url: 'https://owncast.online',
|
||||
icon: 'https://watch.owncast.online/img/platformlogos/link.svg',
|
||||
},
|
||||
{
|
||||
platform: 'mastodon',
|
||||
url: 'https://fosstodon.org/users/owncast',
|
||||
icon: 'https://watch.owncast.online/img/platformlogos/mastodon.svg',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const LongContent = Template.bind({});
|
||||
LongContent.args = {
|
||||
name: 'My Awesome Owncast Stream, streaming the best of streams and some lorem ipsum too',
|
||||
summary:
|
||||
'A calvacade of glorious sights and sounds. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.',
|
||||
tags: [
|
||||
'word',
|
||||
'tag with spaces',
|
||||
'music',
|
||||
'more tags',
|
||||
'a bunch',
|
||||
'keep going',
|
||||
'and more',
|
||||
'just a few more',
|
||||
'video games',
|
||||
'things',
|
||||
'stuff',
|
||||
'ok some more',
|
||||
'this should do it',
|
||||
],
|
||||
logo: 'https://watch.owncast.online/logo',
|
||||
links: [
|
||||
{
|
||||
platform: 'github',
|
||||
url: 'https://github.com/owncast/owncast',
|
||||
icon: 'https://watch.owncast.online/img/platformlogos/github.svg',
|
||||
},
|
||||
{
|
||||
platform: 'Documentation',
|
||||
url: 'https://owncast.online',
|
||||
icon: 'https://watch.owncast.online/img/platformlogos/link.svg',
|
||||
},
|
||||
{
|
||||
platform: 'mastodon',
|
||||
url: 'https://fosstodon.org/users/owncast',
|
||||
icon: 'https://watch.owncast.online/img/platformlogos/mastodon.svg',
|
||||
},
|
||||
],
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user