Start navigator

This commit is contained in:
Bill 2021-04-19 12:34:31 -04:00
parent dde5b6bb2d
commit 44e06a44b6
41 changed files with 365 additions and 53 deletions

14
package-lock.json generated
View File

@ -4158,6 +4158,11 @@
}
}
},
"classnames": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz",
"integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA=="
},
"clean-css": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.3.tgz",
@ -14579,6 +14584,15 @@
"scheduler": "^0.20.2"
}
},
"react-draggable": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz",
"integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==",
"requires": {
"classnames": "^2.2.5",
"prop-types": "^15.6.0"
}
},
"react-error-overlay": {
"version": "6.0.9",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",

View File

@ -10,11 +10,13 @@
"@types/node": "^12.20.7",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3",
"classnames": "^2.3.1",
"immutable": "^4.0.0-rc.12",
"nitro-renderer": "file:../nitro-renderer",
"node-sass": "^5.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-draggable": "^4.4.3",
"react-redux": "^7.2.3",
"react-scripts": "4.0.3",
"react-transition-group": "^4.4.1",

View File

@ -61,7 +61,7 @@
5,
101
],
"communication.packet.log": false,
"communication.packet.log": true,
"communication.pong.manually": true,
"communication.pong.interval.ms": 20000,
"avatar.mandatory.libraries": [

4
src/App.scss Normal file
View File

@ -0,0 +1,4 @@
.nitro-app {
width: 100%;
height: 100%;
}

View File

@ -123,7 +123,7 @@ export function App(): JSX.Element
}
return (
<div>
<div className="nitro-app">
{ (!isReady || isError) && <LoadingView isError={ isError } message={ message } /> }
{ (isReady && !isError) && <MainView /> }
</div>

View File

@ -1 +1,2 @@
export * from './room';
export * from './session';

View File

@ -0,0 +1,7 @@
export * from './DispatchMouseEvent';
export * from './DispatchResizeEvent';
export * from './DispatchTouchEvent';
export * from './GetRoomEngine';
export * from './InitializeRoomInstanceRenderingCanvas';
export * from './ProcessRoomObjectOperation';
export * from './SetActiveRoomId';

View File

@ -1,2 +1,4 @@
export * from './GetRoomSession';
export * from './GetRoomSessionManager';
export * from './GetSessionDataManager';
export * from './StartRoomSession';

1
src/events/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './navigator';

View File

@ -0,0 +1,7 @@
import { NitroEvent } from 'nitro-renderer';
export class NavigatorEvent extends NitroEvent
{
public static SHOW_NAVIGATOR: string = 'NE_SHOW_NAVIGATOR';
public static TOGGLE_NAVIGATOR: string = 'NE_TOGGLE_NAVIGATOR';
}

View File

@ -0,0 +1 @@
export * from './NavigatorEvent';

View File

@ -0,0 +1,11 @@
import Draggable from 'react-draggable';
import { DraggableWindowProps } from './DraggableWindow.types';
export function DraggableWindow(props: DraggableWindowProps): JSX.Element
{
return (
<Draggable handle={ props.handle } bounds="parent" { ...props.draggableOptions }>
{ props.children }
</Draggable>
);
}

View File

@ -0,0 +1,9 @@
import { ReactNode } from 'react';
import { DraggableProps } from 'react-draggable';
export interface DraggableWindowProps
{
handle: string;
draggableOptions?: Partial<DraggableProps>;
children?: ReactNode;
}

View File

@ -1,5 +1,4 @@
import { NitroEvent } from 'nitro-renderer';
import { Nitro } from 'nitro-renderer/src/nitro/Nitro';
import { Nitro, NitroEvent } from 'nitro-renderer';
import { CreateEventDispatcherHook, DispatchEventHook } from '../event-dispatcher.base';
export function useMainEvent(type: string, handler: (event: NitroEvent) => void): void

View File

@ -0,0 +1,14 @@
import { EventDispatcher, IEventDispatcher, NitroEvent } from 'nitro-renderer';
import { CreateEventDispatcherHook, DispatchEventHook } from '../event-dispatcher.base';
const uiEventDispatcher: IEventDispatcher = new EventDispatcher();
export function useUiEvent(type: string, handler: (event: NitroEvent) => void): void
{
CreateEventDispatcherHook(type, uiEventDispatcher, handler);
}
export function dispatchUiEvent(event: NitroEvent): void
{
DispatchEventHook(uiEventDispatcher, event);
}

View File

@ -6,13 +6,15 @@ export function CreateMessageHook(event: IMessageEvent): void
{
useEffect(() =>
{
console.log('register', event);
Nitro.instance.communication.registerMessageEvent(event);
return () =>
{
console.log('unregister', event);
Nitro.instance.communication.removeMessageEvent(event);
}
});
}, []);
}
export function SendMessageHook(event: IMessageComposer<unknown[]>): void

