reafctor: normalize component formatting (#2082)
* refactor: move/rename BanUserButton file * refactor: move/rename Chart file * refactor: update generic component filenames to PascalCase * refactor: update config component filenames to PascalCase * refactor: update AdminLayout component filename to PascalCase * refactor: update/move VideoJS component * chore(eslint): disable bad react/require-default-props rule * refactor: normalize ActionButton component * refactor: normalize ActionButtonRow component * refactor: normalize FollowButton component * refactor: normalize NotifyButton component * refactor: normalize ChatActionMessage component * refactor: normalize ChatContainer component * refactor: normalize ChatJoinMessage component * refactor: normalize ChatModerationActionMenu component * refactor: normalize ChatModerationDetailsModal component * refactor: normalize ChatModeratorNotification component * refactor: normalize ChatSocialMessage component * refactor: normalize ChatSystemMessage component * refactor: normalize ChatTextField component * refactor: normalize ChatUserBadge component * refactor: normalize ChatUserMessage component * refactor: normalize ContentHeader component * refactor: normalize OwncastLogo component * refactor: normalize UserDropdown component * chore(eslint): modify react/function-component-definition rule * refactor: normalize CodecSelector component * refactor: update a bunch of functional components using eslint * refactor: update a bunch of functional components using eslint, pt2 * refactor: update a bunch of functional components using eslint, pt3 * refactor: replace all component->component default imports with named imports * refactor: replace all component-stories->component default imports with named imports * refactor: remove default exports from most components * chore(eslint): add eslint config files for the components and pages dirs * fix: use-before-define error in ChatContainer * Fix ChatContainer import * Only process .tsx files in Next builds Co-authored-by: Gabe Kangas <gabek@real-ity.com>
This commit is contained in:
18
web/components/video/VideoJS/VideoJS.module.scss
Normal file
18
web/components/video/VideoJS/VideoJS.module.scss
Normal file
@@ -0,0 +1,18 @@
|
||||
.player {
|
||||
height: auto !important;
|
||||
width: 100%;
|
||||
video {
|
||||
position: static !important;
|
||||
}
|
||||
}
|
||||
|
||||
.poster {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.player {
|
||||
height: 30vh !important;
|
||||
}
|
||||
}
|
||||
51
web/components/video/VideoJS/VideoJS.scss
Normal file
51
web/components/video/VideoJS/VideoJS.scss
Normal file
@@ -0,0 +1,51 @@
|
||||
/* Change all text and icon colors in the player. */
|
||||
.vjs-owncast.video-js {
|
||||
color: var(--theme-color-components-text-on-light);
|
||||
}
|
||||
|
||||
.vjs-owncast .vjs-big-play-button {
|
||||
z-index: 10;
|
||||
color: var(--theme-color-action);
|
||||
font-size: 8rem !important;
|
||||
border-color: transparent !important;
|
||||
border-radius: var(--theme-rounded-corners) !important;
|
||||
background-color: transparent !important;
|
||||
text-shadow: 2px 3px 4px #0000005f;
|
||||
|
||||
-webkit-text-stroke: 2px white;
|
||||
text-stroke: 2px white;
|
||||
|
||||
:hover {
|
||||
transition: all 0.2s ease-in-out;
|
||||
font-size: 10rem;
|
||||
text-shadow: 2px 5px 4px #00000093;
|
||||
}
|
||||
}
|
||||
|
||||
.vjs-owncast .vjs-loading-spinner {
|
||||
z-index: 10;
|
||||
display: block;
|
||||
color: var(--theme-color-components-text-on-light);
|
||||
}
|
||||
|
||||
.vjs-owncast .vjs-control-bar {
|
||||
color: var(--theme-color-components-text-on-light);
|
||||
background-color: var(--theme-color-background-main) !important;
|
||||
}
|
||||
|
||||
.vjs-owncast .vjs-control {
|
||||
color: var(--theme-color-components-text-on-light);
|
||||
}
|
||||
|
||||
.vjs-airplay .vjs-icon-placeholder::before {
|
||||
content: url('../airplay.png');
|
||||
}
|
||||
|
||||
.vjs-quality-selector .vjs-icon-placeholder {
|
||||
font-family: VideoJS;
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
||||
.vjs-quality-selector .vjs-icon-placeholder::before {
|
||||
content: '\f110';
|
||||
}
|
||||
448
web/components/video/VideoJS/VideoJS.tsx
Normal file
448
web/components/video/VideoJS/VideoJS.tsx
Normal file
@@ -0,0 +1,448 @@
|
||||
import React, { FC } from 'react';
|
||||
import videojs from 'video.js';
|
||||
import styles from './VideoJS.module.scss';
|
||||
|
||||
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';
|
||||
export type VideoJSProps = {
|
||||
options: any;
|
||||
onReady: (player: videojs.Player, vjsInstance: videojs) => void;
|
||||
};
|
||||
|
||||
export const VideoJS: FC<VideoJSProps> = ({ options, onReady }) => {
|
||||
const videoRef = React.useRef(null);
|
||||
const playerRef = React.useRef(null);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Make sure Video.js player is only initialized once
|
||||
if (!playerRef.current) {
|
||||
const videoElement = videoRef.current;
|
||||
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
const player = (playerRef.current = videojs(videoElement, options, () => {
|
||||
player.log('player is ready');
|
||||
return onReady && onReady(player, videojs);
|
||||
}));
|
||||
|
||||
// TODO: Add airplay support, video settings menu, latency compensator, etc.
|
||||
|
||||
// You can update player in the `else` block here, for example:
|
||||
// } else {
|
||||
player.autoplay(options.autoplay);
|
||||
player.src(options.sources);
|
||||
}
|
||||
}, [options, videoRef]);
|
||||
|
||||
// Dispose the Video.js player when the functional component unmounts
|
||||
React.useEffect(() => {
|
||||
const player = playerRef.current;
|
||||
|
||||
return () => {
|
||||
if (player) {
|
||||
player.dispose();
|
||||
playerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [playerRef]);
|
||||
|
||||
return (
|
||||
<div data-vjs-player>
|
||||
{/* eslint-disable-next-line jsx-a11y/media-has-caption */}
|
||||
<video
|
||||
ref={videoRef}
|
||||
className={`video-js vjs-big-play-centered ${styles.player} vjs-owncast`}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default VideoJS;
|
||||
|
||||
// import PlaybackMetrics from '../metrics/playback.js';
|
||||
// import LatencyCompensator from './latencyCompensator.js';
|
||||
|
||||
// const VIDEO_ID = 'video';
|
||||
// const LATENCY_COMPENSATION_ENABLED = 'latencyCompensatorEnabled';
|
||||
|
||||
// // Video setup
|
||||
// const VIDEO_SRC = {
|
||||
// src: URL_STREAM,
|
||||
// type: 'application/x-mpegURL',
|
||||
// };
|
||||
// const VIDEO_OPTIONS = {
|
||||
// autoplay: false,
|
||||
// liveui: true,
|
||||
// preload: 'auto',
|
||||
// controlBar: {
|
||||
// progressControl: {
|
||||
// seekBar: false,
|
||||
// },
|
||||
// },
|
||||
// html5: {
|
||||
// vhs: {
|
||||
// // used to select the lowest bitrate playlist initially. This helps to decrease playback start time. This setting is false by default.
|
||||
// enableLowInitialPlaylist: true,
|
||||
// experimentalBufferBasedABR: true,
|
||||
// useNetworkInformationApi: true,
|
||||
// maxPlaylistRetries: 30,
|
||||
// },
|
||||
// },
|
||||
// liveTracker: {
|
||||
// trackingThreshold: 0,
|
||||
// liveTolerance: 15,
|
||||
// },
|
||||
// sources: [VIDEO_SRC],
|
||||
// };
|
||||
|
||||
// export const POSTER_DEFAULT = `/img/logo.png`;
|
||||
// export const POSTER_THUMB = `/thumbnail.jpg`;
|
||||
|
||||
// export default class MenuSeparator extends VjsMenuItem {
|
||||
// constructor(player, options) {
|
||||
// super(player, options);
|
||||
// }
|
||||
|
||||
// createEl(tag = 'button', props = {}, attributes = {}) {
|
||||
// const el = super.createEl(tag, props, attributes);
|
||||
// el.innerHTML = '<hr style="opacity: 0.3; margin-left: 10px; margin-right: 10px;" />';
|
||||
// return el;
|
||||
// }
|
||||
// }
|
||||
|
||||
// class OwncastPlayer {
|
||||
// constructor() {
|
||||
// window.VIDEOJS_NO_DYNAMIC_STYLE = true; // style override
|
||||
|
||||
// this.vjsPlayer = null;
|
||||
// this.latencyCompensator = null;
|
||||
// this.playbackMetrics = null;
|
||||
|
||||
// this.appPlayerReadyCallback = null;
|
||||
// this.appPlayerPlayingCallback = null;
|
||||
// this.appPlayerEndedCallback = null;
|
||||
|
||||
// this.hasStartedPlayback = false;
|
||||
// this.latencyCompensatorEnabled = false;
|
||||
|
||||
// // bind all the things because safari
|
||||
// this.startPlayer = this.startPlayer.bind(this);
|
||||
// this.handleReady = this.handleReady.bind(this);
|
||||
// this.handlePlaying = this.handlePlaying.bind(this);
|
||||
// this.handleVolume = this.handleVolume.bind(this);
|
||||
// this.handleEnded = this.handleEnded.bind(this);
|
||||
// this.handleError = this.handleError.bind(this);
|
||||
// this.addQualitySelector = this.addVideoSettingsMenu.bind(this);
|
||||
// this.addQualitySelector = this.addVideoSettingsMenu.bind(this);
|
||||
// this.toggleLatencyCompensator = this.toggleLatencyCompensator.bind(this);
|
||||
// this.startLatencyCompensator = this.startLatencyCompensator.bind(this);
|
||||
// this.stopLatencyCompensator = this.stopLatencyCompensator.bind(this);
|
||||
// this.qualitySelectionMenu = null;
|
||||
// this.latencyCompensatorToggleButton = null;
|
||||
// }
|
||||
|
||||
// init() {
|
||||
// // this.addAirplay();
|
||||
// // this.addVideoSettingsMenu();
|
||||
|
||||
// // Add a cachebuster param to playlist URLs.
|
||||
// videojs.Vhs.xhr.beforeRequest = options => {
|
||||
// if (options.uri.match('m3u8')) {
|
||||
// const cachebuster = Math.random().toString(16).substr(2, 8);
|
||||
// options.uri = `${options.uri}?cachebust=${cachebuster}`;
|
||||
// }
|
||||
|
||||
// return options;
|
||||
// };
|
||||
|
||||
// this.vjsPlayer = videojs(VIDEO_ID, VIDEO_OPTIONS);
|
||||
|
||||
// this.vjsPlayer.ready(this.handleReady);
|
||||
// }
|
||||
|
||||
// setupPlayerCallbacks(callbacks) {
|
||||
// const { onReady, onPlaying, onEnded, onError } = callbacks;
|
||||
|
||||
// this.appPlayerReadyCallback = onReady;
|
||||
// this.appPlayerPlayingCallback = onPlaying;
|
||||
// this.appPlayerEndedCallback = onEnded;
|
||||
// this.appPlayerErrorCallback = onError;
|
||||
// }
|
||||
|
||||
// // play
|
||||
// startPlayer() {
|
||||
// this.log('Start playing');
|
||||
// const source = { ...VIDEO_SRC };
|
||||
|
||||
// try {
|
||||
// this.vjsPlayer.volume(getLocalStorage(PLAYER_VOLUME) || 1);
|
||||
// } catch (err) {
|
||||
// console.warn(err);
|
||||
// }
|
||||
// this.vjsPlayer.src(source);
|
||||
// }
|
||||
|
||||
// setupPlaybackMetrics() {
|
||||
// // this.playbackMetrics = new PlaybackMetrics(this.vjsPlayer, videojs);
|
||||
// }
|
||||
|
||||
// setupLatencyCompensator() {
|
||||
// const tech = this.vjsPlayer.tech({ IWillNotUseThisInPlugins: true });
|
||||
|
||||
// // VHS is required.
|
||||
// if (!tech || !tech.vhs) {
|
||||
// }
|
||||
|
||||
// // const latencyCompensatorEnabledSaved = getLocalStorage(LATENCY_COMPENSATION_ENABLED);
|
||||
|
||||
// // if (latencyCompensatorEnabledSaved === 'true' && tech && tech.vhs) {
|
||||
// // this.startLatencyCompensator();
|
||||
// // } else {
|
||||
// // this.stopLatencyCompensator();
|
||||
// // }
|
||||
// }
|
||||
|
||||
// startLatencyCompensator() {
|
||||
// // this.latencyCompensator = new LatencyCompensator(this.vjsPlayer);
|
||||
// // this.latencyCompensator.enable();
|
||||
// // this.latencyCompensatorEnabled = true;
|
||||
// // this.setLatencyCompensatorItemTitle('disable minimized latency');
|
||||
// }
|
||||
|
||||
// stopLatencyCompensator() {
|
||||
// // if (this.latencyCompensator) {
|
||||
// // this.latencyCompensator.disable();
|
||||
// // }
|
||||
// // this.LatencyCompensator = null;
|
||||
// // this.latencyCompensatorEnabled = false;
|
||||
// // this.setLatencyCompensatorItemTitle(
|
||||
// // '<span style="font-size: 0.8em">enable minimized latency (experimental)</span>',
|
||||
// // );
|
||||
// }
|
||||
|
||||
// handleReady() {
|
||||
// console.log('handleReady');
|
||||
// this.vjsPlayer.on('error', this.handleError);
|
||||
// this.vjsPlayer.on('playing', this.handlePlaying);
|
||||
// this.vjsPlayer.on('volumechange', this.handleVolume);
|
||||
// this.vjsPlayer.on('ended', this.handleEnded);
|
||||
|
||||
// this.vjsPlayer.on('loadeddata', () => {
|
||||
// this.setupPlaybackMetrics();
|
||||
// this.setupLatencyCompensator();
|
||||
// });
|
||||
|
||||
// if (this.appPlayerReadyCallback) {
|
||||
// // start polling
|
||||
// this.appPlayerReadyCallback();
|
||||
// }
|
||||
|
||||
// this.vjsPlayer.log.level('debug');
|
||||
// }
|
||||
|
||||
// handleVolume() {
|
||||
// setLocalStorage(PLAYER_VOLUME, this.vjsPlayer.muted() ? 0 : this.vjsPlayer.volume());
|
||||
// }
|
||||
|
||||
// handlePlaying() {
|
||||
// if (this.appPlayerPlayingCallback) {
|
||||
// // start polling
|
||||
// this.appPlayerPlayingCallback();
|
||||
// }
|
||||
|
||||
// if (this.latencyCompensator && !this.hasStartedPlayback) {
|
||||
// this.latencyCompensator.enable();
|
||||
// }
|
||||
|
||||
// this.hasStartedPlayback = true;
|
||||
// }
|
||||
|
||||
// handleEnded() {
|
||||
// if (this.appPlayerEndedCallback) {
|
||||
// this.appPlayerEndedCallback();
|
||||
// }
|
||||
|
||||
// this.stopLatencyCompensator();
|
||||
// }
|
||||
|
||||
// handleError(e) {
|
||||
// this.log(`on Error: ${JSON.stringify(e)}`);
|
||||
// if (this.appPlayerEndedCallback) {
|
||||
// this.appPlayerEndedCallback();
|
||||
// }
|
||||
// }
|
||||
|
||||
// toggleLatencyCompensator() {
|
||||
// if (this.latencyCompensatorEnabled) {
|
||||
// this.stopLatencyCompensator();
|
||||
// setLocalStorage(LATENCY_COMPENSATION_ENABLED, false);
|
||||
// } else {
|
||||
// this.startLatencyCompensator();
|
||||
// setLocalStorage(LATENCY_COMPENSATION_ENABLED, true);
|
||||
// }
|
||||
// }
|
||||
|
||||
// setLatencyCompensatorItemTitle(title) {
|
||||
// const item = document.querySelector('.latency-toggle-item > .vjs-menu-item-text');
|
||||
// if (!item) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// item.innerHTML = title;
|
||||
// }
|
||||
|
||||
// log(message) {
|
||||
// // console.log(`>>> Player: ${message}`);
|
||||
// }
|
||||
|
||||
// render(): JSX.Element {
|
||||
// return (
|
||||
// <div data-vjs-player>
|
||||
// <video ref={videoRef} className='video-js vjs-big-play-centered' />
|
||||
// </div>
|
||||
// );
|
||||
// }
|
||||
|
||||
// async addVideoSettingsMenu() {
|
||||
// if (this.qualityMenuButton) {
|
||||
// player.controlBar.removeChild(this.qualityMenuButton);
|
||||
// }
|
||||
|
||||
// videojs.hookOnce('setup', async player => {
|
||||
// let qualities = [];
|
||||
|
||||
// try {
|
||||
// const response = await fetch('http://localhost:8080/api/video/variants');
|
||||
// qualities = await response.json();
|
||||
// } catch (e) {
|
||||
// console.error(e);
|
||||
// }
|
||||
|
||||
// const MenuItem = videojs.getComponent('MenuItem');
|
||||
// const MenuButtonClass = videojs.getComponent('MenuButton');
|
||||
|
||||
// const lowLatencyItem = new MenuItem(player, {
|
||||
// selectable: true,
|
||||
// });
|
||||
// lowLatencyItem.setAttribute('class', 'latency-toggle-item');
|
||||
// lowLatencyItem.on('click', () => {
|
||||
// this.toggleLatencyCompensator();
|
||||
// });
|
||||
// this.latencyCompensatorToggleButton = lowLatencyItem;
|
||||
|
||||
// const separator = new MenuSeparator(player, {
|
||||
// selectable: false,
|
||||
// });
|
||||
|
||||
// const MenuButton = videojs.extend(MenuButtonClass, {
|
||||
// // The `init()` method will also work for constructor logic here, but it is
|
||||
// // deprecated. If you provide an `init()` method, it will override the
|
||||
// // `constructor()` method!
|
||||
// constructor() {
|
||||
// MenuButtonClass.call(this, player);
|
||||
// },
|
||||
|
||||
// createItems() {
|
||||
// const tech = this.player_.tech({ IWillNotUseThisInPlugins: true });
|
||||
|
||||
// const defaultAutoItem = new MenuItem(player, {
|
||||
// selectable: true,
|
||||
// label: 'Auto',
|
||||
// });
|
||||
|
||||
// const items = qualities.map(item => {
|
||||
// const newMenuItem = new MenuItem(player, {
|
||||
// selectable: true,
|
||||
// label: item.name,
|
||||
// });
|
||||
|
||||
// // Quality selected
|
||||
// newMenuItem.on('click', () => {
|
||||
// // If for some reason tech doesn't exist, then don't do anything
|
||||
// if (!tech) {
|
||||
// console.warn('Invalid attempt to access null player tech');
|
||||
// return;
|
||||
// }
|
||||
// // Only enable this single, selected representation.
|
||||
// tech.vhs.representations().forEach((rep, index) => {
|
||||
// rep.enabled(index === item.index);
|
||||
// });
|
||||
// newMenuItem.selected(false);
|
||||
// });
|
||||
|
||||
// return newMenuItem;
|
||||
// });
|
||||
|
||||
// defaultAutoItem.on('click', () => {
|
||||
// // Re-enable all representations.
|
||||
// tech.vhs.representations().forEach((rep, index) => {
|
||||
// rep.enabled(true);
|
||||
// });
|
||||
// defaultAutoItem.selected(false);
|
||||
// });
|
||||
|
||||
// const supportsLatencyCompensator = !!tech && !!tech.vhs;
|
||||
|
||||
// // Only show the quality selector if there is more than one option.
|
||||
// if (qualities.length < 2 && supportsLatencyCompensator) {
|
||||
// return [lowLatencyItem];
|
||||
// }
|
||||
// if (qualities.length > 1 && supportsLatencyCompensator) {
|
||||
// return [defaultAutoItem, ...items, separator, lowLatencyItem];
|
||||
// }
|
||||
// if (!supportsLatencyCompensator && qualities.length == 1) {
|
||||
// return [];
|
||||
// }
|
||||
|
||||
// return [defaultAutoItem, ...items];
|
||||
// },
|
||||
// });
|
||||
|
||||
// // If none of the settings in this menu are applicable then don't show it.
|
||||
// const tech = player.tech({ IWillNotUseThisInPlugins: true });
|
||||
|
||||
// if (qualities.length < 2 && (!tech || !tech.vhs)) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const menuButton = new MenuButton();
|
||||
// menuButton.addClass('vjs-quality-selector');
|
||||
// player.controlBar.addChild(menuButton, {}, player.controlBar.children_.length - 2);
|
||||
// this.qualityMenuButton = menuButton;
|
||||
// this.latencyCompensatorToggleButton = lowLatencyItem;
|
||||
// });
|
||||
// }
|
||||
|
||||
// addAirplay() {
|
||||
// videojs.hookOnce('setup', player => {
|
||||
// if (window.WebKitPlaybackTargetAvailabilityEvent) {
|
||||
// const videoJsButtonClass = videojs.getComponent('Button');
|
||||
// const concreteButtonClass = videojs.extend(videoJsButtonClass, {
|
||||
// // The `init()` method will also work for constructor logic here, but it is
|
||||
// // deprecated. If you provide an `init()` method, it will override the
|
||||
// // `constructor()` method!
|
||||
// constructor() {
|
||||
// videoJsButtonClass.call(this, player);
|
||||
// },
|
||||
|
||||
// handleClick() {
|
||||
// const videoElement = document.getElementsByTagName('video')[0];
|
||||
// videoElement.webkitShowPlaybackTargetPicker();
|
||||
// },
|
||||
// });
|
||||
|
||||
// const concreteButtonInstance = player.controlBar.addChild(new concreteButtonClass());
|
||||
// concreteButtonInstance.addClass('vjs-airplay');
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
// export { OwncastPlayer };
|
||||
|
||||
// const VjsMenuItem = videojs.getComponent('MenuItem');
|
||||
|
||||
// VjsMenuItem.registerComponent('MenuSeparator', MenuSeparator);
|
||||
Reference in New Issue
Block a user