diff --git a/web/README.md b/web/README.md index 324eeaf1b..debfe7fa4 100644 --- a/web/README.md +++ b/web/README.md @@ -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. diff --git a/web/components/CustomPageContent.tsx b/web/components/CustomPageContent.tsx index 5a0bfbfcb..dae759bb2 100644 --- a/web/components/CustomPageContent.tsx +++ b/web/components/CustomPageContent.tsx @@ -4,5 +4,5 @@ interface Props { export default function CustomPageContent(props: Props) { const { content } = props; - return
{content}
; + return
; } diff --git a/web/components/Follower.tsx b/web/components/Follower.tsx new file mode 100644 index 000000000..e7d395dd3 --- /dev/null +++ b/web/components/Follower.tsx @@ -0,0 +1,9 @@ +import { Follower } from '../interfaces/follower'; + +interface Props { + follower: Follower; +} + +export default function FollowerCollection(props: Props) { + return
This is a single follower
; +} diff --git a/web/components/FollowersCollection.tsx b/web/components/FollowersCollection.tsx new file mode 100644 index 000000000..aa2eb333e --- /dev/null +++ b/web/components/FollowersCollection.tsx @@ -0,0 +1,9 @@ +import { Follower } from '../interfaces/follower'; + +interface Props { + followers: Follower[]; +} + +export default function FollowerCollection(props: Props) { + return
List of followers go here
; +} diff --git a/web/components/chat/ChatContainer.tsx b/web/components/chat/ChatContainer.tsx index d793bee62..e6a484d70 100644 --- a/web/components/chat/ChatContainer.tsx +++ b/web/components/chat/ChatContainer.tsx @@ -5,5 +5,5 @@ interface Props { } export default function ChatContainer(props: Props) { - return
Component goes here
; + return
Chat container goes here
; } diff --git a/web/components/stores/ClientConfigStore.tsx b/web/components/stores/ClientConfigStore.tsx index d4b026edd..18fff4fb2 100644 --- a/web/components/stores/ClientConfigStore.tsx +++ b/web/components/stores/ClientConfigStore.tsx @@ -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(clientConfigState); diff --git a/web/components/ui/Content/Content.tsx b/web/components/ui/Content/Content.tsx index dacc70208..2e5467fd9 100644 --- a/web/components/ui/Content/Content.tsx +++ b/web/components/ui/Content/Content.tsx @@ -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() {
- Video player goes here + + + - + + + + + + + +
diff --git a/web/components/ui/Footer/Footer.tsx b/web/components/ui/Footer/Footer.tsx index 80eabfb01..39f464e15 100644 --- a/web/components/ui/Footer/Footer.tsx +++ b/web/components/ui/Footer/Footer.tsx @@ -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(clientConfigState); - const { version } = clientConfig; +export default function FooterComponent(props) { + const { version } = props; return
Footer: Owncast {version}
; } diff --git a/web/components/ui/Header/Header.tsx b/web/components/ui/Header/Header.tsx index 854926032..51b70c77d 100644 --- a/web/components/ui/Header/Header.tsx +++ b/web/components/ui/Header/Header.tsx @@ -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(clientConfigState); - const [chatOpen, setChatOpen] = useRecoilState(chatCurrentlyVisible); - - const { name } = clientConfig; - - useEffect(() => { - console.log({ chatOpen }); - }, [chatOpen]); +export default function HeaderComponent(props) { + const { name } = props; return (
+ Logo goes here {name} - +
); } diff --git a/web/components/ui/Sidebar/Sidebar.tsx b/web/components/ui/Sidebar/Sidebar.tsx index c4e4dac81..2071b6913 100644 --- a/web/components/ui/Sidebar/Sidebar.tsx +++ b/web/components/ui/Sidebar/Sidebar.tsx @@ -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(chatMessages); + return ( + > + + ); } diff --git a/web/interfaces/external-action.tsx b/web/interfaces/external-action.ts similarity index 100% rename from web/interfaces/external-action.tsx rename to web/interfaces/external-action.ts diff --git a/web/interfaces/follower.ts b/web/interfaces/follower.ts new file mode 100644 index 000000000..36be283fd --- /dev/null +++ b/web/interfaces/follower.ts @@ -0,0 +1,7 @@ +export interface Follower { + name: string; + description?: string; + username?: string; + image?: string; + link: string; +} diff --git a/web/stories/Follower.stories.tsx b/web/stories/Follower.stories.tsx new file mode 100644 index 000000000..2f2847dbd --- /dev/null +++ b/web/stories/Follower.stories.tsx @@ -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; + +const Template: ComponentStory = 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', + }, +}; diff --git a/web/stories/Followercollection.stories.tsx b/web/stories/Followercollection.stories.tsx new file mode 100644 index 000000000..f1ea227cd --- /dev/null +++ b/web/stories/Followercollection.stories.tsx @@ -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; + +const Template: ComponentStory = 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', + }, + ], +}; diff --git a/web/stories/Footer.stories.tsx b/web/stories/Footer.stories.tsx new file mode 100644 index 000000000..fa3ad61d1 --- /dev/null +++ b/web/stories/Footer.stories.tsx @@ -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; + +// eslint-disable-next-line @typescript-eslint/no-unused-Footer +const Template: ComponentStory = args =>