Fix web project build errors

This commit is contained in:
Gabe Kangas
2022-05-11 23:31:31 -07:00
parent b66617961d
commit 72c01e1b9a
86 changed files with 863 additions and 813 deletions

View File

@@ -4,5 +4,6 @@ interface Props {
export default function CustomPageContent(props: Props) {
const { content } = props;
// eslint-disable-next-line react/no-danger
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}

View File

@@ -1,7 +1,3 @@
interface Props {
url: string;
}
export default function PageLogo(props: Props) {
export default function PageLogo() {
return <div>Pimary logo component goes here</div>;
}

View File

@@ -1,9 +1,11 @@
import { SocialLink } from '../interfaces/social-link.model';
interface Props {
// eslint-disable-next-line react/no-unused-prop-types
links: SocialLink[];
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function SocialLinks(props: Props) {
return <div>Social links component goes here</div>;
}

View File

@@ -4,4 +4,4 @@
width: 20px;
margin-right: 3px;
}
}
}

View File

@@ -1,7 +1,7 @@
import { Button } from 'antd';
import { useState } from 'react';
import Modal from '../ui/Modal/Modal';
import { ExternalAction } from '../interfaces/external-action.interface';
import { ExternalAction } from '../../interfaces/external-action';
import s from './ActionButton.module.scss';
interface Props {

View File

@@ -1,9 +1,11 @@
import { ChatMessage } from '../../interfaces/chat-message.model';
interface Props {
// eslint-disable-next-line react/no-unused-prop-types
message: ChatMessage;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function ChatSystemMessage(props: Props) {
return <div>Component goes here</div>;
}

View File

@@ -1,6 +1,6 @@
import { Spin } from 'antd';
import { Virtuoso } from 'react-virtuoso';
import { useState, useMemo, useCallback, useEffect, useRef } from 'react';
import { useRef } from 'react';
import { LoadingOutlined } from '@ant-design/icons';
import { ChatMessage } from '../../interfaces/chat-message.model';
import { ChatState } from '../../interfaces/application-state';

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Props {}
export default function ChatModerationNotification(props: Props) {

View File

@@ -1,3 +1,5 @@
/* eslint-disable react/no-unused-prop-types */
/* eslint-disable @typescript-eslint/no-unused-vars */
import { ChatMessage } from '../../interfaces/chat-message.model';
interface Props {

View File

@@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable react/no-unused-prop-types */
import { ChatMessage } from '../../interfaces/chat-message.model';
interface Props {

View File

@@ -2,6 +2,7 @@ import { useState } from 'react';
interface Props {}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function ChatTextField(props: Props) {
const [value, setValue] = useState('');
const [showEmojis, setShowEmojis] = useState(false);

View File

@@ -1,4 +1,4 @@
.root {
height: 2rem;
color: var(--black);
}
height: 2rem;
color: var(--black);
}

View File

@@ -1,14 +1,13 @@
import { SmileOutlined } from '@ant-design/icons';
import { Button, Popover } from 'antd';
import React, { useState, useMemo, useRef, useEffect } from 'react';
import React, { useState } from 'react';
import { useRecoilValue } from 'recoil';
import { Transforms, createEditor, Node, BaseEditor, Text } from 'slate';
import { Transforms, createEditor, BaseEditor, Text } from 'slate';
import { Slate, Editable, withReact, ReactEditor } from 'slate-react';
import EmojiPicker from './EmojiPicker';
import WebsocketService from '../../../services/websocket-service';
import { websocketServiceAtom } from '../../stores/ClientConfigStore';
import { MessageType } from '../../../interfaces/socket-events';
import s from './ChatTextField.module.scss';
type CustomElement = { type: 'paragraph'; children: CustomText[] };
type CustomText = { text: string };
@@ -25,24 +24,30 @@ interface Props {
value?: string;
}
// eslint-disable-next-line react/prop-types
const Image = ({ element }) => (
<img
// eslint-disable-next-line no-undef
// eslint-disable-next-line react/prop-types
src={element.url}
alt="emoji"
style={{ display: 'inline', position: 'relative', width: '30px', bottom: '10px' }}
/>
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const insertImage = (editor, url) => {
const text = { text: '' };
const image: ImageElement = { type: 'image', url, children: [text] };
Transforms.insertNodes(editor, image);
// const text = { text: '' };
// const image: ImageElement = { type: 'image', url, children: [text] };
// Transforms.insertNodes(editor, image);
};
const withImages = editor => {
const { isVoid } = editor;
// eslint-disable-next-line no-param-reassign
editor.isVoid = element => (element.type === 'image' ? true : isVoid(element));
// eslint-disable-next-line no-param-reassign
editor.isInline = element => element.type === 'image';
return editor;
@@ -52,13 +57,13 @@ export type EmptyText = {
text: string;
};
type ImageElement = {
type: 'image';
url: string;
children: EmptyText[];
};
// type ImageElement = {
// type: 'image';
// url: string;
// children: EmptyText[];
// };
const Element = props => {
const Element = (props: any) => {
const { attributes, children, element } = props;
switch (element.type) {
@@ -71,10 +76,10 @@ const Element = props => {
const serialize = node => {
if (Text.isText(node)) {
let string = node.text;
if (node.bold) {
string = `<strong>${string}</strong>`;
}
const string = node.text;
// if (node.bold) {
// string = `<strong>${string}</strong>`;
// }
return string;
}
@@ -90,8 +95,9 @@ const serialize = node => {
}
};
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function ChatTextField(props: Props) {
const { value: originalValue } = props;
// const { value: originalValue } = props;
const [showEmojis, setShowEmojis] = useState(false);
const websocketService = useRecoilValue<WebsocketService>(websocketServiceAtom);
const [editor] = useState(() => withImages(withReact(createEditor())));
@@ -113,7 +119,7 @@ export default function ChatTextField(props: Props) {
Transforms.delete(editor);
};
const handleChange = e => {};
const handleChange = () => {};
const handleEmojiSelect = emoji => {
console.log(emoji);
@@ -135,19 +141,12 @@ export default function ChatTextField(props: Props) {
}
};
const initialValue = [
{
type: 'paragraph',
children: [{ text: originalValue }],
},
];
return (
<div>
<Slate editor={editor} value={initialValue} onChange={handleChange}>
<Slate editor={editor} value={[]} onChange={handleChange}>
<Editable
onKeyDown={onKeyDown}
renderElement={props => <Element {...props} />}
renderElement={p => <Element {...p} />}
placeholder="Chat message goes here..."
/>
</Slate>

View File

@@ -1,22 +1,28 @@
import data from '@emoji-mart/data';
import React, { useRef, useEffect } from 'react';
// import data from '@emoji-mart/data';
import React, { useRef } from 'react';
export default function EmojiPicker(props) {
interface Props {
// eslint-disable-next-line react/no-unused-prop-types
onEmojiSelect: (emoji: string) => void;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export default function EmojiPicker(props: Props) {
const ref = useRef();
// TODO: Pull this custom emoji data in from the emoji API.
const custom = [
{
emojis: [
{
id: 'party_parrot',
name: 'Party Parrot',
keywords: ['dance', 'dancing'],
skins: [{ src: 'https://watch.owncast.online/img/emoji/bluntparrot.gif' }],
},
],
},
];
// const custom = [
// {
// emojis: [
// {
// id: 'party_parrot',
// name: 'Party Parrot',
// keywords: ['dance', 'dancing'],
// skins: [{ src: 'https://watch.owncast.online/img/emoji/bluntparrot.gif' }],
// },
// ],
// },
// ];
// TODO: Fix the emoji picker from throwing errors.
// useEffect(() => {

View File

@@ -7,7 +7,9 @@ interface Props {
export default function ChatUserMessage(props: Props) {
const { message, showModeratorMenu } = props;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { body, user, timestamp } = message;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { displayName, displayColor } = user;
// TODO: Convert displayColor (a hue) to a usable color.

View File

@@ -101,45 +101,6 @@ export function convertToText(str = '') {
return value;
}
/*
You would call this when a user pastes from
the clipboard into a `contenteditable` area.
*/
export function convertOnPaste(event = { preventDefault() {} }, emojiList) {
// Prevent paste.
event.preventDefault();
// Set later.
let value = '';
// Does method exist?
const hasEventClipboard = !!(
event.clipboardData &&
typeof event.clipboardData === 'object' &&
typeof event.clipboardData.getData === 'function'
);
// Get clipboard data?
if (hasEventClipboard) {
value = event.clipboardData.getData('text/plain');
}
// Insert into temp `<textarea>`, read back out.
const textarea = document.createElement('textarea');
textarea.innerHTML = value;
value = textarea.innerText;
// Clean up text.
value = convertToText(value);
const HTML = emojify(value, emojiList);
// Insert text.
if (typeof document.execCommand === 'function') {
document.execCommand('insertHTML', false, HTML);
}
}
export function createEmojiMarkup(data, isCustom) {
const emojiUrl = isCustom ? data.emoji : data.url;
const emojiName = (
@@ -156,6 +117,7 @@ export function trimNbsp(html) {
export function emojify(HTML, emojiList) {
const textValue = convertToText(HTML);
// eslint-disable-next-line no-plusplus
for (let lastPos = textValue.length; lastPos >= 0; lastPos--) {
const endPos = textValue.lastIndexOf(':', lastPos);
if (endPos <= 0) {
@@ -170,8 +132,9 @@ export function emojify(HTML, emojiList) {
emojiItem => emojiItem.name.toLowerCase() === typedEmoji.toLowerCase(),
);
if (emojiIndex != -1) {
if (emojiIndex !== -1) {
const emojiImgElement = createEmojiMarkup(emojiList[emojiIndex], true);
// eslint-disable-next-line no-param-reassign
HTML = HTML.replace(`:${typedEmoji}:`, emojiImgElement);
}
}

View File

@@ -1,19 +1,16 @@
import React from 'react';
import cn from 'classnames';
import s from './Logo.module.scss'
import s from './Logo.module.scss';
interface Props {
variant: 'simple' | 'contrast'
variant: 'simple' | 'contrast';
}
export default function Logo({variant = 'simple'}: Props) {
const rootClassName = cn(
s.root,
{
[s.simple]: variant === 'simple',
[s.contrast]: variant === 'contrast',
}
)
export default function Logo({ variant = 'simple' }: Props) {
const rootClassName = cn(s.root, {
[s.simple]: variant === 'simple',
[s.contrast]: variant === 'contrast',
});
return (
<div className={rootClassName}>

View File

@@ -6,8 +6,8 @@ import { ChatState, ChatVisibilityState } from '../../../interfaces/application-
import s from './UserDropdown.module.scss';
interface Props {
username?: string;
chatState?: ChatState;
username: string;
chatState: ChatState;
}
export default function UserDropdown({ username = 'test-user', chatState }: Props) {
@@ -44,11 +44,6 @@ export default function UserDropdown({ username = 'test-user', chatState }: Prop
<DownOutlined />
</Space>
</Button>
{/*
<button type="button" className="ant-dropdown-link" onClick={e => e.preventDefault()}>
{username} <DownOutlined />
</button>
*/}
</Dropdown>
</div>
);

View File

@@ -1 +1 @@
export { default } from './UserDropdown'
export { default } from './UserDropdown';

View File

@@ -1,2 +1,2 @@
export { default as UserDropdown } from './UserDropdown'
export { default as OwncastLogo } from './Logo'
export { default as UserDropdown } from './UserDropdown';
export { default as OwncastLogo } from './Logo';

View File

@@ -0,0 +1,18 @@
import { AppProps } from 'next/app';
import ServerStatusProvider from '../../utils/server-status-context';
import AlertMessageProvider from '../../utils/alert-message-context';
import MainLayout from '../main-layout';
function AdminLayout({ Component, pageProps }: AppProps) {
return (
<ServerStatusProvider>
<AlertMessageProvider>
<MainLayout>
<Component {...pageProps} />
</MainLayout>
</AlertMessageProvider>
</ServerStatusProvider>
);
}
export default AdminLayout;

View File

@@ -130,7 +130,7 @@ export default function MainLayout(props) {
<Sider width={240} className="side-nav">
<h1 className="owncast-title">
<span className="logo-container">
<OwncastLogo />
<OwncastLogo variant="simple" />
</span>
<span className="title-label">Owncast Admin</span>
</h1>

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Props {}
export default function AuthModal(props: Props) {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Props {}
export default function BrowserNotifyModal(props: Props) {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Props {}
export default function FediAuthModal(props: Props) {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Props {}
export default function FollowModal(props: Props) {

View File

@@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
interface Props {}
export default function IndieAuthModal(props: Props) {

View File

@@ -3,7 +3,7 @@ import { Card, Col, Row, Typography } from 'antd';
import Link from 'next/link';
import { useContext } from 'react';
import LogTable from './log-table';
import OwncastLogo from './logo';
import OwncastLogo from './common/Logo/Logo';
import NewsFeed from './news-feed';
import { ConfigDetails } from '../types/config-section';
import { ServerStatusContext } from '../utils/server-status-context';
@@ -125,7 +125,7 @@ export default function Offline({ logs = [], config }: OfflineProps) {
<Col span={12} offset={6}>
<div className="offline-intro">
<span className="logo">
<OwncastLogo />
<OwncastLogo variant="simple" />
</span>
<div>
<Title level={2}>No stream is active</Title>

View File

@@ -6,7 +6,6 @@ import ClientConfigService from '../../services/client-config-service';
import ChatService from '../../services/chat-service';
import WebsocketService from '../../services/websocket-service';
import { ChatMessage } from '../../interfaces/chat-message.model';
import { getLocalStorage, setLocalStorage } from '../../utils/helpers';
import {
AppState,
ChatState,
@@ -16,10 +15,10 @@ import {
getChatVisibilityState,
} from '../../interfaces/application-state';
import {
SocketEvent,
ConnectedClientInfoEvent,
MessageType,
ChatEvent,
SocketEvent,
} from '../../interfaces/socket-events';
import handleConnectedClientInfoMessage from './eventhandlers/connectedclientinfo';
import handleChatMessage from './eventhandlers/handleChatMessage';
@@ -77,10 +76,8 @@ export function ClientConfigStore() {
const [chatMessages, setChatMessages] = useRecoilState<ChatMessage[]>(chatMessagesAtom);
const setChatDisplayName = useSetRecoilState<string>(chatDisplayNameAtom);
const [appState, setAppState] = useRecoilState<AppState>(appStateAtom);
const [videoState, setVideoState] = useRecoilState<VideoState>(videoStateAtom);
const [accessToken, setAccessToken] = useRecoilState<string>(accessTokenAtom);
const [websocketService, setWebsocketService] =
useRecoilState<WebsocketService>(websocketServiceAtom);
const setWebsocketService = useSetRecoilState<WebsocketService>(websocketServiceAtom);
let ws: WebsocketService;

View File

@@ -1,4 +1,4 @@
import { ConnectedClientInfoEvent, SocketEvent } from '../../../interfaces/socket-events';
import { ConnectedClientInfoEvent } from '../../../interfaces/socket-events';
export default function handleConnectedClientInfoMessage(message: ConnectedClientInfoEvent) {
console.log('connected client', message);

View File

@@ -1,26 +1,26 @@
.root {
display: grid;
grid-template-columns: 1fr;
display: grid;
grid-template-columns: 1fr;
}
.mobileChat {
display: block;
position: absolute;
background-color: white;
top: 0px;
width: 100%;
height: calc(50vh - var(--header-h));
display: block;
position: absolute;
background-color: white;
top: 0px;
width: 100%;
height: calc(50vh - var(--header-h));
}
.leftCol {
display: grid;
// -64px, which is the header
grid-template-rows: 50vh calc(50vh - var(--header-h));
display: grid;
// -64px, which is the header
grid-template-rows: 50vh calc(50vh - var(--header-h));
}
.lowerRow {
position: relative;
display: grid;
grid-template-rows: 1fr var(--header-h);
position: relative;
display: grid;
grid-template-rows: 1fr var(--header-h);
}
.pageContentSection {
@@ -32,10 +32,10 @@
}
@media (min-width: 768px) {
.mobileChat {
display: none;
}
.root[data-columns='2'] {
grid-template-columns: 1fr var(--chat-w);
}
.mobileChat {
display: none;
}
.root[data-columns='2'] {
grid-template-columns: 1fr var(--chat-w);
}
}

View File

@@ -1,6 +1,5 @@
import { useRecoilValue } from 'recoil';
import { Layout, Button, Col, Tabs } from 'antd';
import Grid from 'antd/lib/card/Grid';
import { Layout, Button, Tabs } from 'antd';
import {
chatVisibilityAtom,
clientConfigStateAtom,
@@ -23,6 +22,7 @@ import ActionButtonRow from '../../action-buttons/ActionButtonRow';
import ActionButton from '../../action-buttons/ActionButton';
import Statusbar from '../Statusbar/Statusbar';
import { ServerStatus } from '../../../interfaces/server-status.model';
import { Follower } from '../../../interfaces/follower';
const { TabPane } = Tabs;
const { Content } = Layout;
@@ -34,8 +34,8 @@ export default function ContentComponent() {
const messages = useRecoilValue<ChatMessage[]>(chatMessagesAtom);
const chatState = useRecoilValue<ChatState>(chatStateAtom);
const { extraPageContent } = clientConfig;
const { online, viewerCount, lastConnectTime, lastDisconnectTime, streamTitle } = status;
const { extraPageContent, version } = clientConfig;
const { online, viewerCount, lastConnectTime, lastDisconnectTime } = status;
const followers: Follower[] = [];
@@ -88,7 +88,7 @@ export default function ContentComponent() {
<ChatTextField />
</div>
)}
<Footer />
<Footer version={version} />
</div>
</div>
{chatOpen && <Sidebar />}

View File

@@ -1 +1 @@
export { default } from "./Content"
export { default } from './Content';

View File

@@ -54,11 +54,11 @@ export default function CrossfadeImage({
return (
<span style={spanStyle}>
{[...srcs, nextSrc].map(
(src, index) =>
src !== '' && (
(singleSrc, index) =>
singleSrc !== '' && (
<img
key={(key + index) % 3}
src={src}
key={singleSrc}
src={singleSrc}
alt=""
style={imgStyles[index]}
onLoad={index === 2 ? onLoadImg : undefined}

View File

@@ -2,7 +2,11 @@ import { Layout } from 'antd';
const { Footer } = Layout;
export default function FooterComponent(props) {
interface Props {
version: string;
}
export default function FooterComponent(props: Props) {
const { version } = props;
return <Footer style={{ textAlign: 'center', height: '64px' }}>Footer: Owncast {version}</Footer>;

View File

@@ -3,7 +3,7 @@
align-items: center;
justify-content: space-between;
z-index: 1;
padding: .5rem 1rem;
padding: 0.5rem 1rem;
.logo {
display: flex;
align-items: center;

View File

@@ -1,4 +1,5 @@
import { Layout } from 'antd';
import { ChatState } from '../../../interfaces/application-state';
import { OwncastLogo, UserDropdown } from '../../common';
import s from './Header.module.scss';
@@ -12,10 +13,10 @@ export default function HeaderComponent({ name = 'Your stream title' }: Props) {
return (
<Header className={`${s.header}`}>
<div className={`${s.logo}`}>
<OwncastLogo variant='contrast'/>
<OwncastLogo variant="contrast" />
<span>{name}</span>
</div>
<UserDropdown />
<UserDropdown username="fillmein" chatState={ChatState.Available} />
</Header>
);
}

View File

@@ -8,4 +8,4 @@
display: block;
height: 100%;
padding: 2vw;
}
}

View File

@@ -29,7 +29,6 @@ export default function Modal(props: Props) {
width="100%"
height="100%"
sandbox="allow-same-origin allow-scripts allow-popups allow-forms"
allowpaymentrequest="true"
frameBorder="0"
allowFullScreen
onLoad={() => setLoading(false)}

View File

@@ -8,5 +8,3 @@
display: flex;
}
}

View File

@@ -7,4 +7,4 @@
height: 30px;
width: 100%;
background-color: black;
}
}

View File

@@ -40,7 +40,7 @@ export default function Statusbar(props: Props) {
const duration = makeDurationString(new Date(lastConnectTime));
onlineMessage = online ? `Live for ${duration}` : 'Offline';
rightSideMessage = `${viewerCount > 0 ? `${viewerCount}` : 'No'} ${
viewerCount == 1 ? 'viewer' : 'viewers'
viewerCount === 1 ? 'viewer' : 'viewers'
}`;
} else {
onlineMessage = 'Offline';

View File

@@ -1,4 +1,4 @@
export { default as Header } from './Header/index'
export { default as Sidebar } from './Sidebar/index'
export { default as Footer } from './Footer/index'
export { default as Content } from './Content/index'
export { default as Header } from './Header/index';
export { default as Sidebar } from './Sidebar/index';
export { default as Footer } from './Footer/index';
export { default as Content } from './Content/index';

View File

@@ -107,11 +107,7 @@ export default function OwncastPlayer(props: Props) {
<div style={{ display: 'grid' }}>
{online && (
<div style={{ gridColumn: 1, gridRow: 1 }}>
<VideoJS
style={{ gridColumn: 1, gridRow: 1 }}
options={videoJsOptions}
onReady={handlePlayerReady}
/>
<VideoJS options={videoJsOptions} onReady={handlePlayerReady} />
</div>
)}
<div style={{ gridColumn: 1, gridRow: 1 }}>

View File

@@ -6,4 +6,4 @@
.vjs-big-play-centered .vjs-big-play-button {
z-index: 99999 !important;
}
}
}

View File

@@ -2,4 +2,4 @@
background-color: black;
display: flex;
justify-content: center;
}
}

View File

@@ -7,8 +7,12 @@ require('video.js/dist/video-js.css');
// TODO: Restore volume that was saved in local storage.
// import { getLocalStorage, setLocalStorage } from '../../utils/helpers.js';
// import { PLAYER_VOLUME, URL_STREAM } from '../../utils/constants.js';
interface Props {
options: any;
onReady: (player: videojs.Player) => void;
}
export function VideoJS(props) {
export function VideoJS(props: Props) {
const videoRef = React.useRef(null);
const playerRef = React.useRef(null);
const { options, onReady } = props;
@@ -18,11 +22,10 @@ export function VideoJS(props) {
if (!playerRef.current) {
const videoElement = videoRef.current;
// if (!videoElement) return;
// eslint-disable-next-line no-multi-assign
const player = (playerRef.current = videojs(videoElement, options, () => {
player.log('player is ready');
onReady && onReady(player);
return onReady && onReady(player);
}));
// TODO: Add airplay support, video settings menu, latency compensator, etc.
@@ -48,6 +51,7 @@ export function VideoJS(props) {
return (
<div data-vjs-player>
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
<video ref={videoRef} className={`video-js vjs-big-play-centered ${s.player}`} />
</div>
);