Start navigator changes

This commit is contained in:
Bill 2022-01-07 16:41:59 -05:00
parent 35efd87188
commit 37c53ff062
46 changed files with 373 additions and 348 deletions

View File

@ -59,6 +59,11 @@ $nitropedia-height: 400px;
$messenger-width: 500px; $messenger-width: 500px;
$messenger-height: 370px; $messenger-height: 370px;
$marketplace-post-offer-width: 430px;
$marketplace-post-offer-height: 250px;
$room-info-width: 325px;
.nitro-app { .nitro-app {
width: 100%; width: 100%;
height: 100%; height: 100%;

View File

@ -2,4 +2,5 @@
@import './avatar-editor/AvatarEditorView'; @import './avatar-editor/AvatarEditorView';
@import './catalog/CatalogView'; @import './catalog/CatalogView';
@import './inventory/InventoryView'; @import './inventory/InventoryView';
@import './navigator/NavigatorView';
@import './toolbar/ToolbarView'; @import './toolbar/ToolbarView';

View File

@ -4,7 +4,7 @@ import classNames from 'classnames';
import { FC, useCallback, useEffect, useState } from 'react'; import { FC, useCallback, useEffect, useState } from 'react';
import { GetConfiguration, GetSessionDataManager, LocalizeText } from '../../../../api'; import { GetConfiguration, GetSessionDataManager, LocalizeText } from '../../../../api';
import { SendMessageHook } from '../../../../hooks/messages'; import { SendMessageHook } from '../../../../hooks/messages';
import { CurrencyIcon } from '../../../shared/currency-icon/CurrencyIcon'; import { CurrencyIcon } from '../../../../views/shared/currency-icon/CurrencyIcon';
import { useNavigatorContext } from '../../context/NavigatorContext'; import { useNavigatorContext } from '../../context/NavigatorContext';
import { NavigatorRoomCreatorViewProps, NAVIGATOR_ROOM_MODELS } from './NavigatorRoomCreatorView.types'; import { NavigatorRoomCreatorViewProps, NAVIGATOR_ROOM_MODELS } from './NavigatorRoomCreatorView.types';

View File

@ -1,5 +1,7 @@
.nitro-room-info { .nitro-room-info {
width: 250px; width: $room-info-width;
max-height: 300px;
.gray { .gray {
filter: grayscale(1); filter: grayscale(1);
@ -10,12 +12,8 @@
position: relative; position: relative;
width: 110px; width: 110px;
height: 110px; height: 110px;
margin: 0 auto; background: url("../../../../assets/images/navigator/thumbnail_placeholder.png") no-repeat center;
background-image: url(../../../../assets/images/navigator/thumbnail_placeholder.png);
background-repeat: no-repeat;
background-position: center;
background-color: rgba($black, .125); background-color: rgba($black, .125);
border-color: $black !important;
} }
.group-badge { .group-badge {

View File

@ -0,0 +1,203 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { RoomMuteComposer, RoomSettingsComposer, RoomStaffPickComposer, SecurityLevel, UserHomeRoomComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames';
import { FC, useCallback, useEffect, useState } from 'react';
import { GetConfiguration, GetGroupInformation, GetSessionDataManager, LocalizeText } from '../../../../api';
import { Button } from '../../../../common/Button';
import { Column } from '../../../../common/Column';
import { Flex } from '../../../../common/Flex';
import { Grid } from '../../../../common/Grid';
import { Text } from '../../../../common/Text';
import { NavigatorEvent } from '../../../../events';
import { FloorplanEditorEvent } from '../../../../events/floorplan-editor/FloorplanEditorEvent';
import { RoomWidgetThumbnailEvent } from '../../../../events/room-widgets/thumbnail';
import { BatchUpdates } from '../../../../hooks';
import { dispatchUiEvent } from '../../../../hooks/events';
import { SendMessageHook } from '../../../../hooks/messages';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, UserProfileIconView } from '../../../../layout';
import { RoomThumbnailView } from '../../../../layout/room-thumbnail/RoomThumbnailView';
import { BadgeImageView } from '../../../../views/shared/badge-image/BadgeImageView';
import { useNavigatorContext } from '../../context/NavigatorContext';
import { NavigatorActions } from '../../reducers/NavigatorReducer';
export class NavigatorRoomInfoViewProps
{
onCloseClick: () => void;
}
export const NavigatorRoomInfoView: FC<NavigatorRoomInfoViewProps> = props =>
{
const { onCloseClick = null } = props;
const [ roomThumbnail, setRoomThumbnail ] = useState(null);
const [ isRoomPicked, setIsRoomPicked ] = useState(false);
const [ isRoomMuted, setIsRoomMuted ] = useState(false);
const { navigatorState = null, dispatchNavigatorState = null } = useNavigatorContext();
const { roomInfoData = null, homeRoomId = null } = navigatorState;
const hasPermission = (permission: string) =>
{
switch(permission)
{
case 'settings':
return GetSessionDataManager().securityLevel >= SecurityLevel.MODERATOR || roomInfoData.currentRoomOwner;
case 'staff_pick':
return GetSessionDataManager().securityLevel >= SecurityLevel.COMMUNITY;
default: return false;
}
}
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':
GetGroupInformation(roomInfoData.enteredGuestRoom.habboGroupId);
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 'open_floorplan_editor':
dispatchUiEvent(new FloorplanEditorEvent(FloorplanEditorEvent.TOGGLE_FLOORPLAN_EDITOR));
return;
case 'close':
onCloseClick();
return;
}
}, [ onCloseClick, dispatchNavigatorState, roomInfoData, homeRoomId ]);
useEffect(() =>
{
if(!roomInfoData || !roomInfoData.enteredGuestRoom) return;
let thumbnailUrl: string = null;
if(roomInfoData.enteredGuestRoom.officialRoomPicRef)
{
thumbnailUrl = (GetConfiguration<string>('image.library.url') + roomInfoData.enteredGuestRoom.officialRoomPicRef);
}
else
{
thumbnailUrl = (GetConfiguration<string>('thumbnails.url').replace('%thumbnail%', roomInfoData.enteredGuestRoom.roomId.toString()));
}
BatchUpdates(() =>
{
setRoomThumbnail(thumbnailUrl);
setIsRoomPicked(roomInfoData.enteredGuestRoom.roomPicker);
setIsRoomMuted(roomInfoData.enteredGuestRoom.allInRoomMuted);
});
}, [ roomInfoData ]);
if(!roomInfoData) return null;
return (
<NitroCardView className="nitro-room-info" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('navigator.roomsettings.roominfo') } onCloseClick={ () => processAction('close') } />
<NitroCardContentView className="text-black">
{ roomInfoData.enteredGuestRoom &&
<>
<Flex gap={ 2 } overflow="hidden">
<Flex center>
<RoomThumbnailView customUrl={ roomInfoData.enteredGuestRoom.officialRoomPicRef }>
{ hasPermission('settings') && <i className="icon icon-camera-small position-absolute b-0 r-0 m-1 cursor-pointer" onClick={ () => processAction('open_room_thumbnail_camera') } /> }
</RoomThumbnailView>
</Flex>
<Column grow gap={ 1 } overflow="hidden">
<Flex gap={ 1 }>
<Column grow gap={ 1 }>
<Text bold>{ roomInfoData.enteredGuestRoom.roomName }</Text>
{ roomInfoData.enteredGuestRoom.showOwner &&
<Flex alignItems="center" gap={ 1 }>
<Text variant="muted">{ LocalizeText('navigator.roomownercaption') }</Text>
<Flex alignItems="center">
<UserProfileIconView userId={ roomInfoData.enteredGuestRoom.ownerId } />
<Text>{ roomInfoData.enteredGuestRoom.ownerName }</Text>
</Flex>
</Flex> }
<Flex alignItems="center" gap={ 1 }>
<Text variant="muted">{ LocalizeText('navigator.roomrating') }</Text>
<Text>{ roomInfoData.enteredGuestRoom.score }</Text>
</Flex>
{ (roomInfoData.enteredGuestRoom.tags.length > 0) &&
<Flex alignItems="center" gap={ 1 }>
{ roomInfoData.enteredGuestRoom.tags.map(tag =>
{
return <Text key={ tag } pointer className="bg-muted rounded p-1" onClick={ event => processAction('navigator_search_tag', tag) }>#{ tag }</Text>
}) }
</Flex> }
</Column>
<Grid maxContent columnCount={ 2 } gap={ 1 }>
<i onClick={ () => processAction('set_home_room') } className={ 'flex-shrink-0 icon icon-house-small cursor-pointer' + classNames({ ' gray': homeRoomId !== roomInfoData.enteredGuestRoom.roomId }) } />
<FontAwesomeIcon icon="link" title={ LocalizeText('navigator.embed.caption') } className="cursor-pointer" onClick={ event => dispatchUiEvent(new NavigatorEvent(NavigatorEvent.TOGGLE_ROOM_LINK)) } />
{ hasPermission('settings') &&
<>
<FontAwesomeIcon icon="cogs" title={ LocalizeText('navigator.room.popup.info.room.settings') } className="cursor-pointer" onClick={ event => processAction('open_room_settings') } />
<FontAwesomeIcon icon="tools" title={ LocalizeText('open.floor.plan.editor') } className="cursor-pointer" onClick={ event => processAction('open_floorplan_editor') } />
</> }
</Grid>
</Flex>
<Text overflow="auto">{ roomInfoData.enteredGuestRoom.description }</Text>
{ (roomInfoData.enteredGuestRoom.habboGroupId > 0) &&
<Flex pointer alignItems="center" gap={ 1 } onClick={ () => processAction('open_group_info') }>
<BadgeImageView className="flex-none" badgeCode={ roomInfoData.enteredGuestRoom.groupBadgeCode } isGroup={ true } />
<Text underline>
{ LocalizeText('navigator.guildbase', ['groupName'], [roomInfoData.enteredGuestRoom.groupName]) }
</Text>
</Flex> }
</Column>
</Flex>
<Column gap={ 1 }>
{ hasPermission('staff_pick') &&
<Button size="sm" onClick={ () => processAction('toggle_pick') }>
{ LocalizeText(isRoomPicked ? 'navigator.staffpicks.unpick' : 'navigator.staffpicks.pick') }
</Button> }
<Button size="sm" variant="danger" disabled>
{ LocalizeText('help.emergency.main.report.room') }
</Button>
{ hasPermission('settings') &&
<Button size="sm" onClick={ () => processAction('toggle_mute') }>
{ LocalizeText(isRoomMuted ? 'navigator.muteall_on' : 'navigator.muteall_off') }
</Button> }
</Column>
</> }
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -0,0 +1,3 @@
.nitro-room-link {
width: 400px;
}

View File

@ -1,18 +1,24 @@
import { FC, useCallback, useEffect, useRef, useState } from 'react'; import { FC, useCallback, useEffect, useRef, useState } from 'react';
import { GetConfiguration, GetNitroInstance, LocalizeText } from '../../../../api'; import { GetConfiguration, GetNitroInstance, LocalizeText } from '../../../../api';
import { Column } from '../../../../common/Column';
import { Flex } from '../../../../common/Flex';
import { Text } from '../../../../common/Text';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout'; import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
import { RoomThumbnailView } from '../../../../layout/room-thumbnail/RoomThumbnailView';
import { useNavigatorContext } from '../../context/NavigatorContext'; import { useNavigatorContext } from '../../context/NavigatorContext';
import { NavigatorRoomLinkViewProps } from './NavigatorRoomLinkView.types';
export class NavigatorRoomLinkViewProps
{
onCloseClick: () => void;
}
export const NavigatorRoomLinkView: FC<NavigatorRoomLinkViewProps> = props => export const NavigatorRoomLinkView: FC<NavigatorRoomLinkViewProps> = props =>
{ {
const { onCloseClick = null } = props; const { onCloseClick = null } = props;
const { navigatorState = null } = useNavigatorContext();
const { roomInfoData = null } = navigatorState;
const [ roomThumbnail, setRoomThumbnail ] = useState(null); const [ roomThumbnail, setRoomThumbnail ] = useState(null);
const [ roomLink, setRoomLink ] = useState(null); const [ roomLink, setRoomLink ] = useState(null);
const { navigatorState = null } = useNavigatorContext();
const { roomInfoData = null } = navigatorState;
const elementRef = useRef<HTMLInputElement>(); const elementRef = useRef<HTMLInputElement>();
useEffect(() => useEffect(() =>
@ -50,18 +56,16 @@ export const NavigatorRoomLinkView: FC<NavigatorRoomLinkViewProps> = props =>
return ( return (
<NitroCardView className="nitro-room-link" simple={ true }> <NitroCardView className="nitro-room-link" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('navigator.embed.title') } onCloseClick={ () => processAction('close') } /> <NitroCardHeaderView headerText={ LocalizeText('navigator.embed.title') } onCloseClick={ onCloseClick } />
<NitroCardContentView className="text-black d-flex align-items-center"> <NitroCardContentView className="text-black d-flex align-items-center">
<div className="me-3"> <Flex gap={ 2 }>
<div className="room-thumbnail border"> <RoomThumbnailView customUrl={ roomInfoData.enteredGuestRoom.officialRoomPicRef } />
{ roomThumbnail && <img alt="" src={ roomThumbnail } /> } <Column>
</div> <Text bold fontSize={ 5 }>{ LocalizeText('navigator.embed.headline') }</Text>
</div> <Text>{ LocalizeText('navigator.embed.info') }</Text>
<div>
<div className="h5 fw-bold m-0">{ LocalizeText('navigator.embed.headline') }</div>
<div>{ LocalizeText('navigator.embed.info') }</div>
{ roomLink && <input ref={ elementRef } type="text" readOnly className="form-control form-control-sm" value={ roomLink } /> } { roomLink && <input ref={ elementRef } type="text" readOnly className="form-control form-control-sm" value={ roomLink } /> }
</div> </Column>
</Flex>
</NitroCardContentView> </NitroCardContentView>
</NitroCardView> </NitroCardView>
); );

View File

@ -1,10 +1,6 @@
.nitro-room-settings { .nitro-room-settings {
width: 400px; width: 400px;
.content-area {
height: 350px;
}
.user-rights-container { .user-rights-container {
.row { .row {

View File

@ -135,15 +135,15 @@ export const NavigatorRoomSettingsView: FC<{}> = props =>
if(!roomSettingsData) return null; if(!roomSettingsData) return null;
return ( return (
<NitroCardView className="nitro-room-settings"> <NitroCardView uniqueKey="nitro-room-settings" className="nitro-room-settings">
<NitroCardHeaderView headerText={ LocalizeText('navigator.roomsettings') } onCloseClick={ () => processAction('close') } /> <NitroCardHeaderView headerText={ LocalizeText('navigator.roomsettings') } onCloseClick={ () => processAction('close') } />
<NitroCardTabsView> <NitroCardTabsView>
{ TABS.map(tab => { TABS.map(tab =>
{ {
return <NitroCardTabsItemView key={ tab } isActive={ currentTab === tab } onClick={ event => setCurrentTab(tab) }>{ LocalizeText(tab) }</NitroCardTabsItemView> return <NitroCardTabsItemView key={ tab } isActive={ (currentTab === tab) } onClick={ event => setCurrentTab(tab) }>{ LocalizeText(tab) }</NitroCardTabsItemView>
}) } }) }
</NitroCardTabsView> </NitroCardTabsView>
<NitroCardContentView className="text-black px-4"> <NitroCardContentView className="text-black">
{ currentTab === TABS[0] && <NavigatorRoomSettingsBasicTabView roomSettingsData={ roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } /> } { currentTab === TABS[0] && <NavigatorRoomSettingsBasicTabView roomSettingsData={ roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } /> }
{ currentTab === TABS[1] && <NavigatorRoomSettingsAccessTabView roomSettingsData={ roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } /> } { currentTab === TABS[1] && <NavigatorRoomSettingsAccessTabView roomSettingsData={ roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } /> }
{ currentTab === TABS[2] && <NavigatorRoomSettingsRightsTabView roomSettingsData= {roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } friends={friends} /> } { currentTab === TABS[2] && <NavigatorRoomSettingsRightsTabView roomSettingsData= {roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } friends={friends} /> }

View File

@ -0,0 +1,102 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../../api';
import { Column } from '../../../../../../common/Column';
import { Flex } from '../../../../../../common/Flex';
import { Text } from '../../../../../../common/Text';
import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types';
export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabViewProps> = props =>
{
const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props;
const handleChange = useCallback((field: string, value: string | number | boolean) =>
{
const roomSettings = Object.assign({}, 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(roomSettings);
}, [ roomSettingsData, setRoomSettingsData, onSave ]);
const isPasswordValid = useCallback(() =>
{
return (roomSettingsData.password && (roomSettingsData.password.length > 0) && (roomSettingsData.password === roomSettingsData.confirmPassword));
}, [ roomSettingsData ]);
const trySave = useCallback(() =>
{
if(isPasswordValid()) onSave(roomSettingsData);
}, [isPasswordValid, onSave, roomSettingsData]);
return (
<>
<Text bold>{LocalizeText('navigator.roomsettings.roomaccess.caption')}</Text>
<Text>{ LocalizeText('navigator.roomsettings.roomaccess.info') }</Text>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('navigator.roomsettings.doormode') }</Text>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 0 } onChange={ (e) => handleChange('lock_state', 0) } />
<Text>{ LocalizeText('navigator.roomsettings.doormode.open') }</Text>
</Flex>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 1 } onChange={ (e) => handleChange('lock_state', 1) } />
<Text>{ LocalizeText('navigator.roomsettings.doormode.doorbell') }</Text>
</Flex>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 2 } onChange={ (e) => handleChange('lock_state', 2) } />
<Text>{ LocalizeText('navigator.roomsettings.doormode.invisible') }</Text>
</Flex>
<Flex fullWidth gap={ 1 }>
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 3 } onChange={ (e) => handleChange('lock_state', 3) } />
{ (roomSettingsData.lockState !== 3) && <Text>{ LocalizeText('navigator.roomsettings.doormode.password') }</Text> }
{ roomSettingsData.lockState === 3 &&
<Column gap={ 1 }>
<Text>{ LocalizeText('navigator.roomsettings.doormode.password') }</Text>
<input type="password" className="form-control form-control-sm col-4" value={ roomSettingsData.password ?? '' } onChange={ (e) => handleChange('password', e.target.value) } onBlur={ trySave } placeholder={ LocalizeText('navigator.roomsettings.password') } />
<input type="password" className="form-control form-control-sm col-4" value={ roomSettingsData.confirmPassword ?? '' } onChange={ (e) => handleChange('confirm_password', e.target.value) } onBlur={ trySave } placeholder={ LocalizeText('navigator.roomsettings.passwordconfirm') } />
{ !isPasswordValid() &&
<small className="text-danger fw-bold">
{ LocalizeText('navigator.roomsettings.invalidconfirm') }
</small> }
</Column> }
</Flex>
</Column>
<Column gap={ 1 }>
<Text bold>{ LocalizeText('navigator.roomsettings.pets') }</Text>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowPets } onChange={ (e) => handleChange('allow_pets', e.target.checked) } />
<Text>{ LocalizeText('navigator.roomsettings.allowpets') }</Text>
</Flex>
<Flex alignItems="center" gap={ 1 }>
<input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowPetsEat } onChange={ (e) => handleChange('allow_pets_eat', e.target.checked) } />
<Text>{ LocalizeText('navigator.roomsettings.allowfoodconsume') }</Text>
</Flex>
</Column>
</>
);
};

View File

@ -1,9 +1,15 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { FC, useCallback, useState } from 'react'; import { FC, useCallback, useState } from 'react';
import { LocalizeText } from '../../../../../../api'; import { LocalizeText } from '../../../../../../api';
import { Base } from '../../../../../../common/Base';
import { Flex } from '../../../../../../common/Flex';
import { Text } from '../../../../../../common/Text';
import { GetMaxVisitorsList } from '../../../../common/RoomSettingsUtils'; import { GetMaxVisitorsList } from '../../../../common/RoomSettingsUtils';
import { useNavigatorContext } from '../../../../context/NavigatorContext'; import { useNavigatorContext } from '../../../../context/NavigatorContext';
import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types'; import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types';
const DESC_MAX_LENGTH = 255;
export const NavigatorRoomSettingsBasicTabView: FC<NavigatorRoomSettingsTabViewProps> = props => export const NavigatorRoomSettingsBasicTabView: FC<NavigatorRoomSettingsTabViewProps> = props =>
{ {
const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props; const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props;
@ -48,49 +54,49 @@ export const NavigatorRoomSettingsBasicTabView: FC<NavigatorRoomSettingsTabViewP
return ( return (
<> <>
<div className="form-group mb-1"> <Flex alignItems="center" gap={ 1 }>
<label>{ LocalizeText('navigator.roomname') }</label> <Text className="col-3">{ LocalizeText('navigator.roomname') }</Text>
<input className="form-control form-control-sm" value={ roomSettingsData.roomName } onChange={ event => handleChange('name', event.target.value) } onBlur={ () => onSave(roomSettingsData) } /> <input className="form-control form-control-sm" value={ roomSettingsData.roomName } onChange={ event => handleChange('name', event.target.value) } onBlur={ () => onSave(roomSettingsData) } />
</div> </Flex>
<div className="form-group mb-1"> <Flex alignItems="center" gap={ 1 }>
<label>{ LocalizeText('navigator.roomsettings.desc') }</label> <Text className="col-3">{ LocalizeText('navigator.roomsettings.desc') }</Text>
<input className="form-control form-control-sm" value={ roomSettingsData.roomDescription } onChange={ event => handleChange('description', event.target.value) } onBlur={ () => onSave(roomSettingsData) } /> <textarea className="form-control form-control-sm" value={ roomSettingsData.roomDescription } onChange={ event => handleChange('description', event.target.value) } onBlur={ () => onSave(roomSettingsData) } maxLength={ DESC_MAX_LENGTH } />
</div> </Flex>
<div className="form-group mb-1"> <Flex alignItems="center" gap={ 1 }>
<label>{ LocalizeText('navigator.category') }</label> <Text className="col-3">{ LocalizeText('navigator.category') }</Text>
<select className="form-select form-select-sm" value={ roomSettingsData.categoryId } onChange={ event => handleChange('category', event.target.value) }> <select className="form-select form-select-sm" value={ roomSettingsData.categoryId } onChange={ event => handleChange('category', event.target.value) }>
{ categories && categories.map(category => { categories && categories.map(category =>
{ {
return <option key={ category.id } value={ category.id }>{ LocalizeText(category.name) }</option> return <option key={ category.id } value={ category.id }>{ LocalizeText(category.name) }</option>
}) } }) }
</select> </select>
</div> </Flex>
<div className="form-group mb-1"> <Flex alignItems="center" gap={ 1 }>
<label>{ LocalizeText('navigator.maxvisitors') }</label> <Text className="col-3">{ LocalizeText('navigator.maxvisitors') }</Text>
<select className="form-select form-select-sm" value={ roomSettingsData.userCount } onChange={ event => handleChange('max_visitors', event.target.value) }> <select className="form-select form-select-sm" value={ roomSettingsData.userCount } onChange={ event => handleChange('max_visitors', event.target.value) }>
{ maxVisitorsList && maxVisitorsList.map(value => { maxVisitorsList && maxVisitorsList.map(value =>
{ {
return <option key={ value } value={ value }>{ value }</option> return <option key={ value } value={ value }>{ value }</option>
}) } }) }
</select> </select>
</div> </Flex>
<div className="form-group mb-1"> <Flex alignItems="center" gap={ 1 }>
<label>{ LocalizeText('navigator.tradesettings') }</label> <Text className="col-3">{ LocalizeText('navigator.tradesettings') }</Text>
<select className="form-select form-select-sm" value={ roomSettingsData.tradeState } onChange={ event => handleChange('trade_state', event.target.value) }> <select className="form-select form-select-sm" value={ roomSettingsData.tradeState } onChange={ event => handleChange('trade_state', event.target.value) }>
<option value="0">{ LocalizeText('navigator.roomsettings.trade_not_allowed') }</option> <option value="0">{ LocalizeText('navigator.roomsettings.trade_not_allowed') }</option>
<option value="1">{ LocalizeText('navigator.roomsettings.trade_not_with_Controller') }</option> <option value="1">{ LocalizeText('navigator.roomsettings.trade_not_with_Controller') }</option>
<option value="2">{ LocalizeText('navigator.roomsettings.trade_allowed') }</option> <option value="2">{ LocalizeText('navigator.roomsettings.trade_allowed') }</option>
</select> </select>
</div> </Flex>
<div className="form-check"> <Flex alignItems="center" gap={ 1 }>
<Base className="col-3" />
<input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowWalkthrough } onChange={ event => handleChange('allow_walkthrough', event.target.checked) } /> <input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowWalkthrough } onChange={ event => handleChange('allow_walkthrough', event.target.checked) } />
<label className="form-check-label">{ LocalizeText('navigator.roomsettings.allow_walk_through') }</label> <Text>{ LocalizeText('navigator.roomsettings.allow_walk_through') }</Text>
</div> </Flex>
<div className="form-group d-flex justify-content-center mt-1"> <Text variant="danger" underline bold pointer className="d-flex justify-content-center align-items-center gap-1 mt-2">
<button type="button" className="btn btn-link btn-sm text-danger fw-bold"> <FontAwesomeIcon icon="times" />
<i className="fas fa-times me-1" /> { LocalizeText('navigator.roomsettings.delete') }
{ LocalizeText('navigator.roomsettings.delete') }</button> </Text>
</div>
</> </>
); );
}; };

View File

@ -5,7 +5,6 @@
@import "./hotel-view/HotelView"; @import "./hotel-view/HotelView";
@import "./loading/LoadingView"; @import "./loading/LoadingView";
@import "./main/MainView"; @import "./main/MainView";
@import "./navigator/NavigatorView";
@import "./notification-center/NotificationCenterView"; @import "./notification-center/NotificationCenterView";
@import "./purse/PurseView"; @import "./purse/PurseView";
@import "./right-side/RightSideView"; @import "./right-side/RightSideView";

View File

@ -5,6 +5,7 @@ import { AchievementsView } from '../../components/achievements/AchievementsView
import { AvatarEditorView } from '../../components/avatar-editor/AvatarEditorView'; import { AvatarEditorView } from '../../components/avatar-editor/AvatarEditorView';
import { CatalogView } from '../../components/catalog/CatalogView'; import { CatalogView } from '../../components/catalog/CatalogView';
import { InventoryView } from '../../components/inventory/InventoryView'; import { InventoryView } from '../../components/inventory/InventoryView';
import { NavigatorView } from '../../components/navigator/NavigatorView';
import { ToolbarView } from '../../components/toolbar/ToolbarView'; import { ToolbarView } from '../../components/toolbar/ToolbarView';
import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event'; import { useRoomSessionManagerEvent } from '../../hooks/events/nitro/session/room-session-manager-event';
import { TransitionAnimation, TransitionAnimationTypes } from '../../layout'; import { TransitionAnimation, TransitionAnimationTypes } from '../../layout';
@ -18,7 +19,6 @@ import { HcCenterView } from '../hc-center/HcCenterView';
import { HelpView } from '../help/HelpView'; import { HelpView } from '../help/HelpView';
import { HotelView } from '../hotel-view/HotelView'; import { HotelView } from '../hotel-view/HotelView';
import { ModToolsView } from '../mod-tools/ModToolsView'; import { ModToolsView } from '../mod-tools/ModToolsView';
import { NavigatorView } from '../navigator/NavigatorView';
import { NitropediaView } from '../nitropedia/NitropediaView'; import { NitropediaView } from '../nitropedia/NitropediaView';
import { RightSideView } from '../right-side/RightSideView'; import { RightSideView } from '../right-side/RightSideView';
import { RoomHostView } from '../room-host/RoomHostView'; import { RoomHostView } from '../room-host/RoomHostView';

View File

@ -1,171 +0,0 @@
import { RoomMuteComposer, RoomSettingsComposer, RoomStaffPickComposer, SecurityLevel, UserHomeRoomComposer } from '@nitrots/nitro-renderer';
import classNames from 'classnames';
import { FC, useCallback, useEffect, useState } from 'react';
import { GetConfiguration, GetGroupInformation, GetSessionDataManager, LocalizeText } from '../../../../api';
import { NavigatorEvent } from '../../../../events';
import { FloorplanEditorEvent } from '../../../../events/floorplan-editor/FloorplanEditorEvent';
import { RoomWidgetThumbnailEvent } from '../../../../events/room-widgets/thumbnail';
import { dispatchUiEvent } from '../../../../hooks/events';
import { SendMessageHook } from '../../../../hooks/messages';
import { NitroCardContentView, NitroCardHeaderView, NitroCardView, UserProfileIconView } from '../../../../layout';
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<NavigatorRoomInfoViewProps> = 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;
let thumbnailUrl: string = null;
if(roomInfoData.enteredGuestRoom.officialRoomPicRef)
{
thumbnailUrl = (GetConfiguration<string>('image.library.url') + roomInfoData.enteredGuestRoom.officialRoomPicRef);
}
else
{
thumbnailUrl = (GetConfiguration<string>('thumbnails.url').replace('%thumbnail%', roomInfoData.enteredGuestRoom.roomId.toString()));
}
setRoomThumbnail(thumbnailUrl);
setIsRoomPicked(roomInfoData.enteredGuestRoom.roomPicker);
setIsRoomMuted(roomInfoData.enteredGuestRoom.allInRoomMuted);
}, [ roomInfoData ]);
const hasPermission = useCallback((permission: string) =>
{
switch(permission)
{
case 'settings': return GetSessionDataManager().securityLevel >= SecurityLevel.MODERATOR || roomInfoData.currentRoomOwner;
case 'staff_pick': return GetSessionDataManager().securityLevel >= SecurityLevel.COMMUNITY;
default: return false;
}
}, [ 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':
GetGroupInformation(roomInfoData.enteredGuestRoom.habboGroupId);
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 'open_floorplan_editor':
dispatchUiEvent(new FloorplanEditorEvent(FloorplanEditorEvent.TOGGLE_FLOORPLAN_EDITOR));
return;
case 'close':
onCloseClick();
return;
}
}, [ onCloseClick, dispatchNavigatorState, roomInfoData, homeRoomId ]);
if(!roomInfoData) return null;
return (
<NitroCardView className="nitro-room-info" simple={ true }>
<NitroCardHeaderView headerText={ LocalizeText('navigator.roomsettings.roominfo') } onCloseClick={ () => processAction('close') } />
<NitroCardContentView className="text-black">
{ roomInfoData.enteredGuestRoom && <>
<div className="d-flex justify-content-between align-items-center">
<div className="fw-bold">
{ roomInfoData.enteredGuestRoom.roomName }
</div>
<i onClick={ () => processAction('set_home_room') } className={ 'flex-shrink-0 icon icon-house-small cursor-pointer' + classNames({ ' gray': homeRoomId !== roomInfoData.enteredGuestRoom.roomId }) } />
</div>
<div className="d-flex align-items-center">
{ roomInfoData.enteredGuestRoom.showOwner && <>
<div className="fw-bold text-muted me-1">{ LocalizeText('navigator.roomownercaption') }</div>
<div className="d-flex align-items-center cursor-pointer">
<UserProfileIconView userId={ roomInfoData.enteredGuestRoom.ownerId } />
<div>{ roomInfoData.enteredGuestRoom.ownerName }</div>
</div>
</> }
</div>
<div>
<span className="fw-bold text-muted me-1">{ LocalizeText('navigator.roomrating') }</span> { roomInfoData.enteredGuestRoom.score }
</div>
<div className="d-flex mb-1">
{ roomInfoData.enteredGuestRoom.tags.map(tag =>
{
return <div className="bg-muted p-1 rounded me-1 cursor-pointer" onClick={ () => processAction('navigator_search_tag', tag) }>#{ tag }</div>
}) }
</div>
<div>{ roomInfoData.enteredGuestRoom.description }</div>
<div className="room-thumbnail rounded overflow-hidden border mt-1 mb-2">
{ hasPermission('settings') && <i className="icon icon-camera-small position-absolute b-0 r-0 m-1 cursor-pointer" onClick={ () => processAction('open_room_thumbnail_camera') } /> }
{ roomThumbnail && <img alt="" src={ roomThumbnail } /> }
</div>
{ roomInfoData.enteredGuestRoom.habboGroupId > 0 && <div className="d-flex align-items-center mb-2 cursor-pointer" onClick={ () => processAction('open_group_info') }>
<div className="group-badge flex-shrink-0 me-1">
<BadgeImageView badgeCode={ roomInfoData.enteredGuestRoom.groupBadgeCode } isGroup={ true } />
</div>
<div className="text-decoration-underline small">
{ LocalizeText('navigator.guildbase', ['groupName'], [roomInfoData.enteredGuestRoom.groupName]) }
</div>
</div> }
<div className="cursor-pointer text-decoration-underline d-flex align-items-center mb-2" onClick={ () => processAction('toggle_room_link') }>
<i className="icon icon-arrows me-1" />
<span>{ LocalizeText('navigator.embed.caption') }</span>
</div>
{ hasPermission('settings') && <>
<button className="btn btn-sm btn-primary w-100 mb-1" onClick={ () => processAction('open_room_settings') }>{ LocalizeText('navigator.room.popup.info.room.settings') }</button>
<button className="btn btn-sm btn-primary w-100 mb-1" onClick={ () => processAction('open_floorplan_editor') }>{ LocalizeText('open.floor.plan.editor') }</button>
</> }
{ hasPermission('staff_pick') && <button className="btn btn-sm btn-primary w-100 mb-1" onClick={ () => processAction('toggle_pick') }>{ LocalizeText(isRoomPicked ? 'navigator.staffpicks.unpick' : 'navigator.staffpicks.pick') }</button> }
<button className="btn btn-sm btn-danger w-100 mb-1" disabled={ true }>{ LocalizeText('help.emergency.main.report.room') }</button>
{ hasPermission('settings') && <button className="btn btn-sm btn-primary w-100" onClick={ () => processAction('toggle_mute') }>{ LocalizeText(isRoomMuted ? 'navigator.muteall_on' : 'navigator.muteall_off') }</button> }
</> }
</NitroCardContentView>
</NitroCardView>
);
};

View File

@ -1,5 +0,0 @@
export class NavigatorRoomInfoViewProps
{
onCloseClick: () => void;
}

View File

@ -1,14 +0,0 @@
.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;
}
}

View File

@ -1,5 +0,0 @@
export class NavigatorRoomLinkViewProps
{
onCloseClick: () => void;
}

View File

@ -1,97 +0,0 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../../api';
import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types';
export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabViewProps> = props =>
{
const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props;
const handleChange = useCallback((field: string, value: string | number | boolean) =>
{
const roomSettings = Object.assign({}, 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(roomSettings);
}, [ roomSettingsData, setRoomSettingsData, onSave ]);
const isPasswordValid = useCallback(() =>
{
return (roomSettingsData.password && roomSettingsData.password.length > 0 && roomSettingsData.password === roomSettingsData.confirmPassword);
}, [ roomSettingsData ]);
const trySave = useCallback(() =>
{
if(isPasswordValid()) onSave(roomSettingsData);
}, [isPasswordValid, onSave, roomSettingsData]);
return (
<>
<div className="fw-bold">{LocalizeText('navigator.roomsettings.roomaccess.caption')}</div>
<div className="mb-3">{ LocalizeText('navigator.roomsettings.roomaccess.info') }</div>
<div className="fw-bold">{ LocalizeText('navigator.roomsettings.doormode') }</div>
<div className="form-check">
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 0 } onChange={ (e) => handleChange('lock_state', 0) } />
<label className="form-check-label">{ LocalizeText('navigator.roomsettings.doormode.open') }</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 1 } onChange={ (e) => handleChange('lock_state', 1) } />
<label className="form-check-label">{ LocalizeText('navigator.roomsettings.doormode.doorbell') }</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 2 } onChange={ (e) => handleChange('lock_state', 2) } />
<label className="form-check-label">{ LocalizeText('navigator.roomsettings.doormode.invisible') }</label>
</div>
<div className="form-check">
<input className="form-check-input" type="radio" name="lockState" checked={ roomSettingsData.lockState === 3 } onChange={ (e) => handleChange('lock_state', 3) } />
<label className="form-check-label">{ LocalizeText('navigator.roomsettings.doormode.password') }</label>
</div>
{ roomSettingsData.lockState === 3 && <>
<div className="form-group mt-2">
<label>{ LocalizeText('navigator.roomsettings.password') }</label>
<input type="password" className="form-control form-control-sm" value={ roomSettingsData.password ?? '' } onChange={ (e) => handleChange('password', e.target.value) } onBlur={ trySave } placeholder="*****" />
</div>
<div className="form-group">
<label>{ LocalizeText('navigator.roomsettings.passwordconfirm') }</label>
<input type="password" className="form-control form-control-sm" value={ roomSettingsData.confirmPassword ?? '' } onChange={ (e) => handleChange('confirm_password', e.target.value) } onBlur={ trySave } placeholder="*****" />
{ !isPasswordValid() && <small className="text-danger fw-bold">
{ LocalizeText('navigator.roomsettings.invalidconfirm') }
</small> }
</div>
</> }
<div className="fw-bold mt-2">{ LocalizeText('navigator.roomsettings.pets') }</div>
<div className="form-check">
<input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowPets } onChange={ (e) => handleChange('allow_pets', e.target.checked) } />
<label className="form-check-label">{ LocalizeText('navigator.roomsettings.allowpets') }</label>
</div>
<div className="form-check">
<input className="form-check-input" type="checkbox" checked={ roomSettingsData.allowPetsEat } onChange={ (e) => handleChange('allow_pets_eat', e.target.checked) } />
<label className="form-check-label">{ LocalizeText('navigator.roomsettings.allowfoodconsume') }</label>
</div>
</>
);
};