Name change is DONE!

This commit is contained in:
MyNameIsBatman 2022-01-15 01:04:50 -03:00
parent b1b7affa21
commit 9d664896d5
9 changed files with 257 additions and 0 deletions

View 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';
}

View File

@ -12,3 +12,7 @@
.nitro-cfh-sanction-status {
width: 400px;
}
.nitro-change-username {
width: 300px;
}

View File

@ -9,6 +9,7 @@ import { IHelpReportState, initialReportState } from './context/HelpContext.type
import { HelpMessageHandler } from './HelpMessageHandler';
import { DescribeReportView } from './views/DescribeReportView';
import { HelpIndexView } from './views/HelpIndexView';
import { NameChangeView } from './views/name-change/NameChangeView';
import { SanctionSatusView } from './views/SanctionStatusView';
import { SelectReportedChatsView } from './views/SelectReportedChatsView';
import { SelectReportedUserView } from './views/SelectReportedUserView';
@ -76,6 +77,7 @@ export const HelpView: FC<{}> = props =>
</NitroCardView>
}
<SanctionSatusView />
<NameChangeView />
</HelpContextProvider>
);
}

View File

@ -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>
);
}

View 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>
);
}

View 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>
);
}

View 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>
)
}

View File

@ -0,0 +1,5 @@
export interface NameChangeLayoutViewProps
{
username?: string;
onAction: (action: string, value?: string) => void;
}

View File

@ -2,6 +2,7 @@ import { AvatarAction, AvatarExpressionEnum, RoomControllerLevel, RoomObjectCate
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 { AvatarEditorEvent } from '../../../../../../events';
import { HelpNameChangeEvent } from '../../../../../../events/help/HelpNameChangeEvent';
import { dispatchUiEvent } from '../../../../../../hooks';
import { CurrencyIcon } from '../../../../../shared/currency-icon/CurrencyIcon';
import { useRoomContext } from '../../../../context/RoomContext';
@ -42,6 +43,9 @@ export const AvatarInfoWidgetOwnAvatarView: FC<AvatarInfoWidgetOwnAvatarViewProp
case 'decorate':
eventDispatcher.dispatchEvent(new RoomWidgetUpdateDecorateModeEvent(true));
break;
case 'change_name':
dispatchUiEvent(new HelpNameChangeEvent(HelpNameChangeEvent.INIT));
break;
case 'change_looks':
dispatchUiEvent(new AvatarEditorEvent(AvatarEditorEvent.SHOW_EDITOR));
break;