Merge branch 'feature/room-settings' into dev

This commit is contained in:
Bill 2021-08-26 02:48:06 -04:00
commit fe22d0b183
15 changed files with 572 additions and 65 deletions

View File

@ -0,0 +1,26 @@
import { MessengerFriend } from '../../views/friend-list/common/MessengerFriend';
import { FriendListEvent } from './FriendListEvent';
export class FriendListContentEvent extends FriendListEvent
{
public static FRIEND_LIST_CONTENT: string = 'FLSFRE_FRIEND_LIST_CONTENT';
private _friends: Map<number, string>;
constructor(friends: MessengerFriend[])
{
super(FriendListContentEvent.FRIEND_LIST_CONTENT);
this._friends = new Map();
friends.forEach(entry =>
{
this._friends.set(entry.id, entry.name);
});
}
public get friends(): Map<number, string>
{
return this._friends;
}
}

View File

@ -4,4 +4,5 @@ export class FriendListEvent extends NitroEvent
{
public static SHOW_FRIEND_LIST: string = 'IE_SHOW_FRIEND_LIST';
public static TOGGLE_FRIEND_LIST: string = 'IE_TOGGLE_FRIEND_LIST';
public static REQUEST_FRIEND_LIST: string = 'FLSFRE_REQUEST_FRIEND_LIST';
}

View File

@ -1,8 +1,9 @@
import { MessengerInitComposer, RoomEngineObjectEvent, RoomObjectCategory, RoomObjectUserType } from '@nitrots/nitro-renderer';
import React, { FC, useCallback, useEffect, useReducer, useState } from 'react';
import { FC, useCallback, useEffect, useReducer, useState } from 'react';
import { createPortal } from 'react-dom';
import { GetRoomSession, LocalizeText } from '../../api';
import { FriendEnteredRoomEvent, FriendListEvent } from '../../events';
import { FriendListContentEvent } from '../../events/friend-list/FriendListContentEvent';
import { FriendListSendFriendRequestEvent } from '../../events/friend-list/FriendListSendFriendRequestEvent';
import { useRoomEngineEvent } from '../../hooks/events';
import { dispatchUiEvent, useUiEvent } from '../../hooks/events/ui/ui-event';
@ -35,12 +36,17 @@ export const FriendListView: FC<FriendListViewProps> = props =>
case FriendListSendFriendRequestEvent.SEND_FRIEND_REQUEST:
const requestEvent = (event as FriendListSendFriendRequestEvent);
return;
case FriendListEvent.REQUEST_FRIEND_LIST:
console.log('requested');
dispatchUiEvent(new FriendListContentEvent(friendListState.friends));
return;
}
}, []);
}, [friendListState.friends]);
useUiEvent(FriendListEvent.SHOW_FRIEND_LIST, onFriendListEvent);
useUiEvent(FriendListEvent.TOGGLE_FRIEND_LIST, onFriendListEvent);
useUiEvent(FriendListSendFriendRequestEvent.SEND_FRIEND_REQUEST, onFriendListEvent);
useUiEvent(FriendListEvent.REQUEST_FRIEND_LIST, onFriendListEvent);
const onRoomEngineObjectEvent = useCallback((event: RoomEngineObjectEvent) =>
{

View File

@ -20,7 +20,6 @@ export default class RoomSettingsData
public allowPetsEat: boolean;
public usersWithRights: Map<number, string>;
public friendsWithoutRights: Map<number, string>;
public hideWalls: boolean;
public wallThickness: number;
@ -59,7 +58,6 @@ export default class RoomSettingsData
this.allowPetsEat = parser.allowPetsEat;
this.usersWithRights = new Map<number, string>();
this.friendsWithoutRights = new Map<number, string>();
this.hideWalls = parser.hideWalls;
this.wallThickness = parser.thicknessWall;
@ -74,26 +72,5 @@ export default class RoomSettingsData
this.kickState = parser.moderationSettings.allowKick;
this.banState = parser.moderationSettings.allowBan;
this.bannedUsers = new Map<number, string>();
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;
}
}

View File

