diff --git a/public/configuration.json b/public/configuration.json index 8180a6a8..c46111bc 100644 --- a/public/configuration.json +++ b/public/configuration.json @@ -2,7 +2,6 @@ "socket.url": "wss://ws.nitrots.co:2096", "asset.url": "https://nitro.nitrots.co", "image.library.url": "https://swf.nitrots.co/c_images/", - "internal.samples.url": "", "external.samples.url": "https://swf.nitrots.co/dcr/hof_furni/mp3/sound_machine_sample_%sample%.mp3", "image.library.notifications.url": "${image.library.url}notifications/%image%.png", "achievements.images.url": "${image.library.url}Quests/%image%.png", @@ -20,7 +19,7 @@ "avatar.asset.url": "${asset.url}/bundled/figure/%libname%.nitro", "avatar.asset.effect.url": "${asset.url}/bundled/effect/%libname%.nitro", "furni.extras.url": "${asset.url}/images/furniextras/%image%.png", - "url.prefix": "", + "url.prefix": "http://localhost:3000", "chat.viewer.height.percentage": 0.40, "auth.system.enabled": true, "auth.system.http.enabled": true, diff --git a/src/assets/images/icons/arrows.png b/src/assets/images/icons/arrows.png new file mode 100644 index 00000000..47a833ce Binary files /dev/null and b/src/assets/images/icons/arrows.png differ diff --git a/src/assets/images/icons/camera-small.png b/src/assets/images/icons/camera-small.png new file mode 100644 index 00000000..1c0563ee Binary files /dev/null and b/src/assets/images/icons/camera-small.png differ diff --git a/src/assets/images/icons/house-small.png b/src/assets/images/icons/house-small.png new file mode 100644 index 00000000..e106a456 Binary files /dev/null and b/src/assets/images/icons/house-small.png differ diff --git a/src/assets/images/navigator/thumbnail_placeholder.png b/src/assets/images/navigator/thumbnail_placeholder.png index cc6d606c..be26f847 100644 Binary files a/src/assets/images/navigator/thumbnail_placeholder.png and b/src/assets/images/navigator/thumbnail_placeholder.png differ diff --git a/src/assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png b/src/assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png new file mode 100644 index 00000000..63a9397f Binary files /dev/null and b/src/assets/images/room-widgets/thumbnail-widget/thumbnail-camera-spritesheet.png differ diff --git a/src/assets/styles/icons.scss b/src/assets/styles/icons.scss index dcdc0935..95ef7a2b 100644 --- a/src/assets/styles/icons.scss +++ b/src/assets/styles/icons.scss @@ -442,12 +442,36 @@ width: 7px; height: 14px; } - + &.icon-sign-soccer { background: url('../images/icons/sign-soccer.png'); width: 20px; height: 20px; } + + &.icon-house-small { + background: url('../images/icons/house-small.png'); + width: 19px; + height: 14px; + } + + &.icon-camera-small { + background: url('../images/icons/camera-small.png'); + width: 17px; + height: 15px; + } + + &.icon-arrows { + background: url('../images/icons/arrows.png'); + width: 17px; + height: 15px; + } + + &.icon-fb-profile { + background: url('../images/toolbar/icons/friend-bar/profile.png'); + width: 21px; + height: 21px; + } &.icon-camera-colormatrix { background: url('../images/icons/camera-colormatrix.png'); diff --git a/src/events/navigator/NavigatorEvent.ts b/src/events/navigator/NavigatorEvent.ts index 46b496ea..19f3a81c 100644 --- a/src/events/navigator/NavigatorEvent.ts +++ b/src/events/navigator/NavigatorEvent.ts @@ -5,6 +5,9 @@ export class NavigatorEvent extends NitroEvent public static SHOW_NAVIGATOR: string = 'NE_SHOW_NAVIGATOR'; public static HIDE_NAVIGATOR: string = 'NE_HIDE_NAVIGATOR'; public static TOGGLE_NAVIGATOR: string = 'NE_TOGGLE_NAVIGATOR'; + public static TOGGLE_ROOM_INFO: string = 'NE_TOGGLE_ROOM_INFO'; + public static TOGGLE_ROOM_LINK: string = 'NE_TOGGLE_ROOM_LINK'; + public static TOGGLE_ROOM_SETTINGS: string = 'NE_TOGGLE_ROOM_SETTINGS'; private _roomId: number; private _password: string; diff --git a/src/events/room-widgets/thumbnail/RoomWidgetThumbnailEvent.ts b/src/events/room-widgets/thumbnail/RoomWidgetThumbnailEvent.ts new file mode 100644 index 00000000..03451226 --- /dev/null +++ b/src/events/room-widgets/thumbnail/RoomWidgetThumbnailEvent.ts @@ -0,0 +1,8 @@ +import { RoomWidgetUpdateEvent } from '../../../views/room/events/RoomWidgetUpdateEvent'; + +export class RoomWidgetThumbnailEvent extends RoomWidgetUpdateEvent +{ + public static SHOW_THUMBNAIL: string = 'NE_SHOW_THUMBNAIL'; + public static HIDE_THUMBNAIL: string = 'NE_HIDE_THUMBNAIL'; + public static TOGGLE_THUMBNAIL: string = 'NE_TOGGLE_THUMBNAIL'; +} diff --git a/src/events/room-widgets/thumbnail/index.ts b/src/events/room-widgets/thumbnail/index.ts new file mode 100644 index 00000000..56f116d3 --- /dev/null +++ b/src/events/room-widgets/thumbnail/index.ts @@ -0,0 +1 @@ +export * from './RoomWidgetThumbnailEvent'; diff --git a/src/views/avatar-editor/context/AvatarEditorContext.tsx b/src/views/avatar-editor/context/AvatarEditorContext.tsx deleted file mode 100644 index 95087f57..00000000 --- a/src/views/avatar-editor/context/AvatarEditorContext.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { createContext, FC, useContext } from 'react'; -import { AvatarEditorContextProps, IAvatarEditorContext } from './AvatarEditorContext.types'; - -const AvatarEditorContext = createContext({ - avatarEditorState: null, - dispatchAvatarEditorState: null -}); - -export const AvatarEditorContextProvider: FC = props => -{ - return { props.children } -} - -export const useAvatarEditorContext = () => useContext(AvatarEditorContext); diff --git a/src/views/avatar-editor/context/AvatarEditorContext.types.ts b/src/views/avatar-editor/context/AvatarEditorContext.types.ts deleted file mode 100644 index 0df02ff4..00000000 --- a/src/views/avatar-editor/context/AvatarEditorContext.types.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Dispatch, ProviderProps } from 'react'; -import { IAvatarEditorAction, IAvatarEditorState } from '../reducers/AvatarEditorReducer'; - -export interface IAvatarEditorContext -{ - avatarEditorState: IAvatarEditorState; - dispatchAvatarEditorState: Dispatch; -} - -export interface AvatarEditorContextProps extends ProviderProps -{ - -} diff --git a/src/views/navigator/NavigatorMessageHandler.tsx b/src/views/navigator/NavigatorMessageHandler.tsx index bde182e4..f3a62300 100644 --- a/src/views/navigator/NavigatorMessageHandler.tsx +++ b/src/views/navigator/NavigatorMessageHandler.tsx @@ -1,4 +1,4 @@ -import { GenericErrorEvent, NavigatorCategoriesComposer, NavigatorCategoriesEvent, NavigatorMetadataEvent, NavigatorSearchEvent, NavigatorSettingsComposer, RoomCreatedEvent, RoomDataParser, RoomDoorbellAcceptedEvent, RoomDoorbellEvent, RoomForwardEvent, RoomInfoComposer, RoomInfoEvent, RoomInfoOwnerEvent, UserInfoEvent } from 'nitro-renderer'; +import { GenericErrorEvent, NavigatorCategoriesComposer, NavigatorCategoriesEvent, NavigatorHomeRoomEvent, NavigatorMetadataEvent, NavigatorSearchEvent, NavigatorSettingsComposer, RoomCreatedEvent, RoomDataParser, RoomDoorbellAcceptedEvent, RoomDoorbellEvent, RoomForwardEvent, RoomInfoComposer, RoomInfoEvent, RoomInfoOwnerEvent, RoomSettingsUpdatedEvent, UserInfoEvent } from 'nitro-renderer'; import { FC, useCallback } from 'react'; import { GetRoomSessionManager, GetSessionDataManager } from '../../api'; import { VisitRoom } from '../../api/navigator/VisitRoom'; @@ -9,7 +9,7 @@ import { NavigatorActions } from './reducers/NavigatorReducer'; export const NavigatorMessageHandler: FC = props => { - const { dispatchNavigatorState = null } = useNavigatorContext(); + const { navigatorState = null, dispatchNavigatorState = null } = useNavigatorContext(); const onUserInfoEvent = useCallback((event: UserInfoEvent) => { @@ -28,8 +28,19 @@ export const NavigatorMessageHandler: FC = props = { const parser = event.getParser(); + const roomInfoData = navigatorState.roomInfoData; + roomInfoData.currentRoomOwner = parser.isOwner; + roomInfoData.currentRoomId = parser.roomId; + + dispatchNavigatorState({ + type: NavigatorActions.SET_ROOM_INFO_DATA, + payload: { + roomInfoData: roomInfoData + } + }); + SendMessageHook(new RoomInfoComposer(parser.roomId, true, false)); - }, []); + }, [ navigatorState, dispatchNavigatorState ]); const onRoomInfoEvent = useCallback((event: RoomInfoEvent) => { @@ -37,17 +48,15 @@ export const NavigatorMessageHandler: FC = props = if(parser.roomEnter) { - // this._data.enteredGuestRoom = parser.data; - // this._data.staffPick = parser.data.roomPicker; + const roomInfoData = navigatorState.roomInfoData; + roomInfoData.enteredGuestRoom = parser.data; - // const isCreatedRoom = (this._data.createdRoomId === parser.data.roomId); - - // if(!isCreatedRoom && parser.data.displayRoomEntryAd) - // { - // // display ad - // } - - // this._data.createdRoomId = 0; + dispatchNavigatorState({ + type: NavigatorActions.SET_ROOM_INFO_DATA, + payload: { + roomInfoData: roomInfoData + } + }); } else if(parser.roomForward) { @@ -66,10 +75,17 @@ export const NavigatorMessageHandler: FC = props = } else { - // this._data.enteredGuestRoom = parser.data; - // this._data.staffPick = parser.data.roomPicker; + const roomInfoData = navigatorState.roomInfoData; + roomInfoData.enteredGuestRoom = parser.data; + + dispatchNavigatorState({ + type: NavigatorActions.SET_ROOM_INFO_DATA, + payload: { + roomInfoData: roomInfoData + } + }); } - }, []); + }, [ dispatchNavigatorState, navigatorState ]); const onRoomDoorbellEvent = useCallback((event: RoomDoorbellEvent) => { @@ -146,6 +162,25 @@ export const NavigatorMessageHandler: FC = props = VisitRoom(parser.roomId); }, []); + const onNavigatorHomeRoomEvent = useCallback((event: NavigatorHomeRoomEvent) => + { + const parser = event.getParser(); + + dispatchNavigatorState({ + type: NavigatorActions.SET_HOME_ROOM_ID, + payload: { + homeRoomId: parser.homeRoomId + } + }); + }, [ dispatchNavigatorState ]); + + const onRoomSettingsUpdatedEvent = useCallback((event: RoomSettingsUpdatedEvent) => + { + const parser = event.getParser(); + + SendMessageHook(new RoomInfoComposer(parser.roomId, false, false)); + }, []); + CreateMessageHook(UserInfoEvent, onUserInfoEvent); CreateMessageHook(RoomForwardEvent, onRoomForwardEvent); CreateMessageHook(RoomInfoOwnerEvent, onRoomInfoOwnerEvent); @@ -157,6 +192,8 @@ export const NavigatorMessageHandler: FC = props = CreateMessageHook(NavigatorSearchEvent, onNavigatorSearchEvent); CreateMessageHook(NavigatorCategoriesEvent, onNavigatorCategoriesEvent); CreateMessageHook(RoomCreatedEvent, onRoomCreatedEvent); + CreateMessageHook(NavigatorHomeRoomEvent, onNavigatorHomeRoomEvent); + CreateMessageHook(RoomSettingsUpdatedEvent, onRoomSettingsUpdatedEvent); return null; } diff --git a/src/views/navigator/NavigatorView.tsx b/src/views/navigator/NavigatorView.tsx index e1c1ecb3..3b4d5c52 100644 --- a/src/views/navigator/NavigatorView.tsx +++ b/src/views/navigator/NavigatorView.tsx @@ -11,6 +11,9 @@ import { NavigatorMessageHandler } from './NavigatorMessageHandler'; import { NavigatorViewProps } from './NavigatorView.types'; import { initialNavigator, NavigatorActions, NavigatorReducer } from './reducers/NavigatorReducer'; import { NavigatorRoomCreatorView } from './views/creator/NavigatorRoomCreatorView'; +import { NavigatorRoomInfoView } from './views/room-info/NavigatorRoomInfoView'; +import { NavigatorRoomLinkView } from './views/room-link/NavigatorRoomLinkView'; +import { NavigatorRoomSettingsView } from './views/room-settings/NavigatorRoomSettingsView'; import { NavigatorSearchResultSetView } from './views/search-result-set/NavigatorSearchResultSetView'; import { NavigatorSearchView } from './views/search/NavigatorSearchView'; @@ -18,6 +21,8 @@ export const NavigatorView: FC = props => { const [ isVisible, setIsVisible ] = useState(false); const [ isCreatorOpen, setCreatorOpen ] = useState(false); + const [ isRoomInfoOpen, setRoomInfoOpen ] = useState(false); + const [ isRoomLinkOpen, setRoomLinkOpen ] = useState(false); const [ navigatorState, dispatchNavigatorState ] = useReducer(NavigatorReducer, initialNavigator); const { needsNavigatorUpdate = false, topLevelContext = null, topLevelContexts = null } = navigatorState; @@ -34,12 +39,20 @@ export const NavigatorView: FC = props => case NavigatorEvent.TOGGLE_NAVIGATOR: setIsVisible(value => !value); return; + case NavigatorEvent.TOGGLE_ROOM_INFO: + setRoomInfoOpen(value => !value); + return; + case NavigatorEvent.TOGGLE_ROOM_LINK: + setRoomLinkOpen(value => !value); + return; } }, []); useUiEvent(NavigatorEvent.SHOW_NAVIGATOR, onNavigatorEvent); useUiEvent(NavigatorEvent.HIDE_NAVIGATOR, onNavigatorEvent); useUiEvent(NavigatorEvent.TOGGLE_NAVIGATOR, onNavigatorEvent); + useUiEvent(NavigatorEvent.TOGGLE_ROOM_INFO, onNavigatorEvent); + useUiEvent(NavigatorEvent.TOGGLE_ROOM_LINK, onNavigatorEvent); const onRoomSessionEvent = useCallback((event: RoomSessionEvent) => { @@ -110,6 +123,9 @@ export const NavigatorView: FC = props => } + { isRoomInfoOpen && setRoomInfoOpen(false) } /> } + { isRoomLinkOpen && setRoomLinkOpen(false) } /> } + ); } diff --git a/src/views/navigator/common/RoomInfoData.ts b/src/views/navigator/common/RoomInfoData.ts new file mode 100644 index 00000000..3b251b1c --- /dev/null +++ b/src/views/navigator/common/RoomInfoData.ts @@ -0,0 +1,60 @@ +import { RoomDataParser } from 'nitro-renderer'; + +export class RoomInfoData +{ + private _enteredGuestRoom: RoomDataParser = null; + private _createdRoomId: number = 0; + private _currentRoomId: number = 0; + private _currentRoomOwner: boolean = false; + private _canRate: boolean = false; + + public get enteredGuestRoom(): RoomDataParser + { + return this._enteredGuestRoom; + } + + public set enteredGuestRoom(data: RoomDataParser) + { + this._enteredGuestRoom = data; + } + + public get createdRoomId(): number + { + return this._createdRoomId; + } + + public set createdRoomId(id: number) + { + this._createdRoomId = id; + } + + public get currentRoomId(): number + { + return this._currentRoomId; + } + + public set currentRoomId(id: number) + { + this._currentRoomId = id; + } + + public get currentRoomOwner(): boolean + { + return this._currentRoomOwner; + } + + public set currentRoomOwner(flag: boolean) + { + this._currentRoomOwner = flag; + } + + public get canRate(): boolean + { + return this._canRate; + } + + public set canRate(flag: boolean) + { + this._canRate = flag; + } +} diff --git a/src/views/navigator/common/RoomSettingsData.ts b/src/views/navigator/common/RoomSettingsData.ts new file mode 100644 index 00000000..95837d6a --- /dev/null +++ b/src/views/navigator/common/RoomSettingsData.ts @@ -0,0 +1,95 @@ +export default class RoomSettingsData +{ + public roomId: number; + public roomName: string; + public roomOriginalName: string; + public roomDescription: string; + public categoryId: number; + public userCount: number; + public tags: string[]; + public tradeState: number; + public allowWalkthrough: boolean; + + public lockState: number; + public originalLockState: number; + public password: string; + public confirmPassword: string; + public allowPets: boolean; + public allowPetsEat: boolean; + + public usersWithRights: Map; + public friendsWithoutRights: Map; + + public hideWalls: boolean; + public wallThickness: number; + public floorThickness: number; + public chatBubbleMode: number; + public chatBubbleWeight: number; + public chatBubbleSpeed: number; + public chatFloodProtection: number; + public chatDistance: number; + + public muteState: number; + public kickState: number; + public banState: number; + public bannedUsers: Map; + public selectedUserToUnban: number; + + constructor() + { + this.roomId = 0; + this.roomName = null; + this.roomOriginalName = null; + this.roomDescription = null; + this.categoryId = 0; + this.userCount = 0; + this.tags = []; + this.tradeState = 0; + this.allowWalkthrough = false; + + this.lockState = 0; + this.originalLockState = 0; + this.password = null; + this.confirmPassword = null; + this.allowPets = false; + this.allowPetsEat = false; + + this.usersWithRights = new Map(); + this.friendsWithoutRights = new Map(); + + this.hideWalls = false; + this.wallThickness = 0; + this.floorThickness = 0; + this.chatBubbleMode = 0; + this.chatBubbleWeight = 0; + this.chatBubbleSpeed = 0; + this.chatFloodProtection = 0; + this.chatDistance = 0; + + this.muteState = 0; + this.kickState = 0; + this.banState = 0; + this.bannedUsers = new Map(); + this.selectedUserToUnban = 0; + } + + public selectUserToUnban(userId: number): void + { + if(this.selectedUserToUnban === userId) + { + this.selectedUserToUnban = 0; + } + else + { + this.selectedUserToUnban = userId; + } + } + + public get selectedUsernameToUnban(): string + { + if(this.selectedUserToUnban > 0) + return this.bannedUsers.get(this.selectedUserToUnban); + + return null; + } +} diff --git a/src/views/navigator/reducers/NavigatorReducer.tsx b/src/views/navigator/reducers/NavigatorReducer.tsx index f6da40e4..16cdce5d 100644 --- a/src/views/navigator/reducers/NavigatorReducer.tsx +++ b/src/views/navigator/reducers/NavigatorReducer.tsx @@ -1,5 +1,6 @@ import { NavigatorCategoryDataParser, NavigatorSearchResultSet, NavigatorTopLevelContext } from 'nitro-renderer'; import { Reducer } from 'react'; +import { RoomInfoData } from '../common/RoomInfoData'; export interface INavigatorState { @@ -8,6 +9,8 @@ export interface INavigatorState topLevelContexts: NavigatorTopLevelContext[]; searchResult: NavigatorSearchResultSet; categories: NavigatorCategoryDataParser[]; + roomInfoData: RoomInfoData; + homeRoomId: number; } export interface INavigatorAction @@ -19,6 +22,8 @@ export interface INavigatorAction topLevelContexts?: NavigatorTopLevelContext[]; searchResult?: NavigatorSearchResultSet; categories?: NavigatorCategoryDataParser[]; + roomInfoData?: RoomInfoData; + homeRoomId?: number; } } @@ -29,6 +34,8 @@ export class NavigatorActions public static SET_TOP_LEVEL_CONTEXTS: string = 'NA_SET_TOP_LEVEL_CONTEXTS'; public static SET_SEARCH_RESULT: string = 'NA_SET_SEARCH_RESULT'; public static SET_CATEGORIES: string = 'NA_SET_CATEGORIES'; + public static SET_ROOM_INFO_DATA: string = 'NA_SET_ROOM_INFO_DATA'; + public static SET_HOME_ROOM_ID: string = 'NA_SET_HOME_ROOM_ID'; } export const initialNavigator: INavigatorState = { @@ -36,7 +43,9 @@ export const initialNavigator: INavigatorState = { topLevelContext: null, topLevelContexts: null, searchResult: null, - categories: null + categories: null, + roomInfoData: new RoomInfoData(), + homeRoomId: null } export const NavigatorReducer: Reducer = (state, action) => @@ -90,6 +99,16 @@ export const NavigatorReducer: Reducer = (sta return { ...state, categories }; } + case NavigatorActions.SET_ROOM_INFO_DATA: { + const roomInfoData = (action.payload.roomInfoData || state.roomInfoData || null); + + return { ...state, roomInfoData }; + } + case NavigatorActions.SET_HOME_ROOM_ID: { + const homeRoomId = (action.payload.homeRoomId || state.homeRoomId || null); + + return { ...state, homeRoomId }; + } default: return state; } diff --git a/src/views/navigator/views/NavigatorViews.scss b/src/views/navigator/views/NavigatorViews.scss index 60c10722..91af623e 100644 --- a/src/views/navigator/views/NavigatorViews.scss +++ b/src/views/navigator/views/NavigatorViews.scss @@ -2,3 +2,6 @@ @import './search/NavigatorSearchView'; @import './search-result/NavigatorSearchResultView'; @import './search-result-item/NavigatorSearchResultItemView'; +@import './room-info/NavigatorRoomInfoView'; +@import './room-link/NavigatorRoomLinkView'; +@import './room-settings/NavigatorRoomSettingsView'; diff --git a/src/views/navigator/views/room-info/NavigatorRoomInfoView.scss b/src/views/navigator/views/room-info/NavigatorRoomInfoView.scss new file mode 100644 index 00000000..9763a4fc --- /dev/null +++ b/src/views/navigator/views/room-info/NavigatorRoomInfoView.scss @@ -0,0 +1,20 @@ +.nitro-room-info { + width: 230px; + + .gray { + filter: grayscale(1); + opacity: .5; + } + + .room-thumbnail { + position: relative; + width: 110px; + height: 110px; + margin: 0 auto; + background-image: url(../../../../assets/images/navigator/thumbnail_placeholder.png); + background-repeat: no-repeat; + background-position: center; + background-color: rgba($black, .125); + border-color: $black !important; + } +} diff --git a/src/views/navigator/views/room-info/NavigatorRoomInfoView.tsx b/src/views/navigator/views/room-info/NavigatorRoomInfoView.tsx new file mode 100644 index 00000000..d927791d --- /dev/null +++ b/src/views/navigator/views/room-info/NavigatorRoomInfoView.tsx @@ -0,0 +1,148 @@ +import classNames from 'classnames'; +import { RoomMuteComposer, RoomSettingsComposer, RoomStaffPickComposer, UserHomeRoomComposer } from 'nitro-renderer'; +import { FC, useCallback, useEffect, useState } from 'react'; +import { GetConfiguration } from '../../../../api'; +import { NavigatorEvent } from '../../../../events'; +import { RoomWidgetThumbnailEvent } from '../../../../events/room-widgets/thumbnail'; +import { dispatchUiEvent } from '../../../../hooks/events'; +import { SendMessageHook } from '../../../../hooks/messages'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { LocalizeText } from '../../../../utils/LocalizeText'; +import { BadgeImageView } from '../../../shared/badge-image/BadgeImageView'; +import { useNavigatorContext } from '../../context/NavigatorContext'; +import { NavigatorActions } from '../../reducers/NavigatorReducer'; +import { NavigatorRoomInfoViewProps } from './NavigatorRoomInfoView.types'; + +export const NavigatorRoomInfoView: FC = props => +{ + const { onCloseClick = null } = props; + + const { navigatorState = null, dispatchNavigatorState = null } = useNavigatorContext(); + const { roomInfoData = null, homeRoomId = null } = navigatorState; + const [ roomThumbnail, setRoomThumbnail ] = useState(null); + const [ isRoomPicked, setIsRoomPicked ] = useState(false); + const [ isRoomMuted, setIsRoomMuted ] = useState(false); + + useEffect(() => + { + if(!roomInfoData || !roomInfoData.enteredGuestRoom) return; + + if(roomInfoData.enteredGuestRoom.officialRoomPicRef) + { + setRoomThumbnail(GetConfiguration('image.library.url') + roomInfoData.enteredGuestRoom.officialRoomPicRef); + } + + setIsRoomPicked(roomInfoData.enteredGuestRoom.roomPicker); + setIsRoomMuted(roomInfoData.enteredGuestRoom.allInRoomMuted); + }, [ roomInfoData ]); + + const processAction = useCallback((action: string, value?: string) => + { + if(!roomInfoData || !roomInfoData.enteredGuestRoom) return; + + switch(action) + { + case 'set_home_room': + let newRoomId = -1; + + if(homeRoomId !== roomInfoData.enteredGuestRoom.roomId) + { + newRoomId = roomInfoData.enteredGuestRoom.roomId; + } + + dispatchNavigatorState({ + type: NavigatorActions.SET_HOME_ROOM_ID, + payload: { + homeRoomId: newRoomId + } + }); + + SendMessageHook(new UserHomeRoomComposer(newRoomId)); + return; + case 'navigator_search_tag': + return; + case 'open_room_thumbnail_camera': + dispatchUiEvent(new RoomWidgetThumbnailEvent(RoomWidgetThumbnailEvent.TOGGLE_THUMBNAIL)); + return; + case 'open_group_info': + return; + case 'toggle_room_link': + dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_LINK)); + return; + case 'open_room_settings': + SendMessageHook(new RoomSettingsComposer(roomInfoData.enteredGuestRoom.roomId)); + return; + case 'toggle_pick': + setIsRoomPicked(value => !value); + SendMessageHook(new RoomStaffPickComposer(roomInfoData.enteredGuestRoom.roomId)); + return; + case 'toggle_mute': + setIsRoomMuted(value => !value); + SendMessageHook(new RoomMuteComposer()); + return; + case 'close': + onCloseClick(); + return; + } + + }, [ onCloseClick, dispatchNavigatorState, roomInfoData, homeRoomId ]); + + if(!roomInfoData) return null; + + return ( + + processAction('close') } /> + + { roomInfoData.enteredGuestRoom && <> +
+
+ { roomInfoData.enteredGuestRoom.roomName } +
+ processAction('set_home_room') } className={ 'icon icon-house-small cursor-pointer' + classNames({' gray': homeRoomId !== roomInfoData.enteredGuestRoom.roomId }) } /> +
+
+ { roomInfoData.enteredGuestRoom.showOwner && <> +
{ LocalizeText('navigator.roomownercaption') }
+
+ +
{ roomInfoData.enteredGuestRoom.ownerName }
+
+ } +
+
+ { LocalizeText('navigator.roomrating') } { roomInfoData.enteredGuestRoom.score } +
+
+ { roomInfoData.enteredGuestRoom.tags.map(tag => + { + return
processAction('navigator_search_tag', tag) }>#{ tag }
+ }) } +
+
{ roomInfoData.enteredGuestRoom.description }
+
+ processAction('open_room_thumbnail_camera') } /> + { roomThumbnail && } +
+ { roomInfoData.enteredGuestRoom.habboGroupId > 0 &&
processAction('open_group_info') }> +
+ +
+
+ { LocalizeText('navigator.guildbase', ['groupName'], [roomInfoData.enteredGuestRoom.groupName]) } +
+
} +
processAction('toggle_room_link') }> + + { LocalizeText('navigator.embed.caption') } +
+ + + + + + } + +
+
+ ); +}; diff --git a/src/views/navigator/views/room-info/NavigatorRoomInfoView.types.ts b/src/views/navigator/views/room-info/NavigatorRoomInfoView.types.ts new file mode 100644 index 00000000..a4b6b81b --- /dev/null +++ b/src/views/navigator/views/room-info/NavigatorRoomInfoView.types.ts @@ -0,0 +1,5 @@ + +export class NavigatorRoomInfoViewProps +{ + onCloseClick: () => void; +} diff --git a/src/views/navigator/views/room-link/NavigatorRoomLinkView.scss b/src/views/navigator/views/room-link/NavigatorRoomLinkView.scss new file mode 100644 index 00000000..ae215535 --- /dev/null +++ b/src/views/navigator/views/room-link/NavigatorRoomLinkView.scss @@ -0,0 +1,14 @@ +.nitro-room-link { + width: 400px; + + .room-thumbnail { + position: relative; + width: 110px; + height: 110px; + background-image: url(../../../../assets/images/navigator/thumbnail_placeholder.png); + background-repeat: no-repeat; + background-position: center; + background-color: rgba($black, .125); + border-color: $black !important; + } +} diff --git a/src/views/navigator/views/room-link/NavigatorRoomLinkView.tsx b/src/views/navigator/views/room-link/NavigatorRoomLinkView.tsx new file mode 100644 index 00000000..cf697e19 --- /dev/null +++ b/src/views/navigator/views/room-link/NavigatorRoomLinkView.tsx @@ -0,0 +1,70 @@ +import { Nitro } from 'nitro-renderer'; +import { FC, useCallback, useEffect, useRef, useState } from 'react'; +import { GetConfiguration } from '../../../../api'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; +import { LocalizeText } from '../../../../utils/LocalizeText'; +import { useNavigatorContext } from '../../context/NavigatorContext'; +import { NavigatorRoomLinkViewProps } from './NavigatorRoomLinkView.types'; + +export const NavigatorRoomLinkView: FC = props => +{ + const { onCloseClick = null } = props; + const { navigatorState = null } = useNavigatorContext(); + const { roomInfoData = null } = navigatorState; + + const [ roomThumbnail, setRoomThumbnail ] = useState(null); + const [ roomLink, setRoomLink ] = useState(null); + + const elementRef = useRef(); + + useEffect(() => + { + if(!roomInfoData || !roomInfoData.enteredGuestRoom) return; + + if(roomInfoData.enteredGuestRoom.officialRoomPicRef) + { + setRoomThumbnail(GetConfiguration('image.library.url') + roomInfoData.enteredGuestRoom.officialRoomPicRef); + } + + const roomLinkRaw = Nitro.instance.core.configuration.interpolate(LocalizeText('navigator.embed.src', ['roomId'], [roomInfoData.enteredGuestRoom.roomId.toString()])); + + setRoomLink(roomLinkRaw); + }, [ roomInfoData ]); + + const processAction = useCallback((action: string) => + { + if(!roomInfoData || !roomInfoData.enteredGuestRoom) return; + + switch(action) + { + case 'copy_room_link': + elementRef.current.select(); + document.execCommand('copy'); + return; + case 'close': + onCloseClick(); + return; + } + + }, [onCloseClick, roomInfoData]); + + if(!roomInfoData) return null; + + return ( + + processAction('close') } /> + +
+
+ { roomThumbnail && } +
+
+
+
{ LocalizeText('navigator.embed.headline') }
+
{ LocalizeText('navigator.embed.info') }
+ { roomLink && } +
+
+
+ ); +}; diff --git a/src/views/navigator/views/room-link/NavigatorRoomLinkView.types.ts b/src/views/navigator/views/room-link/NavigatorRoomLinkView.types.ts new file mode 100644 index 00000000..3c206b6f --- /dev/null +++ b/src/views/navigator/views/room-link/NavigatorRoomLinkView.types.ts @@ -0,0 +1,5 @@ + +export class NavigatorRoomLinkViewProps +{ + onCloseClick: () => void; +} diff --git a/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.scss b/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.scss new file mode 100644 index 00000000..ac5adf97 --- /dev/null +++ b/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.scss @@ -0,0 +1,3 @@ +.nitro-room-settings { + width: 400px; +} diff --git a/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.tsx b/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.tsx new file mode 100644 index 00000000..c9fe09a0 --- /dev/null +++ b/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.tsx @@ -0,0 +1,127 @@ +import { RoomSettingsEvent, SaveRoomSettingsComposer } from 'nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { CreateMessageHook, SendMessageHook } from '../../../../hooks/messages'; +import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout'; +import { LocalizeText } from '../../../../utils/LocalizeText'; +import RoomSettingsData from '../../common/RoomSettingsData'; +import { NavigatorRoomSettingsAccessTabView } from './views/tab-access/NavigatorRoomSettingsAccessTabView'; +import { NavigatorRoomSettingsBasicTabView } from './views/tab-basic/NavigatorRoomSettingsBasicTabView'; + +const TABS: string[] = [ + 'navigator.roomsettings.tab.1', + 'navigator.roomsettings.tab.2', + 'navigator.roomsettings.tab.3', + 'navigator.roomsettings.tab.4', + 'navigator.roomsettings.tab.5' +]; + +export const NavigatorRoomSettingsView: FC<{}> = props => +{ + const [ roomSettingsData, setRoomSettingsData ] = useState(null); + const [ currentTab, setCurrentTab ] = useState(TABS[0]); + + const updateSettings = useCallback((roomSettings: RoomSettingsData) => + { + console.log('update', roomSettings); + setRoomSettingsData(roomSettings); + }, [ setRoomSettingsData ]); + + const onRoomSettingsEvent = useCallback((event: RoomSettingsEvent) => + { + const parser = event.getParser(); + + const roomSettingsData = new RoomSettingsData(); + + roomSettingsData.roomId = parser.roomId; + roomSettingsData.roomName = parser.name; + roomSettingsData.roomOriginalName = parser.name; + roomSettingsData.roomDescription = parser.description; + roomSettingsData.categoryId = parser.categoryId; + roomSettingsData.userCount = parser.userCount; + roomSettingsData.tradeState = parser.tradeMode; + roomSettingsData.allowWalkthrough = parser.allowWalkthrough; + + roomSettingsData.lockState = parser.state; + roomSettingsData.originalLockState = parser.state; + roomSettingsData.allowPets = parser.allowPets; + + roomSettingsData.hideWalls = parser.hideWalls; + roomSettingsData.wallThickness = parser.thicknessWall; + roomSettingsData.floorThickness = parser.thicknessFloor; + roomSettingsData.chatBubbleMode = parser.chatSettings.mode; + roomSettingsData.chatBubbleWeight = parser.chatSettings.weight; + roomSettingsData.chatBubbleSpeed = parser.chatSettings.speed; + roomSettingsData.chatFloodProtection = parser.chatSettings.protection; + roomSettingsData.chatDistance = parser.chatSettings.distance; + + roomSettingsData.muteState = parser.moderationSettings.allowMute; + roomSettingsData.kickState = parser.moderationSettings.allowKick; + roomSettingsData.banState = parser.moderationSettings.allowBan; + + setRoomSettingsData(roomSettingsData); + }, []); + + CreateMessageHook(RoomSettingsEvent, onRoomSettingsEvent); + + const save = useCallback(() => + { + console.log('save', roomSettingsData) + const composer = new SaveRoomSettingsComposer( + roomSettingsData.roomId, + roomSettingsData.roomName, + roomSettingsData.roomDescription, + roomSettingsData.lockState, + roomSettingsData.password, + roomSettingsData.userCount, + roomSettingsData.categoryId, + roomSettingsData.tags.length, + roomSettingsData.tags, + roomSettingsData.tradeState, + roomSettingsData.allowPets, + roomSettingsData.allowPetsEat, + roomSettingsData.allowWalkthrough, + roomSettingsData.hideWalls, + roomSettingsData.wallThickness, + roomSettingsData.floorThickness, + roomSettingsData.muteState, + roomSettingsData.kickState, + roomSettingsData.banState, + roomSettingsData.chatBubbleMode, + roomSettingsData.chatBubbleWeight, + roomSettingsData.chatBubbleSpeed, + roomSettingsData.chatDistance, + roomSettingsData.chatFloodProtection + ); + + SendMessageHook(composer); + }, [ roomSettingsData ]); + + const processAction = useCallback((action: string) => + { + switch(action) + { + case 'close': + setRoomSettingsData(null); + setCurrentTab(TABS[0]); + return; + } + }, [ setRoomSettingsData ]); + + if(!roomSettingsData) return null; + + return ( + + processAction('close') } /> + + { TABS.map(tab => + { + return setCurrentTab(tab) }>{ LocalizeText(tab) } + }) } + + + { currentTab === TABS[0] && } + { currentTab === TABS[1] && } + + + ); +}; diff --git a/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.types.ts b/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.types.ts new file mode 100644 index 00000000..52eded57 --- /dev/null +++ b/src/views/navigator/views/room-settings/NavigatorRoomSettingsView.types.ts @@ -0,0 +1,8 @@ +import RoomSettingsData from '../../common/RoomSettingsData'; + +export class NavigatorRoomSettingsTabViewProps +{ + roomSettingsData: RoomSettingsData; + setRoomSettingsData: (roomSettings: RoomSettingsData) => void; + onSave: () => void; +} diff --git a/src/views/navigator/views/room-settings/views/tab-access/NavigatorRoomSettingsAccessTabView.tsx b/src/views/navigator/views/room-settings/views/tab-access/NavigatorRoomSettingsAccessTabView.tsx new file mode 100644 index 00000000..579f2e55 --- /dev/null +++ b/src/views/navigator/views/room-settings/views/tab-access/NavigatorRoomSettingsAccessTabView.tsx @@ -0,0 +1,96 @@ +import { FC, useCallback } from 'react'; +import { LocalizeText } from '../../../../../../utils/LocalizeText'; +import RoomSettingsData from '../../../../common/RoomSettingsData'; +import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types'; + +export const NavigatorRoomSettingsAccessTabView: FC = props => +{ + const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props; + + const handleChange = useCallback((field: string, value: string | number | boolean) => + { + const roomSettings = ({...roomSettingsData} as RoomSettingsData); + let save = true; + + switch(field) + { + case 'lock_state': + roomSettings.lockState = Number(value); + + if(Number(value) === 3) save = false; + break; + case 'password': + roomSettings.password = String(value); + save = false; + break; + case 'confirm_password': + roomSettings.confirmPassword = String(value); + save = false; + break; + case 'allow_pets': + roomSettings.allowPets = Boolean(value); + break; + case 'allow_pets_eat': + roomSettings.allowPetsEat = Boolean(value); + break; + } + + setRoomSettingsData(roomSettings); + + if(save) onSave(); + }, [ roomSettingsData, setRoomSettingsData, onSave ]); + + const isPasswordValid = useCallback(() => + { + return (roomSettingsData.password && roomSettingsData.password.length > 0 && roomSettingsData.password === roomSettingsData.confirmPassword); + }, [ roomSettingsData ]); + + const trySave = useCallback(() => + { + if(isPasswordValid()) onSave(); + }, [ isPasswordValid, onSave ]); + + return ( + <> +
{ LocalizeText('navigator.roomsettings.doormode') }
+
+ handleChange('lock_state', 0) } /> + +
+
+ handleChange('lock_state', 1) } /> + +
+
+ handleChange('lock_state', 2) } /> + +
+
+ handleChange('lock_state', 3) } /> + +
+ { roomSettingsData.lockState === 3 && <> +
+ + handleChange('password', e.target.value) } onBlur={ trySave } placeholder="*****" /> +
+
+ + handleChange('confirm_password', e.target.value) } onBlur={ trySave } placeholder="*****" /> + { !isPasswordValid() && + { LocalizeText('navigator.roomsettings.invalidconfirm') } + } +
+ } +
{ LocalizeText('navigator.roomsettings.pets') }
+
+ handleChange('allow_pets', e.target.checked) } /> + +
+
+ handleChange('allow_pets_eat', e.target.checked) } /> + +
+ + ); +}; diff --git a/src/views/navigator/views/room-settings/views/tab-basic/NavigatorRoomSettingsBasicTabView.tsx b/src/views/navigator/views/room-settings/views/tab-basic/NavigatorRoomSettingsBasicTabView.tsx new file mode 100644 index 00000000..1a362e82 --- /dev/null +++ b/src/views/navigator/views/room-settings/views/tab-basic/NavigatorRoomSettingsBasicTabView.tsx @@ -0,0 +1,107 @@ +import { FC, useCallback, useEffect, useState } from 'react'; +import { LocalizeText } from '../../../../../../utils/LocalizeText'; +import RoomSettingsData from '../../../../common/RoomSettingsData'; +import { useNavigatorContext } from '../../../../context/NavigatorContext'; +import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types'; + +export const NavigatorRoomSettingsBasicTabView: FC = props => +{ + const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props; + + const { navigatorState = null } = useNavigatorContext(); + const { categories = null } = navigatorState; + + const [ maxVisitorsList, setMaxVisitorsList ] = useState(null); + + useEffect(() => + { + if(!maxVisitorsList) + { + const list = []; + + for(let i = 10; i <= 100; i = i + 10) + { + list.push(i); + } + + setMaxVisitorsList(list); + } + }, [ maxVisitorsList ]); + + const handleChange = useCallback((field: string, value: string | number | boolean) => + { + const roomSettings = ({...roomSettingsData} as RoomSettingsData); + let save = true; + + switch(field) + { + case 'name': + roomSettings.roomName = String(value); + save = false; + break; + case 'description': + roomSettings.roomDescription = String(value); + save = false; + break; + case 'category': + roomSettings.categoryId = Number(value); + break; + case 'max_visitors': + roomSettings.userCount = Number(value); + break; + case 'trade_state': + roomSettings.tradeState = Number(value); + break; + case 'allow_walkthrough': + roomSettings.allowWalkthrough = Boolean(value); + break; + } + + setRoomSettingsData(roomSettings); + + if(save) onSave(); + }, [ roomSettingsData, setRoomSettingsData, onSave ]); + + return ( + <> +
+ + handleChange('name', e.target.value) } onBlur={ onSave } /> +
+
+ + handleChange('description', e.target.value) } onBlur={ () => onSave() } /> +
+
+ + +
+
+ + +
+
+ + +
+
+ handleChange('allow_walkthrough', e.target.checked) } /> + +
+ + ); +}; diff --git a/src/views/room/RoomView.tsx b/src/views/room/RoomView.tsx index 0ec62aad..3c187e72 100644 --- a/src/views/room/RoomView.tsx +++ b/src/views/room/RoomView.tsx @@ -7,7 +7,10 @@ import { DispatchTouchEvent } from '../../api/nitro/room/DispatchTouchEvent'; import { GetRoomEngine } from '../../api/nitro/room/GetRoomEngine'; import { RoomContextProvider } from './context/RoomContext'; import { RoomWidgetUpdateRoomViewEvent } from './events/RoomWidgetUpdateRoomViewEvent'; -import { FurnitureContextMenuWidgetHandler, FurnitureCustomStackHeightWidgetHandler, IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler } from './handlers'; +import { IRoomWidgetHandlerManager, RoomWidgetAvatarInfoHandler, RoomWidgetChatHandler, RoomWidgetChatInputHandler, RoomWidgetHandlerManager, RoomWidgetInfostandHandler } from './handlers'; +import { FurnitureContextMenuWidgetHandler } from './handlers/FurnitureContextMenuWidgetHandler'; +import { FurnitureCustomStackHeightWidgetHandler } from './handlers/FurnitureCustomStackHeightWidgetHandler'; +import { RoomWidgetRoomToolsHandler } from './handlers/RoomWidgetRoomToolsHandler'; import { RoomColorView } from './RoomColorView'; import { RoomViewProps } from './RoomView.types'; import { RoomWidgetsView } from './widgets/RoomWidgetsView'; @@ -36,6 +39,7 @@ export const RoomView: FC = props => widgetHandlerManager.registerHandler(new RoomWidgetAvatarInfoHandler()); widgetHandlerManager.registerHandler(new RoomWidgetInfostandHandler()); + widgetHandlerManager.registerHandler(new RoomWidgetRoomToolsHandler()); widgetHandlerManager.registerHandler(new RoomWidgetChatInputHandler()); widgetHandlerManager.registerHandler(new RoomWidgetChatHandler()); widgetHandlerManager.registerHandler(new FurnitureContextMenuWidgetHandler()); diff --git a/src/views/room/handlers/RoomWidgetRoomToolsHandler.ts b/src/views/room/handlers/RoomWidgetRoomToolsHandler.ts new file mode 100644 index 00000000..2a275b47 --- /dev/null +++ b/src/views/room/handlers/RoomWidgetRoomToolsHandler.ts @@ -0,0 +1,38 @@ +import { NitroEvent, RoomWidgetEnum, RoomZoomEvent } from 'nitro-renderer'; +import { GetRoomEngine } from '../../../api'; +import { RoomWidgetUpdateEvent } from '../events'; +import { RoomWidgetMessage, RoomWidgetZoomToggleMessage } from '../messages'; +import { RoomWidgetHandler } from './RoomWidgetHandler'; + +export class RoomWidgetRoomToolsHandler extends RoomWidgetHandler +{ + public processEvent(event: NitroEvent): void + {} + + public processWidgetMessage(message: RoomWidgetMessage): RoomWidgetUpdateEvent + { + if(message instanceof RoomWidgetZoomToggleMessage) + { + GetRoomEngine().events.dispatchEvent(new RoomZoomEvent(GetRoomEngine().activeRoomId, message.zoomedIn ? 0 : 1, false)); + } + + return null; + } + + public get type(): string + { + return RoomWidgetEnum.ROOM_TOOLS; + } + + public get eventTypes(): string[] + { + return []; + } + + public get messageTypes(): string[] + { + return [ + RoomWidgetZoomToggleMessage.ZOOM_TOGGLE + ]; + } +} diff --git a/src/views/room/messages/RoomWidgetZoomToggleMessage.ts b/src/views/room/messages/RoomWidgetZoomToggleMessage.ts new file mode 100644 index 00000000..dae47e5b --- /dev/null +++ b/src/views/room/messages/RoomWidgetZoomToggleMessage.ts @@ -0,0 +1,18 @@ +import { RoomWidgetMessage } from '.'; + +export class RoomWidgetZoomToggleMessage extends RoomWidgetMessage +{ + public static ZOOM_TOGGLE: string = 'RWZTM_ZOOM_TOGGLE'; + private _zoomedIn: boolean; + + constructor(zoomedIn: boolean) + { + super(RoomWidgetZoomToggleMessage.ZOOM_TOGGLE); + this._zoomedIn = zoomedIn; + } + + public get zoomedIn(): boolean + { + return this._zoomedIn; + } +} diff --git a/src/views/room/messages/index.ts b/src/views/room/messages/index.ts index bc86f9f3..873903a7 100644 --- a/src/views/room/messages/index.ts +++ b/src/views/room/messages/index.ts @@ -12,3 +12,4 @@ export * from './RoomWidgetRequestWidgetMessage'; export * from './RoomWidgetRoomObjectMessage'; export * from './RoomWidgetUseProductMessage'; export * from './RoomWidgetUserActionMessage'; +export * from './RoomWidgetZoomToggleMessage'; diff --git a/src/views/room/widgets/RoomWidgets.scss b/src/views/room/widgets/RoomWidgets.scss index 744fa399..10c9d521 100644 --- a/src/views/room/widgets/RoomWidgets.scss +++ b/src/views/room/widgets/RoomWidgets.scss @@ -6,3 +6,5 @@ @import './furniture/FurnitureWidgets'; @import './infostand/InfoStandWidgetView'; @import './object-location/ObjectLocationView'; +@import './room-tools/RoomToolsWidgetView'; +@import './room-thumbnail/RoomThumbnailView'; diff --git a/src/views/room/widgets/RoomWidgetsView.tsx b/src/views/room/widgets/RoomWidgetsView.tsx index f5bf25fb..502489a5 100644 --- a/src/views/room/widgets/RoomWidgetsView.tsx +++ b/src/views/room/widgets/RoomWidgetsView.tsx @@ -11,8 +11,9 @@ import { ChatInputView } from './chat-input/ChatInputView'; import { ChatWidgetView } from './chat/ChatWidgetView'; import { FurnitureWidgetsView } from './furniture/FurnitureWidgetsView'; import { InfoStandWidgetView } from './infostand/InfoStandWidgetView'; +import { RoomThumbnailWidgetView } from './room-thumbnail/RoomThumbnailWidgetView'; +import { RoomToolsWidgetView } from './room-tools/RoomToolsWidgetView'; import { RoomWidgetViewProps } from './RoomWidgets.types'; - export const RoomWidgetsView: FC = props => { const { roomSession = null, eventDispatcher = null, widgetHandler = null } = useRoomContext(); @@ -243,6 +244,8 @@ export const RoomWidgetsView: FC = props => + + ); } diff --git a/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx b/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx index 2c6a6679..f84abbfd 100644 --- a/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx +++ b/src/views/room/widgets/furniture/friend-furni/FurnitureFriendFurniView.tsx @@ -103,7 +103,7 @@ export const FurnitureFriendFurniView: FC<{}> = props => return ( <> - { engravingStage > 0 && + { engravingStage > 0 && processAction('close_request') } />
diff --git a/src/views/room/widgets/room-thumbnail/RoomThumbnailView.scss b/src/views/room/widgets/room-thumbnail/RoomThumbnailView.scss new file mode 100644 index 00000000..0e39af10 --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/RoomThumbnailView.scss @@ -0,0 +1,14 @@ +.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 new file mode 100644 index 00000000..d7d736d2 --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/RoomThumbnailWidgetView.tsx @@ -0,0 +1,69 @@ +import { FC, useCallback, useState } from 'react'; +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'; + +export const RoomThumbnailWidgetView: FC<{}> = props => +{ + const [ isSelectorVisible, setIsSelectorVisible ] = useState(false); + const [ isBuilderVisible, setIsBuilderVisible ] = useState(false); + const [ isCameraVisible, setIsCameraVisible ] = useState(false); + + const onNitroEvent = useCallback((event: RoomWidgetThumbnailEvent) => + { + switch(event.type) + { + case RoomWidgetThumbnailEvent.SHOW_THUMBNAIL: + setIsSelectorVisible(true); + return; + case RoomWidgetThumbnailEvent.HIDE_THUMBNAIL: + setIsSelectorVisible(false); + setIsBuilderVisible(false); + setIsCameraVisible(false); + return; + case RoomWidgetThumbnailEvent.TOGGLE_THUMBNAIL: + setIsSelectorVisible(value => !value); + setIsBuilderVisible(false); + setIsCameraVisible(false); + return; + } + }, []); + + useUiEvent(RoomWidgetThumbnailEvent.SHOW_THUMBNAIL, onNitroEvent); + useUiEvent(RoomWidgetThumbnailEvent.HIDE_THUMBNAIL, onNitroEvent); + useUiEvent(RoomWidgetThumbnailEvent.TOGGLE_THUMBNAIL, onNitroEvent); + + const handleAction = useCallback((action: string) => + { + switch(action) + { + case 'camera': + setIsSelectorVisible(false); + setIsCameraVisible(true); + return; + case 'builder': + setIsSelectorVisible(false); + setIsBuilderVisible(true); + return; + } + }, [ setIsSelectorVisible, setIsCameraVisible, setIsBuilderVisible ]); + + return (<> + { isSelectorVisible && + setIsSelectorVisible(false) } /> + +
handleAction('camera') }> + +
+
handleAction('builder') }> + +
+
+
} + { isBuilderVisible && setIsBuilderVisible(false) } /> } + { isCameraVisible && setIsCameraVisible(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 new file mode 100644 index 00000000..77c8b94b --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.props.ts @@ -0,0 +1,4 @@ +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 new file mode 100644 index 00000000..d011f674 --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.scss @@ -0,0 +1,3 @@ +.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 new file mode 100644 index 00000000..f5061555 --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/views/builder/RoomThumbnailWidgetBuilderView.tsx @@ -0,0 +1,63 @@ +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 new file mode 100644 index 00000000..b472dcda --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.scss @@ -0,0 +1,13 @@ +.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 new file mode 100644 index 00000000..ffe7439d --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.tsx @@ -0,0 +1,39 @@ +import { NitroRectangle } from '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().createRoomScreenshot(GetRoomSession().roomId, 1, rectangle); + + //SendMessageHook(new RoomWidgetCameraRoomThumbnailComposer(0, [])); + 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 new file mode 100644 index 00000000..ec49ac02 --- /dev/null +++ b/src/views/room/widgets/room-thumbnail/views/camera/RoomThumbnailWidgetCameraView.types.ts @@ -0,0 +1,4 @@ +export class RoomThumbnailWidgetCameraViewProps +{ + onCloseClick: () => void; +} diff --git a/src/views/room/widgets/room-tools/RoomToolsWidgetView.scss b/src/views/room/widgets/room-tools/RoomToolsWidgetView.scss new file mode 100644 index 00000000..33313ed6 --- /dev/null +++ b/src/views/room/widgets/room-tools/RoomToolsWidgetView.scss @@ -0,0 +1,39 @@ +.nitro-room-tools { + position: fixed; + bottom: 125px; + left: -133px; + background: rgba($dark,.95); + box-shadow: inset 0px 5px lighten(rgba($dark,.6),2.5), inset 0 -4px darken(rgba($dark,.6),4); + border-top-right-radius: $border-radius; + border-bottom-right-radius: $border-radius; + transition: all .2s ease; + + &.open { + left: 0px; + } + + .list-group-item { + background: transparent; + padding: 3px 0px; + color: $white; + border-color: rgba($black, 0.3); + cursor: pointer; + + &:hover { + text-decoration: underline; + } + + &:first-child { + padding-top: 8px; + } + + &:last-child { + border-bottom: none; + padding-bottom: 8px; + } + + &.disabled { + opacity: .5; + } + } +} diff --git a/src/views/room/widgets/room-tools/RoomToolsWidgetView.tsx b/src/views/room/widgets/room-tools/RoomToolsWidgetView.tsx new file mode 100644 index 00000000..f76dbd04 --- /dev/null +++ b/src/views/room/widgets/room-tools/RoomToolsWidgetView.tsx @@ -0,0 +1,68 @@ +import classNames from 'classnames'; +import { RoomLikeRoomComposer } from 'nitro-renderer'; +import { FC, useCallback, useState } from 'react'; +import { NavigatorEvent } from '../../../../events'; +import { dispatchUiEvent } from '../../../../hooks/events'; +import { SendMessageHook } from '../../../../hooks/messages'; +import { LocalizeText } from '../../../../utils/LocalizeText'; +import { useRoomContext } from '../../context/RoomContext'; +import { RoomWidgetZoomToggleMessage } from '../../messages'; + +export const RoomToolsWidgetView: FC<{}> = props => +{ + const { widgetHandler = null } = useRoomContext(); + + const [ isExpended, setIsExpanded ] = useState(false); + const [ isZoomedIn, setIsZoomedIn ] = useState(false); + const [ isLiked, setIsLiked ] = useState(false); + + const handleToolClick = useCallback((action: string) => + { + switch(action) + { + case 'settings': + dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_INFO)); + return; + case 'zoom': + widgetHandler.processWidgetMessage(new RoomWidgetZoomToggleMessage(!isZoomedIn)); + setIsZoomedIn(value => !value); + return; + case 'chat_history': + return; + case 'like_room': + if(isLiked) return; + + SendMessageHook(new RoomLikeRoomComposer(1)); + setIsLiked(true); + return; + case 'toggle_room_link': + dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_LINK)); + return; + } + }, [ isZoomedIn, isLiked, widgetHandler ]); + + return ( +
+
+
handleToolClick('settings') }> + { LocalizeText('room.settings.button.text') } +
+
handleToolClick('zoom') }> + { LocalizeText('room.zoom.button.text') } +
+
handleToolClick('chat_history') }> + { LocalizeText('room.chathistory.button.text') } +
+
handleToolClick('like_room') }> + { LocalizeText('room.like.button.text') } +
+
handleToolClick('toggle_room_link') }> + { LocalizeText('navigator.embed.caption') } +
+
+
setIsExpanded(value => !value)}> + +
+
+ ); +};