Update to the page content header

This commit is contained in:
Gabe Kangas 2022-08-16 17:49:21 -07:00
parent fa1c680f6e
commit 6ffe720d90
No known key found for this signature in database
GPG Key ID: 9A56337728BC81EA
10 changed files with 197 additions and 167 deletions

View File

@ -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;
}
}

View 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}&nbsp;</span>)}
</div>
<div className={cn(s.socialLinks, s.row)}>
<SocialLinks links={links} />
</div>
</div>
</div>
</div>
);
}

View File

@ -0,0 +1 @@
export { default } from './ContentHeader';

View File

@ -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;
}
}

View File

@ -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}&nbsp;</span>)}
</div>
<SocialLinks links={socialHandles} />
</div>
</div>
</div>
);
}

View File

@ -1 +0,0 @@
export { default } from './StreamInfo';

View File

@ -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%' }}>

View File

@ -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;
}

View File

@ -1,6 +1,6 @@
.link {
width: 2em;
margin-right: 4px;
width: 1.8em;
margin-right: 16px;
}
.links {

View 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',
},
],
};