View File

@ -26,6 +26,7 @@ $infostand-zindex: 30;
$chat-zindex: 20;
$highscore-zindex: 19;
@import './transitions/TransitionStyles.scss';
@import './utils/Styles.scss';
@import './views/Styles.scss';
@import './transitions/TransitionStyles';
@import './utils/Styles';
@import './App';
@import './views/Styles';

View File

@ -4,5 +4,5 @@ export interface FadeTransitionProps
{
inProp: boolean;
timeout?: number;
children?: ReactNode
children?: ReactNode;
}

View File

@ -0,0 +1,6 @@
import { Nitro } from 'nitro-renderer';
export function LocalizeText(key: string, parameters: string[] = null, replacements: string[] = null): string
{
return Nitro.instance.getLocalizationWithParameters(key, parameters, replacements);
}

View File

@ -1,9 +1,10 @@
@import './avatar-image/AvatarImage.scss';
@import './hotel-view/HotelView.scss';
@import './loading/LoadingView.scss';
@import './main/MainView.scss';
@import './navigator/NavigatorView.scss';
@import './purse/PurseView.scss';
@import './right-side/RightSideView.scss';
@import './room/RoomView.scss';
@import './toolbar/ToolbarView.scss';
@import './avatar-image/AvatarImage';
@import './hotel-view/HotelView';
@import './loading/LoadingView';
@import './main/MainView';
@import './navigator/NavigatorView';
@import './purse/PurseView';
@import './right-side/RightSideView';
@import './room/RoomView';
@import './room-host/RoomHostView';
@import './toolbar/ToolbarView';

View File

@ -0,0 +1,4 @@
.nitro-main {
width: 100%;
height: 100%;
}

View File

@ -38,14 +38,14 @@ export function MainView(props: MainViewProps): JSX.Element
}, []);
return (
<div>
<div className="nitro-main">
{ landingViewVisible && <HotelView /> }
<RoomHostView />
<FadeTransition inProp={ isReady } timeout={ 300 }>
<ToolbarView isInRoom={ !landingViewVisible } />
</FadeTransition>
<NavigatorView />
<RightSideView />
<FadeTransition inProp={ isReady } timeout={ 300 }>
<ToolbarView />
</FadeTransition>
</div>
);
}

View File

@ -0,0 +1,6 @@
.nitro-navigator {
width: 400px;
height: 400px;
}
@import './tabs/NavigatorTabsView';

View File

