Browse Source

Fill out the follower component

pull/2032/head
Gabe Kangas 3 years ago
parent
commit
008f607cf7
No known key found for this signature in database
GPG Key ID: 9A56337728BC81EA
  1. 14
      web/components/Follower.tsx
  2. 17
      web/components/FollowersCollection.tsx
  3. 20
      web/components/chat/ChatContainer.tsx
  4. 6
      web/components/stores/ClientConfigStore.tsx
  5. 17
      web/components/ui/Content/Content.tsx
  6. 4
      web/interfaces/socket-events.ts
  7. 10
      web/services/websocket-service.ts
  8. 8
      web/stories/Follower.stories.tsx

14
web/components/Follower.tsx

@ -1,9 +1,19 @@
import { Avatar, Comment } from 'antd';
import React from 'react';
import { Follower } from '../interfaces/follower'; import { Follower } from '../interfaces/follower';
interface Props { interface Props {
follower: Follower; follower: Follower;
} }
export default function FollowerCollection(props: Props) { export default function SingleFollower(props: Props) {
return <div>This is a single follower</div>; const { follower } = props;
return (
<Comment
author={follower.username}
avatar={<Avatar src={follower.image} alt="Han Solo" />}
content={follower.name}
/>
);
} }

17
web/components/FollowersCollection.tsx

@ -1,9 +1,24 @@
import { Pagination } from 'antd';
import { Follower } from '../interfaces/follower'; import { Follower } from '../interfaces/follower';
import SingleFollower from './Follower';
interface Props { interface Props {
total: number;
followers: Follower[]; followers: Follower[];
} }
export default function FollowerCollection(props: Props) { export default function FollowerCollection(props: Props) {
return <div>List of followers go here</div>; const ITEMS_PER_PAGE = 24;
const { followers, total } = props;
const pages = Math.ceil(total / ITEMS_PER_PAGE);
return (
<div>
{followers.map(follower => (
<SingleFollower key={follower.link} follower={follower} />
))}
<Pagination current={1} pageSize={ITEMS_PER_PAGE} total={pages || 1} />
</div>
);
} }

20
web/components/chat/ChatContainer.tsx

