mirror of
https://github.com/billsonnn/nitro-react.git
synced 2024-11-26 23:50:52 +01:00
Merge branch 'dev' into more-layout-changes
This commit is contained in:
commit
bdc2861e20
@ -12,3 +12,7 @@
|
|||||||
.nitro-cfh-sanction-status {
|
.nitro-cfh-sanction-status {
|
||||||
width: 400px;
|
width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nitro-change-username {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
@ -10,6 +10,7 @@ import { HelpContextProvider } from './HelpContext';
|
|||||||
import { HelpMessageHandler } from './HelpMessageHandler';
|
import { HelpMessageHandler } from './HelpMessageHandler';
|
||||||
import { DescribeReportView } from './views/DescribeReportView';
|
import { DescribeReportView } from './views/DescribeReportView';
|
||||||
import { HelpIndexView } from './views/HelpIndexView';
|
import { HelpIndexView } from './views/HelpIndexView';
|
||||||
|
import { NameChangeView } from './views/name-change/NameChangeView';
|
||||||
import { SanctionSatusView } from './views/SanctionStatusView';
|
import { SanctionSatusView } from './views/SanctionStatusView';
|
||||||
import { SelectReportedChatsView } from './views/SelectReportedChatsView';
|
import { SelectReportedChatsView } from './views/SelectReportedChatsView';
|
||||||
import { SelectReportedUserView } from './views/SelectReportedUserView';
|
import { SelectReportedUserView } from './views/SelectReportedUserView';
|
||||||
@ -124,6 +125,7 @@ export const HelpView: FC<{}> = props =>
|
|||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
</NitroCardView> }
|
</NitroCardView> }
|
||||||
<SanctionSatusView />
|
<SanctionSatusView />
|
||||||
|
<NameChangeView />
|
||||||
</HelpContextProvider>
|
</HelpContextProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
import { ChangeUserNameMessageComposer, UserNameChangeMessageEvent } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useState } from 'react';
|
||||||
|
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||||
|
import { CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||||
|
import { NameChangeLayoutViewProps } from './NameChangeView.types';
|
||||||
|
|
||||||
|
export const NameChangeConfirmationView:FC<NameChangeLayoutViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { username = '', onAction = null } = props;
|
||||||
|
|
||||||
|
const [ isConfirming, setIsConfirming ] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const onUserNameChangeMessageEvent = useCallback((event: UserNameChangeMessageEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(!parser) return;
|
||||||
|
|
||||||
|
if(parser.webId !== GetSessionDataManager().userId) return;
|
||||||
|
|
||||||
|
onAction('close');
|
||||||
|
}, [ onAction ]);
|
||||||
|
|
||||||
|
CreateMessageHook(UserNameChangeMessageEvent, onUserNameChangeMessageEvent);
|
||||||
|
|
||||||
|
const confirm = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(isConfirming) return;
|
||||||
|
|
||||||
|
setIsConfirming(true);
|
||||||
|
SendMessageHook(new ChangeUserNameMessageComposer(username));
|
||||||
|
}, [ isConfirming, username ]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-column gap-4 h-100">
|
||||||
|
<div className="bg-muted rounded p-2 text-center">{ LocalizeText('tutorial.name_change.info.confirm') }</div>
|
||||||
|
<div className="d-flex flex-column align-items-center gap-1 h-100">
|
||||||
|
<div>{ LocalizeText('tutorial.name_change.confirm') }</div>
|
||||||
|
<div className="fw-bold">{ username }</div>
|
||||||
|
</div>
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
<button className="btn btn-success w-100" disabled={ isConfirming } onClick={ confirm }>{ LocalizeText('generic.ok') }</button>
|
||||||
|
<button className="btn btn-primary w-100" onClick={ () => onAction('close') }>{ LocalizeText('cancel') }</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
19
src/components/help/views/name-change/NameChangeInitView.tsx
Normal file
19
src/components/help/views/name-change/NameChangeInitView.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { GetSessionDataManager, LocalizeText } from '../../../../api';
|
||||||
|
import { NameChangeLayoutViewProps } from './NameChangeView.types';
|
||||||
|
|
||||||
|
export const NameChangeInitView:FC<NameChangeLayoutViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { onAction = null } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-column gap-4 h-100">
|
||||||
|
<div className="bg-muted rounded p-2 text-center">{ LocalizeText('tutorial.name_change.info.main') }</div>
|
||||||
|
<div className="fw-bold d-flex align-items-center justify-content-center h-100 w-100">{ LocalizeText('tutorial.name_change.current', ['name'], [GetSessionDataManager().userName]) }</div>
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
<button className="btn btn-success w-100" onClick={ () => onAction('start') }>{ LocalizeText('tutorial.name_change.change') }</button>
|
||||||
|
<button className="btn btn-primary w-100" onClick={ () => onAction('confirmation', GetSessionDataManager().userName) }>{ LocalizeText('tutorial.name_change.keep') }</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
102
src/components/help/views/name-change/NameChangeInputView.tsx
Normal file
102
src/components/help/views/name-change/NameChangeInputView.tsx
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { CheckUserNameMessageComposer, CheckUserNameResultMessageEvent } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { CreateMessageHook, SendMessageHook } from '../../../../hooks';
|
||||||
|
import { NameChangeLayoutViewProps } from './NameChangeView.types';
|
||||||
|
|
||||||
|
const AVAILABLE: number = 0;
|
||||||
|
const TOO_SHORT: number = 2;
|
||||||
|
const TOO_LONG: number = 3;
|
||||||
|
const NOT_VALID: number = 4;
|
||||||
|
const TAKEN_WITH_SUGGESTIONS: number = 5;
|
||||||
|
const DISABLED: number = 6;
|
||||||
|
|
||||||
|
export const NameChangeInputView:FC<NameChangeLayoutViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { onAction = null } = props;
|
||||||
|
|
||||||
|
const [ newUsername, setNewUsername ] = useState<string>('');
|
||||||
|
const [ canProceed, setCanProceed ] = useState<boolean>(false);
|
||||||
|
const [ isChecking, setIsChecking ] = useState<boolean>(false);
|
||||||
|
const [ errorCode, setErrorCode ] = useState<string>(null);
|
||||||
|
const [ suggestions, setSuggestions ] = useState<string[]>([]);
|
||||||
|
|
||||||
|
const onCheckUserNameResultMessageEvent = useCallback((event: CheckUserNameResultMessageEvent) =>
|
||||||
|
{
|
||||||
|
setIsChecking(false);
|
||||||
|
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(!parser) return;
|
||||||
|
|
||||||
|
switch(parser.resultCode)
|
||||||
|
{
|
||||||
|
case AVAILABLE:
|
||||||
|
setCanProceed(true);
|
||||||
|
break;
|
||||||
|
case TOO_SHORT:
|
||||||
|
setErrorCode('short');
|
||||||
|
break;
|
||||||
|
case TOO_LONG:
|
||||||
|
setErrorCode('long');
|
||||||
|
break;
|
||||||
|
case NOT_VALID:
|
||||||
|
setErrorCode('invalid');
|
||||||
|
break;
|
||||||
|
case TAKEN_WITH_SUGGESTIONS:
|
||||||
|
setSuggestions(parser.nameSuggestions);
|
||||||
|
setErrorCode('taken');
|
||||||
|
break;
|
||||||
|
case DISABLED:
|
||||||
|
setErrorCode('change_not_allowed');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
CreateMessageHook(CheckUserNameResultMessageEvent, onCheckUserNameResultMessageEvent);
|
||||||
|
|
||||||
|
const check = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(newUsername === '') return;
|
||||||
|
|
||||||
|
setCanProceed(false);
|
||||||
|
setSuggestions([]);
|
||||||
|
setErrorCode(null);
|
||||||
|
|
||||||
|
setIsChecking(true);
|
||||||
|
SendMessageHook(new CheckUserNameMessageComposer(newUsername));
|
||||||
|
}, [ newUsername ]);
|
||||||
|
|
||||||
|
const handleUsernameChange = useCallback((username: string) =>
|
||||||
|
{
|
||||||
|
setCanProceed(false);
|
||||||
|
setSuggestions([]);
|
||||||
|
setErrorCode(null);
|
||||||
|
|
||||||
|
setNewUsername(username);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="d-flex flex-column gap-3 h-100">
|
||||||
|
<div>{ LocalizeText('tutorial.name_change.info.select') }</div>
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
<input type="text" className="form-control form-control-sm" value={newUsername} onChange={ (e) => handleUsernameChange(e.target.value) } />
|
||||||
|
<button className="btn btn-primary" disabled={ newUsername === '' || isChecking } onClick={ check }>{ LocalizeText('tutorial.name_change.check') }</button>
|
||||||
|
</div>
|
||||||
|
{ !errorCode && !canProceed && <div className="bg-muted rounded p-2 text-center">{ LocalizeText('help.tutorial.name.info') }</div> }
|
||||||
|
{ errorCode && <div className="bg-danger rounded p-2 text-center text-white">{ LocalizeText(`help.tutorial.name.${errorCode}`, ['name'], [newUsername]) }</div> }
|
||||||
|
{ canProceed && <div className="bg-success rounded p-2 text-center text-white">{ LocalizeText('help.tutorial.name.available', ['name'], [newUsername]) }</div> }
|
||||||
|
{ suggestions && <div className="d-flex flex-column gap-2">
|
||||||
|
{
|
||||||
|
suggestions.map((suggestion, i) =>
|
||||||
|
{
|
||||||
|
return (<div key={ i } className="col bg-muted rounded p-1 cursor-pointer" onClick={ () => handleUsernameChange(suggestion) }>{ suggestion }</div>);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</div> }
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
<button className="btn btn-success w-100" disabled={ !canProceed } onClick={ () => onAction('confirmation', newUsername) }>{ LocalizeText('tutorial.name_change.pick') }</button>
|
||||||
|
<button className="btn btn-primary w-100" onClick={ () => onAction('close') }>{ LocalizeText('cancel') }</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
68
src/components/help/views/name-change/NameChangeView.tsx
Normal file
68
src/components/help/views/name-change/NameChangeView.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { FC, useCallback, useMemo, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { HelpNameChangeEvent } from '../../../../events/help/HelpNameChangeEvent';
|
||||||
|
import { useUiEvent } from '../../../../hooks';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
|
import { NameChangeConfirmationView } from './NameChangeConfirmationView';
|
||||||
|
import { NameChangeInitView } from './NameChangeInitView';
|
||||||
|
import { NameChangeInputView } from './NameChangeInputView';
|
||||||
|
|
||||||
|
const INIT: string = 'INIT';
|
||||||
|
const INPUT: string = 'INPUT';
|
||||||
|
const CONFIRMATION: string = 'CONFIRMATION';
|
||||||
|
|
||||||
|
export const NameChangeView:FC<{}> = props =>
|
||||||
|
{
|
||||||
|
const [ isVisible, setIsVisible ] = useState<boolean>(false);
|
||||||
|
const [ layout, setLayout ] = useState<string>(INIT);
|
||||||
|
const [ newUsername, setNewUsername ] = useState<string>('');
|
||||||
|
|
||||||
|
const onHelpNameChangeEvent = useCallback((event: HelpNameChangeEvent) =>
|
||||||
|
{
|
||||||
|
setLayout(INIT);
|
||||||
|
setIsVisible(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useUiEvent(HelpNameChangeEvent.INIT, onHelpNameChangeEvent);
|
||||||
|
|
||||||
|
const onAction = useCallback((action: string, value?: string) =>
|
||||||
|
{
|
||||||
|
switch(action)
|
||||||
|
{
|
||||||
|
case 'start':
|
||||||
|
setLayout(INPUT);
|
||||||
|
break;
|
||||||
|
case 'confirmation':
|
||||||
|
setNewUsername(value);
|
||||||
|
setLayout(CONFIRMATION);
|
||||||
|
break;
|
||||||
|
case 'close':
|
||||||
|
setNewUsername('');
|
||||||
|
setIsVisible(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const titleKey = useMemo(() =>
|
||||||
|
{
|
||||||
|
switch(layout)
|
||||||
|
{
|
||||||
|
case INIT: return 'tutorial.name_change.title.main';
|
||||||
|
case INPUT: return 'tutorial.name_change.title.select';
|
||||||
|
case CONFIRMATION: return 'tutorial.name_change.title.confirm';
|
||||||
|
}
|
||||||
|
}, [layout]);
|
||||||
|
|
||||||
|
if(!isVisible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroCardView className="nitro-change-username" simple={ true }>
|
||||||
|
<NitroCardHeaderView headerText={LocalizeText(titleKey)} onCloseClick={ () => onAction('close') } />
|
||||||
|
<NitroCardContentView className="text-black">
|
||||||
|
{ layout === INIT && <NameChangeInitView onAction={ onAction } /> }
|
||||||
|
{ layout === INPUT && <NameChangeInputView onAction={ onAction } /> }
|
||||||
|
{ layout === CONFIRMATION && <NameChangeConfirmationView username={ newUsername } onAction={ onAction } /> }
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
export interface NameChangeLayoutViewProps
|
||||||
|
{
|
||||||
|
username?: string;
|
||||||
|
onAction: (action: string, value?: string) => void;
|
||||||
|
}
|
6
src/events/help/HelpNameChangeEvent.ts
Normal file
6
src/events/help/HelpNameChangeEvent.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { NitroEvent } from '@nitrots/nitro-renderer/src/core/events/NitroEvent';
|
||||||
|
|
||||||
|
export class HelpNameChangeEvent extends NitroEvent
|
||||||
|
{
|
||||||
|
public static INIT: string = 'HC_NAME_CHANGE_INIT';
|
||||||
|
}
|
@ -92,5 +92,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nitro-friends-room-invite {
|
||||||
|
width: $friends-list-width;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nitro-friends-remove-confirmation {
|
||||||
|
width: $friends-list-width;
|
||||||
|
}
|
||||||
|
|
||||||
@import "./views/friend-bar/FriendBarView";
|
@import "./views/friend-bar/FriendBarView";
|
||||||
@import "./views/messenger/FriendsMessengerView";
|
@import "./views/messenger/FriendsMessengerView";
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
import { FollowFriendMessageComposer, SetRelationshipStatusComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import classNames from 'classnames';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useState } from 'react';
|
||||||
import { LocalizeText, OpenMessengerChat } from '../../../../api';
|
import { LocalizeText, OpenMessengerChat } from '../../../../api';
|
||||||
import { SendMessageHook } from '../../../../hooks';
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
@ -9,7 +10,7 @@ import { FriendsGroupItemViewProps } from './FriendsGroupItemView.types';
|
|||||||
|
|
||||||
export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { friend = null, selected = false, children = null, ...rest } = props;
|
const { friend = null, selected = false, selectFriend = null, children = null, ...rest } = props;
|
||||||
|
|
||||||
const [ isExpanded, setIsExpanded ] = useState<boolean>(false);
|
const [ isExpanded, setIsExpanded ] = useState<boolean>(false);
|
||||||
|
|
||||||
@ -20,8 +21,10 @@ export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
|||||||
SendMessageHook(new FollowFriendMessageComposer(friend.id));
|
SendMessageHook(new FollowFriendMessageComposer(friend.id));
|
||||||
}, [ friend ]);
|
}, [ friend ]);
|
||||||
|
|
||||||
const openMessengerChat = useCallback(() =>
|
const openMessengerChat = useCallback((e) =>
|
||||||
{
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
if(!friend) return;
|
if(!friend) return;
|
||||||
|
|
||||||
OpenMessengerChat(friend.id);
|
OpenMessengerChat(friend.id);
|
||||||
@ -40,8 +43,16 @@ export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
|||||||
}
|
}
|
||||||
}, [ friend ]);
|
}, [ friend ]);
|
||||||
|
|
||||||
const updateRelationship = useCallback((type: number) =>
|
const initUpdateRelationship = useCallback((e) =>
|
||||||
{
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsExpanded(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const updateRelationship = useCallback((e, type: number) =>
|
||||||
|
{
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
if(type !== friend.relationshipStatus) SendMessageHook(new SetRelationshipStatusComposer(friend.id, type));
|
if(type !== friend.relationshipStatus) SendMessageHook(new SetRelationshipStatusComposer(friend.id, type));
|
||||||
|
|
||||||
setIsExpanded(false);
|
setIsExpanded(false);
|
||||||
@ -50,8 +61,10 @@ export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
|||||||
if(!friend) return null;
|
if(!friend) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroLayoutFlex className="px-2 py-1 align-items-center" gap={ 1 } { ...rest }>
|
<NitroLayoutFlex className={ 'px-2 py-1 align-items-center' + classNames({ ' bg-primary text-white': selected }) } gap={ 1 } { ...rest } onClick={ selectFriend }>
|
||||||
<UserProfileIconView userId={ friend.id } />
|
<div onClick={ (e) => e.stopPropagation() }>
|
||||||
|
<UserProfileIconView userId={ friend.id } />
|
||||||
|
</div>
|
||||||
<div>{ friend.name }</div>
|
<div>{ friend.name }</div>
|
||||||
<NitroLayoutFlex className="ms-auto align-items-center" gap={ 1 }>
|
<NitroLayoutFlex className="ms-auto align-items-center" gap={ 1 }>
|
||||||
{ !isExpanded &&
|
{ !isExpanded &&
|
||||||
@ -60,14 +73,14 @@ export const FriendsGroupItemView: FC<FriendsGroupItemViewProps> = props =>
|
|||||||
<NitroLayoutBase onClick={ followFriend } className="nitro-friends-spritesheet icon-follow cursor-pointer" title={ LocalizeText('friendlist.tip.follow') } /> }
|
<NitroLayoutBase onClick={ followFriend } className="nitro-friends-spritesheet icon-follow cursor-pointer" title={ LocalizeText('friendlist.tip.follow') } /> }
|
||||||
{ friend.online &&
|
{ friend.online &&
|
||||||
<NitroLayoutBase className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> }
|
<NitroLayoutBase className="nitro-friends-spritesheet icon-chat cursor-pointer" onClick={ openMessengerChat } title={ LocalizeText('friendlist.tip.im') } /> }
|
||||||
<NitroLayoutBase className={ `nitro-friends-spritesheet icon-${ getCurrentRelationshipName() } cursor-pointer` }onClick={ event => setIsExpanded(true) } title={ LocalizeText('infostand.link.relationship') } />
|
<NitroLayoutBase className={ `nitro-friends-spritesheet icon-${ getCurrentRelationshipName() } cursor-pointer` } onClick={ initUpdateRelationship } title={ LocalizeText('infostand.link.relationship') } />
|
||||||
</> }
|
</> }
|
||||||
{ isExpanded &&
|
{ isExpanded &&
|
||||||
<>
|
<>
|
||||||
<NitroLayoutBase className="nitro-friends-spritesheet icon-heart cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_HEART) } />
|
<NitroLayoutBase className="nitro-friends-spritesheet icon-heart cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_HEART) } />
|
||||||
<NitroLayoutBase className="nitro-friends-spritesheet icon-smile cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_SMILE) } />
|
<NitroLayoutBase className="nitro-friends-spritesheet icon-smile cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_SMILE) } />
|
||||||
<NitroLayoutBase className="nitro-friends-spritesheet icon-bobba cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_BOBBA) } />
|
<NitroLayoutBase className="nitro-friends-spritesheet icon-bobba cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_BOBBA) } />
|
||||||
<NitroLayoutBase className="nitro-friends-spritesheet icon-none cursor-pointer" onClick={ () => updateRelationship(MessengerFriend.RELATIONSHIP_NONE) } />
|
<NitroLayoutBase className="nitro-friends-spritesheet icon-none cursor-pointer" onClick={ (e) => updateRelationship(e, MessengerFriend.RELATIONSHIP_NONE) } />
|
||||||
</> }
|
</> }
|
||||||
</NitroLayoutFlex>
|
</NitroLayoutFlex>
|
||||||
{ children }
|
{ children }
|
||||||
|
@ -5,4 +5,5 @@ export interface FriendsGroupItemViewProps extends NitroLayoutFlexProps
|
|||||||
{
|
{
|
||||||
friend: MessengerFriend;
|
friend: MessengerFriend;
|
||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
|
selectFriend: () => void;
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,15 @@ import { FriendsGroupViewProps } from './FriendsGroupView.types';
|
|||||||
|
|
||||||
export const FriendsGroupView: FC<FriendsGroupViewProps> = props =>
|
export const FriendsGroupView: FC<FriendsGroupViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { list = null } = props;
|
const { list = null, selectedFriendsIds = null, selectFriend = null } = props;
|
||||||
|
|
||||||
if(!list) return null;
|
if(!list) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{ list.map((item, index) =>
|
{ selectedFriendsIds && list && list.map((item, index) =>
|
||||||
{
|
{
|
||||||
return <FriendsGroupItemView key={ index } friend={ item } />;
|
return <FriendsGroupItemView key={ index } friend={ item } selected={ selectedFriendsIds.includes(item.id) } selectFriend={ () => selectFriend(item.id) } />;
|
||||||
}) }
|
}) }
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -3,4 +3,6 @@ import { MessengerFriend } from '../../common/MessengerFriend';
|
|||||||
export interface FriendsGroupViewProps
|
export interface FriendsGroupViewProps
|
||||||
{
|
{
|
||||||
list: MessengerFriend[];
|
list: MessengerFriend[];
|
||||||
|
selectedFriendsIds: number[];
|
||||||
|
selectFriend: (userId: number) => void;
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
import { FC, useEffect, useState } from 'react';
|
import { RemoveFriendComposer, SendRoomInviteComposer } from '@nitrots/nitro-renderer';
|
||||||
|
import { FC, useCallback, useMemo, useState } from 'react';
|
||||||
import { LocalizeText } from '../../../../api';
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { SendMessageHook } from '../../../../hooks';
|
||||||
import { NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout';
|
import { NitroCardAccordionSetView, NitroCardAccordionView, NitroCardContentView, NitroCardHeaderView, NitroCardTabsItemView, NitroCardTabsView, NitroCardView } from '../../../../layout';
|
||||||
import { MessengerFriend } from '../../common/MessengerFriend';
|
import { MessengerFriend } from '../../common/MessengerFriend';
|
||||||
import { FriendsGroupView } from '../friends-group/FriendsGroupView';
|
import { FriendsGroupView } from '../friends-group/FriendsGroupView';
|
||||||
|
import { FriendsRemoveConfirmationView } from '../friends-remove-confirmation/FriendsRemoveConfirmationView';
|
||||||
import { FriendsRequestView } from '../friends-request/FriendsRequestView';
|
import { FriendsRequestView } from '../friends-request/FriendsRequestView';
|
||||||
|
import { FriendsRoomInviteView } from '../friends-room-invite/FriendsRoomInviteView';
|
||||||
import { FriendsSearchView } from '../friends-search/FriendsSearchView';
|
import { FriendsSearchView } from '../friends-search/FriendsSearchView';
|
||||||
import { FriendsListViewProps } from './FriendsListView.types';
|
import { FriendsListViewProps } from './FriendsListView.types';
|
||||||
|
|
||||||
@ -13,39 +17,107 @@ const MODE_SEARCH: number = 1;
|
|||||||
export const FriendsListView: FC<FriendsListViewProps> = props =>
|
export const FriendsListView: FC<FriendsListViewProps> = props =>
|
||||||
{
|
{
|
||||||
const { onlineFriends = [], offlineFriends = [], friendRequests = [], onCloseClick = null } = props;
|
const { onlineFriends = [], offlineFriends = [], friendRequests = [], onCloseClick = null } = props;
|
||||||
const [ selectedFriends, setSelectedFriends ] = useState<MessengerFriend[]>([]);
|
|
||||||
const [ mode, setMode ] = useState<number>(0);
|
|
||||||
|
|
||||||
useEffect(() =>
|
const [ selectedFriendsIds, setSelectedFriendsIds ] = useState<number[]>([]);
|
||||||
|
const [ mode, setMode ] = useState<number>(0);
|
||||||
|
|
||||||
|
const [ showRoomInvite, setShowRoomInvite ] = useState<boolean>(false);
|
||||||
|
const [ showRemoveFriendsConfirmation, setShowRemoveFriendsConfirmation ] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const removeFriendsText = useMemo(() =>
|
||||||
{
|
{
|
||||||
setSelectedFriends([]);
|
if(!selectedFriendsIds || !selectedFriendsIds.length) return '';
|
||||||
}, [ onlineFriends, offlineFriends ]);
|
|
||||||
|
const userNames: string[] = [];
|
||||||
|
|
||||||
|
for(const userId of selectedFriendsIds)
|
||||||
|
{
|
||||||
|
let existingFriend: MessengerFriend = onlineFriends.find(f => f.id === userId);
|
||||||
|
|
||||||
|
if(!existingFriend) existingFriend = offlineFriends.find(f => f.id === userId);
|
||||||
|
|
||||||
|
if(!existingFriend) continue;
|
||||||
|
|
||||||
|
userNames.push(existingFriend.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return LocalizeText('friendlist.removefriendconfirm.userlist', ['user_names'], [userNames.join(', ')]);
|
||||||
|
}, [offlineFriends, onlineFriends, selectedFriendsIds]);
|
||||||
|
|
||||||
|
const selectFriend = useCallback((userId: number) =>
|
||||||
|
{
|
||||||
|
if(userId < 0) return;
|
||||||
|
|
||||||
|
const existingUserIdIndex: number = selectedFriendsIds.indexOf(userId);
|
||||||
|
|
||||||
|
if(existingUserIdIndex > -1)
|
||||||
|
{
|
||||||
|
const clonedFriend = [...selectedFriendsIds];
|
||||||
|
clonedFriend.splice(existingUserIdIndex, 1)
|
||||||
|
|
||||||
|
setSelectedFriendsIds([...clonedFriend]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setSelectedFriendsIds([...selectedFriendsIds, userId]);
|
||||||
|
}
|
||||||
|
}, [ selectedFriendsIds, setSelectedFriendsIds ]);
|
||||||
|
|
||||||
|
const sendRoomInvite = useCallback((message: string) =>
|
||||||
|
{
|
||||||
|
if(selectedFriendsIds.length === 0 || !message || message.length === 0) return;
|
||||||
|
|
||||||
|
SendMessageHook(new SendRoomInviteComposer(message, ...selectedFriendsIds));
|
||||||
|
setShowRoomInvite(false);
|
||||||
|
}, [ selectedFriendsIds, setShowRoomInvite ]);
|
||||||
|
|
||||||
|
const removeSelectedFriends = useCallback(() =>
|
||||||
|
{
|
||||||
|
if(selectedFriendsIds.length === 0) return;
|
||||||
|
|
||||||
|
SendMessageHook(new RemoveFriendComposer(...selectedFriendsIds));
|
||||||
|
setSelectedFriendsIds([]);
|
||||||
|
setShowRemoveFriendsConfirmation(false);
|
||||||
|
}, [ selectedFriendsIds ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardView className="nitro-friends" uniqueKey="nitro-friends">
|
<>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ onCloseClick } />
|
<NitroCardView className="nitro-friends" uniqueKey="nitro-friends">
|
||||||
<NitroCardTabsView>
|
<NitroCardHeaderView headerText={ LocalizeText('friendlist.friends') } onCloseClick={ onCloseClick } />
|
||||||
<NitroCardTabsItemView isActive={ (mode === MODE_FRIENDS) } count={ friendRequests.length } onClick={ event => setMode(MODE_FRIENDS) }>
|
<NitroCardTabsView>
|
||||||
{ LocalizeText('friendlist.friends') }
|
<NitroCardTabsItemView isActive={ (mode === MODE_FRIENDS) } count={ friendRequests.length } onClick={ event => setMode(MODE_FRIENDS) }>
|
||||||
</NitroCardTabsItemView>
|
{ LocalizeText('friendlist.friends') }
|
||||||
<NitroCardTabsItemView isActive={ (mode === MODE_SEARCH) } onClick={ event => setMode(MODE_SEARCH) }>
|
</NitroCardTabsItemView>
|
||||||
{ LocalizeText('generic.search') }
|
<NitroCardTabsItemView isActive={ (mode === MODE_SEARCH) } onClick={ event => setMode(MODE_SEARCH) }>
|
||||||
</NitroCardTabsItemView>
|
{ LocalizeText('generic.search') }
|
||||||
</NitroCardTabsView>
|
</NitroCardTabsItemView>
|
||||||
<NitroCardContentView className="p-0 text-black">
|
</NitroCardTabsView>
|
||||||
{ (mode === MODE_FRIENDS) &&
|
<NitroCardContentView className="p-0 text-black">
|
||||||
<NitroCardAccordionView>
|
{ (mode === MODE_FRIENDS) &&
|
||||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends') + ` (${onlineFriends.length})` } isExpanded={ true }>
|
<>
|
||||||
<FriendsGroupView list={ onlineFriends } />
|
<NitroCardAccordionView className="overflow-y-auto">
|
||||||
</NitroCardAccordionSetView>
|
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends') + ` (${onlineFriends.length})` } isExpanded={ true }>
|
||||||
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }>
|
<FriendsGroupView list={ onlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
|
||||||
<FriendsGroupView list={ offlineFriends } />
|
</NitroCardAccordionSetView>
|
||||||
</NitroCardAccordionSetView>
|
<NitroCardAccordionSetView headerText={ LocalizeText('friendlist.friends.offlinecaption') + ` (${offlineFriends.length})` }>
|
||||||
<FriendsRequestView requests={ friendRequests } />
|
<FriendsGroupView list={ offlineFriends } selectedFriendsIds={ selectedFriendsIds } selectFriend={ selectFriend } />
|
||||||
</NitroCardAccordionView> }
|
</NitroCardAccordionSetView>
|
||||||
{ (mode === MODE_SEARCH) &&
|
<FriendsRequestView requests={ friendRequests } />
|
||||||
<FriendsSearchView /> }
|
</NitroCardAccordionView>
|
||||||
</NitroCardContentView>
|
{ selectedFriendsIds && selectedFriendsIds.length > 0 && <div className="d-flex gap-2 p-2">
|
||||||
</NitroCardView>
|
<button className="btn btn-primary w-100" onClick={ () => setShowRoomInvite(true) }>Invite</button>
|
||||||
|
<button className="btn btn-danger w-100" onClick={ () => setShowRemoveFriendsConfirmation(true) }>Delete</button>
|
||||||
|
</div> }
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
{ (mode === MODE_SEARCH) &&
|
||||||
|
<FriendsSearchView /> }
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
{ showRoomInvite &&
|
||||||
|
<FriendsRoomInviteView selectedFriendsIds={ selectedFriendsIds } onCloseClick={ () => setShowRoomInvite(false) } sendRoomInvite={ sendRoomInvite } /> }
|
||||||
|
{ showRemoveFriendsConfirmation &&
|
||||||
|
<FriendsRemoveConfirmationView selectedFriendsIds={ selectedFriendsIds } removeFriendsText={ removeFriendsText } onCloseClick={ () => setShowRemoveFriendsConfirmation(false) } removeSelectedFriends={ removeSelectedFriends } /> }
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
|
import { FriendsRemoveConfirmationViewProps } from './FriendsRemoveConfirmationView.types';
|
||||||
|
|
||||||
|
export const FriendsRemoveConfirmationView: FC<FriendsRemoveConfirmationViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { selectedFriendsIds = null, removeFriendsText = null, removeSelectedFriends = null, onCloseClick = null } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroCardView className="nitro-friends-remove-confirmation" uniqueKey="nitro-friends-remove-confirmation" simple={ true }>
|
||||||
|
<NitroCardHeaderView headerText={ LocalizeText('friendlist.removefriendconfirm.title') } onCloseClick={ onCloseClick } />
|
||||||
|
<NitroCardContentView className="text-black d-flex flex-column gap-3">
|
||||||
|
<div>{ removeFriendsText }</div>
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
<button className="btn btn-danger w-100" disabled={ selectedFriendsIds.length === 0 } onClick={ removeSelectedFriends }>{ LocalizeText('generic.ok') }</button>
|
||||||
|
<button className="btn btn-primary w-100" onClick={ onCloseClick }>{ LocalizeText('generic.cancel') }</button>
|
||||||
|
</div>
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,7 @@
|
|||||||
|
export interface FriendsRemoveConfirmationViewProps
|
||||||
|
{
|
||||||
|
selectedFriendsIds: number[];
|
||||||
|
removeFriendsText: string;
|
||||||
|
removeSelectedFriends: () => void;
|
||||||
|
onCloseClick: () => void;
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
import { FC, useState } from 'react';
|
||||||
|
import { LocalizeText } from '../../../../api';
|
||||||
|
import { NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../layout';
|
||||||
|
import { FriendsRoomInviteViewProps } from './FriendsRoomInviteView.types';
|
||||||
|
|
||||||
|
export const FriendsRoomInviteView: FC<FriendsRoomInviteViewProps> = props =>
|
||||||
|
{
|
||||||
|
const { selectedFriendsIds = null, onCloseClick = null, sendRoomInvite = null } = props;
|
||||||
|
|
||||||
|
const [ roomInviteMessage, setRoomInviteMessage ] = useState<string>('');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NitroCardView className="nitro-friends-room-invite" uniqueKey="nitro-friends-room-invite" simple={ true }>
|
||||||
|
<NitroCardHeaderView headerText={ LocalizeText('friendlist.invite.title') } onCloseClick={ onCloseClick } />
|
||||||
|
<NitroCardContentView className="text-black d-flex flex-column gap-2">
|
||||||
|
{ LocalizeText('friendlist.invite.summary', ['count'], [selectedFriendsIds.length.toString()]) }
|
||||||
|
<textarea className="form-control" value={roomInviteMessage} onChange={e => setRoomInviteMessage(e.target.value)}></textarea>
|
||||||
|
<div className="bg-muted rounded text-center p-2">{ LocalizeText('friendlist.invite.note') }</div>
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
<button className="btn btn-success w-100" disabled={ roomInviteMessage.length === 0 || selectedFriendsIds.length === 0 } onClick={ () => sendRoomInvite(roomInviteMessage) }>{ LocalizeText('friendlist.invite.send') }</button>
|
||||||
|
<button className="btn btn-primary w-100" onClick={ onCloseClick }>{ LocalizeText('generic.cancel') }</button>
|
||||||
|
</div>
|
||||||
|
</NitroCardContentView>
|
||||||
|
</NitroCardView>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,6 @@
|
|||||||
|
export interface FriendsRoomInviteViewProps
|
||||||
|
{
|
||||||
|
selectedFriendsIds: number[];
|
||||||
|
onCloseClick: () => void;
|
||||||
|
sendRoomInvite: (message: string) => void;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { FollowFriendMessageComposer, ILinkEventTracker, NewConsoleMessageEvent, SendMessageComposer } from '@nitrots/nitro-renderer';
|
import { FollowFriendMessageComposer, ILinkEventTracker, NewConsoleMessageEvent, RoomInviteEvent, SendMessageComposer } from '@nitrots/nitro-renderer';
|
||||||
import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { FC, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { AddEventLinkTracker, GetSessionDataManager, GetUserProfile, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
|
import { AddEventLinkTracker, GetSessionDataManager, GetUserProfile, LocalizeText, RemoveLinkEventTracker } from '../../../../api';
|
||||||
import { MESSENGER_MESSAGE_RECEIVED, MESSENGER_NEW_THREAD, PlaySound } from '../../../../api/utils/PlaySound';
|
import { MESSENGER_MESSAGE_RECEIVED, MESSENGER_NEW_THREAD, PlaySound } from '../../../../api/utils/PlaySound';
|
||||||
@ -105,6 +105,21 @@ export const FriendsMessengerView: FC<{}> = props =>
|
|||||||
|
|
||||||
CreateMessageHook(NewConsoleMessageEvent, onNewConsoleMessageEvent);
|
CreateMessageHook(NewConsoleMessageEvent, onNewConsoleMessageEvent);
|
||||||
|
|
||||||
|
const onRoomInviteEvent = useCallback((event: RoomInviteEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
const [threadIndex, thread] = getMessageThreadWithIndex(parser.senderId);
|
||||||
|
|
||||||
|
if((threadIndex === -1) || !thread) return;
|
||||||
|
|
||||||
|
thread.addMessage(parser.senderId, parser.messageText, 0, null, MessengerThreadChat.ROOM_INVITE);
|
||||||
|
|
||||||
|
setMessageThreads(prevValue => [...prevValue]);
|
||||||
|
}, [getMessageThreadWithIndex]);
|
||||||
|
|
||||||
|
CreateMessageHook(RoomInviteEvent, onRoomInviteEvent);
|
||||||
|
|
||||||
const sendMessage = useCallback(() =>
|
const sendMessage = useCallback(() =>
|
||||||
{
|
{
|
||||||
if(!messageText || !messageText.length) return;
|
if(!messageText || !messageText.length) return;
|
||||||
|
@ -2,6 +2,7 @@ import { AvatarAction, AvatarExpressionEnum, RoomControllerLevel, RoomObjectCate
|
|||||||
import { FC, useCallback, useMemo, useState } from 'react';
|
import { FC, useCallback, useMemo, useState } from 'react';
|
||||||
import { GetCanStandUp, GetCanUseExpression, GetOwnPosture, GetUserProfile, HasHabboClub, HasHabboVip, IsRidingHorse, LocalizeText, RoomWidgetAvatarExpressionMessage, RoomWidgetChangePostureMessage, RoomWidgetDanceMessage, RoomWidgetMessage, RoomWidgetUpdateDecorateModeEvent, RoomWidgetUserActionMessage } from '../../../../../../api';
|
import { GetCanStandUp, GetCanUseExpression, GetOwnPosture, GetUserProfile, HasHabboClub, HasHabboVip, IsRidingHorse, LocalizeText, RoomWidgetAvatarExpressionMessage, RoomWidgetChangePostureMessage, RoomWidgetDanceMessage, RoomWidgetMessage, RoomWidgetUpdateDecorateModeEvent, RoomWidgetUserActionMessage } from '../../../../../../api';
|
||||||
import { AvatarEditorEvent } from '../../../../../../events';
|
import { AvatarEditorEvent } from '../../../../../../events';
|
||||||
|
import { HelpNameChangeEvent } from '../../../../../../events/help/HelpNameChangeEvent';
|
||||||
import { dispatchUiEvent } from '../../../../../../hooks';
|
import { dispatchUiEvent } from '../../../../../../hooks';
|
||||||
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
|
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
|
||||||
import { useRoomContext } from '../../../../context/RoomContext';
|
import { useRoomContext } from '../../../../context/RoomContext';
|
||||||
@ -42,6 +43,9 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
|
|||||||
case 'decorate':
|
case 'decorate':
|
||||||
eventDispatcher.dispatchEvent(new RoomWidgetUpdateDecorateModeEvent(true));
|
eventDispatcher.dispatchEvent(new RoomWidgetUpdateDecorateModeEvent(true));
|
||||||
break;
|
break;
|
||||||
|
case 'change_name':
|
||||||
|
dispatchUiEvent(new HelpNameChangeEvent(HelpNameChangeEvent.INIT));
|
||||||
|
break;
|
||||||
case 'change_looks':
|
case 'change_looks':
|
||||||
dispatchUiEvent(new AvatarEditorEvent(AvatarEditorEvent.SHOW_EDITOR));
|
dispatchUiEvent(new AvatarEditorEvent(AvatarEditorEvent.SHOW_EDITOR));
|
||||||
break;
|
break;
|
||||||
|
@ -1,47 +1,55 @@
|
|||||||
.nitro-room-tools {
|
.nitro-room-tools-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: $toolbar-height + 65px;
|
bottom: $toolbar-height + 65px;
|
||||||
left: 0;
|
left: 0;
|
||||||
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;
|
|
||||||
|
|
||||||
.list-group-item {
|
.nitro-room-tools {
|
||||||
background: transparent;
|
background: rgba($dark,.95);
|
||||||
padding: 3px 0px;
|
box-shadow: inset 0px 5px lighten(rgba($dark,.6),2.5), inset 0 -4px darken(rgba($dark,.6),4);
|
||||||
color: $white;
|
border-top-right-radius: $border-radius;
|
||||||
border-color: rgba($black, 0.3);
|
border-bottom-right-radius: $border-radius;
|
||||||
cursor: pointer;
|
transition: all .2s ease;
|
||||||
|
|
||||||
&:hover {
|
.list-group-item {
|
||||||
text-decoration: underline;
|
background: transparent;
|
||||||
}
|
padding: 3px 0px;
|
||||||
|
color: $white;
|
||||||
&:first-child {
|
border-color: rgba($black, 0.3);
|
||||||
padding-top: 8px;
|
cursor: pointer;
|
||||||
}
|
|
||||||
|
&:hover {
|
||||||
&:last-child {
|
text-decoration: underline;
|
||||||
border-bottom: none;
|
}
|
||||||
padding-bottom: 8px;
|
|
||||||
}
|
&:first-child {
|
||||||
|
padding-top: 8px;
|
||||||
&.disabled {
|
}
|
||||||
opacity: .5;
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-item {
|
||||||
|
.icon {
|
||||||
|
width: 22px;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tools-item {
|
.nitro-room-tools-info {
|
||||||
.icon {
|
margin-left: 10px;
|
||||||
width: 22px;
|
background: rgba($dark,.95);
|
||||||
background-repeat: no-repeat;
|
box-shadow: inset 0px 5px lighten(rgba($dark,.6),2.5), inset 0 -4px darken(rgba($dark,.6),4);
|
||||||
background-position: center;
|
transition: all .2s ease;
|
||||||
}
|
pointer-events: none;
|
||||||
|
max-width: 250px;
|
||||||
&:hover {
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
import { RoomLikeRoomComposer } from '@nitrots/nitro-renderer';
|
import { GetGuestRoomResultEvent, RoomLikeRoomComposer } from '@nitrots/nitro-renderer';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { FC, useCallback, useState } from 'react';
|
import { FC, useCallback, useEffect, useState } from 'react';
|
||||||
import { LocalizeText, RoomWidgetZoomToggleMessage } from '../../../../api';
|
import { LocalizeText, RoomWidgetZoomToggleMessage } from '../../../../api';
|
||||||
import { Base } from '../../../../common/Base';
|
import { Base, Column, Flex, Text } from '../../../../common';
|
||||||
import { Column } from '../../../../common/Column';
|
|
||||||
import { NavigatorEvent } from '../../../../events';
|
import { NavigatorEvent } from '../../../../events';
|
||||||
import { ChatHistoryEvent } from '../../../../events/chat-history/ChatHistoryEvent';
|
import { ChatHistoryEvent } from '../../../../events/chat-history/ChatHistoryEvent';
|
||||||
import { dispatchUiEvent } from '../../../../hooks/events';
|
import { dispatchUiEvent } from '../../../../hooks/events';
|
||||||
import { SendMessageHook } from '../../../../hooks/messages';
|
import { CreateMessageHook, SendMessageHook } from '../../../../hooks/messages';
|
||||||
import { useRoomContext } from '../../context/RoomContext';
|
import { useRoomContext } from '../../context/RoomContext';
|
||||||
|
|
||||||
export const RoomToolsWidgetView: FC<{}> = props =>
|
export const RoomToolsWidgetView: FC<{}> = props =>
|
||||||
@ -16,6 +15,12 @@ export const RoomToolsWidgetView: FC<{}> = props =>
|
|||||||
const [ isLiked, setIsLiked ] = useState(false);
|
const [ isLiked, setIsLiked ] = useState(false);
|
||||||
const { widgetHandler = null } = useRoomContext();
|
const { widgetHandler = null } = useRoomContext();
|
||||||
|
|
||||||
|
const [ roomName, setRoomName ] = useState(null);
|
||||||
|
const [ roomOwner, setRoomOwner ] = useState(null);
|
||||||
|
const [ roomTags, setRoomTags ] = useState(null);
|
||||||
|
const [ roomInfoDisplay, setRoomInfoDisplay ] = useState(false);
|
||||||
|
const [ isOpen, setIsOpen ] = useState(false);
|
||||||
|
|
||||||
const handleToolClick = useCallback((action: string) =>
|
const handleToolClick = useCallback((action: string) =>
|
||||||
{
|
{
|
||||||
switch(action)
|
switch(action)
|
||||||
@ -41,13 +46,50 @@ export const RoomToolsWidgetView: FC<{}> = props =>
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}, [ isZoomedIn, isLiked, widgetHandler ]);
|
}, [ isZoomedIn, isLiked, widgetHandler ]);
|
||||||
|
|
||||||
|
const onGetGuestRoomResultEvent = useCallback((event: GetGuestRoomResultEvent) =>
|
||||||
|
{
|
||||||
|
const parser = event.getParser();
|
||||||
|
|
||||||
|
if(roomName !== parser.data.roomName) setRoomName(parser.data.roomName);
|
||||||
|
|
||||||
|
if(roomOwner !== parser.data.ownerName) setRoomOwner(parser.data.ownerName);
|
||||||
|
|
||||||
|
if(roomTags !== parser.data.tags) setRoomTags(parser.data.tags);
|
||||||
|
}, [ roomName, roomOwner, roomTags ]);
|
||||||
|
|
||||||
|
CreateMessageHook(GetGuestRoomResultEvent, onGetGuestRoomResultEvent);
|
||||||
|
|
||||||
|
useEffect(() =>
|
||||||
|
{
|
||||||
|
setIsOpen(true);
|
||||||
|
|
||||||
|
const timeout = setTimeout(() => setIsOpen(false), 5000);
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, [ roomName, roomOwner, roomTags ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Column center className="nitro-room-tools p-2">
|
<Flex className="nitro-room-tools-container">
|
||||||
<Base pointer title={ LocalizeText('room.settings.button.text') } className="icon icon-cog" onClick={ () => handleToolClick('settings') } />
|
<Column center className="nitro-room-tools p-2">
|
||||||
<Base pointer title={ LocalizeText('room.zoom.button.text') } onClick={ () => handleToolClick('zoom') } className={ 'icon ' + classNames({ 'icon-zoom-less': !isZoomedIn, 'icon-zoom-more': isZoomedIn }) } />
|
<Base pointer title={ LocalizeText('room.settings.button.text') } className="icon icon-cog" onClick={ () => handleToolClick('settings') } />
|
||||||
<Base pointer title={ LocalizeText('room.chathistory.button.text') } onClick={ () => handleToolClick('chat_history') } className="icon icon-chat-history" />
|
<Base pointer title={ LocalizeText('room.zoom.button.text') } onClick={ () => handleToolClick('zoom') } className={ 'icon ' + classNames({ 'icon-zoom-less': !isZoomedIn, 'icon-zoom-more': isZoomedIn }) } />
|
||||||
{ !isLiked && <Base pointer title={ LocalizeText('room.like.button.text') } onClick={ () => handleToolClick('like_room') } className="icon icon-like-room" /> }
|
<Base pointer title={ LocalizeText('room.chathistory.button.text') } onClick={ () => handleToolClick('chat_history') } className="icon icon-chat-history" />
|
||||||
</Column>
|
{ !isLiked && <Base pointer title={ LocalizeText('room.like.button.text') } onClick={ () => handleToolClick('like_room') } className="icon icon-like-room" /> }
|
||||||
|
</Column>
|
||||||
|
{ isOpen &&
|
||||||
|
<Column center>
|
||||||
|
<Column className="nitro-room-tools-info rounded py-2 px-3">
|
||||||
|
<Column gap={ 1 }>
|
||||||
|
<Text variant="white" fontSize={ 4 }>{ roomName }</Text>
|
||||||
|
<Text variant="muted" fontSize={ 5 }>{ roomOwner }</Text>
|
||||||
|
</Column>
|
||||||
|
{ roomTags && roomTags.length > 0 &&
|
||||||
|
<div className="d-flex gap-2">
|
||||||
|
{ roomTags.map((tag: string) => <div className="rounded bg-primary text-white p-1 text-sm">#{ tag }</div>) }
|
||||||
|
</div> }
|
||||||
|
</Column>
|
||||||
|
</Column> }
|
||||||
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -114,44 +114,48 @@ export const UserSettingsView: FC<{}> = props =>
|
|||||||
if(!isVisible) return null;
|
if(!isVisible) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NitroCardView uniqueKey="user-settings" className="user-settings-window">
|
<NitroCardView uniqueKey="user-settings" className="user-settings-window" simple>
|
||||||
<NitroCardHeaderView headerText={ LocalizeText('widget.memenu.settings.title') } onCloseClick={event => processAction('close_view')} />
|
<NitroCardHeaderView headerText={ LocalizeText('widget.memenu.settings.title') } onCloseClick={event => processAction('close_view')} />
|
||||||
<NitroCardContentView className="text-black">
|
<NitroCardContentView className="text-black d-flex flex-column gap-2">
|
||||||
<div className="form-check">
|
<div className="d-flex flex-column gap-1">
|
||||||
<input className="form-check-input" type="checkbox" checked={ userSettings.oldChat } onChange={ event => processAction('oldchat', event.target.checked) } />
|
<div className="form-check">
|
||||||
<label className="form-check-label">{ LocalizeText('memenu.settings.chat.prefer.old.chat') }</label>
|
<input className="form-check-input" type="checkbox" checked={ userSettings.oldChat } onChange={ event => processAction('oldchat', event.target.checked) } />
|
||||||
</div>
|
<label className="form-check-label">{ LocalizeText('memenu.settings.chat.prefer.old.chat') }</label>
|
||||||
<div className="form-check">
|
</div>
|
||||||
<input className="form-check-input" type="checkbox" checked={ userSettings.roomInvites } onChange={ event => processAction('room_invites', event.target.checked) } />
|
<div className="form-check">
|
||||||
<label className="form-check-label">{ LocalizeText('memenu.settings.other.ignore.room.invites') }</label>
|
<input className="form-check-input" type="checkbox" checked={ userSettings.roomInvites } onChange={ event => processAction('room_invites', event.target.checked) } />
|
||||||
</div>
|
<label className="form-check-label">{ LocalizeText('memenu.settings.other.ignore.room.invites') }</label>
|
||||||
<div className="form-check">
|
</div>
|
||||||
<input className="form-check-input" type="checkbox" checked={ userSettings.cameraFollow } onChange={ event => processAction('camera_follow', event.target.checked) } />
|
<div className="form-check">
|
||||||
<label className="form-check-label">{ LocalizeText('memenu.settings.other.disable.room.camera.follow') }</label>
|
<input className="form-check-input" type="checkbox" checked={ userSettings.cameraFollow } onChange={ event => processAction('camera_follow', event.target.checked) } />
|
||||||
</div>
|
<label className="form-check-label">{ LocalizeText('memenu.settings.other.disable.room.camera.follow') }</label>
|
||||||
<div className="mt-3 mb-2">{ LocalizeText('widget.memenu.settings.volume') }</div>
|
|
||||||
<div className="mb-2">
|
|
||||||
<label>{ LocalizeText('widget.memenu.settings.volume.ui') }</label>
|
|
||||||
<div className={ 'd-flex align-items-center justify-content-center' }>
|
|
||||||
<i className={ 'fas' + ((userSettings.volumeSystem === 0) ? ' fa-volume-mute' : '') + ((userSettings.volumeSystem > 0) ? ' fa-volume-down' : '') + ((userSettings.volumeSystem >= 50) ? ' text-muted' : '') } />
|
|
||||||
<input type="range" className="custom-range ms-2 me-2 w-100" min="0" max="100" step="1" id="volumeSystem" value={ userSettings.volumeSystem } onChange={ event => processAction('system_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/>
|
|
||||||
<i className={ 'fas fa-volume-up' + ((userSettings.volumeSystem < 50) ? ' text-muted': '') } />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mb-2">
|
<div className="d-flex flex-column gap-2">
|
||||||
<label>{ LocalizeText('widget.memenu.settings.volume.furni') }</label>
|
<div className="fw-bold">{ LocalizeText('widget.memenu.settings.volume') }</div>
|
||||||
<div className={ 'd-flex align-items-center justify-content-center' }>
|
<div>
|
||||||
<i className={ 'fas' + ((userSettings.volumeFurni === 0) ? ' fa-volume-mute' : '') + ((userSettings.volumeFurni > 0) ? ' fa-volume-down' : '') + ((userSettings.volumeFurni >= 50) ? ' text-muted' : '') } />
|
<label>{ LocalizeText('widget.memenu.settings.volume.ui') }</label>
|
||||||
<input type="range" className="custom-range ms-2 me-2 w-100" min="0" max="100" step="1" id="volumeFurni" value={ userSettings.volumeFurni } onChange={ event => processAction('furni_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/>
|
<div className={ 'd-flex align-items-center justify-content-center' }>
|
||||||
<i className={ 'fas fa-volume-up' + ((userSettings.volumeFurni < 50) ? ' text-muted': '') } />
|
<i className={ 'fas' + ((userSettings.volumeSystem === 0) ? ' fa-volume-mute' : '') + ((userSettings.volumeSystem > 0) ? ' fa-volume-down' : '') + ((userSettings.volumeSystem >= 50) ? ' text-muted' : '') } />
|
||||||
|
<input type="range" className="custom-range ms-2 me-2 w-100" min="0" max="100" step="1" id="volumeSystem" value={ userSettings.volumeSystem } onChange={ event => processAction('system_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/>
|
||||||
|
<i className={ 'fas fa-volume-up' + ((userSettings.volumeSystem < 50) ? ' text-muted': '') } />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
<div className="mb-2">
|
<label>{ LocalizeText('widget.memenu.settings.volume.furni') }</label>
|
||||||
<label>{ LocalizeText('widget.memenu.settings.volume.trax') }</label>
|
<div className={ 'd-flex align-items-center justify-content-center' }>
|
||||||
<div className={ 'd-flex align-items-center justify-content-center' }>
|
<i className={ 'fas' + ((userSettings.volumeFurni === 0) ? ' fa-volume-mute' : '') + ((userSettings.volumeFurni > 0) ? ' fa-volume-down' : '') + ((userSettings.volumeFurni >= 50) ? ' text-muted' : '') } />
|
||||||
<i className={ 'fas' + ((userSettings.volumeTrax === 0) ? ' fa-volume-mute' : '') + ((userSettings.volumeTrax > 0) ? ' fa-volume-down' : '') + ((userSettings.volumeTrax >= 50) ? ' text-muted' : '') } />
|
<input type="range" className="custom-range ms-2 me-2 w-100" min="0" max="100" step="1" id="volumeFurni" value={ userSettings.volumeFurni } onChange={ event => processAction('furni_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/>
|
||||||
<input type="range" className="custom-range ms-2 me-2 w-100" min="0" max="100" step="1" id="volumeTrax" value={ userSettings.volumeTrax } onChange={ event => processAction('trax_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/>
|
<i className={ 'fas fa-volume-up' + ((userSettings.volumeFurni < 50) ? ' text-muted': '') } />
|
||||||
<i className={ 'fas fa-volume-up' + ((userSettings.volumeTrax < 50) ? ' text-muted': '') } />
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>{ LocalizeText('widget.memenu.settings.volume.trax') }</label>
|
||||||
|
<div className={ 'd-flex align-items-center justify-content-center' }>
|
||||||
|
<i className={ 'fas' + ((userSettings.volumeTrax === 0) ? ' fa-volume-mute' : '') + ((userSettings.volumeTrax > 0) ? ' fa-volume-down' : '') + ((userSettings.volumeTrax >= 50) ? ' text-muted' : '') } />
|
||||||
|
<input type="range" className="custom-range ms-2 me-2 w-100" min="0" max="100" step="1" id="volumeTrax" value={ userSettings.volumeTrax } onChange={ event => processAction('trax_volume', event.target.value) } onMouseUp={ () => saveRangeSlider('volume') }/>
|
||||||
|
<i className={ 'fas fa-volume-up' + ((userSettings.volumeTrax < 50) ? ' text-muted': '') } />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</NitroCardContentView>
|
</NitroCardContentView>
|
||||||
|
Loading…
Reference in New Issue
Block a user