@ -1,25 +1,55 @@
import { RoomDataParser, RoomForwardEvent, RoomInfoComposer, RoomInfoEvent, RoomInfoOwnerEvent } from 'nitro-renderer';
import { NavigatorCategoriesComposer, NavigatorInitComposer, NavigatorMetadataEvent, NavigatorSearchComposer, NavigatorSettingsComposer, NavigatorTopLevelContext, RoomDataParser, RoomForwardEvent, RoomInfoComposer, RoomInfoEvent, RoomInfoOwnerEvent, RoomSessionEvent, UserInfoEvent } from 'nitro-renderer';
import { MouseEvent, useCallback, useEffect, useState } from 'react';
import { GetRoomSessionManager, GetSessionDataManager } from '../../api';
import { NavigatorEvent } from '../../events';
import { DraggableWindow } from '../../hooks/draggable-window/DraggableWindow';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { useUiEvent } from '../../hooks/events/ui/ui-event';
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
import { LocalizeText } from '../../utils/LocalizeText';
import { NavigatorViewProps } from './NavigatorView.types';
import { NavigatorTabsView } from './tabs/NavigatorTabsView';
export function NavigatorView(props: NavigatorViewProps): JSX.Element
{
const onRoomForwardEvent = (event: RoomForwardEvent) =>
const [ isVisible, setIsVisible ] = useState(false);
const [ isLoaded, setIsLoaded ] = useState(false);
const [ isLoading, setIsLoading ] = useState(false);
const [ isSearching, setIsSearching ] = useState(false);
const [ topLevelContexts, setTopLevelContexts ] = useState<NavigatorTopLevelContext[]>(null);
const [ topLevelContext, setTopLevelContext ] = useState<NavigatorTopLevelContext>(null);
function hideNavigator(event: MouseEvent = null): void
{
if(event) event.preventDefault();
setIsVisible(false);
}
const onUserInfoEvent = useCallback((event: UserInfoEvent) =>
{
const parser = event.getParser();
SendMessageHook(new NavigatorCategoriesComposer());
SendMessageHook(new NavigatorSettingsComposer());
}, []);
const onRoomForwardEvent = useCallback((event: RoomForwardEvent) =>
{
const parser = event.getParser();
SendMessageHook(new RoomInfoComposer(parser.roomId, false, true));
};
}, []);
const onRoomInfoOwnerEvent = (event: RoomInfoOwnerEvent) =>
const onRoomInfoOwnerEvent = useCallback((event: RoomInfoOwnerEvent) =>
{
const parser = event.getParser();
SendMessageHook(new RoomInfoComposer(parser.roomId, true, false));
};
}, []);
const onRoomInfoEvent = (event: RoomInfoEvent) =>
const onRoomInfoEvent = useCallback((event: RoomInfoEvent) =>
{
const parser = event.getParser();
@ -61,13 +91,94 @@ export function NavigatorView(props: NavigatorViewProps): JSX.Element
// this._data.enteredGuestRoom = parser.data;
// this._data.staffPick = parser.data.roomPicker;
}
};
}, []);
const onNavigatorMetadataEvent = useCallback((event: NavigatorMetadataEvent) =>
{
const parser = event.getParser();
setTopLevelContexts(parser.topLevelContexts);
if(parser.topLevelContexts.length > 0) setTopLevelContext(parser.topLevelContexts[0]);
// clear search
}, []);
const onNavigatorEvent = useCallback((event: NavigatorEvent) =>
{
switch(event.type)
{
case NavigatorEvent.SHOW_NAVIGATOR:
setIsVisible(true);
return;
case NavigatorEvent.TOGGLE_NAVIGATOR:
setIsVisible(value => !value);
return;
}
}, []);
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
{
switch(event.type)
{
case RoomSessionEvent.CREATED:
setIsVisible(false);
return;
}
}, []);
const search = useCallback((value: string = null) =>
{
if(!topLevelContext) return;
sendSearch(topLevelContext.code, '');
}, [ topLevelContext ]);
function sendSearch(code: string, query: string): void
{
SendMessageHook(new NavigatorSearchComposer(code, query));
}
useEffect(() =>
{
if(!isVisible) return;
if(!isLoaded)
{
SendMessageHook(new NavigatorInitComposer());
setIsLoaded(true);
}
else
{
search();
}
}, [ isVisible, isLoaded, search ]);
useUiEvent(NavigatorEvent.SHOW_NAVIGATOR, onNavigatorEvent);
useUiEvent(NavigatorEvent.TOGGLE_NAVIGATOR, onNavigatorEvent);
useRoomSessionManagerEvent(RoomSessionEvent.CREATED, onRoomSessionEvent);
CreateMessageHook(new UserInfoEvent(onUserInfoEvent));
CreateMessageHook(new RoomForwardEvent(onRoomForwardEvent));
CreateMessageHook(new RoomInfoOwnerEvent(onRoomInfoOwnerEvent));
CreateMessageHook(new RoomInfoEvent(onRoomInfoEvent));
CreateMessageHook(new NavigatorMetadataEvent(onNavigatorMetadataEvent));
if(!isVisible) return null;
return (
<div>navigator</div>
<DraggableWindow handle=".drag-handler">
<div className="nitro-navigator d-flex flex-column bg-primary border border-black shadow rounded">
<div className="drag-handler d-flex justify-content-between align-items-center px-3 pt-3">
<div className="h6 m-0">{ LocalizeText(isLoading ? 'navigator.title.is.busy' : 'navigator.title') }</div>
<button type="button" className="close" onClick={ hideNavigator }>
<i className="fas fa-times"></i>
</button>
</div>
<NavigatorTabsView topLevelContext={ topLevelContext } topLevelContexts={ topLevelContexts } setTopLevelContext={ setTopLevelContext } />
</div>
</DraggableWindow>
);
}

View File

@ -0,0 +1 @@
@import './tab/NavigatorTabView';

View File