@ -1,10 +1,11 @@
import { Spin } from 'antd'; import { Spin } from 'antd';
import { Virtuoso } from 'react-virtuoso'; import { Virtuoso } from 'react-virtuoso';
import { useState, useMemo, useCallback, useEffect, useRef } from 'react'; import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
import { LoadingOutlined } from '@ant-design/icons';
import { ChatMessage } from '../../interfaces/chat-message.model'; import { ChatMessage } from '../../interfaces/chat-message.model';
import { ChatState } from '../../interfaces/application-state'; import { ChatState } from '../../interfaces/application-state';
import ChatUserMessage from './ChatUserMessage'; import ChatUserMessage from './ChatUserMessage';
import { LoadingOutlined } from '@ant-design/icons'; import { MessageType } from '../../interfaces/socket-events';
interface Props { interface Props {
messages: ChatMessage[]; messages: ChatMessage[];
@ -16,8 +17,19 @@ export default function ChatContainer(props: Props) {
const loading = state === ChatState.Loading; const loading = state === ChatState.Loading;
const chatContainerRef = useRef(null); const chatContainerRef = useRef(null);
const spinIcon = <LoadingOutlined style={{fontSize: '32px'}} spin /> const spinIcon = <LoadingOutlined style={{ fontSize: '32px' }} spin />;
const getViewForMessage = message => {
switch (message.type) {
case MessageType.CHAT:
return <ChatUserMessage message={message} showModeratorMenu={false} />;
default:
return null;
}
return null;
};
console.log(messages);
return ( return (
<div> <div>
<h1>Chat</h1> <h1>Chat</h1>
@ -26,9 +38,7 @@ export default function ChatContainer(props: Props) {
ref={chatContainerRef} ref={chatContainerRef}
initialTopMostItemIndex={999} initialTopMostItemIndex={999}
data={messages} data={messages}
itemContent={(index, message) => ( itemContent={(index, message) => getViewForMessage(message)}
<ChatUserMessage message={message} showModeratorMenu={false} />
)}
followOutput="smooth" followOutput="smooth"
/> />
</div> </div>

6
web/components/stores/ClientConfigStore.tsx

@ -17,7 +17,7 @@ import {
import { import {
SocketEvent, SocketEvent,
ConnectedClientInfoEvent, ConnectedClientInfoEvent,
SocketMessageType, MessageType,
ChatEvent, ChatEvent,
} from '../../interfaces/socket-events'; } from '../../interfaces/socket-events';
import handleConnectedClientInfoMessage from './eventhandlers/connectedclientinfo'; import handleConnectedClientInfoMessage from './eventhandlers/connectedclientinfo';
@ -102,10 +102,10 @@ export function ClientConfigStore() {
const handleMessage = (message: SocketEvent) => { const handleMessage = (message: SocketEvent) => {
switch (message.type) { switch (message.type) {
case SocketMessageType.CONNECTED_USER_INFO: case MessageType.CONNECTED_USER_INFO:
handleConnectedClientInfoMessage(message as ConnectedClientInfoEvent); handleConnectedClientInfoMessage(message as ConnectedClientInfoEvent);
break; break;
case SocketMessageType.CHAT: case MessageType.CHAT:
handleChatMessage(message as ChatEvent, chatMessages, setChatMessages); handleChatMessage(message as ChatEvent, chatMessages, setChatMessages);
break; break;
default: default:

17
web/components/ui/Content/Content.tsx

@ -1,6 +1,12 @@
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { Layout, Tabs } from 'antd'; import { Layout, Tabs, Layout, Row, Col, Tabs } from 'antd';
import { chatVisibilityAtom, clientConfigStateAtom } from '../../stores/ClientConfigStore'; import Grid from 'antd/lib/card/Grid';
import {
chatVisibilityAtom,
clientConfigStateAtom,
chatMessagesAtom,
chatStateAtom,
} from '../../stores/ClientConfigStore';
import { ClientConfig } from '../../../interfaces/client-config.model'; import { ClientConfig } from '../../../interfaces/client-config.model';
import CustomPageContent from '../../CustomPageContent'; import CustomPageContent from '../../CustomPageContent';
import OwncastPlayer from '../../video/OwncastPlayer'; import OwncastPlayer from '../../video/OwncastPlayer';
@ -10,7 +16,6 @@ import Sidebar from '../Sidebar';
import Footer from '../Footer'; import Footer from '../Footer';
import ChatContainer from '../../chat/ChatContainer'; import ChatContainer from '../../chat/ChatContainer';
import { ChatMessage } from '../../../interfaces/chat-message.model'; import { ChatMessage } from '../../../interfaces/chat-message.model';
import { chatMessagesAtom, chatStateAtom } from '../../stores/ClientConfigStore';
import { ChatState, ChatVisibilityState } from '../../../interfaces/application-state'; import { ChatState, ChatVisibilityState } from '../../../interfaces/application-state';
import ChatTextField from '../../chat/ChatTextField/ChatTextField'; import ChatTextField from '../../chat/ChatTextField/ChatTextField';
@ -26,6 +31,10 @@ export default function FooterComponent() {
const { extraPageContent } = clientConfig; const { extraPageContent } = clientConfig;
const followers: Follower[] = [];
const total = 0;
return ( return (
<Content className={`${s.root}`} data-columns={chatOpen ? 2 : 1}> <Content className={`${s.root}`} data-columns={chatOpen ? 2 : 1}>
<div className={`${s.leftCol}`}> <div className={`${s.leftCol}`}>
@ -36,7 +45,7 @@ export default function FooterComponent() {
<CustomPageContent content={extraPageContent} /> <CustomPageContent content={extraPageContent} />
</TabPane> </TabPane>
<TabPane tab="Followers" key="2"> <TabPane tab="Followers" key="2">
<FollowerCollection /> <FollowerCollection total={total} followers={followers} />
</TabPane> </TabPane>
</Tabs> </Tabs>
{chatOpen && ( {chatOpen && (

4
web/interfaces/socket-events.ts

@ -1,6 +1,6 @@
import { User } from './user.model'; import { User } from './user.model';
export enum SocketMessageType { export enum MessageType {
CHAT = 'CHAT', CHAT = 'CHAT',
PING = 'PING', PING = 'PING',
NAME_CHANGE = 'NAME_CHANGE', NAME_CHANGE = 'NAME_CHANGE',
@ -21,7 +21,7 @@ export enum SocketMessageType {
export interface SocketEvent { export interface SocketEvent {
id: string; id: string;
timestamp: Date; timestamp: Date;
type: SocketMessageType; type: MessageType;
} }
export interface ConnectedClientInfoEvent extends SocketEvent { export interface ConnectedClientInfoEvent extends SocketEvent {

10
web/services/websocket-service.ts

@ -1,8 +1,8 @@
import { message } from 'antd'; import { message } from 'antd';
import { SocketMessageType } from '../interfaces/socket-events'; import { MessageType } from '../interfaces/socket-events';
interface SocketMessage { interface SocketMessage {
type: SocketMessageType; type: MessageType;
data: any; data: any;
} }
@ -96,7 +96,7 @@ export default class WebsocketService {
} }
// Send PONGs // Send PONGs
if (message.type === SocketMessageType.PING) { if (message.type === MessageType.PING) {
this.sendPong(); this.sendPong();
return; return;
} }
@ -106,7 +106,7 @@ export default class WebsocketService {
// Outbound: Other components can pass an object to `send`. // Outbound: Other components can pass an object to `send`.
send(message: any) { send(message: any) {
// Sanity check that what we're sending is a valid type. // Sanity check that what we're sending is a valid type.
if (!message.type || !SocketMessageType[message.type]) { if (!message.type || !MessageType[message.type]) {
console.warn(`Outbound message: Unknown socket message type: "${message.type}" sent.`); console.warn(`Outbound message: Unknown socket message type: "${message.type}" sent.`);
} }
@ -116,7 +116,7 @@ export default class WebsocketService {
// Reply to a PING as a keep alive. // Reply to a PING as a keep alive.
sendPong() { sendPong() {
const pong = { type: SocketMessageType.PONG }; const pong = { type: MessageType.PONG };
this.send(pong); this.send(pong);
} }
} }

8
web/stories/Follower.stories.tsx

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react'; import { ComponentStory, ComponentMeta } from '@storybook/react';
import * as FollowerComponent from '../components/Follower'; import SingleFollower from '../components/Follower';
export default { export default {
title: 'owncast/Follower', title: 'owncast/Follower',
component: FollowerComponent, component: SingleFollower,
parameters: {}, parameters: {},
} as ComponentMeta<typeof FollowerComponent>; } as ComponentMeta<typeof SingleFollower>;
const Template: ComponentStory<typeof FollowerComponent> = args => <FollowerComponent {...args} />; const Template: ComponentStory<typeof SingleFollower> = args => <SingleFollower {...args} />;
export const Example = Template.bind({}); export const Example = Template.bind({});
Example.args = { Example.args = {

Loading…
Cancel
Save