mirror of
https://github.com/owncast/owncast.git
synced 2024-11-21 20:28:15 +03:00
More blank components and stories to be filled in
This commit is contained in:
parent
e0c073171d
commit
e5d3b0e4ee
16 changed files with 194 additions and 36 deletions
|
@ -18,6 +18,15 @@ Make sure you're running an instance of Owncast on localhost:8080, as your copy
|
|||
|
||||
```npm run dev```
|
||||
|
||||
### Components and Styles
|
||||
|
||||
You can start the [Storybook](https://storybook.js.org/) UI for exploring, testing, and developing components by running:
|
||||
|
||||
```npm run storybook```
|
||||
|
||||
This allows for components to be made available without the need of the server to be running and changes to be made in
|
||||
isolation.
|
||||
|
||||
### Update the project
|
||||
|
||||
You can add or edit a pages by modifying `pages/something.js`. The page auto-updates as you edit the file.
|
||||
|
|
|
@ -4,5 +4,5 @@ interface Props {
|
|||
|
||||
export default function CustomPageContent(props: Props) {
|
||||
const { content } = props;
|
||||
return <div>{content}</div>;
|
||||
return <div dangerouslySetInnerHTML={{ __html: content }} />;
|
||||
}
|
||||
|
|
9
web/components/Follower.tsx
Normal file
9
web/components/Follower.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { Follower } from '../interfaces/follower';
|
||||
|
||||
interface Props {
|
||||
follower: Follower;
|
||||
}
|
||||
|
||||
export default function FollowerCollection(props: Props) {
|
||||
return <div>This is a single follower</div>;
|
||||
}
|
9
web/components/FollowersCollection.tsx
Normal file
9
web/components/FollowersCollection.tsx
Normal file
|
@ -0,0 +1,9 @@
|
|||
import { Follower } from '../interfaces/follower';
|
||||
|
||||
interface Props {
|
||||
followers: Follower[];
|
||||
}
|
||||
|
||||
export default function FollowerCollection(props: Props) {
|
||||
return <div>List of followers go here</div>;
|
||||
}
|
|
@ -5,5 +5,5 @@ interface Props {
|
|||
}
|
||||
|
||||
export default function ChatContainer(props: Props) {
|
||||
return <div>Component goes here</div>;
|
||||
return <div>Chat container goes here</div>;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import { ReactElement } from 'react-markdown/lib/react-markdown';
|
|||
import { atom, useRecoilState } from 'recoil';
|
||||
import { makeEmptyClientConfig, ClientConfig } from '../../interfaces/client-config.model';
|
||||
import ClientConfigService from '../../services/client-config-service';
|
||||
import { ChatMessage } from '../../interfaces/chat-message.model';
|
||||
|
||||
// The config that comes from the API.
|
||||
export const clientConfigState = atom({
|
||||
|
@ -15,11 +16,16 @@ export const chatCurrentlyVisible = atom({
|
|||
default: false,
|
||||
});
|
||||
|
||||
export const chatDislayName = atom({
|
||||
export const chatDisplayName = atom({
|
||||
key: 'chatDisplayName',
|
||||
default: '',
|
||||
});
|
||||
|
||||
export const chatMessages = atom({
|
||||
key: 'chatMessages',
|
||||
default: [] as ChatMessage[],
|
||||
});
|
||||
|
||||
export function ClientConfigStore(): ReactElement {
|
||||
const [, setClientConfig] = useRecoilState<ClientConfig>(clientConfigState);
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import { Layout, Row, Col, Tabs } from 'antd';
|
||||
import { clientConfigState } from '../../stores/ClientConfigStore';
|
||||
import { ClientConfig } from '../../../interfaces/client-config.model';
|
||||
import { Layout, Row, Col } from 'antd';
|
||||
import CustomPageContent from '../../CustomPageContent';
|
||||
import OwncastPlayer from '../../video/OwncastPlayer';
|
||||
import FollowerCollection from '../../FollowersCollection';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
|
||||
const { Content } = Layout;
|
||||
|
||||
|
@ -13,11 +18,20 @@ export default function FooterComponent() {
|
|||
<Content style={{ margin: '80px 16px 0', overflow: 'initial' }}>
|
||||
<div>
|
||||
<Row>
|
||||
<Col span={24}>Video player goes here</Col>
|
||||
<Col span={24}>
|
||||
<OwncastPlayer source="https://watch.owncast.online" />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Content dangerouslySetInnerHTML={{ __html: extraPageContent }} />
|
||||
<Tabs defaultActiveKey="1" type="card">
|
||||
<TabPane tab="About" key="1">
|
||||
<CustomPageContent content={extraPageContent} />
|
||||
</TabPane>
|
||||
<TabPane tab="Followers" key="2">
|
||||
<FollowerCollection />
|
||||
</TabPane>
|
||||
</Tabs>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
import { useRecoilValue } from 'recoil';
|
||||
import { clientConfigState } from '../../stores/ClientConfigStore';
|
||||
import { ClientConfig } from '../../../interfaces/client-config.model';
|
||||
import { Layout } from 'antd';
|
||||
|
||||
const { Footer } = Layout;
|
||||
|
||||
export default function FooterComponent() {
|
||||
const clientConfig = useRecoilValue<ClientConfig>(clientConfigState);
|
||||
const { version } = clientConfig;
|
||||
export default function FooterComponent(props) {
|
||||
const { version } = props;
|
||||
|
||||
return <Footer style={{ textAlign: 'center' }}>Footer: Owncast {version}</Footer>;
|
||||
}
|
||||
|
|
|
@ -1,31 +1,17 @@
|
|||
import s from './Header.module.scss';
|
||||
import { Layout } from 'antd';
|
||||
import { ServerStatusStore, serverStatusState } from '../../stores/ServerStatusStore';
|
||||
import {
|
||||
ClientConfigStore,
|
||||
clientConfigState,
|
||||
chatCurrentlyVisible,
|
||||
} from '../../stores/ClientConfigStore';
|
||||
import { ClientConfig } from '../../../interfaces/client-config.model';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import { useEffect } from 'react';
|
||||
import UserDropdown from '../../UserDropdownMenu';
|
||||
import s from './Header.module.scss';
|
||||
|
||||
const { Header } = Layout;
|
||||
|
||||
export default function HeaderComponent() {
|
||||
const clientConfig = useRecoilValue<ClientConfig>(clientConfigState);
|
||||
const [chatOpen, setChatOpen] = useRecoilState(chatCurrentlyVisible);
|
||||
|
||||
const { name } = clientConfig;
|
||||
|
||||
useEffect(() => {
|
||||
console.log({ chatOpen });
|
||||
}, [chatOpen]);
|
||||
export default function HeaderComponent(props) {
|
||||
const { name } = props;
|
||||
|
||||
return (
|
||||
<Header className={`${s.header}`}>
|
||||
Logo goes here
|
||||
{name}
|
||||
<button onClick={() => setChatOpen(!chatOpen)}>Toggle Chat</button>
|
||||
<UserDropdown />
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
import Sider from 'antd/lib/layout/Sider';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { chatCurrentlyVisible } from '../../stores/ClientConfigStore';
|
||||
import { ChatMessage } from '../../../interfaces/chat-message.model';
|
||||
import ChatContainer from '../../chat/ChatContainer';
|
||||
import { chatMessages } from '../../stores/ClientConfigStore';
|
||||
|
||||
export default function Sidebar() {
|
||||
let chatOpen = useRecoilValue(chatCurrentlyVisible);
|
||||
const messages = useRecoilValue<ChatMessage[]>(chatMessages);
|
||||
|
||||
return (
|
||||
<Sider
|
||||
collapsed={!chatOpen}
|
||||
collapsed={false}
|
||||
width={300}
|
||||
style={{
|
||||
position: 'fixed',
|
||||
|
@ -14,6 +17,8 @@ export default function Sidebar() {
|
|||
top: 0,
|
||||
bottom: 0,
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<ChatContainer messages={messages} />
|
||||
</Sider>
|
||||
);
|
||||
}
|
||||
|
|
7
web/interfaces/follower.ts
Normal file
7
web/interfaces/follower.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
export interface Follower {
|
||||
name: string;
|
||||
description?: string;
|
||||
username?: string;
|
||||
image?: string;
|
||||
link: string;
|
||||
}
|
22
web/stories/Follower.stories.tsx
Normal file
22
web/stories/Follower.stories.tsx
Normal file
|
@ -0,0 +1,22 @@
|
|||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import * as FollowerComponent from '../components/Follower';
|
||||
|
||||
export default {
|
||||
title: 'owncast/Follower',
|
||||
component: FollowerComponent,
|
||||
parameters: {},
|
||||
} as ComponentMeta<typeof FollowerComponent>;
|
||||
|
||||
const Template: ComponentStory<typeof FollowerComponent> = args => <FollowerComponent {...args} />;
|
||||
|
||||
export const Example = Template.bind({});
|
||||
Example.args = {
|
||||
follower: {
|
||||
name: 'John Doe',
|
||||
description: 'User',
|
||||
username: '@account@domain.tld',
|
||||
image: 'https://avatars0.githubusercontent.com/u/1234?s=460&v=4',
|
||||
link: 'https://yahoo.com',
|
||||
},
|
||||
};
|
61
web/stories/Followercollection.stories.tsx
Normal file
61
web/stories/Followercollection.stories.tsx
Normal file
|
@ -0,0 +1,61 @@
|
|||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import FollowerCollection from '../components/FollowersCollection';
|
||||
|
||||
export default {
|
||||
title: 'owncast/Follower collection',
|
||||
component: FollowerCollection,
|
||||
parameters: {},
|
||||
} as ComponentMeta<typeof FollowerCollection>;
|
||||
|
||||
const Template: ComponentStory<typeof FollowerCollection> = args => (
|
||||
<FollowerCollection {...args} />
|
||||
);
|
||||
|
||||
export const Example = Template.bind({});
|
||||
Example.args = {
|
||||
followers: [
|
||||
{
|
||||
name: 'John Doe',
|
||||
description: 'User',
|
||||
username: '@account@domain.tld',
|
||||
image: 'https://avatars0.githubusercontent.com/u/1234?s=460&v=4',
|
||||
link: 'https://yahoo.com',
|
||||
},
|
||||
{
|
||||
name: 'John Doe',
|
||||
description: 'User',
|
||||
username: '@account@domain.tld',
|
||||
image: 'https://avatars0.githubusercontent.com/u/1234?s=460&v=4',
|
||||
link: 'https://yahoo.com',
|
||||
},
|
||||
{
|
||||
name: 'John Doe',
|
||||
description: 'User',
|
||||
username: '@account@domain.tld',
|
||||
image: 'https://avatars0.githubusercontent.com/u/1234?s=460&v=4',
|
||||
link: 'https://yahoo.com',
|
||||
},
|
||||
{
|
||||
name: 'John Doe',
|
||||
description: 'User',
|
||||
username: '@account@domain.tld',
|
||||
image: 'https://avatars0.githubusercontent.com/u/1234?s=460&v=4',
|
||||
link: 'https://yahoo.com',
|
||||
},
|
||||
{
|
||||
name: 'John Doe',
|
||||
description: 'User',
|
||||
username: '@account@domain.tld',
|
||||
image: 'https://avatars0.githubusercontent.com/u/1234?s=460&v=4',
|
||||
link: 'https://yahoo.com',
|
||||
},
|
||||
{
|
||||
name: 'John Doe',
|
||||
description: 'User',
|
||||
username: '@account@domain.tld',
|
||||
image: 'https://avatars0.githubusercontent.com/u/1234?s=460&v=4',
|
||||
link: 'https://yahoo.com',
|
||||
},
|
||||
],
|
||||
};
|
18
web/stories/Footer.stories.tsx
Normal file
18
web/stories/Footer.stories.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import Footer from '../components/ui/Footer/Footer';
|
||||
|
||||
export default {
|
||||
title: 'owncast/Footer',
|
||||
component: Footer,
|
||||
parameters: {},
|
||||
} as ComponentMeta<typeof Footer>;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-Footer
|
||||
const Template: ComponentStory<typeof Footer> = args => <Footer {...args} />;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
export const Example = Template.bind({});
|
||||
Example.args = {
|
||||
version: 'v1.2.3',
|
||||
};
|
16
web/stories/Header.stories.tsx
Normal file
16
web/stories/Header.stories.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import React from 'react';
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import Header from '../components/ui/Header/Header';
|
||||
|
||||
export default {
|
||||
title: 'owncast/Header',
|
||||
component: Header,
|
||||
parameters: {},
|
||||
} as ComponentMeta<typeof Header>;
|
||||
|
||||
const Template: ComponentStory<typeof Header> = args => <Header {...args} />;
|
||||
|
||||
export const Example = Template.bind({});
|
||||
Example.args = {
|
||||
name: 'Example Stream Name',
|
||||
};
|
Loading…
Reference in a new issue