From a3350b8b54afeaa9ebe72ae4faacef471d9ba85a Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 7 Aug 2021 19:05:31 -0400 Subject: [PATCH 01/24] More camera changes --- .../room/widgets/camera/CameraWidgetView.scss | 57 +++- .../room/widgets/camera/CameraWidgetView.tsx | 4 +- .../camera/common/CameraPictureThumbnail.ts | 6 + .../context/CameraWidgetContext.types.ts | 10 +- .../views/editor/CameraWidgetEditorView.tsx | 260 +++++++----------- .../editor/CameraWidgetEditorView.types.ts | 2 + .../CameraWidgetEffectListItemView.tsx | 29 ++ .../CameraWidgetEffectListItemView.types.ts | 11 + .../CameraWidgetEffectListView.tsx | 21 ++ .../CameraWidgetEffectListView.types.ts | 11 + tsconfig.json | 3 +- 11 files changed, 240 insertions(+), 174 deletions(-) create mode 100644 src/views/room/widgets/camera/common/CameraPictureThumbnail.ts create mode 100644 src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx create mode 100644 src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts create mode 100644 src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx create mode 100644 src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts diff --git a/src/views/room/widgets/camera/CameraWidgetView.scss b/src/views/room/widgets/camera/CameraWidgetView.scss index 52844d70..244d7be6 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.scss +++ b/src/views/room/widgets/camera/CameraWidgetView.scss @@ -87,6 +87,53 @@ .nitro-camera-editor { width: 600px; + .content-area { + min-height: 350px; + height: 350px; + 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,16 +164,6 @@ } } } - - .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 { diff --git a/src/views/room/widgets/camera/CameraWidgetView.tsx b/src/views/room/widgets/camera/CameraWidgetView.tsx index a8e2ef3e..85b535b9 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.tsx +++ b/src/views/room/widgets/camera/CameraWidgetView.tsx @@ -24,7 +24,7 @@ export const CameraWidgetView: FC<{}> = props => const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1); const [ selectedEffects, setSelectedEffects ] = useState([]); const [ isZoomed, setIsZoomed ] = useState(false); - const [ myLevel, setMyLevel ] = useState(10); + const [ myLevel, setMyLevel ] = useState(1); const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null); const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) => @@ -111,7 +111,7 @@ export const CameraWidgetView: FC<{}> = props => return ( { (mode === MODE_CAPTURE) && processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> } - { (mode === MODE_EDITOR) && processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> } + { (mode === MODE_EDITOR) && processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> } { (mode === MODE_CHECKOUT) && processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }> } ); diff --git a/src/views/room/widgets/camera/common/CameraPictureThumbnail.ts b/src/views/room/widgets/camera/common/CameraPictureThumbnail.ts new file mode 100644 index 00000000..d88f9dcd --- /dev/null +++ b/src/views/room/widgets/camera/common/CameraPictureThumbnail.ts @@ -0,0 +1,6 @@ +export class CameraPictureThumbnail +{ + constructor( + public effectName: string, + public thumbnailUrl: string) {} +} diff --git a/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts index 8c6c4d82..b162cbb7 100644 --- a/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts +++ b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts @@ -1,17 +1,17 @@ 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>; selectedPictureIndex: number, - setSelectedPictureIndex: (index: number) => void, + setSelectedPictureIndex: Dispatch>; selectedEffects: IRoomCameraWidgetSelectedEffect[], - setSelectedEffects: (selectedEffects: IRoomCameraWidgetSelectedEffect[]) => void, + setSelectedEffects: Dispatch>; isZoomed: boolean, - setIsZoomed: (isZoomed: boolean) => void + setIsZoomed: Dispatch>; } export interface CameraWidgetContextProps extends ProviderProps diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx index cce2c55c..8cb13259 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx @@ -1,94 +1,85 @@ -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 { 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 = 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(null); + const [ selectedEffects, setSelectedEffects ] = useState([]); + const [ effectsThumbnails, setEffectsThumbnails ] = useState([]); + 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 setCurrentEffectAlpha = 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) { @@ -102,131 +93,88 @@ export const CameraWidgetEditorView: FC = props => onCheckout(); 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) - { - existingIndex = getEffectIndex(value); - } - - let effect = null; + if(existingIndex >= 0) return; + + const effect = availableEffects.find(effect => (effect.name === effectName)); - 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(!effect) return; - if(selectedEffectName !== value) + setSelectedEffects(prevValue => { - setSelectedEffectName(value); - } - else - { - setSelectedEffectName(null); - } - } + return [ ...prevValue, new RoomCameraWidgetSelectedEffect(effect, 1) ]; + }); + + 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'); + //window.open(getCurrentPicture().src, '_blank'); return; case 'zoom': setIsZoomed(!isZoomed); return; } - }, [ onClose, onCancel, onCheckout, getCurrentPicture, myLevel, selectedEffectName, getEffectIndex, availableEffects, isZoomed, setIsZoomed, selectedEffects, setSelectedEffects ]); + }, [ isZoomed, availableEffects, getSelectedEffectIndex, selectedEffectName, 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 ( processAction('close') } /> -
-
- - { TABS.map(tab => - { - return processAction('change_tab', tab) }> - }) } - - -
-
- { getEffectList().map(effect => - { - return ( -
- { getEffectIndex(effect.name) > -1 && } -
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 &&
- -
} - { effect.minLevel > myLevel &&
-
-
{ effect.minLevel }
-
} -
-
- ); - }) } -
+ + { TABS.map(tab => + { + return processAction('change_tab', tab) }> + }) } + + +
+
+ +
+
+
+
- +
-
- - -
- { selectedEffectName &&
-
{ LocalizeText('camera.effect.name.' + selectedEffectName) + ' - ' + getCurrentEffectAlpha() }
- setSelectedEffectAlpha(Number(event.target.value)) } className="form-range w-100" /> -
} -
-
-
- - - -
-
- - -
-
-
-
-
+
); } diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts index 6a58547a..c33ba910 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts @@ -1,7 +1,9 @@ import { IRoomCameraWidgetEffect } from '@nitrots/nitro-renderer'; +import { CameraPicture } from '../../common/CameraPicture'; export interface CameraWidgetEditorViewProps { + picture: CameraPicture; availableEffects: IRoomCameraWidgetEffect[]; myLevel: number; onClose: () => void; diff --git a/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx new file mode 100644 index 00000000..ae84972f --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.tsx @@ -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 = props => +{ + const { effect = null, thumbnailUrl = null, isActive = false, isLocked = false, selectEffect = null, removeEffect = null } = props; + + return ( + (!isActive && selectEffect()) }> + { isActive && + } + { !isLocked && (thumbnailUrl && thumbnailUrl.length > 0) && +
+ +
} + { isLocked && +
+
+ +
+
{ effect.minLevel }
+
} +
+ ); +} diff --git a/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts new file mode 100644 index 00000000..19028be3 --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list-item/CameraWidgetEffectListItemView.types.ts @@ -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; +} diff --git a/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx new file mode 100644 index 00000000..5f36ce7c --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.tsx @@ -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 = props => +{ + const { myLevel = 0, selectedEffects = [], effects = [], thumbnails = [], processAction = null } = props; + + return ( + + { 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 myLevel) } selectEffect={ () => processAction('select_effect', effect.name) } removeEffect={ () => processAction('remove_effect', effect.name) } /> + }) } + + ); +} diff --git a/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts new file mode 100644 index 00000000..7bdfccf1 --- /dev/null +++ b/src/views/room/widgets/camera/views/editor/effect-list/CameraWidgetEffectListView.types.ts @@ -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; +} diff --git a/tsconfig.json b/tsconfig.json index d05ec3bd..ac2a9fd7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -23,6 +23,7 @@ "jsx": "react-jsx" }, "include": [ - "src" + "src", + "node_modules/@nitrots/nitro-renderer/src/**/*.ts", ] } From 29b62081ad7643f750050ea266e35920ab5838d8 Mon Sep 17 00:00:00 2001 From: Bill Date: Sat, 7 Aug 2021 22:04:19 -0400 Subject: [PATCH 02/24] More camera updates --- .../room/widgets/camera/CameraWidgetView.scss | 4 +-- .../room/widgets/camera/CameraWidgetView.tsx | 14 +++++--- .../checkout/CameraWidgetCheckoutView.tsx | 25 ++++++++----- .../CameraWidgetCheckoutView.types.ts | 1 + .../views/editor/CameraWidgetEditorView.tsx | 36 +++++++++++++++++-- .../editor/CameraWidgetEditorView.types.ts | 2 +- 6 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/views/room/widgets/camera/CameraWidgetView.scss b/src/views/room/widgets/camera/CameraWidgetView.scss index 244d7be6..7274426d 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.scss +++ b/src/views/room/widgets/camera/CameraWidgetView.scss @@ -88,8 +88,8 @@ width: 600px; .content-area { - min-height: 350px; - height: 350px; + min-height: 441px; + height: 441px; resize: vertical; .picture-preview { diff --git a/src/views/room/widgets/camera/CameraWidgetView.tsx b/src/views/room/widgets/camera/CameraWidgetView.tsx index 85b535b9..1a15b828 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.tsx +++ b/src/views/room/widgets/camera/CameraWidgetView.tsx @@ -25,6 +25,7 @@ export const CameraWidgetView: FC<{}> = props => const [ selectedEffects, setSelectedEffects ] = useState([]); const [ isZoomed, setIsZoomed ] = useState(false); const [ myLevel, setMyLevel ] = useState(1); + const [ savedPictureUrl, setSavedPictureUrl ] = useState(null); const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null); const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) => @@ -102,17 +103,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 ( { (mode === MODE_CAPTURE) && processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> } - { (mode === MODE_EDITOR) && processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ () => processAction('checkout') } availableEffects={ availableEffects } /> } - { (mode === MODE_CHECKOUT) && processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }> } + { (mode === MODE_EDITOR) && processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ checkoutPictureUrl } availableEffects={ availableEffects } /> } + { (mode === MODE_CHECKOUT) && processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }> } ); } diff --git a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx index 64355107..a6400007 100644 --- a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx +++ b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx @@ -1,7 +1,7 @@ /* 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 { FC, useCallback, useEffect, useState } from 'react'; +import { GetRoomEngine } from '../../../../../../api'; import { CreateMessageHook, SendMessageHook } from '../../../../../../hooks/messages/message-event'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../../../layout'; import { LocalizeText } from '../../../../../../utils/LocalizeText'; @@ -11,15 +11,25 @@ import { CameraWidgetCheckoutViewProps } from './CameraWidgetCheckoutView.types' export const CameraWidgetCheckoutView: FC = props => { - const { onCloseClick = null, onCancelClick = null, price = null } = props; + const { pictureUrl = null, onCloseClick = null, onCancelClick = null, price = null } = props; 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(!pictureUrl) return; + + console.log(pictureUrl); + + GetRoomEngine().saveBase64AsScreenshot(pictureUrl); + }, [ pictureUrl ]); + const onCameraPurchaseOKMessageEvent = useCallback((event: CameraPurchaseOKMessageEvent) => { + console.log(event); setPicturesBought(value => value + 1); setIsWaiting(false); }, []); @@ -30,6 +40,8 @@ export const CameraWidgetCheckoutView: FC = props { const parser = event.getParser(); + console.log(parser); + setPublishCooldown(parser.secondsToWait); setWasPicturePublished(parser.ok); setIsWaiting(false); @@ -37,11 +49,6 @@ export const CameraWidgetCheckoutView: FC = props CreateMessageHook(CameraPublishStatusMessageEvent, onCameraPublishStatusMessageEvent); - const getCurrentPicture = useCallback(() => - { - return GetRoomCameraWidgetManager().applyEffects(cameraRoll[selectedPictureIndex].texture, selectedEffects, isZoomed); - }, [ cameraRoll, selectedPictureIndex, selectedEffects, isZoomed ]); - const processAction = useCallback((type: string, value: string | number = null) => { switch(type) @@ -73,7 +80,7 @@ export const CameraWidgetCheckoutView: FC = props processAction('close') } /> -
+
{ LocalizeText('camera.purchase.header') }{ price.credits > 0 && <>: { price.credits } }
diff --git a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts index dbbac494..4698df7d 100644 --- a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts +++ b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts @@ -1,5 +1,6 @@ export interface CameraWidgetCheckoutViewProps { + pictureUrl: string; onCloseClick: () => void; onCancelClick: () => void; price: { credits: number, duckets: number, publishDucketPrice: number }; diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx index 8cb13259..6ee965b9 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx @@ -1,5 +1,6 @@ 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'; @@ -57,7 +58,7 @@ export const CameraWidgetEditorView: FC = props => return (selectedEffects[getCurrentEffectIndex] || null); }, [ selectedEffectName, getCurrentEffectIndex, selectedEffects ]); - const setCurrentEffectAlpha = useCallback((alpha: number) => + const setSelectedEffectAlpha = useCallback((alpha: number) => { const index = getCurrentEffectIndex; @@ -90,7 +91,7 @@ export const CameraWidgetEditorView: FC = props => onCancel(); return; case 'checkout': - onCheckout(); + onCheckout(getCurrentPictureUrl); return; case 'change_tab': setCurrentTab(String(effectName)); @@ -140,7 +141,7 @@ export const CameraWidgetEditorView: FC = props => setIsZoomed(!isZoomed); return; } - }, [ isZoomed, availableEffects, getSelectedEffectIndex, selectedEffectName, onCancel, onCheckout, onClose, setIsZoomed, setSelectedEffects ]); + }, [ isZoomed, availableEffects, selectedEffectName, getCurrentPictureUrl, getSelectedEffectIndex, onCancel, onCheckout, onClose, setIsZoomed, setSelectedEffects ]); useEffect(() => { @@ -172,6 +173,35 @@ export const CameraWidgetEditorView: FC = props =>
+ { selectedEffectName && +
+
{ LocalizeText('camera.effect.name.' + selectedEffectName) }
+ setSelectedEffectAlpha(event) } + renderThumb={ (props, state) =>
{ state.valueNow }
} /> +
} +
+
+ + + +
+
+ + +
+
diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts index c33ba910..4bd58fbc 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.types.ts @@ -8,7 +8,7 @@ export interface CameraWidgetEditorViewProps myLevel: number; onClose: () => void; onCancel: () => void; - onCheckout: () => void; + onCheckout: (pictureUrl: string) => void; } export class CameraWidgetEditorTabs From 16773c05012fb4dde36238be0d55535e6dadc721 Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 8 Aug 2021 16:53:14 -0400 Subject: [PATCH 03/24] Add color vars --- src/assets/styles/bootstrap/_variables.scss | 2 ++ src/views/purse/PurseView.scss | 4 ++-- src/views/purse/views/seasonal/SeasonalView.scss | 4 ++-- src/views/room/widgets/camera/CameraWidgetView.scss | 5 ----- src/views/room/widgets/context-menu/ContextMenu.scss | 4 ++-- 5 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/assets/styles/bootstrap/_variables.scss b/src/assets/styles/bootstrap/_variables.scss index b2592966..fe6cb582 100644 --- a/src/assets/styles/bootstrap/_variables.scss +++ b/src/assets/styles/bootstrap/_variables.scss @@ -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; diff --git a/src/views/purse/PurseView.scss b/src/views/purse/PurseView.scss index 82ea9cc3..72ed8e27 100644 --- a/src/views/purse/PurseView.scss +++ b/src/views/purse/PurseView.scss @@ -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; } diff --git a/src/views/purse/views/seasonal/SeasonalView.scss b/src/views/purse/views/seasonal/SeasonalView.scss index 9b5b8e25..e5f8894d 100644 --- a/src/views/purse/views/seasonal/SeasonalView.scss +++ b/src/views/purse/views/seasonal/SeasonalView.scss @@ -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; } } diff --git a/src/views/room/widgets/camera/CameraWidgetView.scss b/src/views/room/widgets/camera/CameraWidgetView.scss index 7274426d..b100900d 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.scss +++ b/src/views/room/widgets/camera/CameraWidgetView.scss @@ -172,10 +172,5 @@ .picture-preview { width: 320px; height: 320px; - - .slider { - background: linear-gradient(180deg, transparent, black); - text-shadow: 1px 1px rgba(0, 0, 0, .5); - } } } diff --git a/src/views/room/widgets/context-menu/ContextMenu.scss b/src/views/room/widgets/context-menu/ContextMenu.scss index 1f21f858..06d98dce 100644 --- a/src/views/room/widgets/context-menu/ContextMenu.scss +++ b/src/views/room/widgets/context-menu/ContextMenu.scss @@ -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; From a34235f16daa98d264d2605249151d4e9d1689e2 Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 8 Aug 2021 22:21:40 -0400 Subject: [PATCH 04/24] Finish room camera --- .../room/widgets/camera/CameraWidgetView.scss | 2 +- .../room/widgets/camera/CameraWidgetView.tsx | 12 +-- .../camera/context/CameraWidgetContext.tsx | 6 +- .../context/CameraWidgetContext.types.ts | 5 - .../checkout/CameraWidgetCheckoutView.tsx | 99 +++++++++++++------ .../CameraWidgetCheckoutView.types.ts | 2 +- 6 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/views/room/widgets/camera/CameraWidgetView.scss b/src/views/room/widgets/camera/CameraWidgetView.scss index b100900d..22217552 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.scss +++ b/src/views/room/widgets/camera/CameraWidgetView.scss @@ -167,7 +167,7 @@ } .nitro-camera-checkout { - width: 336px; + width: 388px; .picture-preview { width: 320px; diff --git a/src/views/room/widgets/camera/CameraWidgetView.tsx b/src/views/room/widgets/camera/CameraWidgetView.tsx index 1a15b828..0dcfe8aa 100644 --- a/src/views/room/widgets/camera/CameraWidgetView.tsx +++ b/src/views/room/widgets/camera/CameraWidgetView.tsx @@ -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,10 +22,8 @@ export const CameraWidgetView: FC<{}> = props => const [ availableEffects, setAvailableEffects ] = useState([]); const [ cameraRoll, setCameraRoll ] = useState([]); const [ selectedPictureIndex, setSelectedPictureIndex ] = useState(-1); - const [ selectedEffects, setSelectedEffects ] = useState([]); - const [ isZoomed, setIsZoomed ] = useState(false); - const [ myLevel, setMyLevel ] = useState(1); - const [ savedPictureUrl, setSavedPictureUrl ] = useState(null); + const [ myLevel, setMyLevel ] = useState(10); + const [ base64Url, setSavedPictureUrl ] = useState(null); const [ price, setPrice ] = useState<{ credits: number, duckets: number, publishDucketPrice: number }>(null); const onNitroEvent = useCallback((event: RoomWidgetCameraEvent) => @@ -113,10 +111,10 @@ export const CameraWidgetView: FC<{}> = props => }, []); return ( - + { (mode === MODE_CAPTURE) && processAction('close') } onEdit={ () => processAction('edit') } onDelete={ () => processAction('delete') } /> } { (mode === MODE_EDITOR) && processAction('close') } onCancel={ () => processAction('editor_cancel') } onCheckout={ checkoutPictureUrl } availableEffects={ availableEffects } /> } - { (mode === MODE_CHECKOUT) && processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }> } + { (mode === MODE_CHECKOUT) && processAction('close') } onCancelClick={ () => processAction('editor_cancel') } price={ price }> } ); } diff --git a/src/views/room/widgets/camera/context/CameraWidgetContext.tsx b/src/views/room/widgets/camera/context/CameraWidgetContext.tsx index 3fd936aa..16253f22 100644 --- a/src/views/room/widgets/camera/context/CameraWidgetContext.tsx +++ b/src/views/room/widgets/camera/context/CameraWidgetContext.tsx @@ -5,11 +5,7 @@ const CameraWidgetContext = createContext({ cameraRoll: null, setCameraRoll: null, selectedPictureIndex: null, - setSelectedPictureIndex: null, - selectedEffects: null, - setSelectedEffects: null, - isZoomed: null, - setIsZoomed: null + setSelectedPictureIndex: null }); export const CameraWidgetContextProvider: FC = props => diff --git a/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts index b162cbb7..00dba3f0 100644 --- a/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts +++ b/src/views/room/widgets/camera/context/CameraWidgetContext.types.ts @@ -1,4 +1,3 @@ -import { IRoomCameraWidgetSelectedEffect } from '@nitrots/nitro-renderer'; import { Dispatch, ProviderProps, SetStateAction } from 'react'; import { CameraPicture } from '../common/CameraPicture'; @@ -8,10 +7,6 @@ export interface ICameraWidgetContext setCameraRoll: Dispatch>; selectedPictureIndex: number, setSelectedPictureIndex: Dispatch>; - selectedEffects: IRoomCameraWidgetSelectedEffect[], - setSelectedEffects: Dispatch>; - isZoomed: boolean, - setIsZoomed: Dispatch>; } export interface CameraWidgetContextProps extends ProviderProps diff --git a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx index a6400007..66d5f7d4 100644 --- a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx +++ b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.tsx @@ -1,36 +1,32 @@ -/* eslint-disable jsx-a11y/anchor-is-valid */ -import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer'; +import { CameraPublishStatusMessageEvent, CameraPurchaseOKMessageEvent, CameraStorageUrlMessageEvent, PublishPhotoMessageComposer, PurchasePhotoMessageComposer } from '@nitrots/nitro-renderer'; import { FC, useCallback, useEffect, useState } from 'react'; -import { GetRoomEngine } from '../../../../../../api'; +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 = props => { - const { pictureUrl = null, onCloseClick = null, onCancelClick = null, price = null } = props; + const { base64Url = null, onCloseClick = null, onCancelClick = null, price = null } = props; + const [ pictureUrl, setPictureUrl ] = useState(null); + const [ publishUrl, setPublishUrl ] = useState(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(!pictureUrl) return; + if(!base64Url) return; - console.log(pictureUrl); - - GetRoomEngine().saveBase64AsScreenshot(pictureUrl); - }, [ pictureUrl ]); + GetRoomEngine().saveBase64AsScreenshot(base64Url); + }, [ base64Url ]); const onCameraPurchaseOKMessageEvent = useCallback((event: CameraPurchaseOKMessageEvent) => { - console.log(event); - setPicturesBought(value => value + 1); + setPicturesBought(value => (value + 1)); setIsWaiting(false); }, []); @@ -40,8 +36,7 @@ export const CameraWidgetCheckoutView: FC = props { const parser = event.getParser(); - console.log(parser); - + setPublishUrl(parser.extraDataId); setPublishCooldown(parser.secondsToWait); setWasPicturePublished(parser.ok); setIsWaiting(false); @@ -49,6 +44,15 @@ export const CameraWidgetCheckoutView: FC = props CreateMessageHook(CameraPublishStatusMessageEvent, onCameraPublishStatusMessageEvent); + const onCameraStorageUrlMessageEvent = useCallback((event: CameraStorageUrlMessageEvent) => + { + const parser = event.getParser(); + + setPictureUrl(GetConfiguration('camera.url') + '/' + parser.url); + }, []); + + CreateMessageHook(CameraStorageUrlMessageEvent, onCameraStorageUrlMessageEvent); + const processAction = useCallback((type: string, value: string | number = null) => { switch(type) @@ -60,7 +64,7 @@ export const CameraWidgetCheckoutView: FC = props if(isWaiting) return; setIsWaiting(true); - SendMessageHook(new PurchasePhotoMessageComposer('1_1627697499')); + SendMessageHook(new PurchasePhotoMessageComposer('')); return; case 'publish': if(isWaiting) return; @@ -80,27 +84,58 @@ export const CameraWidgetCheckoutView: FC = props processAction('close') } /> -
-
-
-
{ LocalizeText('camera.purchase.header') }{ price.credits > 0 && <>: { price.credits } }
- { picturesBought > 0 &&
{ LocalizeText('camera.purchase.count.info') + ' ' + picturesBought }
} +
+ { !pictureUrl && +
+ { LocalizeText('camera.loading') } +
} +
+ { pictureUrl &&
{ LocalizeText('camera.confirm_phase.info') }
} +
+
+
{ LocalizeText('camera.purchase.header') }
+ { ((price.credits > 0) || (price.duckets > 0)) && +
+
{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }
+ { (price.credits > 0) && +
+ { price.credits } +
} + { (price.duckets > 0) && +
+ { price.duckets } +
} +
} + { (picturesBought > 0) && +
+ { LocalizeText('camera.purchase.count.info') } { picturesBought } + { LocalizeText('camera.open.inventory') } +
}
-
- - { picturesBought > 0 && } +
+
-
-
-
-
{ LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation') }{ !wasPicturePublished && price.duckets > 0 && <>: { price.duckets } }
-
{ LocalizeText(wasPicturePublished ? 'camera.publish.success.short.info' : 'camera.publish.detailed.explanation') }
- { wasPicturePublished && { LocalizeText('camera.link.to.published') } } +
+
+
{ LocalizeText(wasPicturePublished ? 'camera.publish.successful' : 'camera.publish.explanation') }
+
+ { LocalizeText(wasPicturePublished ? 'camera.publish.success.short.info' : 'camera.publish.detailed.explanation') }
- { !wasPicturePublished && } + { wasPicturePublished && { LocalizeText('camera.link.to.published') } } + { !wasPicturePublished && (price.publishDucketPrice > 0) && +
+
{ LocalizeText('catalog.purchase.confirmation.dialog.cost') }
+
+ { price.publishDucketPrice } +
+
} + { (publishCooldown > 0) &&
{ LocalizeText('camera.publish.wait', [ 'minutes' ], [ Math.ceil( publishCooldown / 60).toString() ]) }
}
- { publishCooldown > 0 &&
{ LocalizeText('camera.publish.wait', ['minutes'], [Math.ceil(publishCooldown/60).toString()]) }
} + { !wasPicturePublished && +
+ +
}
{ LocalizeText('camera.warning.disclaimer') }
diff --git a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts index 4698df7d..a4fafb06 100644 --- a/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts +++ b/src/views/room/widgets/camera/views/checkout/CameraWidgetCheckoutView.types.ts @@ -1,6 +1,6 @@ export interface CameraWidgetCheckoutViewProps { - pictureUrl: string; + base64Url: string; onCloseClick: () => void; onCancelClick: () => void; price: { credits: number, duckets: number, publishDucketPrice: number }; From cbe7558b4b3b99efba665c3b2c613cf82e117f43 Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 8 Aug 2021 22:54:20 -0400 Subject: [PATCH 05/24] Add generic mini camera component --- src/layout/index.ts | 1 + .../mini-camera/NitroLayoutMiniCameraView.tsx | 38 +++++++++++++++++++ .../NitroLayoutMiniCameraView.types.ts | 8 ++++ src/layout/mini-camera/index.ts | 2 + 4 files changed, 49 insertions(+) create mode 100644 src/layout/mini-camera/NitroLayoutMiniCameraView.tsx create mode 100644 src/layout/mini-camera/NitroLayoutMiniCameraView.types.ts create mode 100644 src/layout/mini-camera/index.ts diff --git a/src/layout/index.ts b/src/layout/index.ts index c9708368..5fc0d19f 100644 --- a/src/layout/index.ts +++ b/src/layout/index.ts @@ -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'; diff --git a/src/layout/mini-camera/NitroLayoutMiniCameraView.tsx b/src/layout/mini-camera/NitroLayoutMiniCameraView.tsx new file mode 100644 index 00000000..d209586d --- /dev/null +++ b/src/layout/mini-camera/NitroLayoutMiniCameraView.tsx @@ -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 = props => +{ + const { roomId = -1, textureReceiver = null, onClose = null } = props; + const elementRef = useRef(); + + 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 ( + +
+
+
+ + +
+
+ + ); +}; diff --git a/src/layout/mini-camera/NitroLayoutMiniCameraView.types.ts b/src/layout/mini-camera/NitroLayoutMiniCameraView.types.ts new file mode 100644 index 00000000..b1964563 --- /dev/null +++ b/src/layout/mini-camera/NitroLayoutMiniCameraView.types.ts @@ -0,0 +1,8 @@ +import { NitroRenderTexture } from '@nitrots/nitro-renderer'; + +export interface NitroLayoutMiniCameraViewProps +{ + roomId: number; + textureReceiver: (texture: NitroRenderTexture) => void; + onClose: () => void; +} diff --git a/src/layout/mini-camera/index.ts b/src/layout/mini-camera/index.ts new file mode 100644 index 00000000..4f572ea3 --- /dev/null +++ b/src/layout/mini-camera/index.ts @@ -0,0 +1,2 @@ +export * from './NitroLayoutMiniCameraView'; +export * from './NitroLayoutMiniCameraView.types'; From 1b646c734452128222cd4cb1f524d1096683244f Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 8 Aug 2021 22:54:49 -0400 Subject: [PATCH 06/24] Update room thumbnail widget --- src/views/room/widgets/RoomWidgets.scss | 1 - .../room-thumbnail/RoomThumbnailView.scss | 14 ----- .../RoomThumbnailWidgetView.tsx | 59 +++++------------ .../RoomThumbnailWidgetBuilderView.props.ts | 4 -- .../RoomThumbnailWidgetBuilderView.scss | 3 - .../RoomThumbnailWidgetBuilderView.tsx | 63 ------------------- .../camera/RoomThumbnailWidgetCameraView.scss | 13 ---- .../camera/RoomThumbnailWidgetCameraView.tsx | 38 ----------- .../RoomThumbnailWidgetCameraView.types.ts | 4 -- 9 files changed, 17 insertions(+), 182 deletions(-) delete mode 100644 src/views/room/widgets/room-thumbnail/RoomThumbnailView.scss delete mode 100644 src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.props.ts delete mode 100644 src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.scss delete mode 100644 src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.tsx delete mode 100644 src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.scss delete mode 100644 src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.tsx delete mode 100644 src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.types.ts diff --git a/src/views/room/widgets/RoomWidgets.scss b/src/views/room/widgets/RoomWidgets.scss index 10c9d521..ed284cd9 100644 --- a/src/views/room/widgets/RoomWidgets.scss +++ b/src/views/room/widgets/RoomWidgets.scss @@ -7,4 +7,3 @@ @import './infostand/InfoStandWidgetView'; @import './object-location/ObjectLocationView'; @import './room-tools/RoomToolsWidgetView'; -@import './room-thumbnail/RoomThumbnailView'; diff --git a/src/views/room/widgets/room-thumbnail/RoomThumbnailView.scss b/src/views/room/widgets/room-thumbnail/RoomThumbnailView.scss deleted file mode 100644 index 0e39af10..00000000 --- a/src/views/room/widgets/room-thumbnail/RoomThumbnailView.scss +++ /dev/null @@ -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'; diff --git a/src/views/room/widgets/room-thumbnail/RoomThumbnailWidgetView.tsx b/src/views/room/widgets/room-thumbnail/RoomThumbnailWidgetView.tsx index d7d736d2..032b6978 100644 --- a/src/views/room/widgets/room-thumbnail/RoomThumbnailWidgetView.tsx +++ b/src/views/room/widgets/room-thumbnail/RoomThumbnailWidgetView.tsx @@ -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 && - setIsSelectorVisible(false) } /> - -
handleAction('camera') }> - -
-
handleAction('builder') }> - -
-
-
} - { isBuilderVisible && setIsBuilderVisible(false) } /> } - { isCameraVisible && setIsCameraVisible(false) } /> } - ); + setIsVisible(false); + }, []); + + if(!isVisible) return null; + + return setIsVisible(false) } /> }; diff --git a/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.props.ts b/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.props.ts deleted file mode 100644 index 77c8b94b..00000000 --- a/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.props.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class RoomThumbnailWidgetBuilderViewProps -{ - onCloseClick: () => void; -} diff --git a/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.scss b/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.scss deleted file mode 100644 index d011f674..00000000 --- a/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.scss +++ /dev/null @@ -1,3 +0,0 @@ -.nitro-room-thumbnail-builder { - width: 600px; -} diff --git a/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.tsx b/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.tsx deleted file mode 100644 index f5061555..00000000 --- a/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.tsx +++ /dev/null @@ -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 = 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 ( - - -
-
- - { TABS.map(tab => - { - return processAction('change_tab', tab) }> - { LocalizeText(tab) } - - }) } - - -
-
- -
-
-
-
-
- - -
-
- - -
-
-
-
-
-
- ); -}; diff --git a/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.scss b/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.scss deleted file mode 100644 index b472dcda..00000000 --- a/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.scss +++ /dev/null @@ -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; - } -} diff --git a/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.tsx b/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.tsx deleted file mode 100644 index 9f5fe6b9..00000000 --- a/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.tsx +++ /dev/null @@ -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 = props => -{ - const { onCloseClick = null } = props; - - const cameraFrameRef = useRef(); - - 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 ( - -
-
-
- - -
-
-
- ); -}; diff --git a/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.types.ts b/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.types.ts deleted file mode 100644 index ec49ac02..00000000 --- a/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class RoomThumbnailWidgetCameraViewProps -{ - onCloseClick: () => void; -} From 5b6c8d9ce9c8ea42866427bb45c7b869bbc44f19 Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 8 Aug 2021 22:57:29 -0400 Subject: [PATCH 07/24] Fix camera download button --- .../camera/views/editor/CameraWidgetEditorView.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx index 6ee965b9..ab826e1d 100644 --- a/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx +++ b/src/views/room/widgets/camera/views/editor/CameraWidgetEditorView.tsx @@ -134,9 +134,15 @@ export const CameraWidgetEditorView: FC = props => 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; From e8a5bca6c984dbcbdf2a0acde448d07f5344f064 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Aug 2021 12:14:37 -0400 Subject: [PATCH 08/24] Update draggable component --- src/layout/card/NitroCardView.tsx | 4 +- src/layout/card/NitroCardView.types.ts | 5 +- .../draggable-window/DraggableWindow.tsx | 185 ++++++++++++++---- .../DraggableWindow.types.tsx | 17 +- 4 files changed, 163 insertions(+), 48 deletions(-) diff --git a/src/layout/card/NitroCardView.tsx b/src/layout/card/NitroCardView.tsx index a00913cd..aaf4178a 100644 --- a/src/layout/card/NitroCardView.tsx +++ b/src/layout/card/NitroCardView.tsx @@ -5,12 +5,12 @@ import { NitroCardViewProps } from './NitroCardView.types'; export const NitroCardView: FC = props => { - const { className = '', disableDrag = false, simple = false, theme = 'primary', children = null } = props; + const { className = '', simple = false, theme = 'primary', children = null, ...rest } = props; return (
- +
{ children }
diff --git a/src/layout/card/NitroCardView.types.ts b/src/layout/card/NitroCardView.types.ts index fcdd2f9a..0e41ed2f 100644 --- a/src/layout/card/NitroCardView.types.ts +++ b/src/layout/card/NitroCardView.types.ts @@ -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; } diff --git a/src/layout/draggable-window/DraggableWindow.tsx b/src/layout/draggable-window/DraggableWindow.tsx index e9fc0537..0a299d42 100644 --- a/src/layout/draggable-window/DraggableWindow.tsx +++ b/src/layout/draggable-window/DraggableWindow.tsx @@ -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 = new Map(); +const BOUNDS_THRESHOLD_TOP: number = 0; +const BOUNDS_THRESHOLD_LEFT: number = 0; export const DraggableWindow: FC = 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(null); const elementRef = useRef(); 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 = 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; + const element = (elementRef.current as HTMLElement); - currentWindows.push(element); + if(!element) return; + + 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 ( -
- { children } -
- ); - }, [ children, onMouseDown ]); + if(!offset && !delta) return; + + const element = (elementRef.current as HTMLElement); - return disableDrag ? getWindowContent : { getWindowContent }; + 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 ( +
+ { children } +
+ ); } diff --git a/src/layout/draggable-window/DraggableWindow.types.tsx b/src/layout/draggable-window/DraggableWindow.types.tsx index 9bba06fa..278e866a 100644 --- a/src/layout/draggable-window/DraggableWindow.types.tsx +++ b/src/layout/draggable-window/DraggableWindow.types.tsx @@ -1,11 +1,16 @@ -import { ReactNode } from 'react'; -import { DraggableProps } from 'react-draggable'; +import { Key } from 'react'; export interface DraggableWindowProps { - handle?: string; - draggableOptions?: Partial; + 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'; } From 812a0a80d62bed65bfb044cb5a694cdec848894b Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Aug 2021 12:15:08 -0400 Subject: [PATCH 09/24] Add unique key for draggable --- src/views/achievements/AchievementsView.tsx | 2 +- src/views/avatar-editor/AvatarEditorView.tsx | 2 +- src/views/catalog/CatalogView.tsx | 2 +- src/views/friend-list/FriendListView.tsx | 2 +- src/views/inventory/InventoryView.tsx | 2 +- src/views/mod-tools/ModToolsView.tsx | 2 +- src/views/navigator/NavigatorView.tsx | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/views/achievements/AchievementsView.tsx b/src/views/achievements/AchievementsView.tsx index 892bca9d..a60d0638 100644 --- a/src/views/achievements/AchievementsView.tsx +++ b/src/views/achievements/AchievementsView.tsx @@ -40,7 +40,7 @@ export const AchievementsView: FC = props => { isVisible && - + setIsVisible(false) } />
diff --git a/src/views/avatar-editor/AvatarEditorView.tsx b/src/views/avatar-editor/AvatarEditorView.tsx index be5ea7df..e9f14a43 100644 --- a/src/views/avatar-editor/AvatarEditorView.tsx +++ b/src/views/avatar-editor/AvatarEditorView.tsx @@ -276,7 +276,7 @@ export const AvatarEditorView: FC = props => if(!isVisible || !figureData) return null; return ( - + setIsVisible(false) } /> { categories && (categories.size > 0) && Array.from(categories.keys()).map(category => diff --git a/src/views/catalog/CatalogView.tsx b/src/views/catalog/CatalogView.tsx index 008e0c06..736be0de 100644 --- a/src/views/catalog/CatalogView.tsx +++ b/src/views/catalog/CatalogView.tsx @@ -190,7 +190,7 @@ export const CatalogView: FC = props => { isVisible && - + { saveActivePages(); setIsVisible(false); } } /> { root && root.children.length && root.children.map((page, index) => diff --git a/src/views/friend-list/FriendListView.tsx b/src/views/friend-list/FriendListView.tsx index 8430cc5f..0163d7cd 100644 --- a/src/views/friend-list/FriendListView.tsx +++ b/src/views/friend-list/FriendListView.tsx @@ -84,7 +84,7 @@ export const FriendListView: FC = props => { isReady && createPortal(, document.getElementById('toolbar-friend-bar-container')) } { isVisible && - + setIsVisible(false) } />
{ LocalizeText('friendlist.search.friendscaption') }
diff --git a/src/views/inventory/InventoryView.tsx b/src/views/inventory/InventoryView.tsx index 3a337c29..91369e35 100644 --- a/src/views/inventory/InventoryView.tsx +++ b/src/views/inventory/InventoryView.tsx @@ -197,7 +197,7 @@ export const InventoryView: FC = props => { isVisible && - + { !furnitureState.tradeData && <> diff --git a/src/views/mod-tools/ModToolsView.tsx b/src/views/mod-tools/ModToolsView.tsx index 132f19f4..54f759d0 100644 --- a/src/views/mod-tools/ModToolsView.tsx +++ b/src/views/mod-tools/ModToolsView.tsx @@ -161,7 +161,7 @@ export const ModToolsView: FC = props => return ( { isVisible && - + setIsVisible(false) } /> diff --git a/src/views/navigator/NavigatorView.tsx b/src/views/navigator/NavigatorView.tsx index a64bf81a..62a60ab9 100644 --- a/src/views/navigator/NavigatorView.tsx +++ b/src/views/navigator/NavigatorView.tsx @@ -139,7 +139,7 @@ export const NavigatorView: FC = props => { isVisible && - + setIsVisible(false) } /> { topLevelContexts.map((context, index) => From fb740631c42deb3c1fe683a7faa666ada7cca8d1 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Aug 2021 12:23:20 -0400 Subject: [PATCH 10/24] Remove dependencies --- package-lock.json | 9 --------- package.json | 1 - 2 files changed, 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c86d5d3..d2ba455a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14834,15 +14834,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", diff --git a/package.json b/package.json index 8aa3854f..ddedcddc 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,6 @@ "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", From 54ab6a5f616746b5f7f3db44cd864bb22755417d Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Aug 2021 12:34:31 -0400 Subject: [PATCH 11/24] Update sliders --- .../views/actions/base/WiredActionBaseView.tsx | 7 ++++--- .../give-reward/WiredActionGiveRewardView.tsx | 17 ++++++++--------- ...WiredActionGiveScoreToPredefinedTeamView.tsx | 12 +++++++----- .../give-score/WiredActionGiveScoreView.tsx | 12 +++++++----- .../WiredActionMoveFurniToView.tsx | 7 ++++--- .../mute-user/WiredActionMuteUserView.tsx | 7 ++++--- .../WiredConditionTimeElapsedLessView.tsx | 7 ++++--- .../WiredConditionTimeElapsedMoreView.tsx | 7 ++++--- .../WiredConditionUserCountInRoomView.tsx | 12 +++++++----- .../WiredTriggerExecuteOnceView.tsx | 7 ++++--- .../WiredTriggerExecutePeriodicallyLongView.tsx | 7 ++++--- .../WiredTriggerExecutePeriodicallyView.tsx | 7 ++++--- .../WiredTriggerScoreAchievedView.tsx | 7 ++++--- 13 files changed, 65 insertions(+), 51 deletions(-) diff --git a/src/views/wired/views/actions/base/WiredActionBaseView.tsx b/src/views/wired/views/actions/base/WiredActionBaseView.tsx index cecd0b5b..0d7a4efa 100644 --- a/src/views/wired/views/actions/base/WiredActionBaseView.tsx +++ b/src/views/wired/views/actions/base/WiredActionBaseView.tsx @@ -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 = props => }
- setDelay(event) } />
diff --git a/src/views/wired/views/actions/give-reward/WiredActionGiveRewardView.tsx b/src/views/wired/views/actions/give-reward/WiredActionGiveRewardView.tsx index 0d857a67..039ddffa 100644 --- a/src/views/wired/views/actions/give-reward/WiredActionGiveRewardView.tsx +++ b/src/views/wired/views/actions/give-reward/WiredActionGiveRewardView.tsx @@ -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 &&
Reward limit not set. Make sure rewards are badges or non-tradeable items.
} - { limitEnabled && setRewardsLimit(event) } - /> } + { limitEnabled && + setRewardsLimit(event) } /> }
How ofter can a user be rewarded?
diff --git a/src/views/wired/views/actions/give-score-to-predefined-team/WiredActionGiveScoreToPredefinedTeamView.tsx b/src/views/wired/views/actions/give-score-to-predefined-team/WiredActionGiveScoreToPredefinedTeamView.tsx index 3c5d444a..9e82c14d 100644 --- a/src/views/wired/views/actions/give-score-to-predefined-team/WiredActionGiveScoreToPredefinedTeamView.tsx +++ b/src/views/wired/views/actions/give-score-to-predefined-team/WiredActionGiveScoreToPredefinedTeamView.tsx @@ -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 =>
- setPoints(event) } />
- setTime(event) } />
diff --git a/src/views/wired/views/actions/give-score/WiredActionGiveScoreView.tsx b/src/views/wired/views/actions/give-score/WiredActionGiveScoreView.tsx index 1360e889..3b909fbf 100644 --- a/src/views/wired/views/actions/give-score/WiredActionGiveScoreView.tsx +++ b/src/views/wired/views/actions/give-score/WiredActionGiveScoreView.tsx @@ -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 =>
- setPoints(event) } />
- setTime(event) } />
diff --git a/src/views/wired/views/actions/move-furni-to/WiredActionMoveFurniToView.tsx b/src/views/wired/views/actions/move-furni-to/WiredActionMoveFurniToView.tsx index 96bf209a..c36062f8 100644 --- a/src/views/wired/views/actions/move-furni-to/WiredActionMoveFurniToView.tsx +++ b/src/views/wired/views/actions/move-furni-to/WiredActionMoveFurniToView.tsx @@ -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 =>
- setSpacing(event) } />
diff --git a/src/views/wired/views/actions/mute-user/WiredActionMuteUserView.tsx b/src/views/wired/views/actions/mute-user/WiredActionMuteUserView.tsx index 1c6f912f..5f4f8c44 100644 --- a/src/views/wired/views/actions/mute-user/WiredActionMuteUserView.tsx +++ b/src/views/wired/views/actions/mute-user/WiredActionMuteUserView.tsx @@ -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 =>
- setTime(event) } />
diff --git a/src/views/wired/views/conditions/time-elapsed-less/WiredConditionTimeElapsedLessView.tsx b/src/views/wired/views/conditions/time-elapsed-less/WiredConditionTimeElapsedLessView.tsx index 72210acf..65e7252b 100644 --- a/src/views/wired/views/conditions/time-elapsed-less/WiredConditionTimeElapsedLessView.tsx +++ b/src/views/wired/views/conditions/time-elapsed-less/WiredConditionTimeElapsedLessView.tsx @@ -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 =>
- setTime(event) } />
diff --git a/src/views/wired/views/conditions/time-elapsed-more/WiredConditionTimeElapsedMoreView.tsx b/src/views/wired/views/conditions/time-elapsed-more/WiredConditionTimeElapsedMoreView.tsx index a8210ccc..5b1fc350 100644 --- a/src/views/wired/views/conditions/time-elapsed-more/WiredConditionTimeElapsedMoreView.tsx +++ b/src/views/wired/views/conditions/time-elapsed-more/WiredConditionTimeElapsedMoreView.tsx @@ -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 =>
- setTime(event) } />
diff --git a/src/views/wired/views/conditions/user-count-in-room/WiredConditionUserCountInRoomView.tsx b/src/views/wired/views/conditions/user-count-in-room/WiredConditionUserCountInRoomView.tsx index aa07267b..92474103 100644 --- a/src/views/wired/views/conditions/user-count-in-room/WiredConditionUserCountInRoomView.tsx +++ b/src/views/wired/views/conditions/user-count-in-room/WiredConditionUserCountInRoomView.tsx @@ -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 =>
- setMin(event) } />
- setMax(event) } />
diff --git a/src/views/wired/views/triggers/execute-once/WiredTriggerExecuteOnceView.tsx b/src/views/wired/views/triggers/execute-once/WiredTriggerExecuteOnceView.tsx index f9f7cb4c..647ddc9d 100644 --- a/src/views/wired/views/triggers/execute-once/WiredTriggerExecuteOnceView.tsx +++ b/src/views/wired/views/triggers/execute-once/WiredTriggerExecuteOnceView.tsx @@ -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 =>
- setTime(event) } />
diff --git a/src/views/wired/views/triggers/execute-periodically-long/WiredTriggerExecutePeriodicallyLongView.tsx b/src/views/wired/views/triggers/execute-periodically-long/WiredTriggerExecutePeriodicallyLongView.tsx index 5a444837..bb0b1d3d 100644 --- a/src/views/wired/views/triggers/execute-periodically-long/WiredTriggerExecutePeriodicallyLongView.tsx +++ b/src/views/wired/views/triggers/execute-periodically-long/WiredTriggerExecutePeriodicallyLongView.tsx @@ -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 =>
- setTime(event) } />
diff --git a/src/views/wired/views/triggers/execute-periodically/WiredTriggerExecutePeriodicallyView.tsx b/src/views/wired/views/triggers/execute-periodically/WiredTriggerExecutePeriodicallyView.tsx index 98049d39..1570aa72 100644 --- a/src/views/wired/views/triggers/execute-periodically/WiredTriggerExecutePeriodicallyView.tsx +++ b/src/views/wired/views/triggers/execute-periodically/WiredTriggerExecutePeriodicallyView.tsx @@ -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 =>
- setTime(event) } />
diff --git a/src/views/wired/views/triggers/score-achieved/WiredTriggerScoreAchievedView.tsx b/src/views/wired/views/triggers/score-achieved/WiredTriggerScoreAchievedView.tsx index a6c53191..b950f48d 100644 --- a/src/views/wired/views/triggers/score-achieved/WiredTriggerScoreAchievedView.tsx +++ b/src/views/wired/views/triggers/score-achieved/WiredTriggerScoreAchievedView.tsx @@ -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'; @@ -24,10 +24,11 @@ export const WiredTriggeScoreAchievedView: FC<{}> = props =>
- setPoints(event) } />
From 80fbb4b23f638fe3546bf3d6136a0fb946fe4fb1 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Aug 2021 12:34:45 -0400 Subject: [PATCH 12/24] Update dependencies --- package-lock.json | 85 ----------------------------------------------- package.json | 2 -- src/index.scss | 2 -- 3 files changed, 89 deletions(-) diff --git a/package-lock.json b/package-lock.json index d2ba455a..69072740 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4529,11 +4529,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", @@ -6969,11 +6964,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 +14578,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", @@ -15301,11 +15226,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", @@ -16096,11 +16016,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", diff --git a/package.json b/package.json index ddedcddc..116274d8 100644 --- a/package.json +++ b/package.json @@ -4,10 +4,8 @@ "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", diff --git a/src/index.scss b/src/index.scss index 08696a5c..73ca3e87 100644 --- a/src/index.scss +++ b/src/index.scss @@ -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, From 5defa0fd0cdf764791feafa533d03a8a4913dfea Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Aug 2021 12:35:17 -0400 Subject: [PATCH 13/24] More draggable updates --- src/layout/mini-camera/NitroLayoutMiniCameraView.tsx | 2 +- src/layout/trophy/NitroLayoutTrophyView.tsx | 2 +- .../views/rentable-bot-chat/AvatarInfoRentableBotChatView.tsx | 4 ++-- .../furniture/friend-furni/FurnitureFriendFurniView.tsx | 2 +- .../room/widgets/furniture/stickie/FurnitureStickieView.tsx | 3 ++- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/layout/mini-camera/NitroLayoutMiniCameraView.tsx b/src/layout/mini-camera/NitroLayoutMiniCameraView.tsx index d209586d..ea1b34f8 100644 --- a/src/layout/mini-camera/NitroLayoutMiniCameraView.tsx +++ b/src/layout/mini-camera/NitroLayoutMiniCameraView.tsx @@ -25,7 +25,7 @@ export const NitroLayoutMiniCameraView: FC = pro }, [ roomId, getCameraBounds, textureReceiver ]); return ( - +
diff --git a/src/layout/trophy/NitroLayoutTrophyView.tsx b/src/layout/trophy/NitroLayoutTrophyView.tsx index 0d66ef49..3f34e210 100644 --- a/src/layout/trophy/NitroLayoutTrophyView.tsx +++ b/src/layout/trophy/NitroLayoutTrophyView.tsx @@ -8,7 +8,7 @@ export const NitroLayoutTrophyView: FC = props => const { color = '', message = '', date = '', senderName = '', onCloseClick = null } = props; return ( - +
diff --git a/src/views/room/widgets/avatar-info/views/rentable-bot-chat/AvatarInfoRentableBotChatView.tsx b/src/views/room/widgets/avatar-info/views/rentable-bot-chat/AvatarInfoRentableBotChatView.tsx index ac51bf0f..f39f7f26 100644 --- a/src/views/room/widgets/avatar-info/views/rentable-bot-chat/AvatarInfoRentableBotChatView.tsx +++ b/src/views/room/widgets/avatar-info/views/rentable-bot-chat/AvatarInfoRentableBotChatView.tsx @@ -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 +
test!!!!!
diff --git a/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx b/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx index 392967c0..e67b9e5f 100644 --- a/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx +++ b/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx @@ -119,7 +119,7 @@ export const FurnitureFriendFurniView: FC<{}> = props =>
} - { engravingLockData && engravingLockData.usernames.length > 0 && + { engravingLockData && engravingLockData.usernames.length > 0 &&
processAction('close_view') }>
diff --git a/src/views/room/widgets/furniture/stickie/FurnitureStickieView.tsx b/src/views/room/widgets/furniture/stickie/FurnitureStickieView.tsx index e0bad98d..d839f214 100644 --- a/src/views/room/widgets/furniture/stickie/FurnitureStickieView.tsx +++ b/src/views/room/widgets/furniture/stickie/FurnitureStickieView.tsx @@ -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 ( - +
From 3b5974a67b6e25ca91cc5d8b59d8e4d96c16e902 Mon Sep 17 00:00:00 2001 From: Bill Date: Mon, 9 Aug 2021 12:35:22 -0400 Subject: [PATCH 14/24] Update config --- public/ui-config.json | 49 +------------------------------------------ 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/public/ui-config.json b/public/ui-config.json index 5057190c..2cd31c79 100644 --- a/public/ui-config.json +++ b/public/ui-config.json @@ -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": [ { From 63f41afe5894a6c16355087afc194b560d0e5f68 Mon Sep 17 00:00:00 2001 From: Bill Date: Tue, 10 Aug 2021 15:08:09 -0400 Subject: [PATCH 15/24] Update RoomView --- src/views/room/RoomView.tsx | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/views/room/RoomView.tsx b/src/views/room/RoomView.tsx index ed5f14aa..a82d4b9a 100644 --- a/src/views/room/RoomView.tsx +++ b/src/views/room/RoomView.tsx @@ -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'; @@ -21,6 +20,7 @@ export const RoomView: FC = props => const [ roomCanvas, setRoomCanvas ] = useState(null); const [ canvasId, setCanvasId ] = useState(-1); const [ widgetHandler, setWidgetHandler ] = useState(null); + const elementRef = useRef(); useEffect(() => { @@ -111,6 +111,8 @@ export const RoomView: FC = props => GetNitroInstance().render(); } + if(elementRef && elementRef.current) elementRef.current.appendChild(canvas); + setRoomCanvas(canvas); setCanvasId(canvasId); }, [ roomSession ]); @@ -119,15 +121,12 @@ export const RoomView: FC = props => return ( -
-
- { roomCanvas && createPortal(null, document.getElementById('room-view').appendChild(roomCanvas)) } - { widgetHandler && - <> - - - } -
+
+ { widgetHandler && + <> + + + } ); } From 47061493427cafff3f042e6c0f71f607524ca68d Mon Sep 17 00:00:00 2001 From: Dank074 Date: Tue, 10 Aug 2021 19:47:16 -0500 Subject: [PATCH 16/24] created user and furni choosers --- package.json | 1 + .../room-widgets/choosers/RoomObjectItem.ts | 28 +++++ .../choosers/RoomWidgetChooserContentEvent.ts | 29 +++++ src/views/room/RoomView.tsx | 4 + .../handlers/FurniChooserWidgetHandler.ts | 108 ++++++++++++++++ .../room/handlers/UserChooserWidgetHandler.ts | 84 +++++++++++++ src/views/room/widgets/RoomWidgets.scss | 1 + src/views/room/widgets/RoomWidgetsView.tsx | 4 + .../widgets/choosers/ChooserWidgetView.scss | 9 ++ .../choosers/FurniChooserWidgetView.tsx | 118 ++++++++++++++++++ .../choosers/UserChooserWidgetView.tsx | 117 +++++++++++++++++ .../room/widgets/choosers/utils/sorting.ts | 18 +++ 12 files changed, 521 insertions(+) create mode 100644 src/events/room-widgets/choosers/RoomObjectItem.ts create mode 100644 src/events/room-widgets/choosers/RoomWidgetChooserContentEvent.ts create mode 100644 src/views/room/handlers/FurniChooserWidgetHandler.ts create mode 100644 src/views/room/handlers/UserChooserWidgetHandler.ts create mode 100644 src/views/room/widgets/choosers/ChooserWidgetView.scss create mode 100644 src/views/room/widgets/choosers/FurniChooserWidgetView.tsx create mode 100644 src/views/room/widgets/choosers/UserChooserWidgetView.tsx create mode 100644 src/views/room/widgets/choosers/utils/sorting.ts diff --git a/package.json b/package.json index 8aa3854f..6ec3672f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "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" }, diff --git a/src/events/room-widgets/choosers/RoomObjectItem.ts b/src/events/room-widgets/choosers/RoomObjectItem.ts new file mode 100644 index 00000000..d11fc5a8 --- /dev/null +++ b/src/events/room-widgets/choosers/RoomObjectItem.ts @@ -0,0 +1,28 @@ +export class RoomObjectItem +{ + private readonly _id: number; + private readonly _category: number; + private readonly _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; + } +} diff --git a/src/events/room-widgets/choosers/RoomWidgetChooserContentEvent.ts b/src/events/room-widgets/choosers/RoomWidgetChooserContentEvent.ts new file mode 100644 index 00000000..aabb254e --- /dev/null +++ b/src/events/room-widgets/choosers/RoomWidgetChooserContentEvent.ts @@ -0,0 +1,29 @@ +import { RoomWidgetUpdateEvent } from '../../../views/room/events'; +import { RoomObjectItem } from './RoomObjectItem'; + +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[]; + private _isAnyRoomController: boolean; + + constructor(type: string, items: RoomObjectItem[], isAnyRoomController: boolean = false) + { + super(type); + + this._items = items.slice(); + this._isAnyRoomController = isAnyRoomController; + } + + public get items(): RoomObjectItem[] + { + return this._items; + } + + public get isAnyRoomController(): boolean + { + return this._isAnyRoomController; + } +} diff --git a/src/views/room/RoomView.tsx b/src/views/room/RoomView.tsx index ed5f14aa..0d7a0362 100644 --- a/src/views/room/RoomView.tsx +++ b/src/views/room/RoomView.tsx @@ -8,9 +8,11 @@ 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 { 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'; @@ -44,6 +46,8 @@ export const RoomView: FC = props => widgetHandlerManager.registerHandler(new RoomWidgetChatHandler()); widgetHandlerManager.registerHandler(new FurnitureContextMenuWidgetHandler()); widgetHandlerManager.registerHandler(new FurnitureCustomStackHeightWidgetHandler()); + widgetHandlerManager.registerHandler(new FurniChooserWidgetHandler()); + widgetHandlerManager.registerHandler(new UserChooserWidgetHandler()); setWidgetHandler(widgetHandlerManager); diff --git a/src/views/room/handlers/FurniChooserWidgetHandler.ts b/src/views/room/handlers/FurniChooserWidgetHandler.ts new file mode 100644 index 00000000..2e2c7630 --- /dev/null +++ b/src/views/room/handlers/FurniChooserWidgetHandler.ts @@ -0,0 +1,108 @@ +import { NitroEvent, RoomObjectCategory, RoomObjectVariable, RoomWidgetEnum } from '@nitrots/nitro-renderer'; +import { GetNitroInstance, GetRoomEngine, GetSessionDataManager } from '../../../api'; +import { RoomObjectItem } from '../../../events/room-widgets/choosers/RoomObjectItem'; +import { RoomWidgetChooserContentEvent } from '../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; +import { dispatchUiEvent } from '../../../hooks'; +import { RoomWidgetUpdateEvent } from '../events'; +import { RoomWidgetMessage, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../messages'; +import { dynamicSort } from '../widgets/choosers/utils/sorting'; +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.processFurniChooser(); + break; + case RoomWidgetRoomObjectMessage.SELECT_OBJECT: + this.selectFurni(message); + break; + } + + return null; + } + + private selectFurni(message: RoomWidgetMessage): void + { + const event = message as RoomWidgetRoomObjectMessage; + + if(event == null) return; + + if(event.category === RoomObjectCategory.WALL || event.category === RoomObjectCategory.FLOOR) + { + GetRoomEngine().selectRoomObject(this.container.roomSession.roomId, event.id, event.category); + } + } + + private processFurniChooser(): void + { + + if(this.container == null || this.container.roomSession == null || GetRoomEngine() == null || this.container.roomSession.userDataManager == null) return; + + const roomId = this.container.roomSession.roomId; + const furniInRoom : RoomObjectItem[] = []; + + furniInRoom.push(...GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.WALL).map(roomObject => { + const type = roomObject.type; + let name = null; + if(type.startsWith('poster')) + { + const posterNumber = Number.parseInt(type.replace('poster', '')); + name = GetNitroInstance().localization.getValue('poster_' + posterNumber + '_name'); + } + else + { + const furniTypeId = Number.parseInt(roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID)); + const wallItemData = GetSessionDataManager().getWallItemData(furniTypeId); + if(wallItemData != null && wallItemData.name.length > 0) + { + name = wallItemData.name; + } + else + { + name = type; + } + } + return new RoomObjectItem(roomObject.id, RoomObjectCategory.WALL, name) + })); + + furniInRoom.push(...GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.FLOOR).map(roomObject => { + const furniTypeId = Number.parseInt(roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID)); + const floorItemData = GetSessionDataManager().getFloorItemData(furniTypeId); + const name = floorItemData != null ? floorItemData.name : roomObject.type; + + return new RoomObjectItem(roomObject.id, RoomObjectCategory.FLOOR, name); + })); + + furniInRoom.sort(dynamicSort('name')); + + dispatchUiEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, furniInRoom, false)); + } + + public get type(): string + { + return RoomWidgetEnum.FURNI_CHOOSER; + } + + public get eventTypes(): string[] + { + return []; + } + + public get messageTypes(): string[] + { + return [ + RoomWidgetRequestWidgetMessage.FURNI_CHOOSER, + RoomWidgetRoomObjectMessage.SELECT_OBJECT + ]; + } +} diff --git a/src/views/room/handlers/UserChooserWidgetHandler.ts b/src/views/room/handlers/UserChooserWidgetHandler.ts new file mode 100644 index 00000000..379b479d --- /dev/null +++ b/src/views/room/handlers/UserChooserWidgetHandler.ts @@ -0,0 +1,84 @@ +import { NitroEvent, RoomObjectCategory, RoomWidgetEnum } from '@nitrots/nitro-renderer'; +import { RoomWidgetHandler } from '.'; +import { GetRoomEngine } from '../../../api'; +import { RoomObjectItem } from '../../../events/room-widgets/choosers/RoomObjectItem'; +import { RoomWidgetChooserContentEvent } from '../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; +import { dispatchUiEvent } from '../../../hooks'; +import { RoomWidgetUpdateEvent } from '../events'; +import { RoomWidgetMessage, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../messages'; +import { dynamicSort } from '../widgets/choosers/utils/sorting'; + +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.processUserChooser(); + break; + case RoomWidgetRoomObjectMessage.SELECT_OBJECT: + this.selectUnit(message); + break; + } + + return null; + } + + private processUserChooser(): void + { + + if(this.container == null || this.container.roomSession == null || GetRoomEngine() == null || this.container.roomSession.userDataManager == null) return; + + const roomId = this.container.roomSession.roomId; + const categoryId = RoomObjectCategory.UNIT; + const units = []; + + units.push(...GetRoomEngine().getRoomObjects(roomId, categoryId).map(roomObject => { + const unitData = this.container.roomSession.userDataManager.getUserDataByIndex(roomObject.id); + + if(!unitData) return null; + + return new RoomObjectItem(unitData.roomIndex, categoryId, unitData.name); + })); + + units.sort(dynamicSort('name')); + dispatchUiEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, units)); + } + + private selectUnit(k: RoomWidgetMessage): void + { + const event = k as RoomWidgetRoomObjectMessage; + + if(event == null) return; + + if(event.category === RoomObjectCategory.UNIT) + { + GetRoomEngine().selectRoomObject(this.container.roomSession.roomId, event.id, event.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 + ]; + } +} diff --git a/src/views/room/widgets/RoomWidgets.scss b/src/views/room/widgets/RoomWidgets.scss index ed284cd9..e7c33301 100644 --- a/src/views/room/widgets/RoomWidgets.scss +++ b/src/views/room/widgets/RoomWidgets.scss @@ -7,3 +7,4 @@ @import './infostand/InfoStandWidgetView'; @import './object-location/ObjectLocationView'; @import './room-tools/RoomToolsWidgetView'; +@import './choosers/ChooserWidgetView'; diff --git a/src/views/room/widgets/RoomWidgetsView.tsx b/src/views/room/widgets/RoomWidgetsView.tsx index 2ed8ba9d..66c85b43 100644 --- a/src/views/room/widgets/RoomWidgetsView.tsx +++ b/src/views/room/widgets/RoomWidgetsView.tsx @@ -9,6 +9,8 @@ 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 { FurnitureWidgetsView } from './furniture/FurnitureWidgetsView'; import { InfoStandWidgetView } from './infostand/InfoStandWidgetView'; import { RoomThumbnailWidgetView } from './room-thumbnail/RoomThumbnailWidgetView'; @@ -246,6 +248,8 @@ export const RoomWidgetsView: FC = props => + + ); } diff --git a/src/views/room/widgets/choosers/ChooserWidgetView.scss b/src/views/room/widgets/choosers/ChooserWidgetView.scss new file mode 100644 index 00000000..bca2d777 --- /dev/null +++ b/src/views/room/widgets/choosers/ChooserWidgetView.scss @@ -0,0 +1,9 @@ +.chooser-widget { + .selected-item { + background-color: cadetblue; + } + .list-item { + color: black; + overflow: hidden; + } +} diff --git a/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx b/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx new file mode 100644 index 00000000..7bc25c54 --- /dev/null +++ b/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx @@ -0,0 +1,118 @@ +import { FC, useCallback, useEffect, useState } from 'react'; +import List from 'react-virtualized/dist/commonjs/List'; +import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; +import { RoomWidgetChooserContentEvent } from '../../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; +import { CreateEventDispatcherHook, useUiEvent } from '../../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { LocalizeText } from '../../../../utils'; +import { useRoomContext } from '../../context/RoomContext'; +import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; +import { RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../../messages'; + +export const FurniChooserWidgetView: FC = props => +{ + const [isVisible, setIsVisible] = useState(false); + const [items, setItems] = useState(null); + const [filteredItems, setFilteredItems] = useState(null); + const [selectedItem, setSelectedItem] = useState(null); + const [refreshTimeout, setRefreshTimeout] = useState>(null); + const [searchValue, setSearchValue] = useState(''); + const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); + + const onFurniChooserContent = useCallback((event: RoomWidgetChooserContentEvent) => + { + setItems(event.items); + setIsVisible(true); + }, []); + + const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => + { + if (!event || !isVisible) return; + + if (refreshTimeout) clearTimeout(refreshTimeout); + + setRefreshTimeout(setTimeout(() => + { + widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.FURNI_CHOOSER)); + }, 100)); + + }, [isVisible, refreshTimeout, widgetHandler]); + + useUiEvent(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, onFurniChooserContent); + CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); + CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); + + useEffect(() => + { + if(!items) return; + + let filteredGroupItems = [ ...items ]; + + if(searchValue && searchValue.length) + { + const comparison = searchValue.toLocaleLowerCase(); + + filteredGroupItems = items.filter(item => + { + if(comparison && comparison.length) + { + if(item.name.toLocaleLowerCase().includes(comparison)) return item; + } + + return null; + }); + } + + setFilteredItems(filteredGroupItems); + }, [ items, searchValue, setFilteredItems ]); + + + const onClose = useCallback(() => + { + setIsVisible(false); + setItems(null); + }, []); + + const onClickItem = useCallback((item: RoomObjectItem) => + { + setSelectedItem(item); + widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category)); + }, [setSelectedItem, widgetHandler]); + + const rowRenderer = function ({ + key, // Unique key within array of rows + index, // Index of row within collection + isScrolling, // The List is currently being scrolled + isVisible, // This row is visible within the List (eg it is not an overscanned row) + style, // Style object to be applied to row (to position it) + }) + { + return ( +
onClickItem(filteredItems[index])} className={(selectedItem === filteredItems[index] ? 'selected-item ' : '') + 'list-item'}> + {filteredItems[index].name} - {filteredItems[index].id} +
+ ); + } + + if (!isVisible) return null; + + return ( +
+ + + +
+
+ setSearchValue(event.target.value)} /> +
+
+ +
+
+
+ ) +} diff --git a/src/views/room/widgets/choosers/UserChooserWidgetView.tsx b/src/views/room/widgets/choosers/UserChooserWidgetView.tsx new file mode 100644 index 00000000..86ebaafd --- /dev/null +++ b/src/views/room/widgets/choosers/UserChooserWidgetView.tsx @@ -0,0 +1,117 @@ +import { FC, useCallback, useEffect, useState } from 'react'; +import List from 'react-virtualized/dist/commonjs/List'; +import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; +import { RoomWidgetChooserContentEvent } from '../../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; +import { CreateEventDispatcherHook, useUiEvent } from '../../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { LocalizeText } from '../../../../utils'; +import { useRoomContext } from '../../context/RoomContext'; +import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; +import { RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../../messages'; + +export const UserChooserWidgetView : FC = props => +{ + const [isVisible, setIsVisible] = useState(false); + const [items, setItems] = useState(null); + const [filteredItems, setFilteredItems] = useState(null); + const [selectedItem, setSelectedItem] = useState(null); + const [refreshTimeout, setRefreshTimeout] = useState>(null); + const [searchValue, setSearchValue] = useState(''); + const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); + + const onUserChooserContent = useCallback((event: RoomWidgetChooserContentEvent) => + { + setItems(event.items); + setIsVisible(true); + }, []); + + const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => + { + if (!event || !isVisible) return; + + if (refreshTimeout) clearTimeout(refreshTimeout); + + setRefreshTimeout(setTimeout(() => + { + widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.USER_CHOOSER)); + }, 100)); + + }, [isVisible, refreshTimeout, widgetHandler]); + + useUiEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, onUserChooserContent); + CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); + CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); + + useEffect(() => + { + if(!items) return; + + let filteredGroupItems = [ ...items ]; + + if(searchValue && searchValue.length) + { + const comparison = searchValue.toLocaleLowerCase(); + + filteredGroupItems = items.filter(item => + { + if(comparison && comparison.length) + { + if(item.name.toLocaleLowerCase().includes(comparison)) return item; + } + + return null; + }); + } + + setFilteredItems(filteredGroupItems); + }, [ items, searchValue, setFilteredItems ]); + + const onClose = useCallback(() => + { + setIsVisible(false); + setItems(null); + }, []); + + const onClickItem = useCallback((item: RoomObjectItem) => + { + setSelectedItem(item); + widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category)); + }, [setSelectedItem, widgetHandler]); + + const rowRenderer = function ({ + key, // Unique key within array of rows + index, // Index of row within collection + isScrolling, // The List is currently being scrolled + isVisible, // This row is visible within the List (eg it is not an overscanned row) + style, // Style object to be applied to row (to position it) + }) + { + return ( +
onClickItem(filteredItems[index])} className={(selectedItem === filteredItems[index] ? 'selected-item ' : '') + 'list-item'}> + {filteredItems[index].name} +
+ ); + } + + if(!isVisible) return null; + + return ( +
+ + + +
+
+ setSearchValue(event.target.value)} /> +
+
+ +
+
+
+ ); +} diff --git a/src/views/room/widgets/choosers/utils/sorting.ts b/src/views/room/widgets/choosers/utils/sorting.ts new file mode 100644 index 00000000..eee12529 --- /dev/null +++ b/src/views/room/widgets/choosers/utils/sorting.ts @@ -0,0 +1,18 @@ +export function dynamicSort(property) +{ + // Source: https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value + let sortOrder = 1; + if(property[0] === '-') + { + sortOrder = -1; + property = property.substr(1); + } + return function (a,b) + { + /* next line works with strings and numbers, + * and you may want to customize it to your needs + */ + const result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0; + return result * sortOrder; + }; +} From 05fa86d46440fdf7d297741e6e07766709ef1746 Mon Sep 17 00:00:00 2001 From: Dank074 Date: Wed, 11 Aug 2021 00:05:22 -0500 Subject: [PATCH 17/24] refactor choosers to use composition --- .../handlers/FurniChooserWidgetHandler.ts | 23 +++-- .../room/handlers/UserChooserWidgetHandler.ts | 14 ++- .../widgets/choosers/ChooserWidgetView.tsx | 87 ++++++++++++++++++ .../choosers/ChooserWidgetView.type.ts | 12 +++ .../choosers/FurniChooserWidgetView.tsx | 91 +------------------ .../choosers/UserChooserWidgetView.tsx | 91 +------------------ 6 files changed, 133 insertions(+), 185 deletions(-) create mode 100644 src/views/room/widgets/choosers/ChooserWidgetView.tsx create mode 100644 src/views/room/widgets/choosers/ChooserWidgetView.type.ts diff --git a/src/views/room/handlers/FurniChooserWidgetHandler.ts b/src/views/room/handlers/FurniChooserWidgetHandler.ts index 2e2c7630..9dc3f12e 100644 --- a/src/views/room/handlers/FurniChooserWidgetHandler.ts +++ b/src/views/room/handlers/FurniChooserWidgetHandler.ts @@ -51,8 +51,13 @@ export class FurniChooserWidgetHandler extends RoomWidgetHandler const roomId = this.container.roomSession.roomId; const furniInRoom : RoomObjectItem[] = []; - furniInRoom.push(...GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.WALL).map(roomObject => { - const type = roomObject.type; + const wallItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.WALL); + const floorItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.FLOOR); + + wallItems.forEach( wallItem => { + if(!wallItem) return; + + const type = wallItem.type; let name = null; if(type.startsWith('poster')) { @@ -61,7 +66,7 @@ export class FurniChooserWidgetHandler extends RoomWidgetHandler } else { - const furniTypeId = Number.parseInt(roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID)); + const furniTypeId = Number.parseInt(wallItem.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID)); const wallItemData = GetSessionDataManager().getWallItemData(furniTypeId); if(wallItemData != null && wallItemData.name.length > 0) { @@ -72,16 +77,18 @@ export class FurniChooserWidgetHandler extends RoomWidgetHandler name = type; } } - return new RoomObjectItem(roomObject.id, RoomObjectCategory.WALL, name) - })); + furniInRoom.push(new RoomObjectItem(wallItem.id, RoomObjectCategory.WALL, name)); + }); - furniInRoom.push(...GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.FLOOR).map(roomObject => { + floorItems.forEach(roomObject => { + if(!roomObject) return; + const furniTypeId = Number.parseInt(roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID)); const floorItemData = GetSessionDataManager().getFloorItemData(furniTypeId); const name = floorItemData != null ? floorItemData.name : roomObject.type; - return new RoomObjectItem(roomObject.id, RoomObjectCategory.FLOOR, name); - })); + furniInRoom.push(new RoomObjectItem(roomObject.id, RoomObjectCategory.FLOOR, name)); + }); furniInRoom.sort(dynamicSort('name')); diff --git a/src/views/room/handlers/UserChooserWidgetHandler.ts b/src/views/room/handlers/UserChooserWidgetHandler.ts index 379b479d..ce889635 100644 --- a/src/views/room/handlers/UserChooserWidgetHandler.ts +++ b/src/views/room/handlers/UserChooserWidgetHandler.ts @@ -38,15 +38,19 @@ export class UserChooserWidgetHandler extends RoomWidgetHandler const roomId = this.container.roomSession.roomId; const categoryId = RoomObjectCategory.UNIT; - const units = []; + const units : RoomObjectItem[] = []; - units.push(...GetRoomEngine().getRoomObjects(roomId, categoryId).map(roomObject => { + const roomObjects = GetRoomEngine().getRoomObjects(roomId, categoryId); + + roomObjects.forEach(roomObject => { + if(!roomObject) return; + const unitData = this.container.roomSession.userDataManager.getUserDataByIndex(roomObject.id); - if(!unitData) return null; + if(!unitData) return; - return new RoomObjectItem(unitData.roomIndex, categoryId, unitData.name); - })); + units.push(new RoomObjectItem(unitData.roomIndex, categoryId, unitData.name)); + }); units.sort(dynamicSort('name')); dispatchUiEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, units)); diff --git a/src/views/room/widgets/choosers/ChooserWidgetView.tsx b/src/views/room/widgets/choosers/ChooserWidgetView.tsx new file mode 100644 index 00000000..05d3e7bf --- /dev/null +++ b/src/views/room/widgets/choosers/ChooserWidgetView.tsx @@ -0,0 +1,87 @@ +import { FC, useCallback, useEffect, useState } from 'react'; +import List from 'react-virtualized/dist/commonjs/List'; +import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; +import { CreateEventDispatcherHook } from '../../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { LocalizeText } from '../../../../utils'; +import { useRoomContext } from '../../context/RoomContext'; +import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; +import { RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../../messages'; +import { ChooserWidgetViewProps } from './ChooserWidgetView.type'; + +export const ChooserWidgetView: FC = props => +{ + const [filteredItems, setFilteredItems] = useState([]); + const [selectedItem, setSelectedItem] = useState(null); + const [refreshTimeout, setRefreshTimeout] = useState>(null); + const [searchValue, setSearchValue] = useState(''); + const { title = null, onCloseClick = null, displayItemId = false, items = null, messageType = null, roomWidgetRoomObjectUpdateEvents = null } = props; + const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); + + useEffect(() => + { + if (!items) return; + + const filteredGroupItems = items.filter(item => + { + return item.name.toLocaleLowerCase().includes(searchValue.toLocaleLowerCase()); + }); + + setFilteredItems(filteredGroupItems); + }, [items, searchValue, setFilteredItems]); + + const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => + { + if (!event) return; + + if (refreshTimeout) clearTimeout(refreshTimeout); + + setRefreshTimeout(setTimeout(() => + { + widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(messageType)); + }, 100)); + + }, [refreshTimeout, messageType, widgetHandler]); + + roomWidgetRoomObjectUpdateEvents.forEach(event => CreateEventDispatcherHook(event, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent)); + + const onClickItem = useCallback((item: RoomObjectItem) => + { + setSelectedItem(item); + widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category)); + }, [setSelectedItem, widgetHandler]); + + const rowRenderer = function ({ + key, // Unique key within array of rows + index, // Index of row within collection + isScrolling, // The List is currently being scrolled + isVisible, // This row is visible within the List (eg it is not an overscanned row) + style, // Style object to be applied to row (to position it) + }) + { + return ( +
onClickItem(filteredItems[index])} className={(selectedItem === filteredItems[index] ? 'selected-item ' : '') + 'list-item'}> + {filteredItems[index].name} + {displayItemId && (' - ' + filteredItems[index].id)} +
+ ); + } + + return ( + + + +
+
+ setSearchValue(event.target.value)} /> +
+
+ +
+
+ ); +} diff --git a/src/views/room/widgets/choosers/ChooserWidgetView.type.ts b/src/views/room/widgets/choosers/ChooserWidgetView.type.ts new file mode 100644 index 00000000..87c9ef9f --- /dev/null +++ b/src/views/room/widgets/choosers/ChooserWidgetView.type.ts @@ -0,0 +1,12 @@ +import { MouseEvent } from 'react'; +import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; + +export interface ChooserWidgetViewProps +{ + title: string; + onCloseClick: (event: MouseEvent) => void; + displayItemId: boolean; + items: RoomObjectItem[]; + messageType: string; + roomWidgetRoomObjectUpdateEvents: string[]; +} diff --git a/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx b/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx index 7bc25c54..d3d1b617 100644 --- a/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx +++ b/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx @@ -1,23 +1,16 @@ -import { FC, useCallback, useEffect, useState } from 'react'; -import List from 'react-virtualized/dist/commonjs/List'; +import { FC, useCallback, useState } from 'react'; import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; import { RoomWidgetChooserContentEvent } from '../../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; -import { CreateEventDispatcherHook, useUiEvent } from '../../../../hooks'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { useUiEvent } from '../../../../hooks'; import { LocalizeText } from '../../../../utils'; -import { useRoomContext } from '../../context/RoomContext'; import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; -import { RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../../messages'; +import { RoomWidgetRequestWidgetMessage } from '../../messages'; +import { ChooserWidgetView } from './ChooserWidgetView'; export const FurniChooserWidgetView: FC = props => { const [isVisible, setIsVisible] = useState(false); const [items, setItems] = useState(null); - const [filteredItems, setFilteredItems] = useState(null); - const [selectedItem, setSelectedItem] = useState(null); - const [refreshTimeout, setRefreshTimeout] = useState>(null); - const [searchValue, setSearchValue] = useState(''); - const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); const onFurniChooserContent = useCallback((event: RoomWidgetChooserContentEvent) => { @@ -25,46 +18,7 @@ export const FurniChooserWidgetView: FC = props => setIsVisible(true); }, []); - const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => - { - if (!event || !isVisible) return; - - if (refreshTimeout) clearTimeout(refreshTimeout); - - setRefreshTimeout(setTimeout(() => - { - widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.FURNI_CHOOSER)); - }, 100)); - - }, [isVisible, refreshTimeout, widgetHandler]); - useUiEvent(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, onFurniChooserContent); - CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); - CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.FURNI_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); - - useEffect(() => - { - if(!items) return; - - let filteredGroupItems = [ ...items ]; - - if(searchValue && searchValue.length) - { - const comparison = searchValue.toLocaleLowerCase(); - - filteredGroupItems = items.filter(item => - { - if(comparison && comparison.length) - { - if(item.name.toLocaleLowerCase().includes(comparison)) return item; - } - - return null; - }); - } - - setFilteredItems(filteredGroupItems); - }, [ items, searchValue, setFilteredItems ]); const onClose = useCallback(() => @@ -73,46 +27,11 @@ export const FurniChooserWidgetView: FC = props => setItems(null); }, []); - const onClickItem = useCallback((item: RoomObjectItem) => - { - setSelectedItem(item); - widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category)); - }, [setSelectedItem, widgetHandler]); - - const rowRenderer = function ({ - key, // Unique key within array of rows - index, // Index of row within collection - isScrolling, // The List is currently being scrolled - isVisible, // This row is visible within the List (eg it is not an overscanned row) - style, // Style object to be applied to row (to position it) - }) - { - return ( -
onClickItem(filteredItems[index])} className={(selectedItem === filteredItems[index] ? 'selected-item ' : '') + 'list-item'}> - {filteredItems[index].name} - {filteredItems[index].id} -
- ); - } - if (!isVisible) return null; return (
- - - -
-
- setSearchValue(event.target.value)} /> -
-
- -
-
+
) } diff --git a/src/views/room/widgets/choosers/UserChooserWidgetView.tsx b/src/views/room/widgets/choosers/UserChooserWidgetView.tsx index 86ebaafd..e7f1e3be 100644 --- a/src/views/room/widgets/choosers/UserChooserWidgetView.tsx +++ b/src/views/room/widgets/choosers/UserChooserWidgetView.tsx @@ -1,23 +1,16 @@ -import { FC, useCallback, useEffect, useState } from 'react'; -import List from 'react-virtualized/dist/commonjs/List'; +import { FC, useCallback, useState } from 'react'; import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; import { RoomWidgetChooserContentEvent } from '../../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; -import { CreateEventDispatcherHook, useUiEvent } from '../../../../hooks'; -import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { useUiEvent } from '../../../../hooks'; import { LocalizeText } from '../../../../utils'; -import { useRoomContext } from '../../context/RoomContext'; import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; -import { RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../../messages'; +import { RoomWidgetRequestWidgetMessage } from '../../messages'; +import { ChooserWidgetView } from './ChooserWidgetView'; export const UserChooserWidgetView : FC = props => { const [isVisible, setIsVisible] = useState(false); const [items, setItems] = useState(null); - const [filteredItems, setFilteredItems] = useState(null); - const [selectedItem, setSelectedItem] = useState(null); - const [refreshTimeout, setRefreshTimeout] = useState>(null); - const [searchValue, setSearchValue] = useState(''); - const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); const onUserChooserContent = useCallback((event: RoomWidgetChooserContentEvent) => { @@ -25,93 +18,19 @@ export const UserChooserWidgetView : FC = props => setIsVisible(true); }, []); - const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => - { - if (!event || !isVisible) return; - - if (refreshTimeout) clearTimeout(refreshTimeout); - - setRefreshTimeout(setTimeout(() => - { - widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(RoomWidgetRequestWidgetMessage.USER_CHOOSER)); - }, 100)); - - }, [isVisible, refreshTimeout, widgetHandler]); - useUiEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, onUserChooserContent); - CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_REMOVED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); - CreateEventDispatcherHook(RoomWidgetRoomObjectUpdateEvent.USER_ADDED, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent); - useEffect(() => - { - if(!items) return; - - let filteredGroupItems = [ ...items ]; - - if(searchValue && searchValue.length) - { - const comparison = searchValue.toLocaleLowerCase(); - - filteredGroupItems = items.filter(item => - { - if(comparison && comparison.length) - { - if(item.name.toLocaleLowerCase().includes(comparison)) return item; - } - - return null; - }); - } - - setFilteredItems(filteredGroupItems); - }, [ items, searchValue, setFilteredItems ]); - const onClose = useCallback(() => { setIsVisible(false); setItems(null); }, []); - - const onClickItem = useCallback((item: RoomObjectItem) => - { - setSelectedItem(item); - widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category)); - }, [setSelectedItem, widgetHandler]); - - const rowRenderer = function ({ - key, // Unique key within array of rows - index, // Index of row within collection - isScrolling, // The List is currently being scrolled - isVisible, // This row is visible within the List (eg it is not an overscanned row) - style, // Style object to be applied to row (to position it) - }) - { - return ( -
onClickItem(filteredItems[index])} className={(selectedItem === filteredItems[index] ? 'selected-item ' : '') + 'list-item'}> - {filteredItems[index].name} -
- ); - } if(!isVisible) return null; return (
- - - -
-
- setSearchValue(event.target.value)} /> -
-
- -
-
+
); } From cf3a56206453f59e0b38fc4cd376377c2ece02ee Mon Sep 17 00:00:00 2001 From: Bill Date: Wed, 11 Aug 2021 20:49:51 -0400 Subject: [PATCH 18/24] Add friendly time component --- .../shared/friendly-time/FriendlyTimeView.tsx | 34 +++++++++++++++++++ .../friendly-time/FriendlyTimeView.types.ts | 7 ++++ 2 files changed, 41 insertions(+) create mode 100644 src/views/shared/friendly-time/FriendlyTimeView.tsx create mode 100644 src/views/shared/friendly-time/FriendlyTimeView.types.ts diff --git a/src/views/shared/friendly-time/FriendlyTimeView.tsx b/src/views/shared/friendly-time/FriendlyTimeView.tsx new file mode 100644 index 00000000..4df913e6 --- /dev/null +++ b/src/views/shared/friendly-time/FriendlyTimeView.tsx @@ -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 = 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 ( +
{ getFriendlyTime() }
+ ); +} diff --git a/src/views/shared/friendly-time/FriendlyTimeView.types.ts b/src/views/shared/friendly-time/FriendlyTimeView.types.ts new file mode 100644 index 00000000..7ce31f31 --- /dev/null +++ b/src/views/shared/friendly-time/FriendlyTimeView.types.ts @@ -0,0 +1,7 @@ +import { DetailsHTMLAttributes } from 'react'; + +export interface FriendlyTimeViewProps extends DetailsHTMLAttributes +{ + seconds: number; + isShort?: boolean; +} From 567064b716601d4d8450edf7fb3d41c60934f63b Mon Sep 17 00:00:00 2001 From: Bill Date: Thu, 12 Aug 2021 03:22:11 -0400 Subject: [PATCH 19/24] Fix math issue --- src/views/room/widgets/context-menu/ContextMenuView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/views/room/widgets/context-menu/ContextMenuView.tsx b/src/views/room/widgets/context-menu/ContextMenuView.tsx index ce3a4397..81a28b5a 100644 --- a/src/views/room/widgets/context-menu/ContextMenuView.tsx +++ b/src/views/room/widgets/context-menu/ContextMenuView.tsx @@ -80,8 +80,8 @@ export const ContextMenuView: FC = 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); From cfcea786fa6be12ab2b487d2acf7336b399cc7ad Mon Sep 17 00:00:00 2001 From: Bill Date: Fri, 13 Aug 2021 02:56:26 -0400 Subject: [PATCH 20/24] Update API --- src/App.tsx | 8 ++++++-- src/api/navigator/VisitRoom.ts | 6 ------ src/api/nitro/session/CreateRoomSession.ts | 6 ++++++ src/api/nitro/session/GoToDesktop.ts | 7 +++++++ src/api/nitro/session/index.ts | 2 ++ src/views/toolbar/ToolbarView.tsx | 10 +++++----- 6 files changed, 26 insertions(+), 13 deletions(-) delete mode 100644 src/api/navigator/VisitRoom.ts create mode 100644 src/api/nitro/session/CreateRoomSession.ts create mode 100644 src/api/nitro/session/GoToDesktop.ts diff --git a/src/App.tsx b/src/App.tsx index 8a9c2f2c..6b759d0e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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(() => { diff --git a/src/api/navigator/VisitRoom.ts b/src/api/navigator/VisitRoom.ts deleted file mode 100644 index 9448f66d..00000000 --- a/src/api/navigator/VisitRoom.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { GetRoomSessionManager } from '../nitro'; - -export function VisitRoom(roomId: number, password: string = null): void -{ - GetRoomSessionManager().createSession(roomId, password); -} diff --git a/src/api/nitro/session/CreateRoomSession.ts b/src/api/nitro/session/CreateRoomSession.ts new file mode 100644 index 00000000..3be6a8ab --- /dev/null +++ b/src/api/nitro/session/CreateRoomSession.ts @@ -0,0 +1,6 @@ +import { GetRoomSessionManager } from './GetRoomSessionManager'; + +export function CreateRoomSession(roomId: number, password: string = null): void +{ + GetRoomSessionManager().createSession(roomId, password); +} diff --git a/src/api/nitro/session/GoToDesktop.ts b/src/api/nitro/session/GoToDesktop.ts new file mode 100644 index 00000000..4617043c --- /dev/null +++ b/src/api/nitro/session/GoToDesktop.ts @@ -0,0 +1,7 @@ +import { DesktopViewComposer } from '@nitrots/nitro-renderer'; +import { SendMessageHook } from '../../../hooks'; + +export function GoToDesktop(): void +{ + SendMessageHook(new DesktopViewComposer()); +} diff --git a/src/api/nitro/session/index.ts b/src/api/nitro/session/index.ts index 31861a96..5409266d 100644 --- a/src/api/nitro/session/index.ts +++ b/src/api/nitro/session/index.ts @@ -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'; diff --git a/src/views/toolbar/ToolbarView.tsx b/src/views/toolbar/ToolbarView.tsx index 0fcd77a0..7445958f 100644 --- a/src/views/toolbar/ToolbarView.tsx +++ b/src/views/toolbar/ToolbarView.tsx @@ -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'; @@ -128,8 +128,8 @@ export const ToolbarView: FC = props => const visitDesktop = useCallback(() => { if(!GetRoomSession()) return; - - SendMessageHook(new DesktopViewComposer()); + + GoToDesktop(); GetRoomSessionManager().removeSession(-1); }, []); From 51558c4f3ccda81bc52142ebab1a16eb6efa44ac Mon Sep 17 00:00:00 2001 From: Bill Date: Fri, 13 Aug 2021 02:56:39 -0400 Subject: [PATCH 21/24] Add doorbell / password --- src/events/navigator/UpdateDoorStateEvent.ts | 27 ++++++ src/events/navigator/index.ts | 1 + .../navigator/NavigatorMessageHandler.tsx | 54 +++++++----- src/views/navigator/NavigatorView.scss | 18 ++++ src/views/navigator/NavigatorView.tsx | 85 ++++++++++++++++++- .../NavigatorRoomDoorbellView.tsx | 43 ++++++++++ .../NavigatorRoomDoorbellView.types.ts | 8 ++ .../NavigatorRoomPasswordView.tsx | 45 ++++++++++ .../NavigatorRoomPasswordView.types.ts | 8 ++ .../NavigatorSearchResultItemView.tsx | 27 +++++- 10 files changed, 290 insertions(+), 26 deletions(-) create mode 100644 src/events/navigator/UpdateDoorStateEvent.ts create mode 100644 src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.tsx create mode 100644 src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.types.ts create mode 100644 src/views/navigator/views/room-password/NavigatorRoomPasswordView.tsx create mode 100644 src/views/navigator/views/room-password/NavigatorRoomPasswordView.types.ts diff --git a/src/events/navigator/UpdateDoorStateEvent.ts b/src/events/navigator/UpdateDoorStateEvent.ts new file mode 100644 index 00000000..0c4bdfca --- /dev/null +++ b/src/events/navigator/UpdateDoorStateEvent.ts @@ -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; + } +} diff --git a/src/events/navigator/index.ts b/src/events/navigator/index.ts index c2e7371c..892c758a 100644 --- a/src/events/navigator/index.ts +++ b/src/events/navigator/index.ts @@ -1 +1,2 @@ export * from './NavigatorEvent'; +export * from './UpdateDoorStateEvent'; diff --git a/src/views/navigator/NavigatorMessageHandler.tsx b/src/views/navigator/NavigatorMessageHandler.tsx index 964007e6..f2e4e019 100644 --- a/src/views/navigator/NavigatorMessageHandler.tsx +++ b/src/views/navigator/NavigatorMessageHandler.tsx @@ -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 = 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 = 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 = props = { const parser = event.getParser(); - VisitRoom(parser.roomId); + CreateRoomSession(parser.roomId); }, []); const onNavigatorHomeRoomEvent = useCallback((event: NavigatorHomeRoomEvent) => @@ -187,6 +200,7 @@ export const NavigatorMessageHandler: FC = props = CreateMessageHook(RoomInfoEvent, onRoomInfoEvent); CreateMessageHook(RoomDoorbellEvent, onRoomDoorbellEvent); CreateMessageHook(RoomDoorbellAcceptedEvent, onRoomDoorbellAcceptedEvent); + CreateMessageHook(RoomDoorbellRejectedEvent, onRoomDoorbellRejectedEvent); CreateMessageHook(GenericErrorEvent, onGenericErrorEvent); CreateMessageHook(NavigatorMetadataEvent, onNavigatorMetadataEvent); CreateMessageHook(NavigatorSearchEvent, onNavigatorSearchEvent); diff --git a/src/views/navigator/NavigatorView.scss b/src/views/navigator/NavigatorView.scss index e17312aa..d29e353b 100644 --- a/src/views/navigator/NavigatorView.scss +++ b/src/views/navigator/NavigatorView.scss @@ -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'; diff --git a/src/views/navigator/NavigatorView.tsx b/src/views/navigator/NavigatorView.tsx index 62a60ab9..320e1d11 100644 --- a/src/views/navigator/NavigatorView.tsx +++ b/src/views/navigator/NavigatorView.tsx @@ -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 = 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 = 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 = props => } }, []); + const closePendingDoorState = useCallback((state: string) => + { + if(state !== null) + { + setPendingDoorState(prevValue => + { + return { roomData: prevValue.roomData, state }; + }); + } + else setPendingDoorState(null); + }, []); + useEffect(() => { const linkTracker: ILinkEventTracker = { @@ -135,9 +193,28 @@ export const NavigatorView: FC = 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 ; + case UpdateDoorStateEvent.START_PASSWORD: + case UpdateDoorStateEvent.STATE_WRONG_PASSWORD: + return ; + } + + return null; + }, [ pendingDoorState, closePendingDoorState ]); + return ( + { getRoomDoorState } { isVisible && setIsVisible(false) } /> diff --git a/src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.tsx b/src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.tsx new file mode 100644 index 00000000..7bb331b6 --- /dev/null +++ b/src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.tsx @@ -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 = 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 ( + + + + { roomData && { roomData.roomName } } + { (state === UpdateDoorStateEvent.START_DOORBELL) && { LocalizeText('navigator.doorbell.info') } } + { (state === UpdateDoorStateEvent.STATE_WAITING) && { LocalizeText('navigator.doorbell.waiting') } } + { (state === UpdateDoorStateEvent.STATE_NO_ANSWER) && { LocalizeText('navigator.doorbell.no.answer') } } +
+ { (state === UpdateDoorStateEvent.START_DOORBELL) && } + +
+
+
+ ); +} diff --git a/src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.types.ts b/src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.types.ts new file mode 100644 index 00000000..f471bd21 --- /dev/null +++ b/src/views/navigator/views/room-doorbell/NavigatorRoomDoorbellView.types.ts @@ -0,0 +1,8 @@ +import { RoomDataParser } from '@nitrots/nitro-renderer'; + +export interface NavigatorRoomDoorbellViewProps +{ + roomData: RoomDataParser; + state: string; + onClose: (state: string) => void; +} diff --git a/src/views/navigator/views/room-password/NavigatorRoomPasswordView.tsx b/src/views/navigator/views/room-password/NavigatorRoomPasswordView.tsx new file mode 100644 index 00000000..247d0f71 --- /dev/null +++ b/src/views/navigator/views/room-password/NavigatorRoomPasswordView.tsx @@ -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 = 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 ( + + + + { roomData && { roomData.roomName } } + { (state === UpdateDoorStateEvent.START_PASSWORD) && { LocalizeText('navigator.password.info') } } + { (state === UpdateDoorStateEvent.STATE_WRONG_PASSWORD) && { LocalizeText('navigator.password.retryinfo') } } +
+ + setPassword(event.target.value) } /> +
+
+ + +
+
+
+ ); +} diff --git a/src/views/navigator/views/room-password/NavigatorRoomPasswordView.types.ts b/src/views/navigator/views/room-password/NavigatorRoomPasswordView.types.ts new file mode 100644 index 00000000..4d1ea7d2 --- /dev/null +++ b/src/views/navigator/views/room-password/NavigatorRoomPasswordView.types.ts @@ -0,0 +1,8 @@ +import { RoomDataParser } from '@nitrots/nitro-renderer'; + +export interface NavigatorRoomPasswordViewProps +{ + roomData: RoomDataParser; + state: string; + onClose: (state: string) => void; +} diff --git a/src/views/navigator/views/search-result-item/NavigatorSearchResultItemView.tsx b/src/views/navigator/views/search-result-item/NavigatorSearchResultItemView.tsx index e38eebb1..946b9ed8 100644 --- a/src/views/navigator/views/search-result-item/NavigatorSearchResultItemView.tsx +++ b/src/views/navigator/views/search-result-item/NavigatorSearchResultItemView.tsx @@ -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 = props => @@ -38,7 +41,27 @@ export const NavigatorSearchResultItemView: FC { roomData.habboGroupId > 0 && } { roomData.doorMode !== RoomDataParser.OPEN_STATE && - + }
From dc628d605b954c756b6ea54a74f69a55ad006116 Mon Sep 17 00:00:00 2001 From: Bill Date: Fri, 13 Aug 2021 04:05:46 -0400 Subject: [PATCH 22/24] Add doorbell room widget --- src/views/room/RoomView.tsx | 2 + .../room/events/RoomWidgetDoorbellEvent.ts | 22 +++++ src/views/room/events/index.ts | 1 + .../room/handlers/DoorbellWidgetHandler.ts | 60 +++++++++++++ .../messages/RoomWidgetLetUserInMessage.ts | 27 ++++++ src/views/room/messages/index.ts | 1 + src/views/room/widgets/RoomWidgets.scss | 1 + src/views/room/widgets/RoomWidgetsView.tsx | 2 + .../widgets/doorbell/DoorbellWidgetView.scss | 22 +++++ .../widgets/doorbell/DoorbellWidgetView.tsx | 88 +++++++++++++++++++ 10 files changed, 226 insertions(+) create mode 100644 src/views/room/events/RoomWidgetDoorbellEvent.ts create mode 100644 src/views/room/handlers/DoorbellWidgetHandler.ts create mode 100644 src/views/room/messages/RoomWidgetLetUserInMessage.ts create mode 100644 src/views/room/widgets/doorbell/DoorbellWidgetView.scss create mode 100644 src/views/room/widgets/doorbell/DoorbellWidgetView.tsx diff --git a/src/views/room/RoomView.tsx b/src/views/room/RoomView.tsx index a82d4b9a..1dc5e629 100644 --- a/src/views/room/RoomView.tsx +++ b/src/views/room/RoomView.tsx @@ -7,6 +7,7 @@ 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 { FurnitureContextMenuWidgetHandler } from './handlers/FurnitureContextMenuWidgetHandler'; import { FurnitureCustomStackHeightWidgetHandler } from './handlers/FurnitureCustomStackHeightWidgetHandler'; import { RoomWidgetRoomToolsHandler } from './handlers/RoomWidgetRoomToolsHandler'; @@ -44,6 +45,7 @@ export const RoomView: FC = props => widgetHandlerManager.registerHandler(new RoomWidgetChatHandler()); widgetHandlerManager.registerHandler(new FurnitureContextMenuWidgetHandler()); widgetHandlerManager.registerHandler(new FurnitureCustomStackHeightWidgetHandler()); + widgetHandlerManager.registerHandler(new DoorbellWidgetHandler()); setWidgetHandler(widgetHandlerManager); diff --git a/src/views/room/events/RoomWidgetDoorbellEvent.ts b/src/views/room/events/RoomWidgetDoorbellEvent.ts new file mode 100644 index 00000000..9bad7fa4 --- /dev/null +++ b/src/views/room/events/RoomWidgetDoorbellEvent.ts @@ -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; + } +} diff --git a/src/views/room/events/index.ts b/src/views/room/events/index.ts index 60f8d0c8..4440c0af 100644 --- a/src/views/room/events/index.ts +++ b/src/views/room/events/index.ts @@ -1,4 +1,5 @@ export * from './RoomWidgetAvatarInfoEvent'; +export * from './RoomWidgetDoorbellEvent'; export * from './RoomWidgetFloodControlEvent'; export * from './RoomWidgetObjectNameEvent'; export * from './RoomWidgetRoomEngineUpdateEvent'; diff --git a/src/views/room/handlers/DoorbellWidgetHandler.ts b/src/views/room/handlers/DoorbellWidgetHandler.ts new file mode 100644 index 00000000..b2b69ee9 --- /dev/null +++ b/src/views/room/handlers/DoorbellWidgetHandler.ts @@ -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 + ]; + } +} diff --git a/src/views/room/messages/RoomWidgetLetUserInMessage.ts b/src/views/room/messages/RoomWidgetLetUserInMessage.ts new file mode 100644 index 00000000..9001fe2a --- /dev/null +++ b/src/views/room/messages/RoomWidgetLetUserInMessage.ts @@ -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; + } +} diff --git a/src/views/room/messages/index.ts b/src/views/room/messages/index.ts index 873903a7..732e5665 100644 --- a/src/views/room/messages/index.ts +++ b/src/views/room/messages/index.ts @@ -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'; diff --git a/src/views/room/widgets/RoomWidgets.scss b/src/views/room/widgets/RoomWidgets.scss index ed284cd9..3308120c 100644 --- a/src/views/room/widgets/RoomWidgets.scss +++ b/src/views/room/widgets/RoomWidgets.scss @@ -3,6 +3,7 @@ @import './chat/ChatWidgetView'; @import './chat-input/ChatInputView'; @import './context-menu/ContextMenu'; +@import './doorbell/DoorbellWidgetView'; @import './furniture/FurnitureWidgets'; @import './infostand/InfoStandWidgetView'; @import './object-location/ObjectLocationView'; diff --git a/src/views/room/widgets/RoomWidgetsView.tsx b/src/views/room/widgets/RoomWidgetsView.tsx index 2ed8ba9d..aa20bf27 100644 --- a/src/views/room/widgets/RoomWidgetsView.tsx +++ b/src/views/room/widgets/RoomWidgetsView.tsx @@ -9,6 +9,7 @@ import { AvatarInfoWidgetView } from './avatar-info/AvatarInfoWidgetView'; import { CameraWidgetView } from './camera/CameraWidgetView'; import { ChatInputView } from './chat-input/ChatInputView'; import { ChatWidgetView } from './chat/ChatWidgetView'; +import { DoorbellWidgetView } from './doorbell/DoorbellWidgetView'; import { FurnitureWidgetsView } from './furniture/FurnitureWidgetsView'; import { InfoStandWidgetView } from './infostand/InfoStandWidgetView'; import { RoomThumbnailWidgetView } from './room-thumbnail/RoomThumbnailWidgetView'; @@ -242,6 +243,7 @@ export const RoomWidgetsView: FC = props => + diff --git a/src/views/room/widgets/doorbell/DoorbellWidgetView.scss b/src/views/room/widgets/doorbell/DoorbellWidgetView.scss new file mode 100644 index 00000000..125a5e69 --- /dev/null +++ b/src/views/room/widgets/doorbell/DoorbellWidgetView.scss @@ -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; + } + } + } +} diff --git a/src/views/room/widgets/doorbell/DoorbellWidgetView.tsx b/src/views/room/widgets/doorbell/DoorbellWidgetView.tsx new file mode 100644 index 00000000..7dad34dd --- /dev/null +++ b/src/views/room/widgets/doorbell/DoorbellWidgetView.tsx @@ -0,0 +1,88 @@ +import { FC, useCallback, useState } from 'react'; +import { CreateEventDispatcherHook } from '../../../../hooks'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { LocalizeText } from '../../../../utils'; +import { useRoomContext } from '../../context/RoomContext'; +import { RoomWidgetDoorbellEvent } from '../../events'; +import { RoomWidgetLetUserInMessage } from '../../messages'; + +export const DoorbellWidgetView: FC<{}> = props => +{ + const [ isVisible, setIsVisible ] = useState(false); + const [ users, setUsers ] = useState([]); + const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); + + const addUser = useCallback((userName: string) => + { + if(users.indexOf(userName) >= 0) return; + + const newUsers = [ ...users, userName ]; + + setUsers(newUsers); + setIsVisible(true); + }, [ users ]); + + const removeUser = useCallback((userName: string) => + { + const index = users.indexOf(userName); + + if(index === -1) return; + + const newUsers = [ ...users ]; + + newUsers.splice(index, 1); + + setUsers(newUsers); + + if(!newUsers.length) setIsVisible(false); + }, [ users ]); + + const onRoomWidgetDoorbellEvent = useCallback((event: RoomWidgetDoorbellEvent) => + { + switch(event.type) + { + case RoomWidgetDoorbellEvent.RINGING: + addUser(event.userName); + return; + case RoomWidgetDoorbellEvent.REJECTED: + case RoomWidgetDoorbellEvent.ACCEPTED: + removeUser(event.userName); + return; + } + }, [ addUser, removeUser ]); + + CreateEventDispatcherHook(RoomWidgetDoorbellEvent.RINGING, eventDispatcher, onRoomWidgetDoorbellEvent); + CreateEventDispatcherHook(RoomWidgetDoorbellEvent.REJECTED, eventDispatcher, onRoomWidgetDoorbellEvent); + CreateEventDispatcherHook(RoomWidgetDoorbellEvent.ACCEPTED, eventDispatcher, onRoomWidgetDoorbellEvent); + + const answer = useCallback((userName: string, flag: boolean) => + { + widgetHandler.processWidgetMessage(new RoomWidgetLetUserInMessage(userName, flag)); + + removeUser(userName); + }, [ widgetHandler, removeUser ]); + + if(!users.length) return null; + + return ( + + setIsVisible(false) } /> + +
+ { (users.length > 0) && users.map(userName => + { + return ( +
+ { userName } +
+ + +
+
+ ); + }) } +
+
+
+ ); +} From 077c016212df9a05358e1627442f2605add3f881 Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 15 Aug 2021 00:54:17 -0400 Subject: [PATCH 23/24] Update choosers --- package-lock.json | 10 ++ package.json | 3 +- src/events/room-widgets/index.ts | 1 + .../room/events}/RoomObjectItem.ts | 6 +- .../events}/RoomWidgetChooserContentEvent.ts | 13 +-- src/views/room/events/index.ts | 2 + .../handlers/FurniChooserWidgetHandler.ts | 95 ++++++++----------- .../room/handlers/UserChooserWidgetHandler.ts | 52 ++++------ .../widgets/choosers/ChooserWidgetView.scss | 10 +- .../widgets/choosers/ChooserWidgetView.tsx | 85 +++++++---------- .../choosers/ChooserWidgetView.type.ts | 9 +- .../choosers/FurniChooserWidgetView.tsx | 54 ++++++++--- .../choosers/UserChooserWidgetView.tsx | 55 ++++++++--- .../room/widgets/choosers/utils/sorting.ts | 18 ---- 14 files changed, 203 insertions(+), 210 deletions(-) rename src/{events/room-widgets/choosers => views/room/events}/RoomObjectItem.ts (78%) rename src/{events/room-widgets/choosers => views/room/events}/RoomWidgetChooserContentEvent.ts (52%) delete mode 100644 src/views/room/widgets/choosers/utils/sorting.ts diff --git a/package-lock.json b/package-lock.json index 2b0533bd..8db31629 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index ae0751c7..9a30909a 100644 --- a/package.json +++ b/package.json @@ -55,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" } } diff --git a/src/events/room-widgets/index.ts b/src/events/room-widgets/index.ts index 5eeca25b..960db095 100644 --- a/src/events/room-widgets/index.ts +++ b/src/events/room-widgets/index.ts @@ -1 +1,2 @@ export * from './camera'; +export * from './thumbnail'; diff --git a/src/events/room-widgets/choosers/RoomObjectItem.ts b/src/views/room/events/RoomObjectItem.ts similarity index 78% rename from src/events/room-widgets/choosers/RoomObjectItem.ts rename to src/views/room/events/RoomObjectItem.ts index d11fc5a8..f4fb2d6f 100644 --- a/src/events/room-widgets/choosers/RoomObjectItem.ts +++ b/src/views/room/events/RoomObjectItem.ts @@ -1,8 +1,8 @@ export class RoomObjectItem { - private readonly _id: number; - private readonly _category: number; - private readonly _name: string; + private _id: number; + private _category: number; + private _name: string; constructor(id: number, category: number, name: string) { diff --git a/src/events/room-widgets/choosers/RoomWidgetChooserContentEvent.ts b/src/views/room/events/RoomWidgetChooserContentEvent.ts similarity index 52% rename from src/events/room-widgets/choosers/RoomWidgetChooserContentEvent.ts rename to src/views/room/events/RoomWidgetChooserContentEvent.ts index aabb254e..75ec5f8e 100644 --- a/src/events/room-widgets/choosers/RoomWidgetChooserContentEvent.ts +++ b/src/views/room/events/RoomWidgetChooserContentEvent.ts @@ -1,5 +1,5 @@ -import { RoomWidgetUpdateEvent } from '../../../views/room/events'; import { RoomObjectItem } from './RoomObjectItem'; +import { RoomWidgetUpdateEvent } from './RoomWidgetUpdateEvent'; export class RoomWidgetChooserContentEvent extends RoomWidgetUpdateEvent { @@ -7,23 +7,16 @@ export class RoomWidgetChooserContentEvent extends RoomWidgetUpdateEvent public static FURNI_CHOOSER_CONTENT: string = 'RWCCE_FURNI_CHOOSER_CONTENT'; private _items: RoomObjectItem[]; - private _isAnyRoomController: boolean; - constructor(type: string, items: RoomObjectItem[], isAnyRoomController: boolean = false) + constructor(type: string, items: RoomObjectItem[]) { super(type); - this._items = items.slice(); - this._isAnyRoomController = isAnyRoomController; + this._items = items; } public get items(): RoomObjectItem[] { return this._items; } - - public get isAnyRoomController(): boolean - { - return this._isAnyRoomController; - } } diff --git a/src/views/room/events/index.ts b/src/views/room/events/index.ts index 4440c0af..d584aa5a 100644 --- a/src/views/room/events/index.ts +++ b/src/views/room/events/index.ts @@ -1,4 +1,6 @@ +export * from './RoomObjectItem'; export * from './RoomWidgetAvatarInfoEvent'; +export * from './RoomWidgetChooserContentEvent'; export * from './RoomWidgetDoorbellEvent'; export * from './RoomWidgetFloodControlEvent'; export * from './RoomWidgetObjectNameEvent'; diff --git a/src/views/room/handlers/FurniChooserWidgetHandler.ts b/src/views/room/handlers/FurniChooserWidgetHandler.ts index 9dc3f12e..f1dc79d7 100644 --- a/src/views/room/handlers/FurniChooserWidgetHandler.ts +++ b/src/views/room/handlers/FurniChooserWidgetHandler.ts @@ -1,11 +1,8 @@ import { NitroEvent, RoomObjectCategory, RoomObjectVariable, RoomWidgetEnum } from '@nitrots/nitro-renderer'; -import { GetNitroInstance, GetRoomEngine, GetSessionDataManager } from '../../../api'; -import { RoomObjectItem } from '../../../events/room-widgets/choosers/RoomObjectItem'; -import { RoomWidgetChooserContentEvent } from '../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; -import { dispatchUiEvent } from '../../../hooks'; -import { RoomWidgetUpdateEvent } from '../events'; +import { GetRoomEngine, GetSessionDataManager } from '../../../api'; +import { LocalizeText } from '../../../utils'; +import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetUpdateEvent } from '../events'; import { RoomWidgetMessage, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../messages'; -import { dynamicSort } from '../widgets/choosers/utils/sorting'; import { RoomWidgetHandler } from './RoomWidgetHandler'; export class FurniChooserWidgetHandler extends RoomWidgetHandler @@ -21,78 +18,68 @@ export class FurniChooserWidgetHandler extends RoomWidgetHandler switch(message.type) { case RoomWidgetRequestWidgetMessage.FURNI_CHOOSER: - this.processFurniChooser(); + this.processChooser(); break; case RoomWidgetRoomObjectMessage.SELECT_OBJECT: - this.selectFurni(message); + this.selectRoomObject((message as RoomWidgetRoomObjectMessage)); break; } return null; } - private selectFurni(message: RoomWidgetMessage): void + private processChooser(): void { - const event = message as RoomWidgetRoomObjectMessage; - - if(event == null) return; - - if(event.category === RoomObjectCategory.WALL || event.category === RoomObjectCategory.FLOOR) - { - GetRoomEngine().selectRoomObject(this.container.roomSession.roomId, event.id, event.category); - } - } - - private processFurniChooser(): void - { - - if(this.container == null || this.container.roomSession == null || GetRoomEngine() == null || this.container.roomSession.userDataManager == null) return; - const roomId = this.container.roomSession.roomId; - const furniInRoom : RoomObjectItem[] = []; + const items: RoomObjectItem[] = []; const wallItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.WALL); const floorItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.FLOOR); - wallItems.forEach( wallItem => { - if(!wallItem) return; + wallItems.forEach(roomObject => + { + let name = roomObject.type; - const type = wallItem.type; - let name = null; - if(type.startsWith('poster')) - { - const posterNumber = Number.parseInt(type.replace('poster', '')); - name = GetNitroInstance().localization.getValue('poster_' + posterNumber + '_name'); - } - else - { - const furniTypeId = Number.parseInt(wallItem.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID)); - const wallItemData = GetSessionDataManager().getWallItemData(furniTypeId); - if(wallItemData != null && wallItemData.name.length > 0) + if(name.startsWith('poster')) { - name = wallItemData.name; + name = LocalizeText(`poster_${ name.replace('poster', '') }_name`); } else { - name = type; + const typeId = roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID); + const furniData = GetSessionDataManager().getWallItemData(typeId); + + if(furniData && furniData.name.length) name = furniData.name; } - } - furniInRoom.push(new RoomObjectItem(wallItem.id, RoomObjectCategory.WALL, name)); - }); - floorItems.forEach(roomObject => { - if(!roomObject) return; - - const furniTypeId = Number.parseInt(roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID)); - const floorItemData = GetSessionDataManager().getFloorItemData(furniTypeId); - const name = floorItemData != null ? floorItemData.name : roomObject.type; + items.push(new RoomObjectItem(roomObject.id, RoomObjectCategory.WALL, name)); + }); - furniInRoom.push(new RoomObjectItem(roomObject.id, RoomObjectCategory.FLOOR, name)); - }); + floorItems.forEach(roomObject => + { + let name = roomObject.type; - furniInRoom.sort(dynamicSort('name')); + const typeId = roomObject.model.getValue(RoomObjectVariable.FURNITURE_TYPE_ID); + const furniData = GetSessionDataManager().getFloorItemData(typeId); - dispatchUiEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, furniInRoom, false)); + 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 diff --git a/src/views/room/handlers/UserChooserWidgetHandler.ts b/src/views/room/handlers/UserChooserWidgetHandler.ts index ce889635..5ab8ecc6 100644 --- a/src/views/room/handlers/UserChooserWidgetHandler.ts +++ b/src/views/room/handlers/UserChooserWidgetHandler.ts @@ -1,12 +1,8 @@ import { NitroEvent, RoomObjectCategory, RoomWidgetEnum } from '@nitrots/nitro-renderer'; import { RoomWidgetHandler } from '.'; import { GetRoomEngine } from '../../../api'; -import { RoomObjectItem } from '../../../events/room-widgets/choosers/RoomObjectItem'; -import { RoomWidgetChooserContentEvent } from '../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; -import { dispatchUiEvent } from '../../../hooks'; -import { RoomWidgetUpdateEvent } from '../events'; +import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetUpdateEvent } from '../events'; import { RoomWidgetMessage, RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../messages'; -import { dynamicSort } from '../widgets/choosers/utils/sorting'; export class UserChooserWidgetHandler extends RoomWidgetHandler { @@ -21,51 +17,45 @@ export class UserChooserWidgetHandler extends RoomWidgetHandler switch(message.type) { case RoomWidgetRequestWidgetMessage.USER_CHOOSER: - this.processUserChooser(); + this.processChooser(); break; case RoomWidgetRoomObjectMessage.SELECT_OBJECT: - this.selectUnit(message); + this.selectRoomObject((message as RoomWidgetRoomObjectMessage)); break; } return null; } - private processUserChooser(): void + private processChooser(): void { - - if(this.container == null || this.container.roomSession == null || GetRoomEngine() == null || this.container.roomSession.userDataManager == null) return; - const roomId = this.container.roomSession.roomId; - const categoryId = RoomObjectCategory.UNIT; - const units : RoomObjectItem[] = []; - - const roomObjects = GetRoomEngine().getRoomObjects(roomId, categoryId); + const items: RoomObjectItem[] = []; - roomObjects.forEach(roomObject => { - if(!roomObject) return; + const userItems = GetRoomEngine().getRoomObjects(roomId, RoomObjectCategory.UNIT); - const unitData = this.container.roomSession.userDataManager.getUserDataByIndex(roomObject.id); + userItems.forEach(roomObject => + { + const userData = this.container.roomSession.userDataManager.getUserDataByIndex(roomObject.id); - if(!unitData) return; + if(!userData) return; - units.push(new RoomObjectItem(unitData.roomIndex, categoryId, unitData.name)); + items.push(new RoomObjectItem(userData.roomIndex, RoomObjectCategory.UNIT, userData.name)); + }); + + items.sort((a, b) => + { + return (a.name < b.name) ? -1 : 1; }); - units.sort(dynamicSort('name')); - dispatchUiEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, units)); + this.container.eventDispatcher.dispatchEvent(new RoomWidgetChooserContentEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, items)); } - private selectUnit(k: RoomWidgetMessage): void + private selectRoomObject(message: RoomWidgetRoomObjectMessage): void { - const event = k as RoomWidgetRoomObjectMessage; - - if(event == null) return; - - if(event.category === RoomObjectCategory.UNIT) - { - GetRoomEngine().selectRoomObject(this.container.roomSession.roomId, event.id, event.category); - } + if(message.category !== RoomObjectCategory.UNIT) return; + + GetRoomEngine().selectRoomObject(this.container.roomSession.roomId, message.id, message.category); } public get type(): string diff --git a/src/views/room/widgets/choosers/ChooserWidgetView.scss b/src/views/room/widgets/choosers/ChooserWidgetView.scss index bca2d777..9a461869 100644 --- a/src/views/room/widgets/choosers/ChooserWidgetView.scss +++ b/src/views/room/widgets/choosers/ChooserWidgetView.scss @@ -1,9 +1,11 @@ -.chooser-widget { - .selected-item { - background-color: cadetblue; - } +.nitro-chooser-widget { + .list-item { color: black; overflow: hidden; + + &.selected { + background-color: cadetblue; + } } } diff --git a/src/views/room/widgets/choosers/ChooserWidgetView.tsx b/src/views/room/widgets/choosers/ChooserWidgetView.tsx index 05d3e7bf..db2da704 100644 --- a/src/views/room/widgets/choosers/ChooserWidgetView.tsx +++ b/src/views/room/widgets/choosers/ChooserWidgetView.tsx @@ -1,86 +1,65 @@ -import { FC, useCallback, useEffect, useState } from 'react'; -import List from 'react-virtualized/dist/commonjs/List'; -import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; -import { CreateEventDispatcherHook } from '../../../../hooks'; +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 { RoomWidgetRoomObjectUpdateEvent } from '../../events'; -import { RoomWidgetRequestWidgetMessage, RoomWidgetRoomObjectMessage } from '../../messages'; +import { RoomObjectItem } from '../../events'; +import { RoomWidgetRoomObjectMessage } from '../../messages'; import { ChooserWidgetViewProps } from './ChooserWidgetView.type'; export const ChooserWidgetView: FC = props => { - const [filteredItems, setFilteredItems] = useState([]); - const [selectedItem, setSelectedItem] = useState(null); - const [refreshTimeout, setRefreshTimeout] = useState>(null); - const [searchValue, setSearchValue] = useState(''); - const { title = null, onCloseClick = null, displayItemId = false, items = null, messageType = null, roomWidgetRoomObjectUpdateEvents = null } = props; + const { title = null, items = null, displayItemId = false, onCloseClick = null } = props; + const [ selectedItem, setSelectedItem ] = useState(null); + const [ searchValue, setSearchValue ] = useState(''); const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); - useEffect(() => + const filteredItems = useMemo(() => { - if (!items) return; + if(!items) return []; - const filteredGroupItems = items.filter(item => - { - return item.name.toLocaleLowerCase().includes(searchValue.toLocaleLowerCase()); - }); + if(!searchValue || !searchValue.length) return items; - setFilteredItems(filteredGroupItems); - }, [items, searchValue, setFilteredItems]); + const value = searchValue.toLocaleLowerCase(); - const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => - { - if (!event) return; + return items.filter(item => + { + return item.name.toLocaleLowerCase().includes(value); + }); + }, [ items, searchValue ]); - if (refreshTimeout) clearTimeout(refreshTimeout); - - setRefreshTimeout(setTimeout(() => - { - widgetHandler.processWidgetMessage(new RoomWidgetRequestWidgetMessage(messageType)); - }, 100)); - - }, [refreshTimeout, messageType, widgetHandler]); - - roomWidgetRoomObjectUpdateEvents.forEach(event => CreateEventDispatcherHook(event, eventDispatcher, onRoomWidgetRoomObjectUpdateEvent)); - - const onClickItem = useCallback((item: RoomObjectItem) => + const onItemClick = useCallback((item: RoomObjectItem) => { setSelectedItem(item); widgetHandler.processWidgetMessage(new RoomWidgetRoomObjectMessage(RoomWidgetRoomObjectMessage.SELECT_OBJECT, item.id, item.category)); - }, [setSelectedItem, widgetHandler]); + }, [ widgetHandler, setSelectedItem]); - const rowRenderer = function ({ - key, // Unique key within array of rows - index, // Index of row within collection - isScrolling, // The List is currently being scrolled - isVisible, // This row is visible within the List (eg it is not an overscanned row) - style, // Style object to be applied to row (to position it) - }) + const rowRenderer: ListRowRenderer = (props: ListRowProps) => { + const item = filteredItems[props.index]; + return ( -
onClickItem(filteredItems[index])} className={(selectedItem === filteredItems[index] ? 'selected-item ' : '') + 'list-item'}> - {filteredItems[index].name} - {displayItemId && (' - ' + filteredItems[index].id)} +
onItemClick(item) }> + { item.name } { displayItemId && (' - ' + item.id) }
); } return ( - - + +
- setSearchValue(event.target.value)} /> + setSearchValue(event.target.value)} />
- +
); diff --git a/src/views/room/widgets/choosers/ChooserWidgetView.type.ts b/src/views/room/widgets/choosers/ChooserWidgetView.type.ts index 87c9ef9f..8266f8b9 100644 --- a/src/views/room/widgets/choosers/ChooserWidgetView.type.ts +++ b/src/views/room/widgets/choosers/ChooserWidgetView.type.ts @@ -1,12 +1,9 @@ -import { MouseEvent } from 'react'; -import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; +import { RoomObjectItem } from '../../events'; export interface ChooserWidgetViewProps { title: string; - onCloseClick: (event: MouseEvent) => void; - displayItemId: boolean; items: RoomObjectItem[]; - messageType: string; - roomWidgetRoomObjectUpdateEvents: string[]; + displayItemId: boolean; + onCloseClick: () => void; } diff --git a/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx b/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx index d3d1b617..0ac7ab55 100644 --- a/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx +++ b/src/views/room/widgets/choosers/FurniChooserWidgetView.tsx @@ -1,27 +1,55 @@ import { FC, useCallback, useState } from 'react'; -import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; -import { RoomWidgetChooserContentEvent } from '../../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; -import { useUiEvent } from '../../../../hooks'; +import { CreateEventDispatcherHook } from '../../../../hooks'; import { LocalizeText } from '../../../../utils'; -import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; +import { useRoomContext } from '../../context/RoomContext'; +import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRoomObjectUpdateEvent } from '../../events'; import { RoomWidgetRequestWidgetMessage } from '../../messages'; import { ChooserWidgetView } from './ChooserWidgetView'; -export const FurniChooserWidgetView: FC = props => +export const FurniChooserWidgetView: FC<{}> = props => { - const [isVisible, setIsVisible] = useState(false); - const [items, setItems] = useState(null); + const [ isVisible, setIsVisible ] = useState(false); + const [ items, setItems ] = useState(null); + const [ refreshTimeout, setRefreshTimeout ] = useState>(null); + const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); - const onFurniChooserContent = useCallback((event: RoomWidgetChooserContentEvent) => + 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); }, []); - useUiEvent(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, onFurniChooserContent); + CreateEventDispatcherHook(RoomWidgetChooserContentEvent.FURNI_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent); + const onRoomWidgetRoomObjectUpdateEvent = useCallback((event: RoomWidgetRoomObjectUpdateEvent) => + { + if(!isVisible) return; - const onClose = useCallback(() => + 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); @@ -29,9 +57,5 @@ export const FurniChooserWidgetView: FC = props => if (!isVisible) return null; - return ( -
- -
- ) + return ; } diff --git a/src/views/room/widgets/choosers/UserChooserWidgetView.tsx b/src/views/room/widgets/choosers/UserChooserWidgetView.tsx index e7f1e3be..4c8056af 100644 --- a/src/views/room/widgets/choosers/UserChooserWidgetView.tsx +++ b/src/views/room/widgets/choosers/UserChooserWidgetView.tsx @@ -1,26 +1,55 @@ import { FC, useCallback, useState } from 'react'; -import { RoomObjectItem } from '../../../../events/room-widgets/choosers/RoomObjectItem'; -import { RoomWidgetChooserContentEvent } from '../../../../events/room-widgets/choosers/RoomWidgetChooserContentEvent'; -import { useUiEvent } from '../../../../hooks'; +import { CreateEventDispatcherHook } from '../../../../hooks'; import { LocalizeText } from '../../../../utils'; -import { RoomWidgetRoomObjectUpdateEvent } from '../../events'; +import { useRoomContext } from '../../context/RoomContext'; +import { RoomObjectItem, RoomWidgetChooserContentEvent, RoomWidgetRoomObjectUpdateEvent } from '../../events'; import { RoomWidgetRequestWidgetMessage } from '../../messages'; import { ChooserWidgetView } from './ChooserWidgetView'; -export const UserChooserWidgetView : FC = props => +export const UserChooserWidgetView: FC<{}> = props => { - const [isVisible, setIsVisible] = useState(false); - const [items, setItems] = useState(null); + const [ isVisible, setIsVisible ] = useState(false); + const [ items, setItems ] = useState(null); + const [ refreshTimeout, setRefreshTimeout ] = useState>(null); + const { eventDispatcher = null, widgetHandler = null } = useRoomContext(); - const onUserChooserContent = useCallback((event: RoomWidgetChooserContentEvent) => + 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); }, []); - useUiEvent(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, onUserChooserContent); + CreateEventDispatcherHook(RoomWidgetChooserContentEvent.USER_CHOOSER_CONTENT, eventDispatcher, onRoomWidgetChooserContentEvent); - const onClose = useCallback(() => + 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); @@ -28,9 +57,5 @@ export const UserChooserWidgetView : FC = props => if(!isVisible) return null; - return ( -
- -
- ); + return ; } diff --git a/src/views/room/widgets/choosers/utils/sorting.ts b/src/views/room/widgets/choosers/utils/sorting.ts deleted file mode 100644 index eee12529..00000000 --- a/src/views/room/widgets/choosers/utils/sorting.ts +++ /dev/null @@ -1,18 +0,0 @@ -export function dynamicSort(property) -{ - // Source: https://stackoverflow.com/questions/1129216/sort-array-of-objects-by-string-property-value - let sortOrder = 1; - if(property[0] === '-') - { - sortOrder = -1; - property = property.substr(1); - } - return function (a,b) - { - /* next line works with strings and numbers, - * and you may want to customize it to your needs - */ - const result = (a[property] < b[property]) ? -1 : (a[property] > b[property]) ? 1 : 0; - return result * sortOrder; - }; -} From 39d337e28798a1663d33a9a39dc4b5efe2d8dee5 Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 15 Aug 2021 00:55:12 -0400 Subject: [PATCH 24/24] Update doorbell --- .../widgets/doorbell/DoorbellWidgetView.tsx | 13 +++--------- .../doorbell-item/DoorbellWidgetItemView.tsx | 21 +++++++++++++++++++ .../DoorbellWidgetItemView.types.ts | 6 ++++++ 3 files changed, 30 insertions(+), 10 deletions(-) create mode 100644 src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.tsx create mode 100644 src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.types.ts diff --git a/src/views/room/widgets/doorbell/DoorbellWidgetView.tsx b/src/views/room/widgets/doorbell/DoorbellWidgetView.tsx index 7dad34dd..b4ac0512 100644 --- a/src/views/room/widgets/doorbell/DoorbellWidgetView.tsx +++ b/src/views/room/widgets/doorbell/DoorbellWidgetView.tsx @@ -5,6 +5,7 @@ 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 => { @@ -62,7 +63,7 @@ export const DoorbellWidgetView: FC<{}> = props => removeUser(userName); }, [ widgetHandler, removeUser ]); - if(!users.length) return null; + if(!isVisible) return null; return ( @@ -71,15 +72,7 @@ export const DoorbellWidgetView: FC<{}> = props =>
{ (users.length > 0) && users.map(userName => { - return ( -
- { userName } -
- - -
-
- ); + return answer(userName, true) } deny={ () => answer(userName, false) } />; }) }
diff --git a/src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.tsx b/src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.tsx new file mode 100644 index 00000000..dcd90189 --- /dev/null +++ b/src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.tsx @@ -0,0 +1,21 @@ +import { FC } from 'react'; +import { DoorbellWidgetItemViewProps } from './DoorbellWidgetItemView.types'; + +export const DoorbellWidgetItemView: FC = props => +{ + const { userName = '', accept = null, deny = null } = props; + + return ( +
+ { userName } +
+ + +
+
+ ); +} diff --git a/src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.types.ts b/src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.types.ts new file mode 100644 index 00000000..a0ba4caa --- /dev/null +++ b/src/views/room/widgets/doorbell/doorbell-item/DoorbellWidgetItemView.types.ts @@ -0,0 +1,6 @@ +export interface DoorbellWidgetItemViewProps +{ + userName: string; + accept: () => void; + deny: () => void; +}