@ -0,0 +1,19 @@
import { NavigatorTabsViewProps } from './NavigatorTabsView.types';
import { NavigatorTabView } from './tab/NavigatorTabView';
export function NavigatorTabsView(props: NavigatorTabsViewProps): JSX.Element
{
const { topLevelContext = null, topLevelContexts = null, setTopLevelContext = null } = props;
return (
<div className="d-flex flex-column p-3">
{ topLevelContexts && topLevelContexts.length &&
<div className="btn-group w-100 mb-2">
{ topLevelContexts.map((context, index) =>
{
return <NavigatorTabView key={ index } context={ context } isActive={ context === topLevelContext } setTopLevelContext={ setTopLevelContext } />
}) }
</div> }
</div>
);
}

View File

@ -0,0 +1,8 @@
import { NavigatorTopLevelContext } from 'nitro-renderer';
export interface NavigatorTabsViewProps
{
topLevelContext: NavigatorTopLevelContext;
topLevelContexts: NavigatorTopLevelContext[];
setTopLevelContext: (context: NavigatorTopLevelContext) => void;
}

View File

@ -0,0 +1,18 @@
import classNames from 'classnames';
import { MouseEvent } from 'react';
import { LocalizeText } from '../../../../utils/LocalizeText';
import { NavigatorTabViewProps } from './NavigatorTabView.types';
export function NavigatorTabView(props: NavigatorTabViewProps): JSX.Element
{
const { context = null, isActive = false, setTopLevelContext = null } = props;
function onClick(event: MouseEvent): void
{
setTopLevelContext(context);
}
return (
<button type="button" className={classNames({ 'btn': true, 'btn-primary': true, 'active': isActive })} onClick={ onClick }>{ LocalizeText(('navigator.toplevelview.' + context.code)) }</button>
);
}

View File

@ -0,0 +1,8 @@
import { NavigatorTopLevelContext } from 'nitro-renderer';
export interface NavigatorTabViewProps
{
context: NavigatorTopLevelContext;
isActive?: boolean;
setTopLevelContext: (context: NavigatorTopLevelContext) => void;
}

View File

@ -1 +1,5 @@
@import './currency/CurrencyView.scss';
.nitro-purse {
}
@import './currency/CurrencyView';

View File

@ -1,4 +1,4 @@
.nitro-purse-item {
.nitro-currency {
flex: 46%;
margin-right: 3px;

View File

@ -6,7 +6,7 @@ export function CurrencyView(props: CurrencyViewProps): JSX.Element
const { type = -1, amount = 0 } = props;
return (
<div className="d-flex bg-primary rounded shadow border border-black mb-1 p-1 nitro-purse-item">
<div className="d-flex bg-primary rounded shadow border border-black mb-1 p-1 nitro-currency">
<div className="d-flex flex-grow-1 align-items-center justify-content-end">{ amount }</div>
<div className="bg-secondary rounded ml-1"><CurrencyIcon type={ type } /></div>
</div>

View File

@ -0,0 +1,3 @@
.nitro-room-host {
}

View File

@ -31,11 +31,9 @@ export function RoomHostView(props: RoomHostViewProps): JSX.Element
case RoomEngineEvent.INITIALIZED:
SetActiveRoomId(event.roomId);
setRoomSession(session);
setEventDispatcher(new EventDispatcher());
return;
case RoomEngineEvent.DISPOSED:
setRoomSession(null);
setEventDispatcher(null);
return;
}
}, []);
@ -118,10 +116,12 @@ export function RoomHostView(props: RoomHostViewProps): JSX.Element
switch(event.type)
{
case RoomSessionEvent.CREATED:
setEventDispatcher(new EventDispatcher());
StartRoomSession(event.session);
return;
case RoomSessionEvent.ENDED:
setRoomSession(null);
setEventDispatcher(null);
return;
}
}, []);
@ -147,7 +147,7 @@ export function RoomHostView(props: RoomHostViewProps): JSX.Element
useRoomSessionManagerEvent(RoomSessionEvent.ENDED, onRoomSessionEvent);
return (
<div>
<div className="nitro-room-host">
<RoomErrorHandler />
<RoomView events={ eventDispatcher } roomSession={ roomSession } />
</div>

View File

@ -0,0 +1,12 @@
.nitro-room {
z-index: 0;
width: 100%;
height: 100%;
.nitro-room-container {
.client-canvas {
position: absolute;
}
}
}

View File

@ -6,10 +6,7 @@ import { WindowResizeEvent } from '../../api/nitro/room/DispatchResizeEvent';
import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent';
import { GetRoomEngine } from '../../api/nitro/room/GetRoomEngine';
import { RoomViewProps } from './RoomView.types';
import { FurnitureHighScoreView } from './widgets/furniture/high-score/FurnitureHighScoreView';
import { FurnitureMannequinView } from './widgets/furniture/mannequin/FurnitureMannequinView';
import { FurniturePresentView } from './widgets/furniture/present/FurniturePresentView';
import { FurnitureStickieView } from './widgets/furniture/stickie/FurnitureStickieView';
import { FurnitureWidgetsView } from './widgets/furniture/FurnitureWidgetsView';
export function RoomView(props: RoomViewProps): JSX.Element
{
@ -84,16 +81,11 @@ export function RoomView(props: RoomViewProps): JSX.Element
if(!roomSession) return null;
return (
<div>
{ roomSession && <div id="room-view"></div> }
<div className="nitro-room">
{ roomSession && <div id="room-view" className="nitro-room-container"></div> }
{ roomSession && events && roomCanvas &&
createPortal(props.children, document.getElementById('room-view').appendChild(roomCanvas)) &&
<div className="nitro-room-widgets">
<FurnitureHighScoreView events={ events } />
<FurnitureMannequinView events={ events } />
<FurniturePresentView events={ events } />
<FurnitureStickieView events={ events } />
</div> }
<FurnitureWidgetsView events={ events } /> }
</div>
);
}