@ -4,4 +4,50 @@
.content-area {
height: 350px;
}
.user-rights-container {
.row {
.col:nth-child(even) {
//background: $light !important;
}
}
}
.list-container {
max-height: 100px;
min-height: 100px;
overflow-y: scroll;
background-color: white;
border-radius: 5px;
text-align: center;
&.rights {
max-height: 80%;
min-height: 80%;
margin-bottom: 5px;
}
&.friends-rights {
max-height: 90%;
min-height: 90%;
}
.rights-button
{
float: right;
margin: 2px;
font-size: 7px;
padding: 3px;
min-height: unset;
}
.list-item {
&.selected {
background-color: cadetblue;
}
}
}
}

View File

@ -1,11 +1,17 @@
import { RoomSettingsEvent, SaveRoomSettingsComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { RoomBannedUsersComposer, RoomBannedUsersEvent, RoomSettingsEvent, RoomUsersWithRightsComposer, RoomUsersWithRightsEvent, SaveRoomSettingsComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useEffect, useState } from 'react';
import { LocalizeText } from '../../../../api';
import { FriendListEvent } from '../../../../events';
import { FriendListContentEvent } from '../../../../events/friend-list/FriendListContentEvent';
import { dispatchUiEvent, useUiEvent } from '../../../../hooks';
import { CreateMessageHook, SendMessageHook } from '../../../../hooks/messages';
import { NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout';
import RoomSettingsData from '../../common/RoomSettingsData';
import { NavigatorRoomSettingsAccessTabView } from './views/tab-access/NavigatorRoomSettingsAccessTabView';
import { NavigatorRoomSettingsBasicTabView } from './views/tab-basic/NavigatorRoomSettingsBasicTabView';
import { NavigatorRoomSettingsModTabView } from './views/tab-mod/NavigatorRoomSettingsModTabView';
import { NavigatorRoomSettingsRightsTabView } from './views/tab-rights/NavigatorRoomSettingsRightsTabView';
import { NavigatorRoomSettingsVipChatTabView } from './views/tab-vipchat/NavigatorRoomSettingsVipChatTabView';
const TABS: string[] = [
'navigator.roomsettings.tab.1',
@ -19,10 +25,10 @@ export const NavigatorRoomSettingsView: FC<{}> = props =>
{
const [ roomSettingsData, setRoomSettingsData ] = useState<RoomSettingsData>(null);
const [ currentTab, setCurrentTab ] = useState(TABS[0]);
const [ friends, setFriends ] = useState<Map<number, string>>(new Map());
const updateSettings = useCallback((roomSettings: RoomSettingsData) =>
{
console.log('update', roomSettings);
setRoomSettingsData(roomSettings);
}, [ setRoomSettingsData ]);
@ -33,40 +39,87 @@ export const NavigatorRoomSettingsView: FC<{}> = props =>
if(!parser) return;
setRoomSettingsData(new RoomSettingsData(parser));
SendMessageHook(new RoomUsersWithRightsComposer(parser.roomId));
SendMessageHook(new RoomBannedUsersComposer(parser.roomId));
}, []);
CreateMessageHook(RoomSettingsEvent, onRoomSettingsEvent);
const save = useCallback(() =>
const onRoomUsersWithRightsEvent = useCallback((event: RoomUsersWithRightsEvent) =>
{
const parser = event.getParser();
if(!parser || !roomSettingsData) return;
if(roomSettingsData.roomId !== parser.roomId) return;
const data = Object.assign({}, roomSettingsData);
data.usersWithRights = new Map(parser.users);
setRoomSettingsData(data);
}, [roomSettingsData]);
const onRoomBannedUsersEvent = useCallback((event: RoomBannedUsersEvent) =>
{
const parser = event.getParser();
if(!parser || !roomSettingsData) return;
if(roomSettingsData.roomId !== parser.roomId) return;
const data = Object.assign({}, roomSettingsData);
data.bannedUsers = new Map(parser.users);
setRoomSettingsData(data);
}, [roomSettingsData]);
const onFriendsListContentEvent = useCallback((event: FriendListContentEvent) =>
{
if(!roomSettingsData || !event.friends) return;
setFriends(event.friends);
}, [roomSettingsData]);
CreateMessageHook(RoomSettingsEvent, onRoomSettingsEvent);
CreateMessageHook(RoomUsersWithRightsEvent, onRoomUsersWithRightsEvent);
CreateMessageHook(RoomBannedUsersEvent, onRoomBannedUsersEvent);
useUiEvent(FriendListContentEvent.FRIEND_LIST_CONTENT, onFriendsListContentEvent);
useEffect(() =>
{
if(roomSettingsData) dispatchUiEvent(new FriendListEvent(FriendListEvent.REQUEST_FRIEND_LIST));
}, [roomSettingsData])
const save = useCallback((data: RoomSettingsData) =>
{
SendMessageHook(
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
data.roomId,
data.roomName,
data.roomDescription,
data.lockState,
data.password,
data.userCount,
data.categoryId,
data.tags.length,
data.tags,
data.tradeState,
data.allowPets,
data.allowPetsEat,
data.allowWalkthrough,
data.hideWalls,
data.wallThickness,
data.floorThickness,
data.muteState,
data.kickState,
data.banState,
data.chatBubbleMode,
data.chatBubbleWeight,
data.chatBubbleSpeed,
data.chatDistance,
data.chatFloodProtection
));
}, [ roomSettingsData ]);
}, []);
const processAction = useCallback((action: string) =>
{
@ -93,6 +146,9 @@ export const NavigatorRoomSettingsView: FC<{}> = props =>
<NitroCardContentView className="text-black px-4">
{ currentTab === TABS[0] && <NavigatorRoomSettingsBasicTabView 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[3] && <NavigatorRoomSettingsVipChatTabView roomSettingsData={ roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } /> }
{ currentTab === TABS[4] && <NavigatorRoomSettingsModTabView roomSettingsData={ roomSettingsData } setRoomSettingsData={ updateSettings } onSave={ save } /> }
</NitroCardContentView>
</NitroCardView>
);

View File

@ -4,5 +4,5 @@ export class NavigatorRoomSettingsTabViewProps
{
roomSettingsData: RoomSettingsData;
setRoomSettingsData: (roomSettings: RoomSettingsData) => void;
onSave: () => void;
onSave: (data: RoomSettingsData) => void;
}

View File

@ -1,6 +1,5 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../../api';
import RoomSettingsData from '../../../../common/RoomSettingsData';
import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types';
export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabViewProps> = props =>
@ -9,7 +8,7 @@ export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabView
const handleChange = useCallback((field: string, value: string | number | boolean) =>
{
const roomSettings = ({ ...roomSettingsData } as RoomSettingsData);
const roomSettings = Object.assign({}, roomSettingsData);
let save = true;
switch(field)
@ -37,7 +36,7 @@ export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabView
setRoomSettingsData(roomSettings);
if(save) onSave();
if(save) onSave(roomSettings);
}, [ roomSettingsData, setRoomSettingsData, onSave ]);
const isPasswordValid = useCallback(() =>
@ -47,11 +46,13 @@ export const NavigatorRoomSettingsAccessTabView: FC<NavigatorRoomSettingsTabView
const trySave = useCallback(() =>
{
if(isPasswordValid()) onSave();
}, [ isPasswordValid, onSave ]);
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) } />

