mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-23 14:40:50 +01:00
Merge pull request #23 from billsonnn/feature/youtube-tv
Feature/youtube tv
This commit is contained in:
commit
29fcaa31b4
@ -22,6 +22,7 @@
|
||||
"react-slider": "^1.3.1",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"react-youtube": "^7.13.1",
|
||||
"typescript": "^4.3.5",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
|
@ -0,0 +1,27 @@
|
||||
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
|
||||
|
||||
export class RoomWidgetUpdateYoutubeDisplayEvent extends RoomWidgetUpdateEvent
|
||||
{
|
||||
public static UPDATE_YOUTUBE_DISPLAY: string = 'RWUEIE_UPDATE_YOUTUBE_DISPLAY';
|
||||
|
||||
private _objectId: number;
|
||||
private _hasControl: boolean;
|
||||
|
||||
constructor(objectId: number, hasControl = false)
|
||||
{
|
||||
super(RoomWidgetUpdateYoutubeDisplayEvent.UPDATE_YOUTUBE_DISPLAY);
|
||||
|
||||
this._objectId = objectId;
|
||||
this._hasControl = hasControl;
|
||||
}
|
||||
|
||||
public get objectId(): number
|
||||
{
|
||||
return this._objectId;
|
||||
}
|
||||
|
||||
public get hasControl(): boolean
|
||||
{
|
||||
return this._hasControl;
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
import { SecurityLevel } from '@nitrots/nitro-renderer';
|
||||
import { NitroEvent } from '@nitrots/nitro-renderer/src/core/events/NitroEvent';
|
||||
import { GetYoutubeDisplayStatusMessageComposer } from '@nitrots/nitro-renderer/src/nitro/communication/messages/outgoing/room/furniture/youtube';
|
||||
import { RoomEngineTriggerWidgetEvent } from '@nitrots/nitro-renderer/src/nitro/room/events/RoomEngineTriggerWidgetEvent';
|
||||
import { RoomWidgetEnum } from '@nitrots/nitro-renderer/src/nitro/ui/widget/enums/RoomWidgetEnum';
|
||||
import { RoomWidgetMessage, RoomWidgetUpdateEvent } from '..';
|
||||
import { GetSessionDataManager, IsOwnerOfFurniture } from '../../..';
|
||||
import { SendMessageHook } from '../../../../../hooks';
|
||||
import { GetRoomEngine } from '../../GetRoomEngine';
|
||||
import { RoomWidgetUpdateYoutubeDisplayEvent } from '../events/RoomWidgetUpdateYoutubeDisplayEvent';
|
||||
import { RoomWidgetHandler } from './RoomWidgetHandler';
|
||||
|
||||
export class FurnitureYoutubeDisplayWidgetHandler extends RoomWidgetHandler
|
||||
{
|
||||
public static readonly CONTROL_COMMAND_PREVIOUS_VIDEO = 0;
|
||||
public static readonly CONTROL_COMMAND_NEXT_VIDEO = 1;
|
||||
public static readonly CONTROL_COMMAND_PAUSE_VIDEO = 2;
|
||||
public static readonly CONTROL_COMMAND_CONTINUE_VIDEO = 3;
|
||||
|
||||
private _lastFurniId: number = -1;
|
||||
|
||||
public processEvent(event: NitroEvent): void
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case RoomEngineTriggerWidgetEvent.OPEN_WIDGET: {
|
||||
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
|
||||
|
||||
const roomObject = GetRoomEngine().getRoomObject(widgetEvent.roomId, widgetEvent.objectId, widgetEvent.category);
|
||||
|
||||
if(!roomObject) return;
|
||||
|
||||
this._lastFurniId = widgetEvent.objectId;
|
||||
|
||||
const hasControl = GetSessionDataManager().hasSecurity(SecurityLevel.EMPLOYEE) || IsOwnerOfFurniture(roomObject);
|
||||
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateYoutubeDisplayEvent(roomObject.id, hasControl));
|
||||
SendMessageHook(new GetYoutubeDisplayStatusMessageComposer(this._lastFurniId));
|
||||
return;
|
||||
}
|
||||
case RoomEngineTriggerWidgetEvent.CLOSE_WIDGET: {
|
||||
const widgetEvent = (event as RoomEngineTriggerWidgetEvent);
|
||||
|
||||
if(widgetEvent.objectId !== this._lastFurniId) return;
|
||||
|
||||
this.container.eventDispatcher.dispatchEvent(new RoomWidgetUpdateYoutubeDisplayEvent(-1));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
|
||||
{
|
||||
switch(message.type)
|
||||
{
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public get type(): string
|
||||
{
|
||||
return RoomWidgetEnum.YOUTUBE;
|
||||
}
|
||||
|
||||
public get eventTypes(): string[]
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public get messageTypes(): string[]
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
BIN
src/assets/images/room-widgets/youtube-widget/next.png
Normal file
BIN
src/assets/images/room-widgets/youtube-widget/next.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 164 B |
BIN
src/assets/images/room-widgets/youtube-widget/prev.png
Normal file
BIN
src/assets/images/room-widgets/youtube-widget/prev.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 249 B |
@ -693,6 +693,17 @@
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&.icon-youtube-next {
|
||||
background: url("../images/room-widgets/youtube-widget/next.png");
|
||||
width: 21px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&.icon-youtube-prev {
|
||||
background: url("../images/room-widgets/youtube-widget/prev.png");
|
||||
width: 21px;
|
||||
height: 16px;
|
||||
}
|
||||
&.icon-friendlist-warning {
|
||||
background: url("../images/friendlist/icons/icon_warning.png");
|
||||
width: 23px;
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import { DispatchMouseEvent, DispatchTouchEvent, DoorbellWidgetHandler, FurniChooserWidgetHandler, FurnitureContextMenuWidgetHandler, FurnitureCreditWidgetHandler, FurnitureCustomStackHeightWidgetHandler, FurnitureDimmerWidgetHandler, FurnitureExternalImageWidgetHandler, FurnitureMannequinWidgetHandler, FurniturePresentWidgetHandler, GetNitroInstance, GetRoomEngine, InitializeRoomInstanceRenderingCanvas, IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler, RoomWidgetRoomToolsHandler, RoomWidgetUpdateRoomViewEvent, UserChooserWidgetHandler } from '../../api';
|
||||
import { FurnitureYoutubeDisplayWidgetHandler } from '../../api/nitro/room/widgets/handlers/FurnitureYoutubeDisplayWidgetHandler';
|
||||
import { RoomContextProvider } from './context/RoomContext';
|
||||
import { RoomColorView } from './RoomColorView';
|
||||
import { RoomViewProps } from './RoomView.types';
|
||||
@ -44,6 +45,7 @@ export const RoomView: FC<RoomViewProps> = props =>
|
||||
widgetHandlerManager.registerHandler(new FurnitureExternalImageWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new FurniturePresentWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new FurnitureDimmerWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new FurnitureYoutubeDisplayWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new FurnitureMannequinWidgetHandler());
|
||||
|
||||
setWidgetHandler(widgetHandlerManager);
|
||||
|
@ -13,3 +13,4 @@
|
||||
@import './stickie/FurnitureStickieView';
|
||||
@import './high-score/FurnitureHighScoreView';
|
||||
@import './gift-opening/FurnitureGiftOpeningView';
|
||||
@import './youtube-tv/FurnitureYoutubeDisplayView';
|
||||
|
@ -13,6 +13,7 @@ import { FurnitureManipulationMenuView } from './manipulation-menu/FurnitureMani
|
||||
import { FurnitureMannequinView } from './mannequin/FurnitureMannequinView';
|
||||
import { FurnitureStickieView } from './stickie/FurnitureStickieView';
|
||||
import { FurnitureTrophyView } from './trophy/FurnitureTrophyView';
|
||||
import { FurnitureYoutubeDisplayView } from './youtube-tv/FurnitureYoutubeDisplayView';
|
||||
|
||||
export const FurnitureWidgetsView: FC<{}> = props =>
|
||||
{
|
||||
@ -32,6 +33,7 @@ export const FurnitureWidgetsView: FC<{}> = props =>
|
||||
<FurnitureTrophyView />
|
||||
<FurnitureBadgeDisplayView />
|
||||
<FurnitureExternalImageView />
|
||||
<FurnitureYoutubeDisplayView />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,54 @@
|
||||
.youtube-tv-widget {
|
||||
width: 600px;
|
||||
height: 380px;
|
||||
|
||||
.youtube-video-container {
|
||||
//min-height: 366px;
|
||||
|
||||
.empty-video {
|
||||
background-color: black;
|
||||
color: white;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.youtubeContainer {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
//height: 0;
|
||||
//padding-bottom: 56.25%;
|
||||
overflow: hidden;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.youtubeContainer iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.playlist-container {
|
||||
overflow-y: hidden;
|
||||
margin-right: -10px;
|
||||
color: black;
|
||||
height: 100%;
|
||||
|
||||
.playlist-controls {
|
||||
width: 100%;
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.playlist-grid {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,247 @@
|
||||
import { ControlYoutubeDisplayPlaybackMessageComposer, SetYoutubeDisplayPlaylistMessageComposer, YoutubeControlVideoMessageEvent, YoutubeDisplayPlaylist, YoutubeDisplayPlaylistsEvent, YoutubeDisplayVideoMessageEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import YouTube, { Options } from 'react-youtube';
|
||||
import { LocalizeText } from '../../../../../api';
|
||||
import { RoomWidgetUpdateYoutubeDisplayEvent } from '../../../../../api/nitro/room/widgets/events/RoomWidgetUpdateYoutubeDisplayEvent';
|
||||
import { FurnitureYoutubeDisplayWidgetHandler } from '../../../../../api/nitro/room/widgets/handlers/FurnitureYoutubeDisplayWidgetHandler';
|
||||
import { BatchUpdates, CreateEventDispatcherHook, CreateMessageHook, SendMessageHook } from '../../../../../hooks';
|
||||
import { NitroCardContentView, NitroCardGridItemView, NitroCardGridView, NitroCardHeaderView, NitroCardView } from '../../../../../layout';
|
||||
import { useRoomContext } from '../../../context/RoomContext';
|
||||
import { YoutubeVideoPlaybackStateEnum } from './utils/YoutubeVideoPlaybackStateEnum';
|
||||
|
||||
export const FurnitureYoutubeDisplayView: FC<{}> = props =>
|
||||
{
|
||||
const [objectId, setObjectId] = useState(-1);
|
||||
const [videoId, setVideoId] = useState<string>(null);
|
||||
const [videoStart, setVideoStart] = useState<number>(null);
|
||||
const [videoEnd, setVideoEnd] = useState<number>(null);
|
||||
const [currentVideoState, setCurrentVideoState] = useState(-1);
|
||||
const [selectedItem, setSelectedItem] = useState<string>(null);
|
||||
const [playlists, setPlaylists] = useState<YoutubeDisplayPlaylist[]>(null);
|
||||
const [hasControl, setHasControl] = useState(false);
|
||||
const [player, setPlayer] = useState<any>(null);
|
||||
const { eventDispatcher = null } = useRoomContext();
|
||||
|
||||
const onRoomWidgetUpdateYoutubeDisplayEvent = useCallback((event: RoomWidgetUpdateYoutubeDisplayEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case RoomWidgetUpdateYoutubeDisplayEvent.UPDATE_YOUTUBE_DISPLAY: {
|
||||
setObjectId(event.objectId);
|
||||
setHasControl(event.hasControl);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
|
||||
const close = useCallback(() =>
|
||||
{
|
||||
setObjectId(-1);
|
||||
setSelectedItem(null);
|
||||
setPlaylists(null);
|
||||
setHasControl(false);
|
||||
setVideoId(null);
|
||||
setVideoEnd(null);
|
||||
setVideoStart(null);
|
||||
setCurrentVideoState(-1);
|
||||
}, []);
|
||||
|
||||
CreateEventDispatcherHook(RoomWidgetUpdateYoutubeDisplayEvent.UPDATE_YOUTUBE_DISPLAY, eventDispatcher, onRoomWidgetUpdateYoutubeDisplayEvent);
|
||||
|
||||
const onVideo = useCallback((event: YoutubeDisplayVideoMessageEvent) =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
const parser = event.getParser();
|
||||
|
||||
if(objectId !== parser.furniId) return;
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setVideoId(parser.videoId);
|
||||
setVideoStart(parser.startAtSeconds);
|
||||
setVideoEnd(parser.endAtSeconds);
|
||||
setCurrentVideoState(parser.state);
|
||||
});
|
||||
}, [objectId]);
|
||||
|
||||
const onPlaylists = useCallback((event: YoutubeDisplayPlaylistsEvent) =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
const parser = event.getParser();
|
||||
|
||||
if(objectId !== parser.furniId) return;
|
||||
|
||||
BatchUpdates(() =>
|
||||
{
|
||||
setPlaylists(parser.playlists);
|
||||
setSelectedItem(parser.selectedPlaylistId);
|
||||
setVideoId(null);
|
||||
setCurrentVideoState(-1);
|
||||
setVideoEnd(null);
|
||||
setVideoStart(null);
|
||||
});
|
||||
}, [objectId]);
|
||||
|
||||
const onControlVideo = useCallback((event: YoutubeControlVideoMessageEvent) =>
|
||||
{
|
||||
if(objectId === -1) return;
|
||||
|
||||
const parser = event.getParser();
|
||||
|
||||
if(objectId !== parser.furniId) return;
|
||||
|
||||
switch(parser.commandId)
|
||||
{
|
||||
case 1:
|
||||
setCurrentVideoState(YoutubeVideoPlaybackStateEnum.PLAYING);
|
||||
if(player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PLAYING)
|
||||
player.playVideo();
|
||||
break;
|
||||
case 2:
|
||||
setCurrentVideoState(YoutubeVideoPlaybackStateEnum.PAUSED);
|
||||
if(player.getPlayerState() !== YoutubeVideoPlaybackStateEnum.PAUSED)
|
||||
player.pauseVideo();
|
||||
break;
|
||||
}
|
||||
}, [objectId, player]);
|
||||
|
||||
CreateMessageHook(YoutubeDisplayVideoMessageEvent, onVideo);
|
||||
CreateMessageHook(YoutubeDisplayPlaylistsEvent, onPlaylists);
|
||||
CreateMessageHook(YoutubeControlVideoMessageEvent, onControlVideo);
|
||||
|
||||
const processAction = useCallback((action: string) =>
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case 'playlist_prev':
|
||||
SendMessageHook(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, FurnitureYoutubeDisplayWidgetHandler.CONTROL_COMMAND_PREVIOUS_VIDEO));
|
||||
break;
|
||||
case 'playlist_next':
|
||||
SendMessageHook(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, FurnitureYoutubeDisplayWidgetHandler.CONTROL_COMMAND_NEXT_VIDEO));
|
||||
break;
|
||||
case 'video_pause':
|
||||
if(hasControl && videoId && videoId.length)
|
||||
{
|
||||
SendMessageHook(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, FurnitureYoutubeDisplayWidgetHandler.CONTROL_COMMAND_PAUSE_VIDEO));
|
||||
}
|
||||
break;
|
||||
case 'video_play':
|
||||
if(hasControl && videoId && videoId.length)
|
||||
{
|
||||
SendMessageHook(new ControlYoutubeDisplayPlaybackMessageComposer(objectId, FurnitureYoutubeDisplayWidgetHandler.CONTROL_COMMAND_CONTINUE_VIDEO));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if(selectedItem === action)
|
||||
{
|
||||
setSelectedItem(null);
|
||||
SendMessageHook(new SetYoutubeDisplayPlaylistMessageComposer(objectId, ''));
|
||||
return;
|
||||
}
|
||||
SendMessageHook(new SetYoutubeDisplayPlaylistMessageComposer(objectId, action));
|
||||
setSelectedItem(action);
|
||||
}
|
||||
}, [hasControl, objectId, selectedItem, videoId]);
|
||||
|
||||
const onReady = useCallback((event: any) =>
|
||||
{
|
||||
setPlayer(event.target);
|
||||
}, []);
|
||||
|
||||
const onStateChange = useCallback((event: any) =>
|
||||
{
|
||||
setPlayer(event.target);
|
||||
if(objectId)
|
||||
{
|
||||
switch(event.target.getPlayerState())
|
||||
{
|
||||
case -1:
|
||||
case 1:
|
||||
if(currentVideoState === 2)
|
||||
{
|
||||
//event.target.pauseVideo();
|
||||
}
|
||||
if(currentVideoState !== 1)
|
||||
{
|
||||
processAction('video_play');
|
||||
}
|
||||
return;
|
||||
case 2:
|
||||
if(currentVideoState !== 2)
|
||||
{
|
||||
processAction('video_pause');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [currentVideoState, objectId, processAction]);
|
||||
|
||||
const getYoutubeOpts = useMemo( () =>
|
||||
{
|
||||
if(!videoStart && !videoEnd)
|
||||
{
|
||||
return {
|
||||
height: '375',
|
||||
width: '500',
|
||||
playerVars: {
|
||||
autoplay: 1,
|
||||
disablekb: 1,
|
||||
controls: 0,
|
||||
origin: window.origin,
|
||||
modestbranding: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
height: '375',
|
||||
width: '500',
|
||||
playerVars: {
|
||||
autoplay: 1,
|
||||
disablekb: 1,
|
||||
controls: 0,
|
||||
origin: window.origin,
|
||||
modestbranding: 1,
|
||||
start: videoStart,
|
||||
end: videoEnd
|
||||
}
|
||||
}
|
||||
}, [videoEnd, videoStart]);
|
||||
|
||||
if((objectId === -1)) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="youtube-tv-widget">
|
||||
<NitroCardHeaderView headerText={''} onCloseClick={close} />
|
||||
<NitroCardContentView>
|
||||
<div className="row w-100 h-100">
|
||||
<div className="youtube-video-container col-9">
|
||||
{(videoId && videoId.length > 0) &&
|
||||
<YouTube videoId={videoId} opts={getYoutubeOpts as Options} onReady={onReady} onStateChange={onStateChange} containerClassName={'youtubeContainer'} />
|
||||
}
|
||||
{(!videoId || videoId.length === 0) &&
|
||||
<div className="empty-video w-100 h-100 justify-content-center align-items-center d-flex">{LocalizeText('widget.furni.video_viewer.no_videos')}</div>
|
||||
}
|
||||
</div>
|
||||
<div className="playlist-container col-3">
|
||||
<span className="playlist-controls justify-content-center d-flex">
|
||||
<i className="icon icon-youtube-prev cursor-pointer" onClick={() => processAction('playlist_prev')} />
|
||||
<i className="icon icon-youtube-next cursor-pointer" onClick={() => processAction('playlist_next')} />
|
||||
</span>
|
||||
<div className="mb-1">{LocalizeText('widget.furni.video_viewer.playlists')}</div>
|
||||
<NitroCardGridView columns={1} className="playlist-grid">
|
||||
{playlists && playlists.map((entry, index) =>
|
||||
{
|
||||
return (
|
||||
<NitroCardGridItemView key={index} onClick={() => processAction(entry.video)} itemActive={entry.video === selectedItem}>
|
||||
<b>{entry.title}</b> - {entry.description}
|
||||
</NitroCardGridItemView>
|
||||
)
|
||||
})}
|
||||
</NitroCardGridView>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
)
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
export class YoutubeVideoPlaybackStateEnum
|
||||
{
|
||||
public static readonly UNSTARTED = -1;
|
||||
public static readonly ENDED = 0;
|
||||
public static readonly PLAYING = 1;
|
||||
public static readonly PAUSED = 2;
|
||||
public static readonly BUFFERING = 3;
|
||||
public static readonly CUED = 5;
|
||||
}
|
Loading…
Reference in New Issue
Block a user