View File

@ -0,0 +1,19 @@
import { FurnitureWidgetsViewProps } from './FurnitureWidgetsView.types';
import { FurnitureHighScoreView } from './high-score/FurnitureHighScoreView';
import { FurnitureMannequinView } from './mannequin/FurnitureMannequinView';
import { FurniturePresentView } from './present/FurniturePresentView';
import { FurnitureStickieView } from './stickie/FurnitureStickieView';
export function FurnitureWidgetsView(props: FurnitureWidgetsViewProps): JSX.Element
{
const { events } = props;
return (
<div className="nitro-room-widgets">
<FurnitureHighScoreView events={ events } />
<FurnitureMannequinView events={ events } />
<FurniturePresentView events={ events } />
<FurnitureStickieView events={ events } />
</div>
);
}

View File

@ -0,0 +1,6 @@
import { FurnitureWidgetProps } from './FurnitureWidget.types';
export interface FurnitureWidgetsViewProps extends FurnitureWidgetProps
{
}

View File

@ -1,14 +1,16 @@
import { RoomInfoComposer } from 'nitro-renderer';
import { UserInfoEvent } from 'nitro-renderer/src/nitro/communication/messages/incoming/user/data/UserInfoEvent';
import { UserInfoDataParser } from 'nitro-renderer/src/nitro/communication/messages/parser/user/data/UserInfoDataParser';
import { KeyboardEvent, useState } from 'react';
import { KeyboardEvent, MouseEvent, useState } from 'react';
import { NavigatorEvent } from '../../events';
import { dispatchUiEvent } from '../../hooks/events/ui/ui-event';
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
import { AvatarImageView } from '../avatar-image/AvatarImageView';
import { ToolbarViewProps } from './ToolbarView.types';
import { ToolbarViewItems, ToolbarViewProps } from './ToolbarView.types';
export function ToolbarView(props: ToolbarViewProps): JSX.Element
{
const [ isInRoom, setIsInRoom ] = useState(false);
const { isInRoom } = props;
const [ userInfo, setUserInfo ] = useState<UserInfoDataParser>(null);
@ -34,6 +36,18 @@ export function ToolbarView(props: ToolbarViewProps): JSX.Element
SendMessageHook(new RoomInfoComposer(parseInt(roomId), false, true));
}
function handleToolbarItemClick(event: MouseEvent, item: string): void
{
event.preventDefault();
switch(item)
{
case ToolbarViewItems.NAVIGATOR_ITEM:
dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_NAVIGATOR));
return;
}
}
CreateMessageHook(new UserInfoEvent(onUserInfoEvent));
return (
@ -48,7 +62,7 @@ export function ToolbarView(props: ToolbarViewProps): JSX.Element
<li className="list-group-item">
<i className="icon icon-house"></i>
</li>) }
<li className="list-group-item">
<li className="list-group-item" onClick={ event => handleToolbarItemClick(event, ToolbarViewItems.NAVIGATOR_ITEM) }>
<i className="icon icon-rooms"></i>
</li>
<li className="list-group-item">

View File

@ -1,4 +1,9 @@
export interface ToolbarViewProps
{
isInRoom: boolean;
}
export class ToolbarViewItems
{
public static NAVIGATOR_ITEM: string = 'TVI_NAVIGATOR_ITEM';
}