View File

@ -43,18 +43,18 @@ export const NavigatorRoomSettingsBasicTabView: FC<NavigatorRoomSettingsTabViewP
setRoomSettingsData(roomSettings);
if(save) onSave();
if(save) onSave(roomSettings);
}, [ roomSettingsData, setRoomSettingsData, onSave ]);
return (
<>
<div className="form-group mb-1">
<label>{ LocalizeText('navigator.roomname') }</label>
<input className="form-control form-control-sm" value={ roomSettingsData.roomName } onChange={ event => handleChange('name', event.target.value) } onBlur={ onSave } />
<input className="form-control form-control-sm" value={ roomSettingsData.roomName } onChange={ event => handleChange('name', event.target.value) } onBlur={ () => onSave(roomSettingsData) } />
</div>
<div className="form-group mb-1">
<label>{ LocalizeText('navigator.roomsettings.desc') }</label>
<input className="form-control form-control-sm" value={ roomSettingsData.roomDescription } onChange={ event => handleChange('description', event.target.value) } onBlur={ onSave } />
<input className="form-control form-control-sm" value={ roomSettingsData.roomDescription } onChange={ event => handleChange('description', event.target.value) } onBlur={ () => onSave(roomSettingsData) } />
</div>
<div className="form-group mb-1">
<label>{ LocalizeText('navigator.category') }</label>

View File

@ -0,0 +1,107 @@
import { RoomUnbanUserComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useState } from 'react';
import { LocalizeText } from '../../../../../../api';
import { SendMessageHook } from '../../../../../../hooks';
import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types';
export const NavigatorRoomSettingsModTabView: FC<NavigatorRoomSettingsTabViewProps> = props =>
{
const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props;
const [selectedUserId, setSelectedUserId] = useState<number>(-1);
const handleChange = useCallback((field: string, value: string | number | boolean) =>
{
const clone = Object.assign({}, roomSettingsData);
let save = true;
switch(field)
{
case 'moderation_mute':
clone.muteState = Number(value);
break;
case 'moderation_kick':
clone.kickState = Number(value);
break;
case 'moderation_ban':
clone.banState = Number(value);
break;
}
setRoomSettingsData(clone);
if(save) onSave(clone);
}, [roomSettingsData, setRoomSettingsData, onSave]);
const unBanUser = useCallback((userId: number) =>
{
const clone = Object.assign({}, roomSettingsData);
clone.bannedUsers.delete(userId);
setRoomSettingsData(clone);
SendMessageHook(new RoomUnbanUserComposer(userId, roomSettingsData.roomId));
setSelectedUserId(-1);
}, [roomSettingsData, setRoomSettingsData]);
return (
<>
<div className="mb-3">{LocalizeText('navigator.roomsettings.moderation.header')}</div>
<div className="form-group mb-1" >
<div className="fw-bold">{LocalizeText('navigator.roomsettings.moderation.mute.header')}</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="moderation_mute" checked={roomSettingsData.muteState === 0} onChange={(e) => handleChange('moderation_mute', 0)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.moderation.none')}</label>
</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="moderation_mute" checked={roomSettingsData.muteState === 1} onChange={(e) => handleChange('moderation_mute', 1)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.moderation.rights')}</label>
</div>
</div>
<div className="form-group mb-1" >
<div className="fw-bold">{LocalizeText('navigator.roomsettings.moderation.kick.header')}</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="moderation_kick" checked={roomSettingsData.kickState === 0} onChange={(e) => handleChange('moderation_kick', 0)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.moderation.all')}</label>
</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="moderation_kick" checked={roomSettingsData.kickState === 1} onChange={(e) => handleChange('moderation_kick', 1)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.moderation.rights')}</label>
</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="moderation_kick" checked={roomSettingsData.kickState === 2} onChange={(e) => handleChange('moderation_kick', 2)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.moderation.none')}</label>
</div>
</div>
<div className="form-group mb-1" >
<div className="fw-bold">{LocalizeText('navigator.roomsettings.moderation.ban.header')}</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="moderation_ban" checked={roomSettingsData.banState === 0} onChange={(e) => handleChange('moderation_ban', 0)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.moderation.none')}</label>
</div>
<div className="form-check form-check-inline">
<input className="form-check-input" type="radio" name="moderation_ban" checked={roomSettingsData.banState === 1} onChange={(e) => handleChange('moderation_ban', 1)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.moderation.rights')}</label>
</div>
</div>
<div className="fw-bold mt-3 mb-1">{LocalizeText('navigator.roomsettings.moderation.banned.users')} ({roomSettingsData.bannedUsers.size})</div>
<div className="row">
<div className="col-6">
<div className="list-container">
{
Array.from(roomSettingsData.bannedUsers.entries()).map(([id, name], index) =>
{
return <div key={index} className={'list-item' + ((id === selectedUserId) ? ' selected' : '')} onClick={e => setSelectedUserId(id)} >
{name}
</div>
})
}
</div>
</div>
<div className="col-6">
<button className={'btn btn-primary btn-sm w-100' + ((selectedUserId < 1) ? ' disabled' : '')} onClick={ e => unBanUser(selectedUserId) }>{LocalizeText('navigator.roomsettings.moderation.unban')} {selectedUserId > 0 && roomSettingsData.bannedUsers.get(selectedUserId)}
</button>
</div>
</div>
</>
);
}

View File

@ -0,0 +1,152 @@
import { RemoveAllRightsMessageComposer, RoomGiveRightsComposer, RoomTakeRightsComposer } from '@nitrots/nitro-renderer';
import { FC, useCallback, useMemo } from 'react';
import { LocalizeText } from '../../../../../../api';
import { SendMessageHook } from '../../../../../../hooks';
import { UserProfileIconView } from '../../../../../shared/user-profile-icon/UserProfileIconView';
import { NavigatorRoomSettingsRightsTabViewProps } from './NavigatorRoomSettingsRightsTabView.types';
export const NavigatorRoomSettingsRightsTabView: FC<NavigatorRoomSettingsRightsTabViewProps> = props =>
{
const { roomSettingsData = null, setRoomSettingsData = null, onSave = null, friends = null } = props;
const removeUserRights = useCallback( (userId: number) =>
{
const clone = Object.assign({}, roomSettingsData);
clone.usersWithRights.delete(userId);
setRoomSettingsData(clone);
SendMessageHook(new RoomTakeRightsComposer(userId));
}, [roomSettingsData, setRoomSettingsData]);
const giveUserRights = useCallback( (userId: number, name: string) =>
{
const clone = Object.assign({}, roomSettingsData);
clone.usersWithRights.set(userId, name);
setRoomSettingsData(clone);
SendMessageHook(new RoomGiveRightsComposer(userId));
}, [roomSettingsData, setRoomSettingsData]);
const removeAllRights = useCallback( () =>
{
const clone = Object.assign({}, roomSettingsData);
clone.usersWithRights = new Map();
setRoomSettingsData(clone);
SendMessageHook(new RemoveAllRightsMessageComposer(clone.roomId));
}, [roomSettingsData, setRoomSettingsData]);
const friendsWithoutRights = useMemo(() =>
{
const map = new Map<number, string>();
friends.forEach((name, id) =>
{
if(!roomSettingsData.usersWithRights.has(id))
map.set(id, name);
});
return map;
}, [friends, roomSettingsData]);
return (
<div className="d-flex flex-column h-100 overflow-hidden user-rights">
<div className="row">
<div className="col">
filter
</div>
</div>
<div className="row h-100">
<div className="col-6 d-flex flex-column h-100">
<div className="fw-bold mb-1">{ LocalizeText('navigator.flatctrls.userswithrights', [ 'displayed', 'total' ], [ roomSettingsData.usersWithRights.size.toString(), roomSettingsData.usersWithRights.size.toString() ]) }</div>
<div className="d-flex flex-column justify-content-between rounded bg-white h-100 overflow-hidden px-2 py-1 user-rights-container">
<div className="row row-cols-1 overflow-auto">
{ Array.from(roomSettingsData.usersWithRights.entries()).map(([id, name], index) =>
{
return (
<div key={index} className="d-flex align-items-center col w-100" onClick={ () => removeUserRights(id) }>
<div className="d-flex align-items-center me-2">
<UserProfileIconView userName={ name } />
<span>{ name }</span>
</div>
<i className="fas fa-caret-right fw-bold text-black me-2" />
</div>
);
})
}
</div>
<button className="btn btn-danger btn-sm w-100" disabled={ !roomSettingsData.usersWithRights.size } onClick={ removeAllRights } >{ LocalizeText('navigator.flatctrls.clear') }</button>
</div>
</div>
<div className="col-6 d-flex flex-column h-100">
<div className="fw-bold mb-1">{ LocalizeText('navigator.flatctrls.friends', [ 'displayed', 'total' ], [ friendsWithoutRights.size.toString(), friendsWithoutRights.size.toString() ]) }</div>
<div className="d-flex flex-column rounded bg-white h-100 overflow-hidden px-2 py-1 user-rights-container">
<div className="row row-cols-1 overflow-auto">
{ Array.from(friendsWithoutRights.entries()).map(([id, name], index) =>
{
return (
<div key={index} className="d-flex align-items-center col w-100" onClick={ () => giveUserRights(id, name) }>
<i className="fas fa-caret-left fw-bold text-black me-2" />
<div className="d-flex align-items-center">
<UserProfileIconView userName={ name } />
<span>{ name }</span>
</div>
</div>
);
})
}
</div>
</div>
</div>
</div>
</div>
// <div className="row h-100">
// <div className="col-6">
// <div className="fw-bold mb-2">{LocalizeText('navigator.flatctrls.userswithrights', ['displayed', 'total'], [roomSettingsData.usersWithRights.size.toString(), roomSettingsData.usersWithRights.size.toString()])}</div>
// </div>
// <div className="col-6">
// <div className="fw-bold mb-2">{LocalizeText('navigator.flatctrls.friends', ['displayed', 'total'], [friendsWithoutRights.size.toString(), friendsWithoutRights.size.toString()])}</div>
// </div>
// </div>
// <div className="row h-100">
// <div className="col-6 d-flex flex-column h-100">
// <div className="row row-cols-1 g-0 overflow-auto">
// {
// Array.from(roomSettingsData.usersWithRights.entries()).map(([id, name], index) =>
// {
// return (
// <div key={ index } className="col">
// <span className="w-100" >{name}</span>
// <button className="btn btn-danger btn-sm rights-button" onClick={ () => removeUserRights(id) }>
// <i className="fas fa-times" />
// </button>
// </div>
// );
// })
// }
// </div>
// <button className={'btn btn-danger btn-sm w-100' + ((roomSettingsData.usersWithRights.size < 1) ? ' disabled' : '')} onClick={ removeAllRights } >{ LocalizeText('navigator.flatctrls.clear') }</button>
// </div>
// <div className="col-6 d-flex flex-column h-100">
// <div className="row row-cols-1 g-0 overflow-auto bg-white h-100">
// {
// Array.from(friendsWithoutRights.entries()).map(([id, name], index) =>
// {
// return (
// <div key={index} className="col">
// <span className="w-100">{name}</span>
// <button className="btn btn-success btn-sm rights-button" onClick={ () => giveUserRights(id, name) }>
// <i className="fas fa-chevron-left" />
// </button>
// </div>
// );
// })
// }
// </div>
// </div >
// </div >
);
}

View File

@ -0,0 +1,6 @@
import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types';
export interface NavigatorRoomSettingsRightsTabViewProps extends NavigatorRoomSettingsTabViewProps
{
friends: Map<number, string>;
}

View File

@ -0,0 +1,108 @@
import { FC, useCallback } from 'react';
import { LocalizeText } from '../../../../../../api';
import { NavigatorRoomSettingsTabViewProps } from '../../NavigatorRoomSettingsView.types';
export const NavigatorRoomSettingsVipChatTabView: FC<NavigatorRoomSettingsTabViewProps> = props =>
{
const { roomSettingsData = null, setRoomSettingsData = null, onSave = null } = props;
const handleChange = useCallback((field: string, value: string | number | boolean) =>
{
const clone = Object.assign({}, roomSettingsData);
let save = true;
switch(field)
{
case 'hide_walls':
clone.hideWalls = Boolean(value);
break;
case 'wall_thickness':
clone.wallThickness = Number(value);
break;
case 'floor_thickness':
clone.floorThickness = Number(value);
break;
case 'bubble_mode':
clone.chatBubbleMode = Number(value);
break;
case 'chat_weight':
clone.chatBubbleWeight = Number(value);
break;
case 'bubble_speed':
clone.chatBubbleSpeed = Number(value);
break;
case 'flood_protection':
clone.chatFloodProtection = Number(value);
break;
case 'chat_distance':
clone.chatDistance = Number(value);
save = false;
break;
}
setRoomSettingsData(clone);
if(save) onSave(clone);
}, [roomSettingsData, setRoomSettingsData, onSave]);
return (
<>
<div className="fw-bold">{LocalizeText('navigator.roomsettings.vip.caption')}</div>
<div className="mb-3">{LocalizeText('navigator.roomsettings.vip.info')}</div>
<div className="fw-bold">{LocalizeText('navigator.roomsettings.vip_settings')}</div>
<div className="form-check">
<input className="form-check-input" type="checkbox" checked={roomSettingsData.hideWalls} onChange={(e) => handleChange('hide_walls', e.target.checked)} />
<label className="form-check-label">{LocalizeText('navigator.roomsettings.hide_walls')}</label>
</div>
<div className="form-group mb-1">
<select className="form-select form-select-sm" value={roomSettingsData.wallThickness} onChange={event => handleChange('wall_thickness', event.target.value)}>
<option value="0">{LocalizeText('navigator.roomsettings.wall_thickness.normal')}</option>
<option value="1">{LocalizeText('navigator.roomsettings.wall_thickness.thick')}</option>
<option value="-1">{LocalizeText('navigator.roomsettings.wall_thickness.thin')}</option>
<option value="-2">{LocalizeText('navigator.roomsettings.wall_thickness.thinnest')}</option>
</select>
</div>
<div className="form-group mb-1">
<select className="form-select form-select-sm" value={roomSettingsData.floorThickness} onChange={event => handleChange('floor_thickness', event.target.value)}>
<option value="0">{LocalizeText('navigator.roomsettings.floor_thickness.normal')}</option>
<option value="1">{LocalizeText('navigator.roomsettings.floor_thickness.thick')}</option>
<option value="-1">{LocalizeText('navigator.roomsettings.floor_thickness.thin')}</option>
<option value="-2">{LocalizeText('navigator.roomsettings.floor_thickness.thinnest')}</option>
</select>
</div>
<div className="fw-bold">{LocalizeText('navigator.roomsettings.chat_settings')}</div>
<div className="mb-2">{LocalizeText('navigator.roomsettings.chat_settings.info')}</div>
<div className="form-group mb-1">
<select className="form-select form-select-sm" value={roomSettingsData.chatBubbleMode} onChange={event => handleChange('bubble_mode', event.target.value)}>
<option value="0">{LocalizeText('navigator.roomsettings.chat.mode.free.flow')}</option>
<option value="1">{LocalizeText('navigator.roomsettings.chat.mode.line.by.line')}</option>
</select>
</div>
<div className="form-group mb-1">
<select className="form-select form-select-sm" value={roomSettingsData.chatBubbleWeight} onChange={event => handleChange('chat_weight', event.target.value)}>
<option value="0">{LocalizeText('navigator.roomsettings.chat.bubbles.width.normal')}</option>
<option value="1">{LocalizeText('navigator.roomsettings.chat.bubbles.width.thin')}</option>
<option value="2">{LocalizeText('navigator.roomsettings.chat.bubbles.width.wide')}</option>
</select>
</div>
<div className="form-group mb-1">
<select className="form-select form-select-sm" value={roomSettingsData.chatBubbleSpeed} onChange={event => handleChange('bubble_speed', event.target.value)}>
<option value="0">{LocalizeText('navigator.roomsettings.chat.speed.fast')}</option>
<option value="1">{LocalizeText('navigator.roomsettings.chat.speed.normal')}</option>
<option value="2">{LocalizeText('navigator.roomsettings.chat.speed.slow')}</option>
</select>
</div>
<div className="form-group mb-1">
<select className="form-select form-select-sm" value={roomSettingsData.chatFloodProtection} onChange={event => handleChange('flood_protection', event.target.value)}>
<option value="0">{LocalizeText('navigator.roomsettings.chat.flood.loose')}</option>
<option value="1">{LocalizeText('navigator.roomsettings.chat.flood.normal')}</option>
<option value="2">{LocalizeText('navigator.roomsettings.chat.flood.strict')}</option>
</select>
</div>
<div className="form-group mb-0">
<label>{LocalizeText('navigator.roomsettings.chat_settings.hearing.distance')}</label>
<input type="number" min="0" className="form-control form-control-sm" value={roomSettingsData.chatDistance} onChange={event => handleChange('chat_distance', event.target.valueAsNumber)} onBlur={() => onSave(roomSettingsData)} />
</div>
</>
);
}

View File

@ -0,0 +1,16 @@
import { FC, useCallback } from 'react';
import { UserProfileIconViewProps } from './UserProfileIconView.types';
export const UserProfileIconView: FC<UserProfileIconViewProps> = props =>
{
const { userId = -1, userName = null } = props;
const visitProfile = useCallback(() =>
{
}, [ userId, userName ]);
return (
<i className="icon icon-user-profile me-1 cursor-pointer" onClick={ visitProfile } />
);
}

View File

@ -0,0 +1,5 @@
export interface UserProfileIconViewProps
{
userId?: number;
userName?: string;
}