Merge branch 'dev' into feature/choosers

This commit is contained in:
Bill 2021-08-13 04:13:53 -04:00
commit 24215b9c5c
61 changed files with 856 additions and 311 deletions

112
package-lock.json generated
View File

@ -4529,11 +4529,6 @@
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
"integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=" "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU="
}, },
"animate.css": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/animate.css/-/animate.css-4.1.1.tgz",
"integrity": "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="
},
"ansi-colors": { "ansi-colors": {
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
@ -5924,6 +5919,11 @@
"wrap-ansi": "^6.2.0" "wrap-ansi": "^6.2.0"
} }
}, },
"clsx": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
},
"co": { "co": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
@ -6969,11 +6969,6 @@
"integrity": "sha512-ml3lJIq9YjUfM9TUnEPvEYWFSwivwIGBPKpewX7tii7fwCazA8yCioGdqQcNsItPpfFvSJ3VIdMQPj60LJhcQA==", "integrity": "sha512-ml3lJIq9YjUfM9TUnEPvEYWFSwivwIGBPKpewX7tii7fwCazA8yCioGdqQcNsItPpfFvSJ3VIdMQPj60LJhcQA==",
"dev": true "dev": true
}, },
"dom-align": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/dom-align/-/dom-align-1.12.2.tgz",
"integrity": "sha512-pHuazgqrsTFrGU2WLDdXxCFabkdQDx72ddkraZNih1KsMcN5qsRSTR9O4VJRlwTPCPb5COYg3LOfiMHHcPInHg=="
},
"dom-converter": { "dom-converter": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
@ -14588,71 +14583,6 @@
} }
} }
}, },
"rc-align": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/rc-align/-/rc-align-4.0.9.tgz",
"integrity": "sha512-myAM2R4qoB6LqBul0leaqY8gFaiECDJ3MtQDmzDo9xM9NRT/04TvWOYd2YHU9zvGzqk9QXF6S9/MifzSKDZeMw==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "2.x",
"dom-align": "^1.7.0",
"rc-util": "^5.3.0",
"resize-observer-polyfill": "^1.5.1"
}
},
"rc-motion": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/rc-motion/-/rc-motion-2.4.4.tgz",
"integrity": "sha512-ms7n1+/TZQBS0Ydd2Q5P4+wJTSOrhIrwNxLXCZpR7Fa3/oac7Yi803HDALc2hLAKaCTQtw9LmQeB58zcwOsqlQ==",
"requires": {
"@babel/runtime": "^7.11.1",
"classnames": "^2.2.1",
"rc-util": "^5.2.1"
}
},
"rc-slider": {
"version": "9.7.2",
"resolved": "https://registry.npmjs.org/rc-slider/-/rc-slider-9.7.2.tgz",
"integrity": "sha512-mVaLRpDo6otasBs6yVnG02ykI3K6hIrLTNfT5eyaqduFv95UODI9PDS6fWuVVehVpdS4ENgOSwsTjrPVun+k9g==",
"requires": {
"@babel/runtime": "^7.10.1",
"classnames": "^2.2.5",
"rc-tooltip": "^5.0.1",
"rc-util": "^5.0.0",
"shallowequal": "^1.1.0"
}
},
"rc-tooltip": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/rc-tooltip/-/rc-tooltip-5.1.1.tgz",
"integrity": "sha512-alt8eGMJulio6+4/uDm7nvV+rJq9bsfxFDCI0ljPdbuoygUscbsMYb6EQgwib/uqsXQUvzk+S7A59uYHmEgmDA==",
"requires": {
"@babel/runtime": "^7.11.2",
"rc-trigger": "^5.0.0"
}
},
"rc-trigger": {
"version": "5.2.9",
"resolved": "https://registry.npmjs.org/rc-trigger/-/rc-trigger-5.2.9.tgz",
"integrity": "sha512-0Bxsh2Xe+etejMn73am+jZBcOpsueAZiEKLiGoDfA0fvm/JHLNOiiww3zJ0qgyPOTmbYxhsxFcGOZu+VcbaZhQ==",
"requires": {
"@babel/runtime": "^7.11.2",
"classnames": "^2.2.6",
"rc-align": "^4.0.0",
"rc-motion": "^2.0.0",
"rc-util": "^5.5.0"
}
},
"rc-util": {
"version": "5.13.1",
"resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.13.1.tgz",
"integrity": "sha512-Dws2tjXBBihfjVQFlG5JzZ/5O3Wutctm0W94Wb1+M7GD2roWJPrQdSa4AkWm2pn0Ms32zoVPPkWodFeAYZPLfA==",
"requires": {
"@babel/runtime": "^7.12.5",
"react-is": "^16.12.0",
"shallowequal": "^1.1.0"
}
},
"react": { "react": {
"version": "17.0.2", "version": "17.0.2",
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
@ -14834,15 +14764,6 @@
"scheduler": "^0.20.2" "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": { "react-error-overlay": {
"version": "6.0.9", "version": "6.0.9",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
@ -14960,6 +14881,19 @@
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"react-virtualized": {
"version": "9.22.3",
"resolved": "https://registry.npmjs.org/react-virtualized/-/react-virtualized-9.22.3.tgz",
"integrity": "sha512-MKovKMxWTcwPSxE1kK1HcheQTWfuCxAuBoSTf2gwyMM21NdX/PXUhnoP8Uc5dRKd+nKm8v41R36OellhdCpkrw==",
"requires": {
"@babel/runtime": "^7.7.2",
"clsx": "^1.0.4",
"dom-helpers": "^5.1.3",
"loose-envify": "^1.4.0",
"prop-types": "^15.7.2",
"react-lifecycles-compat": "^3.0.4"
}
},
"read-pkg": { "read-pkg": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@ -15310,11 +15244,6 @@
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8="
}, },
"resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
},
"resolve": { "resolve": {
"version": "1.18.1", "version": "1.18.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
@ -16105,11 +16034,6 @@
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
}, },
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
},
"shebang-command": { "shebang-command": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",

View File

@ -4,14 +4,11 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@nitrots/nitro-renderer": "file:../nitro-renderer", "@nitrots/nitro-renderer": "file:../nitro-renderer",
"animate.css": "^4.1.1",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"node-sass": "^5.0.0", "node-sass": "^5.0.0",
"rc-slider": "^9.7.2",
"react": "^17.0.2", "react": "^17.0.2",
"react-bootstrap": "^2.0.0-alpha.2", "react-bootstrap": "^2.0.0-alpha.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draggable": "^4.4.3",
"react-scripts": "4.0.3", "react-scripts": "4.0.3",
"react-slider": "^1.3.1", "react-slider": "^1.3.1",
"react-transition-group": "^4.4.2", "react-transition-group": "^4.4.2",

View File

@ -1,57 +1,10 @@
{ {
"image.library.notifications.url": "${image.library.url}notifications/%image%.png", "image.library.notifications.url": "${image.library.url}notifications/%image%.png",
"achievements.images.url": "${image.library.url}Quests/%image%.png", "achievements.images.url": "${image.library.url}Quests/%image%.png",
"camera.url": "https://nitro.nitrots.co/camera",
"thumbnails.url": "https://nitro.nitrots.co/camera/thumbnail/%thumbnail%.png", "thumbnails.url": "https://nitro.nitrots.co/camera/thumbnail/%thumbnail%.png",
"url.prefix": "http://localhost:3000", "url.prefix": "http://localhost:3000",
"chat.viewer.height.percentage": 0.40, "chat.viewer.height.percentage": 0.40,
"auth.system.enabled": true,
"auth.system.http.enabled": true,
"auth.system.http.endpoint.login": "https://reqres.in/api/posts",
"auth.system.http.endpoint.register": "https://reqres.in/api/posts",
"auth.system.sso_field_name": "username",
"auth.system.recaptcha.public_key": "",
"auth.system.recaptcha.field_name": "recaptcha",
"auth.system.login.fields": [
{
"name": "username",
"label": "Username",
"type": "text",
"col": 12
},
{
"name": "password",
"label": "Password",
"type": "password",
"col": 12
}
],
"auth.system.register.enabled": true,
"auth.system.register.fields": [
{
"name": "username",
"label": "Username",
"type": "text",
"col": 12
},
{
"name": "email",
"label": "Email Address",
"type": "email",
"col": 12
},
{
"name": "password",
"label": "Password",
"type": "password",
"col": 6
},
{
"name": "confirm_password",
"label": "Confirm Password",
"type": "password",
"col": 6
}
],
"navigator.slider.enabled": true, "navigator.slider.enabled": true,
"navigator.slider.content": [ "navigator.slider.content": [
{ {

View File

@ -1,4 +1,4 @@
import { ConfigurationEvent, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, RoomEngineEvent, WebGL } from '@nitrots/nitro-renderer'; import { ConfigurationEvent, LegacyExternalInterface, Nitro, NitroCommunicationDemoEvent, NitroEvent, NitroLocalizationEvent, NitroVersion, RoomEngineEvent, WebGL } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetCommunication, GetConfiguration, GetNitroInstance } from './api'; import { GetCommunication, GetConfiguration, GetNitroInstance } from './api';
import { useConfigurationEvent } from './hooks/events/core/configuration/configuration-event'; import { useConfigurationEvent } from './hooks/events/core/configuration/configuration-event';
@ -17,7 +17,11 @@ export const App: FC<{}> = props =>
//@ts-ignore //@ts-ignore
if(!NitroConfig) throw new Error('NitroConfig is not defined!'); if(!NitroConfig) throw new Error('NitroConfig is not defined!');
if(!GetNitroInstance()) Nitro.bootstrap(); if(!GetNitroInstance())
{
NitroVersion.UI_VERSION = '2.0.0';
Nitro.bootstrap();
}
const getPreloadAssetUrls = useCallback(() => const getPreloadAssetUrls = useCallback(() =>
{ {

View File

@ -1,6 +0,0 @@
import { GetRoomSessionManager } from '../nitro';
export function VisitRoom(roomId: number, password: string = null): void
{
GetRoomSessionManager().createSession(roomId, password);
}

View File

@ -0,0 +1,6 @@
import { GetRoomSessionManager } from './GetRoomSessionManager';
export function CreateRoomSession(roomId: number, password: string = null): void
{
GetRoomSessionManager().createSession(roomId, password);
}

View File

@ -0,0 +1,7 @@
import { DesktopViewComposer } from '@nitrots/nitro-renderer';
import { SendMessageHook } from '../../../hooks';
export function GoToDesktop(): void
{
SendMessageHook(new DesktopViewComposer());
}

View File

@ -1,4 +1,5 @@
export * from './CanManipulateFurniture'; export * from './CanManipulateFurniture';
export * from './CreateRoomSession';
export * from './GetCanStandUp'; export * from './GetCanStandUp';
export * from './GetCanUseExpression'; export * from './GetCanUseExpression';
export * from './GetClubMemberLevel'; export * from './GetClubMemberLevel';
@ -9,6 +10,7 @@ export * from './GetProductDataForLocalization';
export * from './GetRoomSession'; export * from './GetRoomSession';
export * from './GetRoomSessionManager'; export * from './GetRoomSessionManager';
export * from './GetSessionDataManager'; export * from './GetSessionDataManager';
export * from './GoToDesktop';
export * from './HasHabboClub'; export * from './HasHabboClub';
export * from './HasHabboVip'; export * from './HasHabboVip';
export * from './IsOwnerOfFurniture'; export * from './IsOwnerOfFurniture';

View File

@ -0,0 +1,27 @@
import { NitroEvent, RoomDataParser } from '@nitrots/nitro-renderer';
export class UpdateDoorStateEvent extends NitroEvent
{
public static START_DOORBELL: string = 'UDSE_START_DOORBELL';
public static START_PASSWORD: string = 'UDSE_START_PASSWORD';
public static STATE_PENDING_SERVER: string = 'UDSE_STATE_PENDING_SERVER';
public static UPDATE_STATE: string = 'UDSE_UPDATE_STATE';
public static STATE_WAITING: string = 'UDSE_STATE_WAITING';
public static STATE_NO_ANSWER: string = 'UDSE_STATE_NO_ANSWER';
public static STATE_WRONG_PASSWORD: string = 'UDSE_STATE_WRONG_PASSWORD';
public static STATE_ACCEPTED: string = 'UDSE_STATE_ACCEPTED';
private _roomData: RoomDataParser
constructor(type: string, roomData: RoomDataParser = null)
{
super(type);
this._roomData = roomData;
}
public get roomData(): RoomDataParser
{
return this._roomData;
}
}

View File

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

View File

@ -1,6 +1,4 @@
@import url('https://fonts.googleapis.com/css2?family=Ubuntu+Condensed:wght@300;400;500&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Ubuntu+Condensed:wght@300;400;500&display=swap');
@import '../node_modules/animate.css/animate.css';
@import '../node_modules/rc-slider/assets/index.css';
@import './assets/styles'; @import './assets/styles';
html, html,

View File

@ -5,12 +5,12 @@ import { NitroCardViewProps } from './NitroCardView.types';
export const NitroCardView: FC<NitroCardViewProps> = props => export const NitroCardView: FC<NitroCardViewProps> = props =>
{ {
const { className = '', disableDrag = false, simple = false, theme = 'primary', children = null } = props; const { className = '', simple = false, theme = 'primary', children = null, ...rest } = props;
return ( return (
<NitroCardContextProvider value={ { theme, simple } }> <NitroCardContextProvider value={ { theme, simple } }>
<div className="nitro-card-responsive"> <div className="nitro-card-responsive">
<DraggableWindow handle=".drag-handler" disableDrag= { disableDrag }> <DraggableWindow { ...rest }>
<div className={ 'nitro-card d-flex flex-column rounded border shadow overflow-hidden ' + className }> <div className={ 'nitro-card d-flex flex-column rounded border shadow overflow-hidden ' + className }>
{ children } { children }
</div> </div>

View File

@ -1,7 +1,8 @@
export interface NitroCardViewProps import { DraggableWindowProps } from '../draggable-window';
export interface NitroCardViewProps extends DraggableWindowProps
{ {
className?: string; className?: string;
disableDrag?: boolean;
simple?: boolean; simple?: boolean;
theme?: string; theme?: string;
} }

View File

@ -1,19 +1,27 @@
import { FC, MouseEvent, useCallback, useEffect, useMemo, useRef } from 'react'; import { MouseEventType } from '@nitrots/nitro-renderer';
import Draggable from 'react-draggable'; import { FC, Key, MouseEvent as ReactMouseEvent, useCallback, useEffect, useRef, useState } from 'react';
import { DraggableWindowProps } from './DraggableWindow.types'; import { DraggableWindowPosition, DraggableWindowProps } from './DraggableWindow.types';
const currentWindows: HTMLDivElement[] = []; const CURRENT_WINDOWS: HTMLElement[] = [];
const POS_MEMORY: Map<Key, { x: number, y: number }> = new Map();
const BOUNDS_THRESHOLD_TOP: number = 0;
const BOUNDS_THRESHOLD_LEFT: number = 0;
export const DraggableWindow: FC<DraggableWindowProps> = props => export const DraggableWindow: FC<DraggableWindowProps> = props =>
{ {
const { disableDrag = false, noCenter = false, handle = '.drag-handler', draggableOptions = {}, children = null } = props; const { uniqueKey = null, handleSelector = '.drag-handler', position = DraggableWindowPosition.CENTER, disableDrag = false, children = null } = props;
const [ delta, setDelta ] = useState<{ x: number, y: number }>(null);
const [ offset, setOffset ] = useState<{ x: number, y: number }>(null);
const [ start, setStart ] = useState<{ x: number, y: number }>({ x: 0, y: 0 });
const [ isDragging, setIsDragging ] = useState(false);
const [ dragHandler, setDragHandler ] = useState<HTMLElement>(null);
const elementRef = useRef<HTMLDivElement>(); const elementRef = useRef<HTMLDivElement>();
const bringToTop = useCallback(() => const bringToTop = useCallback(() =>
{ {
let zIndex = 400; let zIndex = 400;
for(const existingWindow of currentWindows) for(const existingWindow of CURRENT_WINDOWS)
{ {
zIndex += 1; zIndex += 1;
@ -21,69 +29,170 @@ export const DraggableWindow: FC<DraggableWindowProps> = props =>
} }
}, []); }, []);
const onMouseDown = useCallback((event: MouseEvent) => const onMouseDown = useCallback((event: ReactMouseEvent) =>
{ {
const index = currentWindows.indexOf(elementRef.current); const index = CURRENT_WINDOWS.indexOf(elementRef.current);
if(index === -1) if(index === -1)
{ {
currentWindows.push(elementRef.current); CURRENT_WINDOWS.push(elementRef.current);
} }
else if(index === (currentWindows.length - 1)) return; else if(index === (CURRENT_WINDOWS.length - 1)) return;
else if(index >= 0) else if(index >= 0)
{ {
currentWindows.splice(index, 1); CURRENT_WINDOWS.splice(index, 1);
currentWindows.push(elementRef.current); CURRENT_WINDOWS.push(elementRef.current);
} }
bringToTop(); bringToTop();
}, [ bringToTop ]); }, [ bringToTop ]);
const onDragMouseDown = useCallback((event: MouseEvent) =>
{
setStart({ x: event.clientX, y: event.clientY });
setIsDragging(true);
}, []);
const onDragMouseMove = useCallback((event: MouseEvent) =>
{
setDelta({ x: (event.clientX - start.x), y: (event.clientY - start.y) });
}, [ start ]);
const onDragMouseUp = useCallback((event: MouseEvent) =>
{
if(!elementRef.current || !dragHandler) return;
let offsetX = (offset.x + delta.x);
let offsetY = (offset.y + delta.y);
const left = elementRef.current.offsetLeft + offsetX;
const top = elementRef.current.offsetTop + offsetY;
if(top < BOUNDS_THRESHOLD_TOP)
{
offsetY = -elementRef.current.offsetTop;
}
else if((top + dragHandler.offsetHeight) >= (document.body.offsetHeight - BOUNDS_THRESHOLD_TOP))
{
offsetY = (document.body.offsetHeight - elementRef.current.offsetHeight) - elementRef.current.offsetTop;
}
if((left + elementRef.current.offsetWidth) < BOUNDS_THRESHOLD_LEFT)
{
offsetX = -elementRef.current.offsetLeft;
}
else if(left >= (document.body.offsetWidth - BOUNDS_THRESHOLD_LEFT))
{
offsetX = (document.body.offsetWidth - elementRef.current.offsetWidth) - elementRef.current.offsetLeft;
}
setDelta({ x: 0, y: 0 });
setOffset({ x: offsetX, y: offsetY });
setIsDragging(false);
if(uniqueKey !== null) POS_MEMORY.set(uniqueKey, { x: offsetX, y: offsetY });
}, [ dragHandler, delta, offset, uniqueKey ]);
useEffect(() => useEffect(() =>
{ {
if(!elementRef) return; const element = (elementRef.current as HTMLElement);
const element = elementRef.current; if(!element) return;
currentWindows.push(element); CURRENT_WINDOWS.push(element);
bringToTop(); bringToTop();
if(!noCenter) if(!disableDrag)
{ {
const left = ((document.body.clientWidth / 2) - (element.clientWidth / 2)); const handle = (element.querySelector(handleSelector) as HTMLElement);
const top = ((document.body.clientHeight / 2) - (element.clientHeight / 2));
element.style.left = `${ left }px`; if(handle) setDragHandler(handle);
element.style.top = `${ top }px`;
}
else
{
element.style.left = `0px`;
element.style.top = `0px`;
} }
element.style.visibility = 'visible'; let offsetX = 0;
let offsetY = 0;
switch(position)
{
case DraggableWindowPosition.TOP_CENTER:
element.style.top = '50px';
element.style.left = `calc(50vw - ${ (element.offsetWidth / 2) }px)`;
break;
case DraggableWindowPosition.CENTER:
element.style.top = `calc(50vh - ${ (element.offsetHeight / 2) }px)`;
element.style.left = `calc(50vw - ${ (element.offsetWidth / 2) }px)`;
break;
}
if(uniqueKey !== null)
{
const memory = POS_MEMORY.get(uniqueKey);
if(memory)
{
offsetX = memory.x;
offsetY = memory.y;
}
}
setDelta({ x: 0, y: 0});
setOffset({ x: offsetX, y: offsetY });
return () => return () =>
{ {
const index = currentWindows.indexOf(element); const index = CURRENT_WINDOWS.indexOf(element);
if(index >= 0) currentWindows.splice(index, 1); if(index >= 0) CURRENT_WINDOWS.splice(index, 1);
} }
}, [ elementRef, noCenter, bringToTop ]); }, [ handleSelector, position, uniqueKey, disableDrag, bringToTop ]);
const getWindowContent = useMemo(() => useEffect(() =>
{ {
if(!offset && !delta) return;
const element = (elementRef.current as HTMLElement);
if(!element) return;
element.style.transform = `translate(${ offset.x + delta.x }px, ${ offset.y + delta.y }px)`;
element.style.visibility = 'visible';
}, [ offset, delta ]);
useEffect(() =>
{
if(!dragHandler) return;
dragHandler.addEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown);
return () =>
{
dragHandler.removeEventListener(MouseEventType.MOUSE_DOWN, onDragMouseDown);
}
}, [ dragHandler, onDragMouseDown ]);
useEffect(() =>
{
if(!isDragging) return;
document.addEventListener(MouseEventType.MOUSE_UP, onDragMouseUp);
document.addEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove);
return () =>
{
document.removeEventListener(MouseEventType.MOUSE_UP, onDragMouseUp);
document.removeEventListener(MouseEventType.MOUSE_MOVE, onDragMouseMove);
}
}, [ isDragging, onDragMouseUp, onDragMouseMove ]);
return ( return (
<div ref={ elementRef } className="position-absolute draggable-window" onMouseDownCapture={ onMouseDown }> <div ref={ elementRef } className="position-absolute draggable-window" onMouseDownCapture={ onMouseDown }>
{ children } { children }
</div> </div>
); );
}, [ children, onMouseDown ]);
return disableDrag ? getWindowContent : <Draggable handle={ handle } { ...draggableOptions }>{ getWindowContent }</Draggable>;
} }

View File

@ -1,11 +1,16 @@
import { ReactNode } from 'react'; import { Key } from 'react';
import { DraggableProps } from 'react-draggable';
export interface DraggableWindowProps export interface DraggableWindowProps
{ {
handle?: string; uniqueKey?: Key;
draggableOptions?: Partial<DraggableProps>; handleSelector?: string;
position?: string;
disableDrag?: boolean; disableDrag?: boolean;
noCenter?: boolean; }
children?: ReactNode;
export class DraggableWindowPosition
{
public static CENTER: string = 'DWP_CENTER';
public static TOP_CENTER: string = 'DWP_TOP_CENTER';
public static NOTHING: string = 'DWP_NOTHING';
} }

View File

@ -25,7 +25,7 @@ export const NitroLayoutMiniCameraView: FC<NitroLayoutMiniCameraViewProps> = pro
}, [ roomId, getCameraBounds, textureReceiver ]); }, [ roomId, getCameraBounds, textureReceiver ]);
return ( return (
<DraggableWindow handle=".nitro-room-thumbnail-camera"> <DraggableWindow handleSelector=".nitro-room-thumbnail-camera">
<div className="nitro-room-thumbnail-camera px-2"> <div className="nitro-room-thumbnail-camera px-2">
<div ref={ elementRef } className={ 'camera-frame' } /> <div ref={ elementRef } className={ 'camera-frame' } />
<div className="d-flex align-items-end h-100 pb-2"> <div className="d-flex align-items-end h-100 pb-2">

View File

@ -8,7 +8,7 @@ export const NitroLayoutTrophyView: FC<NitroLayoutTrophyViewProps> = props =>
const { color = '', message = '', date = '', senderName = '', onCloseClick = null } = props; const { color = '', message = '', date = '', senderName = '', onCloseClick = null } = props;
return ( return (
<DraggableWindow handle=".drag-handler"> <DraggableWindow handleSelector=".drag-handler">
<div className={ `nitro-layout-trophy trophy-${ color }` }> <div className={ `nitro-layout-trophy trophy-${ color }` }>
<div className="trophy-header drag-handler"> <div className="trophy-header drag-handler">
<div className="float-end trophy-close" onClick={ onCloseClick }></div> <div className="float-end trophy-close" onClick={ onCloseClick }></div>

View File

@ -40,7 +40,7 @@ export const AchievementsView: FC<AchievementsViewProps> = props =>
<AchievementsContextProvider value={ { achievementsState, dispatchAchievementsState } }> <AchievementsContextProvider value={ { achievementsState, dispatchAchievementsState } }>
<AchievementsMessageHandler /> <AchievementsMessageHandler />
{ isVisible && { isVisible &&
<NitroCardView className="nitro-achievements"> <NitroCardView uniqueKey="achievements" className="nitro-achievements">
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } /> <NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
<NitroCardContentView> <NitroCardContentView>
<div className="row"> <div className="row">

View File

@ -276,7 +276,7 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
if(!isVisible || !figureData) return null; if(!isVisible || !figureData) return null;
return ( return (
<NitroCardView className="nitro-avatar-editor"> <NitroCardView uniqueKey="avatar-editor" className="nitro-avatar-editor">
<NitroCardHeaderView headerText={ LocalizeText('avatareditor.title') } onCloseClick={ event => setIsVisible(false) } /> <NitroCardHeaderView headerText={ LocalizeText('avatareditor.title') } onCloseClick={ event => setIsVisible(false) } />
<NitroCardTabsView> <NitroCardTabsView>
{ categories && (categories.size > 0) && Array.from(categories.keys()).map(category => { categories && (categories.size > 0) && Array.from(categories.keys()).map(category =>

View File

@ -190,7 +190,7 @@ export const CatalogView: FC<CatalogViewProps> = props =>
<CatalogContextProvider value={ { catalogState, dispatchCatalogState } }> <CatalogContextProvider value={ { catalogState, dispatchCatalogState } }>
<CatalogMessageHandler /> <CatalogMessageHandler />
{ isVisible && { isVisible &&
<NitroCardView className="nitro-catalog"> <NitroCardView uniqueKey="catalog" className="nitro-catalog">
<NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => { saveActivePages(); setIsVisible(false); } } /> <NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => { saveActivePages(); setIsVisible(false); } } />
<NitroCardTabsView> <NitroCardTabsView>
{ root && root.children.length && root.children.map((page, index) => { root && root.children.length && root.children.map((page, index) =>

View File

@ -84,7 +84,7 @@ export const FriendListView: FC<FriendListViewProps> = props =>
<FriendListMessageHandler /> <FriendListMessageHandler />
{ isReady && createPortal(<FriendBarView />, document.getElementById('toolbar-friend-bar-container')) } { isReady && createPortal(<FriendBarView />, document.getElementById('toolbar-friend-bar-container')) }
{ isVisible && { isVisible &&
<NitroCardView className="nitro-friend-list"> <NitroCardView uniqueKey="friend-list" className="nitro-friend-list">
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ event => setIsVisible(false) } /> <NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ event => setIsVisible(false) } />
<NitroCardContentView> <NitroCardContentView>
<div className="text-black fw-bold">{ LocalizeText('friendlist.search.friendscaption') }</div> <div className="text-black fw-bold">{ LocalizeText('friendlist.search.friendscaption') }</div>

View File

@ -197,7 +197,7 @@ export const InventoryView: FC<InventoryViewProps> = props =>
<InventoryContextProvider value={ { furnitureState, dispatchFurnitureState, botState, dispatchBotState, petState, dispatchPetState, badgeState, dispatchBadgeState, unseenTracker } }> <InventoryContextProvider value={ { furnitureState, dispatchFurnitureState, botState, dispatchBotState, petState, dispatchPetState, badgeState, dispatchBadgeState, unseenTracker } }>
<InventoryMessageHandler /> <InventoryMessageHandler />
{ isVisible && { isVisible &&
<NitroCardView className="nitro-inventory"> <NitroCardView uniqueKey={ 'inventory' } className="nitro-inventory">
<NitroCardHeaderView headerText={ LocalizeText('inventory.title') } onCloseClick={ close } /> <NitroCardHeaderView headerText={ LocalizeText('inventory.title') } onCloseClick={ close } />
{ !furnitureState.tradeData && { !furnitureState.tradeData &&
<> <>

View File

@ -161,7 +161,7 @@ export const ModToolsView: FC<ModToolsViewProps> = props =>
return ( return (
<ModToolsContextProvider value={ { modToolsState, dispatchModToolsState } }> <ModToolsContextProvider value={ { modToolsState, dispatchModToolsState } }>
{ isVisible && { isVisible &&
<NitroCardView className="nitro-mod-tools" simple={ true }> <NitroCardView uniqueKey="mod-tools" className="nitro-mod-tools" simple={ true }>
<NitroCardHeaderView headerText={ "Mod Tools" } onCloseClick={ event => setIsVisible(false) } /> <NitroCardHeaderView headerText={ "Mod Tools" } onCloseClick={ event => setIsVisible(false) } />
<NitroCardContentView className="text-black"> <NitroCardContentView className="text-black">
<button className="btn btn-primary w-100 mb-2" onClick={ () => handleClick('toggle_room') } disabled={ !currentRoomId }><i className="fas fa-home"></i> Room Tool</button> <button className="btn btn-primary w-100 mb-2" onClick={ () => handleClick('toggle_room') } disabled={ !currentRoomId }><i className="fas fa-home"></i> Room Tool</button>

View File

@ -1,7 +1,8 @@
import { GenericErrorEvent, NavigatorCategoriesComposer, NavigatorCategoriesEvent, NavigatorHomeRoomEvent, NavigatorMetadataEvent, NavigatorSearchEvent, NavigatorSettingsComposer, RoomCreatedEvent, RoomDataParser, RoomDoorbellAcceptedEvent, RoomDoorbellEvent, RoomForwardEvent, RoomInfoComposer, RoomInfoEvent, RoomInfoOwnerEvent, RoomSettingsUpdatedEvent, UserInfoEvent } from '@nitrots/nitro-renderer'; import { GenericErrorEvent, NavigatorCategoriesComposer, NavigatorCategoriesEvent, NavigatorHomeRoomEvent, NavigatorMetadataEvent, NavigatorSearchEvent, NavigatorSettingsComposer, RoomCreatedEvent, RoomDataParser, RoomDoorbellAcceptedEvent, RoomDoorbellEvent, RoomDoorbellRejectedEvent, RoomForwardEvent, RoomInfoComposer, RoomInfoEvent, RoomInfoOwnerEvent, RoomSettingsUpdatedEvent, UserInfoEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback } from 'react'; import { FC, useCallback } from 'react';
import { GetRoomSessionManager, GetSessionDataManager } from '../../api'; import { CreateRoomSession, GetSessionDataManager } from '../../api';
import { VisitRoom } from '../../api/navigator/VisitRoom'; import { UpdateDoorStateEvent } from '../../events';
import { dispatchUiEvent } from '../../hooks';
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event'; import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
import { useNavigatorContext } from './context/NavigatorContext'; import { useNavigatorContext } from './context/NavigatorContext';
import { NavigatorMessageHandlerProps } from './NavigatorMessageHandler.types'; import { NavigatorMessageHandlerProps } from './NavigatorMessageHandler.types';
@ -65,13 +66,15 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
switch(parser.data.doorMode) switch(parser.data.doorMode)
{ {
case RoomDataParser.DOORBELL_STATE: case RoomDataParser.DOORBELL_STATE:
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_DOORBELL, parser.data));
return;
case RoomDataParser.PASSWORD_STATE: case RoomDataParser.PASSWORD_STATE:
//showLock(); dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_PASSWORD, parser.data));
return; return;
} }
} }
GetRoomSessionManager().createSession(parser.data.roomId); CreateRoomSession(parser.data.roomId);
} }
else else
{ {
@ -91,32 +94,42 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
{ {
const parser = event.getParser(); const parser = event.getParser();
// if(!parser.userName || (parser.userName.length === 0)) if(!parser.userName || (parser.userName.length === 0))
// { {
// showLock(NavigatorLockViewStage.WAITING); dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_WAITING));
// } }
}, []); }, []);
const onRoomDoorbellAcceptedEvent = useCallback((event: RoomDoorbellAcceptedEvent) => const onRoomDoorbellAcceptedEvent = useCallback((event: RoomDoorbellAcceptedEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
// if(!parser.userName || (parser.userName.length === 0)) if(!parser.userName || (parser.userName.length === 0))
// { {
// hideLock(); dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_ACCEPTED));
// } }
}, []);
const onRoomDoorbellRejectedEvent = useCallback((event: RoomDoorbellRejectedEvent) =>
{
const parser = event.getParser();
if(!parser.userName || (parser.userName.length === 0))
{
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_NO_ANSWER));
}
}, []); }, []);
const onGenericErrorEvent = useCallback((event: GenericErrorEvent) => const onGenericErrorEvent = useCallback((event: GenericErrorEvent) =>
{ {
const parser = event.getParser(); const parser = event.getParser();
// switch(parser.errorCode) switch(parser.errorCode)
// { {
// case -100002: case -100002:
// showLock(NavigatorLockViewStage.FAILED); dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_WRONG_PASSWORD));
// break; break;
// } }
}, []); }, []);
const onNavigatorMetadataEvent = useCallback((event: NavigatorMetadataEvent) => const onNavigatorMetadataEvent = useCallback((event: NavigatorMetadataEvent) =>
@ -159,7 +172,7 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
{ {
const parser = event.getParser(); const parser = event.getParser();
VisitRoom(parser.roomId); CreateRoomSession(parser.roomId);
}, []); }, []);
const onNavigatorHomeRoomEvent = useCallback((event: NavigatorHomeRoomEvent) => const onNavigatorHomeRoomEvent = useCallback((event: NavigatorHomeRoomEvent) =>
@ -187,6 +200,7 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
CreateMessageHook(RoomInfoEvent, onRoomInfoEvent); CreateMessageHook(RoomInfoEvent, onRoomInfoEvent);
CreateMessageHook(RoomDoorbellEvent, onRoomDoorbellEvent); CreateMessageHook(RoomDoorbellEvent, onRoomDoorbellEvent);
CreateMessageHook(RoomDoorbellAcceptedEvent, onRoomDoorbellAcceptedEvent); CreateMessageHook(RoomDoorbellAcceptedEvent, onRoomDoorbellAcceptedEvent);
CreateMessageHook(RoomDoorbellRejectedEvent, onRoomDoorbellRejectedEvent);
CreateMessageHook(GenericErrorEvent, onGenericErrorEvent); CreateMessageHook(GenericErrorEvent, onGenericErrorEvent);
CreateMessageHook(NavigatorMetadataEvent, onNavigatorMetadataEvent); CreateMessageHook(NavigatorMetadataEvent, onNavigatorMetadataEvent);
CreateMessageHook(NavigatorSearchEvent, onNavigatorSearchEvent); CreateMessageHook(NavigatorSearchEvent, onNavigatorSearchEvent);

View File

@ -8,4 +8,22 @@
} }
} }
.nitro-navigator-doorbell {
width: 250px;
.content-area {
min-height: 143px;
height: 143px;
}
}
.nitro-navigator-password {
width: 250px;
.content-area {
min-height: 218px;
height: 218px;
}
}
@import './views/NavigatorViews'; @import './views/NavigatorViews';

View File

@ -1,8 +1,8 @@
import { ILinkEventTracker, NavigatorInitComposer, NavigatorSearchComposer, RoomSessionEvent } from '@nitrots/nitro-renderer'; import { ILinkEventTracker, NavigatorInitComposer, NavigatorSearchComposer, RoomDataParser, RoomSessionEvent } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useReducer, useState } from 'react'; import { FC, useCallback, useEffect, useMemo, useReducer, useState } from 'react';
import { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api'; import { AddEventLinkTracker, GoToDesktop, RemoveLinkEventTracker } from '../../api';
import { TryVisitRoom } from '../../api/navigator/TryVisitRoom'; import { TryVisitRoom } from '../../api/navigator/TryVisitRoom';
import { NavigatorEvent } from '../../events'; import { NavigatorEvent, UpdateDoorStateEvent } from '../../events';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event'; import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { useUiEvent } from '../../hooks/events/ui/ui-event'; import { useUiEvent } from '../../hooks/events/ui/ui-event';
import { SendMessageHook } from '../../hooks/messages/message-event'; import { SendMessageHook } from '../../hooks/messages/message-event';
@ -13,8 +13,10 @@ import { NavigatorMessageHandler } from './NavigatorMessageHandler';
import { NavigatorViewProps } from './NavigatorView.types'; import { NavigatorViewProps } from './NavigatorView.types';
import { initialNavigator, NavigatorActions, NavigatorReducer } from './reducers/NavigatorReducer'; import { initialNavigator, NavigatorActions, NavigatorReducer } from './reducers/NavigatorReducer';
import { NavigatorRoomCreatorView } from './views/creator/NavigatorRoomCreatorView'; import { NavigatorRoomCreatorView } from './views/creator/NavigatorRoomCreatorView';
import { NavigatorRoomDoorbellView } from './views/room-doorbell/NavigatorRoomDoorbellView';
import { NavigatorRoomInfoView } from './views/room-info/NavigatorRoomInfoView'; import { NavigatorRoomInfoView } from './views/room-info/NavigatorRoomInfoView';
import { NavigatorRoomLinkView } from './views/room-link/NavigatorRoomLinkView'; import { NavigatorRoomLinkView } from './views/room-link/NavigatorRoomLinkView';
import { NavigatorRoomPasswordView } from './views/room-password/NavigatorRoomPasswordView';
import { NavigatorRoomSettingsView } from './views/room-settings/NavigatorRoomSettingsView'; import { NavigatorRoomSettingsView } from './views/room-settings/NavigatorRoomSettingsView';
import { NavigatorSearchResultSetView } from './views/search-result-set/NavigatorSearchResultSetView'; import { NavigatorSearchResultSetView } from './views/search-result-set/NavigatorSearchResultSetView';
import { NavigatorSearchView } from './views/search/NavigatorSearchView'; import { NavigatorSearchView } from './views/search/NavigatorSearchView';
@ -25,6 +27,7 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
const [ isCreatorOpen, setCreatorOpen ] = useState(false); const [ isCreatorOpen, setCreatorOpen ] = useState(false);
const [ isRoomInfoOpen, setRoomInfoOpen ] = useState(false); const [ isRoomInfoOpen, setRoomInfoOpen ] = useState(false);
const [ isRoomLinkOpen, setRoomLinkOpen ] = useState(false); const [ isRoomLinkOpen, setRoomLinkOpen ] = useState(false);
const [ pendingDoorState, setPendingDoorState ] = useState<{ roomData: RoomDataParser, state: string }>(null);
const [ navigatorState, dispatchNavigatorState ] = useReducer(NavigatorReducer, initialNavigator); const [ navigatorState, dispatchNavigatorState ] = useReducer(NavigatorReducer, initialNavigator);
const { needsNavigatorUpdate = false, topLevelContext = null, topLevelContexts = null } = navigatorState; const { needsNavigatorUpdate = false, topLevelContext = null, topLevelContexts = null } = navigatorState;
@ -56,6 +59,49 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
useUiEvent(NavigatorEvent.TOGGLE_ROOM_INFO, onNavigatorEvent); useUiEvent(NavigatorEvent.TOGGLE_ROOM_INFO, onNavigatorEvent);
useUiEvent(NavigatorEvent.TOGGLE_ROOM_LINK, onNavigatorEvent); useUiEvent(NavigatorEvent.TOGGLE_ROOM_LINK, onNavigatorEvent);
const onUpdateDoorStateEvent = useCallback((event: UpdateDoorStateEvent) =>
{
switch(event.type)
{
case UpdateDoorStateEvent.START_DOORBELL:
setPendingDoorState({ roomData: event.roomData, state: event.type });
return;
case UpdateDoorStateEvent.START_PASSWORD:
setPendingDoorState({ roomData: event.roomData, state: event.type });
return;
case UpdateDoorStateEvent.STATE_WAITING:
setPendingDoorState(prevValue =>
{
return { roomData: prevValue.roomData, state: event.type }
});
return;
case UpdateDoorStateEvent.STATE_NO_ANSWER:
setPendingDoorState(prevValue =>
{
if(prevValue.state === UpdateDoorStateEvent.STATE_WAITING) GoToDesktop();
return { roomData: prevValue.roomData, state: event.type }
});
return;
case UpdateDoorStateEvent.STATE_WRONG_PASSWORD:
setPendingDoorState(prevValue =>
{
return { roomData: prevValue.roomData, state: event.type }
});
return;
case UpdateDoorStateEvent.STATE_ACCEPTED:
setPendingDoorState(null);
return;
}
}, []);
useUiEvent(UpdateDoorStateEvent.START_DOORBELL, onUpdateDoorStateEvent);
useUiEvent(UpdateDoorStateEvent.START_PASSWORD, onUpdateDoorStateEvent);
useUiEvent(UpdateDoorStateEvent.STATE_WAITING, onUpdateDoorStateEvent);
useUiEvent(UpdateDoorStateEvent.STATE_NO_ANSWER, onUpdateDoorStateEvent);
useUiEvent(UpdateDoorStateEvent.STATE_WRONG_PASSWORD, onUpdateDoorStateEvent);
useUiEvent(UpdateDoorStateEvent.STATE_ACCEPTED, onUpdateDoorStateEvent);
const onRoomSessionEvent = useCallback((event: RoomSessionEvent) => const onRoomSessionEvent = useCallback((event: RoomSessionEvent) =>
{ {
switch(event.type) switch(event.type)
@ -102,6 +148,18 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
} }
}, []); }, []);
const closePendingDoorState = useCallback((state: string) =>
{
if(state !== null)
{
setPendingDoorState(prevValue =>
{
return { roomData: prevValue.roomData, state };
});
}
else setPendingDoorState(null);
}, []);
useEffect(() => useEffect(() =>
{ {
const linkTracker: ILinkEventTracker = { const linkTracker: ILinkEventTracker = {
@ -135,11 +193,30 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
sendSearch('', topLevelContexts[0].code); sendSearch('', topLevelContexts[0].code);
}, [ topLevelContexts, sendSearch ]); }, [ topLevelContexts, sendSearch ]);
const getRoomDoorState = useMemo(() =>
{
if(!pendingDoorState) return null;
switch(pendingDoorState.state)
{
case UpdateDoorStateEvent.START_DOORBELL:
case UpdateDoorStateEvent.STATE_WAITING:
case UpdateDoorStateEvent.STATE_NO_ANSWER:
return <NavigatorRoomDoorbellView roomData={ pendingDoorState.roomData } state={ pendingDoorState.state } onClose={ closePendingDoorState } />;
case UpdateDoorStateEvent.START_PASSWORD:
case UpdateDoorStateEvent.STATE_WRONG_PASSWORD:
return <NavigatorRoomPasswordView roomData={ pendingDoorState.roomData } state={ pendingDoorState.state } onClose={ closePendingDoorState } />;
}
return null;
}, [ pendingDoorState, closePendingDoorState ]);
return ( return (
<NavigatorContextProvider value={ { navigatorState, dispatchNavigatorState } }> <NavigatorContextProvider value={ { navigatorState, dispatchNavigatorState } }>
<NavigatorMessageHandler /> <NavigatorMessageHandler />
{ getRoomDoorState }
{ isVisible && { isVisible &&
<NitroCardView className="nitro-navigator"> <NitroCardView uniqueKey="navigator" className="nitro-navigator">
<NitroCardHeaderView headerText={ LocalizeText(isCreatorOpen ? 'navigator.createroom.title' : 'navigator.title') } onCloseClick={ event => setIsVisible(false) } /> <NitroCardHeaderView headerText={ LocalizeText(isCreatorOpen ? 'navigator.createroom.title' : 'navigator.title') } onCloseClick={ event => setIsVisible(false) } />
<NitroCardTabsView> <NitroCardTabsView>
{ topLevelContexts.map((context, index) => { topLevelContexts.map((context, index) =>

View File

@ -0,0 +1,43 @@
import { FC, useCallback } from 'react';
import { CreateRoomSession, GoToDesktop } from '../../../../api';
import { UpdateDoorStateEvent } from '../../../../events';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { LocalizeText } from '../../../../utils';
import { NavigatorRoomDoorbellViewProps } from './NavigatorRoomDoorbellView.types';
export const NavigatorRoomDoorbellView: FC<NavigatorRoomDoorbellViewProps> = props =>
{
const { roomData = null, state = null, onClose = null } = props;
const close = useCallback(() =>
{
if(state === UpdateDoorStateEvent.STATE_WAITING) GoToDesktop();
onClose(null);
}, [ state, onClose ]);
const ring = useCallback(() =>
{
if(!roomData) return;
CreateRoomSession(roomData.roomId);
onClose(UpdateDoorStateEvent.STATE_PENDING_SERVER);
}, [ roomData, onClose ]);
return (
<NitroCardView className="nitro-navigator-doorbell" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('navigator.doorbell.title') } onCloseClick={ close } />
<NitroCardContentView className="text-black d-flex flex-column">
{ roomData && <span className="fw-bold">{ roomData.roomName }</span> }
{ (state === UpdateDoorStateEvent.START_DOORBELL) && <span>{ LocalizeText('navigator.doorbell.info') }</span> }
{ (state === UpdateDoorStateEvent.STATE_WAITING) && <span>{ LocalizeText('navigator.doorbell.waiting') }</span> }
{ (state === UpdateDoorStateEvent.STATE_NO_ANSWER) && <span>{ LocalizeText('navigator.doorbell.no.answer') }</span> }
<div className="d-flex flex-column mt-1">
{ (state === UpdateDoorStateEvent.START_DOORBELL) && <button type="button" className="btn btn-success btn-sm" onClick={ ring }>{ LocalizeText('navigator.doorbell.button.ring') }</button> }
<button type="button" className="btn btn-danger btn-sm mt-1" onClick={ close }>{ LocalizeText('generic.cancel') }</button>
</div>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -0,0 +1,8 @@
import { RoomDataParser } from '@nitrots/nitro-renderer';
export interface NavigatorRoomDoorbellViewProps
{
roomData: RoomDataParser;
state: string;
onClose: (state: string) => void;
}

View File

@ -0,0 +1,45 @@
import { FC, useCallback, useState } from 'react';
import { CreateRoomSession } from '../../../../api';
import { UpdateDoorStateEvent } from '../../../../events';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { LocalizeText } from '../../../../utils';
import { NavigatorRoomPasswordViewProps } from './NavigatorRoomPasswordView.types';
export const NavigatorRoomPasswordView: FC<NavigatorRoomPasswordViewProps> = props =>
{
const { roomData = null, state = null, onClose = null } = props;
const [ password, setPassword ] = useState('');
const close = useCallback(() =>
{
onClose(null);
}, [ onClose ]);
const tryEntering = useCallback(() =>
{
if(!roomData) return;
CreateRoomSession(roomData.roomId, password);
onClose(UpdateDoorStateEvent.STATE_PENDING_SERVER);
}, [ roomData, password, onClose ]);
return (
<NitroCardView className="nitro-navigator-password" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('navigator.password.title') } onCloseClick={ close } />
<NitroCardContentView className="text-black d-flex flex-column">
{ roomData && <span className="fw-bold">{ roomData.roomName }</span> }
{ (state === UpdateDoorStateEvent.START_PASSWORD) && <span>{ LocalizeText('navigator.password.info') }</span> }
{ (state === UpdateDoorStateEvent.STATE_WRONG_PASSWORD) && <span>{ LocalizeText('navigator.password.retryinfo') }</span> }
<div className="form-group mt-1">
<label>{ LocalizeText('navigator.password.enter') }</label>
<input type="password" className="form-control form-control-sm" onChange={ event => setPassword(event.target.value) } />
</div>
<div className="d-flex flex-column mt-1">
<button type="button" className="btn btn-success btn-sm" onClick={ tryEntering }>{ LocalizeText('navigator.password.button.try') }</button>
<button type="button" className="btn btn-danger btn-sm mt-1" onClick={ close }>{ LocalizeText('generic.cancel') }</button>
</div>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -0,0 +1,8 @@
import { RoomDataParser } from '@nitrots/nitro-renderer';
export interface NavigatorRoomPasswordViewProps
{
roomData: RoomDataParser;
state: string;
onClose: (state: string) => void;
}

View File

@ -1,7 +1,10 @@
import { RoomDataParser } from '@nitrots/nitro-renderer'; import { RoomDataParser } from '@nitrots/nitro-renderer';
import classNames from 'classnames'; import classNames from 'classnames';
import { FC, MouseEvent } from 'react'; import { FC, MouseEvent } from 'react';
import { CreateRoomSession, GetSessionDataManager } from '../../../../api';
import { TryVisitRoom } from '../../../../api/navigator/TryVisitRoom'; import { TryVisitRoom } from '../../../../api/navigator/TryVisitRoom';
import { UpdateDoorStateEvent } from '../../../../events';
import { dispatchUiEvent } from '../../../../hooks';
import { NavigatorSearchResultItemViewProps } from './NavigatorSearchResultItemView.types'; import { NavigatorSearchResultItemViewProps } from './NavigatorSearchResultItemView.types';
export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProps> = props => export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProps> = props =>
@ -37,8 +40,28 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
} }
function visitRoom(): void function visitRoom(): void
{
if(roomData.ownerId !== GetSessionDataManager().userId)
{
if(roomData.habboGroupId !== 0)
{ {
TryVisitRoom(roomData.roomId); TryVisitRoom(roomData.roomId);
return;
}
switch(roomData.doorMode)
{
case RoomDataParser.DOORBELL_STATE:
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_DOORBELL, roomData));
return;
case RoomDataParser.PASSWORD_STATE:
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_PASSWORD, roomData));
return;
}
}
CreateRoomSession(roomData.roomId);
} }
return ( return (
@ -58,7 +81,7 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
<i className="fas fa-info-circle text-secondary" onClick={ openInfo }></i> <i className="fas fa-info-circle text-secondary" onClick={ openInfo }></i>
{ roomData.habboGroupId > 0 && <i className="fas fa-users mr-2"></i> } { roomData.habboGroupId > 0 && <i className="fas fa-users mr-2"></i> }
{ roomData.doorMode !== RoomDataParser.OPEN_STATE && { roomData.doorMode !== RoomDataParser.OPEN_STATE &&
<i className={ 'mr-2 fas ' + classNames( {'fa-lock': roomData.doorMode === RoomDataParser.DOORBELL_STATE, 'fa-key': roomData.doorMode === RoomDataParser.PASSWORD_STATE })}></i> <i className={ 'me-2 fas ' + classNames( {'fa-lock': roomData.doorMode === RoomDataParser.DOORBELL_STATE, 'fa-key': roomData.doorMode === RoomDataParser.PASSWORD_STATE })}></i>
} }
</div> </div>
</div> </div>

View File

@ -1,6 +1,5 @@
import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer'; import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react'; import { FC, useEffect, useRef, useState } from 'react';
import { createPortal } from 'react-dom';
import { GetNitroInstance, InitializeRoomInstanceRenderingCanvas } from '../../api'; import { GetNitroInstance, InitializeRoomInstanceRenderingCanvas } from '../../api';
import { DispatchMouseEvent } from '../../api/nitro/room/DispatchMouseEvent'; import { DispatchMouseEvent } from '../../api/nitro/room/DispatchMouseEvent';
import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent'; import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent';
@ -8,6 +7,7 @@ import { GetRoomEngine } from '../../api/nitro/room/GetRoomEngine';
import { RoomContextProvider } from './context/RoomContext'; import { RoomContextProvider } from './context/RoomContext';
import { RoomWidgetUpdateRoomViewEvent } from './events/RoomWidgetUpdateRoomViewEvent'; import { RoomWidgetUpdateRoomViewEvent } from './events/RoomWidgetUpdateRoomViewEvent';
import { IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler } from './handlers'; import { IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler } from './handlers';
import { DoorbellWidgetHandler } from './handlers/DoorbellWidgetHandler';
import { FurniChooserWidgetHandler } from './handlers/FurniChooserWidgetHandler'; import { FurniChooserWidgetHandler } from './handlers/FurniChooserWidgetHandler';
import { FurnitureContextMenuWidgetHandler } from './handlers/FurnitureContextMenuWidgetHandler'; import { FurnitureContextMenuWidgetHandler } from './handlers/FurnitureContextMenuWidgetHandler';
import { FurnitureCustomStackHeightWidgetHandler } from './handlers/FurnitureCustomStackHeightWidgetHandler'; import { FurnitureCustomStackHeightWidgetHandler } from './handlers/FurnitureCustomStackHeightWidgetHandler';
@ -23,6 +23,7 @@ export const RoomView: FC<RoomViewProps> = props =>
const [ roomCanvas, setRoomCanvas ] = useState<HTMLCanvasElement>(null); const [ roomCanvas, setRoomCanvas ] = useState<HTMLCanvasElement>(null);
const [ canvasId, setCanvasId ] = useState(-1); const [ canvasId, setCanvasId ] = useState(-1);
const [ widgetHandler, setWidgetHandler ] = useState<IRoomWidgetHandlerManager>(null); const [ widgetHandler, setWidgetHandler ] = useState<IRoomWidgetHandlerManager>(null);
const elementRef = useRef<HTMLDivElement>();
useEffect(() => useEffect(() =>
{ {
@ -48,6 +49,7 @@ export const RoomView: FC<RoomViewProps> = props =>
widgetHandlerManager.registerHandler(new FurnitureCustomStackHeightWidgetHandler()); widgetHandlerManager.registerHandler(new FurnitureCustomStackHeightWidgetHandler());
widgetHandlerManager.registerHandler(new FurniChooserWidgetHandler()); widgetHandlerManager.registerHandler(new FurniChooserWidgetHandler());
widgetHandlerManager.registerHandler(new UserChooserWidgetHandler()); widgetHandlerManager.registerHandler(new UserChooserWidgetHandler());
widgetHandlerManager.registerHandler(new DoorbellWidgetHandler());
setWidgetHandler(widgetHandlerManager); setWidgetHandler(widgetHandlerManager);
@ -115,6 +117,8 @@ export const RoomView: FC<RoomViewProps> = props =>
GetNitroInstance().render(); GetNitroInstance().render();
} }
if(elementRef && elementRef.current) elementRef.current.appendChild(canvas);
setRoomCanvas(canvas); setRoomCanvas(canvas);
setCanvasId(canvasId); setCanvasId(canvasId);
}, [ roomSession ]); }, [ roomSession ]);
@ -123,15 +127,12 @@ export const RoomView: FC<RoomViewProps> = props =>
return ( return (
<RoomContextProvider value={ { roomSession, canvasId, eventDispatcher: (widgetHandler && widgetHandler.eventDispatcher), widgetHandler } }> <RoomContextProvider value={ { roomSession, canvasId, eventDispatcher: (widgetHandler && widgetHandler.eventDispatcher), widgetHandler } }>
<div className="nitro-room w-100 h-100"> <div ref={ elementRef } id="room-view" className="nitro-room-container" />
<div id="room-view" className="nitro-room-container"></div>
{ roomCanvas && createPortal(null, document.getElementById('room-view').appendChild(roomCanvas)) }
{ widgetHandler && { widgetHandler &&
<> <>
<RoomColorView /> <RoomColorView />
<RoomWidgetsView /> <RoomWidgetsView />
</> } </> }
</div>
</RoomContextProvider> </RoomContextProvider>
); );
} }

View File

@ -0,0 +1,22 @@
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
export class RoomWidgetDoorbellEvent extends RoomWidgetUpdateEvent
{
public static RINGING: string = 'RWDE_RINGING';
public static REJECTED: string = 'RWDE_REJECTED';
public static ACCEPTED: string = 'RWDE_ACCEPTED';
private _userName: string = '';
constructor(type: string, userName: string)
{
super(type);
this._userName = userName;
}
public get userName(): string
{
return this._userName;
}
}

View File

@ -1,4 +1,5 @@
export * from './RoomWidgetAvatarInfoEvent'; export * from './RoomWidgetAvatarInfoEvent';
export * from './RoomWidgetDoorbellEvent';
export * from './RoomWidgetFloodControlEvent'; export * from './RoomWidgetFloodControlEvent';
export * from './RoomWidgetObjectNameEvent'; export * from './RoomWidgetObjectNameEvent';
export * from './RoomWidgetRoomEngineUpdateEvent'; export * from './RoomWidgetRoomEngineUpdateEvent';

View File

@ -0,0 +1,60 @@
import { NitroEvent, RoomSessionDoorbellEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer';
import { RoomWidgetDoorbellEvent, RoomWidgetUpdateEvent } from '../events';
import { RoomWidgetLetUserInMessage, RoomWidgetMessage } from '../messages';
import { RoomWidgetHandler } from './RoomWidgetHandler';
export class DoorbellWidgetHandler extends RoomWidgetHandler
{
public processEvent(event: NitroEvent): void
{
const doorbellEvent = (event as RoomSessionDoorbellEvent);
switch(event.type)
{
case RoomSessionDoorbellEvent.DOORBELL:
this.container.eventDispatcher.dispatchEvent(new RoomWidgetDoorbellEvent(RoomWidgetDoorbellEvent.RINGING, doorbellEvent.userName));
return;
case RoomSessionDoorbellEvent.RSDE_REJECTED:
this.container.eventDispatcher.dispatchEvent(new RoomWidgetDoorbellEvent(RoomWidgetDoorbellEvent.REJECTED, doorbellEvent.userName));
return;
case RoomSessionDoorbellEvent.RSDE_ACCEPTED:
this.container.eventDispatcher.dispatchEvent(new RoomWidgetDoorbellEvent(RoomWidgetDoorbellEvent.ACCEPTED, doorbellEvent.userName));
return;
}
}
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
{
switch(message.type)
{
case RoomWidgetLetUserInMessage.LET_USER_IN:
const letUserInMessage = (message as RoomWidgetLetUserInMessage);
this.container.roomSession.sendDoorbellApprovalMessage(letUserInMessage.userName, letUserInMessage.canEnter);
break;
}
return null;
}
public get type(): string
{
return RoomWidgetEnum.DOORBELL;
}
public get eventTypes(): string[]
{
return [
RoomSessionDoorbellEvent.DOORBELL,
RoomSessionDoorbellEvent.RSDE_REJECTED,
RoomSessionDoorbellEvent.RSDE_ACCEPTED
];
}
public get messageTypes(): string[]
{
return [
RoomWidgetLetUserInMessage.LET_USER_IN
];
}
}

View File

@ -0,0 +1,27 @@
import { RoomWidgetMessage } from './RoomWidgetMessage';
export class RoomWidgetLetUserInMessage extends RoomWidgetMessage
{
public static LET_USER_IN: string = 'RWLUIM_LET_USER_IN';
private _userName: string;
private _canEnter: boolean;
constructor(userName: string, canEnter: boolean)
{
super(RoomWidgetLetUserInMessage.LET_USER_IN);
this._userName = userName;
this._canEnter = canEnter;
}
public get userName(): string
{
return this._userName;
}
public get canEnter(): boolean
{
return this._canEnter;
}
}

View File

@ -7,6 +7,7 @@ export * from './RoomWidgetChatTypingMessage';
export * from './RoomWidgetDanceMessage'; export * from './RoomWidgetDanceMessage';
export * from './RoomWidgetFurniActionMessage'; export * from './RoomWidgetFurniActionMessage';
export * from './RoomWidgetFurniToWidgetMessage'; export * from './RoomWidgetFurniToWidgetMessage';
export * from './RoomWidgetLetUserInMessage';
export * from './RoomWidgetMessage'; export * from './RoomWidgetMessage';
export * from './RoomWidgetRequestWidgetMessage'; export * from './RoomWidgetRequestWidgetMessage';
export * from './RoomWidgetRoomObjectMessage'; export * from './RoomWidgetRoomObjectMessage';

View File

@ -3,6 +3,7 @@
@import './chat/ChatWidgetView'; @import './chat/ChatWidgetView';
@import './chat-input/ChatInputView'; @import './chat-input/ChatInputView';
@import './context-menu/ContextMenu'; @import './context-menu/ContextMenu';
@import './doorbell/DoorbellWidgetView';
@import './furniture/FurnitureWidgets'; @import './furniture/FurnitureWidgets';
@import './infostand/InfoStandWidgetView'; @import './infostand/InfoStandWidgetView';
@import './object-location/ObjectLocationView'; @import './object-location/ObjectLocationView';

View File

@ -11,6 +11,7 @@ import { ChatInputView } from './chat-input/ChatInputView';
import { ChatWidgetView } from './chat/ChatWidgetView'; import { ChatWidgetView } from './chat/ChatWidgetView';
import { FurniChooserWidgetView } from './choosers/FurniChooserWidgetView'; import { FurniChooserWidgetView } from './choosers/FurniChooserWidgetView';
import { UserChooserWidgetView } from './choosers/UserChooserWidgetView'; import { UserChooserWidgetView } from './choosers/UserChooserWidgetView';
import { DoorbellWidgetView } from './doorbell/DoorbellWidgetView';
import { FurnitureWidgetsView } from './furniture/FurnitureWidgetsView'; import { FurnitureWidgetsView } from './furniture/FurnitureWidgetsView';
import { InfoStandWidgetView } from './infostand/InfoStandWidgetView'; import { InfoStandWidgetView } from './infostand/InfoStandWidgetView';
import { RoomThumbnailWidgetView } from './room-thumbnail/RoomThumbnailWidgetView'; import { RoomThumbnailWidgetView } from './room-thumbnail/RoomThumbnailWidgetView';
@ -244,6 +245,7 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
<CameraWidgetView /> <CameraWidgetView />
<ChatWidgetView /> <ChatWidgetView />
<ChatInputView /> <ChatInputView />
<DoorbellWidgetView />
<FurnitureWidgetsView /> <FurnitureWidgetsView />
<InfoStandWidgetView /> <InfoStandWidgetView />
<RoomToolsWidgetView /> <RoomToolsWidgetView />

View File

@ -1,5 +1,5 @@
import { FC } from 'react'; import { FC } from 'react';
import { DraggableWindow } from '../../../../../../layout'; import { DraggableWindow, DraggableWindowPosition } from '../../../../../../layout';
import { ObjectLocationView } from '../../../object-location/ObjectLocationView'; import { ObjectLocationView } from '../../../object-location/ObjectLocationView';
import { AvatarInfoRentableBotChatViewProps } from './AvatarInfoRentableBotChatView.types'; import { AvatarInfoRentableBotChatViewProps } from './AvatarInfoRentableBotChatView.types';
@ -8,7 +8,7 @@ export const AvatarInfoRentableBotChatView: FC<AvatarInfoRentableBotChatViewProp
const { chatEvent = null } = props; const { chatEvent = null } = props;
return ( return (
<DraggableWindow noCenter={ true } handle=".drag-handler"> <DraggableWindow position={ DraggableWindowPosition.NOTHING } handleSelector=".drag-handler">
<ObjectLocationView objectId={ chatEvent.objectId } category={ chatEvent.category } noFollow={ true }> <ObjectLocationView objectId={ chatEvent.objectId } category={ chatEvent.category } noFollow={ true }>
<div className="nitro-context-menu"> <div className="nitro-context-menu">
<div className="drag-handler">test!!!!!</div> <div className="drag-handler">test!!!!!</div>

View File

@ -80,8 +80,8 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
const deltaY = (location.y - maxStack); const deltaY = (location.y - maxStack);
let x = (location.x - (elementRef.current.offsetWidth / 2)); let x = Math.round(location.x - (elementRef.current.offsetWidth / 2));
let y = (deltaY + offset); let y = Math.round(deltaY + offset);
const maxLeft = ((GetNitroInstance().width - elementRef.current.offsetWidth) - SPACE_AROUND_EDGES); const maxLeft = ((GetNitroInstance().width - elementRef.current.offsetWidth) - SPACE_AROUND_EDGES);
const maxTop = ((GetNitroInstance().height - elementRef.current.offsetHeight) - SPACE_AROUND_EDGES); const maxTop = ((GetNitroInstance().height - elementRef.current.offsetHeight) - SPACE_AROUND_EDGES);

View File

@ -0,0 +1,22 @@
.nitro-widget-doorbell {
width: 250px;
.content-area {
min-height: 143px;
height: 143px;
}
.doorbell-user-list {
.list-item {
background: $grid-active-bg-color;
}
.col:nth-child(even) {
.list-item {
background: $white !important;
}
}
}
}

View File

@ -0,0 +1,88 @@
import { FC, useCallback, useState } from 'react';
import { CreateEventDispatcherHook } from '../../../../hooks';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { LocalizeText } from '../../../../utils';
import { useRoomContext } from '../../context/RoomContext';
import { RoomWidgetDoorbellEvent } from '../../events';
import { RoomWidgetLetUserInMessage } from '../../messages';
export const DoorbellWidgetView: FC<{}> = props =>
{
const [ isVisible, setIsVisible ] = useState(false);
const [ users, setUsers ] = useState<string[]>([]);
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
const addUser = useCallback((userName: string) =>
{
if(users.indexOf(userName) >= 0) return;
const newUsers = [ ...users, userName ];
setUsers(newUsers);
setIsVisible(true);
}, [ users ]);
const removeUser = useCallback((userName: string) =>
{
const index = users.indexOf(userName);
if(index === -1) return;
const newUsers = [ ...users ];
newUsers.splice(index, 1);
setUsers(newUsers);
if(!newUsers.length) setIsVisible(false);
}, [ users ]);
const onRoomWidgetDoorbellEvent = useCallback((event: RoomWidgetDoorbellEvent) =>
{
switch(event.type)
{
case RoomWidgetDoorbellEvent.RINGING:
addUser(event.userName);
return;
case RoomWidgetDoorbellEvent.REJECTED:
case RoomWidgetDoorbellEvent.ACCEPTED:
removeUser(event.userName);
return;
}
}, [ addUser, removeUser ]);
CreateEventDispatcherHook(RoomWidgetDoorbellEvent.RINGING, eventDispatcher, onRoomWidgetDoorbellEvent);
CreateEventDispatcherHook(RoomWidgetDoorbellEvent.REJECTED, eventDispatcher, onRoomWidgetDoorbellEvent);
CreateEventDispatcherHook(RoomWidgetDoorbellEvent.ACCEPTED, eventDispatcher, onRoomWidgetDoorbellEvent);
const answer = useCallback((userName: string, flag: boolean) =>
{
widgetHandler.processWidgetMessage(new RoomWidgetLetUserInMessage(userName, flag));
removeUser(userName);
}, [ widgetHandler, removeUser ]);
if(!users.length) return null;
return (
<NitroCardView className="nitro-widget-doorbell" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('navigator.doorbell.title') } onCloseClick={ event => setIsVisible(false) } />
<NitroCardContentView>
<div className="row row-cols-1 doorbell-user-list">
{ (users.length > 0) && users.map(userName =>
{
return (
<div className="d-flex col align-items-center justify-content-between" key={ userName }>
<span className="fw-bold text-black">{ userName }</span>
<div>
<button type="button" className="btn btn-success btn-sm me-1" onClick={ event => answer(userName, true) }><i className="fas fa-check" /></button>
<button type="button" className="btn btn-danger btn-sm" onClick={ event => answer(userName, false) }><i className="fas fa-times" /></button>
</div>
</div>
);
}) }
</div>
</NitroCardContentView>
</NitroCardView>
);
}

View File

@ -119,7 +119,7 @@ export const FurnitureFriendFurniView: FC<{}> = props =>
</div> </div>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> } </NitroCardView> }
{ engravingLockData && engravingLockData.usernames.length > 0 && <DraggableWindow handle=".nitro-engraving-lock-view"> { engravingLockData && engravingLockData.usernames.length > 0 && <DraggableWindow handleSelector=".nitro-engraving-lock-view">
<div className={ "nitro-engraving-lock-view engraving-lock-" + engravingLockData.type }> <div className={ "nitro-engraving-lock-view engraving-lock-" + engravingLockData.type }>
<div className="engraving-lock-close" onClick={ event => processAction('close_view') }></div> <div className="engraving-lock-close" onClick={ event => processAction('close_view') }></div>
<div className="d-flex justify-content-center"> <div className="d-flex justify-content-center">

View File

@ -3,6 +3,7 @@ import { FC, useCallback, useState } from 'react';
import { GetRoomEngine, GetRoomSession, GetSessionDataManager } from '../../../../../api'; import { GetRoomEngine, GetRoomSession, GetSessionDataManager } from '../../../../../api';
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base'; import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event'; import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event';
import { DraggableWindowPosition } from '../../../../../layout';
import { DraggableWindow } from '../../../../../layout/draggable-window/DraggableWindow'; import { DraggableWindow } from '../../../../../layout/draggable-window/DraggableWindow';
import { ColorUtils } from '../../../../../utils/ColorUtils'; import { ColorUtils } from '../../../../../utils/ColorUtils';
import { useRoomContext } from '../../../context/RoomContext'; import { useRoomContext } from '../../../context/RoomContext';
@ -114,7 +115,7 @@ export const FurnitureStickieView: FC<{}> = props =>
if(!stickieData) return null; if(!stickieData) return null;
return ( return (
<DraggableWindow handle=".drag-handler" noCenter={ true }> <DraggableWindow handleSelector=".drag-handler" position={ DraggableWindowPosition.NOTHING }>
<div className={ "nitro-stickie nitro-stickie-image stickie-" + getStickieColorName(stickieData.color) }> <div className={ "nitro-stickie nitro-stickie-image stickie-" + getStickieColorName(stickieData.color) }>
<div className="d-flex align-items-center stickie-header drag-handler"> <div className="d-flex align-items-center stickie-header drag-handler">
<div className="d-flex align-items-center flex-grow-1 h-100"> <div className="d-flex align-items-center flex-grow-1 h-100">

View File

@ -0,0 +1,34 @@
import { FriendlyTime } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
import { FriendlyTimeViewProps } from './FriendlyTimeView.types';
export const FriendlyTimeView: FC<FriendlyTimeViewProps> = props =>
{
const { seconds = 0, isShort = false, ...rest } = props;
const [ updateId, setUpdateId ] = useState(-1);
const getStartSeconds = useMemo(() =>
{
return (Math.round(new Date().getSeconds()) - seconds);
}, [ seconds ]);
const getFriendlyTime = useCallback(() =>
{
const value = (Math.round(new Date().getSeconds()) - getStartSeconds);
if(isShort) return FriendlyTime.format(value);
return FriendlyTime.format(value);
}, [ getStartSeconds, isShort ]);
useEffect(() =>
{
const interval = setInterval(() => setUpdateId(prevValue => (prevValue + 1)), 10000);
return () => clearInterval(interval);
}, []);
return (
<div { ...rest }>{ getFriendlyTime() }</div>
);
}

View File

@ -0,0 +1,7 @@
import { DetailsHTMLAttributes } from 'react';
export interface FriendlyTimeViewProps extends DetailsHTMLAttributes<HTMLDivElement>
{
seconds: number;
isShort?: boolean;
}

View File

@ -1,12 +1,12 @@
import { DesktopViewComposer, Dispose, DropBounce, EaseOut, JumpBy, Motions, NitroToolbarAnimateIconEvent, Queue, UserFigureEvent, UserInfoDataParser, UserInfoEvent, Wait } from '@nitrots/nitro-renderer'; import { Dispose, DropBounce, EaseOut, JumpBy, Motions, NitroToolbarAnimateIconEvent, Queue, UserFigureEvent, UserInfoDataParser, UserInfoEvent, Wait } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { GetRoomSession, GetRoomSessionManager } from '../../api'; import { GetRoomSession, GetRoomSessionManager, GoToDesktop } from '../../api';
import { AvatarEditorEvent, CatalogEvent, FriendListEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events'; import { AvatarEditorEvent, CatalogEvent, FriendListEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
import { AchievementsUIEvent } from '../../events/achievements'; import { AchievementsUIEvent } from '../../events/achievements';
import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent'; import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent';
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent'; import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
import { dispatchUiEvent, useRoomEngineEvent, useUiEvent } from '../../hooks'; import { dispatchUiEvent, useRoomEngineEvent, useUiEvent } from '../../hooks';
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event'; import { CreateMessageHook } from '../../hooks/messages/message-event';
import { TransitionAnimation } from '../../layout/transitions/TransitionAnimation'; import { TransitionAnimation } from '../../layout/transitions/TransitionAnimation';
import { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types'; import { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types';
import { AvatarImageView } from '../shared/avatar-image/AvatarImageView'; import { AvatarImageView } from '../shared/avatar-image/AvatarImageView';
@ -129,7 +129,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
{ {
if(!GetRoomSession()) return; if(!GetRoomSession()) return;
SendMessageHook(new DesktopViewComposer()); GoToDesktop();
GetRoomSessionManager().removeSession(-1); GetRoomSessionManager().removeSession(-1);
}, []); }, []);

View File

@ -1,6 +1,6 @@
import { WiredActionDefinition } from '@nitrots/nitro-renderer'; import { WiredActionDefinition } from '@nitrots/nitro-renderer';
import Slider from 'rc-slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale'; import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
@ -34,10 +34,11 @@ export const WiredActionBaseView: FC<WiredActionBaseViewProps> = props =>
</> } </> }
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.delay', [ 'seconds' ], [ GetWiredTimeLocale(delay) ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.delay', [ 'seconds' ], [ GetWiredTimeLocale(delay) ]) }</label>
<Slider <ReactSlider
value={ delay } className={ 'nitro-slider' }
min={ 0 } min={ 0 }
max={ 20 } max={ 20 }
value={ delay }
onChange={ event => setDelay(event) } /> onChange={ event => setDelay(event) } />
</div> </div>
</WiredBaseView> </WiredBaseView>

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -105,14 +105,13 @@ export const WiredActionGiveRewardView: FC<{}> = props =>
{ !limitEnabled && <div className="bg-muted rounded small text-black p-1 text-center"> { !limitEnabled && <div className="bg-muted rounded small text-black p-1 text-center">
Reward limit not set. Make sure rewards are badges or non-tradeable items. Reward limit not set. Make sure rewards are badges or non-tradeable items.
</div> } </div> }
{ limitEnabled && <Slider { limitEnabled &&
defaultValue={ rewardsLimit } <ReactSlider
dots={ true } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 1000 } max={ 1000 }
step={ 1 } value={ rewardsLimit }
onChange={ event => setRewardsLimit(event) } onChange={ event => setRewardsLimit(event) } /> }
/> }
<hr className="my-1 mb-2 bg-dark" /> <hr className="my-1 mb-2 bg-dark" />
<div className="fw-bold">How ofter can a user be rewarded?</div> <div className="fw-bold">How ofter can a user be rewarded?</div>
<div className="d-flex"> <div className="d-flex">

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -37,18 +37,20 @@ export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group mb-2"> <div className="form-group mb-2">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</label>
<Slider <ReactSlider
value={ points } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 100 } max={ 100 }
value={ points }
onChange={ event => setPoints(event) } /> onChange={ event => setPoints(event) } />
</div> </div>
<div className="form-group mb-2"> <div className="form-group mb-2">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 10 } max={ 10 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
<div className="form-group"> <div className="form-group">

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -34,18 +34,20 @@ export const WiredActionGiveScoreView: FC<{}> = props =>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group mb-2"> <div className="form-group mb-2">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</label>
<Slider <ReactSlider
value={ points } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 100 } max={ 100 }
value={ points }
onChange={ event => setPoints(event) } /> onChange={ event => setPoints(event) } />
</div> </div>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 10 } max={ 10 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
</WiredActionBaseView> </WiredActionBaseView>

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -53,10 +53,11 @@ export const WiredActionMoveFurniToView: FC<{}> = props =>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ save }> <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_BY_ID_OR_BY_TYPE } save={ save }>
<div className="form-group mb-2"> <div className="form-group mb-2">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.emptytiles', [ 'tiles' ], [ spacing.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.emptytiles', [ 'tiles' ], [ spacing.toString() ]) }</label>
<Slider <ReactSlider
value={ spacing } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 5 } max={ 5 }
value={ spacing }
onChange={ event => setSpacing(event) } /> onChange={ event => setSpacing(event) } />
</div> </div>
<div className="form-group"> <div className="form-group">

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -27,10 +27,11 @@ export const WiredActionMuteUserView: FC<{}> = props =>
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group mb-2"> <div className="form-group mb-2">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.length.minutes', ['minutes'], [ time.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.length.minutes', ['minutes'], [ time.toString() ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 10 } max={ 10 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
<div className="form-group"> <div className="form-group">

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale'; import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
@ -25,10 +25,11 @@ export const WiredConditionTimeElapsedLessView: FC<{}> = props =>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.allowbefore', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.allowbefore', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 1200 } max={ 1200 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
</WiredConditionBaseView> </WiredConditionBaseView>

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale'; import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
@ -25,10 +25,11 @@ export const WiredConditionTimeElapsedMoreView: FC<{}> = props =>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.allowafter', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.allowafter', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 1200 } max={ 1200 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
</WiredConditionBaseView> </WiredConditionBaseView>

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -34,18 +34,20 @@ export const WiredConditionUserCountInRoomView: FC<{}> = props =>
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group mb-2"> <div className="form-group mb-2">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.usercountmin', [ 'value' ], [ min.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.usercountmin', [ 'value' ], [ min.toString() ]) }</label>
<Slider <ReactSlider
value={ min } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 50 } max={ 50 }
value={ min }
onChange={ event => setMin(event) } /> onChange={ event => setMin(event) } />
</div> </div>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.usercountmax', [ 'value' ], [ max.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.usercountmax', [ 'value' ], [ max.toString() ]) }</label>
<Slider <ReactSlider
value={ max } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 50 } max={ 50 }
value={ max }
onChange={ event => setMax(event) } /> onChange={ event => setMax(event) } />
</div> </div>
</WiredConditionBaseView> </WiredConditionBaseView>

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale'; import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
@ -25,10 +25,11 @@ export const WiredTriggeExecuteOnceView: FC<{}> = props =>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 1200 } max={ 1200 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
</WiredTriggerBaseView> </WiredTriggerBaseView>

View File

@ -1,6 +1,6 @@
import { FriendlyTime } from '@nitrots/nitro-renderer'; import { FriendlyTime } from '@nitrots/nitro-renderer';
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -25,10 +25,11 @@ export const WiredTriggeExecutePeriodicallyLongView: FC<{}> = props =>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.setlongtime', [ 'time' ], [ FriendlyTime.format(time * 5).toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.setlongtime', [ 'time' ], [ FriendlyTime.format(time * 5).toString() ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 120 } max={ 120 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
</WiredTriggerBaseView> </WiredTriggerBaseView>

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale'; import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
@ -25,10 +25,11 @@ export const WiredTriggeExecutePeriodicallyView: FC<{}> = props =>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
<Slider <ReactSlider
value={ time } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 60 } max={ 60 }
value={ time }
onChange={ event => setTime(event) } /> onChange={ event => setTime(event) } />
</div> </div>
</WiredTriggerBaseView> </WiredTriggerBaseView>

View File

@ -1,5 +1,5 @@
import Slider from 'rc-slider/lib/Slider';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import ReactSlider from 'react-slider';
import { LocalizeText } from '../../../../../utils/LocalizeText'; import { LocalizeText } from '../../../../../utils/LocalizeText';
import { useWiredContext } from '../../../context/WiredContext'; import { useWiredContext } from '../../../context/WiredContext';
import { WiredFurniType } from '../../../WiredView.types'; import { WiredFurniType } from '../../../WiredView.types';
@ -24,10 +24,11 @@ export const WiredTriggeScoreAchievedView: FC<{}> = props =>
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }> <WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
<div className="form-group"> <div className="form-group">
<label className="fw-bold">{ LocalizeText('wiredfurni.params.setscore', [ 'points' ], [ points.toString() ]) }</label> <label className="fw-bold">{ LocalizeText('wiredfurni.params.setscore', [ 'points' ], [ points.toString() ]) }</label>
<Slider <ReactSlider
value={ points } className={ 'nitro-slider' }
min={ 1 } min={ 1 }
max={ 1000 } max={ 1000 }
value={ points }
onChange={ event => setPoints(event) } /> onChange={ event => setPoints(event) } />
</div> </div>
</WiredTriggerBaseView> </WiredTriggerBaseView>