mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-27 08:00:51 +01:00
Merge branch 'dev' into feature/user-settings
This commit is contained in:
commit
f40e717dbc
122
package-lock.json
generated
122
package-lock.json
generated
@ -4082,6 +4082,16 @@
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/react-virtualized": {
|
||||
"version": "9.21.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-virtualized/-/react-virtualized-9.21.13.tgz",
|
||||
"integrity": "sha512-tCIQ5wDKj+QJ3sMzjPKSLY0AXsznt+ovAUcq+JCLjPBOcAHbPt4FraGT9HKYEFfmp9E6+ELuN49i5bWtuBmi3w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"@types/resolve": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz",
|
||||
@ -4529,11 +4539,6 @@
|
||||
"resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
|
||||
"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": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
|
||||
@ -5924,6 +5929,11 @@
|
||||
"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": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
|
||||
@ -6969,11 +6979,6 @@
|
||||
"integrity": "sha512-ml3lJIq9YjUfM9TUnEPvEYWFSwivwIGBPKpewX7tii7fwCazA8yCioGdqQcNsItPpfFvSJ3VIdMQPj60LJhcQA==",
|
||||
"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": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz",
|
||||
@ -14588,71 +14593,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": {
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz",
|
||||
@ -14834,15 +14774,6 @@
|
||||
"scheduler": "^0.20.2"
|
||||
}
|
||||
},
|
||||
"react-draggable": {
|
||||
"version": "4.4.3",
|
||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.3.tgz",
|
||||
"integrity": "sha512-jV4TE59MBuWm7gb6Ns3Q1mxX8Azffb7oTtDtBgFkxRvhDp38YAARmRplrj0+XGkhOJB5XziArX+4HUUABtyZ0w==",
|
||||
"requires": {
|
||||
"classnames": "^2.2.5",
|
||||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"react-error-overlay": {
|
||||
"version": "6.0.9",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz",
|
||||
@ -14960,6 +14891,19 @@
|
||||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
|
||||
@ -15310,11 +15254,6 @@
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"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": {
|
||||
"version": "1.18.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz",
|
||||
@ -16105,11 +16044,6 @@
|
||||
"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": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
|
||||
|
@ -4,17 +4,15 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@nitrots/nitro-renderer": "file:../nitro-renderer",
|
||||
"animate.css": "^4.1.1",
|
||||
"classnames": "^2.3.1",
|
||||
"node-sass": "^5.0.0",
|
||||
"rc-slider": "^9.7.2",
|
||||
"react": "^17.0.2",
|
||||
"react-bootstrap": "^2.0.0-alpha.2",
|
||||
"react-dom": "^17.0.2",
|
||||
"react-draggable": "^4.4.3",
|
||||
"react-scripts": "4.0.3",
|
||||
"react-slider": "^1.3.1",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"react-virtualized": "^9.22.3",
|
||||
"typescript": "^4.3.5",
|
||||
"web-vitals": "^1.1.2"
|
||||
},
|
||||
@ -57,6 +55,7 @@
|
||||
"@types/react": "^17.0.15",
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"@types/react-slider": "^1.3.1",
|
||||
"@types/react-transition-group": "^4.4.2"
|
||||
"@types/react-transition-group": "^4.4.2",
|
||||
"@types/react-virtualized": "^9.21.13"
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,10 @@
|
||||
{
|
||||
"image.library.notifications.url": "${image.library.url}notifications/%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",
|
||||
"url.prefix": "http://localhost:3000",
|
||||
"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.content": [
|
||||
{
|
||||
|
@ -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 { GetCommunication, GetConfiguration, GetNitroInstance } from './api';
|
||||
import { useConfigurationEvent } from './hooks/events/core/configuration/configuration-event';
|
||||
@ -17,7 +17,11 @@ export const App: FC<{}> = props =>
|
||||
//@ts-ignore
|
||||
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(() =>
|
||||
{
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { GetRoomSessionManager } from '../nitro';
|
||||
|
||||
export function VisitRoom(roomId: number, password: string = null): void
|
||||
{
|
||||
GetRoomSessionManager().createSession(roomId, password);
|
||||
}
|
6
src/api/nitro/session/CreateRoomSession.ts
Normal file
6
src/api/nitro/session/CreateRoomSession.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { GetRoomSessionManager } from './GetRoomSessionManager';
|
||||
|
||||
export function CreateRoomSession(roomId: number, password: string = null): void
|
||||
{
|
||||
GetRoomSessionManager().createSession(roomId, password);
|
||||
}
|
7
src/api/nitro/session/GoToDesktop.ts
Normal file
7
src/api/nitro/session/GoToDesktop.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { DesktopViewComposer } from '@nitrots/nitro-renderer';
|
||||
import { SendMessageHook } from '../../../hooks';
|
||||
|
||||
export function GoToDesktop(): void
|
||||
{
|
||||
SendMessageHook(new DesktopViewComposer());
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export * from './CanManipulateFurniture';
|
||||
export * from './CreateRoomSession';
|
||||
export * from './GetCanStandUp';
|
||||
export * from './GetCanUseExpression';
|
||||
export * from './GetClubMemberLevel';
|
||||
@ -9,6 +10,7 @@ export * from './GetProductDataForLocalization';
|
||||
export * from './GetRoomSession';
|
||||
export * from './GetRoomSessionManager';
|
||||
export * from './GetSessionDataManager';
|
||||
export * from './GoToDesktop';
|
||||
export * from './HasHabboClub';
|
||||
export * from './HasHabboVip';
|
||||
export * from './IsOwnerOfFurniture';
|
||||
|
@ -82,6 +82,8 @@ $pale-sky: #677181 !default;
|
||||
$oslo-gray: #8F9297 !default;
|
||||
$ghost: #c8cad0 !default;
|
||||
$gray-chateau: #a3a7b1 !default;
|
||||
$gable-green: #1C323F !default;
|
||||
$william: #3d5f6e !default;
|
||||
$success: $green !default;
|
||||
$info: $cyan !default;
|
||||
$warning: $yellow !default;
|
||||
|
27
src/events/navigator/UpdateDoorStateEvent.ts
Normal file
27
src/events/navigator/UpdateDoorStateEvent.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
export * from './NavigatorEvent';
|
||||
export * from './UpdateDoorStateEvent';
|
||||
|
@ -1 +1,2 @@
|
||||
export * from './camera';
|
||||
export * from './thumbnail';
|
||||
|
@ -1,6 +1,4 @@
|
||||
@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';
|
||||
|
||||
html,
|
||||
|
@ -5,12 +5,12 @@ import { NitroCardViewProps } from './NitroCardView.types';
|
||||
|
||||
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 (
|
||||
<NitroCardContextProvider value={ { theme, simple } }>
|
||||
<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 }>
|
||||
{ children }
|
||||
</div>
|
||||
|
@ -1,7 +1,8 @@
|
||||
export interface NitroCardViewProps
|
||||
import { DraggableWindowProps } from '../draggable-window';
|
||||
|
||||
export interface NitroCardViewProps extends DraggableWindowProps
|
||||
{
|
||||
className?: string;
|
||||
disableDrag?: boolean;
|
||||
simple?: boolean;
|
||||
theme?: string;
|
||||
}
|
||||
|
@ -1,19 +1,27 @@
|
||||
import { FC, MouseEvent, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import Draggable from 'react-draggable';
|
||||
import { DraggableWindowProps } from './DraggableWindow.types';
|
||||
import { MouseEventType } from '@nitrots/nitro-renderer';
|
||||
import { FC, Key, MouseEvent as ReactMouseEvent, useCallback, useEffect, useRef, useState } from 'react';
|
||||
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 =>
|
||||
{
|
||||
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 bringToTop = useCallback(() =>
|
||||
{
|
||||
let zIndex = 400;
|
||||
|
||||
for(const existingWindow of currentWindows)
|
||||
for(const existingWindow of CURRENT_WINDOWS)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
currentWindows.splice(index, 1);
|
||||
CURRENT_WINDOWS.splice(index, 1);
|
||||
|
||||
currentWindows.push(elementRef.current);
|
||||
CURRENT_WINDOWS.push(elementRef.current);
|
||||
}
|
||||
|
||||
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(() =>
|
||||
{
|
||||
if(!elementRef) return;
|
||||
const element = (elementRef.current as HTMLElement);
|
||||
|
||||
const element = elementRef.current;
|
||||
if(!element) return;
|
||||
|
||||
currentWindows.push(element);
|
||||
CURRENT_WINDOWS.push(element);
|
||||
|
||||
bringToTop();
|
||||
|
||||
if(!noCenter)
|
||||
if(!disableDrag)
|
||||
{
|
||||
const left = ((document.body.clientWidth / 2) - (element.clientWidth / 2));
|
||||
const top = ((document.body.clientHeight / 2) - (element.clientHeight / 2));
|
||||
const handle = (element.querySelector(handleSelector) as HTMLElement);
|
||||
|
||||
element.style.left = `${ left }px`;
|
||||
element.style.top = `${ top }px`;
|
||||
}
|
||||
else
|
||||
{
|
||||
element.style.left = `0px`;
|
||||
element.style.top = `0px`;
|
||||
if(handle) setDragHandler(handle);
|
||||
}
|
||||
|
||||
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 () =>
|
||||
{
|
||||
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(() =>
|
||||
{
|
||||
return (
|
||||
<div ref={ elementRef } className="position-absolute draggable-window" onMouseDownCapture={ onMouseDown }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}, [ children, onMouseDown ]);
|
||||
if(!offset && !delta) return;
|
||||
|
||||
return disableDrag ? getWindowContent : <Draggable handle={ handle } { ...draggableOptions }>{ getWindowContent }</Draggable>;
|
||||
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 (
|
||||
<div ref={ elementRef } className="position-absolute draggable-window" onMouseDownCapture={ onMouseDown }>
|
||||
{ children }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { DraggableProps } from 'react-draggable';
|
||||
import { Key } from 'react';
|
||||
|
||||
export interface DraggableWindowProps
|
||||
{
|
||||
handle?: string;
|
||||
draggableOptions?: Partial<DraggableProps>;
|
||||
uniqueKey?: Key;
|
||||
handleSelector?: string;
|
||||
position?: string;
|
||||
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';
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
export * from './card';
|
||||
export * from './draggable-window';
|
||||
export * from './loading-spinner';
|
||||
export * from './mini-camera';
|
||||
export * from './transitions';
|
||||
export * from './trophy';
|
||||
|
38
src/layout/mini-camera/NitroLayoutMiniCameraView.tsx
Normal file
38
src/layout/mini-camera/NitroLayoutMiniCameraView.tsx
Normal file
@ -0,0 +1,38 @@
|
||||
import { NitroRectangle } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useRef } from 'react';
|
||||
import { GetRoomEngine } from '../../api';
|
||||
import { LocalizeText } from '../../utils';
|
||||
import { DraggableWindow } from '../draggable-window';
|
||||
import { NitroLayoutMiniCameraViewProps } from './NitroLayoutMiniCameraView.types';
|
||||
|
||||
export const NitroLayoutMiniCameraView: FC<NitroLayoutMiniCameraViewProps> = props =>
|
||||
{
|
||||
const { roomId = -1, textureReceiver = null, onClose = null } = props;
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
const getCameraBounds = useCallback(() =>
|
||||
{
|
||||
if(!elementRef || !elementRef.current) return null;
|
||||
|
||||
const frameBounds = elementRef.current.getBoundingClientRect();
|
||||
|
||||
return new NitroRectangle(Math.floor(frameBounds.x), Math.floor(frameBounds.y), Math.floor(frameBounds.width), Math.floor(frameBounds.height));
|
||||
}, []);
|
||||
|
||||
const takePicture = useCallback(() =>
|
||||
{
|
||||
textureReceiver(GetRoomEngine().createTextureFromRoom(roomId, 1, getCameraBounds()));
|
||||
}, [ roomId, getCameraBounds, textureReceiver ]);
|
||||
|
||||
return (
|
||||
<DraggableWindow handleSelector=".nitro-room-thumbnail-camera">
|
||||
<div className="nitro-room-thumbnail-camera px-2">
|
||||
<div ref={ elementRef } className={ 'camera-frame' } />
|
||||
<div className="d-flex align-items-end h-100 pb-2">
|
||||
<button className="btn btn-sm btn-danger w-100 mb-1 me-2" onClick={ onClose }>{ LocalizeText('cancel') }</button>
|
||||
<button className="btn btn-sm btn-success w-100 mb-1" onClick={ takePicture }>{ LocalizeText('navigator.thumbeditor.save') }</button>
|
||||
</div>
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
import { NitroRenderTexture } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface NitroLayoutMiniCameraViewProps
|
||||
{
|
||||
roomId: number;
|
||||
textureReceiver: (texture: NitroRenderTexture) => void;
|
||||
onClose: () => void;
|
||||
}
|
2
src/layout/mini-camera/index.ts
Normal file
2
src/layout/mini-camera/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './NitroLayoutMiniCameraView';
|
||||
export * from './NitroLayoutMiniCameraView.types';
|
@ -8,7 +8,7 @@ export const NitroLayoutTrophyView: FC<NitroLayoutTrophyViewProps> = props =>
|
||||
const { color = '', message = '', date = '', senderName = '', onCloseClick = null } = props;
|
||||
|
||||
return (
|
||||
<DraggableWindow handle=".drag-handler">
|
||||
<DraggableWindow handleSelector=".drag-handler">
|
||||
<div className={ `nitro-layout-trophy trophy-${ color }` }>
|
||||
<div className="trophy-header drag-handler">
|
||||
<div className="float-end trophy-close" onClick={ onCloseClick }></div>
|
||||
|
@ -40,7 +40,7 @@ export const AchievementsView: FC<AchievementsViewProps> = props =>
|
||||
<AchievementsContextProvider value={ { achievementsState, dispatchAchievementsState } }>
|
||||
<AchievementsMessageHandler />
|
||||
{ isVisible &&
|
||||
<NitroCardView className="nitro-achievements">
|
||||
<NitroCardView uniqueKey="achievements" className="nitro-achievements">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.achievements') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardContentView>
|
||||
<div className="row">
|
||||
|
@ -276,7 +276,7 @@ export const AvatarEditorView: FC<AvatarEditorViewProps> = props =>
|
||||
if(!isVisible || !figureData) return null;
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-avatar-editor">
|
||||
<NitroCardView uniqueKey="avatar-editor" className="nitro-avatar-editor">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('avatareditor.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
{ categories && (categories.size > 0) && Array.from(categories.keys()).map(category =>
|
||||
|
@ -190,7 +190,7 @@ export const CatalogView: FC<CatalogViewProps> = props =>
|
||||
<CatalogContextProvider value={ { catalogState, dispatchCatalogState } }>
|
||||
<CatalogMessageHandler />
|
||||
{ isVisible &&
|
||||
<NitroCardView className="nitro-catalog">
|
||||
<NitroCardView uniqueKey="catalog" className="nitro-catalog">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('catalog.title') } onCloseClick={ event => { saveActivePages(); setIsVisible(false); } } />
|
||||
<NitroCardTabsView>
|
||||
{ root && root.children.length && root.children.map((page, index) =>
|
||||
|
@ -84,7 +84,7 @@ export const FriendListView: FC<FriendListViewProps> = props =>
|
||||
<FriendListMessageHandler />
|
||||
{ isReady && createPortal(<FriendBarView />, document.getElementById('toolbar-friend-bar-container')) }
|
||||
{ isVisible &&
|
||||
<NitroCardView className="nitro-friend-list">
|
||||
<NitroCardView uniqueKey="friend-list" className="nitro-friend-list">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardContentView>
|
||||
<div className="text-black fw-bold">{ LocalizeText('friendlist.search.friendscaption') }</div>
|
||||
|
@ -197,7 +197,7 @@ export const InventoryView: FC<InventoryViewProps> = props =>
|
||||
<InventoryContextProvider value={ { furnitureState, dispatchFurnitureState, botState, dispatchBotState, petState, dispatchPetState, badgeState, dispatchBadgeState, unseenTracker } }>
|
||||
<InventoryMessageHandler />
|
||||
{ isVisible &&
|
||||
<NitroCardView className="nitro-inventory">
|
||||
<NitroCardView uniqueKey={ 'inventory' } className="nitro-inventory">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('inventory.title') } onCloseClick={ close } />
|
||||
{ !furnitureState.tradeData &&
|
||||
<>
|
||||
|
@ -161,7 +161,7 @@ export const ModToolsView: FC<ModToolsViewProps> = props =>
|
||||
return (
|
||||
<ModToolsContextProvider value={ { modToolsState, dispatchModToolsState } }>
|
||||
{ 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) } />
|
||||
<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>
|
||||
|
@ -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 { GetRoomSessionManager, GetSessionDataManager } from '../../api';
|
||||
import { VisitRoom } from '../../api/navigator/VisitRoom';
|
||||
import { CreateRoomSession, GetSessionDataManager } from '../../api';
|
||||
import { UpdateDoorStateEvent } from '../../events';
|
||||
import { dispatchUiEvent } from '../../hooks';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../hooks/messages/message-event';
|
||||
import { useNavigatorContext } from './context/NavigatorContext';
|
||||
import { NavigatorMessageHandlerProps } from './NavigatorMessageHandler.types';
|
||||
@ -65,13 +66,15 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
|
||||
switch(parser.data.doorMode)
|
||||
{
|
||||
case RoomDataParser.DOORBELL_STATE:
|
||||
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_DOORBELL, parser.data));
|
||||
return;
|
||||
case RoomDataParser.PASSWORD_STATE:
|
||||
//showLock();
|
||||
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.START_PASSWORD, parser.data));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
GetRoomSessionManager().createSession(parser.data.roomId);
|
||||
CreateRoomSession(parser.data.roomId);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -91,32 +94,42 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
// if(!parser.userName || (parser.userName.length === 0))
|
||||
// {
|
||||
// showLock(NavigatorLockViewStage.WAITING);
|
||||
// }
|
||||
if(!parser.userName || (parser.userName.length === 0))
|
||||
{
|
||||
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_WAITING));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onRoomDoorbellAcceptedEvent = useCallback((event: RoomDoorbellAcceptedEvent) =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
// if(!parser.userName || (parser.userName.length === 0))
|
||||
// {
|
||||
// hideLock();
|
||||
// }
|
||||
if(!parser.userName || (parser.userName.length === 0))
|
||||
{
|
||||
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 parser = event.getParser();
|
||||
|
||||
// switch(parser.errorCode)
|
||||
// {
|
||||
// case -100002:
|
||||
// showLock(NavigatorLockViewStage.FAILED);
|
||||
// break;
|
||||
// }
|
||||
switch(parser.errorCode)
|
||||
{
|
||||
case -100002:
|
||||
dispatchUiEvent(new UpdateDoorStateEvent(UpdateDoorStateEvent.STATE_WRONG_PASSWORD));
|
||||
break;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onNavigatorMetadataEvent = useCallback((event: NavigatorMetadataEvent) =>
|
||||
@ -159,7 +172,7 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
VisitRoom(parser.roomId);
|
||||
CreateRoomSession(parser.roomId);
|
||||
}, []);
|
||||
|
||||
const onNavigatorHomeRoomEvent = useCallback((event: NavigatorHomeRoomEvent) =>
|
||||
@ -187,6 +200,7 @@ export const NavigatorMessageHandler: FC<NavigatorMessageHandlerProps> = props =
|
||||
CreateMessageHook(RoomInfoEvent, onRoomInfoEvent);
|
||||
CreateMessageHook(RoomDoorbellEvent, onRoomDoorbellEvent);
|
||||
CreateMessageHook(RoomDoorbellAcceptedEvent, onRoomDoorbellAcceptedEvent);
|
||||
CreateMessageHook(RoomDoorbellRejectedEvent, onRoomDoorbellRejectedEvent);
|
||||
CreateMessageHook(GenericErrorEvent, onGenericErrorEvent);
|
||||
CreateMessageHook(NavigatorMetadataEvent, onNavigatorMetadataEvent);
|
||||
CreateMessageHook(NavigatorSearchEvent, onNavigatorSearchEvent);
|
||||
|
@ -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';
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { ILinkEventTracker, NavigatorInitComposer, NavigatorSearchComposer, RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useReducer, useState } from 'react';
|
||||
import { AddEventLinkTracker, RemoveLinkEventTracker } from '../../api';
|
||||
import { ILinkEventTracker, NavigatorInitComposer, NavigatorSearchComposer, RoomDataParser, RoomSessionEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useMemo, useReducer, useState } from 'react';
|
||||
import { AddEventLinkTracker, GoToDesktop, RemoveLinkEventTracker } from '../../api';
|
||||
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 { useUiEvent } from '../../hooks/events/ui/ui-event';
|
||||
import { SendMessageHook } from '../../hooks/messages/message-event';
|
||||
@ -13,8 +13,10 @@ import { NavigatorMessageHandler } from './NavigatorMessageHandler';
|
||||
import { NavigatorViewProps } from './NavigatorView.types';
|
||||
import { initialNavigator, NavigatorActions, NavigatorReducer } from './reducers/NavigatorReducer';
|
||||
import { NavigatorRoomCreatorView } from './views/creator/NavigatorRoomCreatorView';
|
||||
import { NavigatorRoomDoorbellView } from './views/room-doorbell/NavigatorRoomDoorbellView';
|
||||
import { NavigatorRoomInfoView } from './views/room-info/NavigatorRoomInfoView';
|
||||
import { NavigatorRoomLinkView } from './views/room-link/NavigatorRoomLinkView';
|
||||
import { NavigatorRoomPasswordView } from './views/room-password/NavigatorRoomPasswordView';
|
||||
import { NavigatorRoomSettingsView } from './views/room-settings/NavigatorRoomSettingsView';
|
||||
import { NavigatorSearchResultSetView } from './views/search-result-set/NavigatorSearchResultSetView';
|
||||
import { NavigatorSearchView } from './views/search/NavigatorSearchView';
|
||||
@ -25,6 +27,7 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
|
||||
const [ isCreatorOpen, setCreatorOpen ] = useState(false);
|
||||
const [ isRoomInfoOpen, setRoomInfoOpen ] = useState(false);
|
||||
const [ isRoomLinkOpen, setRoomLinkOpen ] = useState(false);
|
||||
const [ pendingDoorState, setPendingDoorState ] = useState<{ roomData: RoomDataParser, state: string }>(null);
|
||||
const [ navigatorState, dispatchNavigatorState ] = useReducer(NavigatorReducer, initialNavigator);
|
||||
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_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) =>
|
||||
{
|
||||
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(() =>
|
||||
{
|
||||
const linkTracker: ILinkEventTracker = {
|
||||
@ -135,11 +193,30 @@ export const NavigatorView: FC<NavigatorViewProps> = props =>
|
||||
sendSearch('', topLevelContexts[0].code);
|
||||
}, [ 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 (
|
||||
<NavigatorContextProvider value={ { navigatorState, dispatchNavigatorState } }>
|
||||
<NavigatorMessageHandler />
|
||||
{ getRoomDoorState }
|
||||
{ isVisible &&
|
||||
<NitroCardView className="nitro-navigator">
|
||||
<NitroCardView uniqueKey="navigator" className="nitro-navigator">
|
||||
<NitroCardHeaderView headerText={ LocalizeText(isCreatorOpen ? 'navigator.createroom.title' : 'navigator.title') } onCloseClick={ event => setIsVisible(false) } />
|
||||
<NitroCardTabsView>
|
||||
{ topLevelContexts.map((context, index) =>
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { RoomDataParser } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface NavigatorRoomDoorbellViewProps
|
||||
{
|
||||
roomData: RoomDataParser;
|
||||
state: string;
|
||||
onClose: (state: string) => void;
|
||||
}
|
@ -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>
|
||||
);
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
import { RoomDataParser } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface NavigatorRoomPasswordViewProps
|
||||
{
|
||||
roomData: RoomDataParser;
|
||||
state: string;
|
||||
onClose: (state: string) => void;
|
||||
}
|
@ -1,7 +1,10 @@
|
||||
import { RoomDataParser } from '@nitrots/nitro-renderer';
|
||||
import classNames from 'classnames';
|
||||
import { FC, MouseEvent } from 'react';
|
||||
import { CreateRoomSession, GetSessionDataManager } from '../../../../api';
|
||||
import { TryVisitRoom } from '../../../../api/navigator/TryVisitRoom';
|
||||
import { UpdateDoorStateEvent } from '../../../../events';
|
||||
import { dispatchUiEvent } from '../../../../hooks';
|
||||
import { NavigatorSearchResultItemViewProps } from './NavigatorSearchResultItemView.types';
|
||||
|
||||
export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProps> = props =>
|
||||
@ -38,7 +41,27 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
|
||||
|
||||
function visitRoom(): void
|
||||
{
|
||||
TryVisitRoom(roomData.roomId);
|
||||
if(roomData.ownerId !== GetSessionDataManager().userId)
|
||||
{
|
||||
if(roomData.habboGroupId !== 0)
|
||||
{
|
||||
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 (
|
||||
@ -58,7 +81,7 @@ export const NavigatorSearchResultItemView: FC<NavigatorSearchResultItemViewProp
|
||||
<i className="fas fa-info-circle text-secondary" onClick={ openInfo }></i>
|
||||
{ roomData.habboGroupId > 0 && <i className="fas fa-users mr-2"></i> }
|
||||
{ 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>
|
||||
|
@ -1,6 +1,6 @@
|
||||
.nitro-purse {
|
||||
padding: 2px;
|
||||
background-color: #1c323f;
|
||||
background-color: $gable-green;
|
||||
border: 2px solid rgba($white, 0.5);
|
||||
border-top: 0;
|
||||
font-size: $font-size-sm;
|
||||
@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.nitro-purse-hc {
|
||||
background-color: #3d5f6e;
|
||||
background-color: $william;
|
||||
margin:0 2px;
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
.nitro-seasonal-currency {
|
||||
pointer-events: all;
|
||||
padding: 2px;
|
||||
background-color: #1c323f;
|
||||
background-color: $gable-green;
|
||||
border: 2px solid rgba($white, 0.5);
|
||||
font-size: $font-size-sm;
|
||||
margin-bottom: 5px;
|
||||
@ -11,6 +11,6 @@
|
||||
}
|
||||
|
||||
.nitro-seasonal-icon {
|
||||
background-color: #3d5f6e
|
||||
background-color: $william;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { EventDispatcher, NitroRectangle, RoomGeometry, RoomVariableEnum, Vector3d } from '@nitrots/nitro-renderer';
|
||||
import { FC, useEffect, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { FC, useEffect, useRef, useState } from 'react';
|
||||
import { GetNitroInstance, InitializeRoomInstanceRenderingCanvas } from '../../api';
|
||||
import { DispatchMouseEvent } from '../../api/nitro/room/DispatchMouseEvent';
|
||||
import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent';
|
||||
@ -8,9 +7,12 @@ import { GetRoomEngine } from '../../api/nitro/room/GetRoomEngine';
|
||||
import { RoomContextProvider } from './context/RoomContext';
|
||||
import { RoomWidgetUpdateRoomViewEvent } from './events/RoomWidgetUpdateRoomViewEvent';
|
||||
import { IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler } from './handlers';
|
||||
import { DoorbellWidgetHandler } from './handlers/DoorbellWidgetHandler';
|
||||
import { FurniChooserWidgetHandler } from './handlers/FurniChooserWidgetHandler';
|
||||
import { FurnitureContextMenuWidgetHandler } from './handlers/FurnitureContextMenuWidgetHandler';
|
||||
import { FurnitureCustomStackHeightWidgetHandler } from './handlers/FurnitureCustomStackHeightWidgetHandler';
|
||||
import { RoomWidgetRoomToolsHandler } from './handlers/RoomWidgetRoomToolsHandler';
|
||||
import { UserChooserWidgetHandler } from './handlers/UserChooserWidgetHandler';
|
||||
import { RoomColorView } from './RoomColorView';
|
||||
import { RoomViewProps } from './RoomView.types';
|
||||
import { RoomWidgetsView } from './widgets/RoomWidgetsView';
|
||||
@ -21,6 +23,7 @@ export const RoomView: FC<RoomViewProps> = props =>
|
||||
const [ roomCanvas, setRoomCanvas ] = useState<HTMLCanvasElement>(null);
|
||||
const [ canvasId, setCanvasId ] = useState(-1);
|
||||
const [ widgetHandler, setWidgetHandler ] = useState<IRoomWidgetHandlerManager>(null);
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@ -44,6 +47,9 @@ export const RoomView: FC<RoomViewProps> = props =>
|
||||
widgetHandlerManager.registerHandler(new RoomWidgetChatHandler());
|
||||
widgetHandlerManager.registerHandler(new FurnitureContextMenuWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new FurnitureCustomStackHeightWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new FurniChooserWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new UserChooserWidgetHandler());
|
||||
widgetHandlerManager.registerHandler(new DoorbellWidgetHandler());
|
||||
|
||||
setWidgetHandler(widgetHandlerManager);
|
||||
|
||||
@ -111,6 +117,8 @@ export const RoomView: FC<RoomViewProps> = props =>
|
||||
GetNitroInstance().render();
|
||||
}
|
||||
|
||||
if(elementRef && elementRef.current) elementRef.current.appendChild(canvas);
|
||||
|
||||
setRoomCanvas(canvas);
|
||||
setCanvasId(canvasId);
|
||||
}, [ roomSession ]);
|
||||
@ -119,15 +127,12 @@ export const RoomView: FC<RoomViewProps> = props =>
|
||||
|
||||
return (
|
||||
<RoomContextProvider value={ { roomSession, canvasId, eventDispatcher: (widgetHandler && widgetHandler.eventDispatcher), widgetHandler } }>
|
||||
<div className="nitro-room w-100 h-100">
|
||||
<div id="room-view" className="nitro-room-container"></div>
|
||||
{ roomCanvas && createPortal(null, document.getElementById('room-view').appendChild(roomCanvas)) }
|
||||
{ widgetHandler &&
|
||||
<>
|
||||
<RoomColorView />
|
||||
<RoomWidgetsView />
|
||||
</> }
|
||||
</div>
|
||||
<div ref={ elementRef } id="room-view" className="nitro-room-container" />
|
||||
{ widgetHandler &&
|
||||
<>
|
||||
<RoomColorView />
|
||||
<RoomWidgetsView />
|
||||
</> }
|
||||
</RoomContextProvider>
|
||||
);
|
||||
}
|
||||
|
28
src/views/room/events/RoomObjectItem.ts
Normal file
28
src/views/room/events/RoomObjectItem.ts
Normal file
@ -0,0 +1,28 @@
|
||||
export class RoomObjectItem
|
||||
{
|
||||
private _id: number;
|
||||
private _category: number;
|
||||
private _name: string;
|
||||
|
||||
constructor(id: number, category: number, name: string)
|
||||
{
|
||||
this._id = id;
|
||||
this._category = category;
|
||||
this._name = name;
|
||||
}
|
||||
|
||||
public get id(): number
|
||||
{
|
||||
return this._id;
|
||||
}
|
||||
|
||||
public get category(): number
|
||||
{
|
||||
return this._category;
|
||||
}
|
||||
|
||||
public get name(): string
|
||||
{
|
||||
return this._name;
|
||||
}
|
||||
}
|
22
src/views/room/events/RoomWidgetChooserContentEvent.ts
Normal file
22
src/views/room/events/RoomWidgetChooserContentEvent.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { RoomObjectItem } from './RoomObjectItem';
|
||||
import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent';
|
||||
|
||||
export class RoomWidgetChooserContentEvent extends RoomWidgetUpdateEvent
|
||||
{
|
||||
public static USER_CHOOSER_CONTENT: string = 'RWCCE_USER_CHOOSER_CONTENT';
|
||||
public static FURNI_CHOOSER_CONTENT: string = 'RWCCE_FURNI_CHOOSER_CONTENT';
|
||||
|
||||
private _items: RoomObjectItem[];
|
||||
|
||||
constructor(type: string, items: RoomObjectItem[])
|
||||
{
|
||||
super(type);
|
||||
|
||||
this._items = items;
|
||||
}
|
||||
|
||||
public get items(): RoomObjectItem[]
|
||||
{
|
||||
return this._items;
|
||||
}
|
||||
}
|
22
src/views/room/events/RoomWidgetDoorbellEvent.ts
Normal file
22
src/views/room/events/RoomWidgetDoorbellEvent.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
export * from './RoomObjectItem';
|
||||
export * from './RoomWidgetAvatarInfoEvent';
|
||||
export * from './RoomWidgetChooserContentEvent';
|
||||
export * from './RoomWidgetDoorbellEvent';
|
||||
export * from './RoomWidgetFloodControlEvent';
|
||||
export * from './RoomWidgetObjectNameEvent';
|
||||
export * from './RoomWidgetRoomEngineUpdateEvent';
|
||||
|
60
src/views/room/handlers/DoorbellWidgetHandler.ts
Normal file
60
src/views/room/handlers/DoorbellWidgetHandler.ts
Normal 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
|
||||
];
|
||||
}
|
||||
}
|
102
src/views/room/handlers/FurniChooserWidgetHandler.ts
Normal file
102
src/views/room/handlers/FurniChooserWidgetHandler.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { NitroEvent, RoomObjectCategory, RoomObjectVariable, RoomWidgetEnum } from '@nitrots/nitro-renderer';
|
||||
import { GetRoomEngine, GetSessionDataManager } from '../../../api';
|
||||
import { LocalizeText } from '../../../utils';
|
||||
import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetUpdateEvent } from '../events';
|
||||
import { RoomWidgetMessage, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../messages';
|
||||
import { RoomWidgetHandler } from './RoomWidgetHandler';
|
||||
|
||||
export class FurniChooserWidgetHandler extends RoomWidgetHandler
|
||||
{
|
||||
public processEvent(event: NitroEvent): void
|
||||
{
|
||||
}
|
||||
|
||||
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
|
||||
{
|
||||
if(!message) return null;
|
||||
|
||||
switch(message.type)
|
||||
{
|
||||
case RoomWidgetRequestWidgetMessage.FURNI_CHOOSER:
|
||||
this.processChooser();
|
||||
break;
|
||||
case RoomWidgetRoomObjectMessage.SELECT_OBJECT:
|
||||
this.selectRoomObject((message as RoomWidgetRoomObjectMessage));
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private processChooser(): void
|
||||
{
|
||||
const roomId = this.container.roomSession.roomId;
|
||||
const items: RoomObjectItem[] = [];
|
||||
|
||||
const wallItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.WALL);
|
||||
const floorItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.FLOOR);
|
||||
|
||||
wallItems.forEach(roomObject =>
|
||||
{
|
||||
let name = roomObject.type;
|
||||
|
||||
if(name.startsWith('poster'))
|
||||
{
|
||||
name = LocalizeText(`poster_${ name.replace('poster', '') }_name`);
|
||||
}
|
||||
else
|
||||
{
|
||||
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
|
||||
const furniData = GetSessionDataManager().getWallItemData(typeId);
|
||||
|
||||
if(furniData && furniData.name.length) name = furniData.name;
|
||||
}
|
||||
|
||||
items.push(new RoomObjectItem(roomObject.id, RoomObjectCategory.WALL, name));
|
||||
});
|
||||
|
||||
floorItems.forEach(roomObject =>
|
||||
{
|
||||
let name = roomObject.type;
|
||||
|
||||
const typeId = roomObject.model.getValue<number>(RoomObjectVariable.FURNITURE_TYPE_ID);
|
||||
const furniData = GetSessionDataManager().getFloorItemData(typeId);
|
||||
|
||||
if(furniData && furniData.name.length) name = furniData.name;
|
||||
|
||||
items.push(new RoomObjectItem(roomObject.id, RoomObjectCategory.FLOOR, name));
|
||||
});
|
||||
|
||||
items.sort((a, b) =>
|
||||
{
|
||||
return (a.name < b.name) ? -1 : 1;
|
||||
});
|
||||
|
||||
this.container.eventDispatcher.dispatchEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, items));
|
||||
}
|
||||
|
||||
private selectRoomObject(message: RoomWidgetRoomObjectMessage): void
|
||||
{
|
||||
if(message.category !== RoomObjectCategory.WALL || message.category !== RoomObjectCategory.FLOOR) return;
|
||||
|
||||
GetRoomEngine().selectRoomObject(this.container.roomSession.roomId, message.id, message.category);
|
||||
}
|
||||
|
||||
public get type(): string
|
||||
{
|
||||
return RoomWidgetEnum.FURNI_CHOOSER;
|
||||
}
|
||||
|
||||
public get eventTypes(): string[]
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public get messageTypes(): string[]
|
||||
{
|
||||
return [
|
||||
RoomWidgetRequestWidgetMessage.FURNI_CHOOSER,
|
||||
RoomWidgetRoomObjectMessage.SELECT_OBJECT
|
||||
];
|
||||
}
|
||||
}
|
78
src/views/room/handlers/UserChooserWidgetHandler.ts
Normal file
78
src/views/room/handlers/UserChooserWidgetHandler.ts
Normal file
@ -0,0 +1,78 @@
|
||||
import { NitroEvent, RoomObjectCategory, RoomWidgetEnum } from '@nitrots/nitro-renderer';
|
||||
import { RoomWidgetHandler } from '.';
|
||||
import { GetRoomEngine } from '../../../api';
|
||||
import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetUpdateEvent } from '../events';
|
||||
import { RoomWidgetMessage, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../messages';
|
||||
|
||||
export class UserChooserWidgetHandler extends RoomWidgetHandler
|
||||
{
|
||||
public processEvent(event: NitroEvent): void
|
||||
{
|
||||
}
|
||||
|
||||
public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent
|
||||
{
|
||||
if(!message) return null;
|
||||
|
||||
switch(message.type)
|
||||
{
|
||||
case RoomWidgetRequestWidgetMessage.USER_CHOOSER:
|
||||
this.processChooser();
|
||||
break;
|
||||
case RoomWidgetRoomObjectMessage.SELECT_OBJECT:
|
||||
this.selectRoomObject((message as RoomWidgetRoomObjectMessage));
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private processChooser(): void
|
||||
{
|
||||
const roomId = this.container.roomSession.roomId;
|
||||
const items: RoomObjectItem[] = [];
|
||||
|
||||
const userItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.UNIT);
|
||||
|
||||
userItems.forEach(roomObject =>
|
||||
{
|
||||
const userData = this.container.roomSession.userDataManager.getUserDataByIndex(roomObject.id);
|
||||
|
||||
if(!userData) return;
|
||||
|
||||
items.push(new RoomObjectItem(userData.roomIndex, RoomObjectCategory.UNIT, userData.name));
|
||||
});
|
||||
|
||||
items.sort((a, b) =>
|
||||
{
|
||||
return (a.name < b.name) ? -1 : 1;
|
||||
});
|
||||
|
||||
this.container.eventDispatcher.dispatchEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, items));
|
||||
}
|
||||
|
||||
private selectRoomObject(message: RoomWidgetRoomObjectMessage): void
|
||||
{
|
||||
if(message.category !== RoomObjectCategory.UNIT) return;
|
||||
|
||||
GetRoomEngine().selectRoomObject(this.container.roomSession.roomId, message.id, message.category);
|
||||
}
|
||||
|
||||
public get type(): string
|
||||
{
|
||||
return RoomWidgetEnum.USER_CHOOSER;
|
||||
}
|
||||
|
||||
public get eventTypes(): string[]
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public get messageTypes(): string[]
|
||||
{
|
||||
return [
|
||||
RoomWidgetRequestWidgetMessage.USER_CHOOSER,
|
||||
RoomWidgetRoomObjectMessage.SELECT_OBJECT
|
||||
];
|
||||
}
|
||||
}
|
27
src/views/room/messages/RoomWidgetLetUserInMessage.ts
Normal file
27
src/views/room/messages/RoomWidgetLetUserInMessage.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ export * from './RoomWidgetChatTypingMessage';
|
||||
export * from './RoomWidgetDanceMessage';
|
||||
export * from './RoomWidgetFurniActionMessage';
|
||||
export * from './RoomWidgetFurniToWidgetMessage';
|
||||
export * from './RoomWidgetLetUserInMessage';
|
||||
export * from './RoomWidgetMessage';
|
||||
export * from './RoomWidgetRequestWidgetMessage';
|
||||
export * from './RoomWidgetRoomObjectMessage';
|
||||
|
@ -3,8 +3,9 @@
|
||||
@import './chat/ChatWidgetView';
|
||||
@import './chat-input/ChatInputView';
|
||||
@import './context-menu/ContextMenu';
|
||||
@import './doorbell/DoorbellWidgetView';
|
||||
@import './furniture/FurnitureWidgets';
|
||||
@import './infostand/InfoStandWidgetView';
|
||||
@import './object-location/ObjectLocationView';
|
||||
@import './room-tools/RoomToolsWidgetView';
|
||||
@import './room-thumbnail/RoomThumbnailView';
|
||||
@import './choosers/ChooserWidgetView';
|
||||
|
@ -9,6 +9,9 @@ import { AvatarInfoWidgetView } from './avatar-info/AvatarInfoWidgetView';
|
||||
import { CameraWidgetView } from './camera/CameraWidgetView';
|
||||
import { ChatInputView } from './chat-input/ChatInputView';
|
||||
import { ChatWidgetView } from './chat/ChatWidgetView';
|
||||
import { FurniChooserWidgetView } from './choosers/FurniChooserWidgetView';
|
||||
import { UserChooserWidgetView } from './choosers/UserChooserWidgetView';
|
||||
import { DoorbellWidgetView } from './doorbell/DoorbellWidgetView';
|
||||
import { FurnitureWidgetsView } from './furniture/FurnitureWidgetsView';
|
||||
import { InfoStandWidgetView } from './infostand/InfoStandWidgetView';
|
||||
import { RoomThumbnailWidgetView } from './room-thumbnail/RoomThumbnailWidgetView';
|
||||
@ -242,10 +245,13 @@ export const RoomWidgetsView: FC<RoomWidgetViewProps> = props =>
|
||||
<CameraWidgetView />
|
||||
<ChatWidgetView />
|
||||
<ChatInputView />
|
||||
<DoorbellWidgetView />
|
||||
<FurnitureWidgetsView />
|
||||
<InfoStandWidgetView />
|
||||
<RoomToolsWidgetView />
|
||||
<RoomThumbnailWidgetView />
|
||||
<FurniChooserWidgetView />
|
||||
<UserChooserWidgetView />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FC } from 'react';
|
||||
import { DraggableWindow } from '../../../../../../layout';
|
||||
import { DraggableWindow, DraggableWindowPosition } from '../../../../../../layout';
|
||||
import { ObjectLocationView } from '../../../object-location/ObjectLocationView';
|
||||
import { AvatarInfoRentableBotChatViewProps } from './AvatarInfoRentableBotChatView.types';
|
||||
|
||||
@ -8,7 +8,7 @@ export const AvatarInfoRentableBotChatView: FC<AvatarInfoRentableBotChatViewProp
|
||||
const { chatEvent = null } = props;
|
||||
|
||||
return (
|
||||
<DraggableWindow noCenter={ true } handle=".drag-handler">
|
||||
<DraggableWindow position={ DraggableWindowPosition.NOTHING } handleSelector=".drag-handler">
|
||||
<ObjectLocationView objectId={ chatEvent.objectId } category={ chatEvent.category } noFollow={ true }>
|
||||
<div className="nitro-context-menu">
|
||||
<div className="drag-handler">test!!!!!</div>
|
||||
|
@ -87,6 +87,53 @@
|
||||
.nitro-camera-editor {
|
||||
width: 600px;
|
||||
|
||||
.content-area {
|
||||
min-height: 441px;
|
||||
height: 441px;
|
||||
resize: vertical;
|
||||
|
||||
.picture-preview {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
|
||||
.slider {
|
||||
background: linear-gradient(180deg, transparent, black);
|
||||
text-shadow: 1px 1px rgba(0, 0, 0, .5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.effect-grid {
|
||||
|
||||
.grid-item-container {
|
||||
height: 60px !important;
|
||||
max-height: 60px !important;
|
||||
|
||||
.grid-item {
|
||||
overflow: unset;
|
||||
|
||||
.remove-effect {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
right: -1px;
|
||||
padding: 2px;
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
min-height: unset;
|
||||
}
|
||||
|
||||
.effect-thumbnail-image {
|
||||
|
||||
img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
image-rendering: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.effects {
|
||||
height: 363px;
|
||||
min-height: 363px;
|
||||
@ -117,28 +164,13 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.picture-preview {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
|
||||
.slider {
|
||||
background: linear-gradient(180deg, transparent, black);
|
||||
text-shadow: 1px 1px rgba(0, 0, 0, .5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nitro-camera-checkout {
|
||||
width: 336px;
|
||||
width: 388px;
|
||||
|
||||
.picture-preview {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
|
||||
.slider {
|
||||
background: linear-gradient(180deg, transparent, black);
|
||||
text-shadow: 1px 1px rgba(0, 0, 0, .5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { InitCameraMessageEvent, IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect, RequestCameraConfigurationComposer, RoomCameraWidgetManagerEvent } from '@nitrots/nitro-renderer';
|
||||
import { InitCameraMessageEvent, IRoomCameraWidgetEffect, RequestCameraConfigurationComposer, RoomCameraWidgetManagerEvent } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { GetRoomCameraWidgetManager } from '../../../../api';
|
||||
import { RoomWidgetCameraEvent } from '../../../../events/room-widgets/camera/RoomWidgetCameraEvent';
|
||||
@ -22,9 +22,8 @@ export const CameraWidgetView: FC<{}> = props =>
|
||||
const [ availableEffects, setAvailableEffects ] = useState<IRoomCameraWidgetEffect[]>([]);
|
||||
const [ cameraRoll, setCameraRoll ] = useState<CameraPicture[]>([]);
|
||||
const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1);
|
||||
const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
|
||||
const [ isZoomed, setIsZoomed ] = useState(false);
|
||||
const [ myLevel, setMyLevel ] = useState(10);
|
||||
const [ base64Url, setSavedPictureUrl ] = useState<string>(null);
|
||||
const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null);
|
||||
|
||||
const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) =>
|
||||
@ -102,17 +101,20 @@ export const CameraWidgetView: FC<{}> = props =>
|
||||
case 'editor_cancel':
|
||||
setMode(MODE_CAPTURE);
|
||||
return;
|
||||
case 'checkout':
|
||||
setMode(MODE_CHECKOUT);
|
||||
return;
|
||||
}
|
||||
}, [ selectedPictureIndex ]);
|
||||
|
||||
const checkoutPictureUrl = useCallback((pictureUrl: string) =>
|
||||
{
|
||||
setSavedPictureUrl(pictureUrl);
|
||||
setMode(MODE_CHECKOUT);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CameraWidgetContextProvider value={ { cameraRoll, setCameraRoll, selectedPictureIndex, setSelectedPictureIndex, selectedEffects, setSelectedEffects, isZoomed, setIsZoomed } }>
|
||||
<CameraWidgetContextProvider value={ { cameraRoll, setCameraRoll, selectedPictureIndex, setSelectedPictureIndex } }>
|
||||
{ (mode === MODE_CAPTURE) && <CameraWidgetCaptureView onClose={ () => processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> }
|
||||
{ (mode === MODE_EDITOR) && <CameraWidgetEditorView myLevel={ myLevel } onClose={ () => processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> }
|
||||
{ (mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView onCloseClick={ () => processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }></CameraWidgetCheckoutView> }
|
||||
{ (mode === MODE_EDITOR) && <CameraWidgetEditorView picture={ cameraRoll[selectedPictureIndex] } myLevel={ myLevel } onClose={ () => processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ checkoutPictureUrl } availableEffects={ availableEffects } /> }
|
||||
{ (mode === MODE_CHECKOUT) && <CameraWidgetCheckoutView base64Url={ base64Url } onCloseClick={ () => processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }></CameraWidgetCheckoutView> }
|
||||
</CameraWidgetContextProvider>
|
||||
);
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
export class CameraPictureThumbnail
|
||||
{
|
||||
constructor(
|
||||
public effectName: string,
|
||||
public thumbnailUrl: string) {}
|
||||
}
|
@ -5,11 +5,7 @@ const CameraWidgetContext = createContext<ICameraWidgetContext>({
|
||||
cameraRoll: null,
|
||||
setCameraRoll: null,
|
||||
selectedPictureIndex: null,
|
||||
setSelectedPictureIndex: null,
|
||||
selectedEffects: null,
|
||||
setSelectedEffects: null,
|
||||
isZoomed: null,
|
||||
setIsZoomed: null
|
||||
setSelectedPictureIndex: null
|
||||
});
|
||||
|
||||
export const CameraWidgetContextProvider: FC<CameraWidgetContextProps> = props =>
|
||||
|
@ -1,17 +1,12 @@
|
||||
import { IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
|
||||
import { ProviderProps } from 'react';
|
||||
import { Dispatch, ProviderProps, SetStateAction } from 'react';
|
||||
import { CameraPicture } from '../common/CameraPicture';
|
||||
|
||||
export interface ICameraWidgetContext
|
||||
{
|
||||
cameraRoll: CameraPicture[],
|
||||
setCameraRoll: (cameraRoll: CameraPicture[]) => void,
|
||||
setCameraRoll: Dispatch<SetStateAction<CameraPicture[]>>;
|
||||
selectedPictureIndex: number,
|
||||
setSelectedPictureIndex: (index: number) => void,
|
||||
selectedEffects: IRoomCameraWidgetSelectedEffect[],
|
||||
setSelectedEffects: (selectedEffects: IRoomCameraWidgetSelectedEffect[]) => void,
|
||||
isZoomed: boolean,
|
||||
setIsZoomed: (isZoomed: boolean) => void
|
||||
setSelectedPictureIndex: Dispatch<SetStateAction<number>>;
|
||||
}
|
||||
|
||||
export interface CameraWidgetContextProps extends ProviderProps<ICameraWidgetContext>
|
||||
|
@ -1,26 +1,32 @@
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetRoomCameraWidgetManager } from '../../../../../../api/nitro/camera/GetRoomCameraWidgetManager';
|
||||
import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraStorageUrlMessageEvent, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { GetConfiguration, GetRoomEngine } from '../../../../../../api';
|
||||
import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks/messages/message-event';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../../layout';
|
||||
import { LocalizeText } from '../../../../../../utils/LocalizeText';
|
||||
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
|
||||
import { useCameraWidgetContext } from '../../context/CameraWidgetContext';
|
||||
import { CameraWidgetCheckoutViewProps } from './CameraWidgetCheckoutView.types';
|
||||
|
||||
export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props =>
|
||||
{
|
||||
const { onCloseClick = null, onCancelClick = null, price = null } = props;
|
||||
const { base64Url = null, onCloseClick = null, onCancelClick = null, price = null } = props;
|
||||
const [ pictureUrl, setPictureUrl ] = useState<string>(null);
|
||||
const [ publishUrl, setPublishUrl ] = useState<string>(null);
|
||||
const [ picturesBought, setPicturesBought ] = useState(0);
|
||||
const [ wasPicturePublished, setWasPicturePublished ] = useState(false);
|
||||
const [ isWaiting, setIsWaiting ] = useState(false);
|
||||
const [ publishCooldown, setPublishCooldown ] = useState(0);
|
||||
const { cameraRoll = null, selectedPictureIndex = -1, selectedEffects = null, isZoomed = false } = useCameraWidgetContext();
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(!base64Url) return;
|
||||
|
||||
GetRoomEngine().saveBase64AsScreenshot(base64Url);
|
||||
}, [ base64Url ]);
|
||||
|
||||
const onCameraPurchaseOKMessageEvent = useCallback((event: CameraPurchaseOKMessageEvent) =>
|
||||
{
|
||||
setPicturesBought(value => value + 1);
|
||||
setPicturesBought(value => (value + 1));
|
||||
setIsWaiting(false);
|
||||
}, []);
|
||||
|
||||
@ -30,6 +36,7 @@ export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setPublishUrl(parser.extraDataId);
|
||||
setPublishCooldown(parser.secondsToWait);
|
||||
setWasPicturePublished(parser.ok);
|
||||
setIsWaiting(false);
|
||||
@ -37,10 +44,14 @@ export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props
|
||||
|
||||
CreateMessageHook(CameraPublishStatusMessageEvent, onCameraPublishStatusMessageEvent);
|
||||
|
||||
const getCurrentPicture = useCallback(() =>
|
||||
const onCameraStorageUrlMessageEvent = useCallback((event: CameraStorageUrlMessageEvent) =>
|
||||
{
|
||||
return GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, selectedEffects, isZoomed);
|
||||
}, [ cameraRoll, selectedPictureIndex, selectedEffects, isZoomed ]);
|
||||
const parser = event.getParser();
|
||||
|
||||
setPictureUrl(GetConfiguration<string>('camera.url') + '/' + parser.url);
|
||||
}, []);
|
||||
|
||||
CreateMessageHook(CameraStorageUrlMessageEvent, onCameraStorageUrlMessageEvent);
|
||||
|
||||
const processAction = useCallback((type: string, value: string | number = null) =>
|
||||
{
|
||||
@ -53,7 +64,7 @@ export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props
|
||||
if(isWaiting) return;
|
||||
|
||||
setIsWaiting(true);
|
||||
SendMessageHook(new PurchasePhotoMessageComposer('1_1627697499'));
|
||||
SendMessageHook(new PurchasePhotoMessageComposer(''));
|
||||
return;
|
||||
case 'publish':
|
||||
if(isWaiting) return;
|
||||
@ -73,27 +84,58 @@ export const CameraWidgetCheckoutView: FC<CameraWidgetCheckoutViewProps> = props
|
||||
<NitroCardView className="nitro-camera-checkout" simple={ true }>
|
||||
<NitroCardHeaderView headerText={ LocalizeText('camera.confirm_phase.title') } onCloseClick={ event => processAction('close') } />
|
||||
<NitroCardContentView>
|
||||
<div className="picture-preview border mb-2" style={ { backgroundImage: 'url(' + getCurrentPicture().src + ')' } }></div>
|
||||
<div className="bg-muted rounded p-2 text-black mb-2 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<div className="fw-bold d-flex justify-content-start">{ LocalizeText('camera.purchase.header') }{ price.credits > 0 && <>: { price.credits } <CurrencyIcon type={ -1 } /></> }</div>
|
||||
{ picturesBought > 0 && <div>{ LocalizeText('camera.purchase.count.info') + ' ' + picturesBought }</div> }
|
||||
<div className="d-flex justify-content-center align-items-center picture-preview border mb-2" style={ pictureUrl ? { backgroundImage: 'url(' + pictureUrl + ')' } : {} }>
|
||||
{ !pictureUrl &&
|
||||
<div className="text-black fw-bold">
|
||||
{ LocalizeText('camera.loading') }
|
||||
</div> }
|
||||
</div>
|
||||
{ pictureUrl && <div className="text-black mb-2">{ LocalizeText('camera.confirm_phase.info') }</div> }
|
||||
<div className="d-flex justify-content-between bg-muted rounded p-2 text-black mb-2">
|
||||
<div className="d-flex flex-column">
|
||||
<div className="fw-bold d-flex justify-content-start">{ LocalizeText('camera.purchase.header') }</div>
|
||||
{ ((price.credits > 0) || (price.duckets > 0)) &&
|
||||
<div className="d-flex">
|
||||
<div className="me-1">{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }</div>
|
||||
{ (price.credits > 0) &&
|
||||
<div className="d-flex fw-bold">
|
||||
{ price.credits } <CurrencyIcon type={ -1 } />
|
||||
</div> }
|
||||
{ (price.duckets > 0) &&
|
||||
<div className="d-flex fw-bold">
|
||||
{ price.duckets } <CurrencyIcon type={ 5 } />
|
||||
</div> }
|
||||
</div> }
|
||||
{ (picturesBought > 0) &&
|
||||
<div>
|
||||
<span className="fw-bold">{ LocalizeText('camera.purchase.count.info') }</span> { picturesBought }
|
||||
<u className="ms-1">{ LocalizeText('camera.open.inventory') }</u>
|
||||
</div> }
|
||||
</div>
|
||||
<div>
|
||||
<button className="btn btn-success" disabled={ isWaiting } onClick={ event => processAction('buy') }>{ LocalizeText(picturesBought === 0 ? 'buy' : 'camera.buy.another.button.text') }</button>
|
||||
{ picturesBought > 0 && <div className="mt-1 text-center"><a href="#">{ LocalizeText('camera.open.inventory') }</a></div> }
|
||||
<div className="d-flex align-items-center">
|
||||
<button className="btn btn-success" disabled={ isWaiting } onClick={ event => processAction('buy') }>{ LocalizeText(!picturesBought ? 'buy' : 'camera.buy.another.button.text') }</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-muted rounded p-2 text-black mb-2">
|
||||
<div className="d-flex justify-content-between align-items-center">
|
||||
<div className="me-2">
|
||||
<div className="fw-bold d-flex justify-content-start">{ LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation') }{ !wasPicturePublished && price.duckets > 0 && <>: { price.duckets } <CurrencyIcon type={ price.publishDucketPrice } /></> }</div>
|
||||
<div>{ LocalizeText(wasPicturePublished ? 'camera.publish.success.short.info' : 'camera.publish.detailed.explanation') }</div>
|
||||
{ wasPicturePublished && <a href="#">{ LocalizeText('camera.link.to.published') }</a> }
|
||||
<div className="d-flex justify-content-between bg-muted rounded p-2 text-black mb-2">
|
||||
<div className="d-flex flex-column">
|
||||
<div className="fw-bold d-flex justify-content-start">{ LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation') }</div>
|
||||
<div>
|
||||
{ LocalizeText(wasPicturePublished ? 'camera.publish.success.short.info' : 'camera.publish.detailed.explanation') }
|
||||
</div>
|
||||
{ !wasPicturePublished && <button className="btn btn-success" disabled={ isWaiting || publishCooldown > 0 } onClick={ event => processAction('publish') }>{ LocalizeText('camera.publish.button.text') }</button> }
|
||||
{ wasPicturePublished && <a href={ publishUrl } rel="noreferrer" target="_blank">{ LocalizeText('camera.link.to.published') }</a> }
|
||||
{ !wasPicturePublished && (price.publishDucketPrice > 0) &&
|
||||
<div className="d-flex">
|
||||
<div className="me-1">{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }</div>
|
||||
<div className="d-flex fw-bold">
|
||||
{ price.publishDucketPrice } <CurrencyIcon type={ 5 } />
|
||||
</div>
|
||||
</div> }
|
||||
{ (publishCooldown > 0) && <div className="mt-1 text-center fw-bold">{ LocalizeText('camera.publish.wait', [ 'minutes' ], [ Math.ceil( publishCooldown / 60).toString() ]) }</div> }
|
||||
</div>
|
||||
{ publishCooldown > 0 && <div className="mt-1 text-center fw-bold">{ LocalizeText('camera.publish.wait', ['minutes'], [Math.ceil(publishCooldown/60).toString()]) }</div> }
|
||||
{ !wasPicturePublished &&
|
||||
<div className="d-flex align-items-end">
|
||||
<button className="btn btn-success" disabled={ isWaiting || publishCooldown > 0 } onClick={ event => processAction('publish') }>{ LocalizeText('camera.publish.button.text') }</button>
|
||||
</div> }
|
||||
</div>
|
||||
<div className="text-black mb-2 text-center">{ LocalizeText('camera.warning.disclaimer') }</div>
|
||||
<div className="d-flex justify-content-end">
|
||||
|
@ -1,5 +1,6 @@
|
||||
export interface CameraWidgetCheckoutViewProps
|
||||
{
|
||||
base64Url: string;
|
||||
onCloseClick: () => void;
|
||||
onCancelClick: () => void;
|
||||
price: { credits: number, duckets: number, publishDucketPrice: number };
|
||||
|
@ -1,94 +1,86 @@
|
||||
import { RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
|
||||
import classNames from 'classnames';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import { IRoomCameraWidgetSelectedEffect, RoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { GetRoomCameraWidgetManager } from '../../../../../../api';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../../../layout';
|
||||
import { LocalizeText } from '../../../../../../utils/LocalizeText';
|
||||
import { useCameraWidgetContext } from '../../context/CameraWidgetContext';
|
||||
import { CameraPictureThumbnail } from '../../common/CameraPictureThumbnail';
|
||||
import { CameraWidgetEditorTabs, CameraWidgetEditorViewProps } from './CameraWidgetEditorView.types';
|
||||
import { CameraWidgetEffectListView } from './effect-list/CameraWidgetEffectListView';
|
||||
|
||||
const TABS: string[] = [ CameraWidgetEditorTabs.COLORMATRIX, CameraWidgetEditorTabs.COMPOSITE ];
|
||||
|
||||
export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
{
|
||||
const { availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props;
|
||||
const { picture = null, availableEffects = null, myLevel = 1, onClose = null, onCancel = null, onCheckout = null } = props;
|
||||
const [ currentTab, setCurrentTab ] = useState(TABS[0]);
|
||||
const [ selectedEffectName, setSelectedEffectName ] = useState(null);
|
||||
const [ effectsThumbnails, setEffectsThumbnails ] = useState<{ name: string, image: HTMLImageElement }[]>([]);
|
||||
const { cameraRoll = null, selectedPictureIndex = -1, selectedEffects = null, isZoomed = false, setSelectedEffects = null, setIsZoomed = null } = useCameraWidgetContext();
|
||||
const [ selectedEffectName, setSelectedEffectName ] = useState<string>(null);
|
||||
const [ selectedEffects, setSelectedEffects ] = useState<IRoomCameraWidgetSelectedEffect[]>([]);
|
||||
const [ effectsThumbnails, setEffectsThumbnails ] = useState<CameraPictureThumbnail[]>([]);
|
||||
const [ isZoomed, setIsZoomed ] = useState(false);
|
||||
|
||||
useEffect(() =>
|
||||
const getColorMatrixEffects = useMemo(() =>
|
||||
{
|
||||
const thumbnails = [];
|
||||
return availableEffects.filter(effect => effect.colorMatrix);
|
||||
}, [ availableEffects ]);
|
||||
|
||||
for(const effect of availableEffects)
|
||||
{
|
||||
thumbnails.push({name: effect.name, image: GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false)});
|
||||
}
|
||||
|
||||
setEffectsThumbnails(thumbnails);
|
||||
}, [ cameraRoll, selectedPictureIndex, availableEffects ]);
|
||||
|
||||
const getEffectThumbnail = useCallback((effectName: string) =>
|
||||
const getCompositeEffects = useMemo(() =>
|
||||
{
|
||||
const search = effectsThumbnails.find(thumbnail => thumbnail.name === effectName);
|
||||
|
||||
if(search) return search.image.src;
|
||||
|
||||
return null;
|
||||
}, [ effectsThumbnails ]);
|
||||
return availableEffects.filter(effect => effect.texture);
|
||||
}, [ availableEffects ]);
|
||||
|
||||
const getEffectList = useCallback(() =>
|
||||
{
|
||||
if(currentTab === CameraWidgetEditorTabs.COLORMATRIX)
|
||||
{
|
||||
return availableEffects.filter(effect => effect.colorMatrix);
|
||||
return getColorMatrixEffects;
|
||||
}
|
||||
else
|
||||
{
|
||||
return availableEffects.filter(effect => effect.texture);
|
||||
}
|
||||
}, [ currentTab, availableEffects ]);
|
||||
|
||||
const getCurrentPicture = useCallback(() =>
|
||||
return getCompositeEffects;
|
||||
}, [ currentTab, getColorMatrixEffects, getCompositeEffects ]);
|
||||
|
||||
const getSelectedEffectIndex = useCallback((name: string) =>
|
||||
{
|
||||
return GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, selectedEffects, isZoomed);
|
||||
}, [ cameraRoll, selectedPictureIndex, selectedEffects, isZoomed ]);
|
||||
if(!name || !name.length || !selectedEffects || !selectedEffects.length) return -1;
|
||||
|
||||
const getCurrentEffectAlpha = useCallback(() =>
|
||||
{
|
||||
if(!selectedEffectName) return 0;
|
||||
|
||||
const selectedEffect = selectedEffects.find(effect => effect.effect.name === selectedEffectName);
|
||||
|
||||
if(!selectedEffect) return 0;
|
||||
|
||||
return selectedEffect.alpha;
|
||||
}, [ selectedEffectName, selectedEffects ]);
|
||||
|
||||
const getEffectIndex = useCallback((effectName) =>
|
||||
{
|
||||
return selectedEffects.findIndex(effect => effect.effect.name === effectName);
|
||||
return selectedEffects.findIndex(effect => (effect.effect.name === name));
|
||||
}, [ selectedEffects ])
|
||||
|
||||
const setSelectedEffectAlpha = useCallback((newAlpha: number) =>
|
||||
const getCurrentEffectIndex = useMemo(() =>
|
||||
{
|
||||
if(!selectedEffectName) return;
|
||||
return getSelectedEffectIndex(selectedEffectName)
|
||||
}, [ selectedEffectName, getSelectedEffectIndex ])
|
||||
|
||||
const selectedEffectIndex = getEffectIndex(selectedEffectName);
|
||||
const getCurrentEffect = useMemo(() =>
|
||||
{
|
||||
if(!selectedEffectName) return null;
|
||||
|
||||
if(selectedEffectIndex === -1) return;
|
||||
return (selectedEffects[getCurrentEffectIndex] || null);
|
||||
}, [ selectedEffectName, getCurrentEffectIndex, selectedEffects ]);
|
||||
|
||||
const clone = Array.from(selectedEffects);
|
||||
const setSelectedEffectAlpha = useCallback((alpha: number) =>
|
||||
{
|
||||
const index = getCurrentEffectIndex;
|
||||
|
||||
const selectedEffect = clone[selectedEffectIndex];
|
||||
if(index === -1) return;
|
||||
|
||||
clone[selectedEffectIndex] = new RoomCameraWidgetSelectedEffect(selectedEffect.effect, newAlpha);
|
||||
setSelectedEffects(prevValue =>
|
||||
{
|
||||
const clone = [ ...prevValue ];
|
||||
const currentEffect = clone[index];
|
||||
|
||||
setSelectedEffects(clone);
|
||||
}, [ selectedEffectName, getEffectIndex, selectedEffects, setSelectedEffects ]);
|
||||
clone[getCurrentEffectIndex] = new RoomCameraWidgetSelectedEffect(currentEffect.effect, alpha);
|
||||
|
||||
const processAction = useCallback((type: string, value: string | number = null) =>
|
||||
return clone;
|
||||
});
|
||||
}, [ getCurrentEffectIndex, setSelectedEffects ]);
|
||||
|
||||
const getCurrentPictureUrl = useMemo(() =>
|
||||
{
|
||||
return GetRoomCameraWidgetManager().applyEffects(picture.texture, selectedEffects, isZoomed).src;
|
||||
}, [ picture, selectedEffects, isZoomed ]);
|
||||
|
||||
const processAction = useCallback((type: string, effectName: string = null) =>
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
@ -99,134 +91,126 @@ export const CameraWidgetEditorView: FC<CameraWidgetEditorViewProps> = props =>
|
||||
onCancel();
|
||||
return;
|
||||
case 'checkout':
|
||||
onCheckout();
|
||||
onCheckout(getCurrentPictureUrl);
|
||||
return;
|
||||
case 'change_tab':
|
||||
setCurrentTab(String(value));
|
||||
setCurrentTab(String(effectName));
|
||||
return;
|
||||
case 'select_effect':
|
||||
{
|
||||
let existingIndex = -1;
|
||||
case 'select_effect': {
|
||||
let existingIndex = getSelectedEffectIndex(effectName);
|
||||
|
||||
if(selectedEffects.length > 0)
|
||||
if(existingIndex >= 0) return;
|
||||
|
||||
const effect = availableEffects.find(effect => (effect.name === effectName));
|
||||
|
||||
if(!effect) return;
|
||||
|
||||
setSelectedEffects(prevValue =>
|
||||
{
|
||||
existingIndex = getEffectIndex(value);
|
||||
}
|
||||
return [ ...prevValue, new RoomCameraWidgetSelectedEffect(effect, 1) ];
|
||||
});
|
||||
|
||||
let effect = null;
|
||||
|
||||
if(existingIndex === -1)
|
||||
{
|
||||
effect = availableEffects.find(effect => effect.name === value);
|
||||
|
||||
if(effect.minLevel > myLevel) return;
|
||||
|
||||
setSelectedEffects([...selectedEffects, new RoomCameraWidgetSelectedEffect(effect, 0.5)]);
|
||||
}
|
||||
|
||||
if(effect && effect.minLevel > myLevel) return;
|
||||
|
||||
if(selectedEffectName !== value)
|
||||
{
|
||||
setSelectedEffectName(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
setSelectedEffectName(null);
|
||||
}
|
||||
}
|
||||
setSelectedEffectName(effect.name);
|
||||
return;
|
||||
case 'remove_effect':
|
||||
{
|
||||
const existingIndex = getEffectIndex(value);
|
||||
}
|
||||
case 'remove_effect': {
|
||||
let existingIndex = getSelectedEffectIndex(effectName);
|
||||
|
||||
if(existingIndex > -1)
|
||||
if(existingIndex === -1) return;
|
||||
|
||||
setSelectedEffects(prevValue =>
|
||||
{
|
||||
const effect = selectedEffects[existingIndex];
|
||||
const clone = [ ...prevValue ];
|
||||
|
||||
if(effect.effect.name === selectedEffectName)
|
||||
{
|
||||
setSelectedEffectName(null);
|
||||
}
|
||||
|
||||
const clone = Array.from(selectedEffects);
|
||||
clone.splice(existingIndex, 1);
|
||||
|
||||
setSelectedEffects(clone);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
});
|
||||
|
||||
if(selectedEffectName === effectName) setSelectedEffectName(null);
|
||||
return;
|
||||
}
|
||||
case 'clear_effects':
|
||||
setSelectedEffectName(null);
|
||||
setSelectedEffects([]);
|
||||
return;
|
||||
case 'download':
|
||||
window.open(getCurrentPicture().src, '_blank');
|
||||
case 'download': {
|
||||
const image = new Image();
|
||||
|
||||
image.src = getCurrentPictureUrl
|
||||
|
||||
const newWindow = window.open('');
|
||||
newWindow.document.write(image.outerHTML);
|
||||
return;
|
||||
}
|
||||
case 'zoom':
|
||||
setIsZoomed(!isZoomed);
|
||||
return;
|
||||
}
|
||||
}, [ onClose, onCancel, onCheckout, getCurrentPicture, myLevel, selectedEffectName, getEffectIndex, availableEffects, isZoomed, setIsZoomed, selectedEffects, setSelectedEffects ]);
|
||||
}, [ isZoomed, availableEffects, selectedEffectName, getCurrentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose, setIsZoomed, setSelectedEffects ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const thumbnails: CameraPictureThumbnail[] = [];
|
||||
|
||||
for(const effect of availableEffects)
|
||||
{
|
||||
thumbnails.push(new CameraPictureThumbnail(effect.name, GetRoomCameraWidgetManager().applyEffects(picture.texture, [ new RoomCameraWidgetSelectedEffect(effect, 1) ], false).src));
|
||||
}
|
||||
|
||||
setEffectsThumbnails(thumbnails);
|
||||
}, [ picture, availableEffects ]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-camera-editor">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('camera.editor.button.text') } onCloseClick={ event => processAction('close') } />
|
||||
<div className="d-flex">
|
||||
<div className="w-100">
|
||||
<NitroCardTabsView>
|
||||
{ TABS.map(tab =>
|
||||
{
|
||||
return <NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ event => processAction('change_tab', tab) }><i className={ 'icon icon-camera-' + tab }></i></NitroCardTabsItemView>
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex h-100 overflow-auto effects px-2">
|
||||
<div className="row row-cols-3">
|
||||
{ getEffectList().map(effect =>
|
||||
{
|
||||
return (
|
||||
<div key={ effect.name } className="col mb-3 position-relative">
|
||||
{ getEffectIndex(effect.name) > -1 && <button className="btn btn-danger btn-sm p-0 position-absolute btn-remove-effect" onClick={ event => processAction('remove_effect', effect.name) }><i className="fas fa-times"></i></button> }
|
||||
<div title={ effect.minLevel <= myLevel ? LocalizeText('camera.effect.name.' + effect.name) : LocalizeText('camera.effect.required.level') + ' ' + effect.minLevel } onClick={ event => processAction('select_effect', effect.name) } className={"effect-thumbnail cursor-pointer position-relative border border-2 rounded d-flex flex-column justify-content-center align-items-center py-1" + classNames({' active': selectedEffectName === effect.name})}>
|
||||
{ effect.minLevel <= myLevel && <div className="effect-thumbnail-image border">
|
||||
<img alt="" src={ getEffectThumbnail(effect.name) } />
|
||||
</div> }
|
||||
{ effect.minLevel > myLevel && <div className="text-center text-black">
|
||||
<div><i className="fas fa-lock"></i></div>
|
||||
<div className="fw-bold">{ effect.minLevel }</div>
|
||||
</div> }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}) }
|
||||
</div>
|
||||
<NitroCardTabsView>
|
||||
{ TABS.map(tab =>
|
||||
{
|
||||
return <NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ event => processAction('change_tab', tab) }><i className={ 'icon icon-camera-' + tab }></i></NitroCardTabsItemView>
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="row h-100">
|
||||
<div className="col-5 d-flex flex-column h-100">
|
||||
<CameraWidgetEffectListView myLevel={ myLevel } selectedEffects={ selectedEffects } effects={ getEffectList() } thumbnails={ effectsThumbnails } processAction={ processAction } />
|
||||
</div>
|
||||
<div className="col-7 d-flex flex-column h-100">
|
||||
<div className="picture-preview">
|
||||
<img alt="" src={ getCurrentPictureUrl } />
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</div>
|
||||
<div className="w-100">
|
||||
<NitroCardTabsView></NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex align-items-end picture-preview border" style={ { backgroundImage: 'url(' + getCurrentPicture().src + ')' } }>
|
||||
{ selectedEffectName && <div className="w-100 p-2 d-flex flex-column justify-content-center slider">
|
||||
<div className="w-100 text-center">{ LocalizeText('camera.effect.name.' + selectedEffectName) + ' - ' + getCurrentEffectAlpha() }</div>
|
||||
<input type="range" min="0" max="1" step="0.01" value={ getCurrentEffectAlpha() } onChange={ event => setSelectedEffectAlpha(Number(event.target.value)) } className="form-range w-100" />
|
||||
{ selectedEffectName &&
|
||||
<div className="w-100 p-2 d-flex flex-column justify-content-center slider">
|
||||
<div className="w-100 text-center">{ LocalizeText('camera.effect.name.' + selectedEffectName) }</div>
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 0 }
|
||||
max={ 1 }
|
||||
step={ 0.01 }
|
||||
value={ getCurrentEffect.alpha }
|
||||
onChange={ event => setSelectedEffectAlpha(event) }
|
||||
renderThumb={ (props, state) => <div { ...props }>{ state.valueNow }</div> } />
|
||||
</div> }
|
||||
</div>
|
||||
<div className="d-flex justify-content-between mt-2">
|
||||
<div className="btn-group">
|
||||
<button className="btn btn-primary" onClick={ event => processAction('clear_effects') }><i className="fas fa-trash"></i></button>
|
||||
<button className="btn btn-primary" onClick={ event => processAction('download') }><i className="fas fa-save"></i></button>
|
||||
<button className="btn btn-primary" onClick={ event => processAction('zoom') }><i className={"fas " + classNames({'fa-search-plus': !isZoomed, 'fa-search-minus': isZoomed})}></i></button>
|
||||
<button className="btn btn-primary" onClick={ event => processAction('clear_effects') }>
|
||||
<i className="fas fa-trash"></i>
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={ event => processAction('download') }>
|
||||
<i className="fas fa-save"></i>
|
||||
</button>
|
||||
<button className="btn btn-primary" onClick={ event => processAction('zoom') }>
|
||||
<i className={ `fas fa-search-${ isZoomed ? 'minus': 'plus' }` } />
|
||||
</button>
|
||||
</div>
|
||||
<div className="d-flex justify-content-end">
|
||||
<button className="btn btn-primary me-2" onClick={ event => processAction('cancel') }>{ LocalizeText('generic.cancel') }</button>
|
||||
<button className="btn btn-success" onClick={ event => processAction('checkout') }>{ LocalizeText('camera.preview.button.text') }</button>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer';
|
||||
import { CameraPicture } from '../../common/CameraPicture';
|
||||
|
||||
export interface CameraWidgetEditorViewProps
|
||||
{
|
||||
picture: CameraPicture;
|
||||
availableEffects: IRoomCameraWidgetEffect[];
|
||||
myLevel: number;
|
||||
onClose: () => void;
|
||||
onCancel: () => void;
|
||||
onCheckout: () => void;
|
||||
onCheckout: (pictureUrl: string) => void;
|
||||
}
|
||||
|
||||
export class CameraWidgetEditorTabs
|
||||
|
@ -0,0 +1,29 @@
|
||||
import { FC } from 'react';
|
||||
import { NitroCardGridItemView } from '../../../../../../../layout/card/grid/item/NitroCardGridItemView';
|
||||
import { LocalizeText } from '../../../../../../../utils';
|
||||
import { CameraWidgetEffectListItemViewProps } from './CameraWidgetEffectListItemView.types';
|
||||
|
||||
export const CameraWidgetEffectListItemView: FC<CameraWidgetEffectListItemViewProps> = props =>
|
||||
{
|
||||
const { effect = null, thumbnailUrl = null, isActive = false, isLocked = false, selectEffect = null, removeEffect = null } = props;
|
||||
|
||||
return (
|
||||
<NitroCardGridItemView title={ LocalizeText(!isLocked ? (`camera.effect.name.${ effect.name }`) : `camera.effect.required.level ${ effect.minLevel }`) } itemActive={ isActive } onClick={ event => (!isActive && selectEffect()) }>
|
||||
{ isActive &&
|
||||
<button className="btn btn-danger btn-sm rounded-circle remove-effect" onClick={ removeEffect }>
|
||||
<i className="fas fa-times" />
|
||||
</button> }
|
||||
{ !isLocked && (thumbnailUrl && thumbnailUrl.length > 0) &&
|
||||
<div className="effect-thumbnail-image border">
|
||||
<img alt="" src={ thumbnailUrl } />
|
||||
</div> }
|
||||
{ isLocked &&
|
||||
<div className="text-center text-black">
|
||||
<div>
|
||||
<i className="fas fa-lock" />
|
||||
</div>
|
||||
<div className="fw-bold">{ effect.minLevel }</div>
|
||||
</div> }
|
||||
</NitroCardGridItemView>
|
||||
);
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer';
|
||||
|
||||
export interface CameraWidgetEffectListItemViewProps
|
||||
{
|
||||
effect: IRoomCameraWidgetEffect;
|
||||
thumbnailUrl: string;
|
||||
isActive: boolean;
|
||||
isLocked: boolean;
|
||||
selectEffect: () => void;
|
||||
removeEffect: () => void;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { FC } from 'react';
|
||||
import { NitroCardGridView } from '../../../../../../../layout/card/grid/NitroCardGridView';
|
||||
import { CameraWidgetEffectListItemView } from '../effect-list-item/CameraWidgetEffectListItemView';
|
||||
import { CameraWidgetEffectListViewProps } from './CameraWidgetEffectListView.types';
|
||||
|
||||
export const CameraWidgetEffectListView: FC<CameraWidgetEffectListViewProps> = props =>
|
||||
{
|
||||
const { myLevel = 0, selectedEffects = [], effects = [], thumbnails = [], processAction = null } = props;
|
||||
|
||||
return (
|
||||
<NitroCardGridView className="effect-grid" columns={ 3 }>
|
||||
{ effects && (effects.length > 0) && effects.map((effect, index) =>
|
||||
{
|
||||
const thumbnailUrl = (thumbnails.find(thumbnail => (thumbnail.effectName === effect.name)));
|
||||
const isActive = (selectedEffects.findIndex(selectedEffect => (selectedEffect.effect.name === effect.name)) > -1);
|
||||
|
||||
return <CameraWidgetEffectListItemView key={ index } effect={ effect } thumbnailUrl={ ((thumbnailUrl && thumbnailUrl.thumbnailUrl) || null) } isActive={ isActive } isLocked={ (effect.minLevel > myLevel) } selectEffect={ () => processAction('select_effect', effect.name) } removeEffect={ () => processAction('remove_effect', effect.name) } />
|
||||
}) }
|
||||
</NitroCardGridView>
|
||||
);
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
import { IRoomCameraWidgetEffect, IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer';
|
||||
import { CameraPictureThumbnail } from '../../../common/CameraPictureThumbnail';
|
||||
|
||||
export interface CameraWidgetEffectListViewProps
|
||||
{
|
||||
myLevel: number;
|
||||
selectedEffects: IRoomCameraWidgetSelectedEffect[];
|
||||
effects: IRoomCameraWidgetEffect[];
|
||||
thumbnails: CameraPictureThumbnail[];
|
||||
processAction: (type: string, name: string) => void;
|
||||
}
|
11
src/views/room/widgets/choosers/ChooserWidgetView.scss
Normal file
11
src/views/room/widgets/choosers/ChooserWidgetView.scss
Normal file
@ -0,0 +1,11 @@
|
||||
.nitro-chooser-widget {
|
||||
|
||||
.list-item {
|
||||
color: black;
|
||||
overflow: hidden;
|
||||
|
||||
&.selected {
|
||||
background-color: cadetblue;
|
||||
}
|
||||
}
|
||||
}
|
66
src/views/room/widgets/choosers/ChooserWidgetView.tsx
Normal file
66
src/views/room/widgets/choosers/ChooserWidgetView.tsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { List, ListRowProps, ListRowRenderer } from 'react-virtualized';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { LocalizeText } from '../../../../utils';
|
||||
import { useRoomContext } from '../../context/RoomContext';
|
||||
import { RoomObjectItem } from '../../events';
|
||||
import { RoomWidgetRoomObjectMessage } from '../../messages';
|
||||
import { ChooserWidgetViewProps } from './ChooserWidgetView.type';
|
||||
|
||||
export const ChooserWidgetView: FC<ChooserWidgetViewProps> = props =>
|
||||
{
|
||||
const { title = null, items = null, displayItemId = false, onCloseClick = null } = props;
|
||||
const [ selectedItem, setSelectedItem ] = useState<RoomObjectItem>(null);
|
||||
const [ searchValue, setSearchValue ] = useState('');
|
||||
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
|
||||
|
||||
const filteredItems = useMemo(() =>
|
||||
{
|
||||
if(!items) return [];
|
||||
|
||||
if(!searchValue || !searchValue.length) return items;
|
||||
|
||||
const value = searchValue.toLocaleLowerCase();
|
||||
|
||||
return items.filter(item =>
|
||||
{
|
||||
return item.name.toLocaleLowerCase().includes(value);
|
||||
});
|
||||
}, [ items, searchValue ]);
|
||||
|
||||
const onItemClick = useCallback((item: RoomObjectItem) =>
|
||||
{
|
||||
setSelectedItem(item);
|
||||
widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category));
|
||||
}, [ widgetHandler, setSelectedItem]);
|
||||
|
||||
const rowRenderer: ListRowRenderer = (props: ListRowProps) =>
|
||||
{
|
||||
const item = filteredItems[props.index];
|
||||
|
||||
return (
|
||||
<div key={ props.key } className={ 'list-item' + ((selectedItem === item) ? ' selected' : '') } style={ props.style } onClick={ () => onItemClick(item) }>
|
||||
{ item.name } { displayItemId && (' - ' + item.id) }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-chooser-widget">
|
||||
<NitroCardHeaderView headerText={ title } onCloseClick={ onCloseClick } />
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex mb-1">
|
||||
<div className="d-flex flex-grow-1 me-1">
|
||||
<input type="text" className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } value={searchValue} onChange={event => setSearchValue(event.target.value)} />
|
||||
</div>
|
||||
</div>
|
||||
<List
|
||||
width={ 150 }
|
||||
height={ 150 }
|
||||
rowCount={ filteredItems.length }
|
||||
rowHeight={ 20 }
|
||||
rowRenderer={ rowRenderer } />
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
import { RoomObjectItem } from '../../events';
|
||||
|
||||
export interface ChooserWidgetViewProps
|
||||
{
|
||||
title: string;
|
||||
items: RoomObjectItem[];
|
||||
displayItemId: boolean;
|
||||
onCloseClick: () => void;
|
||||
}
|
61
src/views/room/widgets/choosers/FurniChooserWidgetView.tsx
Normal file
61
src/views/room/widgets/choosers/FurniChooserWidgetView.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { CreateEventDispatcherHook } from '../../../../hooks';
|
||||
import { LocalizeText } from '../../../../utils';
|
||||
import { useRoomContext } from '../../context/RoomContext';
|
||||
import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRoomObjectUpdateEvent } from '../../events';
|
||||
import { RoomWidgetRequestWidgetMessage } from '../../messages';
|
||||
import { ChooserWidgetView } from './ChooserWidgetView';
|
||||
|
||||
export const FurniChooserWidgetView: FC<{}> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ items, setItems ] = useState<RoomObjectItem[]>(null);
|
||||
const [ refreshTimeout, setRefreshTimeout ] = useState<ReturnType<typeof setTimeout>>(null);
|
||||
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
|
||||
|
||||
const refreshChooser = useCallback(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
setRefreshTimeout(prevValue =>
|
||||
{
|
||||
if(prevValue) clearTimeout(prevValue);
|
||||
|
||||
return setTimeout(() => widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.FURNI_CHOOSER)), 100);
|
||||
})
|
||||
}, [ isVisible, widgetHandler ]);
|
||||
|
||||
const onRoomWidgetChooserContentEvent = useCallback((event: RoomWidgetChooserContentEvent) =>
|
||||
{
|
||||
setItems(event.items);
|
||||
setIsVisible(true);
|
||||
}, []);
|
||||
|
||||
CreateEventDispatcherHook(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent);
|
||||
|
||||
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
switch(event.type)
|
||||
{
|
||||
case RoomWidgetRoomObjectUpdateEvent.FURNI_ADDED:
|
||||
case RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED:
|
||||
refreshChooser();
|
||||
return;
|
||||
}
|
||||
}, [ isVisible, refreshChooser ]);
|
||||
|
||||
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
|
||||
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
|
||||
|
||||
const close = useCallback(() =>
|
||||
{
|
||||
setIsVisible(false);
|
||||
setItems(null);
|
||||
}, []);
|
||||
|
||||
if (!isVisible) return null;
|
||||
|
||||
return <ChooserWidgetView title={ LocalizeText('widget.chooser.furni.title') } displayItemId={ false } items={ items } onCloseClick={ close } />;
|
||||
}
|
61
src/views/room/widgets/choosers/UserChooserWidgetView.tsx
Normal file
61
src/views/room/widgets/choosers/UserChooserWidgetView.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { CreateEventDispatcherHook } from '../../../../hooks';
|
||||
import { LocalizeText } from '../../../../utils';
|
||||
import { useRoomContext } from '../../context/RoomContext';
|
||||
import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRoomObjectUpdateEvent } from '../../events';
|
||||
import { RoomWidgetRequestWidgetMessage } from '../../messages';
|
||||
import { ChooserWidgetView } from './ChooserWidgetView';
|
||||
|
||||
export const UserChooserWidgetView: FC<{}> = props =>
|
||||
{
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const [ items, setItems ] = useState<RoomObjectItem[]>(null);
|
||||
const [ refreshTimeout, setRefreshTimeout ] = useState<ReturnType<typeof setTimeout>>(null);
|
||||
const { eventDispatcher = null, widgetHandler = null } = useRoomContext();
|
||||
|
||||
const refreshChooser = useCallback(() =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
setRefreshTimeout(prevValue =>
|
||||
{
|
||||
if(prevValue) clearTimeout(prevValue);
|
||||
|
||||
return setTimeout(() => widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.FURNI_CHOOSER)), 100);
|
||||
})
|
||||
}, [ isVisible, widgetHandler ]);
|
||||
|
||||
const onRoomWidgetChooserContentEvent = useCallback((event: RoomWidgetChooserContentEvent) =>
|
||||
{
|
||||
setItems(event.items);
|
||||
setIsVisible(true);
|
||||
}, []);
|
||||
|
||||
CreateEventDispatcherHook(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent);
|
||||
|
||||
const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) =>
|
||||
{
|
||||
if(!isVisible) return;
|
||||
|
||||
switch(event.type)
|
||||
{
|
||||
case RoomWidgetRoomObjectUpdateEvent.USER_ADDED:
|
||||
case RoomWidgetRoomObjectUpdateEvent.USER_REMOVED:
|
||||
refreshChooser();
|
||||
return;
|
||||
}
|
||||
}, [ isVisible, refreshChooser ]);
|
||||
|
||||
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
|
||||
CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent);
|
||||
|
||||
const close = useCallback(() =>
|
||||
{
|
||||
setIsVisible(false);
|
||||
setItems(null);
|
||||
}, []);
|
||||
|
||||
if(!isVisible) return null;
|
||||
|
||||
return <ChooserWidgetView title={ LocalizeText('widget.chooser.user.title') } displayItemId={ true } items={ items } onCloseClick={ close } />;
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
width: 125px;
|
||||
max-width: 125px;
|
||||
padding: 2px;
|
||||
background-color: #1c323f;
|
||||
background-color: $gable-green;
|
||||
border: 2px solid rgba($white, 0.5);
|
||||
border-radius: $border-radius;
|
||||
font-size: $font-size-sm;
|
||||
@ -32,7 +32,7 @@
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
background-color: #3d5f6e;
|
||||
background-color: $william;
|
||||
color: $white;
|
||||
height: 25px;
|
||||
max-height: 25px;
|
||||
|
@ -80,8 +80,8 @@ export const ContextMenuView: FC<ContextMenuViewProps> = props =>
|
||||
|
||||
const deltaY = (location.y - maxStack);
|
||||
|
||||
let x = (location.x - (elementRef.current.offsetWidth / 2));
|
||||
let y = (deltaY + offset);
|
||||
let x = Math.round(location.x - (elementRef.current.offsetWidth / 2));
|
||||
let y = Math.round(deltaY + offset);
|
||||
|
||||
const maxLeft = ((GetNitroInstance().width - elementRef.current.offsetWidth) - SPACE_AROUND_EDGES);
|
||||
const maxTop = ((GetNitroInstance().height - elementRef.current.offsetHeight) - SPACE_AROUND_EDGES);
|
||||
|
22
src/views/room/widgets/doorbell/DoorbellWidgetView.scss
Normal file
22
src/views/room/widgets/doorbell/DoorbellWidgetView.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
src/views/room/widgets/doorbell/DoorbellWidgetView.tsx
Normal file
81
src/views/room/widgets/doorbell/DoorbellWidgetView.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
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';
|
||||
import { DoorbellWidgetItemView } from './doorbell-item/DoorbellWidgetItemView';
|
||||
|
||||
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(!isVisible) 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 <DoorbellWidgetItemView key={ userName } userName={ userName } accept={ () => answer(userName, true) } deny={ () => answer(userName, false) } />;
|
||||
}) }
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView>
|
||||
);
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
import { FC } from 'react';
|
||||
import { DoorbellWidgetItemViewProps } from './DoorbellWidgetItemView.types';
|
||||
|
||||
export const DoorbellWidgetItemView: FC<DoorbellWidgetItemViewProps> = props =>
|
||||
{
|
||||
const { userName = '', accept = null, deny = null } = props;
|
||||
|
||||
return (
|
||||
<div className="d-flex col align-items-center justify-content-between">
|
||||
<span className="fw-bold text-black">{ userName }</span>
|
||||
<div>
|
||||
<button type="button" className="btn btn-success btn-sm me-1" onClick={ accept }>
|
||||
<i className="fas fa-check" />
|
||||
</button>
|
||||
<button type="button" className="btn btn-danger btn-sm" onClick={ deny }>
|
||||
<i className="fas fa-times" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
export interface DoorbellWidgetItemViewProps
|
||||
{
|
||||
userName: string;
|
||||
accept: () => void;
|
||||
deny: () => void;
|
||||
}
|
@ -119,7 +119,7 @@ export const FurnitureFriendFurniView: FC<{}> = props =>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</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="engraving-lock-close" onClick={ event => processAction('close_view') }></div>
|
||||
<div className="d-flex justify-content-center">
|
||||
|
@ -3,6 +3,7 @@ import { FC, useCallback, useState } from 'react';
|
||||
import { GetRoomEngine, GetRoomSession, GetSessionDataManager } from '../../../../../api';
|
||||
import { CreateEventDispatcherHook } from '../../../../../hooks/events/event-dispatcher.base';
|
||||
import { useRoomEngineEvent } from '../../../../../hooks/events/nitro/room/room-engine-event';
|
||||
import { DraggableWindowPosition } from '../../../../../layout';
|
||||
import { DraggableWindow } from '../../../../../layout/draggable-window/DraggableWindow';
|
||||
import { ColorUtils } from '../../../../../utils/ColorUtils';
|
||||
import { useRoomContext } from '../../../context/RoomContext';
|
||||
@ -114,7 +115,7 @@ export const FurnitureStickieView: FC<{}> = props =>
|
||||
if(!stickieData) return null;
|
||||
|
||||
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="d-flex align-items-center stickie-header drag-handler">
|
||||
<div className="d-flex align-items-center flex-grow-1 h-100">
|
||||
|
@ -1,14 +0,0 @@
|
||||
.nitro-room-thumbnail {
|
||||
width: 300px;
|
||||
|
||||
.option {
|
||||
font-size: 30px;
|
||||
height: 50px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
@import './views/builder/RoomThumbnailWidgetBuilderView';
|
||||
@import './views/camera/RoomThumbnailWidgetCameraView';
|
@ -1,33 +1,28 @@
|
||||
import { NitroRenderTexture } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { GetRoomEngine } from '../../../../api';
|
||||
import { RoomWidgetThumbnailEvent } from '../../../../events/room-widgets/thumbnail';
|
||||
import { useUiEvent } from '../../../../hooks/events';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||
import { LocalizeText } from '../../../../utils/LocalizeText';
|
||||
import { RoomThumbnailWidgetBuilderView } from './views/builder/RoomThumbnailWidgetBuilderView';
|
||||
import { RoomThumbnailWidgetCameraView } from './views/camera/RoomThumbnailWidgetCameraView';
|
||||
import { NitroLayoutMiniCameraView } from '../../../../layout';
|
||||
import { useRoomContext } from '../../context/RoomContext';
|
||||
|
||||
export const RoomThumbnailWidgetView: FC<{}> = props =>
|
||||
{
|
||||
const [ isSelectorVisible, setIsSelectorVisible ] = useState(false);
|
||||
const [ isBuilderVisible, setIsBuilderVisible ] = useState(false);
|
||||
const [ isCameraVisible, setIsCameraVisible ] = useState(false);
|
||||
const [ isVisible, setIsVisible ] = useState(false);
|
||||
const { roomSession = null } = useRoomContext();
|
||||
|
||||
const onNitroEvent = useCallback((event: RoomWidgetThumbnailEvent) =>
|
||||
{
|
||||
switch(event.type)
|
||||
{
|
||||
case RoomWidgetThumbnailEvent.SHOW_THUMBNAIL:
|
||||
setIsSelectorVisible(true);
|
||||
setIsVisible(true);
|
||||
return;
|
||||
case RoomWidgetThumbnailEvent.HIDE_THUMBNAIL:
|
||||
setIsSelectorVisible(false);
|
||||
setIsBuilderVisible(false);
|
||||
setIsCameraVisible(false);
|
||||
setIsVisible(false);
|
||||
return;
|
||||
case RoomWidgetThumbnailEvent.TOGGLE_THUMBNAIL:
|
||||
setIsSelectorVisible(value => !value);
|
||||
setIsBuilderVisible(false);
|
||||
setIsCameraVisible(false);
|
||||
setIsVisible(value => !value);
|
||||
return;
|
||||
}
|
||||
}, []);
|
||||
@ -36,34 +31,14 @@ export const RoomThumbnailWidgetView: FC<{}> = props =>
|
||||
useUiEvent(RoomWidgetThumbnailEvent.HIDE_THUMBNAIL, onNitroEvent);
|
||||
useUiEvent(RoomWidgetThumbnailEvent.TOGGLE_THUMBNAIL, onNitroEvent);
|
||||
|
||||
const handleAction = useCallback((action: string) =>
|
||||
const receiveTexture = useCallback((texture: NitroRenderTexture) =>
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case 'camera':
|
||||
setIsSelectorVisible(false);
|
||||
setIsCameraVisible(true);
|
||||
return;
|
||||
case 'builder':
|
||||
setIsSelectorVisible(false);
|
||||
setIsBuilderVisible(true);
|
||||
return;
|
||||
}
|
||||
}, [ setIsSelectorVisible, setIsCameraVisible, setIsBuilderVisible ]);
|
||||
GetRoomEngine().saveTextureAsScreenshot(texture, true);
|
||||
|
||||
return (<>
|
||||
{ isSelectorVisible && <NitroCardView className="nitro-room-thumbnail">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('navigator.thumbeditor.caption') } onCloseClick={ () => setIsSelectorVisible(false) } />
|
||||
<NitroCardContentView className="d-flex align-items-center justify-content-center text-muted">
|
||||
<div className="option me-5" onClick={ () => handleAction('camera') }>
|
||||
<i className="fas fa-camera" />
|
||||
</div>
|
||||
<div className="option" onClick={ () => handleAction('builder') }>
|
||||
<i className="fas fa-pencil-ruler" />
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</NitroCardView> }
|
||||
{ isBuilderVisible && <RoomThumbnailWidgetBuilderView onCloseClick={ () => setIsBuilderVisible(false) } /> }
|
||||
{ isCameraVisible && <RoomThumbnailWidgetCameraView onCloseClick={ () => setIsCameraVisible(false) } /> }
|
||||
</>);
|
||||
setIsVisible(false);
|
||||
}, []);
|
||||
|
||||
if(!isVisible) return null;
|
||||
|
||||
return <NitroLayoutMiniCameraView roomId={ roomSession.roomId } textureReceiver={ receiveTexture } onClose={ () => setIsVisible(false) } />
|
||||
};
|
||||
|
@ -1,4 +0,0 @@
|
||||
export class RoomThumbnailWidgetBuilderViewProps
|
||||
{
|
||||
onCloseClick: () => void;
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
.nitro-room-thumbnail-builder {
|
||||
width: 600px;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
import { FC, useCallback, useState } from 'react';
|
||||
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../../../layout';
|
||||
import { LocalizeText } from '../../../../../../utils/LocalizeText';
|
||||
import { RoomThumbnailWidgetBuilderViewProps } from './RoomThumbnailWidgetBuilderView.props';
|
||||
|
||||
const TABS: string[] = [
|
||||
'navigator.thumbeditor.bgtab',
|
||||
'navigator.thumbeditor.objtab',
|
||||
'navigator.thumbeditor.toptab',
|
||||
];
|
||||
|
||||
export const RoomThumbnailWidgetBuilderView: FC<RoomThumbnailWidgetBuilderViewProps> = props =>
|
||||
{
|
||||
const { onCloseClick = null } = props;
|
||||
|
||||
const [ currentTab, setCurrentTab ] = useState(TABS[0]);
|
||||
|
||||
const processAction = useCallback((action: string, value?: string) =>
|
||||
{
|
||||
switch(action)
|
||||
{
|
||||
case 'change_tab':
|
||||
setCurrentTab(value);
|
||||
return;
|
||||
}
|
||||
}, [ setCurrentTab ]);
|
||||
|
||||
return (
|
||||
<NitroCardView className="nitro-room-thumbnail-builder">
|
||||
<NitroCardHeaderView headerText={ LocalizeText('navigator.thumbeditor.caption') } onCloseClick={ onCloseClick } />
|
||||
<div className="d-flex">
|
||||
<div className="w-100">
|
||||
<NitroCardTabsView>
|
||||
{ TABS.map(tab =>
|
||||
{
|
||||
return <NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ event => processAction('change_tab', tab) }>
|
||||
{ LocalizeText(tab) }
|
||||
</NitroCardTabsItemView>
|
||||
}) }
|
||||
</NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex h-100 overflow-auto effects px-2">
|
||||
<div className="row row-cols-3">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</div>
|
||||
<div>
|
||||
<NitroCardTabsView></NitroCardTabsView>
|
||||
<NitroCardContentView>
|
||||
<div className="d-flex justify-content-between mt-2">
|
||||
<div className="d-flex justify-content-end">
|
||||
<button className="btn btn-primary me-2" onClick={ event => processAction('cancel') }>{ LocalizeText('generic.cancel') }</button>
|
||||
<button className="btn btn-success" onClick={ event => processAction('checkout') }>{ LocalizeText('camera.preview.button.text') }</button>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardContentView>
|
||||
</div>
|
||||
</div>
|
||||
</NitroCardView>
|
||||
);
|
||||
};
|
@ -1,13 +0,0 @@
|
||||
.nitro-room-thumbnail-camera {
|
||||
width: 132px;
|
||||
height: 192px;
|
||||
background-image: url('../../../../../../assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png');
|
||||
|
||||
.camera-frame {
|
||||
position: absolute;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
margin-top: 38px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import { NitroRectangle } from '@nitrots/nitro-renderer';
|
||||
import { FC, useCallback, useRef } from 'react';
|
||||
import { GetRoomEngine, GetRoomSession } from '../../../../../../api';
|
||||
import { DraggableWindow } from '../../../../../../layout';
|
||||
import { LocalizeText } from '../../../../../../utils/LocalizeText';
|
||||
import { RoomThumbnailWidgetCameraViewProps } from './RoomThumbnailWidgetCameraView.types';
|
||||
|
||||
export const RoomThumbnailWidgetCameraView: FC<RoomThumbnailWidgetCameraViewProps> = props =>
|
||||
{
|
||||
const { onCloseClick = null } = props;
|
||||
|
||||
const cameraFrameRef = useRef<HTMLDivElement>();
|
||||
|
||||
const takePicture = useCallback(() =>
|
||||
{
|
||||
const frameBounds = cameraFrameRef.current.getBoundingClientRect();
|
||||
|
||||
if(!frameBounds) return;
|
||||
|
||||
const rectangle = new NitroRectangle(Math.floor(frameBounds.x), Math.floor(frameBounds.y), Math.floor(frameBounds.width), Math.floor(frameBounds.height));
|
||||
|
||||
const image = GetRoomEngine().createTextureFromRoom(GetRoomSession().roomId, 1, rectangle);
|
||||
|
||||
onCloseClick();
|
||||
}, [ onCloseClick ]);
|
||||
|
||||
return (
|
||||
<DraggableWindow handle=".nitro-room-thumbnail-camera">
|
||||
<div className="nitro-room-thumbnail-camera px-2">
|
||||
<div ref={ cameraFrameRef } className={ 'camera-frame' }></div>
|
||||
<div className="d-flex align-items-end h-100 pb-2">
|
||||
<button className="btn btn-sm btn-danger w-100 mb-1 me-2" onClick={ onCloseClick }>{ LocalizeText('cancel') }</button>
|
||||
<button className="btn btn-sm btn-success w-100 mb-1" onClick={ () => takePicture() }>{ LocalizeText('navigator.thumbeditor.save') }</button>
|
||||
</div>
|
||||
</div>
|
||||
</DraggableWindow>
|
||||
);
|
||||
};
|
@ -1,4 +0,0 @@
|
||||
export class RoomThumbnailWidgetCameraViewProps
|
||||
{
|
||||
onCloseClick: () => void;
|
||||
}
|
34
src/views/shared/friendly-time/FriendlyTimeView.tsx
Normal file
34
src/views/shared/friendly-time/FriendlyTimeView.tsx
Normal 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>
|
||||
);
|
||||
}
|
7
src/views/shared/friendly-time/FriendlyTimeView.types.ts
Normal file
7
src/views/shared/friendly-time/FriendlyTimeView.types.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { DetailsHTMLAttributes } from 'react';
|
||||
|
||||
export interface FriendlyTimeViewProps extends DetailsHTMLAttributes<HTMLDivElement>
|
||||
{
|
||||
seconds: number;
|
||||
isShort?: boolean;
|
||||
}
|
@ -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 { GetRoomSession, GetRoomSessionManager } from '../../api';
|
||||
import { GetRoomSession, GetRoomSessionManager, GoToDesktop } from '../../api';
|
||||
import { AvatarEditorEvent, CatalogEvent, FriendListEvent, InventoryEvent, NavigatorEvent, RoomWidgetCameraEvent } from '../../events';
|
||||
import { AchievementsUIEvent } from '../../events/achievements';
|
||||
import { UnseenItemTrackerUpdateEvent } from '../../events/inventory/UnseenItemTrackerUpdateEvent';
|
||||
import { ModToolsEvent } from '../../events/mod-tools/ModToolsEvent';
|
||||
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 { TransitionAnimationTypes } from '../../layout/transitions/TransitionAnimation.types';
|
||||
import { AvatarImageView } from '../shared/avatar-image/AvatarImageView';
|
||||
@ -129,7 +129,7 @@ export const ToolbarView: FC<ToolbarViewProps> = props =>
|
||||
{
|
||||
if(!GetRoomSession()) return;
|
||||
|
||||
SendMessageHook(new DesktopViewComposer());
|
||||
GoToDesktop();
|
||||
GetRoomSessionManager().removeSession(-1);
|
||||
}, []);
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { WiredActionDefinition } from '@nitrots/nitro-renderer';
|
||||
import Slider from 'rc-slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
@ -34,10 +34,11 @@ export const WiredActionBaseView: FC<WiredActionBaseViewProps> = props =>
|
||||
</> }
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.delay', [ 'seconds' ], [ GetWiredTimeLocale(delay) ]) }</label>
|
||||
<Slider
|
||||
value={ delay }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 0 }
|
||||
max={ 20 }
|
||||
value={ delay }
|
||||
onChange={ event => setDelay(event) } />
|
||||
</div>
|
||||
</WiredBaseView>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
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">
|
||||
Reward limit not set. Make sure rewards are badges or non-tradeable items.
|
||||
</div> }
|
||||
{ limitEnabled && <Slider
|
||||
defaultValue={ rewardsLimit }
|
||||
dots={ true }
|
||||
min={ 1 }
|
||||
max={ 1000 }
|
||||
step={ 1 }
|
||||
onChange={ event => setRewardsLimit(event) }
|
||||
/> }
|
||||
{ limitEnabled &&
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 1000 }
|
||||
value={ rewardsLimit }
|
||||
onChange={ event => setRewardsLimit(event) } /> }
|
||||
<hr className="my-1 mb-2 bg-dark" />
|
||||
<div className="fw-bold">How ofter can a user be rewarded?</div>
|
||||
<div className="d-flex">
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
import { WiredFurniType } from '../../../WiredView.types';
|
||||
@ -37,18 +37,20 @@ export const WiredActionGiveScoreToPredefinedTeamView: FC<{}> = props =>
|
||||
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group mb-2">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ points }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 100 }
|
||||
value={ points }
|
||||
onChange={ event => setPoints(event) } />
|
||||
</div>
|
||||
<div className="form-group mb-2">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 10 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
import { WiredFurniType } from '../../../WiredView.types';
|
||||
@ -34,18 +34,20 @@ export const WiredActionGiveScoreView: FC<{}> = props =>
|
||||
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group mb-2">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.setpoints', [ 'points' ], [ points.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ points }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 100 }
|
||||
value={ points }
|
||||
onChange={ event => setPoints(event) } />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settimesingame', [ 'times' ], [ time.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 10 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
</WiredActionBaseView>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
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 }>
|
||||
<div className="form-group mb-2">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.emptytiles', [ 'tiles' ], [ spacing.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ spacing }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 5 }
|
||||
value={ spacing }
|
||||
onChange={ event => setSpacing(event) } />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
import { WiredFurniType } from '../../../WiredView.types';
|
||||
@ -27,10 +27,11 @@ export const WiredActionMuteUserView: FC<{}> = props =>
|
||||
<WiredActionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group mb-2">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.length.minutes', ['minutes'], [ time.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 10 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
@ -25,10 +25,11 @@ export const WiredConditionTimeElapsedLessView: FC<{}> = props =>
|
||||
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.allowbefore', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 1200 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
</WiredConditionBaseView>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
@ -25,10 +25,11 @@ export const WiredConditionTimeElapsedMoreView: FC<{}> = props =>
|
||||
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.allowafter', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 1200 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
</WiredConditionBaseView>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
import { WiredFurniType } from '../../../WiredView.types';
|
||||
@ -34,18 +34,20 @@ export const WiredConditionUserCountInRoomView: FC<{}> = props =>
|
||||
<WiredConditionBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group mb-2">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.usercountmin', [ 'value' ], [ min.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ min }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 50 }
|
||||
value={ min }
|
||||
onChange={ event => setMin(event) } />
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.usercountmax', [ 'value' ], [ max.toString() ]) }</label>
|
||||
<Slider
|
||||
value={ max }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 50 }
|
||||
value={ max }
|
||||
onChange={ event => setMax(event) } />
|
||||
</div>
|
||||
</WiredConditionBaseView>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
@ -25,10 +25,11 @@ export const WiredTriggeExecuteOnceView: FC<{}> = props =>
|
||||
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 1200 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
</WiredTriggerBaseView>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FriendlyTime } from '@nitrots/nitro-renderer';
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
import { WiredFurniType } from '../../../WiredView.types';
|
||||
@ -25,10 +25,11 @@ export const WiredTriggeExecutePeriodicallyLongView: FC<{}> = props =>
|
||||
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.setlongtime', [ 'time' ], [ FriendlyTime.format(time * 5).toString() ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 120 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
</WiredTriggerBaseView>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Slider from 'rc-slider/lib/Slider';
|
||||
import { FC, useCallback, useEffect, useState } from 'react';
|
||||
import ReactSlider from 'react-slider';
|
||||
import { LocalizeText } from '../../../../../utils/LocalizeText';
|
||||
import { GetWiredTimeLocale } from '../../../common/GetWiredTimeLocale';
|
||||
import { useWiredContext } from '../../../context/WiredContext';
|
||||
@ -25,10 +25,11 @@ export const WiredTriggeExecutePeriodicallyView: FC<{}> = props =>
|
||||
<WiredTriggerBaseView requiresFurni={ WiredFurniType.STUFF_SELECTION_OPTION_NONE } save={ save }>
|
||||
<div className="form-group">
|
||||
<label className="fw-bold">{ LocalizeText('wiredfurni.params.settime', [ 'seconds' ], [ GetWiredTimeLocale(time) ]) }</label>
|
||||
<Slider
|
||||
value={ time }
|
||||
<ReactSlider
|
||||
className={ 'nitro-slider' }
|
||||
min={ 1 }
|
||||
max={ 60 }
|
||||
value={ time }
|
||||
onChange={ event => setTime(event) } />
|
||||
</div>
|
||||
</WiredTriggerBaseView>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user